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
2 changes: 2 additions & 0 deletions quest/m0/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ Published API or wire breaks still land on dev; each quest's Plan says so.
- [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
- [IETF early streams](/quest/m0/ietf-early-streams.md) - a moq-transport stream that arrives before SETUP is held until SETUP lands, never aborted
- [SUBSCRIBE_TRACKS refusal](/quest/m0/ietf-subscribe-tracks.md) - a draft-18+ SUBSCRIBE_TRACKS gets NOT_SUPPORTED on its stream, not a session close
- [JS unknown bidi](/quest/m0/ietf-unknown-bidi.md) - an unknown moq-transport bidi stream type closes the session in JS, as in Rust
- [moq-lite early streams](/quest/m0/lite-early-streams.md) - a lite uni stream that arrives before SETUP is held until SETUP lands, and a second SETUP is fatal
- [Request caps](/quest/m0/request-caps.md) - lite message sizes, IETF request IDs, and per-session announces and subscriptions are bounded
- [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
Expand Down
36 changes: 36 additions & 0 deletions quest/m0/ietf-unknown-bidi.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# [XS] JS closes an IETF session on an unknown bidi stream type

## Goal

In `@moq/net`, a moq-transport bidi stream whose message type is unknown closes
the session with PROTOCOL_VIOLATION, as Rust and the JS uni path already do,
instead of aborting only that stream. A message a draft defines but we don't
support keeps the refuse-per-request rule
(#4685, #4610).

## Plan

Facts (`origin/main`, 2026-10-01):

- JS: the `default:` arm of the bidi dispatch in
`js/net/src/ietf/connection.ts` logs and calls `stream.abort(...)`, so the
`#runBidis` catch never reaches `#violated` and the session stays open.
- JS uni already throws `ProtocolViolation` for an unknown type.
- Rust: `run_dispatch`'s `_` arm in `rs/moq-net/src/ietf/session.rs` returns
`UnexpectedStream`, which maps to PROTOCOL_VIOLATION. Rust has a uni test
(`an_unknown_uni_type_closes_the_session`) but no bidi one.

Decided (2026-10-01): unknown is fatal; defined but unsupported is refused per
request. Rejected: making Rust lenient to match JS.

- JS reads the bidi type with `u53()`, which throws `RangeError` for a valid
varint above 2^53 before the switch runs, so `#runBidis` never sees a
`ProtocolViolation` for it either. The uni path already reads `u62()`.

Work: read the bidi type at full wire width (`u62()`), throw
`ProtocolViolation` from the JS default arm, and add a bidi test in both
languages that asserts the PROTOCOL_VIOLATION close code for a small unknown
type and one at or above 2^53. moq-lite is out of scope: its draft says an unknown stream type
MUST be reset and MUST NOT be fatal, and both implementations comply.

Public API: none. Wire: none; fixes conformance.
52 changes: 52 additions & 0 deletions quest/m0/lite-early-streams.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# [S] moq-lite streams that arrive before SETUP are held

## Goal

On lite-05 and later (the versions with a Setup Stream), a uni stream that
arrives before the peer's SETUP is held and handled once SETUP lands, never
aborted, in Rust and JS. A second Setup Stream closes the session with
PROTOCOL_VIOLATION in both languages, as the lite draft requires.

## Plan

Facts (`origin/main`, 2026-10-01):

- Rust `lite::accept_setup` (`rs/moq-net/src/lite/session.rs`) aborts every
non-SETUP uni stream with `UnexpectedStream`.
`accept_request_skips_uni_stream_before_setup` in `rs/moq-net/src/server.rs`
pins that. An unknown `DataType` fails the handshake.
- JS lite has no pre-SETUP gate: `#runUni` in `js/net/src/lite/connection.ts`
handles SETUP like any other uni stream, so early groups already work. It
also accepts a second SETUP silently, and the `#runUnis` catch only stops the
stream, so throwing `ProtocolViolation` there would not close the session.
The `#runBidis` catch closes on `ProtocolViolation`, but with a bare
`close()` rather than the PROTOCOL_VIOLATION code.
- `drafts/draft-lcurley-moq-lite.md`: neither endpoint waits for the peer's
SETUP. A receiver MUST buffer only streams whose encoding depends on a
negotiated extension, and a second Setup Stream MUST close with
PROTOCOL_VIOLATION. Rust's abort is stricter than the draft.
- Lite-01 to lite-04 have no Setup Stream and are out of scope.

Decided (2026-10-01):

- ✅ Rust holds early uni streams until SETUP, then hands them to the normal
classifier, the same shape as
IETF early streams (#4686). Rust can't
route before SETUP anyway, since path and role come from it. Rejected:
processing immediately unless extension-dependent, which needs a session
before SETUP.
- ✅ The duplicate-SETUP check rides along, in both languages. In JS the uni
catch must close the session with the PROTOCOL_VIOLATION code, not copy the
bidi path's bare `close()`.

The remaining JS gap is the duplicate SETUP and its close path; the hold is
Rust work unless JS turns out to need an extension-dependent hold.

Crib from #4686: its `died_before_header` helper and peek-then-hold pattern.
QUIC stream credit bounds the held queue. Check whether JS lite must buffer any
extension-dependent stream type today, and hold those until SETUP.

Tests: flip `accept_request_skips_uni_stream_before_setup` to expect the group
to be delivered. In both languages, a duplicate SETUP closes the session.

Public API: none. Wire: none; matches the lite draft.
1 change: 1 addition & 0 deletions quest/m1/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ QUIC studies there on that rule.
- [One route cost](/quest/m1/route-cost.md) - on `dev`, Warm and Cold collapse to one static route cost
- [Delete removed cluster flags](/quest/m1/cluster-shims.md) - on `dev`, once a release has carried their refusals, `mesh` and `linger` leave `cluster::Config`
- [Track tail interop](/quest/m1/track-tail-interop.md) - a Rust publisher ending a track with a group in flight is read to its end by the JS subscriber, and the reverse, in `just test interop`
- [moq-mux truncated group](/quest/m1/mux-truncated-group.md) - a test pins that moq-mux skips a truncated spliced group as aborted
- [Merge queue](/quest/m1/merge-queue.md) - the required checks run on `merge_group`, so a stale green check can no longer break main
- [Binding audio delay](/quest/m1/binding-surface.md) - moq-ffi and every wrapper configure and observe audio playout delay
- [FFI shape](/quest/m1/ffi-shape/README.md) - the bindings mirror Rust's layers: net at the root, then media, json, flate, audio, and video namespaces built from the handle below
Expand Down
23 changes: 23 additions & 0 deletions quest/m1/mux-truncated-group.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# [XS] moq-mux skips a truncated spliced group

## Goal

A moq-mux test feeds its consumer a spliced group whose continuation can never
arrive and asserts the group is skipped as aborted, not read as a short clean
group.

## Plan

Start after #4689 lands: it makes `group::Consumer` reads and `finished()` fail
on a truncated spliced group (`Error::Dropped` for a pruned seam, else the dead
route's error), where they used to end clean. moq-mux's consumer already drops a group
whose read fails, but only code reading verified that; no test covers a spliced
group. Build one with the moq-net resume test fixtures, or the smallest public
path to a pruned seam.

Public API: none. Wire: none.

## Related

- [#4689](https://github.com/moq-dev/moq/pull/4689) - the truncated-group behavior this test pins
- [#4491](https://github.com/moq-dev/moq/pull/4491) - the `resume.rs` splice path
Loading