Hash-Chained Tamper-Evident Audit Log
A plain append-only log (like the basic audit-log hook) is durable but not tamper-evident — a line can be deleted or edited after the fact with nothing to show it. This upgrades the same idea with a hash chain: each entry includes the SHA-256 of the previous entry, so any retroactive edit breaks the chain from that point forward and is immediately visible on verification.
1. Save this as the hook script
#!/usr/bin/env python3
"""PostToolUse hook — hash-chained audit log. Each entry embeds the SHA-256
of the previous entry's JSON. Wire with matcher "*"."""
import json, sys, datetime, hashlib
from pathlib import Path
data = json.load(sys.stdin)
log_path = Path(data.get("cwd", ".")) / ".claude" / "audit_chain.jsonl"
log_path.parent.mkdir(parents=True, exist_ok=True)
prev_hash = "0" * 64
if log_path.exists():
lines = log_path.read_text().strip().splitlines()
if lines:
prev_hash = hashlib.sha256(lines[-1].encode()).hexdigest()
entry = {
"ts": datetime.datetime.utcnow().isoformat() + "Z",
"session_id": data.get("session_id"),
"tool_name": data.get("tool_name"),
"prev_hash": prev_hash,
}
with open(log_path, "a") as f:
f.write(json.dumps(entry) + "\n")
sys.exit(0)
Save to a path inside your project, e.g. .claude/hooks/hash-chained-audit-log.sh, then chmod +x it.
2. Wire it into .claude/settings.json
{
"hooks": {
"PostToolUse": [
{
"matcher": "*",
"hooks": [
{"type": "command", "command": "python3 .claude/hooks/hash-chained-audit-log.py"}
]
}
]
}
}
Before you use this
A hook runs with your local shell's permissions — review the script above before adding it, the same way you'd review any script you didn't write yourself. See our guide on hook security risks for the specific CVEs this category of feature has already produced.
Related hooks