Pass API tokens to a coding agent without leaking them
A sandboxed agent still has to authenticate to its provider API. h5i keeps the real key on the host and lets the box authenticate through a credential-injecting proxy, so a prompt-injected agent has no reusable token to read or exfiltrate.
When you run Claude Code or Codex inside an h5i env sandbox, the agent still needs a credential
to reach api.anthropic.com or api.openai.com. The obvious approaches all put the
token where the agent can read it: an environment variable shows up in /proc/self/environ, and a
credential file sits on disk in the box. A prompt-injected agent can read either one. On a tier without an
airtight network boundary it could even send the key somewhere.
The idea: keep the token on the host
h5i runs a small credential-injecting proxy on the host. The box is pointed at it with a base URL override and a per-run dummy token. The proxy strips the dummy, injects the real credential, and forwards the request to the provider over TLS. The genuine token never enters the box: it lives only in the proxy's memory on the host.
box ──http(dummy)──▶ 10.0.2.2:PORT (host loopback) h5i proxy: verify dummy → inject REAL key └──TLS──▶ https://api.anthropic.com (real key, host only)
Step 1: export the token on the host
Set the credential in the shell you launch h5i from. h5i reads it with the same precedence the runtime's own CLI uses.
# Claude: ANTHROPIC_AUTH_TOKEN, then ANTHROPIC_API_KEY, then CLAUDE_CODE_OAUTH_TOKEN $ export ANTHROPIC_API_KEY=sk-ant-... # Codex: OPENAI_API_KEY $ export OPENAI_API_KEY=sk-...
Step 2: create and enter a sandboxed env
Create an agent env on a tier that can enforce network egress. The credential proxy engages automatically when the profile is an agent runtime and a host token is present. No extra flag.
$ h5i env create review --isolation container env 'review' created (profile agent-claude, isolation container) $ h5i env shell review h5i:review$
Prefer the kernel-level tier when you want an airtight L3/L4 boundary rather than an HTTP-level one. The credential handling is the same.
$ h5i env create review --isolation supervised
Step 3: verify the token is not in the box
Inside the session, the base URL points at the host proxy and the only token present is the per-run dummy. Your real key is nowhere in the box.
h5i:review$ env | grep -i anthropic ANTHROPIC_BASE_URL=http://10.0.2.2:47213 ANTHROPIC_AUTH_TOKEN=h5i-proxy-9f3c1a... # a per-run dummy, not your key h5i:review$ printenv ANTHROPIC_API_KEY # the real key is absent # supervised tier: the stored credential file is scrubbed from the box copy too h5i:review$ cat ~/.claude/.credentials.json cat: /home/agent/.claude/.credentials.json: No such file or directory # and the agent still works — it authenticates through the proxy h5i:review$ claude -p "summarize the staged diff" The change adds a retry around the upload and a test for the timeout path…
What makes it secure
- Token never usable in the box. The box holds only a base URL and a per-run dummy. The genuine credential is resolved from the host environment and handed straight to the upstream request.
- No SSRF. The proxy forwards to one upstream host, pinned per runtime. It ignores the request's own
Hostand reuses only the path, so the box cannot aim the real key at an attacker host. The upstream address is DNS-pinned at startup. - Loopback plus a shared-secret gate. The proxy listens on host loopback and injects the real credential only for a request that presents the per-run dummy, so other processes on the host cannot borrow it.
- Egress locked to the proxy. On the supervised tier, nftables default-drops everything except the single proxy port. Direct calls to the provider and every other host port are blocked at L3/L4.
- Credential absent, not just inert. On the supervised tier the credential file is removed from the box's per-env HOME copy. Only the env's own copy is touched, never your real HOME.
Tier differences at a glance
container supervised real token in box never (no HOME mount) scrubbed from HOME copy egress boundary L7 CONNECT allowlist L3/L4 nftables, proxy-only box reaches proxy at 10.0.2.2 proxy at 10.0.2.2 (1 nft accept) host requirement rootless podman slirp4netns + nft
Check what a host can actually enforce before you rely on it:
$ h5i env probe # isolation tiers available on this host $ h5i env capabilities # the enforced tier, egress, and limits
Turning it off
Sometimes you want the box to use its own in-box login instead, for example to bill a subscription that is
logged in inside the box rather than a host-exported key. Set H5I_CREDENTIAL_PROXY=off and the
proxy stays out of the way.
$ H5I_CREDENTIAL_PROXY=off h5i env shell review
Frequently asked questions
Why not just export the API key inside the agent box?
An environment variable is readable by the agent and everything it spawns via /proc/self/environ. A prompt-injected agent could read the key and, on a tier without an airtight egress boundary, exfiltrate it. The credential-injecting proxy keeps the real key on the host so there is nothing reusable to steal.
How does the agent authenticate if the token is not in the box?
The box is pointed at a host-side proxy with a base URL override and a per-run dummy token. The proxy strips the dummy, injects the real credential, and forwards the request to the provider over TLS. The genuine token lives only in the proxy's memory on the host.
Which host environment variables does h5i use for the credential?
For Claude it checks ANTHROPIC_AUTH_TOKEN, then ANTHROPIC_API_KEY, then CLAUDE_CODE_OAUTH_TOKEN. For Codex it uses OPENAI_API_KEY. This matches each runtime's own precedence. Export the token on the host before creating or entering the env.
Can the agent point the proxy at a different host to steal the token?
No. The proxy forwards to a single upstream host pinned per runtime. It ignores the request's own Host header and reuses only the path, so it cannot be aimed at an attacker host. The upstream address is DNS-pinned once at startup.
Does this work on the kernel-level supervised tier too?
Yes. On the supervised tier the box's network egress is locked by nftables to the proxy alone, and the credential file is removed from the box's per-env HOME copy, so the token is absent from the box entirely. The container tier never mounts host HOME, so there is nothing to remove there.
How do I turn it off for a session that should log in inside the box?
Set H5I_CREDENTIAL_PROXY=off. The box then keeps its own in-box login, which is useful when you want to bill a subscription logged in inside the box rather than a host-exported API key.
Try h5i in your repo
One cargo install, then h5i init. Give each agent a sealed sandbox that authenticates without holding your keys.