Your agent ran last night. It made a call, sent a message, executed a transaction — something. You have a log entry. Now answer this: who authorized that action, with what scope, and can you prove the record hasn't been modified?
If the answer is "we logged it," you don't have an audit trail. You have logs. Those are different things, and the difference is about to matter more than most teams realize.
Here's what a typical "auditable" agent setup looks like:
result = agent.run(action="send_message", to=user_id, content=draft)
logger.info(f"Agent sent message to {user_id}: {result.id}")
The agent ran. Something was sent. The log records it happened — not whether it was allowed to.
This tells you what happened. It does not tell you:
The distinction between capable and authorized sounds like a compliance technicality until your agent sends 400 emails at 2am on a Sunday because a bug caused it to loop on a queue that wasn't supposed to be there. You have logs. They show exactly what happened. What you can't show is a chain of authorization that would have prevented it — or at minimum, detected it instantly.
Authorization means the action was explicitly permitted before it happened, against a defined policy. Capability means the code could do it. An agent that has database write credentials is capable of a lot of things it may not be authorized for. Most teams conflate these. The check is in the application code, not enforced at a boundary — which means as the codebase grows, the "authorization" drifts.
When an agent acts, who triggered it? Not the cron job. Not the service account. The human — the user who uploaded the document, clicked the button, or approved the workflow — whose intent is the origin of the action.
In simple systems this feels easy. In multi-agent systems — where a human triggers an orchestrator that spawns sub-agents that call external APIs — the initiator attribution has to travel through every link in the chain. Most implementations drop it somewhere in the middle. The audit log shows what the sub-agent did, but the human initiator is lost.
A log in a database is not a tamper-evident record. It's a mutable table. If someone with write access to that table modifies or deletes a row, you may never know. For most internal tooling, that's fine. For a system where agents are taking consequential actions on behalf of users — making calls, sending communications, executing financial operations — "trust us, we didn't modify it" is not an acceptable audit posture.
Tamper-evidence requires a mechanism that makes modification detectable. Hash-chaining is the standard approach: each log entry contains the cryptographic hash of the previous entry. Modifying any entry invalidates every subsequent hash in the chain. The tampering becomes visible without needing to compare against a separate source of truth.
The EU AI Act enforcement deadline for high-risk AI systems is August 2026. This is not a distant regulatory abstraction. It applies to autonomous AI systems operating in domains like employment, credit, healthcare, education, and critical infrastructure — a list that covers a lot of what people are building agents for.
Article 12 requires automatic logging for high-risk AI systems: logs sufficient to demonstrate compliance, including the period of operation and key decisions. Article 14 requires human oversight: operators must be able to understand, monitor, override, and halt the system.
"Ensure the traceability of the AI system's outputs" is the language in Article 12. Traceability is not the same as logging. It means being able to reconstruct, from the record, exactly why the system produced a given output — including the chain of authorization behind it.
Most teams building on top of LLM APIs are not thinking about this yet. The regulation is written for institutional risk managers, not developers, so the language doesn't map cleanly to how most agent systems are actually built. But "we have CloudWatch logs" is not going to survive a compliance review.
Four properties. Most implementations have at most one.
The authorization check must happen before the action, not after. Logging records what happened. An authorization gate decides whether it's allowed to happen at all. These are opposite ends of the same action — and only one of them can stop a rogue agent.
The pattern is: agent presents credential → policy evaluated → allow or deny → action proceeds or is blocked. The policy is centrally managed and version-controlled, not scattered across application code.
Every authorization record must include who triggered the chain — the original human initiator, not just the calling service. In delegation chains (human → agent → sub-agent), each link carries the initiator forward, and scope is subset at each step. A sub-agent cannot have broader permissions than the agent that spawned it.
This requires the initiator ID to be treated as a first-class field in the authorization protocol, not an afterthought passed through application state.
Hash-chain the audit log. Each entry:
{
"entry_id": "ae4f...",
"prev_hash": "sha256:c3b1...",
"agent_id": "agt_grace_v5",
"action": "call.initiate",
"initiator": "usr_7a2b",
"decision": "allow",
"timestamp": "2026-05-20T03:14:22Z",
"entry_hash": "sha256:7d9f..."
}
The entry_hash covers all fields including prev_hash.
Any modification to any earlier entry breaks the chain forward from that point.
You don't need a separate verification system — the chain is self-verifying.
You need to be able to suspend an agent and have that suspension take effect across all running instances, immediately. Not "restart the service." Not "revoke the API key and redeploy." Suspend the agent identity — and have every subsequent authorization request from that agent denied until you lift the suspension.
This is what "human oversight" means in practice. Not a dashboard that shows you what happened. A control that stops what's happening.
If you're implementing this from scratch, the core pieces are:
agt_xxx) with associated policies, separate from your application's user tableif statements in route handlersGetting all six right is non-trivial. Getting them right and keeping them correct as your agent system grows is ongoing infrastructure work. Most teams who set out to "add auditability later" end up with logging, because that's the path of least resistance and nobody external is checking yet.
For every agent running in production: if a regulator, a customer's legal team, or a breach investigator asked you to produce the authorization chain for a specific action taken by that agent six months ago — the exact human initiator, the scope at the time, the policy version in effect, and proof the record is unmodified — could you?
If the answer is no, or "probably," or "let me check with the team," that's the gap. The Aug 2026 EU AI Act deadline doesn't care about your roadmap. The audit requirement kicks in retroactively from when you started operating a high-risk system.
Start with the question and work backwards. Build the authorization gate first — before it matters, not after it does.
Agent registry, scoped credentials, policy authorization, and a hash-chained audit trail — as infrastructure. Register your agents and get a provable record on every action.
Get Early Access