An agent that moves money will eventually do something you need to explain. A duplicate payout, a vendor change that should have been caught, a refund issued to the wrong account. When that happens, the first question is always the same: what exactly did the agent do, in what order, and on whose authority? If your answer comes from a log that anyone with database access could have edited, you do not have an answer. You have a story.
This is the gap tamper-evident audit closes. The earlier modules in this course narrow what an agent is allowed to do: scoped credentials, mandates, gateways, human checkpoints. Audit is the layer that proves what it actually did, in a form that resists quiet revision. The threat we are defending against here is the untraceable autonomous action, an event that happened but left no trustworthy record.
Why ordinary logging is not enough
Most application logs are written to a table, a file, or a log aggregation service that operators can modify or delete. That is fine for debugging. It is not fine for accountability, because the people most motivated to alter a record are often the ones with write access to where it lives.
The distinction worth holding onto is between tamper-resistant and tamper-evident. Tamper-resistant means you tried to stop changes from happening. Tamper-evident means you can prove whether a change happened. You will never fully prevent a determined insider with infrastructure access from touching storage, so the realistic goal is to make any alteration detectable after the fact. That shift, from prevention to provable detection, is the whole design idea.
AWS made this concrete when it discontinued Quantum Ledger Database, with support ending July 31, 2025. Its recommended migration path, Aurora PostgreSQL, offers durable audit logging but, by AWS's own framing, does not include cryptographic verifiability. Teams that had leaned on a managed ledger for that property had to rebuild it, which is a useful reminder that verifiability is a property you design for, not a checkbox you buy.
The two primitives: hash chains and Merkle trees
A tamper-evident log rests on two well-understood structures.
Hash chaining
Each log entry includes the cryptographic hash of the entry before it. You compute an entry's hash over its own fields plus the previous hash:
entry_hash = SHA-256(timestamp || actor || action || resource || metadata || previous_hash)
Because every entry's hash depends on its predecessor, changing any past record invalidates every hash that follows it. A verifier who knows the latest hash can detect that the chain has been broken. This is cheap and sequential, which makes it a good fit for a single agent's action stream.
Merkle trees
A hash chain is linear, so verifying or proving anything about an 80-million-event log can mean walking the whole chain. Merkle trees solve the scale problem. Entries become leaves, parent nodes hash their children, and a single root hash commits to every entry beneath it. You can then prove that one specific entry is included with a compact path of hashes rather than the full log. The same 80-million-event log that would need roughly 800 MB as a linear chain can yield an inclusion proof of around 3 KB.
This is exactly the model behind Certificate Transparency, standardized as RFC 9162. CT logs publish a Signed Tree Head, the signed Merkle root plus tree size and timestamp, and support consistency proofs that let anyone verify the log only ever grew and was never rewritten. Borrow that architecture rather than inventing your own.
Anchoring: who you are trusting
A hash chain or Merkle root proves internal consistency, but it does not stop someone from rebuilding the entire structure from scratch with the history they prefer. To prevent that, you anchor the state somewhere hard to alter retrospectively.
Anchoring options, in rough order of strength, are: signing each root with a key held in an HSM so a rewrite requires the signing key; publishing roots to an external append-only service such as an RFC 3161 timestamping authority; or periodically committing roots to a public ledger. The point of anchoring is to move the root of trust outside the system the agent and its operators control. Pick the weakest anchor your threat model tolerates, because each step up adds operational cost.
A worked example
Consider an accounts-payable agent that schedules a $48,000 vendor payment. The action passes through your gateway and clears a human checkpoint, and at that moment the system writes one audit entry:
{ "seq": 91144, "ts": "2026-06-26T14:03:22Z", "actor": "agent:ap-runner@v2.3", "mandate_id": "mnd_7f21", "action": "payment.schedule", "resource": "vendor:acme-supply", "amount": "48000.00 USD", "approver": "human:dmurphy", "prev_hash": "a91c...e07b" }
The entry's hash folds in prev_hash, so it is now welded to entry 91143 and to everything before it. Every few minutes a batch job computes the Merkle root over recent entries and sends it to an RFC 3161 timestamping authority, which returns a signed token binding that root to a time the operator cannot backdate.
Six weeks later, finance asks why Acme was paid twice. You produce a 3 KB inclusion proof showing entry 91144 sits in the tree the timestamp authority signed on June 26, naming the mandate and the human approver. If someone had deleted the duplicate to hide the error, the consistency check against the anchored root would have failed and flagged the gap. The record either verifies cleanly or it visibly does not, and both outcomes are useful.
What to log, and what not to
Capture the decision, not just the outcome. The entry should carry the actor and version, the mandate or credential the action ran under, the tool called, the parameters, the approver if a human was in the loop, and the result. That chain of authority is what lets you reconstruct who permitted what.
Two cautions. First, keep sensitive payloads out of the hashed record where you can, since you cannot easily delete a hashed entry later without breaking the chain, which collides with data-retention and erasure obligations. Store a hash or reference to sensitive data and keep the data itself in a separately governed store. Second, an audit log only earns its keep if someone verifies it. A chain nobody checks is a chain nobody can trust, so run inclusion and consistency checks on a schedule and alarm on any verification failure.
Takeaway
Tamper-evident audit is the difference between asserting what your agent did and proving it. Chain your entries, commit them to a Merkle tree, anchor the root outside your own control, and verify on a schedule. Build it before the incident, because after the incident is exactly when an editable log is worth nothing.