Proving a log was not altered

An automated system did something consequential, and somebody now wants to know that the record of it is the record that was written at the time. This is the part of AI record-keeping that is easiest to claim and hardest to check, and most of what is written about it answers a narrower question than the one being asked.

Nothing here is specific to any format or product. The distinctions apply to whatever you already log.

First, the question underneath the question

Altered by whom? Almost every scheme in use protects against a different party than the one people assume, and the difference decides whether the record is worth anything in a dispute.

  • Against accident and outsiders. A hash of the record detects corruption and detects somebody else editing it afterwards. This is what most systems have and it is worth having.
  • Against the operator. The party that wrote the record can rewrite it and recompute any digest it computed itself. If the question is "did the vendor change this", a digest the vendor calculated answers nothing, however strong the hash.

Internal audit asking about a team's own system, or a customer asking about a supplier's, is always asking the second question. So is a court.

What each mechanism actually proves

MechanismDetects an outsider's edit Detects the operator's editCost
Append-only storagepartly nofree
Hash of each entryyes nofree
Hash chainyes nofree
Digital signatureyes no, the key holder re-signs key management
RFC 3161 timestampyes yes, for time and content free, one HTTP call
Transparency logyes yes, and detects omission infrastructure

Append-only storage is a property of your code path behaving. It is a real control against the ordinary case and it is not evidence, because whoever administers the database can still write to it.

A hash chain makes each entry commit to the one before it, so an edit in the middle breaks everything after. Strong against an outsider and worth nothing against the party that can recompute the whole chain, which is the party that wrote it.

A signature binds the record to a key. Better, and it moves the question rather than settling it: whoever holds the key can sign a different record just as validly. It helps when the key is held somewhere the operator cannot reach.

An RFC 3161 timestamp is the cheapest thing that changes the answer. A Time Stamp Authority signs the pair of your digest and the current time, with its own key. The operator cannot forge that signature, cannot backdate it, and cannot alter the record without the digest ceasing to match. Free authorities operate it, it underpins qualified electronic timestamps under eIDAS, and a verifier checks it with openssl rather than with anything of yours.

A transparency log in the style of RFC 9162 is stronger again, because it also detects omission: a record that was never submitted is visible as a gap. It is the right answer at scale and it needs infrastructure somebody has to run.

The cheap one, in full

Hash the records you want to fix, ask an authority to sign the digest, and keep the token beside them.

# the digest of whatever you are fixing in time
sha256sum records.jsonl

# ask an authority to sign it
openssl ts -query -data records.jsonl -sha256 -cert -out request.tsq
curl -s -H "Content-Type: application/timestamp-query" \
  --data-binary @request.tsq http://timestamp.digicert.com > token.tsr

# what it says
openssl ts -reply -in token.tsr -text

Anybody you later hand the records and the token to checks them against their own certificate store, with no cooperation from you:

openssl ts -verify -in token.tsr -data records.jsonl \
  -CAfile /etc/ssl/certs/ca-certificates.crt

Verification: OK

And what a failure looks like, which is the half nobody prints. One byte changed anywhere in the file:

error:17800067:time stamp routines:ts_check_imprints:message imprint mismatch
Verification: FAILED

Note what that does and does not tell you. It says the file is not the one the authority signed. It does not say who changed it, when, or which byte, and it cannot distinguish a malicious edit from a trailing newline an editor added on save. Hash a canonical form rather than the file as it happens to sit on disk, or you will spend a morning investigating whitespace.

That is the whole mechanism. The common mistakes are worth knowing because they are silent: a DER integer that is not minimally encoded produces a token strict parsers reject, and roughly half of hand-built requests hit it depending on one random byte. If you build the request yourself rather than with openssl ts -query, check your output parses before you rely on it.

What none of this proves

That the record is true. A system can record precisely what it believed, sign it, timestamp it, and be wrong about all of it. Integrity protects the entry, not the claim inside it.

That the record is complete. Everything above fixes what you submitted. Only a transparency log makes what you did not submit visible, and even then only for records that were meant to be there.

That a different record was not produced and discarded. A timestamp proves this one existed at that time, not that it was the only one.

Anyone selling you integrity as an answer to accuracy is selling you the wrong thing, and the distinction is the first one an auditor will make.

A worked example you can check

There is a record published at a record you can check without us, with its token, its digest, and the three commands above run against real files. It is in the Testimony Record format, which is one way to write these things down and not the only one. The mechanism on this page works with whatever you already log.