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 codexThis creates .stashbase/agents/codex.toml with no egress destinations granted. It never overwrites an existing profile unless you pass --force.
Profile reference
| Setting | Purpose |
|---|---|
[secrets] | Configure the Stashbase project/environment or local file 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.environment | Select the Stashbase environment from which configured secret sources are loaded. Both are required when using remote secrets. |
secrets.file | Read configured secret sources from a dotenv, YAML, or JSON file. File values take precedence over remote values. |
egress_hosts | Allow ordinary HTTP(S) traffic without a Stashbase-managed credential. |
deny_hosts | Block destinations even if another allow rule matches. |
[commands] denied | Deny executable names for the agent and its descendants. See Command restrictions. |
[filesystem] deny_read | Deny reads from the listed paths and their descendants. See Filesystem restrictions. |
[filesystem] deny_write | Deny writes to the listed paths and their descendants. |
secrets.<binding>.hosts | Limit where that binding's placeholder can be exchanged for its real secret. |
secrets.<binding>.rules | Allow or deny credential injection by destination host, HTTP method, and normalized URL path. See credential rules. |
secrets.<binding>.source_name | Select a Stashbase secret name that differs from the binding name. |
secrets.<binding>.env | Choose the environment variable through which the child receives the placeholder. |
secrets.<binding>.header | Use a credential header other than Authorization. |
secrets.<binding>.placeholder | Set an opaque, format-compatible placeholder for clients that validate token shape before sending a request. |
Remote secret sources
Without source_name, the profile key is also the secret name loaded from Stashbase.
[secrets]
project = "project"
environment = "environment"
egress_hosts = ["api.openai.com"]
[secrets.GH_TOKEN]
hosts = ["api.github.com"]
[secrets.OPENAI_API_KEY]
hosts = ["api.openai.com"]Use source_name 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.
[secrets.GH_TOKEN]
source_name = "GITHUB_TOKEN"
hosts = ["api.github.com"]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.
[secrets.TOOL_TOKEN]
env = "TOOL_API_KEY"
hosts = ["api.example.com"]
header = "x-api-key"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:
[secrets.TOOL_TOKEN]
placeholder = "provider-shaped-but-non-secret-placeholder"This compatibility value is not a credential. Stashbase still gets the real secret from source_name (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.
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 secrets.file to load configured source names from a local dotenv, YAML, or JSON file. A file-only profile does not require a Stashbase API key.
[secrets]
file = "../../.env.local"
[secrets.GH_TOKEN]
hosts = ["api.github.com"]How Stashbase chooses secret sources
Each configured secret is identified by its source_name value, or by its profile key when source_name is omitted. Stashbase reads configured sources from secrets.file first, then fetches only the still-missing sources from secrets.project and secrets.environment.
| Profile configuration | Where configured sources come from | API key required |
|---|---|---|
secrets.project + secrets.environment | Stashbase fetches every configured source name from that environment. | Yes |
secrets.file | The local file supplies every configured source name. | No |
secrets.project + secrets.environment + secrets.file | The 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:
[secrets]
project = "project"
environment = "environment"
file = ".env.local"
[secrets.GH_TOKEN]
source_name = "GITHUB_TOKEN"
hosts = ["api.github.com"]
[secrets.OPENAI_API_KEY]
hosts = ["api.openai.com"]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.
Restrict commands and filesystem access
See the dedicated Command restrictions and Filesystem restrictions pages for platform behavior, diagnostics, and troubleshooting.
Use [commands] denied to prevent the agent and processes it launches from running selected executables. Use [filesystem] deny_read and [filesystem] deny_write to protect sensitive files and directories:
[commands]
denied = ["ssh", "sudo", "docker"]
[filesystem]
deny_read = [".env", "~/.ssh", "~/.aws"]
deny_write = [".git", "~/.ssh"]Command restrictions cover normal PATH lookups and, where OS-level enforcement is available, absolute executable paths as well. Shell built-ins are not executable files and are outside the command policy. Denied commands exit with status 126 and return a structured command_denied policy error.
Filesystem entries are explicit paths, not globs. They may be relative to the working directory, home-directory paths such as ~/.ssh, or absolute paths. A matching entry also covers its directory descendants. Invalid paths and glob characters are rejected during validation.
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. Existing file descriptors and data already loaded into process memory are outside filesystem restrictions.
A remote session still requires at least one Stashbase-managed secret or Personal Credential; a policy-only profile cannot be used with --remote.
Use the command inspection and validation commands before starting an agent:
stashbase agent command --profile coding --command curl
stashbase agent command --profile coding --command curl --json
stashbase agent validate --profile codingThe command report shows whether a command is denied and whether enforcement is OS-level or uses the PATH-wrapper fallback. Denials observed directly by the proxy appear in the audit log as command_denied or 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,environment, andfileinto the[secrets]table. Top-levelprojectandenvironmentfields are rejected. - Rename a remote secret binding's
fromfield tosource_name. - If using
value_template, use{value}as the placeholder instead of{secret}. - Audit metadata uses
binding_nameandbinding_sourceto 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.
egress_hosts = ["registry.npmjs.org", "pypi.org", "docs.rs"]
[secrets.GH_TOKEN]
hosts = ["api.github.com", "github.com"]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:
egress_hosts = ["*"]
deny_hosts = ["api.stashbase.dev"]
[secrets.GH_TOKEN]
hosts = ["api.github.com"]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.
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].
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]
hosts = ["mcp.context7.com"]
header = "CONTEXT7_API_KEY"