verify --deep's final blob-integrity step hashed the encrypted
downloaded bytes with a single SHA256 and compared that to the blob's
remote name. That name is the double SHA256 of the blob's plaintext
(internal/blobgen/writer.go, Sum256), so the two values can never be
equal and deep verification failed on every healthy blob with blob hash mismatch. The earlier per-chunk hash checks and blob-existence check
were correct; only this final comparison was wrong.
The fix hashes the decompressed plaintext instead: a TeeReader feeds
the plaintext into a hasher as chunk verification streams it, and verifyBlobFinalIntegrity compares the double SHA256 of that plaintext
to the blob name — the same derivation the writer uses.
Test: TestDeepVerifyAcceptsHealthyAndRejectsCorruptBlob backs up a real
snapshot through the on-disk storage backend, runs RunDeepVerify (which
fails before this fix), then flips one byte inside a stored blob and
confirms deep verification then fails.
Reader's note: the pre-existing TestTeeReaderWithDecryption still hashes
encrypted bytes, but it only exercises TeeReader + age + zstd mechanics
and never compares against a blob name, so it stays valid and untouched.
Local make check (test + Docker lint + fmt-check) is green; CI on this
runner is known-unreliable (issue #133).
`verify --deep`'s final blob-integrity step hashed the encrypted
downloaded bytes with a single SHA256 and compared that to the blob's
remote name. That name is the double SHA256 of the blob's plaintext
(`internal/blobgen/writer.go`, `Sum256`), so the two values can never be
equal and deep verification failed on every healthy blob with `blob hash
mismatch`. The earlier per-chunk hash checks and blob-existence check
were correct; only this final comparison was wrong.
The fix hashes the decompressed plaintext instead: a `TeeReader` feeds
the plaintext into a hasher as chunk verification streams it, and
`verifyBlobFinalIntegrity` compares the double SHA256 of that plaintext
to the blob name — the same derivation the writer uses.
Test: `TestDeepVerifyAcceptsHealthyAndRejectsCorruptBlob` backs up a real
snapshot through the on-disk storage backend, runs `RunDeepVerify` (which
fails before this fix), then flips one byte inside a stored blob and
confirms deep verification then fails.
Reader's note: the pre-existing `TestTeeReaderWithDecryption` still hashes
encrypted bytes, but it only exercises `TeeReader` + age + zstd mechanics
and never compares against a blob name, so it stays valid and untouched.
Local `make check` (test + Docker lint + fmt-check) is green; CI on this
runner is known-unreliable (issue
https://git.eeqj.de/sneak/vaultik/issues/133).
Closes https://git.eeqj.de/sneak/vaultik/issues/131
Model: opus-4-8
clawbot
self-assigned this 2026-09-21 19:55:54 +02:00
Deep verification's final blob-integrity check hashed the encrypted
downloaded bytes with a single SHA256 and compared that to the blob's
remote name, which is the double SHA256 of the plaintext
(blobgen.Writer.Sum256). The two can never be equal, so verify --deep
reported every healthy blob as "blob hash mismatch". The per-chunk and
blob-existence checks were correct; only this final comparison was wrong.
It now hashes the decompressed plaintext as chunk verification streams it
and compares its double SHA256 to the blob name.
Added a test that backs up a real snapshot, deep-verifies it (which fails
before this fix), then flips a byte in one stored blob and confirms deep
verification then fails.
Model: opus-4-8
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
verify --deep's final blob-integrity step hashed the encrypteddownloaded bytes with a single SHA256 and compared that to the blob's
remote name. That name is the double SHA256 of the blob's plaintext
(
internal/blobgen/writer.go,Sum256), so the two values can never beequal and deep verification failed on every healthy blob with
blob hash mismatch. The earlier per-chunk hash checks and blob-existence checkwere correct; only this final comparison was wrong.
The fix hashes the decompressed plaintext instead: a
TeeReaderfeedsthe plaintext into a hasher as chunk verification streams it, and
verifyBlobFinalIntegritycompares the double SHA256 of that plaintextto the blob name — the same derivation the writer uses.
Test:
TestDeepVerifyAcceptsHealthyAndRejectsCorruptBlobbacks up a realsnapshot through the on-disk storage backend, runs
RunDeepVerify(whichfails before this fix), then flips one byte inside a stored blob and
confirms deep verification then fails.
Reader's note: the pre-existing
TestTeeReaderWithDecryptionstill hashesencrypted bytes, but it only exercises
TeeReader+ age + zstd mechanicsand never compares against a blob name, so it stays valid and untouched.
Local
make check(test + Docker lint + fmt-check) is green; CI on thisrunner is known-unreliable (issue
#133).
Closes #131
Model: opus-4-8
PASS
model: claude-opus-4-8