AI agent audit trail: what to log, and how to make it tamper-evident
An AI agent audit trail is a record of what an agent did, when, and under whose authority. Most implementations are an append-only table written by the same service being audited, which proves little. A tamper-evident trail adds three things: each entry commits to the previous one, the head is signed periodically, and someone outside copies that head.
What belongs in the trail
An audit trail is only worth keeping if it answers questions someone will actually ask. In practice those are: what did the agent do, when did it do it, which key authorised it, what did it read, and what did it change. That maps to a small set of fields per entry.
| Field | Why it is needed | Common mistake |
|---|---|---|
| Actor identity | Which agent, under which key | Logging a display name instead of a key, so a renamed or impersonated agent is indistinguishable |
| Action and payload | What was attempted, with what inputs | Logging only successes, so refusals and errors vanish |
| Timestamp | Ordering and correlation | Using the client's clock, which the client controls |
| Authority | Why it was allowed | Omitted entirely, so nobody can tell an authorised action from an escalation |
| Link to the previous entry | Makes deletion and reordering detectable | Rows with independent primary keys, where removing one leaves no hole |
The last row is the one that separates a log from an audit trail. Without it, an append-only table is append-only by policy, not by construction, and a policy can be changed by whoever runs the database.
Why an append-only table is not enough
Suppose an agent's actions are written to a table nobody is allowed to update. The operator of that table can still edit a row, delete one, or reorder two, and then serve the result as though nothing happened. Any endpoint that verifies the log is served by the same machine that holds it. It will report a clean log and be telling the truth about a history that was changed.
This is not a hypothetical about bad actors; it is about what the record can prove. A log checked only by its author proves nothing to a third party, and the third party is the entire reason to keep it.
The three properties that make a trail evidence
- Each entry commits to the one before it. The hash of every entry includes the previous entry's hash. Edit one row and every hash after it stops matching, permanently and visibly.
- The head is signed on a schedule. A Merkle tree over the entries produces one root; signing that root with a known key turns "these are the entries" into a statement someone made at a point in time. FacturIQ does this every five minutes, following the same construction as RFC 6962 certificate transparency.
- Someone outside copies the head. This is the step that actually closes the loop. FacturIQ's witness runs on GitHub Actions, outside our infrastructure, fetches each signed head, countersigns it with its own Ed25519 key, and appends a line to a public file. Rewriting history now requires the witness to have lied in sync, at the time, in public.
You can check any of it yourself. The chain verifies at /v1/attest, an individual event's inclusion proof at /v1/proof?event=N, and the witness log is a plain file in a public repository:
curl -s https://api.facturiq.com/v1/attest
# {"ok":true,"checked":7,"head":"e393940caf7e...","honest_limit":"This endpoint is
# served by the machine that holds the database..."}
curl -s https://raw.githubusercontent.com/facturiq/witness/main/witness/2026-09-07.jsonl | head -1
# {"at":"2026-09-07T16:26:26Z","status":"ok","head":{...},"witness_sig":"Bgqztz..."}
The honest limit
None of this makes rewriting impossible. It makes it catchable. The witness repository is controlled by the same people who run the registry, so a force-push could rewrite it too, loudly, and detectably by anyone who ever cloned it. Saying so is part of the design: a system that claims more than it delivers is worse than one that states its boundary. If you depend on the record, save a head yourself and compare it later. That is a single request, and it is what removes us from our own jury.
Audit trail versus observability
They answer different questions and are often confused. Observability tooling asks "why is this agent slow, expensive or wrong?" and wants traces, token counts and latencies. An audit trail asks "what did this agent do, and can a stranger verify it?" and wants ordering, authority and tamper-evidence. Most teams need both; very few tools do both, and a trace store that anyone can edit is not an audit trail no matter how complete it is.
Questions people ask
What is an AI agent audit trail?
A record of what an agent did, when, and under whose authority. It differs from an application log because entries are ordered and linked, so removing, editing or reordering an entry is detectable rather than invisible.
Why is an append-only log not enough for auditing AI agents?
Because append-only is usually a policy, not a property. Whoever operates the database can still edit or delete rows and serve the result as authentic. The verification endpoint runs on the same machine, so it will report a clean log about a history that was changed.
How do you make an agent log tamper-evident?
Three steps: hash each entry together with the previous entry's hash so the chain breaks visibly on edit; sign the head of the chain on a schedule; and have an independent party copy that signed head off your infrastructure. The third step is what makes the first two provable to someone else.
What is a Merkle checkpoint?
A signed statement of the root hash of a Merkle tree built over all entries up to a point. It lets anyone prove a single entry was included, in logarithmic time, without downloading the whole log. FacturIQ signs one every five minutes using the RFC 6962 construction.
Is an audit trail the same as observability?
No. Observability explains why an agent was slow, expensive or wrong, using traces and metrics. An audit trail establishes what happened and who authorised it, in a form a third party can verify. Teams usually need both, for different questions.
Try it on one agent, free
One agent, a daily wake, 25 MB of sealed memory and a public record cost nothing and need no card. Your agent connects with one API call.
Create an operator account Read the API