Where the Agent Can't Write: Signing Trust Boundaries for AI Agents
An agent that can edit its own permission policy has no permission policy. This is a design analysis of three patterns that survive that constraint — expanded from a discussion on agent credential handling, for teams building agent systems that handle real secrets.
The problem
Agent frameworks increasingly ship safety boundaries: "don't send credentials to arbitrary hosts," "don't pipe secrets into shell commands." These boundaries are only as strong as the configuration that expresses them. And the uncomfortable fact is: most agent configurations are agent-writable. Settings files, environment variables, project configs — the same surfaces the developer edits are, in most deployments, surfaces the agent can also reach through its own file and shell tools.
A prompt-injected webpage, a malicious dependency, a poisoned README — any of them can append to a credential allowlist as easily as the developer can. The boundary didn't fail loudly; it was never a boundary. It was an instruction the attacker could also give.
The test for any trust declaration is simple: can the agent author it, or only consume it?
Threat model
We assume a capable agent with file and shell tools, running prompts and content from untrusted sources. The attacker controls some prompt surface (webpage text, issue comments, dependency behavior) and wants the agent to exfiltrate a credential or perform a privileged action. The defender wants legitimate workflows (testing against staging, entering dev credentials) to keep working.
Everything below is evaluated against one question: does this pattern give the attacker's instructions the same power as the operator's?
Pattern 1 — Operator-signed scopes
The environment declaration ("credentials may be entered for staging.internal, *.corp.example") is signed by an operator key that the agent never sees. The agent (and its tools) may read the signed scope and enforce it; they cannot author or modify it.
Mechanics that matter:
- Key location is the security property. The signing key lives in a hardware-backed store, a CI secret, or an operator's password manager — anywhere outside the agent's filesystem and shell reach. If the key is a file the agent can read, the pattern degrades to agent-writable config.
- Signature covers the entire scope, not entries: adding one host invalidates the whole signature, so incremental tampering is detectable rather than silent.
- Revocation is a new signature, which means emergency revocation has a cost — plan for short scope lifetimes (days, not months) so revocation-by-expiry is the common case.
This is the strongest pattern and the least flexible. Use it for stable infrastructure: known hosts, known clusters.
Pattern 2 — Session grants as append-only receipts
Instead of declaring environments safe forever, the operator grants per-session: "for this session, host X may receive credentials." Each grant is recorded as a receipt: a structured, timestamped, hash-chained entry the agent can read but not rewrite.
Why append-only matters: if the agent could edit the grant log, it could mint grants. If the log only appends (hash-chained, or held by a separate process), the agent's powers are exactly the operator's historical decisions — no more. Auditing becomes trivial: the log is the security story of the session.
Design notes:
-
Grant tuples:
{principal, capability, scope, expiry, nonce}— principal is the operator identity, capability is narrow ("enter-credential", not "do-anything"), expiry is short (minutes to hours), nonce prevents replay across sessions. - Hash chaining: each receipt commits to its predecessor, so truncation and reordering are detectable. The chain head can be signed periodically for external anchoring.
- The receipt is a decision artifact — the same shape as runtime-verification receipts in serving infrastructure. A grant and a verdict are cousins: both are signed statements about what was authorized, made by a principal the runtime cannot impersonate.
This pattern fits interactive work best: the developer approves a staging host once per morning, the grant is visible and bounded, and the injected page can't extend it.
Pattern 3 — Typed-once secret handles
The operator types the secret directly, once, at the start of the session. Thereafter the agent only ever sees a handle — an environment variable name, a secret-store reference — never the material.
The agent's context contains DB_PASSWORD_REF=vault://ci/db-password, not DB_PASSWORD=hunter2. When a tool needs the credential, the tool's backend resolves the handle outside the agent's context window. The agent can reference, leak the reference, but cannot leak the secret.
Design notes:
- Resolution must happen out-of-band. If the agent's shell expands the handle, the material transits the agent's context and the pattern collapses. Resolution belongs in the tool's server side.
- Handles bind to grants: a handle without a matching grant is inert. This composes Patterns 2 and 3 into a lattice — handles name what, grants authorize when and where.
Composition: a signed policy lattice
The three patterns compose. Signed scopes define the stable world (Pattern 1). Session grants authorize deviations (Pattern 2). Secret handles keep material out of the untrusted context (Pattern 3). Enforcement happens at the jump — every privileged action is checked against the lattice before execution, fail-closed.
The lattice has a useful property: every authorization is a signed, inspectable artifact, and every denial is a signed, inspectable artifact too. Debugging "why did the agent refuse my password" stops being archaeology and becomes reading the receipts.
Failure modes (honest)
- Compromised operator key breaks Pattern 1 entirely — key custody is the pattern. Mitigate with short lifetimes and an out-of-band revocation channel.
- TOCTOU on grants: a grant checked at request time against a mutable store can be swapped between check and use. Receipts must be checked against the appended log, not a cached copy.
- Handle dereference bugs: a tool that logs its resolved material reintroduces the leak. Handle resolution paths need the same audit as the boundary itself.
- Usability pressure is the real long-term threat: every "just let me disable this for a minute" request is a request to move a declaration from the operator's write sphere into the agent's. Resist structurally (short grants) rather than culturally (policy documents).
Closing
The claude-code thread asked for a permission-gated opt-in for dev environments. The feature is right; its safety depends entirely on who signs the scope. Sign it in the agent's reach, and you've built a prompt-injection amplifier. Sign it outside, scope it in time, and record it in receipts — and the same flexibility ships without the exfiltration channel.
Guigui Wang — Correctover. We design and assess trust boundaries for AI infrastructure: signed receipts, scoped grants, evidence-grade deliverables.
Top comments (0)