Share feedback
Answers are generated based on the documentation.

Credentials

Most agents need an API key for their model provider. An HTTP/HTTPS proxy on your host intercepts outbound requests from the sandbox, looks up the matching credential on the host, and overwrites the auth header before forwarding. The real credential stays on the host when proxy management is active; the sandbox sees only a sentinel value. See Trust boundaries for how credential isolation fits into the broader sandbox security model.

How credential injection works

When a sandbox makes an outbound request, the host-side proxy decides three things: whether the request matches a service the kit (or built-in agent) declares, what header to write, and what value to inject. The kit declares the match and the header; you provide the value on the host. For proxy-managed credentials, the real value never enters the sandbox — the agent sees only a sentinel like proxy-managed.

A kit can set OAuth passthrough: true to opt out of sentinel masking. This sends the real token response into the sandbox and reduces credential isolation. See the oauth kit fields.

There are several ways to provide that value. When more than one source has a value for the same service, the stored secret takes precedence.

FormWhat it isUse it when
Stored secrets (sbx secret set)A value in your OS keychain, keyed by serviceThe default for any built-in or kit-declared service
Custom secrets (sbx secret set-custom)A value keyed to a domain and environment variableThe service model doesn't fit — the agent validates the variable's format, or the secret rides in a request body
OAuthA host-side sign-in flow; the token never enters the sandboxThe agent supports it, such as Claude Code, Codex, Cursor, or Droid
Credential bindings (credentials.yaml)Per-service mechanism and domain approvalRequired for third-party schemaVersion: "2" kits
Registry credentials (sbx secret set --registry)Authentication for pulling images and kitsPulling templates or kits from a private registry

For multi-provider agents (OpenCode, Docker Agent), the proxy selects credentials based on the API endpoint being called. See individual agent pages for provider-specific details.

Stored secrets

sbx secret set stores credentials in your OS keychain, keyed on a service identifier. Built-in agents declare a fixed set of services. Custom kits can declare their own. The same sbx secret set flow works for both.

Where secrets are stored

The store backing sbx secret set depends on your operating system:

  • macOS: the system Keychain.
  • Windows: the Windows Credential Manager.
  • Linux: the Secret Service exposed by your desktop keyring, such as GNOME Keyring or KDE Wallet.

The Ubuntu package depends on GNOME Keyring, so a standard desktop install needs no extra setup.

On Linux hosts without a running Secret Service — headless servers and some WSL setups — sbx falls back to an encrypted file under your user config directory $XDG_CONFIG_HOME/com.docker.sandboxes, which defaults to ~/.config/com.docker.sandboxes when $XDG_CONFIG_HOME is unset. The fallback is automatic and needs no configuration. When you store a secret this way, sbx prints a notice:

No keychain detected - this secret will be stored in an encrypted file on disk

The file is encrypted at rest and protected by 0700 directory permissions, the same posture as ~/.docker/config.json. This is weaker than an OS keychain, which also mediates access per application. If you start a Secret Service on the host later, sbx stores new secrets in the keychain again. For more on running sandboxes without a desktop keyring, see Can I use Docker Sandboxes on headless Linux?

Store a secret

$ sbx secret set anthropic

This prompts you for the secret value interactively. Service secrets are global by default, so the secret is available to all sandboxes. To scope a secret to a specific sandbox instead:

$ sbx secret set openai --sandbox my-sandbox
Note

A sandbox-scoped secret takes effect immediately, even if the sandbox is running. A global secret only applies when a sandbox is created. If you set or change a global secret while a sandbox is running, recreate the sandbox for the new value to take effect.

Import from environment variables

If you already have API keys set in your shell, sbx secret import reads them and stores them in the keychain without typing each value manually:

$ sbx secret import

This scans your current session for the environment variables in the built-in services table below and prompts you to confirm each one before writing. To import a single service:

$ sbx secret import openai

Pass --all to import everything without prompting (new entries only; existing entries are left unchanged), or --force to overwrite existing entries:

$ sbx secret import --all
$ sbx secret import openai --force

Pass --dry-run to preview what would be imported without writing anything. Run sbx secret ls afterwards to confirm what's stored. For setting up credentials in CI, see CI and headless use.

Built-in services

Each built-in service name maps to the environment variables sbx secret import reads and the API domains the proxy injects credentials into:

ServiceEnvironment variablesAPI domains
anthropicANTHROPIC_API_KEYapi.anthropic.com, console.anthropic.com, claude.ai, mcp-proxy.anthropic.com
cursorCURSOR_API_KEYapi2.cursor.sh, api3.cursor.sh, repo42.cursor.sh, cursor.com
droidFACTORY_API_KEYapi.factory.ai, app.factory.ai, relay.factory.ai
githubGH_TOKEN, GITHUB_TOKENapi.github.com, github.com, raw.githubusercontent.com, gist.github.com, copilot.github.com, api.githubcopilot.com
googleGEMINI_API_KEY, GOOGLE_API_KEYgenerativelanguage.googleapis.com, oauth2.googleapis.com, aiplatform.googleapis.com, vertexai.googleapis.com
groqGROQ_API_KEYapi.groq.com
mistralMISTRAL_API_KEYapi.mistral.ai
nebiusNEBIUS_API_KEYapi.studio.nebius.com, api.tokenfactory.nebius.com
openaiOPENAI_API_KEYapi.openai.com, openai.com, chatgpt.com, www.chatgpt.com
openrouterOPENROUTER_API_KEYopenrouter.ai
xaiXAI_API_KEYapi.x.ai

When you store a secret with sbx secret set <service>, the proxy injects it into requests to the listed API domains.

Services declared by kits

Custom kits can declare their own service identifiers in spec.yaml. In schemaVersion: "2", credentials are declared under the credentials: list:

credentials:
  - service: my-service
    apiKey:
      name: MY_SERVICE_TOKEN
      proxyManaged: true
      inject:
        - domain: api.my-service.com
          scheme: bearer

Each service declares apiKey, oauth, or both. When both resolve at runtime, the API key takes precedence and OAuth acts as the fallback. To provide the credential value, run sbx secret set with the same identifier the kit declares:

$ sbx secret set my-service

There's no separate registration step; the keychain entry is keyed on the identifier the kit already uses. See Authenticate to external services for the full kit-side wiring.

List and remove secrets

List all stored secrets:

$ sbx secret ls
SCOPE      TYPE      NAME      SECRET
(global)   service   github    gho_GCaw4o****...****43qy

Remove a secret:

$ sbx secret rm github
Note

Running sbx reset deletes all stored secrets along with all sandbox state. You'll need to re-add your secrets after a reset.

GitHub token

The github service gives the agent access to the gh CLI inside the sandbox. Pass your existing GitHub CLI token:

$ echo "$(gh auth token)" | sbx secret set github

This is useful for agents that create pull requests, open issues, or interact with GitHub APIs on your behalf.

SSH agent

If your host has an SSH agent and SSH_AUTH_SOCK is set, Docker Sandboxes forwards the agent into the sandbox and sets SSH_AUTH_SOCK there. The private keys stay on your host. Processes inside the sandbox can request signatures from the forwarded agent, but they can't read or copy the private key.

Use SSH agent forwarding for Git operations over SSH and SSH-based commit signing. The signing key must be loaded in the host SSH agent for sandboxed commit signing to work. Outbound SSH connections are still subject to sandbox network policy. For details, see Commit signing.

Custom secrets

Important

Custom secrets are experimental. Behavior, flags, and the placeholder format may change without notice.

For credentials that don't fit the service-identifier model — for example, when an agent validates the environment variable format at boot, or when the credential lands in a request body rather than a header — use sbx secret set-custom. The secret is keyed on one or more target domains, an environment variable name, and an optional placeholder string, instead of a service identifier. Custom secrets are global by default. Pass --sandbox to scope one to a specific sandbox.

$ sbx secret set-custom \
    --host api.example.com \
    --env API_KEY \
    --value <secret>

Repeat --host to cover multiple domains with the same secret — useful when an API is split across related hostnames or when two unrelated endpoints share a credential:

$ sbx secret set-custom \
    --host api.example.com \
    --host uploads.example.com \
    --env API_KEY \
    --value <secret>

A --host value can also use wildcards, with the same syntax as network rules: * matches a single label (*.example.com covers api.example.com) and ** matches any number (**.example.com covers api.example.com and v2.api.example.com).

Warning

Passing the secret as --value <secret> records it in your shell history and exposes it to other processes running as your user. Avoid pasting real credentials inline — read the value from a variable that's already in your environment, and clear shell history if a real secret was passed on the command line.

Inside the sandbox, API_KEY is set to a generated placeholder (for example, sbx-cs-<rand>). When a sandboxed process sends a request to any of the configured hosts and the placeholder appears anywhere in the request, the proxy replaces it with the real value. The agent never sees the real secret.

Prefer the service-based flow whenever it's an option — the kit handles the wiring; you only provide the value.

Credential bindings

A credential bindings file records which credential mechanisms and domains you've approved for each service. It lives at ~/.config/sbx/credentials.yaml, or %APPDATA%\sbx\credentials.yaml on Windows.

Third-party kits that declare schemaVersion: "2" require an approved binding for each credential they use. sbx creates one interactively the first time you run such a kit (see First-run approval); you can also write entries by hand. Credentials declared only by embedded, built-in kits are authorized by provenance and don't need a binding.

Each entry under bindings is keyed by a service identifier and approves one or both credential mechanisms:

  • apiKey — approves injecting the service's stored API key. The value comes from the secret store (sbx secret set <service>); the binding records approval, it doesn't hold or locate the value.
  • oauth — approves the OAuth flow for the service. You sign in on the host, and the proxy handles token refresh and routing. OAuth domains include the token endpoint host and any resource hosts declared by the kit.

Each mechanism takes a domains list that records the domains you approved. sbx asks for approval when a kit requests domains that the existing binding doesn't cover.

bindings:
  anthropic:
    apiKey:
      domains: [api.anthropic.com]
  github:
    apiKey:
      domains: [api.github.com, github.com]

A binding is only an approval record: the presence of apiKey or oauth authorizes that mechanism. Declining a credential writes no entry at all.

First-run approval

When a third-party kit needs a credential that has no binding, sbx walks you through approving one. For an API key, you can use a value already in the secret store or enter one at the prompt. For OAuth, you approve the sign-in flow. In both cases, you approve the domains declared by the kit. sbx writes the entry to credentials.yaml.

In non-interactive contexts (CI or --detached), there's no one to answer the prompt. Without a binding, the sandbox starts with the credential withheld. If the kit marks the credential as required: true, sbx also prints a warning. Pre-create the binding by running the kit interactively once or by writing credentials.yaml directly before running unattended.

The bindings file gates whether a third-party v2 kit can use a service credential. The kit's credential injection rules and network permissions still constrain which requests can carry the credential.

Kits that require a binding

Only third-party kits that declare schemaVersion: "2" require a binding. Built-in agents also use schemaVersion: "2", but credentials declared only by embedded kits are authorized by provenance and inject automatically. If a third-party kit also declares the same service, that service requires approval. Kits on schemaVersion: "1" inject their declared credentials without a binding.

Registry credentials

Registry credentials authenticate to private OCI registries when pulling templates or kits, and can also let the agent pull and push images from inside the sandbox through the host-side proxy. Use sbx secret set --registry <host> to store them. For Docker Hub, sbx reuses your sbx login session — no registry secret needed. For other registries (GitHub Container Registry, ECR, ACR, self-hosted Nexus, and so on), store credentials with sbx secret set --registry.

Choose the scope by adding --all-sandboxes, adding --sandbox SANDBOX, or passing neither:

sbx secret set [--all-sandboxes | --sandbox SANDBOX] --registry HOST
  • Host-only (no scope flag): the sbx CLI uses it to pull templates and kits when creating a sandbox. The credential stays on the host and is never available inside the sandbox.
  • All sandboxes (--all-sandboxes): same as host-only, plus the proxy authenticates registry login requests from sandboxes. The credential stays on the host and is never written to the sandbox filesystem. Use it when agents build and publish container images.
  • Sandbox-scoped (--sandbox SANDBOX): same proxy behavior as --all-sandboxes, but only for the named sandbox. Use it when only one sandbox needs registry access.

Store registry credentials

Pipe a token from stdin and target the registry hostname:

$ gh auth token | sbx secret set --registry ghcr.io --password-stdin

For registries that require a username (for example, ACR with an admin account), add --username:

$ echo "$ACR_PASSWORD" | sbx secret set \
    --registry myregistry.azurecr.io \
    --username myuser \
    --password-stdin

Add --all-sandboxes to make the credential available to every new sandbox:

$ gh auth token | sbx secret set --all-sandboxes --registry ghcr.io --password-stdin
$ sbx run claude

Store all-sandboxes registry credentials before creating a sandbox. Existing sandboxes don't pick up all-sandboxes registry credentials added later. To add registry access to an existing sandbox, use a sandbox-scoped credential instead.

To scope the credential to a single sandbox, store it under that sandbox's name:

$ gh auth token | sbx secret set --sandbox my-app --registry ghcr.io --password-stdin

sbx kit pull also uses these credentials, with the Docker credential store as a fallback. sbx kit push uses only the Docker credential store — push targets still require a prior docker login.

Remove registry credentials

Remove both the host-only and all-sandboxes entries for a registry:

$ sbx secret rm --registry ghcr.io -f

To remove only the all-sandboxes entry and leave the host-only credential in place, pass --all-sandboxes:

$ sbx secret rm --all-sandboxes --registry ghcr.io -f

To remove a sandbox-scoped credential, pass the sandbox name:

$ sbx secret rm --sandbox my-sandbox --registry ghcr.io -f

Best practices

  • Use stored secrets to provide credentials. They are encrypted at rest in the OS keychain (or an encrypted file on Linux hosts without a keychain). See Where secrets are stored.
  • Don't set API keys manually inside the sandbox. Sandbox agents are pre-configured to use proxy-managed credentials.
  • Registry credentials stay on the host and are injected by the proxy when a sandbox authenticates to the registry. Reserve them for sandboxes that need registry access, and prefer sandbox scope over --all-sandboxes to limit exposure.
  • Several agents support OAuth as another secure option: the flow runs on the host, so the token is never exposed inside the sandbox. If you haven't stored a credential, the agent prompts you to authenticate — Codex prompts on the host from sbx run codex, while Claude Code, Cursor, and Droid prompt interactively inside the sandbox. To authenticate ahead of time, run sbx secret set openai --oauth for Codex or use /login inside Claude Code; Cursor and Droid have no ahead-of-time option, so their sign-in prompt appears when the agent starts. See the individual agent pages for each agent's flow.
  • If you store credentials in 1Password, see Sourcing credentials from 1Password for how to use op read and op run with sbx.

Custom templates and placeholder values

When building custom templates or installing agents manually in a shell sandbox, some agents require environment variables like OPENAI_API_KEY to be set before they start. Set these to placeholder values (e.g. proxy-managed) if needed. The proxy injects actual credentials regardless of the environment variable value.