# Environment and secrets

> Give every command in a sandbox environment variables, and API keys it can use but never read.

A sandbox keeps a set of environment variables and secrets. Every command started with [exec](https://nanite.run/docs/exec.md) gets them; `env` on an exec request is set on top. Changes apply to commands started after the change, and none of these routes wake the sandbox.

## Environment variables

### List

```http
GET /v1/sandboxes/{name}/env
```

```json
{
  "env": [{ "name": "NODE_ENV", "value": "production", "updated_at": "2026-10-31T09:20:00.000Z" }]
}
```

### Set

```http
PUT /v1/sandboxes/{name}/env/{NAME}
```

```bash
curl -X PUT $API/agent-1/env/NODE_ENV -H "$AUTH" -d '{"value": "production"}'
```

Answers `{ "name", "value", "updated_at" }`. Setting a name that exists replaces its value.

### Delete

```http
DELETE /v1/sandboxes/{name}/env/{NAME}
```

Answers `204`, or `404 not_found` if there is no such variable.

## Secrets

A secret lets an agent call an outside API without ever holding the key. Inside the sandbox, the variable holds a stand-in. When a request leaves the sandbox for one of the hosts of the secret, the stand-in is swapped for the real value on the way out. Sent anywhere else, the stand-in is worthless.

```bash
curl -X PUT $API/agent-1/secrets/OPENAI_API_KEY -H "$AUTH" \
  -d '{"value": "sk-...", "hosts": ["api.openai.com"]}'
```

Inside the sandbox, use it as you would the real key:

```bash
curl https://api.openai.com/v1/models -H "Authorization: Bearer $OPENAI_API_KEY"
```

The swap happens in the request line and the headers of requests to the listed hosts. Request bodies are sent as they are, so put keys in headers or the URL.

The sandbox trusts the certificate that makes this work through the system store. Tools that ship their own certificate list need pointing at it, for example Node.js:

```bash
export NODE_EXTRA_CA_CERTS=/etc/ssl/certs/ca-certificates.crt
```

### List

```http
GET /v1/sandboxes/{name}/secrets
```

```json
{
  "secrets": [{ "name": "OPENAI_API_KEY", "hosts": ["api.openai.com"], "updated_at": "2026-10-31T09:20:00.000Z" }]
}
```

Secret values are never sent back, by any route.

### Set

```http
PUT /v1/sandboxes/{name}/secrets/{NAME}
```

| Field   | About                                                                                      |
| ------- | ------------------------------------------------------------------------------------------ |
| `value` | The real value.                                                                            |
| `hosts` | 1 to 20 hosts the value may be sent to: a domain, or `*.` and a domain for its subdomains. |

Answers `{ "name", "hosts", "updated_at" }`.

### Delete

```http
DELETE /v1/sandboxes/{name}/secrets/{NAME}
```

Answers `204`, or `404 not_found`.

## At create

Both can be given when the sandbox is created, so the first command already has them:

```json
{
  "name": "agent-1",
  "env": { "NODE_ENV": "production" },
  "secrets": { "OPENAI_API_KEY": { "value": "sk-...", "hosts": ["api.openai.com"] } }
}
```

## Rules

- A name is 1 to 128 capital letters, digits and underscores, not starting with a digit.
- A value is a string of up to 32 KiB, with no NUL bytes.
- A sandbox holds up to 100 variables and 50 secrets, at most 256 KiB together.
- Hosts are matched without case.

Breaking any rule answers `400 invalid_argument` with a message that says which.

## Trust

A program in the sandbox can send a secret to its hosts, and so can make those hosts repeat it back. Bind a secret only to hosts you trust with it, and use keys scoped to what the agent needs.
