Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion content/manuals/ai/sandboxes/agents/copilot.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Copilot requires a GitHub token with Copilot access. Store your token using
[stored secrets](../security/credentials.md#stored-secrets):

```console
$ echo "$(gh auth token)" | sbx secret set github
$ sbx secret set github --command 'gh auth token'
```

## Configuration
Expand Down
2 changes: 1 addition & 1 deletion content/manuals/ai/sandboxes/get-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ To give the agent access to GitHub for creating pull requests or interacting
with repositories:

```console
$ sbx secret set github -t "$(gh auth token)"
$ sbx secret set github --command 'gh auth token'
```

## Run your first sandbox
Expand Down
91 changes: 81 additions & 10 deletions content/manuals/ai/sandboxes/security/credentials.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ value for the same service, the stored secret takes precedence.

| Form | What it is | Use it when |
| --------------------------------------------------------------------------- | ------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------- |
| [Stored secrets](#stored-secrets) (`sbx secret set`) | A value in your OS keychain, keyed by service | The default for any built-in or kit-declared service |
| [Stored secrets](#stored-secrets) (`sbx secret set`) | A value or dynamic source in your OS keychain, keyed by service | The default for any built-in or kit-declared service |
| [Custom secrets](#custom-secrets) (`sbx secret set-custom`) | A value keyed to a domain and environment variable | The service model doesn't fit — the agent validates the variable's format, or the secret rides in a request body |
| OAuth | A host-side sign-in flow; the token never enters the sandbox | The agent supports it, such as Claude Code, Codex, Cursor, or Droid |
| [Credential bindings](#credential-bindings) (`credentials.yaml`) | Per-service mechanism and domain approval | Required for third-party `schemaVersion: "2"` kits |
Expand All @@ -43,9 +43,10 @@ credentials based on the API endpoint being called. See individual

## 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.
`sbx secret set` stores credential values or dynamic secret sources 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

Expand Down Expand Up @@ -97,6 +98,60 @@ $ sbx secret set openai --sandbox my-sandbox
> you set or change a global secret while a sandbox is running, recreate the
> sandbox for the new value to take effect.

### Use a dynamic secret source

Dynamic secret sources let `sbx` retrieve a credential from an authenticated
host tool when the proxy needs it. The secret store contains the reference or
command instead of the credential value. Resolution and caching happen on the
host, and the sandbox still receives only the proxy-managed placeholder.

Use `--ref` with a 1Password secret reference or an AWS Secrets Manager ARN:

```console
$ sbx secret set anthropic --ref 'op://Work/Anthropic/credential'
$ sbx secret set openai \
--ref 'arn:aws:secretsmanager:us-west-2:123456789012:secret:openai-api-key'
```

The corresponding `op` or `aws` CLI must be installed and authenticated on the
host.

> [!NOTE]
> To resolve a reference with a specific 1Password account or AWS profile, set
> `OP_ACCOUNT` or `AWS_PROFILE` when you run `sbx secret set`. `sbx` uses that

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[MEDIUM] NOTE callout wording may confuse users about when OP_ACCOUNT/AWS_PROFILE must be set

The NOTE says to set OP_ACCOUNT or AWS_PROFILE "when you run sbx secret set", then adds that "sbx uses that account or profile whenever it resolves the secret." The second sentence implies the account/profile is persisted at registration — but it doesn't say that explicitly. Users could read the first sentence and conclude they only need the variable at registration time, and be confused if the daemon uses a different account at resolution time (e.g., if the variable isn't persisted and must be present in the daemon's environment too).

Consider clarifying the mechanism — for example: "The value is stored with the secret reference and used by the daemon at every resolution. You don't need to keep the variable set after registration."

> account or profile whenever it resolves the secret. If neither variable is
> set, the provider CLI uses its default.

Use `--command` for another host tool that prints a secret to standard output:

```console
$ sbx secret set github --command 'gh auth token'
```

`sbx` runs the command through the host shell and trims its output. The command
text is stored and replayed by the daemon. Don't embed a secret directly in the
command because the text can appear in shell history and process listings.

By default, `sbx` verifies the source when you register it and reports an error
without exposing the resolver's standard error. Use `--no-verify` to store a
source that can't be resolved during registration. To troubleshoot an initial
verification failure, use `--show-error`. Provider error output can contain
sensitive information. You can't combine `--show-error` with `--no-verify`.

Resolved service secrets are cached for 55 minutes by default. To change the
cache duration, use `--refresh <duration>`. To resolve the source for every
credential use instead of caching it, pass `--refresh on-demand`:

```console
$ sbx secret set anthropic \
--ref 'op://Work/Anthropic/credential' \
--refresh 30m
$ sbx secret set github --command 'gh auth token' --refresh on-demand
```

`--ref` and `--command` are mutually exclusive. They can't be combined with
`--token`, `--oauth`, or `--registry`.

### Import from environment variables

If you already have API keys set in your shell, `sbx secret import` reads them
Expand Down Expand Up @@ -201,14 +256,15 @@ $ sbx secret rm github
### GitHub token

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

```console
$ echo "$(gh auth token)" | sbx secret set github
$ sbx secret set github --command 'gh auth token'
```

This is useful for agents that create pull requests, open issues, or interact
with GitHub APIs on your behalf.
The daemon refreshes the token from the host command after the default cache
period. This is useful for agents that create pull requests, open issues, or
interact with GitHub APIs on your behalf.

### SSH agent

Expand Down Expand Up @@ -262,6 +318,21 @@ A `--host` value can also use wildcards, with the same syntax as
single label (`*.example.com` covers `api.example.com`) and `**` matches any
number (`**.example.com` covers `api.example.com` and `v2.api.example.com`).

Custom secrets also accept [dynamic secret sources](#use-a-dynamic-secret-source).
Replace `--value` with either `--ref` or `--command`:

```console
$ sbx secret set-custom \
--host api.example.com \
--env API_KEY \
--ref 'op://Work/Example/credential'
```

Dynamic custom secrets resolve on demand by default. Pass `--refresh` with a

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[MEDIUM] Custom secrets default to on-demand resolution, but service secrets default to 55-minute caching — no explanation given

Line 331 (added by this PR) states "Dynamic custom secrets resolve on demand by default." However, line 141 (existing) states "Resolved service secrets are cached for 55 minutes by default." These are different commands (sbx secret set-custom vs sbx secret set), so the difference may be intentional, but the PR does not explain why the two commands behave differently. A reader comparing the two sections will likely see a contradiction and be uncertain which behavior applies in their case.

If the difference is intentional (a design choice for sbx secret set-custom), add a brief note explaining why, for example: "Custom secrets are resolved on every use because each host may require a fresh value." If the defaults should match, align the text.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[MEDIUM] Custom secrets' on-demand default contrasts with service secrets' 55-minute cache default — asymmetry not called out

Service secrets cache for 55 minutes by default (documented a few sections above); custom secrets resolve on demand by default. Both defaults are correct in their own sections, but no cross-reference or explicit comparison is made. A user who reads only the custom secrets section won't know that the behavior differs from service secrets — they may be surprised by repeated slow lookups on every credential use, or assume the same 55-minute cache applies.

Consider adding a short note such as: "Unlike service secrets, which cache for 55 minutes by default, custom secrets resolve on demand on every use unless you pass --refresh <duration>."

duration to cache the resolved value. The verification and error-output flags
work the same as they do for service secrets. `--ref` and `--command` can't be
combined with `--value` or `--token`.

> [!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
Expand Down Expand Up @@ -453,9 +524,9 @@ $ sbx secret rm --sandbox my-sandbox --registry ghcr.io -f
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](../agents/)
for each agent's flow.
- If you store credentials in 1Password, see
- If you store credentials in 1Password or AWS Secrets Manager, see
[Sourcing credentials from 1Password](../workflows.md#sourcing-credentials-from-1password)
for how to use `op read` and `op run` with `sbx`.
and [Sourcing credentials from AWS Secrets Manager](../workflows.md#sourcing-credentials-from-aws-secrets-manager).

## Custom templates and placeholder values

Expand Down
65 changes: 30 additions & 35 deletions content/manuals/ai/sandboxes/workflows.md
Original file line number Diff line number Diff line change
Expand Up @@ -423,9 +423,10 @@ For troubleshooting, see

The sandbox proxy handles API credentials for model providers automatically,
but agents often also need credentials for tools like `gh`, `docker`, or a
secrets manager. The pattern is the same in each case: configure the
credential on your host once, and the sandbox either forwards it via the
proxy or via SSH agent forwarding.
secrets manager. Configure the credential source on your host, and the proxy
injects the resolved value into matching requests from the sandbox. Dynamic
secret sources can retrieve a value from an authenticated host CLI without
copying the value into the secret store.

> [!NOTE]
> Service secrets are global by default, so all future sandboxes can use them.
Expand All @@ -440,9 +441,14 @@ outbound requests, so `gh` works inside the sandbox without any additional
configuration:

```console
$ echo "$(gh auth token)" | sbx secret set github
$ sbx secret set github --command 'gh auth token'
```

The daemon runs `gh auth token` on the host and caches its output for 55 minutes
by default. After the cache expires, it runs the command again, so token updates
from `gh` don't need to be copied into `sbx` manually. Use `--refresh on-demand`
to run the command for every credential use.

The agent can then create pull requests, open issues, comment on PRs, and
interact with the GitHub API the same way it would from your host:

Expand Down Expand Up @@ -484,50 +490,39 @@ per-registry username requirements, and all-sandbox versus per-sandbox scoping,

### Sourcing credentials from 1Password

#### Populating stored secrets with `op read`

Use `op read` to populate stored secrets without pasting values manually. Store
the value once and it's available to all future sandboxes:
Install the 1Password CLI, sign in on the host, and pass an `op://` reference to
`sbx secret set`. The secret store records the reference, and the daemon uses
`op read` on the host when the proxy needs the credential:

```console
$ op read "op://Work/GitHub/token" | sbx secret set github
$ op read "op://Work/Anthropic/credential" | sbx secret set anthropic
$ sbx secret set github --ref 'op://Work/GitHub/token'
$ sbx secret set anthropic --ref 'op://Work/Anthropic/credential'
```

The real value stays on your host; the sandbox sees the proxy-managed
placeholder as usual.

#### Per-launch injection with `op run`

To resolve credentials fresh from your vault on each launch without storing
them via `sbx secret set`, use `op run`:
The real value stays on your host, and the sandbox sees the proxy-managed
placeholder. Service secrets are cached for 55 minutes by default. To retrieve
the value from 1Password for every credential use, set the refresh policy:

```console
$ ANTHROPIC_API_KEY="op://Work/Anthropic/credential" op run -- sbx run claude
$ OPENAI_API_KEY="op://Work/OpenAI/key" op run -- sbx run codex
$ GEMINI_API_KEY="op://Work/Google/key" op run -- sbx run gemini
$ sbx secret set anthropic \
--ref 'op://Work/Anthropic/credential' \
--refresh on-demand
```

`op run` resolves each `op://` reference in the environment before executing
`sbx`. The sandbox reads the
[built-in service environment variables](security/credentials.md#built-in-services)
at launch and routes them through its proxy — the credential is never stored in
sbx's state and never appears inside the sandbox container.
### Sourcing credentials from AWS Secrets Manager

This only applies to those specific credential variables. The sandbox does not
forward arbitrary environment variables from the host into the sandbox.

For multiple credentials at once, use `--env-file` with a file of `op://`
references:
Install and authenticate the AWS CLI on the host, then register the secret's
ARN. The daemon calls AWS Secrets Manager when the proxy needs the value:

```console
$ cat .sbx-secrets.env
ANTHROPIC_API_KEY=op://Work/Anthropic/credential
GITHUB_TOKEN=op://Work/GitHub/token

$ op run --env-file=.sbx-secrets.env -- sbx run claude
$ sbx secret set anthropic \
--ref 'arn:aws:secretsmanager:us-west-2:123456789012:secret:anthropic-api-key'
```

See [Use a dynamic secret source](security/credentials.md#use-a-dynamic-secret-source)
for refresh policies, verification options, custom secrets, and provider
account or profile selection.

## CI and headless use

For CI environments and scripts where a browser isn't available, authenticate
Expand Down