diff --git a/quest/m1/README.md b/quest/m1/README.md index 88da46e7b72..4f7aaf5b1a6 100644 --- a/quest/m1/README.md +++ b/quest/m1/README.md @@ -86,7 +86,7 @@ Added 2026-10-23 (maintainer, pre-cut triage): breaks that would otherwise force another major (`announce="never"`, the unused OBS outputs, binding durations, and the remaining web-transport-trait adapters) land before the cut, as do the small hostile-input and container fixes from the 2026-10-22 -security audit, the draft 14-16 session errors, and the stuck-window scan. +security audit, and the stuck-window scan. Binding durations wait on Duration support in the generated C. ## Required @@ -120,6 +120,7 @@ Binding durations wait on Duration support in the generated C. - [fMP4 edit lists](/quest/m1/fmp4-edit-lists.md) - the fMP4 importer applies edit lists to frame timestamps, so the interop AAC tone passes the phase check - [fMP4 passthrough sync start](/quest/m1/fmp4-passthrough-sync.md) - a passthrough video import that starts mid-GOP publishes nothing before its first sync sample - [A smaller, safer API than release](/quest/m1/api-audit/README.md) - every surface added since release has a consumer or is gone, overlapping operations are collapsed, names mirror across Rust, JS, and the bindings, and every break says how to migrate +- [moq-transport 18 and 22 only](/quest/m1/ietf-drop/README.md) - drafts 14-17 and 19-21 are deleted from Rust, JS, tests, interop lanes, and docs, removing the legacy control-stream adapter and its request limit - [moq-quic's API improves on quinn's](/quest/m1/quic-api/README.md) - moq-quic and both QUIC drivers ship one mirrored API: `send::`/`recv::` namespaces, one error per direction, consuming terminal operations, plain configs, no quinn baggage - [Drop web-transport-trait](/quest/m1/drop-web-transport-trait.md) - iroh, wasm, and ffi implement moq-net's `transport::poll` traits, and the remaining `web_transport_trait` adapters are deleted - [JavaScript FETCH](/quest/m1/js-fetch.md) - browser publishers answer IETF FETCH through the JS ranges request surface @@ -151,9 +152,7 @@ Binding durations wait on Duration support in the generated C. - [API Extractor checks](/quest/m1/api-extractor-checks.md) - API Extractor fails a PR on a forgotten export, an unresolved declaration, or an undocumented export, replacing the hand-rolled `.d.ts` checkers - [Generated JS API reference](/quest/m1/js-api-reference.md) - the doc site renders an API reference for every `@moq/*` package from API Extractor's doc model - [IETF lazy discovery](/quest/m1/ietf-lazy-discovery.md) - JS moq-transport sessions never hold overlapping SUBSCRIBE_NAMESPACE requests at a peer that refuses them -- [JS readvertisement hold](/quest/m1/ietf-js-readvertise.md) - a JS IETF session handles a namespace readvertisement only after its withdrawal, as Rust does -- [Draft 14-16 session errors](/quest/m1/ietf-adapter-session-errors.md) - the moq-transport adapter keeps session-level error causes, so a terminal rejection is not redialed -- [Interop coverage](/quest/m1/interop-coverage/README.md) - the riskiest moq-transport features moq-net exposes get cross-implementation tests over the even drafts 14 to 22 +- [Interop coverage](/quest/m1/interop-coverage/README.md) - the riskiest moq-transport features moq-net exposes get cross-implementation tests over drafts 18 and 22 - [AMF0 exact reads](/quest/m1/rtmp-amf0-exact-reads.md) - a truncated RTMP string fails with `UnexpectedEof` instead of parsing NUL padding - [HLS variant area](/quest/m1/hls-variant-area.md) - a hostile `RESOLUTION` cannot panic or misorder variant ranking - [MSF duplicate init](/quest/m1/msf-duplicate-init.md) - an MSF catalog refuses a repeated `initDataList` id instead of resolving it last-wins diff --git a/quest/m1/ietf-adapter-session-errors.md b/quest/m1/ietf-adapter-session-errors.md deleted file mode 100644 index 522f19ba8b5..00000000000 --- a/quest/m1/ietf-adapter-session-errors.md +++ /dev/null @@ -1,31 +0,0 @@ -# [S] The draft 14-16 adapter keeps session errors - -## Goal - -On moq-transport drafts 14 to 16, a session-level transport error from -`ControlStreamAdapter` reaches the session with its cause instead of as -`Closed`. A peer that ends the session with a terminal error, such as -ProtocolViolation or Unauthorized, is not redialed as if the drop were -transient. - -## Plan - -From the 2026-10-22 release audit; moved to m1 on 2026-10-23 (maintainer), -though the race is narrow and limited to the older drafts. It is the session-scope follow-up -left by [moq-dev/moq#6251](https://github.com/moq-dev/moq/pull/6251), which -kept stream errors through `AdapterError::Real`. - -In `rs/moq-net/src/ietf/adapter.rs`, the adapter's `Session` impl maps the -native `poll_accept_bi`, `poll_open_uni`, `poll_accept_uni`, the datagram -send and receive, and `poll_closed` errors to `Error::Closed`. In -`ietf/session.rs`, `adapter_run` reads the real SETUP stream first and usually -keeps the peer's code. The code is lost when a unis, dispatch, or datagram -future fails first. The session then reports `Closed`, and -`moq-tokio`'s `is_terminal` treats that as retryable. - -Carry the transport error through the way `AdapterError::Real` does, mapped -with the negotiated registry. Reproduce first: a draft-14 peer closes with -ProtocolViolation while a uni or datagram poll is pending, and the client -must see a terminal error and not redial. - -Public API: none. Wire: none. diff --git a/quest/m1/ietf-drop/README.md b/quest/m1/ietf-drop/README.md new file mode 100644 index 00000000000..24d199fd772 --- /dev/null +++ b/quest/m1/ietf-drop/README.md @@ -0,0 +1,71 @@ +# moq-transport 18 and 22 only + +## Goal + +`moq-net` and `@moq/net` speak moq-transport drafts 18 and 22 and no +others. Drafts 14 to 17 and 19 to 21 are deleted from the wire code, tests, +fuzzing, interop lanes, and docs, and our `moq-dev-rs` and `moq-dev-js` +entries in the interop runner registry declare only 18 and 22. + +Out of scope: moq-lite versions. moq-lite-01/02 still negotiate over +`moql` with the CLIENT_SETUP/SERVER_SETUP codec, which stays. + +## Plan + +Decided with the maintainer on 2026-10-24. moq-transport support matters +only for interop with third-party implementations; our own clients and +servers always prefer moq-lite. A draft stays only when the peers it buys +are worth its code (cost-weighted, not "any live peer"). + +Peers per draft, from `englishm/moq-interop-runner` `implementations.json` +on 2026-10-24: + +- 18: 15 third-party peers, the most deployed draft. +- 22: 8 peers (aiomoqt, imquic, moq-go, moqtail, Nokia, moxygen, red5, moqxr). +- 14: aiomoqt, Cloudflare's draft-14 relay, xquic. Cloudflare and xquic also + run draft-18 builds. +- 16: moxygen (its recommended draft; its 18 is experimental), Cloudflare + `moq-rs` main (16 only), and others that also speak 18. +- 15: none. 17: imquic and quic-zig, both also on 18. 19 and 21: imquic + only. 20: aiomoqt and imquic, both also on 22. + +So dropping 15, 17, and 19 to 21 loses no peer. Dropping 14 and 16 loses +Cloudflare `moq-rs` main and moves moxygen onto its experimental draft-18; +the maintainer accepted that for the savings (the cheaper alternative, +keeping 16, 18, and 22, was declined). + +Why 14 to 16 cost the most, measured on `main` at `28bd16452d`: + +- `ietf/adapter.rs` (2,701 lines) and `js/net/src/ietf/adapter.ts` + (1,211) exist only to multiplex 14-16 requests on one control stream. + 74 commits touched them since 2026-08-01, 44 in October. +- At least 11 fixes since August were 14-16 only, such as + [moq-dev/moq#2664](https://github.com/moq-dev/moq/pull/2664), + [moq-dev/moq#3481](https://github.com/moq-dev/moq/pull/3481), + [moq-dev/moq#4961](https://github.com/moq-dev/moq/pull/4961), + [moq-dev/moq#5798](https://github.com/moq-dev/moq/pull/5798), + [moq-dev/moq#6051](https://github.com/moq-dev/moq/pull/6051), and + [moq-dev/moq#6133](https://github.com/moq-dev/moq/pull/6133). +- Cross-cutting IETF features also land a second path through the adapter, + for example GOAWAY + ([moq-dev/moq#5998](https://github.com/moq-dev/moq/pull/5998)) and watched + prefixes ([moq-dev/moq#6530](https://github.com/moq-dev/moq/pull/6530)). + +m1, ranked beside the API audit, because the drop deletes public API +(`session::Limits::requests` and the relay's request limit) and belongs with +the other breaks before the cut. The two children are independent; land +either first. + +This README's own work, once both children merge: open a PR on +`englishm/moq-interop-runner` narrowing the `moq-dev-rs` and `moq-dev-js` +entries in `implementations.json` to drafts 18 and 22. The maintainer +approved posting there on 2026-10-24. + +## Required + +- [Drop moq-transport 14-17](/quest/m1/ietf-drop/d14-17.md) - the legacy control-stream adapter, legacy SETUP negotiation, the request window and its public limit, and the draft-17 varint family are gone in Rust and JS +- [Drop moq-transport 19-21](/quest/m1/ietf-drop/d19-21.md) - the 18-19 and 20-22 families collapse onto 18 and 22 + +## Related + +- [Interop coverage](/quest/m1/interop-coverage/README.md) - new cross-implementation cases target drafts 18 and 22 only diff --git a/quest/m1/ietf-drop/d14-17.md b/quest/m1/ietf-drop/d14-17.md new file mode 100644 index 00000000000..07b3f969685 --- /dev/null +++ b/quest/m1/ietf-drop/d14-17.md @@ -0,0 +1,95 @@ +# [L] Drop moq-transport 14-17 + +## Goal + +`moq-net` and `@moq/net` no longer offer, accept, or contain code for +moq-transport drafts 14, 15, 16, or 17. A peer that offers only those +drafts fails negotiation loudly. `session::Limits::requests` and the relay's +`limits.*.requests` setting are gone. + +## Plan + +Decided 2026-10-24 with the maintainer; see the +[epic](/quest/m1/ietf-drop/README.md) for which peers this loses and why. +Draft 17 goes with 14-16 because it shares the "14-17" arms +(`SubscribeNamespaceLegacy` and others in `ietf/subscriber.rs` and +`ietf/publisher.rs`), so dropping the four together removes those arms +whole. Estimated deletion: about 2,950 production lines in Rust and 1,750 +in JS, plus their tests. + +Rust (`rs/moq-net`): + +- `ietf::Version` loses `Draft14`..`Draft17`; `version.rs` loses the + `moq-00` and `moqt-15`/`16`/`17` ALPNs and codes. `NEGOTIATED` becomes + `[Lite02, Lite01]`. +- `setup.rs` keeps the codec for `LiteLegacy` only; the `Draft14` + versions-list and `Draft15Plus` paths go. +- Delete `ietf/adapter.rs` and the `ControlStreamAdapter` session path in + `ietf/session.rs`, the request window in `ietf/control.rs`, request IDs + carried in responses, legacy datagrams, absolute parameter keys + (`ietf/parameters.rs`), and the "no Track Properties" branches in + `ietf/properties.rs`. +- Draft 17's own pieces go: the varint family that forbids 7-byte values + (`coding/varint.rs`, `ietf/namespace.rs`), `required_request_id_delta`, and + its `request_stream.rs` branch. +- Collapse every gate that now has one survivor. Fuzz targets + (`fuzz.rs`) drop the four drafts. + +JS (`js/net`): + +- `ietf/version.ts` loses `DRAFT_07` and `DRAFT_14`..`DRAFT_17`; delete + `ControlStreamAdapter` (`ietf/adapter.ts`) and the gated arms. +- SETUP negotiation accepts only lite-01/02. Today `acceptNegotiated` + (`connection/accept.ts`) and `connect.ts` take any value in + `Ietf.Version`, so a peer can select the unsupported 0xff000007 or a + draft that Rust only negotiates by ALPN. Add a test that both sides refuse + an IETF version over SETUP. The SETUP codec's draft-14 encoding that lite + uses stays, named for its role. + +Public API (breaking, `!`): + +- `session::Limits::requests` and the relay's `--limits-client-requests` + and `limits.*.requests` are removed (`rs/moq-relay/src/limits.rs`). They + only sized the 14-16 request window. Migration: delete the setting; an old + config fails loudly instead of being ignored. +- `--version`/`--connect-version` stop accepting `moq-transport-14`..`17`. + The CLI derives its names from `Version::names()`, so only examples change. +- Wire: four ALPNs and SETUP-negotiated draft-14 are no longer offered. + +Tests and CI: + +- The PR-gate interop runs drafts 18 and 22 instead of 14 and 22 + (`.github/workflows/interop.yml`). Nightly (`test/interop/interop.sh`) drops + 14 and 16, and `test/interop/interop.toml` drops every entry for 14 to 17, + since the harness accepts any version listed there. +- Moxygen moves to draft 18: `test/smoke/moxygen.sh` (rewrite its pin + comment) and `test/ietf-conformance/run.sh`. Cloudflare smoke targets + draft 18. +- Fix the fixtures that use these drafts, such as + `rs/moq-relay/tests/integration/cluster_unknown.rs` and + `rs/moq-cli/src/publish.rs`. + +Docs: "draft-17 and later" becomes "draft-18 and later" for Auth, Flate, and +Epoch in `drafts/draft-lcurley-moq-{auth,flate,epoch}.md` (run +`just drafts check`), `doc/draft/`, `doc/bin/relay/auth.md`, +`doc/bin/obs.md`, every `doc/lib/*/index.md`, and `py/moq-rs/README.md`. +`doc/setup/redundancy.md` pins draft 18 as the minimal epoch draft. Fix the +`moq-transport-14` example strings in `doc/lib/js/{watch,publish}.md` and +`js/{watch,publish}/README.md`, the request limit in +`doc/bin/relay/config.md`, and the supported list in +`doc/concept/standard.md`. The moq-lite draft's informational comparisons +with 14-18 stay. + +Quests: rewrite each quest that `rg -l -i 'draft[- ]?1[4-7]|d1[4-7]\b|14-1[67]' quest` +lists to what remains. For example, the Moxygen smoke quest checks its +filter-type bug at draft 18, the d17 FETCH exception in pipelined requests +goes, the 14/15 and 17 families in the passthrough epic go, and the d14 +audit findings go. + +moq-transport compatibility between our own released and current builds is +out of scope, since they prefer moq-lite, so a released client pinned to +these drafts against a new relay is not a regression to guard. + +## Related + +- [Drop moq-transport 19-21](/quest/m1/ietf-drop/d19-21.md) - the other half; touches the same version tables, so whichever lands second merges them diff --git a/quest/m1/ietf-drop/d19-21.md b/quest/m1/ietf-drop/d19-21.md new file mode 100644 index 00000000000..146486e181e --- /dev/null +++ b/quest/m1/ietf-drop/d19-21.md @@ -0,0 +1,40 @@ +# [M] Drop moq-transport 19-21 + +## Goal + +`moq-net` and `@moq/net` no longer offer, accept, or contain code for +moq-transport drafts 19, 20, or 21. Gates shared by 18-19 collapse onto 18, +and the 20-22 family collapses onto 22. + +## Plan + +Decided 2026-10-24 with the maintainer; see the +[epic](/quest/m1/ietf-drop/README.md). No third-party peer is lost: imquic +and aiomoqt, the only speakers of 19 to 21, also speak 18 and 22. The +production savings are small (about 70 lines per language), but the test and +CI matrix drops three drafts, and each future IETF feature stops needing +cases for them. + +- Rust: `ietf::Version` loses `Draft19`..`Draft21`, `version.rs` loses the + `moqt-19`/`20`/`21` ALPNs and codes. Draft 21 is wire-identical to 20, and + 22 differs from 20 only in LOCATION_FILTER, so delete + `draft21_matches_draft20_on_the_wire` and fold + `draft22_changes_only_the_location_filter` into plain draft-22 tests. + Collapse the 18-19 sets (PUBLISH_OK on listings, the GOAWAY request ID, + `INVALID_JOINING_REQUEST_ID`, shared parameter sets in `publish.rs`, + `fetch.rs`, and `error.rs`). Fuzz drops the three drafts. +- JS: `ietf/version.ts` loses `DRAFT_19`..`DRAFT_21`; fix its stale + "unpublished" comment on draft 22, which datatracker published on + 2026-10-01. +- Public API: the `Version` variants and `moq-transport-19`..`21` names go + (breaking, `!`). Wire: three ALPNs are no longer offered. +- CI: move the wasm lane (`test/wasm/run.sh`) and the drill lane off draft + 19, drop 20 from nightly (`test/interop/interop.sh`), drop every 19 to 21 + entry from `test/interop/interop.toml`, and fix fixtures such as + `rs/moq-relay/tests/integration/cluster_unknown.rs`. +- Docs: the supported list in `doc/concept/standard.md`, and any quest that + names 19 to 21 (such as the fill-timeout quest's "drafts 18-22"). + +## Related + +- [Drop moq-transport 14-17](/quest/m1/ietf-drop/d14-17.md) - the other half; touches the same version tables, so whichever lands second merges them diff --git a/quest/m1/ietf-js-readvertise.md b/quest/m1/ietf-js-readvertise.md deleted file mode 100644 index 57154400ab0..00000000000 --- a/quest/m1/ietf-js-readvertise.md +++ /dev/null @@ -1,22 +0,0 @@ -# [S] JS holds a readvertisement until its withdrawal is handled - -## Goal - -On drafts 14 to 16, a JS moq-transport session handles a PUBLISH_NAMESPACE -that follows a PUBLISH_NAMESPACE_DONE for the same namespace only after the -withdrawal's handler has finished, as Rust does. - -## Plan - -[moq-dev/moq#6530](https://github.com/moq-dev/moq/pull/6530) fixed Rust -(`Readvertised` in `rs/moq-net/src/ietf/adapter.rs`, test -`a_readvertisement_waits_for_the_withdrawal_to_be_handled`); without it -`epochs::ietf14` failed every run. `js/net/src/ietf/adapter.ts` treats -PUBLISH_NAMESPACE_DONE as a plain CloseStream, so a readvertisement can -overtake it. Mirror the hold and its test. - -Not planned (2026-10-24, maintainer): the same race per track, where -PUBLISH_DONE routes as CloseStream and a new PUBLISH as NewRequest. It was -found by reading the code and never reproduced. - -Public API: none. Wire: none. diff --git a/quest/m1/interop-coverage/README.md b/quest/m1/interop-coverage/README.md index ffaeeaae355..287c479fc0c 100644 --- a/quest/m1/interop-coverage/README.md +++ b/quest/m1/interop-coverage/README.md @@ -5,10 +5,9 @@ The moq-transport features moq-net exposes with the most interop bug history have cross-implementation tests: Rust, JS, and the bindings exchange hang media and exercise subgroup data, namespace discovery, late joins, -FETCH, GOAWAY, TRACK_STATUS, and datagrams over the even drafts 14 -through 22, and request limits over drafts 14 and 16 (later drafts have -no MAX_REQUEST_ID). Our upstream runner clients implement the runner's -existing cases for those features. +FETCH, GOAWAY, TRACK_STATUS, and datagrams over drafts 18 and 22. Our +upstream runner clients implement the runner's existing cases for those +features. Boundaries: @@ -31,15 +30,18 @@ Boundaries: Decided with the maintainer on 2026-10-21, from an inventory of the coverage on `main` at `3ab6cbf81d`. -- `just test interop --versions ietf` runs the matrix on each even draft; +- `just test interop --versions ietf` runs the matrix on each supported draft; every new case runs on it. Beyond it, moq-transport is covered by in-process codec tests (`rs/moq-net/src/test_interop.rs`), the public relays page (published clients), and the Cloudflare and Moxygen smoke lanes. -- Even drafts only (14, 16, 18, 20, 22), nightly. +- Drafts 18 and 22 only, nightly: the only drafts left once + [moq-transport 18 and 22 only](/quest/m1/ietf-drop/README.md) lands + (decided 2026-10-24, maintainer). The request-limits case went with + drafts 14 and 16, the only ones with MAX_REQUEST_ID. - Case ranking follows interop bug history: subgroup data and namespace discovery, then late join and FETCH, then GOAWAY, TRACK_STATUS, - datagrams, and limits. + and datagrams. ## Required diff --git a/quest/m2/README.md b/quest/m2/README.md index 042871bcf37..0ca3906bafd 100644 --- a/quest/m2/README.md +++ b/quest/m2/README.md @@ -198,7 +198,6 @@ lite-07; bitrate enforcement is not part of this release. - [H.264 parsing with maintained crates](/quest/m2/h264-reader.md) - moq-mux parses SPS VUI with h264-reader and shares one bit reader across codec config parsers - [avcC and hvcC through mp4-atom](/quest/m2/mp4-atom-config.md) - moq-mux stops hand-writing the decoder config records its mp4-atom dependency already parses - [Relay metrics through prometheus-client](/quest/m2/relay-prometheus-client.md) - `/metrics` renders through the maintained client with the same series -- [IETF request order](/quest/m2/ietf-request-order.md) - d14/15 requests take their ID after opening, legacy `control.sending()` races are gone, and SUBSCRIBE_NAMESPACE never closes over its own close - [JS IETF request streams](/quest/m2/js-request-stream.md) - `RequestStream` owns deadlines and cancellation, and TRACK trailing bytes close the session like Rust - [moq-transport finish interop](/quest/m2/ietf-finish.md) - the interop `finish` case runs on every even moq-transport draft instead of being skipped - [Join after a quick finish](/quest/m2/ietf-join-after-finish.md) - a pre-draft-20 joining FETCH that arrives after its subscription finished is still answered, within a bound the peer already negotiates diff --git a/quest/m2/ietf-join-after-finish.md b/quest/m2/ietf-join-after-finish.md index 7c9f790a5b9..5163e38ebe3 100644 --- a/quest/m2/ietf-join-after-finish.md +++ b/quest/m2/ietf-join-after-finish.md @@ -89,4 +89,3 @@ refused. - [JS joining FETCH](/quest/m2/ietf-js-joining-fetch.md) - the JS publisher mirrors the bound settled here - [Interop covers late join and FETCH](/quest/m1/interop-coverage/late-join.md) - the cross-implementation joining FETCH case -- [IETF request order](/quest/m2/ietf-request-order.md) - d14/15 request IDs and their ordering races diff --git a/quest/m2/ietf-request-order.md b/quest/m2/ietf-request-order.md deleted file mode 100644 index da3e86352e3..00000000000 --- a/quest/m2/ietf-request-order.md +++ /dev/null @@ -1,33 +0,0 @@ -# [S] IETF requests take their ID in send order - -## Goal - -On moq-transport drafts 14 and 15, a request never spends an ID it cannot -send, legacy request paths have no `control.sending()` races, and an outbound -SUBSCRIBE_NAMESPACE never closes the session over its own local close. - -## Plan - -Decided 2026-10-21 with the maintainer: m2, since legacy IETF drafts are low -priority. - -- **ID order.** Rust `advertise_namespace` takes its request ID before - opening the stream. On a d14/15 timeout that leaves a gap, and a peer that - requires the next ID refuses every later request. Open first, then take - the ID. -- **`control.sending()` races.** Audit the other legacy paths for the same - shape ([moq-dev/moq#6126](https://github.com/moq-dev/moq/pull/6126)). -- **SUBSCRIBE_NAMESPACE self-close.** A local close during the counted - initial set may make the read loop see an end and throw "namespace stream - ended inside the initial set", closing the session over our own close - ([moq-dev/moq#6130](https://github.com/moq-dev/moq/pull/6130)). - Unverified: reproduce it before fixing, and drop the bullet if it does not - reproduce. - -Each fix lands with a mocked-time regression test on the affected drafts. - -Public API: none. Wire: none. - -## Related - -- [JS IETF request streams](/quest/m2/js-request-stream.md) - the JS side of the legacy request cleanup diff --git a/quest/m2/js-request-stream.md b/quest/m2/js-request-stream.md index b1a39614598..7ffeb84c579 100644 --- a/quest/m2/js-request-stream.md +++ b/quest/m2/js-request-stream.md @@ -19,7 +19,3 @@ the session as it does in Rust. ([moq-dev/moq#6158](https://github.com/moq-dev/moq/pull/6158)). Match Rust. Public API: none (`Deadline` and `RequestStream` are internal). Wire: none. - -## Related - -- [IETF request order](/quest/m2/ietf-request-order.md) - the Rust side of the legacy request cleanup