SandboxHow Tangle keeps credentials out of the agent

Can the agent read the credentials it uses?

Not when you put the credential in proxy custody. The agent process holds a placeholder, and the Tangle egress proxy adds the real value only on HTTPS requests to the hosts, methods and paths you declared, then masks the value if a response tries to echo it back.

This matters because a coding agent runs code you did not write: a model can be prompted into printing its environment, and a dependency can read a file. If the credential is never in the sandbox, none of that reaches it. This page is the threat model and a measured test of what the agent can and cannot see. The mechanics of storing and selecting a secret are on Secrets, and the network policy is on Network access; this page explains why the two work together and what that protects you from.

Two places a credential can live

CustodyThe agent holdsUse it when
Box custodyThe real value, as an environment variable.A tool in the sandbox needs the raw value, such as a database URL or a signing key.
Proxy custodyA placeholder. The egress proxy swaps in the real value on the requests you named.The value is an API credential sent in a header or query, and code in the sandbox should never hold it.

Box custody is the right choice for some secrets, and it is honest about what it is: the value is in the sandbox, and the sandbox’s isolation is what protects it. Proxy custody removes the value from the sandbox entirely. The rest of this page is about proxy custody.

The egress proxy sits between the agent and the internet

Every sandbox reaches the network through an egress proxy that runs beside it, not inside it. The agent’s own user cannot read or reconfigure that proxy. Two jobs run there:

  • The egress policy decides which hosts the sandbox may reach: open, a strict allowlist, or blocked. A refused host gets an HTTP 403 with a named reason. See Network access.
  • Credential substitution holds each proxy-custody value and adds it to a matching request on its way out, then masks it on the way back.

Because both run in the same place, a credential’s reach is bounded by the policy and by its own binding at once. A value declared for one host on one path cannot be sent anywhere else, and the host it is declared for cannot be reached off that path while the value is bound to it.

What the agent can and cannot see

A proxy-custody binding names the hosts, methods and paths a value may go to, and how it is presented (bearer, basic auth, a named header, or a query parameter). Store it on the secret and select the secret by name:

await client.secrets.create("GIT_TOKEN", process.env.GIT_TOKEN, {
  proxySecrets: [
    {
      envName: "GIT_TOKEN",
      secretRef: { kind: "secret-ref", key: "GIT_TOKEN" },
      presentation: { type: "basic", username: "git" },
      hosts: ["git.acme.dev"],
      methods: ["GET", "POST"],
      paths: ["/acme/*"],
    },
  ],
});
const box = await client.create({ secrets: ["GIT_TOKEN"] });

Inside that sandbox:

  • $GIT_TOKEN holds a placeholder, not the value.
  • A request to git.acme.dev on a path under /acme/ carries the real credential, added by the proxy on its way out.
  • A request to git.acme.dev on any other path, a request with another method, or a request to any other host does not carry the value.
  • A response that echoes the credential back comes to the agent with the placeholder in its place.

A measured boundary test

On 2026-11-10 we ran this adversarially in a production sandbox. We stored a throwaway value in proxy custody, bound to one host, one method and one path, then from inside the agent (a non-root user) we tried to read it and to use it where it was not allowed. The value was a random string minted for the test; every check compared hashes, so the value itself was never printed. An internal boundary-test record holds the run.

The agent tried toResult
Read the credential from its environment variableA placeholder, not the value.
Search every readable process environment and the filesystem for the valueZero matches. The proxy’s control files are owned by root; the agent user is refused.
Call the allowed host on the allowed path with the credentialThe request succeeds. When that host echoes the request back, the response carries the placeholder, not the value.
Call the allowed host on a path outside the declared scopeRefused with HTTP 403, policy secrets.
Send the credential over plain http:// instead of https://The value is not added; only the placeholder leaves the sandbox.
Reach a host that is not on the allowlistRefused with HTTP 403 EGRESS_DOMAIN_NOT_ALLOWED, policy allowlist.
Reach a cloud metadata addressRefused.

The one thing to hold honestly: this test proves the agent cannot read the value. That the proxy adds the value to the allowed request on its way to the real host is, by design, not observable from inside the sandbox, because the response is masked. That half is covered by Tangle’s maintained end-to-end tests, and by the request on the allowed path returning a normal 200.

Limits worth knowing

  • A value a remote service re-encodes, for example into hex, cannot be recognized for masking. Name only hosts you trust to handle the credential, the same judgment you would apply to any API key.
  • A host is one exact public DNS name: no scheme, port, wildcard or IP address. Tangle’s own hosts are refused as targets.
  • A strict egress policy does not open a secret’s host for you. List it in allowDomains as well.
  • Rotation and lifetime belong to your own secret manager. update re-keys running sandboxes without a restart; see Secrets.

For the sandbox’s isolation boundary, confidential-hardware attestation and the published compliance scope, see Isolation, privacy and compliance and tangle.tools/security.

Next