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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
22 changes: 14 additions & 8 deletions quest/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,24 @@ grouped into milestones ordered by priority.

## Plan

m0 is everything in flight now: announce and wildcard routing, and audio
playout (jitter target, quality harness, A/V clock). m1 is the next wave across reliability, features,
performance, and planning. m2 holds later features, design studies, and
experiments. m3 is deferred: work whose first step is outside this repository.
m4 waits on an upstream release or external dependency to ship. Priority is
m0 is everything in flight now: relay hardening and IETF interop ahead of
Seattle, wildcard routing, and audio playout (jitter target and quality
harness). m1 is the next wave across reliability, features, performance, and
planning. m2 holds later features, design studies, and experiments. m3 is
gated on the outside world: hardware, a partner, a consumer, or a provider's
offer. m4 waits on an upstream release. Priority is
separate from branch targeting: published API and wire breaks still land on dev
under the repository rules.

A quest waiting on the outside world, in any milestone, states that condition
as a plain-text `Required` bullet, so `quest ready` reports it blocked.
`/quest-audit` re-checks those gates; when one clears, remove the bullet and
move the quest to the milestone its priority belongs in.

## Required

- [m0: immediate priorities](/quest/m0/README.md) - everything in flight now: announce and wildcard routing, and audio playout
- [m0: immediate priorities](/quest/m0/README.md) - everything in flight now: relay hardening and IETF interop for Seattle, wildcard routing, and audio playout
- [m1: next wave](/quest/m1/README.md) - reliability, capabilities, performance, and the planning that settles their contracts
- [m2: later work](/quest/m2/README.md) - deferred features, design studies, and experiments
- [m3: deferred](/quest/m3/README.md) - gated on the outside world: hardware nobody has, a partner, or a provider's offer
- [m4: upstream](/quest/m4/README.md) - waiting on an upstream release or external dependency, re-checked periodically
- [m3: deferred](/quest/m3/README.md) - gated on the outside world: hardware, a partner, a consumer, or a provider's offer
- [m4: upstream](/quest/m4/README.md) - waiting on an upstream release, re-checked periodically
50 changes: 28 additions & 22 deletions quest/m0/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@

## Goal

The work in flight now, in three independent tracks. Relay hardening: every
resource a peer can make the relay hold is bounded by what it sent or by a
budget, no peer input panics the process, and legal moq-transport input never
fails a session, ahead of Seattle interop on 2026-10-12. Routing: a publisher stops
sending announce updates the wire cannot tell apart, a service claims the
prefix it could serve instead of enumerating broadcasts. Audio playout: the target is a measured
estimate of arrival timing in both languages, a browser regression fails a
nightly run, and the audio playhead becomes the clock video follows.
The work in flight now, in three independent tracks. Relay hardening: legal
moq-transport input never fails a session ahead of Seattle interop on
2026-10-12, every resource a peer can make the relay hold is bounded by what
it sent or by a budget, and no peer input panics the process. Routing: a
service claims the prefix it could serve instead of enumerating broadcasts.
Audio playout: the target is a measured estimate of arrival timing in both
languages, and a browser regression fails a nightly run.

## Plan

Expand All @@ -18,40 +17,47 @@ done. moq.pro tracks this repository as a submodule rather than a release, so
no release quest gates this milestone. The Pronto GPU integration lives in
moq.pro.

Routing: announce-update dedupe is a wire-compatible fix on every version. The
wildcard line is prefix-only on the wire; its resolve and demand work is done
on the line branch and waits to land. Serving the relay's ingested-only
view (`origin::Consumer::local()`) to localhost workers belongs to moq.pro's
edge, which embeds moq-relay; it moved there on 2026-09-28.
Relay hardening: IETF interop leads the ranking, since only those quests
block Seattle. Subgroup refusal came from the moxygen line and IETF stream
types from m1; both moved here in the 2026-09-30 audit because a session
ended by legal input is exactly what Seattle would hit. The DoS hardening
from an external review on 2026-09-29, verified against `main`, stays in m0
as security work. Its quests describe fixes, not exploits.

Routing: the wildcard line is prefix-only on the wire; its resolve and demand
work is done on the line branch and waits to land. Serving the relay's
ingested-only view (`origin::Consumer::local()`) to localhost workers belongs
to moq.pro's edge, which embeds moq-relay; it moved there on 2026-09-28.

Audio playout: the jitter target replaces the round-trip guess. The harness's
browser lane grades it nightly and records the traces it replays; the native
lane is a standalone m1 quest, since nothing here waits on it. The A/V clock
builds on the jitter target's per-track spread.

Relay hardening comes from an external review on 2026-09-29, verified against
`main`. Its quests describe fixes, not exploits.
lane is a standalone m1 quest, since nothing here waits on it. The harness
line lands before the jitter line, since both add
`js/watch/src/audio/replay.test.ts`. The [A/V clock](/quest/m1/av-clock.md)
moved to m1 in the 2026-09-30 audit: it waits on the whole jitter line and is
a published `@moq/watch` break on dev.

Published API or wire breaks still land on dev; each quest's Plan says so.

## Required

- [Legal IETF input](/quest/m0/ietf-legal-input.md) - draft-20+ FETCH, allowed parameters, INCLUDE_PROPERTIES and FORWARD=0 decode and are refused per request, not session-fatal
- [IETF FIN semantics](/quest/m0/ietf-fin-not-cancel.md) - a request stream FIN stops updates without cancelling, and REQUEST_UPDATE on a subscribe is parsed
- [Subgroup refusal](/quest/m0/ietf-subgroup-refusal.md) - a non-zero moq-transport subgroup costs that one stream, never the session
- [IETF stream types](/quest/m0/ietf-uni-stream-types.md) - padding streams are discarded stream-only and an unknown uni type closes the session, per draft-21
- [Request caps](/quest/m0/request-caps.md) - lite message sizes, IETF request IDs, and per-session announces and subscriptions are bounded
- [quest check everywhere](/quest/m0/quest-check-everywhere.md) - `quest check` guards `main`, `dev`, and the line branches on push and PR, not only PRs into `main`
- [noq reassembly cap](/quest/m0/noq-reassembly-cap.md) - noq carries quinn's stream reassembly cap and the connection receive window is finite by default
- [qmux reset race](/quest/m0/qmux-reset-race.md) - qmux handles RESET_STREAM under one lock instead of panicking
- [Remove gossip](/quest/m0/remove-gossip.md) - a relay dials only configured peers; `cluster.mesh` is refused at startup
- [Shared fronts](/quest/m0/shared-fronts.md) - viewer sessions share a front, so fronts scale with peers, not viewers
- [Frame alloc budget](/quest/m0/frame-alloc-budget.md) - frame buffers pre-allocate within a per-session budget and otherwise grow with bytes received
- [Handshake deadline](/quest/m0/handshake-deadline.md) - an unfinished handshake or slow HTTP header times out
- [Request caps](/quest/m0/request-caps.md) - lite message sizes, IETF request IDs, and per-session announces and subscriptions are bounded
- [Subscriber prune](/quest/m0/subscriber-prune.md) - a track's subscription list holds only live subscribers
- [Revalidate overflow](/quest/m0/revalidate-overflow.md) - no auth duration can overflow a deadline and abort the relay
- [Legal IETF input](/quest/m0/ietf-legal-input.md) - draft-20+ FETCH, allowed parameters, INCLUDE_PROPERTIES and FORWARD=0 decode and are refused per request, not session-fatal
- [IETF FIN semantics](/quest/m0/ietf-fin-not-cancel.md) - a request stream FIN stops updates without cancelling, and REQUEST_UPDATE on a subscribe is parsed
- [Wildcard](/quest/m0/wildcard/README.md) - a relay resolves subscriptions against advertised prefixes, a service claims the prefix it could serve and refuses the rest instead of enumerating broadcasts, and the browser player treats a covering claim as availability
- [Audio quality harness](/quest/m0/audio-quality-harness/README.md) - a browser playout latency regression fails a nightly run instead of arriving as a bug report, and its recorder supplies the jitter target's replay traces
- [Audio jitter target](/quest/m0/audio-jitter-target/README.md) - the audio playout target is a measured estimate of arrival timing in both languages, not a round-trip guess
- [A/V clock](/quest/m0/plan-av-clock.md) - the audio playhead drives Sync.reference while audio plays, through per-track sync handles

## Related

Expand Down
21 changes: 10 additions & 11 deletions quest/m0/audio-jitter-target/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ the same arrival trace.
Boundaries: convergence still uses skip-ahead and silence, so playing slightly
faster or slower to converge stays [Time
stretch](/quest/m1/watch-audio-time-stretch.md). No packet loss concealment.
Video keeps its own target; making the audio playhead the clock is [Plan: A/V
clock](/quest/m0/plan-av-clock.md).
Video keeps its own target; making the audio playhead the clock is [A/V
clock](/quest/m1/av-clock.md).

## Plan

Expand All @@ -42,15 +42,15 @@ reporter; they replace the #3477 traces wherever the quests name them.
The algorithm is written down at `doc/concept/audio-jitter.md`, with a
conformance corpus beside it that both implementations will read.

Neither `main` nor `dev` has a measured estimator. `js/watch/src/sync.ts:159`
Neither `main` nor `dev` has a measured estimator yet. `js/watch/src/sync.ts:159`
still computes `max(MIN_JITTER, minRtt * 1.25)` from the connection's PROBE,
and `js/watch/src/audio/latency.ts` still exists. `sync.ts` also adds the
advertised jitter to that term, where the document settles on a maximum.

The prior art is the branch of PR #3517,
`origin/quest/m0/3477-watch-auto-latency`, two commits ahead of `dev`. The PR
is closed and never merged; the watch quest starts from the branch rather than
from `dev`. It already deletes the RTT term
The prior art from PR #3517 (closed 2026-09-10, branch deleted 2026-09-30)
landed on this line's branch through
[#3954](https://github.com/moq-dev/moq/pull/3954), so the watch quest works
there. It deletes the RTT term
(`MIN_JITTER`, `FALLBACK_JITTER`, `#minRtt`, and the `probe` input are gone
from `sync.ts`; `latency.ts` survives, minus `reanchorFloor`) and plumbs a
per-track arrival `spread` through `Container.Consumer`, measured at container
Expand All @@ -66,7 +66,7 @@ is immediate and unclamped, so a tune-in across a stale group sets the target
to seconds.

Note that `sync.ts` has since been refactored on `main` to a `register(jitter)`
list, so the branch does not rebase cleanly.
list, which is one of the conflicts merging `main` in resolves.

Native has no jitter buffer at all. `rs/moq-audio`'s `decode::Options`
(`rs/moq-audio/src/decode/consumer.rs`) carries `max_age`, how far
Expand All @@ -76,7 +76,8 @@ buffer against uneven arrivals.

## Required

- [Watch](/quest/m0/audio-jitter-target/watch.md) - js/watch and js/hang bring the #3517 branch's estimator into conformance
- [Audio quality harness](/quest/m0/audio-quality-harness/README.md) - lands first, since both lines add `js/watch/src/audio/replay.test.ts`; it also records the traces the watch quest replays
- [Watch](/quest/m0/audio-jitter-target/watch.md) - js/watch and js/hang bring the #3954 estimator into conformance
- [Native](/quest/m0/audio-jitter-target/native.md) - rs/moq-audio grows a measured jitter buffer from the same algorithm

## Closes
Expand All @@ -85,6 +86,4 @@ buffer against uneven arrivals.

## Related

- [Audio quality harness](/quest/m0/audio-quality-harness/README.md) - the automated proof, and the recorder of the traces the watch quest replays
- [Time stretch](/quest/m1/watch-audio-time-stretch.md) - inaudible convergence, on top of this
- [Plan: A/V clock](/quest/m0/plan-av-clock.md) - the clock this target eventually feeds
6 changes: 1 addition & 5 deletions quest/m0/audio-jitter-target/watch.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,9 @@ The branch also replaces `probe` in `SyncInput` with per-track `audioSpread`
and `videoSpread` inputs, which breaks the published `@moq/watch` type. This
quest lands on `main`, so it adds the spread inputs beside `probe` and stops
reading `probe`; removing it is part of the `SyncInput` reshape in
[Plan: A/V clock](/quest/m0/plan-av-clock.md). Land the estimator so
[Plan: A/V clock](/quest/m1/av-clock.md). Land the estimator so
that quest can adopt it without a second estimator change.

## Required

- [Browser harness](/quest/m0/audio-quality-harness/browser.md) - records the arrival traces this quest replays

## Related

- [Plan: A/V clock](/quest/m0/plan-av-clock.md) - reshapes `SyncInput` around the per-track spread this quest produces
2 changes: 1 addition & 1 deletion quest/m0/frame-alloc-budget.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,5 @@ config, not moq-net. Wire: none.

## Related

- [Frame slot charge](/quest/m1/frame-slot-charge.md) - also changes what a group charges the cache
- [Frame slot charge](/quest/m1/frame-slot-charge.md) - lands first; both change the cache charge in `model/group.rs`
- [Peer limits](/quest/m1/quic/peer-limits.md) - stream counts and windows per peer
5 changes: 4 additions & 1 deletion quest/m0/ietf-fin-not-cancel.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ silently ends the subscription.

Public API: none. Wire: conformance fix; no draft change.

## Required

- [Legal IETF input](/quest/m0/ietf-legal-input.md) - lands first; both change `ietf/fetch.rs` and the request close path

## Related

- [Legal IETF input](/quest/m0/ietf-legal-input.md) - the other interop blocker
- [Lite request streams](/quest/m1/request-stream-serve.md) - lite deliberately treats a FIN as ending the request; don't unify the two
3 changes: 2 additions & 1 deletion quest/m0/ietf-legal-input.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,6 @@ Public API: none. Wire: fixes conformance; no draft change.

## Related

- [Request token](/quest/m1/auth/request-token.md) - also edits `decode_params!`, turning the ignored token into a per-request grant
- [IETF FIN semantics](/quest/m0/ietf-fin-not-cancel.md) - the other interop blocker
- [IETF stream types](/quest/m1/ietf-uni-stream-types.md) - same stream-scoped-before-fatal rule for uni streams
- [IETF stream types](/quest/m0/ietf-uni-stream-types.md) - same stream-scoped-before-fatal rule for uni streams
26 changes: 26 additions & 0 deletions quest/m0/ietf-subgroup-refusal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# [S] Subgroup refusal stays on the stream

## Goal

A peer that sends a non-zero subgroup on moq-transport loses that one stream,
never the session. Every other track on the session keeps flowing.

## Plan

Against moxygen's `moqtest_server`, a track with two subgroups per group ended
the relay's upstream session, and the server reconnected. Our side refuses the
stream today. Whether the session ends because of how we refuse it (the reset
code, STOP_SENDING, or the alias state it leaves behind) or because the peer
reacts badly to a correct refusal is not known yet. Reproduce it first. If the
peer is at fault, say so on its tracker and keep a regression test for our side.

A test with an IETF peer that sends a subgroup 1 stream next to a healthy track
is the check.

Moved from the moxygen line to m0 in the 2026-09-30 audit: a non-zero
subgroup ending the upstream session is exactly m0's "legal input never fails
a session", and moxygen will send it at Seattle interop on 2026-10-12.

## Related

- [Moxygen compatibility](/quest/m1/moxygen/README.md) - subgroups stay out of scope; only the blast radius is in
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ so it ships on main.

## Plan

Moved from m1 to m0 in the 2026-09-30 audit: a padding stream answered with
INTERNAL_ERROR is legal input mishandled, which m0 fixes before Seattle
interop on 2026-10-12.

`run_unis` in `rs/moq-net/src/ietf/session.rs` routes every non-SETUP uni
stream to `run_uni_group`, which rejects padding and unknown types alike while
leaving the session alive. That stream-only rejection reaches the wire as
Expand Down
12 changes: 8 additions & 4 deletions quest/m0/noq-reassembly-cap.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,24 @@ the crates are renamed.
RUSTSEC-2026-0185) to moq-dev/noq: `Assembler::insert` returns an error past
1024 buffered chunks after defragmenting, the stream path closes with
`INTERNAL_ERROR`, and the CRYPTO path does the same. Keep quinn's test.
Release 1.3.3 and 2.0.1, then bump the pins here.
1.3.3 and 2.0.1 are already taken by the open release PRs moq-dev/noq#21
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
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`,
`quic.rs`). Pick the value with a throughput measurement, not a
guess, and update `doc/bin/relay/config.md`.
guess, and update `doc/bin/relay/config.md`. Size it so relay-to-relay
cluster sessions, which carry every viewer's traffic on one connection,
are not throttled; per-peer windows wait for
[Peer limits](/quest/m1/quic/peer-limits.md).
- `rs/moq-uring` depends on `moq-noq-proto` too, so the same pin bump
covers the io_uring workers.

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

## Related

- [QUIC release](/quest/m1/quic/release.md) - owns the fork's security-update procedure this gap shows is missing
- [Peer limits](/quest/m1/quic/peer-limits.md) - per-peer windows and stream limits
- [Peer limits](/quest/m1/quic/peer-limits.md) - per-peer windows and stream limits, which later let cluster sessions take a larger window than clients
2 changes: 1 addition & 1 deletion quest/m0/qmux-reset-race.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@ Public API: none. Wire: none.

## Related

- [qmux on noq-proto](/quest/m1/quic/qmux.md) - replaces these stream maps entirely, later
- [qmux on noq-proto](/quest/m2/quic-qmux.md) - replaces these stream maps entirely, later
24 changes: 8 additions & 16 deletions quest/m0/quest-check-everywhere.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,13 @@

## Plan

- `dev` has no `quest` flake input and no `quest check` in its justfile;
#4428's main-into-dev sync brings both. After it lands, convert the
old-format quests on `dev` until `quest check` passes there.
- Line branches pin their own `quest` revision (the wildcard line pinned
46d7fe8 against main's 8590d2a), so an old pin passes an old format.
Merging `main` into each active line bumps the pin; do that for the lines
that fail today and fix what the new check reports.
- `just ci check` runs `quest check` on pull requests only. Also run it on
push to `main`, `dev`, and `quest/**`, so a direct merge commit (such as
`main` merged into a line) can't land a broken tree. Use a dedicated job
that runs `quest check` unconditionally: `check.yml`'s scope steps diff
against `origin/$GITHUB_BASE_REF`, which is empty on a push.
- `check.yml` runs `quest check` on pull requests only. Also run it on push to
`main`, `dev`, and `quest/**`, so a direct merge commit (such as `main`
merged into a line) can't land a broken tree. Use a dedicated job that runs
`quest check` unconditionally: `check.yml`'s scope steps diff against
`origin/$GITHUB_BASE_REF`, which is empty on a push.
- `dev` already pins main's `quest` (8590d2a) and passes since #4428. Only the
wildcard line still pins 46d7fe8: merge `main` into it to bump the pin, and
fix what the new check reports.

Public API: none. Wire: none.

## Required

- #4428 merged to `dev`
Loading