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.39.1
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: 7649bb32db05e344f4d8d6982b92e8886298d9217d0fb4dc93fef155aff37aa3
point the server at it: INSPEXIMUS_WRITER_KEY_FILE=key.txt
pin this writer as trusted with trust_seeds={'key:7649bb32db05e344f4d8d6982b92e8886298d9217d0fb4dc93fef155aff37aa3'}
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 e1c4d8ccc1 [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 68dfdad73355 SIGNED
$ inspeximus --path mem.json actions knew 0
{
"seq": 0,
"action": "tool:sms",
"ts": 1789648531.390615,
"memory_state": {
"digest": "13360a8e1503ce6d7b387b8a9d7eac8823f3bee94c3d55f72f3e3fe9f2c9180a",
"records": 1,
"last_receipt": "46a855df52cec14d0f4ea6bed822534a112a66c416248afd8a430aa7f73290f4",
"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 7649bb32db05e344f4d8d6982b92e8886298d9217d0fb4dc93fef155aff37aa3 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 70b027d0-3796-41a8-aec5-c098a280a32c, tail e97a9b0bcb8c $ 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.