Skip to content
Runtime

How to use API keys in a sandbox without exposing them

Store the key with runtime secrets set NAME --host api.example.com. Sandboxes see a placeholder, and Runtime adds the value in transit.

On Runtime, the sandbox never holds the key. It is not in the environment, the memory or the disk. So a prompt injection or a stolen sandbox has nothing to leak. Each secret names the hosts it may go to. The proxy on the host adds the value to HTTPS requests to those hosts only. Code that sends the placeholder anywhere else sends a value that is worthless. One stored secret serves every sandbox in the account. An account can hold up to 50 secrets.

Store a secret

Terminalprintf %s "$OPENAI_API_KEY" | runtime secrets set OPENAI_API_KEY --host api.openai.comruntime secrets ls

The CLI reads the value from standard input only. So it never lands in your shell history, and it is not shown when typed at a terminal. ls shows names, hosts and placeholders, never values.

TypeScriptimport { Runtime } from "withruntime";const runtime = new Runtime();await runtime.secrets.set("OPENAI_API_KEY", {  value: process.env.OPENAI_API_KEY ?? "",  hosts: ["api.openai.com"],});
Pythonimport osfrom withruntime import Runtimeruntime = Runtime()runtime.secrets.set("OPENAI_API_KEY", value=os.environ.get("OPENAI_API_KEY", ""),                    hosts=["api.openai.com"])

An agent connected through MCP uses runtime_secrets with set, list and delete.

Use it from code in the sandbox

Nothing changes in the code. Every sandbox has an environment variable with the secret's name. It holds a placeholder such as rtsec_3f9c…. An SDK that reads OPENAI_API_KEY sends it as usual:

TypeScriptimport { Sandbox } from "withruntime";await using sbx = await Sandbox.create();const call = await sbx.exec(  'curl -sS https://api.openai.com/v1/models -H "Authorization: Bearer $OPENAI_API_KEY"',  { timeoutMs: 30_000 },);console.log(call.exitCode);const seen = await sbx.exec("printenv OPENAI_API_KEY");console.log(seen.stdout); // rtsec_…, the placeholder, never the key
Pythonfrom withruntime import Sandboxwith Sandbox.create() as sbx:    call = sbx.exec('curl -sS https://api.openai.com/v1/models '                    '-H "Authorization: Bearer $OPENAI_API_KEY"', timeout_ms=30_000)    print(call.exit_code)    print(sbx.exec("printenv OPENAI_API_KEY").stdout)  # the placeholder

Set the header for the code

With header, the proxy sets that header on every HTTPS request to the hosts. It replaces any header the sandbox sent. So the code needs no placeholder at all. format controls how the value is written into the header:

Terminalruntime secrets set GITHUB_TOKEN --host api.github.com --header Authorization --format 'token {value}' < token.txt
TypeScriptimport { Runtime } from "withruntime";const runtime = new Runtime();await runtime.secrets.set("GITHUB_TOKEN", {  value: process.env.GITHUB_TOKEN ?? "",  hosts: ["api.github.com", "*.githubusercontent.com"],  header: "Authorization",  format: "token {value}",});

A secret with header Authorization and format AWS4-HMAC-SHA256 {value} holds a bucket's ACCESS_KEY_ID:SECRET_ACCESS_KEY. The proxy re-signs S3 requests with the real key. So the secret key is never sent at all (mount your own bucket).

Limits

What Limit
Secrets per account 50
Value At most 8 KiB of visible ASCII
Hosts per secret 16, as names or *.domain
Where it is added The credential header, or header, to the secret's hosts
Never added The URL, other headers, plain HTTP, bodies, any other host
Protocol to its hosts HTTP/2 or HTTP/1.1, as client and host offer; WebSockets, gRPC
Replacing a value Keeps the placeholder; running sandboxes use it within seconds

Mistakes and how Runtime handles them

  • Passing the key in env or the command line. env values are never echoed back, and journals keep only a hash. But the sandbox then holds the key. A command line is recorded as sent. Use a secret instead.
  • Naming a host that echoes requests. A host that reflects headers back would show the value to the sandbox. Name only hosts you trust with it.
  • A program with pinned certificates. The proxy opens the HTTPS connection to the secret's hosts itself. It checks the real server's certificate. Python, Node, curl and git already trust Runtime's certificate through SSL_CERT_FILE, REQUESTS_CA_BUNDLE and NODE_EXTRA_CA_CERTS. A program with its own list of certificates needs /usr/local/share/ca-certificates/runtime-egress.crt added to that list.
  • Deleting to revoke. runtime secrets rm erases the value on Runtime. It does not revoke the key at its provider. Rotate it there too.
  • A database password. Only HTTPS requests are rewritten. So a Postgres password cannot be replaced this way. For your own cloud, an identity token avoids storing a key at all.

Lock it down further

A secret stops the key from leaking. Network rules stop the data from leaving. If you allow only the secret's host, the sandbox can call that API and nothing else. For no network at all, turn the internet off.

Facts on this page were checked on 26 October 2026.

100 free hoursin your first 30 days.

of a 1 GB machine

  • No credit card
  • Four sandboxes at once, 2 vCPU and 4 GiB each
  • Then prepaid credit from $10, no plan fee
Start free, no card
Start free, no card