fix(nwsync): close the review findings on the framing fix and verify
ci / ci (pull_request) Successful in 3m38s

- compressBlob now asserts its own output declares a content size, instead of
  trusting that the only way to lose the field is the one #86 found. An encoder
  upgrade that finds another way would otherwise republish the same undecodable
  blob in silence, and a blob is skipped by every later emit once written.
- inspectBlob asserts the frame whenever one is present, rather than only when
  the payload is non-empty.
- dirSink stats instead of reading when not verifying. Reading every existing
  blob back on the default path was a plain regression, and it contradicted the
  documented promise that verifying is a repair pass, not the default.
- The manifest sha1 is now checked as hex before it is interpolated into a URL
  path, rather than only measured.
- One blobKey rule for where a blob lives, replacing three copies of the
  fanout-path expression.
- Renamed frameSizeThreshold to oneByteContentSizeCeiling, which says whose
  threshold it is.
- The read-back in zoneSink reads the storage API on purpose; said so, and
  corrected a comment that claimed a failed read-back re-uploads when it
  aborts.
- Tests derive their expected blob counts from the run instead of hard-coding
  fixture arithmetic, and the size-mismatch category has a test.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-01 00:26:22 +02:00
co-authored by Claude Opus 5
parent 56d4054118
commit 9b7be2c76e
6 changed files with 79 additions and 30 deletions
+4 -3
View File
@@ -103,7 +103,9 @@ type VerifyResult struct {
// are wrong — and a round-trip check alone would pass a frame that omits its
// content size, because Go's decoder is more capable than the client's (#86).
func Verify(options VerifyOptions) (VerifyResult, error) {
if len(options.ManifestSHA1) != 40 {
// The argument is interpolated straight into a URL path, so it is checked
// rather than trusted: exactly 20 bytes of hex, nothing else.
if sum, err := hex.DecodeString(options.ManifestSHA1); err != nil || len(sum) != sha1.Size {
return VerifyResult{}, fmt.Errorf("%q is not a manifest sha1", options.ManifestSHA1)
}
source := options.Source
@@ -198,7 +200,6 @@ func Verify(options VerifyOptions) (VerifyResult, error) {
// checkEntry fetches one blob and returns a one-line fault, or "" if it is
// exactly what the manifest entry says it is.
func checkEntry(source blobSource, entry Entry) string {
key := path.Join("data", "sha1", entry.sha1Hex()[0:2], entry.sha1Hex()[2:4], entry.sha1Hex())
// Name the resource, not just the hash: an operator has to find the thing
// in a hak, and a bare sha1 says nothing about where to look.
extension, ok := erf.ExtensionForResourceType(entry.ResType)
@@ -206,7 +207,7 @@ func checkEntry(source blobSource, entry Entry) string {
extension = strconv.Itoa(int(entry.ResType))
}
where := fmt.Sprintf("%s (%s.%s)", entry.sha1Hex(), entry.ResRef, extension)
blob, err := source.get(key)
blob, err := source.get(blobKey(entry.sha1Hex()))
if err != nil {
if errors.Is(err, errBlobMissing) {
return where + ": missing"