The delegation-chain audit model
The problem
"Who authorized this tool call, and through which agents did the authority travel?" should be a query, not a log-correlation project across four services. That requires two things most audit logs don't guarantee together: that every decision is actually recorded, and that the recording can't be reordered relative to the decision it describes.
Committed before dispatch, not logged after
The PDP's allow/deny is written to the append-only
Audit Log and committed before the enforcement point is told
the outcome — not in request teardown, not asynchronously afterward. This closes a specific race:
if the commit fails, the call is treated as a deny (AUDIT_UNAVAILABLE, not recoverable) rather
than dispatched with a promise to log it later. A privileged action and its audit row can never
diverge, because the row exists before the action is allowed to happen.
The Token Exchange Service reinforces this from the other side: it mints a narrowed token only after re-checking that a committed allow exists for the decision it's citing — it runs in a separate request and can only ever see committed rows, so there's no window where a token is minted for a decision that turns out not to have been recorded.
Why the chain query is O(1), not a walk
Each hop's token carries the signed action_id of the allow that minted it. When the next hop's
decision is recorded, that row is written as parent.chain + [this hop] — the full chain is
assembled at write time, hop by hop, not reconstructed at query time. Answering "what's the
full delegation chain for this action" is then one primary-key read plus a join to the
originating consent, at any depth. See
Delegation-Chain Query API for the exact query.
A link that can't be established is recorded as a break, not silently dropped — a chain either
reports complete: true or names exactly where it broke and why (an untraceable parent, a
parent whose own chain was already broken, a pre-audit legacy token). Completeness is a property
you can query, not an assumption.
The same rows answer a second question: did we drift
The rule that decides whether an edge is allowed (services/edge_rules.py) is the same rule the
CI Drift Action runs offline against a recorded test trace —
not a reimplementation that could disagree with the runtime. Drift detection and delegation
audit are two reads of one append-only source, which is why "drift caught in CI vs. only in
production" is a meaningful, comparable metric rather than two different notions of correctness.