Secrets

A box needs credentials now and then: a token to clone a private repo, a write key for the log SDK, an API key for a service your tests call. Secrets are how you hand those to a box without putting them in a repo, a prompt or a tool call.

How they work

  • A secret is a name and a value stored in the control plane, per account. Names are environment variable names in capitals, digits and _, starting with a letter: PSBX_LOG_WRITE_KEY, GITHUB_TOKEN, STRIPE_TEST_KEY. Values are at most 64 KB.
  • Nothing reaches a box by default. sandbox_start takes secrets, a list of names; only those are injected, as environment variables of every sandbox_exec command, at the moment the box is claimed. sandbox_secrets with id and names injects more into a box that is already running.
  • A new box starts from a snapshot that holds no tenant data. It receives the tenant's settings and secrets only when claimed, and everything on it is destroyed when it stops.
  • Values never appear in tool results, in sandbox_status, in usage events or in platform logs. A command that prints one gets it back masked: sandbox_exec output and sandbox_procs log tails replace each secret value of 8 characters or more with **** (sandbox_exec has the rules). Files in the box are not changed, so a background command's log file, or a file you fetch with sandbox_get, holds the real value. sandbox_secrets returns names only.
  • A box holds no cloud credentials of its own, and it reaches your other boxes only through the links you declare. The one way it gets cloud access is an AWS role named on its environment, which hands it temporary credentials.

Create and manage

With the REST API, using a token: an API key, or an OAuth access token (where keys come from):

curl -fsS -X PUT https://api.parallelsandbox.com/v1/secrets/GITHUB_TOKEN \
  -H "Authorization: Bearer $PSBX_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "value": "ghp_..." }'

A PUT to an existing name replaces the value; boxes claimed afterwards get the new one. Delete with DELETE /v1/secrets/{name}; GET /v1/secrets lists the names.

A person can also add, replace and delete secrets at app.parallelsandbox.com under Settings → Secrets, which lists only names and when each was last updated and never shows a value. When the token is in the person's hands, send them there: a value typed in the app never passes through your conversation, so it never reaches the AI provider. Do not ask them to paste a value into the conversation.

The agent lists what exists:

sandbox_secrets {}

Use in a box

sandbox_start { "name": "private repo tests", "goal": "Run the private repo's test suite; done when it passes in the box", "services": [{ "name": "web", "port": 8080 }], "secrets": ["GITHUB_TOKEN", "PSBX_LOG_WRITE_KEY"] }
sandbox_exec  { "id": "<id>", "cmd": "git clone https://x-access-token:$GITHUB_TOKEN@github.com/your-org/private-repo.git && cd private-repo && git remote set-url origin https://github.com/your-org/private-repo.git", "note": "clone the private repo" }

A secret the task turns out to need later goes into the running box without a restart; commands from then on see it, while processes and containers already running keep the environment they started with:

sandbox_secrets { "id": "<id>", "names": ["STRIPE_TEST_KEY"] }

docker compose passes environment variables through with ${GITHUB_TOKEN} in the compose file or an environment: entry without a value; docker run with -e GITHUB_TOKEN. Do not bake secrets into images with ARG; images can be published as versions and pulled by other boxes.

Keys a build needs (a front end's VITE_GOOGLE_MAPS_API_KEY, NEXT_PUBLIC_*) go the same way: store the key as a secret named as the build expects, pass it in secrets (or inject it with sandbox_secrets), and run the build in the same sandbox_exec, which reads $VITE_GOOGLE_MAPS_API_KEY from its environment like any other variable. No .env file with the key is synced into /work and left behind. The built bundle still contains the value, as any front-end key does, so fetch only what you need from it. When the key is in the person's hands, ask them to add it under Settings → Secrets.

A name that does not exist fails sandbox_start (and sandbox_secrets with names) with secrets not found: NAME (this account has: …): the error lists the names the account has, so a misspelt or differently cased name shows at once; it never shows a value.

What to store

  • Short-lived tokens whenever the provider offers them: a GitHub App installation token, an STS session, a scoped deploy key. They expire on their own.
  • Public write keys such as the log SDK's psw_... can live in a page, but keeping them as secrets means the repo stays free of anything tenant-specific.
  • Never store a ParallelSandbox API key or token as a secret. The agent already has its own; a box does not need it.