Skip to content
zit Check-in [b8db5ecabd]
Login

Check-in [b8db5ecabd]

Overview
Comment:Merge release-0.1: Add transport_frame_garbage and serve_request_garbage seeded fuzz targets

Lands P4-F6, closing the last unfuzzed attack surface in the release scope. transport.zig and serve.zig are the only two files a remote peer can reach with bytes it fully controls, and neither had a fuzz target: the sync_parsers_adversarial target covers the layer above them, so the HTTP framing itself — the first code to touch untrusted input — was exercised only by hand-written smoke cases. These two targets hit that framing directly.

The increment: src/transport_fuzz.zig (205 L) and src/serve_fuzz.zig (303 L), registered in the fuzz_tests.zig dispatch table alongside the existing 24 and pinned by that file's registration test. serve.zig's driveHttp is promoted to pub with a comment stating why — it is the receiveHead to handleHttpRequest seam the smoke tests and the fuzz target both need, and duplicating it would have meant fuzzing a copy of the request path rather than the real one. Both sides stay in-memory, so neither target binds a port and neither can flake in CI. dst.zig picks up the two targets plus fuzz_tests.zig itself, the latter so the registration pin has a test step to run in: zig build fuzz has none.

Evidence, re-verified on the merged tree rather than on the branch, since trunk had advanced: zig fmt --check ., zig build, and zig build test each exit 0. Both new targets run clean seeded — invoking zig build fuzz with each target name and --seed 1 exits 0 for transport_frame_garbage and for serve_request_garbage — and found no crash, leak, or UB to fix, so this step carries no pinned-seed regression. The merge reported no conflicts; a repo-wide grep for conflict markers is clean, and fossil extras is empty, so both added files are tracked and nothing referenced went unadded.

Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: b8db5ecabd77e5b9e3eba8cca12116f89545169b2e14ee3c6b82ba868684eecf
User & Date: dmestas on 2026-07-19 00:17:51.023
Other Links: manifest | tags
Context
2026-07-19
00:29
CI: target the self-hosted macOS runner instead of a hosted Linux one

GitHub-hosted runners are unavailable to this repository: it is private, and private-repo Actions minutes are billed, so the first hosted run never started — 'recent account payments have failed or your spending limit needs to be increased', zero steps executed. The gate now runs on the self-hosted mac-mini-zit (macOS/ARM64), which is online and already carries the toolchain.

- runs-on names the specific machine ([self-hosted, mac-mini-zit]) rather than bare self-hosted, so a second runner joining the account cannot silently pick this job up on a different OS or architecture. - The Zig tarball is the aarch64-macos build with its published digest from ziglang.org/download/index.json. macOS ships shasum rather than GNU sha256sum, so the digest is compared explicitly; a swapped tarball still fails the job rather than building silently. - fossil is verified rather than apt-installed — the runner is a developer machine carrying 2.28, newer than what apt ships. The step hard-fails when fossil is absent, because a skipped smoke step would silently stop exercising the conformance claim the release rests on.

src/ci_test.zig moves in lockstep: the runs-on line, the digest constant, the digest-check form, and the fossil-guard pin all now assert the macOS shape. Pins and workflow must change together or zig build test goes red, which is the point of having them. check-in: c8cccb2723 user: dmestas tags: trunk

00:17
Merge release-0.1: Add transport_frame_garbage and serve_request_garbage seeded fuzz targets

Lands P4-F6, closing the last unfuzzed attack surface in the release scope. transport.zig and serve.zig are the only two files a remote peer can reach with bytes it fully controls, and neither had a fuzz target: the sync_parsers_adversarial target covers the layer above them, so the HTTP framing itself — the first code to touch untrusted input — was exercised only by hand-written smoke cases. These two targets hit that framing directly.

The increment: src/transport_fuzz.zig (205 L) and src/serve_fuzz.zig (303 L), registered in the fuzz_tests.zig dispatch table alongside the existing 24 and pinned by that file's registration test. serve.zig's driveHttp is promoted to pub with a comment stating why — it is the receiveHead to handleHttpRequest seam the smoke tests and the fuzz target both need, and duplicating it would have meant fuzzing a copy of the request path rather than the real one. Both sides stay in-memory, so neither target binds a port and neither can flake in CI. dst.zig picks up the two targets plus fuzz_tests.zig itself, the latter so the registration pin has a test step to run in: zig build fuzz has none.

Evidence, re-verified on the merged tree rather than on the branch, since trunk had advanced: zig fmt --check ., zig build, and zig build test each exit 0. Both new targets run clean seeded — invoking zig build fuzz with each target name and --seed 1 exits 0 for transport_frame_garbage and for serve_request_garbage — and found no crash, leak, or UB to fix, so this step carries no pinned-seed regression. The merge reported no conflicts; a repo-wide grep for conflict markers is clean, and fossil extras is empty, so both added files are tracked and nothing referenced went unadded. check-in: b8db5ecabd user: dmestas tags: trunk

00:10
Add transport_frame_garbage and serve_request_garbage seeded fuzz targets

The 24 existing fuzz targets cover the delta, artifact, sync-parser and logstore layers, but not the two CLI-layer modules that consume bytes an untrusted remote peer chose. `sync_parsers_adversarial` covers the layer above; the HTTP framing itself was unfuzzed. These are the last two wire surfaces on the release bar, and they are what a hostile peer reaches first.

What landed:

- `src/transport_fuzz.zig` — `frameGarbage`, registered as `transport_frame_garbage`. Client-side §4/§4.1 framing and redirect policy: the round-trip law (whatever `frame` produces, `unframe` returns exactly those plaintext bytes), mutation of framed containers (bit damage never survives as *different* plaintext — it either fails the container's Adler-32/zlib checks or, in the length-field range the §4.1 compression-length erratum makes a capacity bound, decodes back to the same bytes), and garbage `Location` headers through `resolveRedirect`. Totality is the asserted contract: every input yields a value whose postconditions hold or a declared member of `TransportError`.

- `src/serve_fuzz.zig` — `requestGarbage`, registered as `serve_request_garbage`. Server-side HTTP request path: raw bytes into `std.http.Server.receiveHead`, then `handleHttpRequest`'s dispatch (`classifyXfer` -> body read -> responder -> reply framing). It does not re-feed `handleXfer` in isolation — that decoding is already fuzzed twice — it reaches it only as the tail of a full-path dispatch, which is the point. Asserted contract: any `receiveHead` error is a clean rejection, `handleHttpRequest` returns void, and any produced response begins with an HTTP/1.1 status line.

- `src/serve.zig`: `driveHttp` becomes `pub`. It is the request-path seam two callers need and neither should reimplement — the §4 smoke tests and this fuzz target. In-memory reader/writer on both sides, so the target binds no port and cannot flake in CI.

- `src/fuzz_tests.zig`: both targets registered in the `targets` table, and `targets` becomes `pub` for a new structural registration pin. An unregistered target compiles, passes review, and is then never run by any invocation; the pin asserts every target the release bar names is actually reachable from the runner, so the registration cannot rot.

- `src/dst.zig`: aggregates both new files plus `fuzz_tests.zig` into `zig build test`. This root is the one that reaches serve.zig and transport.zig relatively while carrying the `zit` dep those two import; `zig build fuzz` has no test step, so it is also the only place the registration pin can run.

No bug found, so no fix and no pinned-seed regression in this commit — both surfaces held over the seeded runs below. The targets are the standing check, not a one-time sweep.

Test evidence (this worktree, after `fossil update` absorbed the sibling `zit open` commit 5ba418801d and its 8 modified files):

- `zig build fuzz -- transport_frame_garbage --seed 1 --rounds 2000` — clean, no crash/leak/UB. - `zig build fuzz -- serve_request_garbage --seed 1 --rounds 2000` — clean, no crash/leak/UB. - `zig build test` — green, including the new registration pin and the fixed-seed smoke tests at the foot of both new files. - `zig build` and `zig fmt --check .` — green.

No borrowed technique in this change. check-in: cec4afaf89 user: dmestas tags: release-0.1

2026-07-18
23:19
Merge release-0.1: zit open — the clone-to-working-tree path (P3-F3)

Lands the last of the three P3 CLI cliffs. A clone previously produced only an artifact log: there was no verb that turned one into a working tree, so clone-to-usable-checkout was unreachable and unproven by any test. `zit open` closes that path, resolving the trunk tip and materializing the check-in into the target directory.

The merged tree — not merely the branch — was re-verified on trunk after the merge: zig fmt --check ., zig build, and zig build test all exit 0. The merge reported no conflicts and touched six already-tracked files; fossil extras is empty, so nothing referenced went unadded. The conflict-marker byte sequences that a repo-wide grep reports in src/merge3.zig and src/e2e.zig are that module's own conflict sentinel and the tests asserting on it, not merge residue.

Landed separately from the sibling status increment (3b7fe2fbe1) rather than folded into one commit: the two are independent release steps and each keeps its own subject and evidence. check-in: 91c682fd06 user: dmestas tags: trunk

Changes