Where the AI credential lives decides the architecture

    When you hand a coding agent a job, you also hand it keys: a model API key, a token for your git host, cloud access, something for CI. Those keys are what an injected instruction is actually after — and the architecture decision that decides how expensive a compromise gets is where they live.

    By Piotr Zenon Żabrowski · Published · Updated

    Two terms carry the whole argument, so define them up front. The boundary is whatever isolates the agent's process: a system sandbox profile, a container, a VM. Egress is any traffic leaving that boundary — an HTTP request, a DNS lookup, an image URL the agent renders into a reply. The question this article answers is whether a credential ends up inside the boundary where the agent can read it, or outside it where the agent can only ask for it to be used.

    Isolation gets most of the attention, and it matters. But the credential decision is the one that holds when isolation fails, and isolation does fail: a kernel bug, a privilege mistake, one mount too many, or nothing more exotic than a config file the repository controls. If the agent never holds a usable secret, a compromised agent has nothing to send.

    • Whole harness inside the box: isolation is stronger, and the keys go in with the agent, so the boundary protects your machine and not the credential. A container that reads its own API key from the environment is exactly this shape.
    • Harness on the host, only tool execution crossing: the agent never holds the secret. A proxy or gateway outside the boundary swaps a placeholder for the real value on the way out, and decides which destinations are allowed to receive it.
    • The trap in both shapes: extensions, plugins and MCP servers run wherever the harness process runs. Route only the built-in tools into a sandbox and your extension's own file and network access stays on the host, with your credentials in reach.

    Substitution at egress, as actually implemented

    The second shape isn't theoretical any more; it's documented product behaviour. Anthropic's sandboxing docs describe giving sandboxed commands a per-session placeholder — they call the value a sentinel — while a proxy outside the sandbox substitutes the real value only on outbound requests to hosts the config explicitly allows. Two details in there are the interesting part. Masking requires the proxy to terminate TLS, because it has to rewrite the request on the way out. And mask entries are honoured only from user, managed or command-line settings, and ignored when they come from a repository's settings file — the same principle as the rest of this article: configuration that arrives with the code doesn't get to define credential handling.

    The container world arrived at the same answer from the other side. Docker Sandboxes runs one microVM per sandbox, states that credential values are never stored inside the VM, and injects auth headers from a host-side proxy with egress blocked unless a rule permits the destination. Cloudflare's sandbox model runs each sandbox in its own VM. E2B uses a Firecracker microVM per sandbox with a per-sandbox egress firewall. Modal's default sandboxes run on gVisor, with full VMs in beta. None of those architectures is mainly about keeping the agent out of your home directory. They're about making sure the agent has nothing worth stealing and no unrestricted route to send it.

    The same pattern shows up in tooling that predates coding agents: a gateway that holds provider keys and never hands them to the workload, opaque tokens that expire in minutes and get exchanged at the gateway for the real credential, or a workload that receives only a short-lived identity token and no secret at all. OWASP's agentic guidance turns that into an explicit control — short-lived, narrowly scoped tokens per task, and ephemeral access that expires immediately after use — which is least privilege applied to time as well as scope.

    The failure both shapes still miss

    In CVE-2026-21852, a repository-controlled environment variable pointed the CLI at an attacker's endpoint and the agent sent its own API key there in plaintext, before the trust prompt had been answered. A related issue covers code execution from repository configuration at start-up, before any trust decision. The key was never inside a container and never needed to be. The harness's own configuration was the exfiltration channel, on a machine that hadn't been asked yet whether it trusted the repository.

    That generalises past any single product. If the credential is readable by the process, then every channel the process can write to is a potential egress path: an HTTP request, a markdown image whose URL carries the value, a DNS query, a tool result echoed into a log or a chat transcript. The image channel isn't hypothetical either — CVE-2025-59145 exfiltrated private repositories through attacker-controlled image requests generated from invisible markdown. Which is why credential handling needs two halves: the process shouldn't hold the secret, and the set of destinations allowed to receive it should be an allow list rather than whatever the agent decides to contact.

    What the harnesses ship with, and what their docs say

    The major CLI agents converged on the same primitives instead of inventing their own, which tells you something about what's achievable on a developer machine — and each vendor documents its own limits instead of claiming a guarantee.

    Vendor sandboxes and their documented limits
    HarnessPrimitiveWhat the vendor says about its limits
    Claude CodeSeatbelt on macOS; bubblewrap plus a relay proxy on Linux and WSL2; sentinel credentials substituted by the proxySandboxing reduces risk but isn't a complete isolation boundary; the nested-sandbox and Apple Events options are called out as weakening it
    Codex CLISeatbelt on macOS, bubblewrap on Linux; workspace-write by default with network offA domain policy requires the proxy path; full-access mode removes the boundary entirely
    CursorSeatbelt on macOS, Landlock on Linux; network blocked by default with an explicit allowlist fileCommands that need wider access leave the sandbox with an approval step
    GitHub Copilot coding agentIntegrated firewall, on by default, with a recommended allowlistApplies to processes started by its Bash tool, not to MCP servers or setup steps; sophisticated attacks may bypass it and it isn't a comprehensive security solution

    Read that last column as a vendor describing its own mechanism honestly. None of them calls it an isolation guarantee — not because the mechanism is worthless, but because each answers a specific failure and the vendor knows which one it leaves open. The Copilot row is the clearest example: a firewall that covers the agent's shell but not the MCP servers the agent can start has a documented hole, and the documentation says so out loud.

    The test to apply

    Questions that decide the architecture

    • If an injected instruction read everything the agent can read, would it find a usable credential? If yes, the boundary protects the machine and not the access it grants.
    • Is the credential scoped to specific hosts, or can it be sent anywhere the agent chooses?
    • Does it expire on its own, and is the lifetime measured in minutes rather than months?
    • Can the repository's own configuration redirect the harness to another endpoint before a trust decision happens?
    • Do extensions, plugins and MCP servers run under the same restrictions as the harness — or have you accepted in writing that they don't?
    • Is there a leak path that isn't a web request: image URLs, DNS queries, logs, chat transcripts?
    • If you rotate the key today, how many places need updating, and how many of them can the agent write to?

    Sources

    Frequently asked questions

    If the harness already ships a sandbox, why add another layer?

    Vendor sandboxes are built to protect your machine from the agent's mistakes. They don't protect your credentials from the agent, and their own docs say so. A second layer answers the failure mode the first one declares out of scope.

    Is a masked credential in the environment actually safe?

    Only if the value in the environment is genuinely a placeholder. Masking means the sandboxed process sees a sentinel that only the proxy can exchange, and the proxy only exchanges it for destinations on an allow list. An environment variable holding the real value isn't masking, whatever the surrounding config is called.

    So should I stop letting agents run commands that need credentials?

    No. It means the credential should belong to the boundary, not to the process. Commands that need authenticated access keep working; they just stop being able to read, copy and send the secret themselves — which is the part that made the compromise expensive.