Files
alkstore/tasks/fuzz-scaffold.md
T

8.1 KiB

id, name, status, depends_on, scope, risk, impact, level, tags
id name status depends_on scope risk impact level tags
fuzz-scaffold Fuzz sidecar scaffold — `fuzz/` workspace, runner, dict, seed generator, shared-crate skeleton completed
moderate low phase implementation
wave-7
fuzzing

Description

The wave-7 scaffold (docs/research/fuzzing.md §2 — the layout is inherited wholesale from the four upstream siblings, not re-decided): the fuzz/ sidecar workspace and its stable-toolchain gate crate, copied from alksocks' working edition with only naming substitutions.

  • fuzz/ sidecar workspace: fuzz/Cargo.toml (alkstore-fuzz, publish = false, own [workspace] table, libfuzzer-sys + alkstore-fuzz-shared path dep; [[bin]] entries land with the target tasks, not here), fuzz/rust-toolchain.toml pinning channel = "nightly" + llvm-tools-preview inside fuzz/ only — the crates themselves stay on stable, and the root workspace's toolchain behavior must be unaffected.
  • fuzz/shared/ — alkstore-fuzz-shared, the STABLE-toolchain library whose #[cfg(test)] corpus-replay modules are the fuzz gate (§6): skeleton lib.rs with the crate doc carrying the quinn-CI pattern rationale, plus the arbitrary_value.rs JsonValue bounded-depth helper copied from alksocks (the structured-generation path §3.1's targets share). Dependencies: alkstore (path), plus serde_json + arbitrary (derive feature). Engine-crate deps land with the targets that need them. shared consumes public API only — it must never become a dependency of any workspace crate (the wasm note, §2).
  • fuzz/run-detached.sh — copied verbatim from alksocks (CWD-independent, setsid + nohup + full redirection, pid= echo, FUZZ_RUNTIME_SECS override). The §5 isolation rule is inherited unchanged: campaigns never run as a foreground child of an agent session.
  • fuzz/json.dict — copied from a sibling (alksocks has it at fuzz/json.dict).
  • fuzz/gen_fuzz_seeds.py — the deterministic seed generator (quiche pattern): alksocks' script as the structural template, alkstore's own seed functions land per-target. Skeleton lands here with the shared seed_name/write_seed plumbing and an empty target registry.
  • fuzz/.gitignore — the alksocks pattern: target, artifacts, coverage, and corpus/* with !-negations for the committed seed dirs (each target task adds its negation line when its corpus lands).
  • fuzz/README.md — skeleton modeled on alksocks' (layout, targets table filled by the target tasks, commands incl. the run-from-fuzz/-so-rustup-selects-nightly rule).
  • Root Cargo.toml: add exclude = ["fuzz"] to the [workspace] table — defensive insurance against auto-discovery (the alkcall §2 lesson; this repo's explicit-members list already prevents it, the exclude makes it structural). The publish-exclusion verification stays wave 8's (§2); this task lands the mechanism.
  • Corpus dirs: fuzz/corpus/<target>/ scaffolding lands per-target with the seeds; nothing to commit here.

Toolchain prerequisites verified present in the dev environment (2026-10-10): nightly + cargo-fuzz 0.13.2 installed; alksocks' fuzz/Cargo.lock exists as the copy reference.

Acceptance Criteria

  • fuzz/ exists with the §2 layout: sidecar Cargo.toml (own [workspace], publish = false), rust-toolchain.toml (nightly + llvm-tools-preview), shared/ crate, run-detached.sh (executable), json.dict, gen_fuzz_seeds.py, README.md, fuzz/.gitignore
  • fuzz/shared compiles and tests green on stable via cargo test --manifest-path fuzz/shared/Cargo.toml (the gate command works from the wave's first task onward)
  • cargo fuzz build succeeds from fuzz/ (nightly selected by the pin; cargo-fuzz does NOT tolerate zero targets here — a bins-only package with no [[bin]] fails manifest parse, which also blocks the shared gate's workspace discovery; a placeholder target fuzz_targets/placeholder.rs documents the check and is replaced by the wave-1 target tasks — recorded, as this AC asked)
  • Root workspace unaffected: cargo build / cargo test / clippy -D warnings / fmt --check green from the root, and the root toolchain is still stable (the pin did not leak upward)
  • exclude = ["fuzz"] present on the root [workspace] table
  • run-detached.sh is byte-identical to alksocks' modulo the usage-line naming (cmp clean — the usage line is already repo-agnostic, so no diff at all)
  • shared is not referenced by any workspace crate manifest (only the root's exclude = ["fuzz"] mentions fuzz at all; cargo check --target wasm32-unknown-unknown -p alkstore-mem clean)

References

  • docs/research/fuzzing.md §2 (layout + toolchain), §5 (runner), §6 (no-CI / replay gate)
  • /workspace/@alkdev/alksocks/fuzz/ (the copy source: Cargo.toml, rust-toolchain.toml, run-detached.sh, json.dict, gen_fuzz_seeds.py, .gitignore, README.md)
  • docs/plans/implementation.md §Wave 7

Notes

  • Zero targets are NOT tolerated by cargo-fuzz / cargo here — the AC's "if tolerated" branch failed: a bins-only package with no [[bin]] entries cannot manifest-parse ("no targets specified"), and that parse failure also breaks the shared gate's workspace discovery (cargo test --manifest-path fuzz/shared/Cargo.toml resolves the workspace root at fuzz/). Placeholder route taken (as the AC's otherwise-branch prescribed): fuzz_targets/placeholder.rs — a minimal fuzz_target! stub with a doc comment saying the wave-1 target tasks replace it — plus its [[bin]] entry in fuzz/Cargo.toml.
  • Repo-layout substitution beyond naming: alksocks has its package at the repo root (alksocks = { path = "../.." }); alkstore is the ADR-001 workspace split, so the core crate sits in the alkstore/ subdir — shared's dep is alkstore = { path = "../../alkstore" } and the fuzz package's is path = "../alkstore".
  • json.dict is byte-identical across alkcall / alktunnels / alksocks (md5-verified), so it was copied verbatim (alkcall-envelope tokens). Whether any alkstore-specific tokens should be added is a target-task concern, not scaffold scope.
  • fuzz/Cargo.lock is committed (alksocks pattern); generated fresh — alksocks' lock is not portable across dep sets.
  • arbitrary_value.rs copied verbatim except one intra-comment stale reference: alksocks' envelope_frame → "the payload targets".
  • fuzz/.gitignore has no !corpus/<target> negation lines yet — each target task adds its own when its corpus lands (task spec).
  • gen_fuzz_seeds.py is the plumbing-only skeleton (seed_name / write_seed, empty main() registry); runs clean as a no-op.

Summary

Landed the wave-7 scaffold: fuzz/ sidecar workspace (alkstore-fuzz, publish = false, own [workspace], libfuzzer-sys + path-dep shared crate) with rust-toolchain.toml pinning nightly + llvm-tools-preview inside fuzz/ only; stable-toolchain fuzz/shared (alkstore-fuzz-shared) with the quinn-CI-pattern crate doc and the alksocks arbitrary_value.rs JsonValue bounded-depth helper; run-detached.sh (byte-identical to alksocks', executable); json.dict; gen_fuzz_seeds.py skeleton; .gitignore (no corpus negations yet); README.md skeleton; exclude = ["fuzz"] on the root workspace; and the placeholder fuzz target so the toolchain check and the shared gate work before the real targets land.

Verified: cargo test --manifest-path fuzz/shared/Cargo.toml green on stable from the root; cargo fuzz build green from fuzz/ (nightly pin selected; placeholder + shared + alkstore compile under ASAN); shared clippy -D warnings + fmt --check green; root cargo build / cargo test (all suites pass) / clippy -D warnings / fmt --check green with the stable toolchain still active at the root (rustc 1.99.0 stable; no upward pin leak); cargo check -p alkstore-mem --target wasm32-unknown-unknown clean; no workspace manifest references alkstore-fuzz-shared (only the root exclude mentions fuzz).