Content hashing
The hash covers only the captured configuration — not the name, not the
tags, not the timestamp, not the description:
content_hash = "sha256:" + sha256(canonical_json(captured))
Two consequences follow, both intended. Renaming or re-tagging a snapshot does
not change its hash. And two snapshots of identical configuration are visibly
duplicates by matching hash, regardless of when they were taken or what they
were called. The UI renders the trailing hex as a fingerprint chip for exactly
this comparison.
The hash is verified at three points, with escalating strictness: on read, as
a non-fatal warning appended to the snapshot's warnings; on restore, as an
additional confirmation gate; and on import, as a hard rejection.
Layered above the hash is an HMAC provenance signature, applied only on export
and computed under a box-local secret. On import the signature yields one of
three outcomes: a match means the snapshot originated on this box; a mismatch
means it came from another box and is accepted with a warning; and an absent
signature means a legacy export and is accepted silently. This is a provenance
hint, not an authorisation gate — cross-box import is a supported workflow, not
an attack to be blocked.