EU AI Act Art. 12 · event logging for high-risk systems
Action loggers sign what an agent did. inspeximus also signs a digest of what its memory held at that moment, digests the inputs and outputs without storing them, and lets a reader with a retained transcript check it later: a transcript that differs by one character answers false. The transcript on this page is a real run; ids and hashes change per run, nothing else does.
the run, 2026-09-17, inspeximus 2.40.0
One memory, one tool call. The entry is signed and bound to the store's receipt chain; the inputs and output are digested, not stored.
$ inspeximus writer-key --new --out key.txt
secret written to key.txt (0600)
public key: 7c4e59dc326515ba066f820357b96d6e98becead7160e0eb5b7e8a02307e11da
point the server at it: in /home/you/.config/inspeximus/config.json, set {"writer": {"key_file": "/home/you/demo/key.txt"}}
(INSPEXIMUS_WRITER_KEY_FILE cannot name this file: it is inside the project this process runs in, /home/you/demo)
pin this writer as trusted with trust_seeds={'key:7c4e59dc326515ba066f820357b96d6e98becead7160e0eb5b7e8a02307e11da'} (the MCP server: recall.trust_seeds)
KEEP THE SECRET OUT OF GIT. It attests AUTHORSHIP, not truth.
$ inspeximus --path mem.json --receipts --receipt-key-file key.txt remember 'Alice phone is +100' --key alice::phone --source crm/alice
remembered b0520e2bef [key=alice::phone]
$ inspeximus --path mem.json --receipts --receipt-key-file key.txt actions record tool:sms --input '{"to": "+100", "text": "hi"}' --output '{"sent": true}' --actor support-agent
recorded #0 tool:sms hash 3e42f575d77f SIGNED
$ inspeximus --path mem.json actions knew 0
{
"seq": 0,
"action": "tool:sms",
"ts": 1789661451.482288,
"memory_state": {
"digest": "7fb9dbbd3921c1ca0c0055394b9bf85f9e932f075a961e834ae82d7c4962abb8",
"records": 1,
"last_receipt": "5feb104086cfd48edbbaa903706ba82f7909bf740a0d1a1e0668b3df45818356",
"receipts": 1,
"recalled": [],
"recalled_at": null,
"recall_scope": "this handle",
"recall_before_previous_entry": false
},
"recalled_now": []
}
recalled is empty here because the CLI opens
a new handle per command; from Python or the MCP server, a recall made on the same handle before
the action is listed in the entry. The digest is a hash over each record's id, status, timestamp, key,
tenant and content hash at that moment; it holds no content.
The same inputs answer true on both sides. Change "hi" to "hello" in the retained input and the input side answers false while the output side still matches. The comparison is over JSON values: key order and whitespace never flip it, any value does.
$ cat in.json
{"to": "+100", "text": "hi"}
$ inspeximus --path mem.json actions matches 0 --inputs in.json --output out.json
{
"seq": 0,
"action": "tool:sms",
"inputs": true,
"output": true
}
$ cat in_edited.json
{"to": "+100", "text": "hello"}
$ inspeximus --path mem.json actions matches 0 --inputs in_edited.json --output out.json
{
"seq": 0,
"action": "tool:sms",
"inputs": false,
"output": true
}
verify recomputes every hash, link and signature, binds each entry to
a position in the store's receipt chain, and re-hashes each record against its write receipt, so a
record edited on disk under an unchanged chain fails the second line. export-trail
writes the IETF draft's record schema and chain; --verify checks the fields and the
chain of a file from any producer.
$ inspeximus --path mem.json actions verify --expected-pubkey 7c4e59dc326515ba066f820357b96d6e98becead7160e0eb5b7e8a02307e11da OK action ledger mem.json.actions.json (1 entries, bound to mem.json) OK store records against their write receipts (1 receipt(s)) $ inspeximus --path mem.json actions export-trail --out trail.jsonl --agent-id urn:agent:support.example --agent-version 1.0.0 wrote trail.jsonl: 1 records, draft-sharif-agent-audit-trail-04, session 8e3f4852-f865-4174-9ed0-627f9af83e64, tail ac7cb937eebd $ inspeximus actions export-trail --verify trail.jsonl OK trail.jsonl (draft-sharif-agent-audit-trail-04)
The ledger also records human oversight decisions
(approve, refuse, override, stop, review; Art. 14), Art. 50 disclosures, Art. 73 incidents with
their reporting clock, and lifecycle events. inspeximus compliance prints the evidence
labelled by article; the mapping page states the boundary of each row.
Timestamps from a third party, and whether that authority was on the EU trusted list on that date,
are one command away: inspeximus actions timestamp.
An action logger signs what the agent did. This ledger also carries a signed digest of the
memory the agent acted on, which is the half an investigator asks for first: what the agent
held when it did it, beside what it did. Both sit in one entry, and the memory side is bound
to the store's own receipt chain, so a store rewritten by someone without the signing key no
longer matches its ledger. An operator who holds the key can rewrite both, and can drop trailing
entries; a witnessed anchor (inspeximus actions timestamp, or a co-signed anchor)
is what closes that, and the code's own docstrings say so. The
comparison page carries the other measurement this library is
built around: whether a correction stays corrected.