When a parent agent hands work to a sub-agent, most systems trust the sub-agent because it authenticated. That is the bug. Authenticated is not authorized. We reproduced the delegation-security papers as a small, dependency-free capability layer, then measured it: the authenticate-only baseline lets every sub-agent attack through; bounded tokens block every one, and never block a legitimate delegated call.
What we did
There is a cluster of recent work saying the same thing. O'Reilly's "Who Authorized That?" and the arXiv work on authorization propagation both name delegation, not identity, as the security boundary in a multi-agent system. A sub-agent can prove who it is and still run an action no human ever granted. The papers list three hard sub-problems: transitive delegation, aggregation inference, and temporal validity.
We built the fix and the failure side by side. The fix is a macaroon-style
capability token that travels with the task. A holder can only
narrow it (add a caveat) and needs no secret to do so; it can
never widen, drop a caveat, reorder, or forge one, because the signature is
an HMAC chain running from a root secret through the exact ordered caveat
list. A guard then checks every tool call against the token the sub-agent
actually carries. Caveats cover the three sub-problems directly:
tool and path bound scope, max_reads
bounds aggregation, not_after bounds time.
Why it was worth doing
The whole thing is deterministic, so the numbers are exact, not sampled. Four scenarios run through one guard per delegated chain, against two controls: authenticate-only (identity equals authority) versus bounded.
- Confused deputy: a child scoped to read-only on
/data/reportstries to write, escape to a sibling directory, and use a tool it was never handed. Baseline 3/3 through, bounded 0/3. - Aggregation inference: a chain with a two-read budget tries five reads to reconstruct a restricted table. Baseline 3/3 over, bounded 0/3.
- Temporal replay: a token valid for sixty seconds is replayed an hour later. Baseline lets it, bounded rejects it.
- Widening forgery: a holder strips the read-only caveat to restore write. The signature no longer verifies.
Totals: attack success rate 100% to 0% across eight attacks, and the legitimate pass rate stays at 100%, so the fix adds zero false positives. Five extra token-level tests confirm the signature breaks on drop, reorder, edit, and forge, and verifies on honest narrowing, so the result rests on the token mechanics and not on how we framed the scenarios.
What's still off
This is first-party caveats only, so no third-party or discharge caveats yet. The read budget lives in one guard's process state, and the guard only mediates the tool calls it actually sees. It scopes delegated authority; it does not stop an agent that bypasses the guard entirely or breaks out of its sandbox. Scope is the delegation boundary, not the sandbox.
What's now in the stack
bounded_agents.py: the attenuable capability token plus the guard that checks every call.eval.py: the before/after harness that prints the attack and legitimate-pass rates.test_properties.py: five token property tests, all passing.- Sits alongside our swarm-safe skills (coordinate-don't-swarm, parallel-write-merge): those make sub-agent writes safe, this makes sub-agent authority safe.