Snapshots
Cryptographically signed snapshots let you prove — to yourself or anyone else — that your policy and decision ledger have not been tampered with.
What a snapshot is
A snapshot is a JSON file that captures two things at a point in time:
- A hash of your
policy.yaml— so you can tell if the rules changed - The head hash of your ledger — the last entry in the integrity chain
Both are signed with an HMAC-SHA256 signature using a secret you control. Anyone with the same secret can verify the snapshot. No secret leaves your machine.
Setup
Set the signing secret as an environment variable before running any snapshot command. Use a strong random value — at least 32 characters.
The same secret must be present both when creating and when verifying a snapshot. If they do not match, verification fails.
Creating a snapshot
Reads your current policy and ledger, computes their hashes, signs the result, and writes shotoku.snapshot.json to the current directory.
| Flag | Default | Description |
|---|---|---|
| --out <path> | shotoku.snapshot.json | Where to write the snapshot file |
| --policy <path> | policy.yaml | Policy file to hash |
| --ledger <path> | data/decisions.jsonl | Ledger file to hash |
| --key-id <label> | — | Optional label stored in the snapshot for reference |
Verifying a snapshot
Recomputes the hashes of your current policy and ledger and compares them against the snapshot. Also re-derives the HMAC signature and checks it matches.
Verification fails — and exits with code 1 — if:
- The policy file was modified since the snapshot was created
- New entries were appended to the ledger (or entries were removed)
- The snapshot file itself was edited
- The wrong secret is set in
SHOTOKU_SNAPSHOT_SECRET
| Flag | Description |
|---|---|
| --snapshot <path> | Snapshot file to verify (required) |
| --policy <path> | Override the policy path stored in the snapshot |
| --ledger <path> | Override the ledger path stored in the snapshot |
Ledger integrity chain
Every record written to the ledger carries an integrity block — a sequence number, the hash of the previous record, and the hash of the current record. This forms a chain: changing any past record breaks every hash that follows it.
The snapshot captures the head hash — the hash of the latest record. Verifying the snapshot confirms the chain is intact up to that point.
The genesis hash (the “previous hash” of the very first record) is the fixed value sha256:0000…0000 (64 zeros). This is a known constant — it does not need to be kept secret.
When to use snapshots
- Before and after deploying a policy change — snapshot before, verify after
- As part of a CI step to confirm the ledger was not modified during a run
- To share an auditable proof of your policy state with a collaborator
- To detect accidental or unauthorized ledger edits