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
| Custody | The agent holds | Use it when |
|---|---|---|
| Box custody | The 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 custody | A 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, astrictallowlist, orblocked. 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_TOKENholds a placeholder, not the value.- A request to
git.acme.devon a path under/acme/carries the real credential, added by the proxy on its way out. - A request to
git.acme.devon 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 to | Result |
|---|---|
| Read the credential from its environment variable | A placeholder, not the value. |
| Search every readable process environment and the filesystem for the value | Zero 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 credential | The 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 scope | Refused 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 allowlist | Refused with HTTP 403 EGRESS_DOMAIN_NOT_ALLOWED, policy allowlist. |
| Reach a cloud metadata address | Refused. |
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
allowDomainsas well. - Rotation and lifetime belong to your own secret manager.
updatere-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
- Secrets: store a secret, choose custody, and rotate it.
- Network access: the egress policy, denials and the activity record.
- Where a Tangle agent runs: one profile, three run targets.
- Glossary: custody, placeholder, grant, scope and the other terms, defined.