package snapshot import ( "crypto/sha256" "encoding/hex" ) // remoteKeyPrefix is mixed into the snapshot ID hash so the resulting // hex digest is domain-separated from any other "double SHA256 of a // string" identifier the user might also use. Keeping this stable is a // hard compatibility requirement: changing it invalidates every // existing snapshot's remote storage path. const remoteKeyPrefix = "vaultik|" // RemoteSnapshotKey returns the storage-side identifier for a snapshot // given its human snapshot ID. It is hex(SHA256(SHA256(prefix + id))). // The two SHA256 rounds match Bitcoin's "hash256" convention so the // output looks like a 64-character hex blob with no exploitable // structure visible to a remote observer. // // We use this in three places: // // - the "metadata//..." subdirectory on the storage // backend so a directory listing of the bucket / file:// dest // doesn't reveal hostnames, configured snapshot names, or backup // timestamps; // - the `snapshot_id` field of the unencrypted manifest.json.zst // for the same reason; // - any code path that needs to translate a known local snapshot ID // into the path it would occupy on remote storage. // // The human ID stays the user-visible handle everywhere else — local // database joins, CLI arguments, summary lines, log fields — because // it's never written to the public bytes once this function gates // every storage-path construction. func RemoteSnapshotKey(snapshotID string) string { first := sha256.Sum256([]byte(remoteKeyPrefix + snapshotID)) second := sha256.Sum256(first[:]) return hex.EncodeToString(second[:]) }