Skip to content

2026-01-21

Hash-Chained Audit Trails Explained

Intended Team · Founding Team

Why Traditional Logging Fails

Every application writes logs. Web servers log requests. Databases log queries. Operating systems log events. Logging is ubiquitous and well-understood.

But logging is not auditing. A log entry is a text record appended to a file or a stream. It can be modified. It can be deleted. It can be reordered. There is no built-in mechanism to detect any of these modifications.

For AI governance, this is unacceptable. The audit trail is evidence. It proves that governance decisions were made, that policies were enforced, and that actions were authorized. If the audit trail can be tampered with, the evidence is worthless. An adversary who compromises an agent and takes unauthorized actions could also modify the audit trail to hide the evidence.

Hash-chained audit trails solve this problem. Each record in the chain includes a cryptographic hash of the previous record. If any record is modified, the hash chain breaks, and the tampering is detectable.

How Hash Chains Work

A hash chain is a sequence of records where each record includes a hash computed from its own content and the previous record's hash. The first record (the genesis record) uses a known initialization value instead of a previous hash.

Here is the construction. Record N contains the record's data (the governance event, timestamp, and metadata), the hash of Record N-1, and a hash of (Record N's data + hash of Record N-1). This final hash is what Record N+1 will reference.

In concrete terms:

text
Record 1:
  data: { intent: "...", decision: "allow", ... }
  prev_hash: "0000...0000" (genesis)
  hash: SHA-256(data + prev_hash) = "a3f2...c891"

Record 2:
  data: { intent: "...", decision: "deny", ... }
  prev_hash: "a3f2...c891"
  hash: SHA-256(data + prev_hash) = "7b41...e2d4"

Record 3:
  data: { intent: "...", decision: "allow", ... }
  prev_hash: "7b41...e2d4"
  hash: SHA-256(data + prev_hash) = "f198...a5c7"

Each record's hash depends on its own data and the previous record's hash. If someone modifies Record 2's data, its hash changes. But Record 3's `prev_hash` still references the original hash of Record 2. The chain breaks at the boundary between Record 2 and Record 3.

To hide the modification, the attacker would need to recompute Record 3's hash (using the new Record 2 hash), then recompute Record 4's hash, and so on for every subsequent record. For a chain of millions of records, this is computationally expensive. And if the chain is verified periodically and the verification hashes are stored externally, the recomputation is detectable even if completed.

SHA-256: The Hash Function

Intended uses SHA-256 as the hash function for the audit chain. SHA-256 is part of the SHA-2 family, standardized by NIST. It produces a 256-bit (32-byte) hash value.

SHA-256 has three properties critical for hash chains.

**Pre-image resistance.** Given a hash value, it is computationally infeasible to find any input that produces that hash. This prevents an attacker from crafting a modified record that coincidentally produces the same hash as the original.

**Second pre-image resistance.** Given an input and its hash, it is computationally infeasible to find a different input with the same hash. This prevents an attacker from replacing a record with a different record that has the same hash.

**Collision resistance.** It is computationally infeasible to find any two different inputs with the same hash. This prevents birthday attacks on the chain.

SHA-256 has been extensively analyzed by the cryptographic community and is considered secure for the foreseeable future. It is the standard choice for integrity verification in systems ranging from Bitcoin to TLS certificate fingerprints.

Serializable Transactions

The hash chain's integrity depends on records being written in a strict, deterministic order. If two records are written concurrently and both attempt to link to the same previous record, the chain forks. Forks are irrecoverable without manual intervention.

Intended prevents forks by using serializable transaction isolation for chain writes. PostgreSQL's serializable isolation level ensures that concurrent transactions are executed as if they were run sequentially. If two write transactions conflict, one is rolled back and retried.

In practice, this means chain writes go through a single serialized path. The write process reads the current chain head (the most recent record), computes the new record's hash using the head's hash, writes the new record, and updates the chain head pointer. This entire sequence happens in a single serializable transaction.

If two write processes attempt this simultaneously, PostgreSQL detects the serialization conflict (both read the same chain head) and aborts one of them. The aborted write retries, reads the new chain head (which now includes the other write's record), and computes its hash correctly.

Batched Chain Writes

Serializable transactions with per-record writes create a throughput bottleneck. At high decision volumes, the chain write becomes the limiting factor.

Intended addresses this with batched writes. Records are collected in an in-memory buffer for a configurable window (default: 50 milliseconds). At the end of the window, the batch is written as a single chain segment.

Within the batch, records are chained: Record 1 links to the batch's predecessor, Record 2 links to Record 1, and so on. The entire batch is written in a single serializable transaction. The batch's last record becomes the new chain head.

Batching provides two benefits. First, it reduces the number of serializable transactions by a factor proportional to the batch size. Instead of 100 transactions per second, you get 2 transactions per second, each containing 50 records.

Second, batching is compatible with the chain's tamper-detection properties. The chain is still hash-linked end-to-end. Modifying any record in any batch breaks the chain at that point.

Verification

The entire point of a hash chain is tamper detection. Verification is the process that detects tampering.

Full chain verification starts at the genesis record and walks the chain forward. For each record, the verifier recomputes the hash from the record's data and the previous record's hash, then compares the recomputed hash to the stored hash. If they match, the record is intact. If they do not, the record has been tampered with.

Full chain verification is O(n) in the number of records. For a chain of 1 million records, verification takes approximately 20 seconds. For 10 million records, approximately 200 seconds. This is fast enough for periodic verification (daily or weekly) but too slow for real-time verification of every write.

Partial chain verification verifies a subset of the chain: for example, all records from the last 24 hours. Partial verification is useful for frequent checks between full verifications.

Checkpoint verification stores the chain head hash at regular intervals (daily, for example) in an external system. To verify the chain has not been tampered with since the checkpoint, you verify from the checkpoint to the current head. This is much faster than full chain verification and provides a strong integrity guarantee.

Intended's verification tool is open-source. Anyone can verify a Intended audit chain export independently, without trusting Intended's infrastructure. The tool reads the exported records, recomputes the hashes, and reports the verification result. This independence is essential for auditor trust.

Chain Repair

What happens if the chain is corrupted? Hardware failure, software bugs, or operational errors can corrupt records without malicious intent. The chain breaks, and verification fails.

Intended supports chain repair for non-malicious corruption. The repair process identifies the corrupted records (the records where hash verification fails), marks them as repaired (preserving the original data and the repair metadata), recomputes the chain hashes from the repair point forward, and records the repair event in the chain itself.

The repair is visible in the chain. Auditors can see that a repair occurred, when it occurred, and what records were affected. This transparency is important: a repair is not the same as tampering, but it must be documented.

Chain repair is a manual process that requires administrative authorization. It is not automated because automatic repair could mask malicious tampering.

Why This Matters for Compliance

Compliance frameworks increasingly require tamper-evident audit trails for automated decision-making systems. SOC 2's processing integrity criteria, ISO 27001's logging requirements, and the EU AI Act's transparency requirements all benefit from cryptographic audit integrity.

A hash-chained audit trail provides stronger evidence than traditional logging. The chain proves that records are in order (each record links to the previous), unmodified (any modification breaks the chain), and complete (gaps in the chain are detectable by sequence number verification).

For auditors, this means they can independently verify that the audit trail has not been tampered with. They do not need to trust the organization's assertions about log integrity. They can verify it themselves with a deterministic, reproducible process. That level of assurance is what compliance demands.