Environment variables
Use env to bake variables into a newly created sandbox:
{
"env": {
"API_HOST": "https://api.example.com",
"FEATURE_FLAG": "enabled"
}
}
Environment variables:
- are accepted only when creating a chat;
- are passed through the queued run and baked into sandbox creation;
- are not persisted on the chat or turn;
- remain available while that chat uses the sandbox.
Limits:
| Limit | Value |
|---|---|
| Variables | 64 |
| Key length | 256 bytes |
| Value length | 32 KiB |
| Total keys and values | 128 KiB |
Names must match:
[A-Za-z_][A-Za-z0-9_]*
Sending non-empty env while resuming a chat returns 422.
Secret environment variables
Use secret_env_vars for API keys the agent needs to call a specific service
but should never hold. The agent only ever sees a placeholder; the sandbox's
egress proxy swaps in the real value on requests to the hosts you list.
Each entry is an object with a name, a value, and a list of domains:
curl https://app.epho.io/api/v1/chat/async \
-H "Authorization: Bearer $EPHO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"prompt": "List the five most recent Stripe charges.",
"harness": "claude",
"model": "claude-sonnet-5",
"provider_api_key": "'"$ANTHROPIC_API_KEY"'",
"secret_env_vars": [
{
"name": "STRIPE_API_KEY",
"value": "'"$STRIPE_API_KEY"'",
"domains": ["api.stripe.com"]
}
]
}'
Inside the sandbox, the variable holds a per-run placeholder instead of the real key:
$ echo $STRIPE_API_KEY
usersecret-…
The agent uses it like a normal key. When a request to api.stripe.com goes
through the proxy, the placeholder is replaced with the real value wherever it
appears: in headers, the query string, or the body.
curl "https://api.stripe.com/v1/charges?limit=5" \
-H "Authorization: Bearer $STRIPE_API_KEY"
If the placeholder ends up anywhere else (a log, a file, a request to another host), only the placeholder leaks; the real value is never exposed to the agent.
| Field | Required | Description |
|---|---|---|
name | Yes | The environment variable the agent sees. Must match [A-Za-z_][A-Za-z0-9_]*, at most 256 bytes. |
value | Yes | The real secret. Injected by the proxy only; never exposed to the agent. |
domains | Yes | At least one hostname the real value may be sent to, such as api.stripe.com. |
Secret environment variables:
- are applied per turn, so send them on every request that needs them, including resumes with
chat_id; - may change from turn to turn;
- are not persisted on the chat or turn;
- are redacted from the runner's logs.
Write domains as exact hostnames: no scheme, path, port, or wildcards. List
every host the key is used with, for example both api.example.com and
uploads.example.com. The API does not reject values like
https://api.example.com, but they never match a request, so the key is
silently not injected. Requests to listed hosts must use the standard HTTPS or
HTTP ports.
The proxy can only swap the placeholder when it is sent as-is. A value that the client encodes or uses to compute something before sending (for example a Base64-encoded Basic auth header or a signed request) is not replaced, so prefer passing the key directly, such as in a Bearer header.
Limits:
| Limit | Value |
|---|---|
| Entries | 64 |
| Name length | 256 bytes |
| Domains per entry | At least 1 |
Size of the JSON-encoded secret_env_vars array | 32 KiB |
The API returns 422 when:
name,value, ordomainsis missing or not the right type;- a name is invalid, or an entry has no domains or an empty domain;
- there are more than 64 entries, or the encoded array is over 32 KiB;
- the chat runs on an agent runner version too old to support secret environment variables.
env or secret_env_vars?
env | secret_env_vars | |
|---|---|---|
| What the agent sees | The real value | A placeholder |
| When it can be set | First turn only | Every turn |
| Where the value can go | Anywhere | Only the listed hosts |
| Use for | Configuration, URLs, flags | API keys and tokens |
Stdio MCP servers
Define a local MCP process with command, optional args, and optional env:
{
"mcp_servers": [
{
"name": "linear",
"command": "npx",
"args": ["-y", "@tacticlaunch/mcp-linear"],
"env": {
"LINEAR_API_TOKEN": "lin_..."
}
}
]
}
Remote MCP servers
Define a remote server with url, optional transport type, and optional
headers:
{
"mcp_servers": [
{
"name": "figma",
"url": "https://mcp.figma.com/mcp",
"type": "http",
"headers": {
"Authorization": "Bearer ..."
}
}
]
}
Remote type may be http or sse. Omitting it defaults to HTTP behavior in
the runner.
MCP rules
- Every server needs a unique
name. - Each server must set exactly one of
commandorurl. bruinandagent-runnerare reserved names.- Stdio arguments and environment values must be strings.
- Remote header values must be strings.
MCP configuration is stored on the chat and inherited by every later turn.
Unlike top-level env, MCP env and headers are therefore durable chat
configuration. Do not resend mcp_servers with chat_id; the API returns
422.