Stashbase

Profile configuration

Define scoped HTTP credential capabilities and ordinary egress for agents

An agent profile lives under agent_profiles in the user-level Stashbase configuration, or as a direct repository-local file at .stashbase/agents/<name>.toml. Each binding gives the child a placeholder and defines exactly when Stashbase may inject the matching real value.

Create a deliberately closed repository-local starter profile with:

stashbase agent init codex

This creates .stashbase/agents/codex.toml with no egress destinations granted. It never overwrites an existing profile unless you pass --force.

Profile reference

SettingPurpose
allow_hooksExplicitly enable installed agent hooks: dependency_check and secret_scan.
[secrets]Configure the Stashbase project/environment used by secret bindings. These settings are no longer valid at the profile root.
[personal_credentials.<binding>]Bind an account-owned Personal Credential. Personal Credentials are available only in remote sessions.
secrets.project and secrets.environmentSelect the Stashbase environment from which configured secret sources are loaded. Both are required when using remote secrets.
fileTop-level profile key. Read configured secret sources from a dotenv, YAML, or JSON file. File values take precedence over remote values.
egress_hostsAllow ordinary HTTP(S) traffic without a Stashbase-managed credential.
deny_hostsBlock destinations even if another allow rule matches.
allow_network_listenersOn macOS, allow the agent to bind local TCP listeners and temporary Unix sockets. Defaults to false; has no effect on Linux.
[filesystem] deny_readDeny reads from the listed paths, globs, or re: regexes and their descendants. See Filesystem restrictions.
[filesystem] deny_writeDeny writes to the listed paths and their descendants.
[sandbox] backendnative (default) or docker. See Docker sandbox.
[sandbox] imageDocker only: run this image instead of the built-in default. Conflicts with dockerfile.
[sandbox] dockerfileDocker only: build and run this Dockerfile, relative to the working directory. Conflicts with image.
[sandbox] memory and [sandbox] cpusDocker only: cap container memory (2g) and CPUs (1.5). No cap by default.
[workspace] worktreeRun the agent in its own git worktree. See Git worktrees.
[config_env]Plaintext, non-sensitive environment variables for the agent. See Config environment variables.
secrets.<binding>.hostsLimit where that binding's placeholder can be exchanged for its real secret.
secrets.<binding>.rulesAllow or deny credential injection by destination host, HTTP method, and normalized URL path. See credential rules.
secrets.<binding>.fromSelect a Stashbase secret name that differs from the binding name.
secrets.<binding>.envChoose the environment variable through which the child receives the placeholder.
secrets.<binding>.headerUse a credential header other than Authorization.
secrets.<binding>.placeholderSet an opaque, format-compatible placeholder for clients that validate token shape before sending a request.

Remote secret sources

Without from, the profile key is also the secret name loaded from Stashbase.

.stashbase/agents/coding.toml
[secrets]
project = "project"
environment = "environment"

egress_hosts = ["api.openai.com"]

[secrets.GH_TOKEN]

[[secrets.GH_TOKEN.rules]]
effect = "allow"
hosts = ["api.github.com"]
methods = ["GET"]
paths = ["*"]

[secrets.OPENAI_API_KEY]

[[secrets.OPENAI_API_KEY.rules]]
effect = "allow"
hosts = ["api.openai.com"]
methods = ["POST"]
paths = ["*"]

Use from when the Stashbase secret name differs from the variable the tool expects. The child receives the profile key as its placeholder; Stashbase fetches the configured source name.

.stashbase/agents/agent.toml
[secrets.GH_TOKEN]
from = "GITHUB_TOKEN"

[[secrets.GH_TOKEN.rules]]
effect = "allow"
hosts = ["api.github.com"]
methods = ["GET"]
paths = ["*"]

Child variable names and placeholders

Use env when the child expects a different environment variable name. The value remains an opaque placeholder, never the real secret.

.stashbase/agents/agent.toml
[secrets.TOOL_TOKEN]
env = "TOOL_API_KEY"
header = "x-api-key"

[[secrets.TOOL_TOKEN.rules]]
effect = "allow"
hosts = ["api.example.com"]
methods = ["GET", "POST"]
paths = ["/v1/*"]

The child receives TOOL_API_KEY=${STASHBASE_TOOL_TOKEN}. The Agent Proxy exchanges that value only in the x-api-key header at api.example.com.

Some clients check an API-key format before issuing a request. Set placeholder to an opaque value that passes that local check:

.stashbase/agents/agent.toml
[secrets.TOOL_TOKEN]
placeholder = "provider-shaped-but-non-secret-placeholder"

This compatibility value is not a credential. Stashbase still gets the real secret from from (or the binding name) and exchanges only the exact configured placeholder.

Personal Credentials

Personal Credentials are account-owned credentials resolved by Stashbase for the authenticated user. Add them under personal_credentials when a remote Agent Proxy session should use a credential that is private to your account rather than a shared project/environment secret.

Personal Credentials are supported only with --remote. They can be used by themselves or alongside [secrets.<binding>] bindings. A Personal-Credential-only profile does not need project, environment, or file, and its remote session request omits project/environment identifiers.

.stashbase/agents/coding.toml
egress_hosts = ["api.openai.com"]

[personal_credentials.LINEAR_API_KEY]
env = "LINEAR_API_KEY"

[[personal_credentials.LINEAR_API_KEY.rules]]
effect = "allow"
hosts = ["mcp.linear.app"]
methods = ["GET", "POST"]
paths = ["/mcp"]

Personal Credentials are never fetched, printed, exported, persisted, or logged by the CLI. The remote Agent Proxy resolves them only for approved requests. Service API keys cannot be used for sessions that contain Personal Credentials, and Personal Credential access must be enabled for the workspace.

Local files and overrides

Set the top-level file profile key to load configured source names from a local dotenv, YAML, or JSON file. A file-only profile does not require a Stashbase API key.

.stashbase/agents/agent.toml
file = ".env.local"

[secrets.GH_TOKEN]

[[secrets.GH_TOKEN.rules]]
effect = "allow"
hosts = ["api.github.com"]
methods = ["GET"]
paths = ["*"]

How Stashbase chooses secret sources

Each configured secret is identified by its from value, or by its profile key when from is omitted. Stashbase reads configured sources from file first, then fetches only the still-missing sources from secrets.project and secrets.environment.

Profile configurationWhere configured sources come fromAPI key required
secrets.project + secrets.environmentStashbase fetches every configured source name from that environment.Yes
fileThe local file supplies every configured source name.No
secrets.project + secrets.environment + fileThe file wins for sources it contains; Stashbase fetches only the remaining configured sources.Only when one or more sources are missing from the file

A combined profile is useful when a developer keeps one credential locally but gets the rest from Stashbase:

.stashbase/agents/agent.toml
file = ".env.local"

[secrets]
project = "project"
environment = "environment"

[secrets.GH_TOKEN]
from = "GITHUB_TOKEN"

[[secrets.GH_TOKEN.rules]]
effect = "allow"
hosts = ["api.github.com"]
methods = ["GET"]
paths = ["*"]

[secrets.OPENAI_API_KEY]

[[secrets.OPENAI_API_KEY.rules]]
effect = "allow"
hosts = ["api.openai.com"]
methods = ["POST"]
paths = ["*"]

If .env.local contains GITHUB_TOKEN, Stashbase does not request it remotely. It fetches only OPENAI_API_KEY from the project / environment environment. The agent receives placeholders named GH_TOKEN and OPENAI_API_KEY, never either real value.

Never place secret values or API keys in an untrusted file.

Config environment variables

Use [config_env] to give the agent ordinary, non-sensitive configuration. Values are stored in plaintext in the profile and work with both the native and the Docker backend (a Docker container otherwise starts with an empty environment).

.stashbase/agents/coding.toml
[config_env]
NODE_ENV = "development"
RUST_LOG = "info"
PORT = 3000

Values can be strings, numbers, or booleans; the agent always receives them as strings. They take precedence over variables inherited from your shell.

To set or override a value for a single run, pass --config-env (repeatable):

stashbase agent run --profile coding --config-env NODE_ENV=test --config-env PORT=4000 -- claude

Credentials don't belong here; use [secrets] so the agent only ever sees a placeholder. Stashbase checks every value, and one that looks like a credential (a name such as *TOKEN* or *API_KEY*, a known token format, a URL with a password, or a long random-looking string) is flagged: agent validate warns, and agent run asks for confirmation, failing when it can't ask (non-interactive or --silent). Flagged values are never printed. If a flagged value really isn't secret, mark it:

[config_env]
BUILD_HASH = { value = "9f86d081884c7d659a2feaa0c55ad015", not_secret = true }

Some names are rejected:

  • Variables the sandbox sets: proxy and CA variables, HOME, TMPDIR, FORCE_COLOR, and anything starting with STASHBASE_.
  • PATH, LD_*, and DYLD_*, which control the programs and libraries that start the sandbox.
  • A name already provided by a [secrets] or [personal_credentials] binding.

Each NAME=value can be up to 64 KiB and the whole table up to 256 KiB. Keep larger content in a file in the workspace.

Restrict filesystem access

See the dedicated Filesystem restrictions page for platform behavior, diagnostics, and troubleshooting.

Use [filesystem] deny_read and [filesystem] deny_write to protect sensitive files and directories:

.stashbase/agents/coding.toml
[filesystem]
deny_read = [".env", "~/.ssh", "~/.aws"]
deny_write = [".git", "~/.ssh"]

Filesystem entries may be relative to the working directory, home-directory paths such as ~/.ssh, or absolute paths. A matching entry also covers its directory descendants. Entries can also be globs (**/.env*) or re:-prefixed regular expressions; see Filesystem restrictions for backend support. Patterns aren't supported on Windows.

On macOS, filesystem restrictions use the Seatbelt sandbox. On Linux, Stashbase prefers a systemd user session and can use bubblewrap as a fallback. If no supported filesystem-enforcement backend is available, validation and launch fail closed rather than silently weakening the policy. For container isolation with a network-layer firewall on any platform, including Windows, set [sandbox] backend = "docker"; see Docker sandbox. Existing file descriptors and data already loaded into process memory are outside filesystem restrictions.

Allow local network listeners

Agent runs deny incoming network listeners by default. On macOS, set allow_network_listeners = true when a trusted test or worker process needs to bind a local TCP listener or use Unix-socket IPC:

.stashbase/agents/test.toml
allow_network_listeners = true

This allows localhost TCP connections and Unix sockets under the system temporary directories so nested test runners such as Nx, Jest, and Rust workers can communicate. macOS Seatbelt cannot restrict TCP binding to 127.0.0.1, so this also permits binding the host's LAN addresses. Enable it only for profiles that need it. It does not widen direct internet access, which remains restricted to the Agent Proxy. On Linux, loopback remains available for the embedded proxy and this setting has no effect.

A remote session still requires at least one Stashbase-managed secret or Personal Credential; a policy-only profile cannot be used with --remote.

Use validation before starting an agent:

stashbase agent validate --profile coding

Filesystem denials observed directly by the proxy appear in the audit log as filesystem_denied events. A nested tool may instead surface the native operating-system error it received.

Configuration changes

Profiles created for older CLI versions need these changes:

  • Move project and environment into the [secrets] table. Top-level project and environment fields are rejected. file stays a top-level profile key.
  • Use a remote secret binding's from field to select a Stashbase secret when its name differs from the binding.
  • If using value_template, use {value} as the placeholder instead of {secret}.
  • Audit metadata uses binding_name and binding_source to identify the binding and whether it came from a Stashbase secret or a Personal Credential.

Hosts and egress

hosts is the legacy host-only allowlist for credential injection. It applies when the binding has no rules. Keep each list limited to destinations that should receive that specific credential. A leading wildcard such as *.githubcopilot.com matches subdomains, not the apex domain.

egress_hosts controls where the agent may connect, including ordinary HTTP(S) traffic without injecting a Stashbase credential. It remains separate from a secret's credential policy: a matching credential rule never widens ordinary egress.

.stashbase/agents/full-stack.toml
egress_hosts = ["registry.npmjs.org", "pypi.org", "docs.rs"]

[secrets.GH_TOKEN]

[[secrets.GH_TOKEN.rules]]
effect = "allow"
hosts = ["api.github.com", "github.com"]
methods = ["GET"]
paths = ["*"]

egress_hosts = ["*"] permits unrestricted HTTP(S) egress, but does not expand a secret's injection hosts. Prefer a small destination allowlist.

Block destinations explicitly

Use deny_hosts to block a destination even when it would otherwise be allowed by egress_hosts or a secret's hosts list. Deny rules always take precedence.

This is especially useful when an agent needs broad ordinary egress but must not reach the Stashbase API with the developer's normal local credentials:

.stashbase/agents/coding.toml
egress_hosts = ["*"]
deny_hosts = ["api.stashbase.dev"]

[secrets.GH_TOKEN]

[[secrets.GH_TOKEN.rules]]
effect = "allow"
hosts = ["api.github.com"]
methods = ["GET"]
paths = ["*"]

With this profile, the agent can make ordinary HTTP(S) requests to any host and can use GH_TOKEN only at api.github.com. Requests to api.stashbase.dev are denied, regardless of any matching allow rule.

If you use a custom Stashbase API endpoint, add the hostname from STASHBASE_API_URL instead. Keep the denylist narrow and use egress_hosts as the primary allow policy; deny_hosts is an explicit override for destinations that must never be reachable.

Egress-only profiles

An egress-only profile has no secrets or personal_credentials table. It starts the Agent Proxy solely to enforce destination policy and grants the child no Stashbase-managed credentials. This is useful for Codex with an existing local sign-in or MCP-only workflows that authenticate independently.

.stashbase/agents/codex.toml
egress_hosts = ["api.openai.com", "chatgpt.com"]
deny_hosts = ["api.stashbase.dev"]

Stashbase prints an explicit egress-only warning when this mode starts. To prevent accidental credential loading, validation rejects an egress-only profile that defines [secrets] or [personal_credentials].

HTTP MCP servers

Use [mcp_servers.<name>] to associate an HTTP MCP endpoint with a credential binding and restrict its tools. See HTTP MCP servers for configuration, authorization semantics, inspection commands, and local versus remote behavior.

Custom credential headers

By default, Stashbase exchanges a placeholder in an exact Authorization: Bearer <placeholder> header. Use header for providers that use another header.

[secrets.CONTEXT7_API_KEY]
header = "CONTEXT7_API_KEY"

[[secrets.CONTEXT7_API_KEY.rules]]
effect = "allow"
hosts = ["mcp.context7.com"]
methods = ["GET", "POST"]
paths = ["/mcp"]

Next steps

On this page