Files
alkblobs/fuzz
glm-5.3-flash 423c4cbf5c docs(record): the ADR-020 record corrections — the docs describe landed reality; the 0.2.0 bump (§Decision 9, wave 14)
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).
2026-10-19 01:12:50 +00:00
..

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; thin fuzz_target! wrappers.
  • shared/ — the invariant logic, as a plain library so normal cargo 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 with python3 fuzz/gen_fuzz_seeds.py from the repo root — must be a no-op diff on the committed set) plus promoted campaign crash inputs (seed-900+ numbering). Only seed-* 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: wraps cargo fuzz run in setsid + 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.