Guides

Secrets, egress and blast radius

Guide

How do I give an AI agent API keys without exposing them?

Ask ChatGPT Ask Claude

Don't put the key where the agent runs. Store it outside the machine and have an egress proxy add it to the agent's requests on their way out. On Sanbox, that's a Secret Proxy: you save a credential once, grant it to a template, and the agent calls the API's normal HTTPS URL while the key never enters the Computer. The same idea works with any proxy you run yourself that can add headers to HTTPS requests and that the machine trusts.

Run this with your agent

Read https://sanbox.cloud/kb/security/api-keys-your-agent-cannot-see.md and follow it to give my agent a GitHub token it cannot read.
If the Sanbox CLI is missing, install it with `npm install -g @sanlabs/sanbox-cli@latest`
and sign in with `sanbox login`. Show me each command before you create, stop or delete a Computer.

How do agents find secrets?

An agent with a shell can read anything its user can, and it reads files and runs commands to debug. No malice is needed. "Check the config" is enough, and a prompt injection from a web page or an issue makes it deliberate. Keys turn up in:

  • .env files. Compose also reads a .env file in the project directory automatically.
  • docker compose config, which prints the resolved configuration with variables filled in.
  • Shell history. Bash saves commands to ~/.bash_history, including any export TOKEN=... you typed.
  • The environment. Every command the agent runs inherits exported variables, and /proc/<pid>/environ holds the environment a process started with.
  • Home-directory files, such as private keys in ~/.ssh and ~/.git-credentials, which git's store helper writes in plain text.
  • The transcript. Once a key appears in a command's output, it's in the model's context and on its way to the model provider.

Hiding one file doesn't solve this. If the key is on the machine, assume the agent can reach it.

How does a Secret Proxy work?

The Computer holds no key. Its outbound HTTPS traffic leaves through Sanbox's gateway, and when a request matches a proxy's host, paths and methods, the gateway adds the credential. The Computer trusts the gateway's certificate authority, so TLS verification still works.

Credentials are read only from stdin, encrypted at application level with AES-256, and never shown in management reads. Proxies also work inside Docker containers and builds.

Steps: give an agent a GitHub token it can't read

1. Create a narrow token

Create a fine-grained GitHub personal access token for one repository, with an expiry and only the permissions the work needs: for example, read access to contents and pull requests, and write access to issues. Save it to a private file outside any repository.

2. Describe the proxy

Save the settings, with no token in them, as proxy.json. These rules allow reads anywhere under the repository and new issues and comments:

{
  "name": "github-api",
  "host": "api.github.com",
  "authentication": { "type": "bearer" },
  "request_rules": [
    { "path_prefix": "/repos/acme/app", "methods": ["GET"] },
    { "path_prefix": "/repos/acme/app/issues", "methods": ["POST"] }
  ],
  "attach_run_id": false
}

3. Create the proxy

Creating proxies and grants needs the admin or owner role:

sanbox secret-proxies create --file proxy.json --secret-stdin --json < ~/.config/github-agent-token
sanbox secret-proxies get github-api --json

4. Grant it to a template

--add-network-access adds an HTTPS rule for api.github.com if the network policy doesn't have one:

sanbox templates secret-proxies grant <template-id> github-api --add-network-access --json
sanbox templates secret-proxies list <template-id> --json

5. Start or resume a Computer

Grants apply when a Computer starts or resumes. Start a new Computer, or stop and resume a running one:

sanbox run --template <template-id> --json
sanbox runs stop <run-id> --wait --json
sanbox runs resume <run-id> --wait --json
sanbox runs secret-proxies <run-id> --json

6. Call the API without a key

Inside the Computer, the agent uses the normal URL. The second command checks that no GitHub token reached the environment:

sanbox exec <run-id> -- curl --fail-with-body https://api.github.com/repos/acme/app/pulls
sanbox exec <run-id> -- 'env | grep -i github || echo "no GitHub variables in the environment"'

7. Rotate or revoke

A rotation applies from the next matching request, including in running Computers. Revoking a grant applies from each Computer's next start or resume:

sanbox secret-proxies rotate github-api --secret-stdin --json < ~/.config/github-agent-token-new
sanbox templates secret-proxies revoke <template-id> github-api --json

What are the rules?

  • HTTPS on port 443 only. Plain HTTP requests and requests to IP addresses never receive the credential.
  • The network policy must allow the host. A grant alone doesn't open it.
  • Unmatched requests pass through without the credential, keeping whatever authentication the caller sent.
  • Some clients bring their own trust store. Java's keystore needs /dev/sanbox/runtime-ca.pem added. Don't turn off TLS verification to make a client work.
  • Changes apply on the next start or resume, except rotation, which applies at once.
  • Errors carry headers. Intercepted responses include X-Sanbox-Request-ID, and gateway errors add X-Sanbox-Error-Code. A 401 or 403 can still come from the API itself.

What about git push?

This recipe covers GitHub's REST API at api.github.com. Pushing code over HTTPS goes to github.com and is a separate setup that this page doesn't cover. A safe default is to copy the repository in with sanbox cp, review the agent's diff and push it yourself.

What this doesn't do

  • It keeps the key off the machine; it doesn't prevent misuse. The agent can still do anything the token and the proxy's rules allow, and send data it can read to any host the network policy allows. Keep tokens narrow and paths tight.
  • It doesn't protect keys you put on the disk yourself. Files are copied as they are into forks and snapshots.
  • It keeps no request history. No per-request log is stored, so log on the API side if you need an audit trail.
  • The run ID header isn't authentication. If you turn on attach_run_id, the API must still authenticate the request before it trusts X-Sanbox-Run-ID.

Sources