Quickstart | Concepts | API | Verify | Coverage | Teams | Anchors | Recovery | Human approval in CI | Verifiable audit log | Unattended runs | Approval when unattended | Hooks vs deny
Audit log for AI agent actions you can verify offline
An agent runs tools for hours without you. Later someone asks: what did it do, and who allowed it? A plain log file answers only "what the logger says". This guide explains what a log must have to be checked by a third party without trusting its author, then shows how Deus Witness packs it and how to verify a real pack offline.
What a trustworthy log needs
Logging in Claude Code itself is useful but is a record on the same machine as the agent. Its hook events, for example, give you PreToolUse and PostToolUse inputs (Hooks reference), which you can write to a file. For an audit you want more properties than a file has:
- Tamper evidence. Each entry includes the hash of the previous one (a hash chain), so removing or editing an old entry breaks every later one.
- Authorship. Entries and checkpoints are signed, so you know who vouches for them.
- Completeness. The log says up to which entry it is complete; "the log ends here" is itself signed.
- A time that the logger cannot move back. A hash of the log published somewhere else (a timestamp service, a blockchain timestamp) proves it existed by then.
- Checking without the author. The checker is open and runs on your machine without any server.
A signed ledger and receipts
Deus Witness (a service plus a hook, pip install deus-witness) judges every tool call of an agent against a mandate you signed with a passkey. Each decision (allow, deny or defer) is appended to a hash-chained ledger and answered with a receipt: a signed statement "this call, this verdict, this ledger position". The hook only lets a call run after it has verified the receipt. Periodically the ledger gets a signed checkpoint, and every hour a Merkle root of new checkpoints goes to OpenTimestamps calendars, which commit to Bitcoin.
From the dashboard you export an evidence pack, a zip with the ledger, checkpoints, receipts, the registry of keys, anchor files and a signed manifest.
Verify a pack offline
Install the checker (Python 3.10 or newer). It comes from the package, not from the pack, so a doctored pack cannot bring its own checker:
python3 -m venv ~/dw && ~/dw/bin/pip install deus-witness
curl -sLO https://witness.sipkigroup.com/demo/deus-witness-demo-pack.zip
~/dw/bin/deus-witness-verify-pack deus-witness-demo-pack.zip --offline
That is a public demo pack (its key is public, it is not a real account). I ran exactly this with deus-witness 0.2.26. Output, shortened:
manifest: signed by the witness key
complete as of 2026-10-19 03:14:26 UTC (seq 24)
ledger: valid (26 entries)
receipts: 6/6 included
anchor ...: pending (inclusion in batch root proven; not stamped)
anchors: NONE stamped ... the history is vouched for by the witness alone
trust: verdicts: the witness's allow/deny/defer decisions are NOT replayed here ...
RESULT: VALID
The exit code is 0 for RESULT: VALID, 1 for INVALID. Now change one byte of ledger/ledger.cose inside the zip and run it again: the lines become manifest: MISMATCH ledger/ledger.cose, ledger: invalid ... and RESULT: INVALID, exit 1. Add --require-anchors and a pack without a stamped anchor is INVALID too, which is the setting to use when the operator is not you.
Two flags make the check independent of the pack: --witness-kid=<hex> (the key id you fetched yourself from /v1/witness) and --owner-kid=<hex> (a key your Devices page shows). A mismatch is INVALID. Without them the checker reads the keys from the pack and prints a trust: line saying so.
Limits (read the trust lines)
- Verdicts are not replayed.
VALIDmeans the signatures, chain, Merkle roots, key registry and anchors hold. The checker does not re-run the witness's decision rules against the mandate; the linetrust: verdicts ... NOT replayedsays so. You see what the witness decided and that it cannot be altered afterwards, not that the decision was right. - Without an anchor, the witness could have rewritten the history.
pendingis normal for hours; once Bitcoin includes the batch, a re-downloaded pack shows the block. - Only calls that pass through the witness are in it. An agent with other channels is out of scope; see what the witness sees.
- Some refusals are in the operator's database only, not in the ledger (for example a call with no mandate). The concepts page lists them.
- The witness is only as independent as its operator; at this stage the operator is the author of the project.
Full option list and older pack versions: Verify. How the receipts are made: Concepts. To produce such a log from Claude Code in CI: Require human approval for Claude Code tool calls, and to try it yourself, Quickstart. The browser demo at /demo/ builds the same kind of pack in about a minute.