Skip to Content
AI agents & assistants — machine-readable index at /llms.txt
Engineering BlogAI Agent Audit Trail: What to Hash, What to Anchor, What to Bind to the Frame

AI Agent Audit Trail: What to Hash, What to Anchor, What to Bind to the Frame

Date: September 6, 2026 · Author: Dmitrii Zatona 


TL;DR

  • An AI agent audit trail records, per step, what the model received, what it produced, which tool it called and what came back. The record splits into a payload that enters the log only as a hash and metadata that travels with the receipt, readable unless a field was encrypted first (Section 2).
  • “Tamper-evident” is relative to an adversary: a hash chain stops an outsider, a Merkle tree with distributed signed heads stops the operator after publication, an external anchor bounds time (Section 3; mechanics in the companion article).
  • Replay can corroborate a model output but cannot establish what the step produced at the time, so the record carries its frame: model version, prompt-template hash, sampling parameters, tool versions (Section 4).
  • The note to FRE 902(14) names hash values; EU AI Act Article 12 requires automatic logging and names no integrity mechanism (Sections 5 and 6).
  • The trail does not prove that inputs were true, that every step was logged, or when a step happened before it was anchored (Section 8).

An AI agent audit trail is the record an LLM-driven agent leaves behind, one entry per step: what the model was given, what it produced, which tool it called with which arguments, and whether the entry in front of the reader is the entry that was written at the time. It is read backwards: the reader who matters arrives weeks or months later, was not there, and wants an answer to those four questions about one step. The first three questions are about what to log. The last one is about what makes the log tamper-evident, and it is the one a database row with a timestamp column does not answer.

This article takes the trail apart by mechanism: what one agent step’s record contains, which adversary each integrity mechanism stops, what the record has to carry for either to mean anything, and what the US evidence rules, FRE 901 and 902(14) among them, and EU AI Act Article 12 ask of such a record. Where the text names a specification, it is the ATL protocol for existence and history and the APL protocol for the frame; both are specifications I author, and the reasoning below does not depend on either.

1. What gets contested

Five questions arrive after the fact, and each maps to a different property of the record.

What did the model receive? The system prompt, the user turn, the retrieved context, the prior tool results. A hash of the assembled input, not a summary of it.

What did it produce? The raw output bytes, before post-processing, before the application parsed them into an action.

What did it call? Tool name, tool version, arguments as sent, result as received. Where the application acts on the model’s output, through a modeled tool call or directly, that action is what changes state outside the process, and the record has to capture the action as sent and the result as received.

Was anything removed, inserted, or reordered afterward? This is not a question about one record. It is a question about the sequence, and it is the one a per-row hash cannot answer.

When did it run, and under what conditions? Local clocks are assertions made by the same system that wrote the record. Model identifier, prompt template version, sampling parameters and tool versions are the conditions under which the output means anything at all.

The first three are answered by choosing what to hash. The fourth needs an integrity mechanism. The fifth needs two bindings at once: time to something outside the operator, and the record to its frame.

2. The record of one step

One step of an agent produces one entry: the record of that step, as the log stores it. The entry has two parts that are hashed separately and combined: a payload and metadata.

PartContentsWhere it lives
PayloadRaw bytes of the step: the assembled input, the model output, the tool result. One payload per step, or one per component if they must be disclosable separately.Private storage, under the operator’s retention and access rules. Only its hash enters the log.
MetadataStep index; hash of the previous step’s entry; model identifier and version; hash of the prompt template; sampling parameters; tool name, version and hash of the arguments; local timestamp; hash of the frame the step ran under (Section 4).In the entry, and therefore in any receipt handed to a third party; readable unless a field was encrypted before hashing.

Two rules follow from splitting the record this way.

First, metadata must be canonicalized before it is hashed, or two honest parties will compute two different hashes from the same JSON. RFC 8785, the JSON Canonicalization Scheme, exists so that data to be hashed or signed has one invariant serialization (RFC 8785 , abstract). Its number serialization is defined by reference to ECMAScript, and floating-point sampling parameters such as temperature: 0.7 are exactly the kind of value that serializes differently across languages unless a canonicalizer is applied. Duplicate property names are a second trap: a canonicalizer that silently keeps the last one will hash a document the verifier never sees.

Second, anything placed in metadata in cleartext is disclosed to whoever holds a receipt. The ATL draft states the rule as an operator obligation: log operators “MUST ensure that sensitive PII is not included in the metadata object” unless it is meant for every receipt holder, and it recommends encrypted fields, or an off-log hash, for metadata that must stay confidential (draft-zatona-atl-protocol-01 , Security Considerations). For an agent trail the practical consequence is that user content goes into the payload, and the metadata carries hashes and identifiers. The trail then proves that the operator committed, no later than the anchor time, to a record stating that a given user turn reached a named model under a named template, and it discloses the turn itself only when the payload is produced.

The pointer to the previous step’s entry hash is what makes a sequence of entries a chain. What that chain does and does not guarantee is the subject of Section 3.

3. Three tamper-evident mechanisms and three adversaries

“Tamper-evident” is not one property of an audit log; it is a property relative to an adversary. Three adversaries are worth naming: an outsider without write access; the operator of the log, after commitments have left their hands; and the operator of the log, before anyone else has seen anything.

Three integrity levels: hash chain, Merkle tree with signed head, external anchor, and the adversary each holds off

The mechanisms themselves, their constructions, the proof algorithms, working code and the operating decisions around them, are the subject of the companion article, Tamper-Evident Audit Logs. This section keeps only what the agent record needs from them.

Hash chain. Each record carries the hash of the previous one, so a changed record breaks every pointer after it, and anyone holding an unaltered later record can see the break. The party that holds the chain can regenerate the tail, pointers and all. A chain whose head never leaves the operator’s database is tamper-evident against an outsider and against an accident, not against the operator. For an agent trail the operator is the application team, and the question a reviewer asks is not whether the team is honest but whether the record could show it if they were not.

Merkle tree with a signed head. The entries become leaves of a Merkle tree; the operator periodically signs the root together with the tree size; and two proofs follow, an inclusion proof for one entry and a consistency proof that a later tree extends an earlier one. Once a signed head is in a third party’s hands, the operator cannot pass off a different history as its continuation without the consistency proof failing; until a proof verifies, extension is unproven, and the holder of the earlier head can demand it. What the tree does not give is time, anything before the first head was distributed, and one view for everyone: an operator can show different heads to different parties, which is what witnesses and gossip between verifiers exist to catch. Agent trails have the shape this structure was built for: many entries, one operator, external parties who want to check one entry without downloading everything. The same structure applied to records in a data pipeline rather than to agent steps is the subject of an earlier teardown of a federal lineage requirement.

External anchor. A signed head, or the root it commits to, is submitted to a party the operator does not control: an RFC 3161 time-stamp authority, which signs a hash and a time and is trusted through its key, or the Bitcoin chain through OpenTimestamps, which fixes the hash before a block and is trusted through block headers the verifier obtains. Either bounds when the head existed. Neither says anything about content, and neither covers heads that were never anchored. In ATL an RFC 3161 token anchors a Data Tree root and a Bitcoin proof anchors the root of the Super-Tree that aggregates all Data Tree roots; without a verified anchor a receipt proves internal consistency only.

3.1 Hash chain, Merkle tree, anchor: what each one proves

MechanismProvesDoes not proveAdversary held off
Hash chainA single record was not altered in isolation, to anyone holding an unaltered later recordThat the tail was not regenerated by whoever holds the chainOutsider without write access
Merkle tree, signed headInclusion of a specific entry under a signed head; append-only history relative to an earlier head, when a consistency proof between the two verifiesTime; anything before the first head was distributed; that all parties saw the same headThe operator, after publication
External anchorAn anchored head existed no later than the TSA’s time, or before a given Bitcoin blockAnything about the content the head commits to; anything about heads that were never anchoredThe operator, for heads already anchored: rewriting or backdating them; a forged local timestamp

The three stack. A chain inside a tree inside an anchor is what a receipt for one agent step should be able to carry, and each layer is checked by a different party, under its own trust assumption.

4. The frame

Suppose the record is intact. Step 17 says the model produced a refusal, with a hash that matches the payload and an anchored head that bounds the time. The next thing a reviewer will try is to run the step again and compare.

That comparison will not settle the question, and the model vendors say so in their own API references. OpenAI’s documentation for the seed parameter states that “determinism is not guaranteed, and you should refer to the system_fingerprint response parameter” (OpenAI API reference ). Anthropic’s Messages API documentation says that “even with temperature of 0.0, the results will not be fully deterministic” (Anthropic API reference ). Replay can corroborate; it cannot establish what the step produced at the time. The record has to carry, at the time of writing, everything that gave the output its meaning.

That set of conditions is the frame: the model identifier and version, the prompt template (as a hash of the template, not its name), the sampling parameters, the tool versions, and, for evaluation-style claims, the benchmark and split. A trail that records “accuracy 0.78” without the frame records a number, not an observation, and two such numbers taken under different conditions cannot be compared without stating what changed between them.

APL formalizes this as a structural requirement rather than a documentation habit. A claim carries a frame_ref, and “a claim without a pinned frame is not an APL claim” (APL Protocol 1.0 , Design Principles). Frames are content-addressed: the identifier is the SHA-256 of the frame’s JCS-canonicalized bytes, and a verifier recomputes that hash from the resolved frame and compares it to frame_ref.hash by exact byte equality. The verifier’s output is closed, apl-valid or apl-invalid, and the specification is explicit that apl-valid “does NOT mean” the claim is true or that it is comparable to any other claim. Under APL’s rules, claims from different frames are comparable only through a bridge, a separate content-addressed artifact that declares its assumptions and losses and may not assert general equivalence of the two frames. APL’s AI-evaluation profile, the vocabulary for one class of claim, allows a frame’s procedure to carry prompt_protocol, sampling, temperature and max_tokens.

Two gaps. APL’s one published profile, AI-Eval, describes benchmark observations about a model build; there is no published vocabulary for agent traces, tool calls, or per-step records as frame contents, so an agent trail using APL defines that vocabulary in its own profile. And the frame identifies its dataset by name and split, not by a digest of the data. If the dataset can change under the same name, the digest belongs in the frame too.

5. What FRE 901, 902(14) and FRCP 37(e) ask of the record

This section reports what the rules say. It draws no conclusion about admissibility in any case.

Federal Rule of Evidence 901(a) sets the authentication standard: the proponent “must produce evidence sufficient to support a finding” that the item is what it is claimed to be (FRE 901 ). Rule 901(b)(9) lists, as one way to satisfy it, evidence that describes a process or system and shows that it produces an accurate result.

The 2017 amendments added two self-authentication routes for electronic records and for copies of electronic data. Rule 902(13) covers a record generated by an electronic process or system that produces an accurate result, shown by a certification of a qualified person. Rule 902(14) covers data copied from an electronic device, storage medium or file, if “authenticated by a process of digital identification” (FRE 902 ). Both require advance notice under Rule 902(11). The Advisory Committee’s 2017 note to 902(14) names the usual process: hash value, with matching hash values on original and copy attesting that they are exact duplicates. The note also keeps the door open to processes other than hash comparison.

Ten years before the amendment, the opinion in Lorraine v. Markel American Insurance Co., 241 F.R.D. 534 (D. Md. 2007), had already treated hash values as an authentication method under Rule 901(b)(4) and observed that “metadata is a distinctive characteristic of all electronic evidence” that can be used to authenticate it (slip opinion ).

On the preservation side, Federal Rule of Civil Procedure 37(e) applies when electronically stored information that should have been preserved in anticipation of litigation is lost because a party failed to take reasonable steps, and cannot be restored or replaced (FRCP 37 ). The rule scales its measures: curative measures on a finding of prejudice, and the measures of 37(e)(2), including an adverse presumption or dismissal, “only upon finding that the party acted with the intent to deprive another party” of the information’s use in the litigation.

Read against Section 3, the vocabulary lines up, and that is all this paragraph claims. The note to 902(14) speaks of hash values; the record in Section 2 is built from hash values. Rules 901(b)(9) and 902(13) speak of a process or system, described, that produces an accurate result; Section 3 describes mechanisms in those terms and makes no claim about the showing the rules require. An external anchor is a statement by a party other than the producer about when a head existed, and a consistency proof from an earlier distributed head is a statement about whether anything was lost or altered since. Whether any of this satisfies a rule in a given case is decided in that case, not here.

6. EU AI Act Article 12 and NIST AI RMF: what they say and do not say

The EU AI Act, Regulation (EU) 2024/1689, requires that high-risk AI systems “shall technically allow for the automatic recording of events (logs)” over the system’s lifetime (Article 12(1) ). The stated purpose is a level of traceability appropriate to the system’s intended purpose, with events recorded that are relevant for identifying risk situations, post-market monitoring and operational monitoring (Article 12(2)). The one place the Act enumerates minimum log contents is Article 12(3), and it applies only to remote biometric identification systems: period of each use, the reference database, the input data that led to a match, and the identification of the natural persons who verified the results. Providers keep logs under their control for at least six months (Article 19(1)); deployers keep them for the same minimum (Article 26(6)); both periods are subject to other Union or national law, including data-protection law. As amended by Regulation (EU) 2026/1744, the Section 2 and 3 obligations, which contain Articles 12, 19 and 26, apply from 2 December 2027 for Annex III systems and from 2 August 2028 for Annex I systems (Article 113(c)); the amendment left the text of Articles 12, 19 and 26 unchanged.

What the Act does not say matters as much for this article. A full-text search of the text as published on 12 July 2024 finds no occurrence of “tamper” or “unaltered,” and each of the ten occurrences of “integrity” concerns something other than logs, for example data-set integrity, physical infrastructure, the integrity of proceedings, the integrity of notified bodies. Articles 12, 19 and 26(6) contain no integrity, authenticity or tamper-evidence language. The regulation specifies that events must be recorded and for how long; the mechanism that makes the records trustworthy is not specified in these articles.

The NIST AI Risk Management Framework (NIST AI 100-1 , January 2023) describes itself as voluntary, and its subcategory tables do not use the words “log,” “logging,” “audit,” “provenance” or “traceability” at all. The closest entries are MEASURE 2.4, under which system functionality and behavior are monitored in production, and MANAGE 4.3: “processes for tracking, responding to, and recovering from incidents and errors are followed and documented.” The Generative AI Profile (NIST AI 600-1 , July 2024) comes closer in its suggested actions: GV-6.1-008 asks organizations to “maintain records of changes to content made by third parties” with sources, timestamps and metadata, and MG-2.2-007 to use tools that track the lineage and authenticity of AI-generated data. The two actions name timestamps and metadata; the profile’s integrity language, a tamper-proof history in MS-2.8-003 and cryptographic signatures in MS-2.7-005, concerns generated content, not the execution record.

Two standards are in progress. CEN-CENELEC’s JTC 21 lists “AI trustworthiness framework – Part 1: Logging (prEN 18229-1)” and “Logging (prEN ISO/IEC 24970)” on its work programme (JTC 21 working groups ); the second is the European adoption track of ISO/IEC 24970, whose ISO catalogue entry was not accessible for this analysis. Neither text was available; whether either specifies an integrity mechanism is not something this article can report.

Across the AI Act and the two NIST documents the verbs are record, retain, trace; none of the three specifies a hash, an anchor or a consistency proof. The mechanism is a design decision that these texts do not make.

7. A reference assembly

The pieces above assemble into a receipt per step. I describe the assembly using ATL’s draft and its reference verification library, atl-core (test suite at a pinned revision); any log with the same three properties would do.

The leaf hash of an entry is SHA256(0x00 || PayloadHash || MetadataHash), with the metadata hashed as SHA-256 of its JCS canonical form. Interior nodes take the 0x01 prefix. This is the RFC 6962/9162 construction with a two-part leaf. The operator periodically signs a checkpoint, a fixed 98-byte blob containing the tree identifier, tree size, timestamp and root, with Ed25519 by default. The draft is explicit that this signature is an integrity check on the checkpoint fields and does not establish trust; trust comes from anchors. A receipt carries the entry (payload hash and the metadata object), an inclusion path, the signed checkpoint, and a super_proof linking the Data Tree root into the Super-Tree. It also carries an anchors array, in which an rfc3161 token anchors the Data Tree root and a bitcoin_ots proof anchors the Super-Tree root. Receipts verify offline; the Bitcoin step needs block headers locally or a node.

One correction from the 2.1 revision of the draft (Sections 3.3 and 5.4.4) is worth carrying into any design that borrows the pattern. Two receipts from the same log establish, on their own, that they share a genesis, the same first entry. They do not establish that the history between them is one append-only sequence. That needs an RFC 9162 consistency proof between the two Super-Tree states, obtained online from the log (or equal roots at equal sizes); no member of either receipt substitutes for it. The 2.0 text claimed more, and the 2.1 text says so.

In code, the write side of one step and the read side of its receipt look like this. atl-core performs no I/O and does not append or sign; those belong to the log server or to the application. It parses the 2.0 receipt; the 2.1 witnessed-head members are not yet implemented.

use atl_core::prelude::*; use serde_json::json; use sha2::{Digest, Sha256}; // atl-core exports no digest helper; sha2 is a separate dependency // Write side. The payload is the raw bytes of the step and never enters the // log; only its SHA-256 does. The metadata carries the frame reference and // the parameters, in cleartext. let payload_hash: Hash = Sha256::digest(step_bytes).into(); let metadata = json!({ "step": 17, "prev_entry": "sha256:…", "model": "vendor/model-2026-08", "prompt_template": "sha256:…", "sampling": { "temperature": 0.0, "max_tokens": 1024 }, "tool": { "name": "search", "version": "3.2.1", "args_hash": "sha256:…" }, "observed_at": "2026-09-06T14:03:11Z", "apl": { /* the APL claim, including frame_ref; envelope omitted here */ } }); let metadata_hash: Hash = canonicalize_and_hash(&metadata)?; // RFC 8785, then SHA-256 let leaf: Hash = compute_leaf_hash(&payload_hash, &metadata_hash); // `leaf` is appended to the tree by the log server; a checkpoint is signed // and anchored on the server's schedule. // Read side, months later, offline. The receipt carries the entry, the // inclusion path, the signed checkpoint, the super_proof and the anchors. // TSA root certificates are verifier-supplied trust material, obtained out // of band; without them an RFC 3161 anchor can never count as verified. let trust = TrustStore::new().with_anchor_certificate(tsa_root_certificate); let options = VerifyOptions { rfc3161_trust_store: Some(trust), ..VerifyOptions::default() }; let receipt = Receipt::from_json(&receipt_json)?; let result = ReceiptVerifier::anchor_only_with_options(options).verify(&receipt); // `result.is_valid` is true only when at least one anchor verified. // A receipt with no verifiable anchor, and nothing about the receipt itself // refuted, is `result.is_indeterminate()`: neither accepted nor refuted, // which is the correct answer for a signed head with no verified anchor.

atl-core checks the structure of an OpenTimestamps proof and reports the block heights it attests to; comparing the proof’s Merkle path with the block header is the caller’s step, with headers from a local node.

The frame goes in the metadata as an APL claim under metadata.apl, with frame_ref pointing at a content-addressed frame that names the model build, the prompt protocol and the sampling parameters. The ATL layer answers whether the entry is included under a signed, anchored head and whether a later state the operator presents extends that head. Whether that entry is a well-formed observation under a resolvable frame is a question for the APL verifier, and a design that merges the two loses the ability to say which one failed.

8. What the trail does not prove

A trail built to the level above still leaves the following outside its guarantees. Reviewers ask about them whether or not a design document lists them.

  • Truth of inputs and correctness of outputs. The trail proves what the operator recorded as given to the model and returned by it, not that the input was accurate, that the output was right, or that the recorded input is what actually reached the model. APL’s verifier says this in its own terms: apl-valid does not mean the claim is true.
  • Completeness relative to steps that were never submitted. Consistency proofs cover what is in the tree. A step the application chose not to log, or a second run on a shadow deployment, leaves no gap for the proof to find. Completeness is provable only relative to a published head, and only if the operator’s coverage of the application’s steps is itself established by other means.
  • Time before anchoring. The observed_at field is a claim by the operator. The bound the trail can prove is “no later than the anchor,” and the gap between a step and its anchor is a design parameter, not a constant.
  • Same view for all parties. Without witnesses or gossip, the operator can hand different heads to different auditors; each will verify. The tree makes this detectable only when the auditors compare heads.
  • Identity and intent. Which human, if any, prompted a step, and why, is outside the record unless the application binds an authenticated principal into the metadata, and even then the trail proves the binding, not the intent.
  • Privacy of neighboring entries. An inclusion proof reveals O(log n) intermediate hashes, which are opaque but disclose that other entries exist; and every metadata field that was not encrypted before hashing is readable by every receipt holder.
  • Anything at all, if heads are never distributed. A tree whose signed heads stay in the operator’s database offers no more protection against the operator than a hash chain does. A signed head and an anchor start to prove something only from the moment a commitment leaves the operator.

Each mechanism proves a narrow thing to a named party under a stated assumption, and an AI agent audit trail is the sum of those narrow things, not a property of the storage engine.


If you are putting agents into production and the trail behind them has to be designed, built and explained to the people who will question it, that is contract work I take on.

Last updated on