Free playbooks in your inbox
Hands-on tutorials for people who want to build with AI.

The AI Agent Audit Log Lies. Here's How to Fix It

Your AI agent audit log says the human did it. Build distinct-identity attribution with an on-behalf-of trail so every agent action answers who really acted.

From the youcanbuildthings catalog ▸ Build-tested 8 min read

Summary:

  1. Why an agent running under your identity erases the one thing a log exists to record.
  2. The single field that turns a mystery into an answerable record.
  3. A structured audit event and two SQL queries a security team can actually run.
  4. Why this unglamorous chapter is the one that gets your agent shipped at work.

Your AI agent audit log lies, and it lies in the most expensive way possible: it tells you that you did something your agent did. The log says Tom ran a query at 3am. Did Tom run it? Tom was asleep. Did Tom’s agent run it, on Tom’s behalf, because a hostile email talked it into something? You can’t tell, and that “can’t tell” is the most expensive phrase in your whole security posture, because everything that matters about accountability and incident response rests on being able to answer it.

A developer put the whole problem in one comment: “The audit log lies. Once the agent acts as you, your logs say ‘Tom ran this query at 3am.’ Did Tom run it? Did his agent? You can’t tell. SOC 2, SOX, anything that cares about attribution will be broken by default.” This is the fix.

Why does your AI agent audit log lie?

Because the agent acts under your identity. You set it up under your account, with your credentials, because that was the fast way. Now it reads mail and files tickets and sends replies as you, and every one of those actions lands in the log wearing your name. From the log’s point of view, there is no agent. There is only Tom, who is apparently extremely productive at 3am.

The instant something goes wrong, this collapses. An invoice got forwarded to a stranger. The log says you did it. Did you? You have no idea, because the log can’t distinguish the times you acted from the times your agent acted in your name. You’ve lost the single most basic property of a security log, which is telling you who did the thing. This is also how agents quietly become shadow IT: capability your organization can’t see or account for, “happening through prompts, inference APIs, and agent chains instead of random SaaS tools.” Nobody approved the agent as a distinct actor because, as far as the logs know, it isn’t one.

Before-and-after agent audit log: the BEFORE line blames user=tom for a 3:14am send_mail to stranger@evil.example while Tom was asleep; the AFTER line records actor=inbox-agent nhi:agent-7f3, on_behalf_of=tom, trigger=email:msg-8821, making the event queryable by trigger_id so SOC 2 and SOX accountability are answerable

Before and after: one field changes everything

The whole fix is the difference between these two log lines:

# Before: attribution erased  (logged at 03:14, Tom was asleep)
user=tom  action=send_mail  to=stranger@evil.example  result=ok

# After: attribution restored
actor=inbox-agent (nhi:agent-7f3)  on_behalf_of=tom
  action=send_mail  to=stranger@evil.example  trigger=email:msg-8821  result=ok

Read the second line and you can answer every question the first one couldn’t:

QuestionBefore lineAfter line
Who acted?”Tom” (but Tom was asleep)The agent, identity agent-7f3
On whose authority?unknowableTom’s, who configured it (on_behalf_of)
What set it off?unknowablemessage msg-8821, your first lead
Can you investigate it?no, it’s a mysteryyes, query by trigger_id

The on_behalf_of trail doesn’t just assign blame; it gives you the thread to pull. When the 3am forward happens, you don’t start from “did Tom do this?”, you start from “the agent did this, triggered by message 8821,” and you’re already halfway to root cause. Attribution isn’t bureaucracy. It’s the difference between an incident you can investigate and a mystery you can’t.

What SOC 2 actually requires

Here’s where overclaiming gets a security writer mocked, so the honest version. People will tell you a SOC 2 control says your logs must be “attributable to an identifiable entity.” It doesn’t say that in those words. What the logical-access controls (the CC6.1 family) actually require is that the system can distinguish one user from the next, with no shared or duplicate accounts standing in for individuals.

Apply that honestly. A single identity used by both you and your agent, or one API key shared across fifty agents, plainly can’t distinguish one actor from the next. So the argument, and it’s an argument, not a certification ruling, is this: when an agent acts under a human’s identity, a control built on distinguishing actors is not being met in spirit. SOX traceability breaks the same way. Frame it as “broken in spirit,” never as black-letter law. It’s both more honest and harder to dismiss. In OWASP’s agentic taxonomy this lives at:

ASI03, Identity & Privilege Abuse: “Agents misuse credentials, tokens, or inherited permissions to access systems or data beyond intended limits.” A legitimate agent identity becomes silent privilege escalation, and the action is logged as the agent’s or the delegating user’s, obscuring the real actor.

Source: OWASP Top 10 for Agentic Applications (2026).

Build the attribution record

A log line is fine for reading; a structured event is what you query at 3am. Give the agent its own Non-Human Identity, a distinct service identity that belongs to it and nothing else, then emit every agent action in one schema:

{
  "event_id": "evt-0001",
  "timestamp": "2026-01-01T12:00:00Z",
  "actor_type": "agent",
  "actor_id": "nhi:agent-7f3",
  "actor_name": "inbox-agent",
  "on_behalf_of": "tom",
  "trigger_type": "email",
  "trigger_id": "msg-8821",
  "action": "send_mail",
  "target": "dana@acme.example",
  "risk_tier": 2,
  "result": "ok"
}

Every field earns its place. actor_type plus actor_id is the thing the shared-identity log threw away. on_behalf_of keeps the human in the picture without collapsing the two. trigger_id is the lead. And because it’s structured, the two questions a security team asks become two filters, not a forensic project:

-- Everything the agent did, separate from everything the human did directly:
select * from audit_events
where actor_type = 'agent'
order by timestamp desc;

-- Every action one hostile message set in motion (your incident timeline):
select * from audit_events
where trigger_id = 'msg-8821'
order by timestamp asc;

The second query turns an incident from a mystery into a timeline: you give it the message ID and it hands you, in order, every action that one email caused. That’s only possible because trigger_id was on every event from the start.

Protect the log, or the agent rewrites history

One trap undoes everything above. If the agent has write access to its own audit log, you don’t have an audit log, you have a document the attacker can edit. An agent compromised by injection and told to cover its tracks will happily delete the lines that incriminate it, and a tidy log is exactly what a careful attacker leaves behind.

So the log has to live somewhere the agent can’t reach to modify. Ship audit events to an append-only sink the agent’s identity can write to but not read or alter. This is the same least-privilege move applied to the log itself: the agent’s permissions include “append an audit event” and exclude “edit the audit log.” A log the agent can rewrite is worse than no log, because it gives you false confidence in a record the attacker controls.

What should you actually do?

This unglamorous layer is the one that gets your agent shipped at work, so treat it that way. Picture two security reviews. In the first, the reviewer asks “if this agent does something wrong, can you tell us it was the agent and not the user, and what triggered it?” You say “well, it runs under the user’s account, so…” and the review ends. In the second, same question, and you hand them one query: every agent action, distinct from every human action, each line stamped with the human it acted for and the message that triggered it. The reviewer’s next question is about rollout timeline.

  • If your agent shares your identity → mint it its own Non-Human Identity first. Everything else here depends on it, and it’s the same identity your scoped credentials should already be tied to.
  • If you’re stuck getting agents approved → build this layer and walk into the review with the two queries above. Gravitee found only 14.4% of organizations had their full agent fleet go live with security sign-off, and the attribution gap is a big part of why.
  • If you log to a file the agent can touch → move it to an append-only, tamper-evident sink today. An editable log is the one that fails you exactly when you need it.
  • For consulting → this is billable. With agent-specific work sitting at roughly 6% of security budgets (Arkose), the teams that can’t get past their own reviewers will pay for the artifact that unblocks them.

The bottom line

  • When the agent acts as you, you don’t have an audit log, you have a rumor. One field, on_behalf_of, is the difference between a record and a guess.
  • Don’t overclaim the compliance angle. “SOC 2 is broken in spirit for shared-identity agents” is defensible and persuasive; quoting a control as black-letter law in front of an auditor is how you lose the room.
  • A log the agent can rewrite is worse than none. Append-only or it isn’t evidence, it’s whatever the attacker decided to leave you.
Why trust this? Every youcanbuildthings guide is pulled from a build-tested book: code that ran in production before it was written down.

Frequently Asked Questions

Why does an AI agent audit log lose attribution?+

Because the agent runs under your identity. Every action it takes lands in the log wearing your name, so the log can't tell the times you acted from the times your agent acted for you. You fix it by giving the agent its own identity.

Do AI agents break SOC 2 compliance?+

An agent sharing a human's identity defeats the SOC 2 logical-access controls (the CC6.1 family) that require you to distinguish one actor from the next. It's broken in spirit, not by black-letter ruling. Distinct agent identities restore it.

What fields does an AI agent audit log need?+

At minimum: actor (the agent's own identity), on_behalf_of (the human it ran for), and trigger (what set the action off). Those three turn a mystery line into an answerable, queryable record.