Mental model: a policy is a living document with a version history; a snapshot is a photograph of the exact rules used for one decision. The decision pins the photograph, not the document.
This page is about the frozen rule state behind a decision. For the human-facing version lifecycle — drafts, maker-checker review, ratification, and the active-version model — see Policy Lifecycle. For how a decision replays against its snapshot, see Snapshot Pinning.
Why snapshots exist
Consider a policy that changes between a decision and an audit:DENY, and the original decision would be unreproducible. The snapshot makes the Day-1 rules permanently available, so the decision is always explainable on its own terms.
What a snapshot binds
A snapshot is content-addressed: its identity is derived from the rules it contains, not from a sequential version number alone.
Only behavioural rule fields are hashed:
code, condition, severity, and when (when present). Documentation-style metadata — a rule’s name, description, or source_ref — is deliberately excluded, so editing a rule’s prose does not invalidate receipts that reference the snapshot. Inactive rules are excluded entirely.
Two rule sets that differ only in their
when clauses hash differently — conditional applicability is behaviour, so it is bound. Two that differ only in a description hash the same — prose is not behaviour.Content addressing and deduplication
Because identity is derived from content, identical rules produce an identical hash. If a later policy version reverts to a previously-seen rule set, MeshQu reuses the existing snapshot rather than storing a duplicate:Use both identifiers for what each is good at. The version number (“this used policy v3”) is human-readable lineage. The snapshot hash is content-addressable, tamper-evident proof that the rules weren’t altered after the fact. MeshQu keeps both on every decision.
Immutability
Snapshots are written once and never modified. This is enforced at two layers:- By construction — a snapshot is created via a find-or-create path. If a matching snapshot already exists for the tenant, it is reused; otherwise a new one is written. Existing rows are never updated.
- At the database — an immutability trigger rejects every
UPDATEandDELETEon the snapshot table. A privileged operator cannot silently rewrite a snapshot’s rules.
Snapshot, version, and policy
Three layers, different mutability:Snapshots in the receipt
In v2 receipts, the snapshot is bound into the cryptographic proof two ways:policy_snapshot_id— the UUID, identifying which snapshot.policy_snapshot_digest—sha256(canonicalJson({ id, rules, policy_versions, created_at })), binding the full snapshot content, not just its UUID. Approval-receipt lineage is carried transitively through this digest.
policy_snapshot_id and evaluated_rules_hash; the full content-digest binding is the v2 addition. Both verify forever. See the Receipt Reference for the exact payloads.
See also
- Snapshot Pinning — how a recorded decision replays to an identical verdict.
- Policy Lifecycle — drafts, review, ratification, and the active-version model.
- Integrity & Hashing — canonical JSON and the SHA-256 primitives behind snapshot hashes.
- Receipt Reference — how the snapshot is bound into a receipt.