audit-chain — Claude Code command: verify the receipt chain
Checks the Ed25519 signature on every receipt in receipts.jsonl and reports the lines that fail. Useful before a release or a security audit.
/plugin marketplace add wshobson/agents
/plugin install protect-mcp@claude-code-workflowsWhat audit-chain does
The command reads receipts.jsonl from ./receipts/ or from a directory passed with --dir, then checks each line's signature against the public key in ./protect-mcp.key (or the file named by PROTECT_MCP_KEY). Add --last N to keep only the last N lines when the file has grown. Selected lines are numbered from 1, so the command prints the offset you add to get the line number in the original file.
Under the hood it is a bash script that parses the arguments and calls npx @veritasacta/[email protected] --replay-chain. The first run downloads the package. On an offline machine, install it first with npm install --no-save @veritasacta/[email protected] and npx will use the local copy. Node.js is needed too, since the key is read through node -p.
One limit matters: protect-mcp 0.7.4 receipts carry no link to the previous receipt. A deleted, inserted or reordered line will not show up here. If you need to catch deletions, keep a copy of the receipts file where the operator cannot change it. Exit codes: 0 means everything verified, 1 means at least one receipt failed (bad signature, malformed line, wrong or missing key, unsupported algorithm), 2 means the check could not run.
What it helps with
- verify every receipt signature in the file instead of one at a time
- find the line whose receipt was altered after signing
- show auditors the log was not swapped
- run the check in CI to catch silent corruption
- scan only the last N receipts without reading the whole file
Example prompts for the agent
- /audit-chain
- /audit-chain --last 50
- /audit-chain --dir /var/log/receipts
How to install audit-chain
1Plugin marketplace
/plugin marketplace add wshobson/agents/plugin install protect-mcp@claude-code-workflows2Manually: put the files in place
.claude/commands/audit-chain.mdCall it with /audit-chain. For all projects use ~/.claude/commands/
Manually: put the files in place
~/.codex/prompts/audit-chain.mdCall it with /prompts:audit-chain. Codex marks this format deprecated in favour of skills
Manually: put the files in place
.github/prompts/audit-chain.prompt.mdManually: put the files in place
.gemini/commands/audit-chain.tomlGemini stores commands as TOML: the prompt text goes into the prompt field
Questions about audit-chain
How do I install audit-chain in Claude?
Add the marketplace with /plugin marketplace add wshobson/agents, then install the plugin with /plugin install protect-mcp@claude-code-workflows. The command lands at .claude/commands/audit-chain.md.
What does audit-chain do?
It reads receipts.jsonl, verifies the Ed25519 signature on each line against the public key, and prints the line numbers that fail.
Does it catch a deleted line?
No. protect-mcp 0.7.4 receipts carry no link to the previous receipt, so a deletion, insertion or reorder stays invisible. Keep an immutable copy of the file elsewhere.
Source text
audit-chain.md · MIT
---
description: "Verify every receipt in ./receipts/receipts.jsonl against the signer's public key. Detects tampered or malformed receipts across the audit trail."
argument-hint: "[--last N] [--dir path]"
---
# Audit Chain
Verify every receipt in the audit trail, not just a single receipt.
protect-mcp 0.7.4 appends receipts to `receipts.jsonl` in `./receipts/` (or
the specified directory), one per line, and this command checks the signature
on each line.
## Usage
```
/audit-chain # Verify all receipts in ./receipts/
/audit-chain --last 50 # Verify only the last 50 receipts
/audit-chain --dir /var/log/receipts # Use a different directory
```
## What This Command Does
1. Reads `receipts.jsonl` in the target directory
2. Keeps only the last N lines when `--last N` is given
3. Verifies each Ed25519 signature against the `publicKey` value in
`./protect-mcp.key` (or the file named by `PROTECT_MCP_KEY`)
4. Reports the line number of each receipt that fails. With `--last N`, the
verifier numbers the selected lines from 1, so the command prints the
offset to add to get the line in `receipts.jsonl`
protect-mcp 0.7.4 receipts carry no link to the previous receipt, so this
check cannot detect a deleted, inserted, or reordered line. To detect deleted
lines, keep a copy of the receipts file where the operator cannot change it.
## Implementation
```bash
RECEIPT_DIR="./receipts"; N=""
while [ $# -gt 0 ]; do
case "$1" in
--last|--dir)
if [ $# -lt 2 ]; then
echo "usage: /audit-chain [--last N] [--dir path]" >&2; exit 2
fi
if [ "$1" = "--last" ]; then
case "$2" in
''|*[!0-9]*) echo "--last takes a positive whole number" >&2; exit 2 ;;
esac
N="$2"
else
RECEIPT_DIR="$2"
fi
shift 2 ;;
*) shift ;;
esac
done
FILE="$RECEIPT_DIR/receipts.jsonl"
if [ -n "$N" ]; then
N=$((10#$N))
[ "$N" -gt 0 ] || { echo "--last takes a positive whole number" >&2; exit 2; }
TOTAL=$(wc -l < "$FILE"); OFFSET=$(( TOTAL > N ? TOTAL - N : 0 ))
echo "Checking lines $((OFFSET + 1)) to $((TOTAL)). Add $OFFSET to each reported line number."
TMP="$(mktemp)"; tail -n "$N" "$FILE" > "$TMP"; FILE="$TMP"
fi
PUB=$(node -p 'JSON.parse(require("fs").readFileSync(process.env.PROTECT_MCP_KEY || "./protect-mcp.key")).publicKey')
npx @veritasacta/[email protected] --replay-chain "$FILE" --key "$PUB"
```
Exit 0 means every receipt verified. Exit 1 means at least one receipt
failed, because it was tampered with, a line is malformed, the key is wrong or
missing, or the algorithm is unsupported. With `--replay-chain`, the verifier
reports those cases per line with exit 1. Exit 2 means the check could not
run, e.g., because the file could not be read or an option had no value.
`npx` downloads `@veritasacta/[email protected]` the first time it runs. For an
offline machine, install it in the project first with
`npm install --no-save @veritasacta/[email protected]`, and `npx` then runs the
local copy without network access.
## What to Show the User
### All receipts verify
```
Audit verification: PASSED
Scanned: 247 receipts in ./receipts/receipts.jsonl
Signatures: 247/247 valid ✓
Key: 0faf558a90dfbf88...
All 247 receipts verify. A deleted or reordered line would not show here,
because 0.7.4 receipts carry no link to the previous receipt.
```
### Tampered receipt
```
Audit verification: FAILED
Scanned: 247 receipts
Signatures: 246/247 valid (1 failed)
FAILED at line 89: invalid_signature
Request: tu-1790427588265-c8x5
Tool: Bash
Issued at: 2026-09-26T14:22:01Z
The signature on this line does not verify, so the receipt was modified
after signing, or the key is not the signer's key. Compare the line against
a known-good copy to find the altered field.
```
## When to Run This
- Before shipping a release, to confirm that no development receipt was altered
- During security audits, to show auditors that every receipt verifies
- After incidents — verify logs were not tampered with during the incident
- Periodically — CI/CD job to catch silent corruption
- Before compliance reviews — provide evidence of continuous integrity
## References
- [@veritasacta/verify on npm](https://www.npmjs.com/package/@veritasacta/verify)
- Use `/verify-receipt` for single-receipt verification
© Tom Farley · MIT · wshobson/agents