Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion quest/m0/noq-reassembly-cap.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ the crates are renamed.
and #22: fold the port into those releases or take the next patch numbers,
then bump the pins here.
- Open the same port as a PR on n0-computer/noq (approved 2026-09-29; the
advisory is public). iroh builds stay on the unfixed upstream crate until
advisory is public; opened as
[noq#828](https://github.com/n0-computer/noq/pull/828)). iroh builds stay on the unfixed upstream crate until
n0 releases it; bump when they do.
- Give `moq-tokio` a finite default connection `receive_window` instead of
the backend's `VarInt::MAX` (`rs/moq-tokio/src/noq.rs` `apply_windows`,
Expand All @@ -32,6 +33,10 @@ the crates are renamed.
- `rs/moq-uring` depends on `moq-noq-proto` too, so the same pin bump
covers the io_uring workers.

The in-tree [fork](/quest/m1/quic/fork/README.md) inherits the cap from
quinn main, so after it lands only the receive-window default and iroh's noq
pin remain relevant on `dev`.

Public API: none. Wire: a peer that exceeds the chunk cap is closed.

## Related
Expand Down
2 changes: 1 addition & 1 deletion quest/m1/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ QUIC studies there on that rule.
- [Audio rendition pick](/quest/m1/audio-ranked.md) - single-track FLV/RTMP and WHEP serve the best audio rendition, not the first by name
- [FLV catalog stream](/quest/m1/flv-catalog-stream.md) - on dev, `flv::Export` takes a catalog stream like fmp4, replacing `with_select`
- [io_uring flow control](/quest/m1/uring-flow-control-windows.md) - the relay's io_uring workers honor the QUIC flow-control windows instead of refusing them
- [Own the QUIC stack](/quest/m1/quic/README.md) - the moq-noq fork carries ACK progress, reliable reset, hierarchical scheduling, deadlines, peer limits, and qmux
- [Own the QUIC stack](/quest/m1/quic/README.md) - quinn hard-forked in-tree as `moq-quic`, carrying BBR, reliable reset, hierarchical scheduling, peer limits, and endpoint sharding
- [QoS](/quest/m1/qos/README.md) - broadcast health: relay starvation and timeliness histograms, on dev
- [Catalog track alias](/quest/m1/catalog-track-alias.md) - catalog rendition keys become aliases with an optional `track` name, so one catalog lists renditions from several broadcasts
- [Media stats](/quest/m1/stats/README.md) - publishers announce a stats track in the catalog, viewers answer a soliciting catalog through a per-catalog `.echo` broadcast, and a Rust encoder adapts to them
Expand Down
4 changes: 4 additions & 0 deletions quest/m1/bbr-ack-cleanup.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ immutable fork release, and pin the corrected dependency chain here before
completing this quest. Do not wait for the broader QUIC stack release. Update internal packet-lifetime comments inline; no new user guide
is needed.

## Required

- [Hard fork](/quest/m1/quic/fork/README.md) - lands in `moq-quic` on `dev`, not the frozen fork

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Retarget the ACK cleanup steps from the frozen fork

The newly added requirement is fresh evidence that this quest is supposed to land in moq-quic, but its executable plan still says the fix lives in moq-dev/noq and requires publishing and pinning an immutable fork release at lines 13 and 51-53. When this m1 quest is started, following those completion criteria sends the work to the repository this change freezes rather than the in-tree crate, so retarget or delete those steps instead of relying on the parent README's reinterpretation.

AGENTS.md reference: AGENTS.md:L27-L28

Useful? React with 👍 / 👎.


## Related

- [BBR starvation edges](/quest/m1/quic/bbr-app-limited-edges.md) - also edits `bbr3/mod.rs`; one owner there at a time
Expand Down
1 change: 1 addition & 0 deletions quest/m1/perf/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,4 @@ the rest.
- [Ingest batch](/quest/m1/perf/ingest-batch.md) - relay ingest pays one lock, wake, and clock read per chunk burst instead of per chunk
- [#3122](/quest/m1/perf/3122-moq-uring-2-5-of-relay-cpu-is-vdso-clock-reads-the-drive.md) - moq-uring: ~2.5% of relay CPU is vdso clock reads; the drive loop and its callers each re-read Instant::now()
- [#3199](/quest/m1/perf/3199-moq-uring-remove-sq-indirection-and-per-enter-ring-fd.md) - moq-uring: remove SQ indirection and per-enter ring fd lookup
- [Remove moq-uring copies](/quest/m1/perf/uring-copies.md) - egress, stream send and receive, and datagram receive stop copying where the QUIC core already allows it, on `dev` after the fork
31 changes: 31 additions & 0 deletions quest/m1/perf/uring-copies.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# [S] Remove moq-uring's avoidable copies

## Goal

moq-uring stops copying and allocating on paths where the QUIC core already
supports zero-copy, with a measured before/after.

## Plan

Found in the 2026-09-30 fork planning; none needs a QUIC API change:

- Egress: `quic/noq/connection.rs` copies each GSO train from `scratch` into
the TX buffer. Hand the TX pool's `Vec` to `poll_transmit` directly; it
encrypts in place and only reserves when capacity is short.
- Stream send: moq-net's `write_chunk(Bytes)` falls back to the trait's
default `poll_write_buf`, which copies through `poll_write(&[u8])`. Route
it to the core's `write_chunks` so large chunks are kept by reference.
- Stream receive: moq-uring uses the trait's default `poll_read_chunk`, which
allocates an 8 KiB `BytesMut` and copies each read. Return the core's
`Bytes` directly.
- Datagram receive: `endpoint.rs` allocates a `BytesMut` per GRO segment.
Copy once per completion and split it into segments.

The clock-read fix stays in
[#3122](/quest/m1/perf/3122-moq-uring-2-5-of-relay-cpu-is-vdso-clock-reads-the-drive.md).
Measure with the echo benches and `just bench`. This branches from `dev`,
where the fork lands; paths above are pre-fork names.

## Required

- [Hard fork](/quest/m1/quic/fork/README.md) - the copies are removed on `moq-quic`, not the frozen fork
70 changes: 31 additions & 39 deletions quest/m1/quic/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,48 +2,42 @@

## Goal

MoQ owns the QUIC features it needs. noq, the Quinn-derived stack n0
maintains for Iroh, is the parent; the moq-dev fork carries what MoQ needs on
MoQ's schedule and offers it upstream when it is general. One core serves the
tokio backend, the thread-per-core `moq-uring` backend, iroh, and qmux. The
features are BBR correctness, reliable stream resets, hierarchical stream
scheduling with per-broadcast fairness, and wider limits for relay peers.
MoQ owns the QUIC features it needs in an in-tree stack, `moq-quic`, hard
forked from quinn (see [the fork](/quest/m1/quic/fork/README.md) for why
quinn and not noq). One core serves the tokio backend, the thread-per-core
`moq-uring` backend, and qmux; iroh stays on upstream noq. The features are
BBR correctness, reliable stream resets, hierarchical stream scheduling with
per-broadcast fairness, wider limits for relay peers, and endpoint sharding.
Per-stream acknowledgment progress, per-stream deadlines, qmux on the shared
stream state machine, and the experiments (GCC, receive timestamps, the egress
profile, media probing, L4S, careful resume, deadline keep-alive) live in
[m2](/quest/m2/README.md) and do not gate this line.

## Plan

Everything here ships from moq-dev/noq, the fork MoQ publishes as `moq-noq*`.
One stack carries every change on MoQ's own QUIC paths; a build with the `iroh`
feature also compiles upstream noq, and iroh connections are outside what these
quests reach.
Decided in the 2026-09-30 plan: the moq-dev/noq soft fork is replaced by a
hard fork of quinn in `rs/`, so every QUIC change here waits for
[the fork](/quest/m1/quic/fork/README.md) and lands on `dev`. Retarget this
line's PR (#3975) to `dev` when the fork starts. moq-dev/noq is frozen to
security patches for `main`.

The seven BBR correctness fixes shipped in moq-noq 1.3.1 (#4206). The
remaining BBR quests here and [BBR ACK cleanup](/quest/m1/bbr-ack-cleanup.md)
all edit `bbr3/mod.rs`, so one owner should work there at a time.
Controller-level regressions extend the shared test `Sim` in `bbr3/mod.rs`
with only what each needs, rather than adding another simulation loop; a fix
at the transport boundary still needs a transport test through the real
callbacks. The existing loops stay, since the fork merges upstream weekly and
a port would conflict. Each BBR fix ships in a fork patch release without
waiting for the remaining transport features. The comparison against
Google's BBR is part of the separate
[natural drain study](/quest/m2/quic-bbr-natural-drain.md).
Older quests say "the fork" or "noq"; read that as `moq-quic`. Their steps to
publish a fork release, pin it, or offer a change upstream are superseded:
a change lands in-tree with its consumer, and upstreaming is optional.
Comment on lines +24 to +26

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Retarget each quest from the frozen fork

When the remaining m2 QUIC quests are started, this blanket reinterpretation still leaves their actual goals and completion criteria pointing at the repository that is now frozen to security fixes. For example, quic-ack-progress.md:11-12,39-40 requires work and a release from moq-dev/noq, quic-qmux.md:44-55 requires a moq-noq-qmux crates.io release, and noq-socket-close.md:12-15 tells the implementer to cut another noq release. Retarget those quest files and dependencies to the in-tree crates now, rather than leaving contradictory executable plans.

AGENTS.md reference: AGENTS.md:L27-L28

Useful? React with 👍 / 👎.

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.

Agreed. Fixed in 0dffe49: ACK progress, the ACK hook, qmux, and socket close no longer end in a moq-noq release, and every m2/m3 quest that changes the stack now lists the fork under Required.

(Written by Claude Opus 5.5)


Rules the line keeps:
The seven BBR correctness fixes shipped in moq-noq 1.3.1 (#4206) and move to
`moq-quic` with the [BBR3 port](/quest/m1/quic/fork/bbr3.md). The remaining
BBR quests here and [BBR ACK cleanup](/quest/m1/bbr-ack-cleanup.md) all edit
`bbr3/mod.rs`, so one owner should work there at a time. Controller-level
regressions extend the shared test `Sim` in `bbr3/mod.rs` with only what each
needs, rather than adding another simulation loop; a fix at the transport
boundary still needs a transport test through the real callbacks. The
comparison against Google's BBR is part of the separate
[natural drain study](/quest/m2/quic-bbr-natural-drain.md).

- a carried change lists its upstream PR or the reason it has none;
- the fork's packages are `moq-noq-proto`, `moq-noq`, and `moq-noq-udp`,
never a crate that impersonates the parent;
- published MoQ crates depend on crates.io releases of the fork, never a
workspace-only Cargo patch or a mutable branch;
- each feature quest releases the fork crate it changes and records the
parent noq commit it is based on; there is no separate release quest;
- MoQ's config names congestion families (`Loss`, `Delay`, and `RealTime`
once GCC ships), never algorithms; noq's public `Controller` trait is the
seam experiments plug into, and MoQ owns which algorithm each family means.
MoQ's config names congestion families (`Loss`, `Delay`, and `RealTime` once
GCC ships), never algorithms; the `Controller` trait is the seam experiments
plug into, and MoQ owns which algorithm each family means.

The scheduling contract has three levels: strict subscription priority,
byte-fair service between send groups at the same priority, then the
Expand All @@ -54,12 +48,11 @@ This is a transport API change, not a MoQ wire change.

Decided in the 2026-09-30 audit: deadlines, qmux, BBR loss parity, ECN
measurement, ACK progress, and the ACK hook moved to m2, since no m1 quest
consumes them. The release quest was deleted: the fork already publishes to
crates.io (moq-noq 1.3.2, web-transport-moq 2.0.0) with no workspace patch,
and the qmux crate release folds into [qmux](/quest/m2/quic-qmux.md).
consumes them.

## Required

- [Hard fork](/quest/m1/quic/fork/README.md) - quinn in-tree as `moq-quic`, with BBR3 and lazy stream slots, and MoQ switched onto it
- [BBR idle burst](/quest/m1/quic/bbr-app-limited.md) - a fork regression proves a burst after a long idle is paced at the learned bandwidth, closing #4219
- [Mark BBR starvation wherever the source runs dry](/quest/m1/quic/bbr-app-limited-edges.md) - partial polls count, local send caps do not, receiver credit is pinned
- [Deliver the application close before io_uring teardown](/quest/m1/quic/uring-close.md) -
Expand All @@ -71,17 +64,16 @@ and the qmux crate release folds into [qmux](/quest/m2/quic-qmux.md).
the lossy scalar; retransmits follow the same order
- [Relay peers get wider limits](/quest/m1/quic/peer-limits.md) - MAX_STREAMS
and MAX_DATA are raised after SETUP identifies a cluster peer
- [Upstream the fork](/quest/m1/quic/upstream.md) - every general carried
change is offered to n0-computer/noq once its shape has settled
- [Shard the endpoint](/quest/m1/quic/shard.md) - the library shards a
server endpoint across cores by default, replacing the `--workers` group
- [Surface UDP send errors](/quest/m1/quic/send-errors.md) - a client drops a QUIC attempt the moment its address family is unreachable

## Related

- [Scope track priority](/quest/m1/track-priority-scope.md) - the
per-broadcast fairness policy on cluster sessions
- [Starvation](/quest/m1/qos/starvation.md) - the first consumer of ACK
progress: how far behind viewers are, from the relay's point of view
- [Multipath spike](/quest/m2/multipath-spike.md) - a noq capability that
MoQ does not use yet
- [Discover media headroom](/quest/m2/quic-probe.md) - test useful-media pacing before adding redundant probe traffic
- [L4S on the backbone](/quest/m2/quic-ecn.md) - an ECT(1) option in the fork, an `ecn` config knob, and a dualpi2 measurement
- [Careful resume on reconnect](/quest/m2/quic-careful-resume.md) - a redial starts at the previous connection's rate
Expand Down
4 changes: 4 additions & 0 deletions quest/m1/quic/bbr-app-limited-edges.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ drains, a sender blocked only by `send_window`, and a stream blocked by
receiver credit. Builds on the seven fixes released in moq-noq 1.3.1. No public API or wire
change is intended.

## Required

- [Hard fork](/quest/m1/quic/fork/README.md) - lands in `moq-quic` on `dev`, not the frozen fork

## Related

- [BBR idle burst](/quest/m1/quic/bbr-app-limited.md) - the first app-limited fix this extends
Expand Down
11 changes: 5 additions & 6 deletions quest/m1/quic/bbr-app-limited.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,12 @@ minutes, send 250 KB, and assert the pacing rate stays at or above about
If it still stalls, find the remaining cause, such as ProbeRTT entered during
the idle or a stale `bw_shortterm`, and fix it in the fork. Dropping the
estimate after a long idle is a policy change for the m2 study, not this
quest. The `iroh` feature uses upstream noq, which lacks #5; offering it
there belongs to the upstream quest.
quest.

## Closes
## Required

- [#4219](https://github.com/moq-dev/moq/issues/4219) - the first send after an idle period is paced at a trickle
- [Hard fork](/quest/m1/quic/fork/README.md) - lands in `moq-quic` on `dev`, not the frozen fork

## Related
## Closes

- [Upstream the fork](/quest/m1/quic/upstream.md) - offer the starvation fix upstream
- [#4219](https://github.com/moq-dev/moq/issues/4219) - the first send after an idle period is paced at a trickle
72 changes: 72 additions & 0 deletions quest/m1/quic/fork/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Hard-fork quinn into the monorepo as moq-quic

## Goal

MoQ's QUIC stack lives in `rs/` as `moq-quic` (the sans-IO core),
`moq-quic-udp`, `moq-quic-tokio`, and `web-transport-moq`, forked from
quinn-rs/quinn `main`. Every MoQ QUIC path (moq-tokio and moq-uring) runs on
it, the `moq-noq*` dependencies are gone, and a QUIC change lands in the same
PR as the MoQ code that needs it.

Out of scope: iroh keeps upstream noq (the `iroh` feature still compiles it for
iroh's controller factory), and multipath, NAT traversal, and QAD are not
carried.

## Plan

Decided in the 2026-09-30 plan, with reasons, so later sessions don't reopen
them:

- **Hard fork, no merges.** The moq-dev/noq soft fork never merged upstream
(its weekly sync job failed on a workflow-permission error), n0-computer/noq
slowed to 5-7 commits a month in Aug-Sep, and n0's strict semver limits
what we can upstream, not what a fork can change. Upstream fixes are
cherry-picked by hand; offering our changes upstream is optional courtesy,
not tracked work.
- **Base on quinn, not noq.** quinn-proto is 32.9k lines against noq-proto's
58.5k, about 15k of which is multipath, NAT traversal, and QAD that MoQ
never negotiates. Stripping those from noq was estimated at 2-3 weeks with
loss-recovery risk, and would leave every quinn fix a hand port. quinn ships
security fixes the day the advisory publishes (11 in 2026), and staying
close to its code keeps those cherry-picks cheap. The cost is re-porting
BBR3, the controller callbacks, and lazy stream slots, which is why MoQ
moved to noq in the first place (#1706, #3342, #3789).
- **Multipath is deleted**, and with it the multipath spike.
- **In-tree**, so `just check` and CI cover the stack, one PR spans the core
and its consumers, and `main` and `dev` each carry their own copy instead of
double-landing fixes on the 1.3 and 2.0 fork lines.
- **Named by role**: `moq-quic`, `moq-quic-udp`, `moq-quic-tokio`;
`web-transport-moq` keeps its name. Each crate's README and license credit
quinn and noq.
- **Lands on `dev`.** moq-tokio exposes `noq::Endpoint`,
`noq::TransportConfig`, and `noq::Incoming` publicly, so the crate swap is
a break.
- **Security folds into the fork.** Rebasing on quinn picks up the 2026
advisories noq is missing; `main` stays on `moq-noq` until `dev` releases.
[noq reassembly cap](/quest/m0/noq-reassembly-cap.md) still finishes on the
old fork for `main`.
- **moq-dev/noq is frozen**: security patches for `main` only, archived once
no released MoQ crate depends on it. The other
[QUIC quests](/quest/m1/quic/README.md) wait for this line and land
in-tree on `dev`.

This README's own work: delete moq-dev/noq's `moq-sync.yml`, mark its README
frozen, and document advisory triage in `rs/moq-quic/README.md`. `cargo audit`
cannot match renamed crates, so the triage is: watch quinn-rs/quinn's security
advisories and releases, check each fix against `moq-quic`, and port it with
its regression test. Every carried change in moq-dev/noq's `CHANGELOG-MOQ.md`
is either ported by a child quest or recorded as not applicable in the
[switch](/quest/m1/quic/fork/switch.md) PR.

## Required

- [Import quinn](/quest/m1/quic/fork/import.md) - quinn's three crates build and test in-tree as `moq-quic*`, verbatim at a recorded commit, with no consumer yet
- [Port BBR3](/quest/m1/quic/fork/bbr3.md) - the fork's corrected BBR3 and controller callbacks run on `moq-quic` as the default controller
- [Lazy stream slots](/quest/m1/quic/fork/stream-slots.md) - relay memory on `moq-quic` matches `moq-noq`
- [Switch](/quest/m1/quic/fork/switch.md) - `web-transport-moq`, moq-tokio, and moq-uring run on `moq-quic`, and `moq-noq*` is gone

## Related

- [Shard the endpoint](/quest/m1/quic/shard.md) - the first feature the fork makes possible
- [Remove moq-uring copies](/quest/m1/perf/uring-copies.md) - cheap copy removals on the new core
- [Redesign the QUIC I/O boundary](/quest/m2/quic-io-boundary.md) - the breaking perf rewrite, gated on a profile
37 changes: 37 additions & 0 deletions quest/m1/quic/fork/bbr3.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# [L] Port BBR3 and the controller callbacks

## Goal

`moq-quic` carries the BBR3 controller from moq-dev/noq, with every
correctness fix the fork has shipped, and it is the default controller. The
fork's BBR regressions pass on `moq-quic`.

## Plan

Move moq-dev/noq's `congestion/bbr3/` over as a unit rather than starting from
quinn#2481: the fork's copy already descends from #2481 and carries the seven
BBR fixes (#4206), the app-limited fix, and the classic ECN CE response
(moq-dev/noq#12). Port the `Controller` extensions it depends on (`PacketId`
and packet-space callbacks, app-limited, cwnd-limited, ACK frequency) by hand;
they are about 350 lines in `connection/mod.rs` and `pacing.rs`.

The fork's roughly 580 lines of regression tests are written against noq's
test harness; rework them onto quinn's rather than porting the harness. Keep
MoQ's config naming controller families (`Loss`, `Delay`), never algorithms.

BBR3 must fill in `ControllerMetrics::bandwidth_estimate` (quinn#2802, merged),
which the [switch](/quest/m1/quic/fork/switch.md) reads for
`web-transport-moq`'s bandwidth stat.

Verify with the fork's BBR tests and the benchmark matrix against `moq-noq`
1.3.x on the same workloads; report any throughput or latency difference.

## Required

- [Import quinn](/quest/m1/quic/fork/import.md)

## Related

- [noq#819](https://github.com/n0-computer/noq/issues/819) - BBR3 as the default, proposed to n0; their sims often favor Cubic
- [noq#475](https://github.com/n0-computer/noq/issues/475) - the original BBR ask
- [noq#768](https://github.com/n0-computer/noq/issues/768) - the `inflight_at_loss` underflow the fork fixed with `saturating_sub`; keep that regression
Loading
Loading