Code-side record corrections, last of the ADR-020 implementation
round's tasks: no current-surface document advertises a deleted shape
or the superseded wire posture.
- README.md: the opening hash description restated around put_as and
family-explicit Key::from_bytes construction (the wave-13 gate's
put_git_object / from_git_oid_hex / documented-scoping phrasing
dropped); the body's Store::put reads and the quick-start fence moved
to the one seam (matching tests/readme_examples.rs, already on the
seam); the docs bullet's ADR range 001-020.
- fuzz/README.md: the key_encoding target's surface shrunk to the
landed cluster (from_bytes/as_bytes, the Algorithm registry mapping,
the explicit Algorithm::derive) — the from_git_oid_hex / Key::derive
/ Digest::derive entries and the length-selects-family hex-posture
clause dropped; the invariant list aligned with fuzz/shared's own
doc header.
- tests/conformance/README.md: INV-1's pinning text names put_as.
- ops-surface.md / store-api.md / hashing-and-keys.md: already
described the landed shapes exactly (their WHAT text rode the
implementation commits); the "Reviewed pending the implementation
round's verification" banners flipped to landed (verified by the
wave-14 pinning test, the conformance family legs, and the fuzz
key_encoding invariants).
- CHANGELOG.md: the [0.2.0] entry — the wire break (tag-prefixed key
hex, bare hex dead, mandatory put claims, the {token, key} response)
and the facade break (put, put_git_object, from_git_oid_hex,
Digest::derive, Key::derive removed; put_as added), the ADR-020 ref,
and the refreshed Verified counts.
- Cargo.toml: version = "0.2.0" (no shim, no dual-form period);
fuzz/Cargo.lock follows the bump.
Verification: cargo test (default) 219 green (unit 156, conformance
54, git-interop 7, README examples 2) and cargo test --all-features
against the live pg harness 476 green, both clippy variants
(-D warnings), cargo fmt --check, cargo doc --no-deps,
cargo publish --dry-run --allow-dirty (0.2.0 packages), and the fuzz
corpora replay gate 7/7 — all green.
Task: tasks/record-corrections-adr-020.md (status: completed).
alkblobs fuzzing
libFuzzer targets for alkblobs' parse surfaces (see docs/plans/implementation.md §Wave 11 for the family pattern and target inventory).
Layout
fuzz_targets/— one binary per target; thinfuzz_target!wrappers.shared/— the invariant logic, as a plain library so normalcargo test(stable toolchain) can replay the committed corpora through the same invariants (fuzz/shared/src/*.rs#[cfg(test)]modules; quinn's CI pattern). The fuzz binaries are nightly-only; the shared crate is stable-clean. It consumes alkblobs' public API only and must never become a dependency of the root crate.corpus/<target>/— committed hand-made seeds (regenerate withpython3 fuzz/gen_fuzz_seeds.pyfrom the repo root — must be a no-op diff on the committed set) plus promoted campaign crash inputs (seed-900+ numbering). Onlyseed-*files are tracked; grown corpora (SHA-named units), artifacts, and coverage output are gitignored and are not merged back into the committed seeds.run-detached.sh— mandatory runner for agent sessions: wrapscargo fuzz runinsetsid+nohup+ log redirection so an OOM in a target can never take down the agent host.json.dict— JSON token dictionary for the structured targets.gen_fuzz_seeds.py— deterministic seed generator (quiche pattern).
Targets
| Target | Drives | Invariants |
|---|---|---|
key_encoding |
Key::from_bytes / Key::as_bytes (src/keys.rs), Algorithm::try_from_byte / byte / digest_len, Algorithm::derive (src/hashing.rs) — entire public API of the one-way door's parser cluster (ADR-020 §Decision 5: the length-routed from_git_oid_hex and the silent constructor wrappers are gone; every derivation is explicit) |
no-panic on any byte sequence and any string (lossy-decoded and arbitrary-structured alike); every from_bytes failure exactly Error::KeyInvalid (rejects, never guesses — truncated, over-long, reserved/unknown tags, tag/digest-length mismatches, wrong-length, uppercase and non-hex hex); round-trip closure (accepted keys re-encode byte-identically, derived keys decode back); double-invocation determinism on the parser; the derived Ord sort equals the as_bytes byte sort over arbitrary accepted-key collections with reparses staying accepted (ADR-002's sort-stability clause); decoded length == digest_len per tag; registry agreement (byte(try_from_byte(b)) == b for every accepted byte, reserved 0x00 and all unknown bytes reject — full byte-space sweep); derive determinism with the git-computed known-answer vectors pinned (blob family and git-object family both) |
ops_ingress |
fetch_ranges_ingress + FetchRange/RangedBounds (the ranged-fetch bounds ingress, review-001 S-2 — via the #[doc(hidden)] fuzz/test re-exports of the exact production function), PinToken::from_hex (the renewal token ingress, review-001 S-5, src/ops/tokens.rs) |
ranges: no-panic on any Value (raw-byte serde_json parses plus arbitrary-derived JsonValues); the absent-field contract (no ranges field → Ok(None), the whole-form fetch); item-shape rejects (non-array ranges; items missing offset/len or carrying non-u64 shapes — negatives, floats, strings, nulls, nested) → Err with the stable domain text, never a panic, never Ok; bound arithmetic (checked_add overflow of the running sum → Err; a sum over max_total_bytes → Err carrying the wire numbers; a sum within the bound → Ok(Some) with every range's (offset, len) preserved exactly and in order); boundary exactness (total == bound accepts, bound + 1 rejects); the count bound is not this ingress's duty (an over-count array within the bytes bound parses Ok — the maxItems gate lives at the schema, alkcall's; pinned honestly, not asserted away); determinism. tokens: no-panic on any string (lossy-decoded and arbitrary-derived alike); length-before-decode (any length ≠ 64 → None, the 1 MiB canary pins the rejection); non-hex 64-char → None; case-insensitive decode agreement with the wire schema's pattern (upper == lower == Some, same bytes); round-trip closure (to_hex ∘ from_hex on accepted tokens); determinism |
Commands
cargo fuzz build and cargo fuzz run must be executed with fuzz/
(or deeper) as the working directory so rustup selects the pinned
nightly toolchain — the detached runner handles that itself.
# build (nightly, pinned by rust-toolchain.toml inside fuzz/; run from fuzz/)
cargo fuzz build
# agent sessions: detached campaign (never foreground; CWD-independent)
FUZZ_RUNTIME_SECS=600 fuzz/run-detached.sh <target> [extra libfuzzer args...]
# corpus replay through the invariants (stable toolchain, no nightly)
cargo test --manifest-path fuzz/shared/Cargo.toml
# coverage
cargo fuzz coverage <target>
The fuzz workspace is excluded from the main workspace (exclude in
the root Cargo.toml); it pins its own nightly toolchain via
rust-toolchain.toml and does not affect the root workspace's stable
toolchain or MSRV.