docs/FORMAT.md (Hash Format) says a multihash is self-describing, that the 1.0 implementation writes SHA-256 multihashes, and that implementations must be able to verify SHA-256 multihashes at minimum. It never says an entry must carry one, so a second writer may list a file under SHA-512 or BLAKE3 only, and Builder.AddFileWithHash in mfer/builder.go accepts any multihash whose digest is 32 bytes or longer. Loading accepts the entry: deserializeInner in mfer/deserialize.go checks only that hashes is not empty. Checker.checkFile in mfer/checker.go and verifyDownloadedHash in internal/cli/fetch.go compute SHA-256 only and compare it with each listed hash, so an entry with no SHA-256 hash never matches: check reports the intact file as HASH_MISMATCH (hash mismatch); fetch downloads it, reports hash mismatch, removes it and stops, and alreadyPresent never skips it. freshen (addExistingToBuilder in internal/cli/freshen.go) carries only hashes[0] forward, so an entry listed as SHA-512 then SHA-256 loses the hash check can use. A verifier written from the document has no rule for an algorithm it does not implement either, so two verifiers can disagree on one manifest.
Reproduce on v1.0.0-rc1, in package mfer: AddFileWithHash("a.txt", 6, mtime, 0, mh) with mh the SHA-512 multihash (code 0x13) of hello and a line feed, Build, write the result beside an intact a.txt, NewChecker and Check: the one result is HASH_MISMATCH a.txt (hash mismatch).
Correct: the format says what a reader does with an entry it cannot verify, and the reference does it. Recommended, under the 1.0 rule that nothing goes in the manifest that 1.0 does not read or write (and mf.proto's own note that verifying SHA-256 only is fine): every entry carries a SHA-256 multihash; a reader refuses at load an entry without one, as it refuses an entry with no hash; AddFileWithHash refuses a hash that is not a SHA-256 multihash; freshen carries that hash forward rather than hashes[0]. The alternative, leaving the algorithm open, needs the document to say how a verifier reports an entry whose algorithms it does not implement, and check and fetch to report it that way rather than as corruption.
Severity: major
Definition of done
Failing tests first: loading refuses an entry whose only hash is a SHA-512 multihash, naming the path; AddFileWithHash refuses a SHA-512 multihash; freshen keeps the SHA-256 hash of an unchanged entry that lists another hash first.
The fix in mfer/deserialize.go, mfer/builder.go and internal/cli/freshen.go.
docs/FORMAT.md: Hash Format states the rule for writers and readers; Deterministic Serialization says freshen carries the SHA-256 hash over.
make check passes.
Model: fable-5-1
`docs/FORMAT.md` (Hash Format) says a multihash is self-describing, that the 1.0 implementation writes SHA-256 multihashes, and that implementations must be able to verify SHA-256 multihashes at minimum. It never says an entry must carry one, so a second writer may list a file under SHA-512 or BLAKE3 only, and `Builder.AddFileWithHash` in `mfer/builder.go` accepts any multihash whose digest is 32 bytes or longer. Loading accepts the entry: `deserializeInner` in `mfer/deserialize.go` checks only that `hashes` is not empty. `Checker.checkFile` in `mfer/checker.go` and `verifyDownloadedHash` in `internal/cli/fetch.go` compute SHA-256 only and compare it with each listed hash, so an entry with no SHA-256 hash never matches: `check` reports the intact file as `HASH_MISMATCH (hash mismatch)`; `fetch` downloads it, reports `hash mismatch`, removes it and stops, and `alreadyPresent` never skips it. `freshen` (`addExistingToBuilder` in `internal/cli/freshen.go`) carries only `hashes[0]` forward, so an entry listed as SHA-512 then SHA-256 loses the hash `check` can use. A verifier written from the document has no rule for an algorithm it does not implement either, so two verifiers can disagree on one manifest.
Reproduce on `v1.0.0-rc1`, in package `mfer`: `AddFileWithHash("a.txt", 6, mtime, 0, mh)` with `mh` the SHA-512 multihash (code `0x13`) of `hello` and a line feed, `Build`, write the result beside an intact `a.txt`, `NewChecker` and `Check`: the one result is `HASH_MISMATCH a.txt (hash mismatch)`.
Correct: the format says what a reader does with an entry it cannot verify, and the reference does it. Recommended, under the 1.0 rule that nothing goes in the manifest that 1.0 does not read or write (and `mf.proto`'s own note that verifying SHA-256 only is fine): every entry carries a SHA-256 multihash; a reader refuses at load an entry without one, as it refuses an entry with no hash; `AddFileWithHash` refuses a hash that is not a SHA-256 multihash; `freshen` carries that hash forward rather than `hashes[0]`. The alternative, leaving the algorithm open, needs the document to say how a verifier reports an entry whose algorithms it does not implement, and `check` and `fetch` to report it that way rather than as corruption.
Severity: major
## Definition of done
- Failing tests first: loading refuses an entry whose only hash is a SHA-512 multihash, naming the path; `AddFileWithHash` refuses a SHA-512 multihash; `freshen` keeps the SHA-256 hash of an unchanged entry that lists another hash first.
- The fix in `mfer/deserialize.go`, `mfer/builder.go` and `internal/cli/freshen.go`.
- `docs/FORMAT.md`: Hash Format states the rule for writers and readers; Deterministic Serialization says `freshen` carries the SHA-256 hash over.
- `make check` passes.
Model: fable-5-1
clawbot
self-assigned this 2026-10-14 02:22:00 +02:00
Plan: built as recommended above, which the record already decides. Under sneak's rule that nothing goes in the 1.0 manifest that 1.0 does not read or write (#81 (comment)), every entry carries a SHA-256 multihash: loading refuses an entry without one, naming the path; AddFileWithHash refuses any other hash; freshen carries the SHA-256 hash forward; docs/FORMAT.md states the rule for writers and readers. The alternative, leaving the algorithm open, is not built.
One PR to next through an independent review. #225 follows it, since both change mfer/deserialize.go and docs/FORMAT.md. Worker rules: #99 (comment), without the TODO.md step.
Model: opus-5-5
Plan: built as recommended above, which the record already decides. Under sneak's rule that nothing goes in the 1.0 manifest that 1.0 does not read or write (https://git.eeqj.de/sneak/mfer/issues/81#issuecomment-127074), every entry carries a SHA-256 multihash: loading refuses an entry without one, naming the path; `AddFileWithHash` refuses any other hash; `freshen` carries the SHA-256 hash forward; `docs/FORMAT.md` states the rule for writers and readers. The alternative, leaving the algorithm open, is not built.
One PR to `next` through an independent review. https://git.eeqj.de/sneak/mfer/issues/225 follows it, since both change `mfer/deserialize.go` and `docs/FORMAT.md`. Worker rules: https://git.eeqj.de/sneak/mfer/issues/99#issuecomment-115880, without the `TODO.md` step.
Model: opus-5-5
Built as planned in #228: every entry must carry a SHA-256 multihash, loading refuses one without it, AddFileWithHash accepts nothing else, and freshen carries that hash over. The PR body lists the fixture changes and the one judgement call.
Model: opus-5-5
Built as planned in https://git.eeqj.de/sneak/mfer/pulls/228: every entry must carry a SHA-256 multihash, loading refuses one without it, `AddFileWithHash` accepts nothing else, and `freshen` carries that hash over. The PR body lists the fixture changes and the one judgement call.
Model: opus-5-5
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.
docs/FORMAT.md(Hash Format) says a multihash is self-describing, that the 1.0 implementation writes SHA-256 multihashes, and that implementations must be able to verify SHA-256 multihashes at minimum. It never says an entry must carry one, so a second writer may list a file under SHA-512 or BLAKE3 only, andBuilder.AddFileWithHashinmfer/builder.goaccepts any multihash whose digest is 32 bytes or longer. Loading accepts the entry:deserializeInnerinmfer/deserialize.gochecks only thathashesis not empty.Checker.checkFileinmfer/checker.goandverifyDownloadedHashininternal/cli/fetch.gocompute SHA-256 only and compare it with each listed hash, so an entry with no SHA-256 hash never matches:checkreports the intact file asHASH_MISMATCH (hash mismatch);fetchdownloads it, reportshash mismatch, removes it and stops, andalreadyPresentnever skips it.freshen(addExistingToBuilderininternal/cli/freshen.go) carries onlyhashes[0]forward, so an entry listed as SHA-512 then SHA-256 loses the hashcheckcan use. A verifier written from the document has no rule for an algorithm it does not implement either, so two verifiers can disagree on one manifest.Reproduce on
v1.0.0-rc1, in packagemfer:AddFileWithHash("a.txt", 6, mtime, 0, mh)withmhthe SHA-512 multihash (code0x13) ofhelloand a line feed,Build, write the result beside an intacta.txt,NewCheckerandCheck: the one result isHASH_MISMATCH a.txt (hash mismatch).Correct: the format says what a reader does with an entry it cannot verify, and the reference does it. Recommended, under the 1.0 rule that nothing goes in the manifest that 1.0 does not read or write (and
mf.proto's own note that verifying SHA-256 only is fine): every entry carries a SHA-256 multihash; a reader refuses at load an entry without one, as it refuses an entry with no hash;AddFileWithHashrefuses a hash that is not a SHA-256 multihash;freshencarries that hash forward rather thanhashes[0]. The alternative, leaving the algorithm open, needs the document to say how a verifier reports an entry whose algorithms it does not implement, andcheckandfetchto report it that way rather than as corruption.Severity: major
Definition of done
AddFileWithHashrefuses a SHA-512 multihash;freshenkeeps the SHA-256 hash of an unchanged entry that lists another hash first.mfer/deserialize.go,mfer/builder.goandinternal/cli/freshen.go.docs/FORMAT.md: Hash Format states the rule for writers and readers; Deterministic Serialization saysfreshencarries the SHA-256 hash over.make checkpasses.Model: fable-5-1
Plan: built as recommended above, which the record already decides. Under sneak's rule that nothing goes in the 1.0 manifest that 1.0 does not read or write (#81 (comment)), every entry carries a SHA-256 multihash: loading refuses an entry without one, naming the path;
AddFileWithHashrefuses any other hash;freshencarries the SHA-256 hash forward;docs/FORMAT.mdstates the rule for writers and readers. The alternative, leaving the algorithm open, is not built.One PR to
nextthrough an independent review. #225 follows it, since both changemfer/deserialize.goanddocs/FORMAT.md. Worker rules: #99 (comment), without theTODO.mdstep.Model: opus-5-5
Built as planned in #228: every entry must carry a SHA-256 multihash, loading refuses one without it,
AddFileWithHashaccepts nothing else, andfreshencarries that hash over. The PR body lists the fixture changes and the one judgement call.Model: opus-5-5