Skip to content

quest: plan proofs and nightly checks for moq-net - #8472

Merged
kixelated merged 1 commit into
mainfrom
quest-plan/net-confidence
Nov 11, 2026
Merged

kixelated merged 1 commit into
mainfrom
quest-plan/net-confidence

Conversation

@kixelated

@kixelated kixelated commented Nov 10, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

The maintainer asked what could be proved about moq-net, which tools would raise confidence in the protocol and its implementation, and how to keep such checks from rotting. They must fit Nightly. Loom was removed in moq-dev/moq#7474 because it never found a race and needed a shim around every primitive.

No tool proves 87k lines of async Rust. Today's checks are four TLA+ models bounded to small runs (moq-dev/moq#8447), codec-level fuzz targets, a narrow Miri run, and seeded sweeps. The gaps: nothing holds for unbounded runs, the hand-rolled route key proof has no analysis, no fuzz target drives a whole session, the Rust and JS codecs are compared only on a hand-written corpus, and only the route model has a planned mechanical tie to its code.

Approach

Goal confirmed with the maintainer: every layer of moq-net has a nightly check that either runs the real code or is mechanically tied to it, and the routing design is proved beyond bounded runs.

A check that runs the real code can fail but cannot rot; a model rots unless something mechanical ties it to the code. So the plan adds real-code checks, and ties every model to its code.

Quest Milestone What it adds
Inductive routing invariant [L] m1 Apalache checks the route rules from any state, on meshes of up to five relays
Route key proof model [M] m1, gates the cut Tamarin proves no replay, reflection, or relaying, and pins what each transport's exporter must guarantee
Whole-session fuzz [L] m1 A hostile peer on moq-lite or moq-transport cannot panic, leak, or disturb a bystander's broadcast
Route trace validation [L] m4 to m1 Existing quest, promoted; its trace format is now built for reuse
Nightly models [M] m1 Existing quest, gains a just check failure when a function a Coverage table names is renamed or deleted
Resume [L], reconnect [M], subscribe [L] trace validation m4 Each model's sweep is checked against its spec, behind route's
Kani codec proofs [M] m4 Varints and length arithmetic proved for every input within a bound
JS replays the fuzz corpus [M] m4 js/net and moq-net agree on every input the Rust fuzzers keep
Mutation testing [L] m4 cargo-mutants on route/ and model/resume.rs, failing on any survivor
Miri seeds and ThreadSanitizer [M] m4 Race checks on kio and moq-net with no shims

Finalize moq-lite-07 now lists the route key model under Required.

Decisions

  • Goal: ✅ layered nightly checks plus one proof for routing (rejected: real-code checks only; verifying the implementation in Verus or Lean).
  • Design-level proofs: ✅ an inductive invariant in Apalache, and the route key proof in Tamarin (rejected for now: a TLAPS proof for every mesh size, until the invariant settles).
  • Real-code checks: ✅ Kani codec proofs, whole-session fuzz, the JS corpus replay, and mutation testing.
  • Concurrency: ✅ Miri across many seeds plus ThreadSanitizer (rejected: Shuttle; loom for kio only; nothing new). Both rejected tools need the shims that got loom removed.
  • Model drift: ✅ route trace validation moves to m1, ✅ a Coverage-table symbol check in just check, ✅ trace validation for the other three models now. The last reverses "route only for now; judge the other models after" from earlier the same day.
  • Milestones: ✅ the invariant, the Tamarin model, and the session fuzz in m1, the rest in m4 (rejected: all m4; all m1).
  • Cut gate: ✅ only the Tamarin model gates the lite-07 cut (rejected: neither; both). The signed transcript freezes at the cut.
  • Mutants: ✅ Nightly fails on any survivor (rejected: report only).
  • Session fuzz: ✅ moq-lite and moq-transport (rejected: moq-lite only).

Chosen without asking, as recommendations in each Plan: no Bolero for Kani, mutant exclusions in .cargo/mutants.toml over #[mutants::skip], and separate quests over an epic, as the existing model quests are flat in m1.

Impact

Public API: none. Wire: none. Quest files only.

Alternatives

  • Exhaustive exploration of the real Router (a model checker over Sim): moq-dev/moq#8447 rejected it for trace validation, and this plan keeps that.
  • Ivy, P, Quint, and Alloy for the design models: a second modelling language beside TLA+ buys nothing the TLA+ tools lack here.
  • OSS-Fuzz, Antithesis, and Jepsen-style runs: see Follow-ups.

Follow-ups

  • Miri's GenMC mode would explore every schedule of kio's log without shims, which is loom without the cost that got it removed. It does not support cargo miri test today. Recommend: plan it as an m5 quest waiting on rust-lang/miri.
  • OSS-Fuzz would fuzz around the clock where Nightly gives each target five minutes. Recommend: plan it once the session fuzz targets exist, since stateful targets gain most from the time; it needs an application to an outside project, so ask first.
  • moq-dev/moq#8452 plans a lite-07 session task leak; the session fuzz target's leak oracle covers that class. Recommend: link the two once both are on main. No new quest.
  • Antithesis and Jepsen-style whole-cluster testing. Recommend: drop; the seeded mesh sweep and the QUIC sim quests already plan deterministic whole-system runs.

Models

Size: M
Implementer: Claude Code, claude-fable-5-2, high effort

🤖 Generated with Claude Code

Review

Rewritten by quest review after each round.

  • Reviewer: Codex, gpt-6.1-sol, medium effort
  • Session: 01a1c7e5-42d4-70b2-aa90-e0b987751646, round 1
  • Head: 6732990
  • Base: origin/main at d7699f8
  • Score: 10/10, The change only adds and reorganizes quest plans, with no runtime changes or actionable defects, and is ready to land as planning documentation.
  • Findings: none

(Written by Claude Fable 5 with high effort)

Adds ten quests from a /quest-plan on what could be proved about moq-net: an inductive routing invariant, a Tamarin model of the route key proof, whole-session fuzzing, Kani codec proofs, a JS replay of the fuzz corpus, mutation testing, Miri seeds with ThreadSanitizer, and trace validation for the resume, reconnect, and subscribe models. Moves route trace validation to m1 and adds a Coverage-table symbol check to the nightly models quest.

Co-Authored-By: Claude <noreply@anthropic.com>
@kixelated
kixelated marked this pull request as ready for review November 10, 2026 23:58
@coderabbitai

coderabbitai Bot commented Nov 10, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 16 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 4 included reviews currently available.

Learn how review limits work.

Review configuration:

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 041a13b5-b221-4ad5-b8cf-8312b787eda7
Commits

Reviewing files that changed from the base of the PR and between d7699f8 and 6732990.

Files selected for processing: 15

  • quest/m1/README.md
  • quest/m1/model-checks-nightly.md
  • quest/m1/route-inductive.md
  • quest/m1/route-key-model.md
  • quest/m1/route-trace-validation.md
  • quest/m1/session-fuzz.md
  • quest/m2/lite07-finalize.md
  • quest/m4/README.md
  • quest/m4/codec-kani.md
  • quest/m4/fuzz-corpus-js.md
  • quest/m4/miri-seeds-tsan.md
  • quest/m4/mutants.md
  • quest/m4/reconnect-trace-validation.md
  • quest/m4/resume-trace-validation.md
  • quest/m4/subscribe-trace-validation.md
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@kixelated
kixelated added this pull request to the merge queue Nov 10, 2026

@kixelated kixelated left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review by review (OpenAI)

Reviewed commit 6732990.

P2 — Correct the varint proof obligations before implementing the harnesses (quest/m4/codec-kani.md:20–24). The stated properties are false for the existing, intentionally supported wire formats: QUIC rejects values above 2^62 - 1, so not every u64 can round-trip through both codecs. Also, a valid non-minimal leading-ones encoding [0x80, 0x25] decodes to 37 but re-encodes as [0x25], which is not a prefix of its input (the existing leading_ones_spec_examples test explicitly covers this). These requirements would make a correct implementation fail the planned proof. Specify round-trip within each form's representable range, rejection without writes outside QUIC's range, and preservation of the decoded value after canonical re-encoding rather than byte-prefix equality.

Overall direction: the layered verification plan and explicit model bounds are sensible; fix the codec proof contract so the future check tests the intended protocol rather than rejecting legal encodings.

Verification limits: GitHub-only static review of all changed plans and targeted implementation checks, including the varint codec/tests and route-proof transcript. No builds, tests, Kani, Tamarin, or TLA+ tools run; this does not establish the proposed proofs.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important

The mutation-testing quest reintroduces work an m0 quest is deleting, without naming the existing weekly workflow, and the cut-gating route key quest sits in m1 where the tree says the cut waits on m2 alone. Both need a sentence of reconciliation before the tree reads consistently.

Reviewed changes

Reviewed the complete 15-file diff at 6732990 end to end and checked its load-bearing claims against the tree: the verify/ specs and rule names, route/proof.rs and js/net/src/lite/route_key.ts, the draft's Route Key Parameter section, the fuzz harness and fixtures, sh/rs/miri.sh, sh/rs/mutants.sh, .cargo/mutants.toml, the Nightly matrix, and every quest link.

  • m1 proofs and checks — route-inductive (Apalache inductive invariant for the route rules from any state), route-key-model (Tamarin proof of the Route Key Parameter, gating the cut), and session-fuzz (whole-session fuzz on both protocols with a bystander broadcast as oracle).
  • Route trace validation promoted — moved from m4 to m1, with its decision log extended to hand its trace format to the other three models.
  • m4 real-code checks — codec-kani (varint and length proofs), fuzz-corpus-js (JS replays the Rust corpus), mutants, and miri-seeds-tsan.
  • m4 trace validation — resume, reconnect, and subscribe quests reusing route's recorder and TLC recipe.
  • Cut gate and drift — lite07-finalize adds the route key model to Required; model-checks-nightly gains a just check failure when a Coverage-table symbol disappears.
  • Indexes — quest/m1/README.md gains four entries; quest/m4/README.md drops the route trace line and adds seven.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using deepseek-v4.1-flash-1017 (free via Pullfrog for OSS) | 𝕏

Comment thread quest/m4/mutants.md

cargo-mutants changes `rs/moq-net/src/route/` and
`rs/moq-net/src/model/resume.rs` one small edit at a time, and the tests fail
on every edit that changes behaviour. Nightly runs it and fails on any

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

quest/m0/ci/trim.md already plans to delete .github/workflows/mutants.yml — its one run failed all 24 shards creating target/mutants, and test/drill/sensitivity.sh already runs mutations nightly — while quest/m3/audit-ci.md records the opposite ("Keep"). This plan never names the existing weekly 24-shard workflow or the current .cargo/mutants.toml scope (which already contains model/resume.rs plus moq-auth, auth.rs, and expiry.rs), so it reads as new work and leaves the tree telling an implementer both to delete the job and to run it nightly.

Technical details
# Reconcile the mutation quest with the m0 workflow trim

## Affected sites
- `quest/m4/mutants.md:7` — "Nightly runs it and fails on any survivor"
- `quest/m4/mutants.md:18` — the same claim as a decision
- `quest/m4/mutants.md:28` — proposes a Nightly matrix with `--shard k/n`, which the weekly job already does
- `quest/m0/ci/trim.md:24` — "Delete `mutants.yml` … This PR deleted its quest"
- `quest/m3/audit-ci.md:215` — the CI audit row says Keep
- `.github/workflows/mutants.yml` — weekly on a 24-shard matrix, already fails on a survivor
- `.cargo/mutants.toml:examine_globs` — already scopes `rs/moq-net/src/model/resume.rs`, `rs/moq-auth/src/**`, `moq-net/src/auth.rs`, `moq-net/src/model/expiry.rs`

## Required outcome
- Say whether this replaces the weekly job (and that m0's deletion is reversed) or keeps it weekly and only widens the scope.
- Say whether the existing `moq-auth`, `auth.rs`, and `expiry.rs` scope stays in the config.
- If reviving the job, name the `target/mutants` creation failure as the first-run prerequisite rather than only "kill or exclude every survivor".

- ✅ Tamarin (rejected: no formal analysis; the proof is a hand-rolled
construction, the kind symbolic provers find reflection and relaying
attacks in).
- ✅ m1 and it gates the cut (rejected: ungated like the TLA+ models; m4).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

quest/README.md says "The cut waits on m2 alone" and quest/m1/README.md says "The cut no longer waits on m1", yet this new m1 quest is listed in the cut's Required and says it "gates the cut". The only other m1 entry in that list (stream-credit/takeover) is held by an open claim, which is the tree's stated mechanism; this one has none. Either move it to m2 (its finding can change the frozen wire) or record the exception in m1's Plan.

Technical details
# A cut gate in m1 has no mechanism

## Affected sites
- `quest/m1/route-key-model.md:20` — "✅ m1 and it gates the cut"
- `quest/m2/lite07-finalize.md:137` — listed under the cut's `Required`
- `quest/README.md:16` and `:59` — "The cut waits on m2 alone"
- `quest/m1/README.md:15` — "The cut no longer waits on m1"
- `quest/m1/README.md:25` — the held-by-open-claims mechanism
- `quest/m2/README.md:30` — held claims are the stated exception

## Required outcome
- The tree no longer states both that the cut waits on m2 alone and that a fresh m1 quest gates it without a claim.

Comment thread quest/m4/codec-kani.md
Comment on lines +20 to +21
- Scope: both varint codecs in `rs/moq-net/src/coding/varint.rs` (every `u64`
encodes and decodes back to itself at the length the draft gives; every

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The QUIC form caps at MAX_QUIC = 2^62 - 1, so "every u64 encodes and decodes back to itself" cannot be a proof obligation for that form, and a harness over kani::any::<u64>() would fail on values above the cap. State each form's admitted range and the round-trip property inside it, next to the existing "prefix of it" decoder property.

Technical details
# The varint proof obligation needs per-form ranges

## Affected sites
- `quest/m4/codec-kani.md:20` — "every `u64` encodes and decodes back to itself at the length the draft gives"
- `rs/moq-net/src/coding/varint.rs:18` — `MAX_QUIC = (1 << 62) - 1`
- `rs/moq-net/src/coding/varint.rs:234` — `decode_quic`, `encode_quic` returns `BoundsExceeded` above the cap
- `rs/moq-net/src/coding/varint.rs:26` — `Form::{Quic, LeadingOnes}`

## Required outcome
- The scope sentence names the admitted range per form (QUIC up to `2^62 - 1`, leading ones up to `u64::MAX`) and the round-trip property within each.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant