Tamper-evident action logging for agents: hash-chained append/verify, one JSONL file, zero deps.
Copy the AI prompt to install this server into Claude Code, Cursor, or another agent β or use 1-click editor setup below.
π‘ Paste the JSON block into your client's configuration file under mcpServers, then restart the application.
Observability tools show you what your agent did. arcaeon-ledger lets you prove it.
Every record is hash-chained to the one before it. Edit a row, delete one, or
reorder history, and every later link breaks β verify names the exact line.
You own the record, and you can prove it wasn't altered. Zero dependencies, one
JSONL file, two verbs.
Tampering is caught, not hoped against:
CLI (wire it into CI or a pre-ship gate β a tampered log exits nonzero, and a log that could only be partially vouched for no longer exits like a fully verified one):
verify exit codes (0.5.7):
| exit | meaning |
|---|---|
0 | fully verified β every row checked, chain intact (ok: true) |
1 | broken β a break was found (ok: false), or bad usage |
3 | verified within scope only β no break found, but unchained prechain rows were skipped unverified (ok: null, verified_scope: "bounded_prechain_skipped"). A fabricated "legacy" prepend lands here, never at 0. Pass --strict to make it a hard 1 instead. |
A CI gate should treat only 0 as green:
A hash chain proves sequence integrity β it can't prove who wrote each entry or
whether they were allowed to. Attach an authority block to bind the actor and
their permission surface into the chained (tamper-evident) row:
Now the audit question sharpens from "was this edited?" to "was this edited and was the writer authorized?" β editing the principal, capability, or schema hash breaks the chain like any other tamper. This composes tamper-evidence with permission-replay. (Shipped in response to community feedback on launch.)
The loudest unmet pain for agent builders in 2026 is the reliability/audit gap:
an agent "completes" a task and the result is quietly wrong, and you can't
reconstruct β or prove β what actually happened. Observability platforms trace
runs; none give you a tamper-evident, portable, ownable record. Regulation
is arriving too: the EU AI Act requires high-risk systems to technically allow
automatic recording of events over their lifetime (Art. 12(1)) and requires
providers and deployers to keep those logs, to the extent under their control,
for at least six months (Art. 19(1), Art. 26(6)). The Act mandates recording
and retention β tamper-evidence is not its word, it is ours: when someone asks
whether a retained log is still the log, that question needs an answer stronger
than trust. arcaeon-ledger is the smallest honest version: a cryptographically
chained action log you drop in, own, and verify.
chain = sha256(prev_chain + canonical_json(row_without_chain))[:32]
The chain value is truncated_sha256_128 β the first 32 hex chars (128 bits)
of SHA-256, not the full digest. Named so nobody cites it as full SHA-256:
128 bits is plenty for edit/accident detection, thinner if you want the chain
itself to be expensive to grind after a rewrite (credit: atomic-raven's review).
Each row commits to the entire history before it. The first row chains from a
fixed "genesis" seed. Rows without a chain field are tolerated only before
the first chained row (so you can adopt it on an existing log); an unchained row
appearing after the chain begins is itself flagged. On a mismatch, verify
keeps going from the claimed value so it counts later damage honestly instead of
cascading one break into noise.
Being precise here is the product, not a disclaimer. A hash chain proves the
recorded content of each row was not altered in place after writing:
mid-file edit, delete, and reorder all break it and verify names the row.
One word in that sentence changed in 0.5.8, and the reason is the kind of thing this section exists for. It used to say "the recorded bytes", which claims more than the chain does. The chain is computed over each row parsed back from the file, and the reader normalises byte sequences it cannot decode β so two different byte strings inside such a region read identically and produce the same verdict. What is protected is the meaning of every row, not the exact bytes of the file. If you need byte-level custody, hash the file itself alongside this.
It does not by itself prove five other things:
1. Truncation. Lop off the most recent rows and what remains verifies clean β no append-only chain catches this alone. Close it by publishing the head somewhere outside your own control, on a cadence:
2. Truth. The chain notarizes whatever was written β a tamper-evident record of a hallucination is still a hallucination with a checksum. To make a row speak about the world, hash a re-fetchable artefact (URL+bytes, a snapshot, tool stdout) and store that digest in the row, so a third party can re-get it and compare.
3. Authorship. authority() (above) records who-claimed-what, but it is data
in the row, not a signature β a rewriter who re-mints from genesis re-mints it too.
External head-anchoring (#1) is the thing a re-minter cannot advance.
4. Fabricated-legacy-prepend. Rows with no chain field are tolerated before
the first chained row β that is deliberate, so you can adopt the chain on top of an
existing log without rewriting its history. But skipped rows are unverified rows,
and the verifier cannot tell real legacy history from a fabricated prepend. So
(0.5.7) a non-strict verify that skipped any rows never mints a green: ok is
None β "no break found, verified within scope" β falsy, with the scope in-band
(verified_scope: "bounded_prechain_skipped") and the count in prechain; the CLI
exits 3, not 0. Only a scan that checked every row returns ok=True. If your
log is chained from genesis and must have no legitimate legacy rows, pass
verify(strict=True) / --strict β it treats any unchained row as a break, hard
red. (An unchained row inserted after the chain begins is already flagged in
every mode.)
5. Completeness. This is the big one, and it is structural: the agent decides
what to call append on. A tamper-evident log of the calls an agent chose to
report is still self-report. Nothing inside this library can close that, because
anything the agent invokes, the agent can decline to invoke.
Close it by moving the pen out of the agent's reach β record at the seam instead, in a separate OS process the agent does not own, cannot skip, and cannot see:
arcaeon-adapter is a
stdio proxy that forwards JSON-RPC byte-for-byte between an MCP client and server,
writing one hash-chained row per tools/call to its own ledger. Wrapping it around
this library's own MCP server produced the number that makes the point: the
server's own diary wrote 0 rows while the seam log captured 5. The gap
between what a system reports about itself and what the seam observed is the
thing worth measuring.
Scoped honestly, the primitive is "this file was not rewritten in place" β small,
true, and testable. The layers above (external anchoring via head(), artefact
binding, signed authorship, seam recording) are how you extend it toward a full
evidence claim.
The two look like the same thing β "no data" β and verify() treats them as
opposites, on purpose:
No reviews yet β be the first to share how this listing worked for you.
Showcase your server listing on GitHub or your project documentation. Embed this dynamic SVG badge to highlight official listing status and live engagement.
[](https://allmcps.com/mcp/arcaeon-ledger)<a href="https://allmcps.com/mcp/arcaeon-ledger"><img src="https://allmcps.com/api/badge/arcaeon-ledger?style=directory" alt="Arcaeon Ledger on AllMCPs" /></a>