16 August 2026 · commands in this article were run before publishing

What your AI audit log cannot tell you

A log is written by the component being audited. It can be complete, internally consistent, and still say nothing about the access that went around it.

Connect an AI assistant to a company database and two questions appear on the same day. What did it read? And how do you know that is all it read?

The first question has an ordinary answer: log the queries. The second one does not. If an assistant reaches the database through some other path — a credential used out of band, a service that was never routed through the gateway, a sink that quietly stopped writing — the log remains complete and internally consistent. Nothing in it is false. The accesses it never saw simply are not there.

That second question is what this project was built around, and it is why the product is not described as a masking tool.

Four mechanisms, in the order they matter

A gate that refuses before it explains

Table allow-lists, row caps, and deterministic masking of protected classes — applied before data leaves the boundary, not after the model has seen it. Policy is a file, and an empty policy denies everything rather than allowing everything.

A receipt for each access

Signed with Ed25519, hash-chained, append-only. It carries the policy decision, masking counts by class, and a digest of the request. It never carries a value. A single-file verifier checks it offline with zero imports from the package it is verifying, so a third party does not have to trust our code to trust the result.

Reconciliation against the source's own counters

This is the part that answers the second question. The database keeps its own accounting of query activity. conarium-reconcile compares snapshots of those counters against the receipt set over a window and reports every pattern the source recorded that the receipts do not cover. Two accounts, produced by two different components — a gateway cannot delete activity from a ledger it does not write.

A second signature on the chain head

A log signed only by the party that wrote it answers “was this altered?” but not “who else saw it?”. A countersigning endpoint signs chain heads with a separate key, so ordering is attested by someone other than the operator, and the head is anchored to a public timestamp.

What ran in production, and what backs the number

121,374
records processed
121,366
identities masked
485,496
fields masked
0
leaked to the model

The only production deployment is my own company's ERP — a 36-year-old furniture business with a real customer table, not a benchmark dataset.

The part most case studies leave out

What backs that number is a hash-chained audit file of 123 lines: the arithmetic can be re-added by hand, and the chain was re-verified seventeen days later. What does not back it is a receipt chain. That run emitted audit entries, not signed portable receipts, and its actor is a batch service identity rather than a person.

So if you ask to see the receipts for those 485,496 fields, the honest answer is that they do not exist. The receipt chain is a separate and much smaller measurement. Scale and offline verifiability are two different claims, and I would rather draw that line myself than have a reader find it. Report 001 lists the rest of its limits.

Where this meets the EU AI Act

Articles 12 and 19 require automatically generated logs and specify what they must contain. The receipt implements the portable half: timestamp and period, model and client identification, the reference data consulted, the policy decision and its rule ids, masking counts by class, and a request fingerprint.

One field is worth pausing on. A receipt never says “the model was X.” It says “X was declared” or “not declared” — because model identity does not exist in the MCP protocol. A connecting client never tells the server which model it is running. Writing a fixed value into config and signing it would mean attesting to something never observed, which is precisely the claim this artifact is supposed to make impossible. So the receipt distinguishes measured from declared, and says which.

What it does not prove

The project ships a LIMITATIONS file, and it is the document I would read first.

Verify it without trusting me

The demo endpoint serves a live receipt chain. This checks it on your machine, with a verifier that imports nothing from the running service:

npm i @conarium-ai/core
curl -fsS https://demo.conarium.dev/proof/chain.jsonl -o chain.jsonl
curl -fsS https://demo.conarium.dev/proof/key.pem      -o key.pem
curl -fsS https://demo.conarium.dev/proof/key.pem.keyid -o key.pem.keyid
npx conarium-verify chain.jsonl --pubkey key.pem     # exit 0

Change one character inside that file and run it again: exit 10, with the recomputed and stored hashes printed side by side. Add --anchor-check and the verifier also checks the chain head against its public timestamp — that path talks to blockstream.info, and if the host is unreachable it reports “could not check” rather than “valid.”

Where this is going

The two mechanisms I have not found elsewhere — transformation evidence and coverage reconciliation — are written up as an IETF Internet-Draft, draft-dogru-scitt-disclosure-evidence, positioned as payloads for a SCITT Transparency Service rather than as a competing receipt format.

A vendor-neutral adversarial conformance suite is in progress, and it will publish the cases this implementation fails alongside the ones it passes. A suite whose author scores full marks proves nothing.

If you know of an implementation that does all three of inline enforcement, portable offline-verifiable receipts, and source-side reconciliation, I would like to be corrected — PRIOR-ART.md exists to be edited.

Found an error in this article — including one that flatters us? → e.dogru@conarium.dev. Corrections get published with the correction noted.
Report 001 · Technical overview · Comparison · Source (MIT) · @conarium-ai/core on npm
© 2026 Conarium · Self-hosted. Fail-closed. Governed by you.