diff --git a/.claude/skills/quest-audit/SKILL.md b/.claude/skills/quest-audit/SKILL.md index 8ae2c71b74..9def5e6c9c 100644 --- a/.claude/skills/quest-audit/SKILL.md +++ b/.claude/skills/quest-audit/SKILL.md @@ -1,6 +1,6 @@ --- name: quest-audit -description: Audit outstanding quests for disagreements, conflicts, misaligned priorities, and stale plans, then resolve them with /quest-plan. Use when the user invokes /quest-audit or asks to check the quest tree for consistency. +description: Audit and fix outstanding quests for disagreements, conflicts, misaligned priorities, and stale plans. Use when the user invokes /quest-audit or asks to audit and fix the quest tree. --- Run `quest skill audit` and follow its output. diff --git a/flake.lock b/flake.lock index a2f2032434..7eb4e50327 100644 --- a/flake.lock +++ b/flake.lock @@ -59,17 +59,17 @@ ] }, "locked": { - "lastModified": 1791747381, - "narHash": "sha256-8K06EqlDr5V/TfHb4cd4fQYxyYzgPD46wrdlyqvV50g=", + "lastModified": 1792127923, + "narHash": "sha256-Stk/OXrhbmm4DAEMo6uyn4VqhGKrgAlsXZMiPIPCm2M=", "owner": "kixelated", "repo": "quest", - "rev": "b7027a3ded4b0a36505f40eb7ed788c1023e7a49", + "rev": "fa5f1fc9b49c8e7f5dd85af1aabead22f681ec01", "type": "github" }, "original": { "owner": "kixelated", "repo": "quest", - "rev": "b7027a3ded4b0a36505f40eb7ed788c1023e7a49", + "rev": "fa5f1fc9b49c8e7f5dd85af1aabead22f681ec01", "type": "github" } }, diff --git a/flake.nix b/flake.nix index 0af367e48f..a027883264 100644 --- a/flake.nix +++ b/flake.nix @@ -27,7 +27,7 @@ # The quest CLI, which also serves the quest guide and skills the stubs in # .claude/skills call. Bump the rev to upgrade them. quest = { - url = "github:kixelated/quest/b7027a3ded4b0a36505f40eb7ed788c1023e7a49"; + url = "github:kixelated/quest/fa5f1fc9b49c8e7f5dd85af1aabead22f681ec01"; inputs.nixpkgs.follows = "nixpkgs"; inputs.flake-utils.follows = "flake-utils"; }; diff --git a/quest/README.md b/quest/README.md index 48b5651cdb..f208235dd5 100644 --- a/quest/README.md +++ b/quest/README.md @@ -21,7 +21,7 @@ Critical fixes continue to reach the existing release through backports. ## Required -- [m0: immediate priorities](/quest/m0/README.md) - everything in flight now: relay hardening and the IETF interop fixes for Seattle, and broadcast epochs -- [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 +- [m0: immediate priorities](/quest/m0/README.md) - urgent correctness fixes and maintenance of the current release +- [m1: next wave](/quest/m1/README.md) - the agreed next breaking cut, with its required correctness and validation +- [m2: later work](/quest/m2/README.md) - release execution first, then deferred features, optimization, and design studies - [m3: deferred](/quest/m3/README.md) - gated on the outside world (hardware, a partner, a consumer, a provider's offer, or an upstream release), or speculative with no named consumer diff --git a/quest/m0/README.md b/quest/m0/README.md index d344a5dfde..cd2eefceab 100644 --- a/quest/m0/README.md +++ b/quest/m0/README.md @@ -77,10 +77,19 @@ have fixes on `main`: macOS tests (#5335), drill-sensitivity (#5338), loom ## Required +- [Stitcher public relay smoke](/quest/m0/smoke-stitcher.md) - the first-party deployment is tested under an anonymous namespace and only on transports it serves + +- [qmux 0.5 upstream](/quest/m0/qmux-credit-backport-upstream.md) - old-release publication, independent of the main fix + +- [qmux release backport](/quest/m0/qmux-credit-backport.md) - adopt the 0.5 fix independently of the next breaking cut + +- [Lite SETUP backport](/quest/m0/lite-setup-backport.md) - `release` closes a moq-lite session on a repeated SETUP parameter or a second Setup Stream, in a later 0.14.x patch (#5521) +- [Kotlin wrapper POM release](/quest/m0/kt-ffi-pom.md) - `dev.moq:moq` 0.5.2 ships from `release`, its POMs naming moq-ffi at `[0.4.3,0.5)` + - [CI cost and latency](/quest/m0/ci/README.md) - PRs stop queueing behind hosted runner caps: macOS and Windows move to the merge queue, caches move to R2, and setup is skipped when no work is selected - [Draft-22 media on 0.17](/quest/m0/release-22/README.md) - a 0.17.x with the LOCATION_FILTER and FIRST_OBJECT fixes and moq-noq 1.3.5, then a patch with draft-20+ FETCH and the unset-clock and quiet-edge fixes - [nixpkgs ships the lld fix](/quest/m0/nixpkgs-lld-fix.md) - waiting on NixOS/nixpkgs#568680 to reach `nixpkgs-unstable`, which the release build fix bumps to - [Release builds link](/quest/m0/release-link.md) - the Linux zigbuild release binaries and moq-gst packages link again after the 2026-10-11 nixpkgs bump, so the next release cut can publish -- [web-transport releases the qmux fixes](/quest/m0/qmux-credit-upstream.md) - waiting on moq-dev/web-transport#412 and #413 to merge and ship, which qmux credit bumps to -- [qmux credit](/quest/m0/qmux-credit.md) - qmux returns connection credit for dropped and stopped streams and delivers its close frame, on both lines +- [web-transport releases the qmux fixes](/quest/m0/qmux-credit-upstream.md) - waiting on #412 to publish the patched 0.6.x that main adopts +- [qmux credit](/quest/m0/qmux-credit.md) - main adopts qmux 0.6 credit and close fixes; the 0.5 backport is independent - [Broadcast epochs](/quest/m0/broadcast-epoch/README.md) - every first-party publisher that can restart mints a fresh route epoch, the newest wins a path, and only routes with the same epoch resume a subscription diff --git a/quest/m0/broadcast-epoch/README.md b/quest/m0/broadcast-epoch/README.md index 00de076c73..3a0dee83cd 100644 --- a/quest/m0/broadcast-epoch/README.md +++ b/quest/m0/broadcast-epoch/README.md @@ -65,11 +65,11 @@ Decided: the lite-07 cut, not this release. It replaces the response-only claim-epoch proposal: an independent route explicitly continues the same served instance, rather than inferring continuity from an epochless claim. -- Decided in the 2026-10-10 resume audit: a catalog reference pins its first - resolved sibling for the catalog instance's lifetime in players and - exporters. Optional reference epochs constrain the first resolution on - epoch-aware transports; on epochless transports they remain best effort. - This supersedes following the newest sibling by name. The +- Decided 2026-10-14: announcement-following readers handle a referenced + sibling's replacement as Restart; other readers pin their first resolution + for the catalog instance's lifetime. Optional reference epochs constrain + resolution on epoch-aware transports and remain best effort on epochless + transports. Following never turns a replacement into seamless resume. The [catalog references quest](/quest/m0/broadcast-epoch/catalog-references.md) owns the schemas, readers, and compatibility documentation. - Every first-party publisher that can restart mints its own: the apps, @@ -108,22 +108,25 @@ maintainer's pre-release merge of `release` into `main` keeps `main`'s The 2026-10-10 resume audit adds release gates for newly issued requests through retired handles and for late duplicate delivery after same-epoch handover. Keep existing reads sticky; stopping new unpinned wire work is a -separate boundary. The catalog-reference work applies the same pinning -policy to players and exporters. Wall-clock expiry, live DROP dispositions, +separate boundary. Catalog references apply the same identity policy to +players and exporters, distinguishing announcement followers from readers +that retain lifetime pins. Wall-clock expiry, live DROP dispositions, FETCH budgets, seamless JS handover, and the IETF extension remain m1. Extend the line's relay/media tests to hold catalog and media from A while B replaces it, exercise later metadata/subscription/FETCH operations, and prove -that following the replacement starts a fresh catalog with fresh pins. Check +that replacing the catalog starts fresh pins. A followed sibling replacement +restarts that sibling while preserving the catalog's explicit epoch +constraints; it never mixes instances through an old handle. Check exact and covering routes, matching/different/absent epochs, and legacy End+Start. The child quests own their focused regressions. ## Required -- [Catalog references](/quest/m0/broadcast-epoch/catalog-references.md) - players and exporters pin siblings consistently, with optional reference epochs and documented legacy best effort +- [Catalog references](/quest/m0/broadcast-epoch/catalog-references.md) - announcement followers restart replaced siblings; other readers retain pins, with optional epochs and legacy best effort ## Related - [JS consume identity](https://github.com/moq-dev/moq/pull/5254) - requests carry the serving prefix's epoch and new winners cannot reuse an old cached broadcast -- [Stats prefix tracks](/quest/m1/stats-prefix-tracks.md) - remains in the breaking release with QoS, independently of epoch correctness +- [Stats prefix tracks](/quest/m2/stats-prefix-tracks.md) - deferred with QoS to m2; neither gates epoch correctness or the next cut diff --git a/quest/m0/broadcast-epoch/catalog-references.md b/quest/m0/broadcast-epoch/catalog-references.md index 8c6ed71eec..f5999c8fee 100644 --- a/quest/m0/broadcast-epoch/catalog-references.md +++ b/quest/m0/broadcast-epoch/catalog-references.md @@ -2,21 +2,21 @@ ## Goal -Catalog, media, and data requests use the same pinned broadcast instance -in players and exporters. Each sibling stays on its first resolved -instance for the catalog instance's lifetime. Optional catalog epochs let -a reference name its intended sibling before that first resolution. +Catalog, media, and data requests use the same broadcast identity in players +and exporters. Announcement-following readers follow a referenced sibling's +Start, End, and Restart; readers without announcement following pin their +first resolution for the catalog's lifetime. Optional reference epochs +constrain the intended identity in either case. ## Plan Decided in the 2026-10-10 resume audit: -- Apply the same pinning policy to players and exporters. This replaces - the earlier rule that a named sibling follows the newest epoch, including - `@moq/watch`'s test that follows a republished rendition independently of - its catalog. A sibling replacement does not silently retarget the old - catalog's reference. A new catalog instance or explicit export stitch - starts fresh pins; same-epoch recovery preserves identity. +- Apply the same identity policy to players and exporters. Per the + 2026-10-14 decision below, announcement-following readers handle a sibling + replacement as Restart, never as seamless same-epoch resume. Readers that + follow no announcements keep their first pin until a new catalog instance + or explicit export stitch. Same-epoch recovery preserves identity. - Add optional `epoch` beside every catalog broadcast reference: video, audio, text, JSON, and binary configs, plus the archive replay reference. Reuse the canonical UUIDv7 representation and validation from MoQ epochs. @@ -31,11 +31,13 @@ Decided in the 2026-10-10 resume audit: - Lazy work has no anticipated epoch: leave it out of unresolved contribution references. Once a worker accepts a job it advertises a concrete path with a unique epoch. A proven Takeover preserves the same pinned instance; a - replacement requires a new catalog instance. An epoch-bound request on an + replacement follows Restart for announcement-following readers and requires + a new catalog instance for other readers. An epoch-bound request on an epoch-capable link requires a matching advertised epoch, not merely an epoch returned from an epochless claim. -- Omission remains compatible with existing catalogs: pin the first - resolved instance, with no promise about an unresolved sibling's past. +- Omission remains compatible with existing catalogs: bind the first + resolved instance, then follow announcements only where the reader opts + into that lifecycle. Make no promise about an unresolved sibling's past. - On epochless transports, explicit catalog epochs are best effort (maintainer choice). Keep the requested identity in the reference, use whatever identity the route can verify, and pin the resolved handle. @@ -46,8 +48,9 @@ Decided in the 2026-10-10 resume audit: Decided by the maintainer, 2026-10-14 (from #5500's Codex review): readers follow a referenced broadcast's announcements, since announcement is the -online/offline signal and `@moq/watch` already behaves this way. The "no -retarget" rule above applies only to readers that follow no announcement. +online/offline signal. The "no retarget" rule applies only to readers that +follow no announcement. An explicit epoch still constrains every resolution; +following does not authorize substituting a different requested epoch. - `moqsrc`: Start binds the rendition, End idles its pad, and Restart restarts its pump. diff --git a/quest/m1/kt-ffi-pom.md b/quest/m0/kt-ffi-pom.md similarity index 100% rename from quest/m1/kt-ffi-pom.md rename to quest/m0/kt-ffi-pom.md diff --git a/quest/m1/lite-setup-backport.md b/quest/m0/lite-setup-backport.md similarity index 100% rename from quest/m1/lite-setup-backport.md rename to quest/m0/lite-setup-backport.md diff --git a/quest/m0/qmux-credit-backport-upstream.md b/quest/m0/qmux-credit-backport-upstream.md new file mode 100644 index 0000000000..2c28a78b8b --- /dev/null +++ b/quest/m0/qmux-credit-backport-upstream.md @@ -0,0 +1,12 @@ +# [XS] web-transport releases the qmux 0.5 credit and close fixes + +## Goal + +Waiting on moq-dev/web-transport: #413 merges into qmux-v0.5.x and ships in +a qmux 0.5.x release after 0.5.2. Check +`gh pr view 413 -R moq-dev/web-transport` and crates.io. Delete this condition +once shipped; the 0.6 publication can complete independently. + +## Related + +- [Release pin](/quest/m0/qmux-credit-backport.md) - adopts the published fix diff --git a/quest/m0/qmux-credit-backport.md b/quest/m0/qmux-credit-backport.md new file mode 100644 index 0000000000..1500545593 --- /dev/null +++ b/quest/m0/qmux-credit-backport.md @@ -0,0 +1,22 @@ +# [XS] Backport the qmux credit and close pin + +## Goal + +The current release uses a patched qmux 0.5.x that returns unread stream +credit and delivers its close frame. It stays on web-transport-trait 0.4. + +## Plan + +Once the upstream 0.5 fix ships, update the release-line pin and run the +WebSocket and TLS transport checks. Follow the backport workflow. This is +independent of the 0.6 main pin and does not gate the next breaking cut. + +Public API: none. Wire: none. + +## Required + +- [qmux 0.5 publication](/quest/m0/qmux-credit-backport-upstream.md) - the release-line patch is available + +## Related + +- [qmux credit](/quest/m0/qmux-credit.md) - shared diagnosis and the main pin diff --git a/quest/m0/qmux-credit-upstream.md b/quest/m0/qmux-credit-upstream.md index 9f1e9d4350..ee8f7111a4 100644 --- a/quest/m0/qmux-credit-upstream.md +++ b/quest/m0/qmux-credit-upstream.md @@ -1,14 +1,12 @@ -# [XS] web-transport releases the qmux credit and close fixes +# [XS] web-transport releases the qmux 0.6 credit and close fixes ## Goal -Waiting on moq-dev/web-transport: the qmux credit and close fixes merge and -ship in a 0.6.x release after 0.6.2 and a 0.5.x release after 0.5.2. Check with -`gh pr view 412 -R moq-dev/web-transport` (0.6, targets `main`), the same for -413 (0.5, targets `qmux-v0.5.x`), and the published `qmux` versions on -crates.io. Advance it by pushing the upstream review along. Delete this quest -once both releases are out. +Waiting on moq-dev/web-transport: #412 merges and ships in a qmux 0.6.x +release after 0.6.2. Check `gh pr view 412 -R moq-dev/web-transport` and +the published qmux versions on crates.io. Delete this condition once shipped. +The independent 0.5 publication does not block this condition. ## Related -- [qmux credit](/quest/m0/qmux-credit.md) - bumps `main` and `release` once these ship +- [qmux credit](/quest/m0/qmux-credit.md) - bumps main after publication diff --git a/quest/m0/qmux-credit.md b/quest/m0/qmux-credit.md index 585a20f00d..87a3db9eb6 100644 --- a/quest/m0/qmux-credit.md +++ b/quest/m0/qmux-credit.md @@ -7,8 +7,8 @@ whether the app reads it, drops the stream unread, or it arrives after STOP_SENDING, so a long-lived session never stalls on MAX_DATA. `close()` delivers its APPLICATION_CLOSE frame before the transport drops, so a TCP or WebSocket peer sees the close code whenever the transport stays writable -within the close bound. Both hold on the 0.5 line that -`release` pins and the 0.6 line `main` uses. +within the close bound. This quest updates the 0.6 line `main` uses; the +0.5 release-line backport is independent. ## Plan @@ -43,10 +43,10 @@ targets `qmux-v0.5.x`): delivering past its initial window, and a peer reads the close code after a close issued mid-write. -Remaining here, once both ship: bump `main` to that 0.6.x and backport -`release`'s pin to that 0.5.x. `release` stays on 0.5: 0.6 needs web-transport-trait 0.5, a -breaking change. Sized XS (2026-10-08), since the fixes live upstream and only -the pin bumps remain here. +Remaining here: bump `main` once the patched 0.6.x ships. The independent +[release backport](/quest/m0/qmux-credit-backport.md) stays on 0.5 because +0.6 needs web-transport-trait 0.5, a breaking change. The old-line publication +is not a prerequisite of m1. Sized XS: only the pin bump remains here. Why m0: the WebSocket fallback, moq.pro's Voice, and the edge-to-core `tls://` links (#4816) that moq.pro's cluster sessions move to all run on @@ -56,7 +56,7 @@ Public API: none. Wire: none. ## Required -- [web-transport releases the fixes](/quest/m0/qmux-credit-upstream.md) - #412 and #413 merge and ship as patched 0.6.x and 0.5.x +- [web-transport releases the fixes](/quest/m0/qmux-credit-upstream.md) - #412 merges and ships as a patched 0.6.x ## Related diff --git a/quest/m1/smoke-stitcher.md b/quest/m0/smoke-stitcher.md similarity index 100% rename from quest/m1/smoke-stitcher.md rename to quest/m0/smoke-stitcher.md diff --git a/quest/m1/README.md b/quest/m1/README.md index 8f90fa4906..bb16590634 100644 --- a/quest/m1/README.md +++ b/quest/m1/README.md @@ -8,51 +8,39 @@ changes. Complete this milestone before cutting main into release. ## Plan -Decided with the maintainer on 2026-10-13: - -- Clustering stability is the first goal: an ended broadcast cannot return - through a stale route; a working backup recovers; matching-epoch handover - preserves eligible content; a new epoch produces an explicit Restart. - Catalogs, long-lived readers, and late joiners must obey the same contract. -- Prioritize the in-tree QUIC stack (`moq-quic`) beside routing. Keep its - correctness, interoperability, memory, and performance evidence; a passing simulator alone does not validate a real transport. -- Finish planned breaking APIs in this release, including public clocks, - signed priority, binding shape, watch worker, and the QoS lag API. Keep - prerequisites of those changes here even when a prerequisite is additive. - New optional capabilities and general optimization work follow in m2. +Decided with the maintainer on 2026-10-15: keep a finite next breaking +release, with clustering stability and the in-tree QUIC stack first. + +- Preserve epoch correctness, routing recovery, outbound federation, and + announcement takeover on both lite and IETF. Catalogs, long-lived readers, + and late joiners obey the same identity contract. +- Keep IETF passthrough, the public model audit, the broad clock conversion, + QUIC endpoint sharding, archive replay/DVR correctness, and the TS fidelity + expansion in m1. Keep their correctness, interoperability, memory, and + performance evidence with them. +- Defer P2P, watch-worker planning and implementation, AMD Vulkan encode and + Intel VA-API/Vulkan import, QoS lag and prefix stats, and active-active + same-epoch importers to m2 with their prerequisites. Their possible API + breaks do not make them prerequisites of this cut. Existing playback and + A/V clock validation remain here. Same-epoch importers require the epoch + anchor and raw TS epoch cuts before claiming redundant-import support. - Finalized lite-07 is preferred by first-party Rust and JS clients, with - older-version negotiation fallback. Servers accept it by default. The wip - identifier is never reused for an incompatible mixed-version rollout. -- Validate the GPU input surface on AMD and Intel before publishing it; - vendor-neutral capacity and health reporting remains m2. -- Package publication, downstream adoption, and production rollout are - successive stages, not prerequisites of the cut that makes them possible. - Their owner is [release execution](/quest/m2/release/README.md). - -The route handoff and watch-worker planning quests settle their existing -implementation choices before code is written. The binding namespace spike -must produce a go/no-go before generated package names freeze. Existing -upgrade and identity guides carry migrations; feature docs change inline. -Keep generic proofs in this repository; deployments and their operational -acceptance belong to the downstream operator. - -The 2026-10-13 clustering interview brings P2P back to m1 with a required -consumer: application-selected peer paths alongside persistent CDN sessions. -Routing proofs precede the shared wire; federation and P2P ship independently, -while epoch-only active/passive route health follows in m2. - -Keep one owner per shared code area. Order each prerequisite before its -consumer; targeted benchmarks and controlled-time regressions remain part of -the feature that needs them. - -Added 2026-10-13: moq-transport passthrough, ranked beside interop, keeps -the fields a relay must forward crate-private, an explicit exception to the -public model plan. - -Added 2026-10-13 by the redundant TS alignment plan: epoch refusals, the -epoch anchor, and raw TS cuts on the epoch timeline, ranked just above -same-epoch importers, which requires them. Catalog clock adoption ranks -above them. + older-version negotiation fallback. Servers accept it by default. Never + reuse a published identifier for an incompatible wire change; deferred + features use the appropriate subsequent wip/version if they need one. +- The next cut makes no new AMD/Intel GPU-input support promise. The later + multi-vendor release needs both hardware proofs before publication. +- Package publication, downstream adoption, and production rollout follow + the cut under [release execution](/quest/m2/release/README.md). + +The route handoff planning quests settle their implementation choices before +code is written. Existing upgrade and identity guides carry migrations; +feature docs change inline. Keep generic proofs here and deployments and +their operational acceptance with the downstream operator. + +Keep one owner per shared code area and order prerequisites before consumers. +The moq-transport passthrough fields remain crate-private, the existing +explicit exception to the public model audit. ## Required @@ -62,16 +50,13 @@ above them. - [Restart followers](/quest/m1/restart-followers.md) - first-party consumers follow an epochless `Restart`, from a GOAWAY drain or a publisher replacement, by resolving the path again - [Cluster routing](/quest/m1/cluster-routing/README.md) - prove shared routing contracts, implement per-origin routes, and connect outbound local clusters -- [P2P](/quest/m1/p2p/README.md) - application-selected peer sessions use shared priorities, routing, and same-epoch handover -- [Routing guide](/quest/m1/routing-guide.md) - concepts and practical outbound federation/P2P setup +- [Routing guide](/quest/m1/routing-guide.md) - concepts and practical outbound federation setup - [Controlled time](/quest/m1/time/README.md) - every crate and package reads time through moq-clock or @moq/clock, with regression tests for the changed contracts - [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 - [Finalize moq-lite-07](/quest/m1/lite07-finalize.md) - when the maintainer cuts it, lite-07 negotiates as `moq-lite-07` and the next release ships it - [moq-transport passthrough](/quest/m1/ietf-extension/README.md) - object IDs, properties, end status, datagram objects, and unknown track properties survive moq-transport -> moq-relay (and a relay cluster) -> moq-transport; moxygen's moq-test sections 1-7 pass nightly, except cases that use a subgroup other than 0 - [Moxygen minimal interop](/quest/m1/smoke-moxygen.md) - waiting on Moxygen to send a registered filter type, then the smoke lane stops hiding its failure -- [Stitcher public relay smoke](/quest/m1/smoke-stitcher.md) - the first-party deployment is tested under an anonymous namespace and only on transports it serves - [FETCH max-delay](/quest/m1/fetch-max-delay.md) - historical readers obey publisher max-age and an optional tighter reader budget, including while holding a group -- [Lite SETUP backport](/quest/m1/lite-setup-backport.md) - `release` closes a moq-lite session on a repeated SETUP parameter or a second Setup Stream, in a later 0.14.x patch (#5521) - [Claim answer epochs](/quest/m1/claim-answer-epochs.md) - a claim answers each output by publishing it at its exact path under its own epoch, so a restarted output never reaches a returning reader as the old one's cache - [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 - [Early drops](/quest/m1/subscribe-drop-early.md) - Rust and JS publishers drop a sequence the moment they give it up, and forward an upstream drop @@ -84,18 +69,14 @@ above them. - [Lazy discovery](/quest/m1/lazy-discovery/README.md) - a `consume` session requests announcements only for prefixes the origin is watching, in JS then Rust; `follow`, `broadcasts`, and the `discovery` option go away - [Announce caught up](/quest/m1/announce-live.md) - Rust and JS announce consumers expose when a session's initial announce snapshot has arrived, from the counts the wire already carries where a version has them - [A spinning loop fails a sim test](/quest/m1/spinning-loops.md) - a sim test names any loop that holds a task poll too long, and each one yields through a budget -- [Kotlin wrapper POM release](/quest/m1/kt-ffi-pom.md) - `dev.moq:moq` 0.5.2 ships from `release`, its POMs naming moq-ffi at `[0.4.3,0.5)` -- [Epoch anchor](/quest/m1/epoch-anchor.md) - same-epoch importers each derive the catalog clock and timestamp era from the epoch and stay live, so they agree across a 33-bit wrap, a forward clock break, and a restart -- [Raw TS cuts on the epoch timeline](/quest/m1/ts-passthrough-epoch-cuts.md) - passthrough fallback cuts fall on the epoch's grid and the M2ts section derives from the epoch and content, so instances started apart publish identical groups and catalogs -- [Same-epoch importers](/quest/m1/hop-aligned-import.md) - importers sharing one `--epoch` and fed one stream publish identical groups and timestamps, so failover between a redundant pair survives - [Importers announce with a track](/quest/m1/import-declared-track.md) - every importer announces once its catalog names a servable track (a rendition or an extension track like `m2ts`), and input that ends with none exits non-zero unannounced - [Data consumer timestamps](/quest/m1/data-consumer-timestamps.md) - json and binary consumers return each value's timestamp, in Rust and every binding; snapshots add `latest()` beside an in-order `next()` - [JSON window timestamps](/quest/m1/json-window-timestamps.md) - Rust and JS window consumers return each event's frame timestamp like the snapshot and stream consumers - [JavaScript FETCH](/quest/m1/js-fetch.md) - browser publishers answer IETF FETCH through the JS ranges request surface - [IETF fill timeout](/quest/m1/ietf-fill-timeout.md) - native FILL_TIMEOUT bounds upstream waiting on drafts 18-22, alongside local content-age enforcement -- [Browser follows a renamed track](/quest/m1/browser-rename-check.md) - a real browser keeps playing when `@moq/publish` renames a track after a codec change or ceiling growth +- [Browser follows a renamed track](/quest/m1/browser-rename-check.md) - a real browser keeps playing when `@moq/publish` renames a track after a codec change, while size-only changes keep their identity - [Rust identity enforcement](/quest/m1/catalog-identity-rs.md) - `moq-mux` refuses a redefined rendition, and every Rust publisher mints a new rendition name for a decoder identity change and updates everything else in place -- [Archive](/quest/m1/archive/README.md) - record selected tracks to any object_store and replay them over FETCH or derived HLS; the catalog entry and format may break in place, since no archives exist +- [Archive](/quest/m1/archive/README.md) - record selected tracks to any object_store and replay them over FETCH or derived HLS; preserve published format boundaries while completing replay and DVR correctness - [In-band auth](/quest/m1/auth/README.md) - a session tells its peer what it may publish and subscribe to, unions tokens presented in band, and fails loud on an out-of-scope publish - [IETF epochs](/quest/m1/ietf-epochs.md) - negotiate explicit broadcast identity on drafts 17-22, retaining unchanged paths and epochless clients on updated relays - [Announcement takeover](/quest/m1/announce-takeover.md) - a new independent route explicitly continues instances served through its predecessor, with shared Rust/JS semantics and lite-07 wire @@ -106,25 +87,13 @@ above them. - [Session report parity](/quest/m1/obs-session-report.md) - a session reports its negotiated draft and reconnect failures, so the OBS dock shows them again - [Generated C++ shape](/quest/m1/cpp-generated-shape.md) - `moq::Client` is the generated type itself and `moq::expected` is one type at every C++ standard - [Generated C bindings](/quest/m1/c/README.md) - C generated from moq-ffi ships as `moq-c` 0.8.0 and replaces the hand-written libmoq -- [BoltFFI or UniFFI](/quest/m1/boltffi.md) - BoltFFI vs UniFFI on a moq-ffi slice in Python, Kotlin, and Swift decides which one the moq-dev fork starts from -- [Hard-fork the binding generator](/quest/m1/uniffi-fork.md) - one moq-dev repo holds the chosen generator's core and every language backend, released under one tag and one pin -- [Benchmark trend site](/quest/m1/bench-site.md) - a GitHub Pages site charts the binding, Rust, and JS nightly benchmarks over time -- [Link the benchmark trends](/quest/m1/bench-site-doc.md) - the docs point language choosers at the trend site -- [Swift, Dart, and C++ benchmarks](/quest/m1/ffi-bench-wrappers.md) - the other three wrappers join the nightly binding benchmark -- [Payload bytes](/quest/m1/ffi-bytes.md) - payloads cross the FFI in one bulk copy, proven on the binding benchmark in every wrapper -- [Kotlin calls](/quest/m1/kt-ffi-calls.md) - Kotlin calls into moq-ffi cost about what Python's and Go's do, sync and async, once the fork's Kotlin generator drops JNA's per-call overhead -- [Poll-native async spike](/quest/m1/ffi-poll.md) - measure kio poll-native exports and foreign poll against `ffi::Task`, then pick the shape -- [Rendition preference](/quest/m1/rendition-preference.md) - automatic selection by ``, `Video::ranked`, and WHEP keeps the highest `preference` that decodes, so a compatibility transcode is only picked when nothing preferred decodes - [Rust PROBE on demand](/quest/m1/probe-on-demand-rs.md) - a Rust client opens PROBE only while a `recv_bandwidth()` consumer is held, and `Stats` drops `estimated_recv_rate` -- [QoS](/quest/m1/qos/README.md) - broadcast health: relay starvation and timeliness histograms - [Session outcomes](/quest/m1/session-outcomes.md) - the relay's session stats count refusals by reason and ends by kind, per root and tier, including refusals an embedder records on its stats handle - [Typed refusal reason](/quest/m1/refusal-reason.md) - an auth server's 403 names the reason (including `expired`) and the root and tier, so the relay's session outcomes count and attribute refusals - [JS track handover](/quest/m1/js-group-handover.md) - a JS track subscription resumes across a route swap from the first frame it lacks, so `test/drain` passes at zero latency budget - [Scope track priority](/quest/m1/track-priority-scope.md) - priority orders one owner's streams, and a shared cluster session is fair across tenants - [Audio jitter target](/quest/m1/audio-jitter-target/README.md) - the playout target is default in both languages; a manual browser proof and a native trace replay remain - [A/V clock](/quest/m1/av-clock.md) - the audio playhead drives Sync.reference while audio plays, through per-track sync handles -- [Plan: watch worker](/quest/m1/plan-watch-worker.md) - prototype an invisible page worker against app-spawned workers, and land the jank harness that decides -- [Watch worker](/quest/m1/watch-worker.md) - watch playback runs in a worker onto an OffscreenCanvas, so main-thread jank never stalls video or audio - [Publish channel count](/quest/m1/publish-audio-channel-count.md) - forcing a channel count on an Audio.Capture stops costing the subscriber gaps of silence, and the media fixture's cold-start audio-tone flake is fixed at its cause - [JS request deadline](/quest/m1/js-request-deadline.md) - each JS request, PUBLISH_NAMESPACE included, has one fatal 10 s timer from create to answer and fails fast without stream credit, so nothing queues or retries - [Handshake deadline covers admission](/quest/m1/handshake-admission-deadline.md) - `listen.timeout` bounds a connection through auth admission on both listeners, replacing the hardcoded admission timeout @@ -144,16 +113,11 @@ above them. - [Plan: HLS export failure signal](/quest/m1/hls-failure-signal.md) - decide what players see when the exporter's timeline fails, live or history, so a truncated recording never looks complete - [Close tail drain](/quest/m1/close-tail-drain.md) - after a peer close, subscribers read the group streams their transport already delivered instead of aborting them, so a finished tail arrives on every version - [Publisher linger](/quest/m1/publisher-linger.md) - every finishing publisher keeps its broadcast up for the media max-age so subscribers can drain it -- [Vulkan Video encode on AMD](/quest/m1/vulkan-encode.md) - H.264 and H.265 from an external Vulkan image on RADV, hand-rolled on ash, proven on an RX 9070 -- [VA-API encodes an external Vulkan image](/quest/m1/vaapi-vulkan-import.md) - an explicit-sync DMA-BUF reaches Intel's encoder, proven on Arrow Lake - [Malformed moq-transport input](/quest/m1/ietf-malformed-close.md) - malformed draft-18 and draft-21 control input closes the session with the draft's code, or PROTOCOL_VIOLATION where a code is a real burden and the fallback is recorded in `doc/concept/standard.md`, in moq-net and js/net - [Binding track resolver](/quest/m1/binding-track-helper.md) - binding apps subscribe to a rendition without inlining `track` or the key - [Exporters follow a rendition to a new track](/quest/m1/export-rendition-rebind.md) - the fMP4, TS, MKV and FLV exporters rebuild a rendition whose resolved `track` or `broadcast` changes under the same ID -- [Stats prefix tracks](/quest/m1/stats-prefix-tracks.md) - additive tracks required by the breaking lag API; existing maps remain supported - - [C++ and OBS package proof](/quest/m1/cpp-package-check.md) - the installed artifacts pass before their first release -- [C++ FFI reuse](/quest/m1/cpp-ffi-reuse.md) - C++ packages link the shared default-feature FFI build once it is proven compatible ## Related diff --git a/quest/m1/archive/README.md b/quest/m1/archive/README.md index 14f6308127..57cd05feda 100644 --- a/quest/m1/archive/README.md +++ b/quest/m1/archive/README.md @@ -19,12 +19,12 @@ landed on main; the line may still reshape both. ### Compatibility -Decided (2026-09-28): the line may break the hang `archive` catalog entry and -the recording format in place on `main`, without a version bump -(for example the track-timeline rework in #4280). No archives exist -yet, so nothing recorded or published depends on either shape. This is an -exception for this line only; once a release ships -recordings, later format changes go through the entry's format version. +The release branch and `moq-archive-v0.0.14` carry format v1; main carries +v2. Published recordings therefore exist as a compatibility obligation, even +without evidence of deployed archives. Preserve version recognition and +explicit refusal of unsupported versions. Before reshaping v2, check whether +it has published; a published format changes through a new version. The old +blanket permission to break recordings in place no longer applies. ### Landed diff --git a/quest/m1/archive/declared-duration.md b/quest/m1/archive/declared-duration.md index 25c56e9c0f..1b5570fb55 100644 --- a/quest/m1/archive/declared-duration.md +++ b/quest/m1/archive/declared-duration.md @@ -15,8 +15,10 @@ the catalog declares a usable target: - **Shape.** Each `timelines` entry becomes an object: `timelines: { video: { track: "video.timeline.z", duration: 4000 } }`, in - the archive `timescale`. The root `durationMax` goes away. This is a break in - place on this line, with no compatibility path. Update `rs/hang`, + the archive `timescale`. The root `durationMax` goes away. Check whether + this format has published before changing it and preserve published version + boundaries per the archive README; do not assume an in-place break is safe. + Update `rs/hang`, `drafts/draft-lcurley-moq-hang.md`, and every `durationMax` reference (grep it, including `doc/`), and the JS catalog schema (`js/hang/src/catalog/archive.ts`) in the same PR, since `@moq/watch` refuses diff --git a/quest/m1/audio-jitter-target/watch.md b/quest/m1/audio-jitter-target/watch.md index 6c5dff9506..95d830c3d6 100644 --- a/quest/m1/audio-jitter-target/watch.md +++ b/quest/m1/audio-jitter-target/watch.md @@ -51,9 +51,9 @@ about 22 s with skip-aheads on the way down. output. Audio now defaults `groupDuration` to 20 ms. A/B that default against a group of 100 ms or so in the same run. -Decided 2026-10-08: moves to m1 with the rest of the line. The estimator -defaults on and only this manual proof is left, so it no longer gates a -release. +Decided 2026-10-15: keep this proof in m1 with the existing playback and +A/V clock work. The estimator defaults on; deferring watch-worker does not +defer validation of the current player. ## Required diff --git a/quest/m1/auth/README.md b/quest/m1/auth/README.md index a652615ee3..f93be9089c 100644 --- a/quest/m1/auth/README.md +++ b/quest/m1/auth/README.md @@ -17,7 +17,7 @@ grant per token, the union of every accepted token as the session's scope, and a loud failure when a publish can never be honored. It ends with the credential able to travel in band, while the URL keeps working for every peer that predates the stream. Node-bound peer grants for direct sessions belong to -[P2P](/quest/m1/p2p/peer-grant.md), their only consumer. +[P2P](/quest/m2/p2p/peer-grant.md), their only consumer. ## Plan @@ -161,7 +161,7 @@ because `main` assigned 0x3A to NOT_FETCHABLE first. session with PROTOCOL_VIOLATION in Rust and JS - [JS fetch grant watch](https://github.com/moq-dev/moq/pull/5260) - a JS `fetchGroup` ends `Unauthorized` when its path leaves the grant -- [Peer grants](/quest/m1/p2p/peer-grant.md) - P2P's node-bound credential, +- [Peer grants](/quest/m2/p2p/peer-grant.md) - P2P's node-bound credential, built on this line's relay tokens - [#5269](https://github.com/moq-dev/moq/pull/5269) - explicit Lite AUTH NOT_SUPPORTED, including grant updates diff --git a/quest/m1/av-clock.md b/quest/m1/av-clock.md index a8582af8af..6820aeeef1 100644 --- a/quest/m1/av-clock.md +++ b/quest/m1/av-clock.md @@ -66,4 +66,4 @@ Recommendations for the implementation: ## Related - [Time stretch](/quest/m2/watch-audio-time-stretch.md) - stretching needs a clock to converge toward -- [Watch worker](/quest/m1/watch-worker.md) - moves `Sync` into a worker afterwards; keep the handles free of main-thread assumptions +- [Watch worker](/quest/m2/watch-worker.md) - moves `Sync` into a worker afterwards; keep the handles free of main-thread assumptions diff --git a/quest/m1/browser-rename-check.md b/quest/m1/browser-rename-check.md index 5f3c314ed3..656b7cab5f 100644 --- a/quest/m1/browser-rename-check.md +++ b/quest/m1/browser-rename-check.md @@ -2,20 +2,21 @@ ## Goal -A real browser keeps playing across both renames -[#5552](https://github.com/moq-dev/moq/pull/5552) introduced: a codec change, -and a `Video.Encoder` growing past its coded-size ceiling. In both cases -`@moq/publish` mints a new `track` for the same rendition, and `js/watch` -follows it without a stall. The check runs at least nightly in CI. +A real browser keeps playing when a codec identity change makes +`@moq/publish` mint a new track for the same rendition. A size-only change, +including coded-size ceiling growth, keeps the track identity and also plays +without a stall. The check runs at least nightly in CI. ## Plan -#5552 landed with unit tests only and listed this check as a follow-up. Unit -tests do not show what WebCodecs and the watch path do across the switch. +The publisher's `broadcast.ts` excludes size from decoder identity; its unit +tests cover same-track size changes. Exercise that contract through WebCodecs +and watch using the existing browser media QA (`just test media`). -Extend the browser media QA (`just test media`) rather than building a new -harness. Fail on a stall, a decode error, or a frame from the old track after -the switch. Check that it fails with the rename disabled. +Test a codec change that requires a new track and a size-only change that +must preserve it. Assert the expected identity in each case, continued +playback, and no decode errors. The codec case must fail with renaming +disabled; the size case must catch an unnecessary rename. Public API: none. Wire: none. diff --git a/quest/m1/cluster-routing/README.md b/quest/m1/cluster-routing/README.md index a891d26210..1ed97dc9e3 100644 --- a/quest/m1/cluster-routing/README.md +++ b/quest/m1/cluster-routing/README.md @@ -76,8 +76,10 @@ Decided with the maintainer, 2026-10-13: explicitly select P2P connections; discovery does not dial everyone. - Each CDN broadcast enters a local network once by configuration: local peers outrank a backup uplink, and origin aggregation joins the rest. -- Retain the CDN session and discovery during P2P use. Release unused CDN - media demand, except for bounded overlap during a same-epoch handoff. +- The deferred P2P consumer owns proving that its CDN session and discovery + stay connected and unused CDN media demand is released after a handoff. + m1 provides and tests the shared routing primitives without requiring an + actual P2P integration. The implementation is gated on [handoff proofs](/quest/m1/cluster-routing/handoff.md): a complete neighbor snapshot does not prove its claims are current. The @@ -125,8 +127,8 @@ setup after the capabilities land. ## Related - [Route health](/quest/m2/route-health/README.md) - epoch-only active/passive delivery monitoring and automatic recovery -- [P2P](/quest/m1/p2p/README.md) - application-selected peer sessions feed the shared routing model -- [Routing guide](/quest/m1/routing-guide.md) - concept page and practical federation/P2P setup +- [P2P](/quest/m2/p2p/README.md) - application-selected peer sessions feed the shared routing model +- [Routing guide](/quest/m1/routing-guide.md) - concept page and federation setup; P2P extends it in m2 - [Route wakes](/quest/m2/route-wakes.md) - local work follows affected fronts rather than the whole covered table - [Link quality](/quest/m2/link-quality.md) - optional measured link cost, distinct from delivery-health failover -- [Same-epoch importers](/quest/m1/hop-aligned-import.md) - independent origins serving the same explicit epoch +- [Same-epoch importers](/quest/m2/hop-aligned-import.md) - independent origins serving the same explicit epoch diff --git a/quest/m1/cluster-routing/identity.md b/quest/m1/cluster-routing/identity.md index 7fc19fffc1..37027d5368 100644 --- a/quest/m1/cluster-routing/identity.md +++ b/quest/m1/cluster-routing/identity.md @@ -16,7 +16,7 @@ Decided 2026-10-14 with the maintainer: and therefore a fresh identity; every live session of the process shares it, so P2P recognizes an origin reached through the CDN and directly. - A session announces the public key once (AUTH_CNF, moved here from - [Peer grants](/quest/m1/p2p/peer-grant.md)) and proves possession bound to + [Peer grants](/quest/m2/p2p/peer-grant.md)) and proves possession bound to this session: a fresh receiver challenge or the authenticated session transcript, never a signature over the key alone, which replays across sessions. The receiver then accepts that identity, and only that identity, @@ -51,4 +51,4 @@ connection setup; report any key handle that leaks into `moq-ffi`. ## Related -- [Peer grants](/quest/m1/p2p/peer-grant.md) - binds this key as `cnf` for direct sessions +- [Peer grants](/quest/m2/p2p/peer-grant.md) - binds this key as `cnf` for direct sessions diff --git a/quest/m1/identity-guide.md b/quest/m1/identity-guide.md index e0eaa1fcf4..03c83d358e 100644 --- a/quest/m1/identity-guide.md +++ b/quest/m1/identity-guide.md @@ -17,7 +17,8 @@ implemented contract, with links from the relevant concept and library docs. Explain path plus epoch as content identity, not an encoder configuration hash; unique epochs for replacement producers; same-epoch replicas; sticky handles; explicit Restart and player resubscription; and publisher name reuse -as a bug. Show catalog-instance lifetime pins, optional expected sibling +as a bug. Show lifetime pins for readers without announcement following and +Start/End/Restart for those that follow siblings. Explain optional expected epochs, known mismatch refusal, and the weaker legacy best-effort guarantee. Include lazy epochless claims, exact per-job announcements, the accepted diff --git a/quest/m1/ietf-extension/lite.md b/quest/m1/ietf-extension/lite.md index 36abb44e6a..e410f5114c 100644 --- a/quest/m1/ietf-extension/lite.md +++ b/quest/m1/ietf-extension/lite.md @@ -17,8 +17,9 @@ Decided 2026-10-13 (see the [epic](/quest/m1/ietf-extension/README.md)). finalize rename alone. - Add a documented `pub ietf: bool` field on `setup::Extensions`, default `false`. moq-relay sets it. Both sides must offer it. -- lite-07 only, while it is the wip version. Frozen versions get no framing - branches. +- Target the current wip version. If lite-07 has finalized before this + lands, use the next wip/version. Published versions get no incompatible + framing changes. - When negotiated, the fields are always present, with no flag bits: - The Group message gains the explicit FIRST_OBJECT state. The first object ID alone cannot recover the FIRST_OBJECT bit. Subgroups other diff --git a/quest/m1/import-declared-track.md b/quest/m1/import-declared-track.md index cf91d88ef6..94d5528fd7 100644 --- a/quest/m1/import-declared-track.md +++ b/quest/m1/import-declared-track.md @@ -47,4 +47,4 @@ Public API: none expected. Wire: none. ## Related -- [Same-epoch importers](/quest/m1/hop-aligned-import.md) - same-epoch importers publish identical tracks +- [Same-epoch importers](/quest/m2/hop-aligned-import.md) - same-epoch importers publish identical tracks diff --git a/quest/m1/quic/shard.md b/quest/m1/quic/shard.md index dd84fbb86f..5646eec724 100644 --- a/quest/m1/quic/shard.md +++ b/quest/m1/quic/shard.md @@ -39,4 +39,4 @@ Update `doc/bin/relay/` for the flag change. ## Related -- [UDP demux](/quest/m1/p2p/udp-demux.md) - demuxes each shard's socket; keep the demux over whatever the library hands out +- [UDP demux](/quest/m2/p2p/udp-demux.md) - demuxes each shard's socket; keep the demux over whatever the library hands out diff --git a/quest/m1/refuse-rewinds.md b/quest/m1/refuse-rewinds.md index aa9110f902..852c6c6c72 100644 --- a/quest/m1/refuse-rewinds.md +++ b/quest/m1/refuse-rewinds.md @@ -136,5 +136,5 @@ code 0x39 broadens. - [#5446](https://github.com/moq-dev/moq/pull/5446) - where the maintainer decided rewinds are never supported - [JS track handover](/quest/m1/js-group-handover.md) - mirrors Rust's resume contract, which relies on starts never falling -- [Same-epoch importers](/quest/m1/hop-aligned-import.md) - importers already refuse a rewind at the container layer -- [Epoch anchor](/quest/m1/epoch-anchor.md) - a rewind stays fatal to an importer, which restarts under a successor epoch +- [Same-epoch importers](/quest/m2/hop-aligned-import.md) - importers already refuse a rewind at the container layer +- [Epoch anchor](/quest/m2/epoch-anchor.md) - a rewind stays fatal to an importer, which restarts under a successor epoch diff --git a/quest/m1/routing-guide.md b/quest/m1/routing-guide.md index b3e50d8301..6fb1d57211 100644 --- a/quest/m1/routing-guide.md +++ b/quest/m1/routing-guide.md @@ -2,36 +2,31 @@ ## Goal -A routing concept page and practical setup guidance explain how one model -connects CDN tiers, outbound local relays, and application-selected P2P links, -including priorities, costs, and continuity. Route health (m2) adds its own -section when it lands. +A routing concept page and practical setup guidance explain CDN tiers and +outbound local relays, including priorities, costs, and continuity. ## Plan -Decided 2026-10-13: add the concept page and runnable federation/P2P guidance, -not only reference edits. Every feature PR still fixes docs it makes stale. +The 2026-10-15 release audit keeps federation guidance in m1. Deferred P2P +owns the later application-selected peer setup and extends this concept page. +Every feature PR still fixes docs it makes stale. Walk an outbound-only local relay through scoped admission, two-way delivery, -two uplinks, partition/healing, and a client retaining its CDN session while -using a preferred peer. Explain application-owned connection selection and -priority, cost comparison within priority, deterministic equal-route selection, -and the absence of browser transit. Do not imply direct always beats CDN. +two uplinks, and partition/healing. Explain application-owned connection +selection and priority, cost comparison within priority, and deterministic +equal-route selection. -Distinguish routing identity, broadcast epoch, advertisement presence, forwarding -and reachability. Resume requires matching explicit epochs; epochless changes -restart. +Distinguish routing identity, broadcast epoch, advertisement presence, +forwarding, and reachability. Resume requires matching explicit epochs; +epochless changes restart. Keep economic domain aggregation and measured +link-cost policy outside the basic setup. -Keep economic domain aggregation and measured link-cost policy outside the -basic setup. Use generic public examples, validate CLI/config examples against -the implemented help. In the P2P setup, list an explicitly selected relay STUN -endpoint (`stun:relay.example.com:3478`) among the application's ICE servers; -link to `/bin/relay/config#stun` for the separate opt-in listener. Cross-link `doc/bin/relay/cluster.md`, JS/native P2P, -and identity guidance without duplicating reference material. +Use generic public examples and validate CLI/config examples against the +implemented help. Cross-link `doc/bin/relay/cluster.md` and identity guidance +without duplicating reference material. Route health adds its section in m2. -Public API/wire: documentation only, reflecting the completed implementations. +Public API/wire: documentation only, reflecting completed implementations. ## Required - [Outbound federation](/quest/m1/cluster-routing/federation.md) - tested operator configuration -- [P2P](/quest/m1/p2p/README.md) - implemented application peer selection and handover diff --git a/quest/m1/subscribe-ranges/ietf.md b/quest/m1/subscribe-ranges/ietf.md index 02337884a7..02b22b3925 100644 --- a/quest/m1/subscribe-ranges/ietf.md +++ b/quest/m1/subscribe-ranges/ietf.md @@ -39,6 +39,6 @@ a live track's Largest Object, and test both cases. ## Related -- [Pipelined first FETCH](/quest/m2/pipeline-requests/fetch.md) - sends TRACK_STATUS alongside the FETCH in adjacent code (`ietf/subscriber.rs`, `model/origin.rs`); decided 2026-10-08 it lands first, since it is unblocked and this is not, and this rebases onto it +- [Pipelined first FETCH](/quest/m2/pipeline-requests/fetch.md) - sends TRACK_STATUS alongside the FETCH in adjacent code (`ietf/subscriber.rs`, `model/origin.rs`); an independent m2 optimization that adapts to these ranges and does not gate them - [FETCH max-delay](/quest/m1/fetch-max-delay.md) - preserve local age enforcement and the omitted-budget default when adapting IETF FETCH to ranges - [IETF fill timeout](/quest/m1/ietf-fill-timeout.md) - handles native timeout gaps; do not mistake them for never-existing objects or media-age limits diff --git a/quest/m1/test-flakes-2/auth-refresh-mock-time.md b/quest/m1/test-flakes-2/auth-refresh-mock-time.md index 085a1ae340..c1328f0866 100644 --- a/quest/m1/test-flakes-2/auth-refresh-mock-time.md +++ b/quest/m1/test-flakes-2/auth-refresh-mock-time.md @@ -14,7 +14,7 @@ load, because they read one mocked clock instead of the host's wall clock: ## Plan -All three failed under load on 2026-10-13 (seen on #5148, #5462, and +These tests failed under load on 2026-10-13 (seen on #5148, #5462, and #5446) and passed alone. One run reported the session outliving its grant at 28.5 s against the outage test's 29 s floor. AGENTS.md requires unit tests to mock time. @@ -40,7 +40,9 @@ deterministic, rather than widen a bound. and bracketing the bounds the way `an_outage_keeps_the_session_until_expires` does cannot stop that. That test also unwraps a wall expiry that may already be past, so it moves onto the mocked clock here too. Hence the - crate is Required, rather than a paused-Tokio fallback. + existing moq-clock crate is used directly, rather than a paused-Tokio + fallback. This quest owns the targeted moq-auth grant clock conversion; + it does not wait for the whole workspace sweep or future relay seam. - `a_fixed_lease_still_expires` moves off real sockets onto `memory_sessions()` and `moq_relay::supervise` under the same clock. If no other real-socket test proves the tcp and ws paths hand their lease to diff --git a/quest/m1/time/crates.md b/quest/m1/time/crates.md index bd61f703a1..f3d180c45b 100644 --- a/quest/m1/time/crates.md +++ b/quest/m1/time/crates.md @@ -20,6 +20,10 @@ tokio, which auto-advances to a timer before the OS event arrives, and never on a fixed sleep. moq-relay's integration tests move onto the sim in [the relay seam](/quest/m1/time/relay-seam.md), not here. +The targeted moq-auth grant conversion belongs to +[auth refresh](/quest/m1/test-flakes-2/auth-refresh-mock-time.md); reuse that +change rather than implementing a second migration. It need not wait here. + Fix mixed clocks first: anything that reads std time but waits on tokio time (a backoff's stability window, a gate computed with std `Instant` and slept with tokio) is a bug under paused time. diff --git a/quest/m1/time/relay-seam.md b/quest/m1/time/relay-seam.md index b8088e24f5..a0aec704b6 100644 --- a/quest/m1/time/relay-seam.md +++ b/quest/m1/time/relay-seam.md @@ -2,33 +2,25 @@ ## Goal -One in-memory session pair implements moq-net's poll transport traits and -replaces the duplicate poll-session mocks (`rs/moq-net/tests/support/mock.rs`, -`rs/moq-e2ee/tests/support/mock.rs`, and the session bench's transport). -moq-tokio's `FakeSession` and `Scripted` stay: they implement -`web_transport_trait::Session` to test moq-tokio's adapter onto the poll -traits, which this pair cannot stand in for. `moq_relay::Connection` runs -over any moq-net transport session, not only a `moq_tokio::server::Request`, -so moq-relay's integration tests (auth, the cluster origin, stats) run on the -sim with no sockets, and [the relay session bench](/quest/m2/bench-relay.md) -uses the same seam. +`moq_relay::Connection` runs over any moq-net transport session, not only a +`moq_tokio::server::Request`, so relay integration tests run on the sim without +sockets and the relay session benchmark can reuse the same seam. ## Plan -Decided 2026-10-10: in-memory, on moq-net's poll transport traits, not a -`web_transport_trait` implementation, and nothing changes in -moq-dev/web-transport. Backend-only methods (`poll_acked`, `set_deadline`, -`set_limits`) answer "unsupported", as -[one adapter](/quest/m2/transport-adapter-dedup.md) decided. Optional latency -and loss run on the moq-clock clock. It abstracts over moq-tokio's server -types as the switch to `moq-quic` (#5664) left them. +The in-memory poll transport landed in #5530 as +`rs/moq-net/src/transport/memory.rs`. The moq-net test mock wraps it and the +e2ee harness uses it. Reuse that pair; do not recreate the transport or its +removed duplicate mocks. moq-tokio's `FakeSession` and `Scripted` still test +the web-transport adapter and are not replaced by a poll transport. -Find the smallest seam that keeps `moq_relay`'s public API narrow. Propose -where the session lives (recommended: a moq-net module behind a cargo -feature, since other crates' tests consume it) and its name in the PR. -Simulated UDP is [QUIC on the sim](/quest/m2/quic-sim.md). +Find the smallest remaining relay connection seam and keep its public API +narrow. Move auth, cluster-origin, and stats integration tests onto the sim +where they do not require real I/O. It abstracts over moq-tokio's server +types as the switch to `moq-quic` (#5664) left them. Simulated UDP belongs to +QUIC on the sim. -Public API: the in-memory session and the relay seam. Wire: none. +Public API: the remaining relay seam. Wire: none. ## Required diff --git a/quest/m1/ts-live-clock.md b/quest/m1/ts-live-clock.md index 2b4491aa1a..bd2863affc 100644 --- a/quest/m1/ts-live-clock.md +++ b/quest/m1/ts-live-clock.md @@ -12,8 +12,8 @@ output from actual timing defects. Decided with the maintainer on 2026-10-11: investigate and fix this finding independently of the same-instance resume failure. Preserve strict publisher identity, the existing validation gates, and production negotiation defaults. -Keep validation PR #5328 draft while its local readiness findings remain -unresolved; a passing CI run alone does not explain the local failure. +Validation PR #5328 has merged. Its passing CI does not explain this local +failure; this follow-up owns the remaining reproduction and fix. The epoch-capable 120-second local run completed with publisher and exporter exit zero, but the grader reported -1382.60 ppm against its +/-30 ppm limit. diff --git a/quest/m1/ts-reliability/README.md b/quest/m1/ts-reliability/README.md index 622110bdd0..b4824e1e35 100644 --- a/quest/m1/ts-reliability/README.md +++ b/quest/m1/ts-reliability/README.md @@ -40,8 +40,11 @@ and distinguish a MoQ group boundary from a decoder acquisition point. RAI, periodic cuts, and an intact object alone do not ensure available PAT/PMT, codec configuration, clock context, or references for every MPTS program. -The README owns the guide and the final two-mode integration proof. Reuse the -children's fixtures and CI recipes, including their known-bad controls. Publish +The README owns the guide and integration proof for the modes present in +m1: semantic remux and raw import/carriage. The deferred raw export quest +extends the guide and proves its output when it lands; it does not block +this proof. Reuse the children's fixtures and CI recipes, including their +known-bad controls. Publish a coverage table linking each supported profile and loss guarantee to its test; unsupported or unmeasured cells stay explicit. Keep CLI instructions and the MPEG-TS draft consistent in each feature change. @@ -106,7 +109,7 @@ remain with their current quests. ## Related -- [Epoch anchor](/quest/m1/epoch-anchor.md) - shared identity across importers requires one source coordinate per epoch, derived from the epoch +- [Epoch anchor](/quest/m2/epoch-anchor.md) - shared identity across importers requires one source coordinate per epoch, derived from the epoch - [TS over RTP legs](/quest/m2/ts-rtp-egress.md) - TS identity alone is not ST 2022-7, which also needs matching RTP headers - [TS health counters](/quest/m2/ts-health-stats.md) - ingress and egress evidence need distinct monitoring points - [MSFTS mapping](/quest/m3/msfts-convergence.md) - document the exact equivalence and differences between the carriage modes diff --git a/quest/m1/ts-reliability/pes-import.md b/quest/m1/ts-reliability/pes-import.md index e729644372..39503a2e45 100644 --- a/quest/m1/ts-reliability/pes-import.md +++ b/quest/m1/ts-reliability/pes-import.md @@ -31,7 +31,7 @@ round-trip test first. existing `Retained` bound; it never spans a PCR discontinuity. The value depends only on the input bytes and the timebase offset, so it adds no disagreement of its own; same-epoch importers agree once the - [epoch anchor](/quest/m1/epoch-anchor.md) fixes that offset. A program + [epoch anchor](/quest/m2/epoch-anchor.md) fixes that offset. A program with no PCR PID cannot place its PES, so its private PES streams are refused. The timebase offset is anchored only by a PTS today, so a PES also waits for that anchor within the same bound; a program whose PES diff --git a/quest/m1/tstd/README.md b/quest/m1/tstd/README.md index 84baddf5fd..910cafe53d 100644 --- a/quest/m1/tstd/README.md +++ b/quest/m1/tstd/README.md @@ -6,8 +6,9 @@ checks for explicitly supported profiles, with admitted decodable access units complete by their decode time and no modelled buffer overflow. Under loss, validate the surviving content, declared discontinuities and recovery; -missing source units cannot all be decoded. Packet passthrough also needs -timing and recovery evidence before making a distribution-readiness claim. +missing source units cannot all be decoded. The deferred packet-passthrough +export owns its timing and recovery proof before claiming readiness; it does +not gate the semantic export proof here. ## Plan @@ -47,6 +48,6 @@ standards check. ## Related -- [TS passthrough export](/quest/m2/ts-passthrough-export.md) - the passthrough lane named in the Goal +- [TS passthrough export](/quest/m2/ts-passthrough-export.md) - the later passthrough lane owns its own output proof - [Automated validation](/test/ts/README.md) - independent decode, profile coverage and CI ownership - [Loss and recovery](/quest/m1/ts-reliability/recovery.md) - actual object/group omissions and recovery diff --git a/quest/m2/README.md b/quest/m2/README.md index b41215ace0..9f4aa07c2c 100644 --- a/quest/m2/README.md +++ b/quest/m2/README.md @@ -23,6 +23,28 @@ ranked just below deterministic TS legs, which it requires. ## Required +- [Release execution](/quest/m2/release/README.md) - publication, installed-artifact proof, cleanup, and downstream adoption after m1 +- [P2P](/quest/m2/p2p/README.md) - application-selected peer sessions use shared priorities, routing, and same-epoch handover +- [Epoch anchor](/quest/m2/epoch-anchor.md) - same-epoch importers each derive the catalog clock and timestamp era from the epoch and stay live, so they agree across a 33-bit wrap, a forward clock break, and a restart +- [Raw TS cuts on the epoch timeline](/quest/m2/ts-passthrough-epoch-cuts.md) - passthrough fallback cuts fall on the epoch's grid and the M2ts section derives from the epoch and content, so instances started apart publish identical groups and catalogs +- [Same-epoch importers](/quest/m2/hop-aligned-import.md) - importers sharing one `--epoch` and fed one stream publish identical groups and timestamps, so failover between a redundant pair survives +- [BoltFFI or UniFFI](/quest/m2/boltffi.md) - BoltFFI vs UniFFI on a moq-ffi slice in Python, Kotlin, and Swift decides which one the moq-dev fork starts from +- [Hard-fork the binding generator](/quest/m2/uniffi-fork.md) - one moq-dev repo holds the chosen generator's core and every language backend, released under one tag and one pin +- [Benchmark trend site](/quest/m2/bench-site.md) - a GitHub Pages site charts the binding, Rust, and JS nightly benchmarks over time +- [Link the benchmark trends](/quest/m2/bench-site-doc.md) - the docs point language choosers at the trend site +- [Swift, Dart, and C++ benchmarks](/quest/m2/ffi-bench-wrappers.md) - the other three wrappers join the nightly binding benchmark +- [Payload bytes](/quest/m2/ffi-bytes.md) - payloads cross the FFI in one bulk copy, proven on the binding benchmark in every wrapper +- [Kotlin calls](/quest/m2/kt-ffi-calls.md) - Kotlin calls into moq-ffi cost about what Python's and Go's do, sync and async, once the fork's Kotlin generator drops JNA's per-call overhead +- [Poll-native async spike](/quest/m2/ffi-poll.md) - measure kio poll-native exports and foreign poll against `ffi::Task`, then pick the shape +- [Rendition preference](/quest/m2/rendition-preference.md) - automatic selection by ``, `Video::ranked`, and WHEP keeps the highest `preference` that decodes, so a compatibility transcode is only picked when nothing preferred decodes +- [Stats prefix tracks](/quest/m2/stats-prefix-tracks.md) - additive tracks required by the breaking lag API; existing maps remain supported +- [QoS](/quest/m2/qos/README.md) - broadcast health: relay starvation and timeliness histograms +- [Plan: watch worker](/quest/m2/plan-watch-worker.md) - prototype an invisible page worker against app-spawned workers, and land the jank harness that decides +- [Watch worker](/quest/m2/watch-worker.md) - watch playback runs in a worker onto an OffscreenCanvas, so main-thread jank never stalls video or audio +- [Vulkan Video encode on AMD](/quest/m2/vulkan-encode.md) - H.264 and H.265 from an external Vulkan image on RADV, hand-rolled on ash, proven on an RX 9070 +- [VA-API encodes an external Vulkan image](/quest/m2/vaapi-vulkan-import.md) - an explicit-sync DMA-BUF reaches Intel's encoder, proven on Arrow Lake +- [C++ FFI reuse](/quest/m2/cpp-ffi-reuse.md) - C++ packages link the shared default-feature FFI build once it is proven compatible + - [Time test sweep](/quest/m2/time-test-sweep.md) - unrelated test conversion and the workspace lint ratchet - [Binding audio delay](/quest/m2/binding-surface.md) - moq-ffi and every wrapper configure and observe audio playout delay @@ -161,7 +183,6 @@ ranked just below deterministic TS legs, which it requires. - [Publisher timeliness at relay ingest](/quest/m2/qos-publisher-timeliness.md) - follow-on capability after the release contract is stable -- [Release execution](/quest/m2/release/README.md) - publication, installed-artifact proof, cleanup, and downstream adoption after m1 - [Play resume](/quest/m2/play-resume.md) - `moq-cli play` plays out pictures buffered before a declared end when new media arrives quickly - [Task supervision](/quest/m2/task-supervision.md) - the auth decider loop, stats publisher, and moq-tokio's token and WebSocket internals are supervised like #5447's tasks - [Auth serve bound address](/quest/m2/auth-serve-bound-addr.md) - `moq auth serve` logs the port it actually bound diff --git a/quest/m2/audio-encode-audiotoolbox.md b/quest/m2/audio-encode-audiotoolbox.md index 5c3870d6cc..352f9d0af2 100644 --- a/quest/m2/audio-encode-audiotoolbox.md +++ b/quest/m2/audio-encode-audiotoolbox.md @@ -18,9 +18,10 @@ the encode seam as the platform candidate on macOS and iOS. `kAudioConverterPrimeInfo` gives the delay the timestamps fold in. - Bitrate through `kAudioConverterEncodeBitRate`, updated live where the converter allows. -- The seam assumes one packet per frame. If the converter holds output back, - the backend needs a `flush` and a zero-or-more return, which changes - `Encoder::encode`. +- This quest owns changing `Encoder::encode` from one packet per frame to + a zero-or-more packet return with a flush operation. MediaCodec requires + this shared contract even if AudioToolbox itself emits immediately. Prove + delayed and multiple outputs and flushing in the shared encoder tests. - Gate the seam's "AAC refused without a platform encoder" test to hosts without one. - Regression: a stereo and a 5.1 encode round-trip through the AudioToolbox diff --git a/quest/m2/audio-encode-mediacodec.md b/quest/m2/audio-encode-mediacodec.md index c2292320c2..1220119513 100644 --- a/quest/m2/audio-encode-mediacodec.md +++ b/quest/m2/audio-encode-mediacodec.md @@ -16,9 +16,8 @@ behind the `mediacodec` feature and the encode seam. - `audio/mp4a-latm` with `KEY_AAC_PROFILE` = LC. The catalog ASC is synthesized at construction per the encode seam, since `csd-0` only arrives with the first output buffer; assert the two match. -- MediaCodec pipelines output, which the one-packet-per-frame seam does not - allow yet: add a `flush` and a zero-or-more return, a change to - `Encoder::encode`, unless the AudioToolbox quest already did. +- MediaCodec pipelines output. Reuse the flush and zero-or-more packet + return owned by AudioToolbox encode; do not design another encoder seam. - Multichannel is device-dependent; probe the encoder's capabilities at open and refuse a layout it does not list. - Round-trip regression through the MediaCodec decoder; runtime proof on a @@ -27,7 +26,4 @@ behind the `mediacodec` feature and the encode seam. ## Required - [MediaCodec decode](/quest/m2/audio-decode-mediacodec.md) - the round-trip regression decodes through it - -## Related - -- [AudioToolbox encode](/quest/m2/audio-encode-audiotoolbox.md) - the macOS and iOS backend, which may already add `flush` to `Encoder::encode` +- [AudioToolbox encode](/quest/m2/audio-encode-audiotoolbox.md) - owns the shared flush and zero-or-more packet encoder contract diff --git a/quest/m2/audio-quality-native.md b/quest/m2/audio-quality-native.md index 2a8878d159..0ad25a794d 100644 --- a/quest/m2/audio-quality-native.md +++ b/quest/m2/audio-quality-native.md @@ -43,7 +43,7 @@ only end-to-end budgets, with the backend-dependent stages isolated: this lane deliberately accepts real device callback noise, so a difference in totals alone proves nothing about the estimator. -Standalone in m1 rather than a child of the m0 audio quality harness line +Standalone in m2 rather than a child of the m0 audio quality harness line (decided in the 2026-09-28 quest audit): nothing in m0 waits on it. The native jitter target it grades is done on the jitter target line. diff --git a/quest/m2/bench-ci.md b/quest/m2/bench-ci.md index 2c706421e1..959affee2c 100644 --- a/quest/m2/bench-ci.md +++ b/quest/m2/bench-ci.md @@ -35,7 +35,7 @@ a GitHub App: VM, so the cross-run history is subject to the same noise as Bencher and stays informational: record it with [github-action-benchmark](https://github.com/benchmark-action/github-action-benchmark) - on the [trend site](/quest/m1/bench-site.md)'s `gh-pages` branch, with its + on the [trend site](/quest/m2/bench-site.md)'s `gh-pages` branch, with its own alert disabled. The `moq-uring` socket benches run only here, and skip loudly when the runner's kernel is too old. Add the workflow's name to `alert.yml` if it isn't `Nightly`. @@ -47,4 +47,4 @@ a GitHub App: ## Related - [Benchmark reporting](/bench/README.md) - the existing `bench/run.sh` retains repeated paired rounds -- [Benchmark trend site](/quest/m1/bench-site.md) - where the nightly history is charted +- [Benchmark trend site](/quest/m2/bench-site.md) - where the nightly history is charted diff --git a/quest/m1/bench-site-doc.md b/quest/m2/bench-site-doc.md similarity index 87% rename from quest/m1/bench-site-doc.md rename to quest/m2/bench-site-doc.md index e02e9ef449..abc76da871 100644 --- a/quest/m1/bench-site-doc.md +++ b/quest/m2/bench-site-doc.md @@ -2,7 +2,7 @@ ## Goal -A reader of the moq docs finds the [benchmark trend site](/quest/m1/bench-site.md) +A reader of the moq docs finds the [benchmark trend site](/quest/m2/bench-site.md) and knows what the binding numbers mean when choosing a language: what each binding costs relative to Rust, and where to see it over time. @@ -15,4 +15,4 @@ summarize, don't copy numbers that go stale. The bindings overview in ## Required -- [Benchmark trend site](/quest/m1/bench-site.md) - the site this links +- [Benchmark trend site](/quest/m2/bench-site.md) - the site this links diff --git a/quest/m1/bench-site.md b/quest/m2/bench-site.md similarity index 100% rename from quest/m1/bench-site.md rename to quest/m2/bench-site.md diff --git a/quest/m1/boltffi.md b/quest/m2/boltffi.md similarity index 96% rename from quest/m1/boltffi.md rename to quest/m2/boltffi.md index c0c55e3b6a..75df7ef7d1 100644 --- a/quest/m1/boltffi.md +++ b/quest/m2/boltffi.md @@ -5,7 +5,7 @@ A reproducible report compares BoltFFI with UniFFI on the same slice of moq-ffi, in Python, Kotlin, and Swift, for call overhead, payload throughput, async cost, and output size. It also estimates what each base costs to -carry. The maintainer picks which one [the hard fork](/quest/m1/uniffi-fork.md) +carry. The maintainer picks which one [the hard fork](/quest/m2/uniffi-fork.md) starts from. This quest does not adopt or fork either one. ## Plan @@ -46,7 +46,7 @@ Decided with the maintainer on 2026-10-13: `just bench-ffi` gives UniFFI's numbers through the real moq-ffi. A known UniFFI cost to weigh: Kotlin sync calls measured 11-22 us against 1.3 us from Python on 2026-10-15, possibly from JNA's per-call structures - ([Kotlin calls](/quest/m1/kt-ffi-calls.md)); async wakeups may add a JVM + ([Kotlin calls](/quest/m2/kt-ffi-calls.md)); async wakeups may add a JVM thread attach each, a separate suspect. - **Size table**: native library, generated bindings, and wasm plus JS glue (gzip), for both generators. Measure the native library with moq-ffi's diff --git a/quest/m2/color-catalog.md b/quest/m2/color-catalog.md index 43ab5bb8a1..159b604fb0 100644 --- a/quest/m2/color-catalog.md +++ b/quest/m2/color-catalog.md @@ -9,7 +9,7 @@ instead of guessing or reparsing the bitstream. ## Plan -Decided 2026-10-08 (WebGPU renderer interview): moved to m1. The +Deferred to m2 with HDR. The [WebGPU HDR](/quest/m2/webgpu-hdr.md) renderer is the consumer this model waited for. Encoder colour correctness stays in m1 as [Encoder colour](/quest/m1/color-model.md). diff --git a/quest/m1/cpp-ffi-reuse.md b/quest/m2/cpp-ffi-reuse.md similarity index 100% rename from quest/m1/cpp-ffi-reuse.md rename to quest/m2/cpp-ffi-reuse.md diff --git a/quest/m2/e2ee/README.md b/quest/m2/e2ee/README.md index 6afcf2c989..e307b23189 100644 --- a/quest/m2/e2ee/README.md +++ b/quest/m2/e2ee/README.md @@ -20,7 +20,7 @@ The contract is [draft-lcurley-moq-e2ee](/drafts/draft-lcurley-moq-e2ee.md), pro ### Epoch and identity - Every publisher instance mints an epoch, a UUIDv7 in lowercase text, and publishes at `` with the epoch on its route, where `` derives from the credential and the semantic broadcast name without the epoch. The epoch is an input to every HKDF derivation, so a restart, takeover, or explicit group sequence cannot repeat a nonce under a key: nothing is persisted across instances and no generation counter is redistributed. The epoch is the shared [`Epoch`](/doc/concept/moq-lite.md#publisher-epochs) primitive. Subscribers resolve `` and take the epoch from its route, where the newest wins; on a wire without route epochs the application supplies it. -- An encrypting publisher refuses a shared or explicit epoch (maintainer, 2026-10-08). A redundant pair under one `moq --epoch`, or the same-epoch importers of [Same-epoch importers](/quest/m1/hop-aligned-import.md), would derive the same keys and repeat every nonce. Both cores produce only under an epoch they minted themselves; consuming still binds a discovered one. Rejected: a per-instance salt. State the rule in the draft's epoch section. +- An encrypting publisher refuses a shared or explicit epoch (maintainer, 2026-10-08). A redundant pair under one `moq --epoch`, or the same-epoch importers of [Same-epoch importers](/quest/m2/hop-aligned-import.md), would derive the same keys and repeat every nonce. Both cores produce only under an epoch they minted themselves; consuming still binds a discovered one. Rejected: a per-instance salt. State the rule in the draft's epoch section. - The epoch is untrusted and unauthenticated. A wrong epoch fails authentication and a withheld one denies service; neither can make a nonce repeat, because only the publisher instance chooses what it encrypts under. This is the same trust a cache needs to serve the right instance. Plaintext broadcasts adopt the same epoch by default through [broadcast epochs](/quest/m0/broadcast-epoch/README.md). - Nothing in the path says the bytes are encrypted. The format after decryption (`meeting.hang`) is inside the opaque name; a plaintext player, exporter, or matcher that opens a protected broadcast finds no catalog it can read and fails with its usual typed refusal, the same as for any format it does not support. - One 32-byte secret authorizes the whole broadcast; HKDF-SHA-256 derives separate AES-128-GCM keys for each physical track and for grouped-frame versus datagram domains. A grouped frame uses the 96-bit nonce `uint64_be(group) || uint32_be(frame)`; a datagram uses its sequence with frame zero under the datagram domain. Empty AAD: every immutable end-to-end field is in the HKDF info or the nonce. @@ -68,4 +68,4 @@ The Rust and TypeScript cores expose the same surface, and nothing else: ## Related - [archive](/quest/m1/archive/README.md) - protected broadcasts are deliberately outside recording and replay formats -- [Same-epoch importers](/quest/m1/hop-aligned-import.md) - the shared-epoch redundancy an encrypting publisher refuses +- [Same-epoch importers](/quest/m2/hop-aligned-import.md) - the shared-epoch redundancy an encrypting publisher refuses diff --git a/quest/m1/epoch-anchor.md b/quest/m2/epoch-anchor.md similarity index 98% rename from quest/m1/epoch-anchor.md rename to quest/m2/epoch-anchor.md index 788e4796d3..d6db4404c9 100644 --- a/quest/m1/epoch-anchor.md +++ b/quest/m2/epoch-anchor.md @@ -82,7 +82,7 @@ precondition. content at hand derives from the epoch and the content instead (maintainer, superseding #5412's adoption from the live catalog). In TS passthrough that is the M2ts section's `random_access` and recorded `mux_rate`, owned by - [raw TS cuts](/quest/m1/ts-passthrough-epoch-cuts.md); audit the other + [raw TS cuts](/quest/m2/ts-passthrough-epoch-cuts.md); audit the other importers for more. - A restart under the same epoch re-derives the same coordinate, including after a total outage, so the epoch keeps naming one timeline and resuming @@ -122,5 +122,5 @@ breaking grant for publish-only tokens is dropped). Wire: none. ## Related -- [Same-epoch importers](/quest/m1/hop-aligned-import.md) - group sequences and the end-to-end pair on this coordinate +- [Same-epoch importers](/quest/m2/hop-aligned-import.md) - group sequences and the end-to-end pair on this coordinate - [Broadcast epochs](/quest/m0/broadcast-epoch/README.md) - same-epoch routes resume each other's subscriptions diff --git a/quest/m1/ffi-bench-wrappers.md b/quest/m2/ffi-bench-wrappers.md similarity index 93% rename from quest/m1/ffi-bench-wrappers.md rename to quest/m2/ffi-bench-wrappers.md index 98678e30c1..3ef4523e63 100644 --- a/quest/m1/ffi-bench-wrappers.md +++ b/quest/m2/ffi-bench-wrappers.md @@ -10,7 +10,7 @@ so every wrapper's FFI cost is tracked and shows on the trend site. Decided with the maintainer on 2026-10-15: every wrapper is covered because each generator lowers bytes differently, and a generator regression only -shows in its own language. [Payload bytes](/quest/m1/ffi-bytes.md) needs +shows in its own language. [Payload bytes](/quest/m2/ffi-bytes.md) needs all six to prove its fix. - Swift has no Linux toolchain in the dev shell; run it in the macOS job in diff --git a/quest/m2/ffi-bytes-return.md b/quest/m2/ffi-bytes-return.md index ee9f05ee6a..7905922a8d 100644 --- a/quest/m2/ffi-bytes-return.md +++ b/quest/m2/ffi-bytes-return.md @@ -4,7 +4,7 @@ A frame payload returned to a binding (`read_frame`, `recv_datagram`, decoded media) reaches the host without a copy, through a Rust-owned buffer the host -releases, in every generator of [moq-dev/uniffi](/quest/m1/uniffi-fork.md). +releases, in every generator of [moq-dev/uniffi](/quest/m2/uniffi-fork.md). Proven on the [binding benchmark](/bench/README.md). ## Plan @@ -25,4 +25,4 @@ Public API: returned payload types may change per language. Wire: none. ## Required -- [Payload bytes](/quest/m1/ffi-bytes.md) - the numbers that justify it +- [Payload bytes](/quest/m2/ffi-bytes.md) - the numbers that justify it diff --git a/quest/m1/ffi-bytes.md b/quest/m2/ffi-bytes.md similarity index 92% rename from quest/m1/ffi-bytes.md rename to quest/m2/ffi-bytes.md index 9777380d7e..2d1e205c66 100644 --- a/quest/m1/ffi-bytes.md +++ b/quest/m2/ffi-bytes.md @@ -19,7 +19,7 @@ Decided with the maintainer on 2026-10-13: each (`uniffi_core` `ffi_converter_impls.rs`), in both directions. That path covers arguments, record fields (`MoqFrame.payload`, video and audio frames), and returns (`read_frame`, `recv_datagram`). Make it one - `memcpy` in [moq-dev/uniffi](/quest/m1/uniffi-fork.md). The serialized + `memcpy` in [moq-dev/uniffi](/quest/m2/uniffi-fork.md). The serialized format is unchanged, so no generator or binding API changes. Two copies remain for records and returns (the wrapper's container to `RustBuffer`, then `RustBuffer` to `Vec`); removing the second needs the ownership @@ -42,5 +42,5 @@ type. Wire: none. ## Required -- [Swift, Dart, and C++ benchmarks](/quest/m1/ffi-bench-wrappers.md) - the other three wrappers' numbers -- [Hard fork](/quest/m1/uniffi-fork.md) - the repository the core patch lands in +- [Swift, Dart, and C++ benchmarks](/quest/m2/ffi-bench-wrappers.md) - the other three wrappers' numbers +- [Hard fork](/quest/m2/uniffi-fork.md) - the repository the core patch lands in diff --git a/quest/m1/ffi-poll.md b/quest/m2/ffi-poll.md similarity index 97% rename from quest/m1/ffi-poll.md rename to quest/m2/ffi-poll.md index 215984d777..1936fe9489 100644 --- a/quest/m1/ffi-poll.md +++ b/quest/m2/ffi-poll.md @@ -22,7 +22,7 @@ Decided with the maintainer on 2026-10-13: and keeps the future until `rust_future_free` (mozilla/uniffi-rs#2771), and `ffi::Task::run` depends on that today. So Shape A includes the core change that drops the future on cancel, - prototyped on a UniFFI branch if [the fork](/quest/m1/uniffi-fork.md) + prototyped on a UniFFI branch if [the fork](/quest/m2/uniffi-fork.md) does not exist yet, and measures with and without it. Measure what remains of `Task` (cancel, shutdown). - **Shape B, foreign poll**: export a sync `poll_x(waker)` whose waker is a diff --git a/quest/m2/gpu-health.md b/quest/m2/gpu-health.md index e2e927b213..7c9e8ad0dd 100644 --- a/quest/m2/gpu-health.md +++ b/quest/m2/gpu-health.md @@ -18,14 +18,14 @@ Those are the caller's. Decided 2026-10-05 in moq.pro's quest audit: vendor-neutral now, not NVIDIA-scoped. Reason: the external GPU surface (#4975), -[Vulkan encode](/quest/m1/vulkan-encode.md), and -[VA-API import](/quest/m1/vaapi-vulkan-import.md) make AMD and Intel encode +[Vulkan encode](/quest/m2/vulkan-encode.md), and +[VA-API import](/quest/m2/vaapi-vulkan-import.md) make AMD and Intel encode first-class, so an NVML-shaped API would be rewritten once they land. moq.pro's transcode admission and its benchmarks assume NVML today; they adopt this instead. Decided 2026-10-05: one device identity wherever a render node exists, the -render node's `dev_t`, shared with [VA-API import](/quest/m1/vaapi-vulkan-import.md). +render node's `dev_t`, shared with [VA-API import](/quest/m2/vaapi-vulkan-import.md). NVIDIA's device UUID maps to its node through `VK_EXT_physical_device_drm`. The external GPU surface (#4975) carries that identity as `frame::vulkan::Device { device_uuid, driver_uuid, render_node: Option }`. @@ -55,10 +55,9 @@ Guidance, to be settled while building: errors (`VK_ERROR_DEVICE_LOST`, CUDA and VA-API equivalents) and from the driver's reset counters where exposed. - A snapshot call, not a callback; cheap enough to poll about once a second. -- The snapshot is additive, but it keys by the surface's identity, and the - surface change is breaking, so it reaches `release` with - [the multi-vendor release](/quest/m2/gpu-release.md), not by an earlier - backport. +- The snapshot is additive and keys by the surface's identity. Publish it + after the vendor implementations it reports are validated. It does not gate + [the multi-vendor release](/quest/m2/gpu-release.md) or the next cut. Test: a unit test of each source's parsing against recorded fixtures, and ignored hardware tests in `just rs gpu`'s per-vendor branches (the recipe @@ -69,5 +68,5 @@ Public API: a device capacity and health snapshot in moq-video. Wire: none. ## Related -- [VA-API encodes an external Vulkan image](/quest/m1/vaapi-vulkan-import.md) - picks a render node by the same `dev_t` -- [Vulkan Video encode on AMD](/quest/m1/vulkan-encode.md) - the AMD backend whose sessions this counts +- [VA-API encodes an external Vulkan image](/quest/m2/vaapi-vulkan-import.md) - picks a render node by the same `dev_t` +- [Vulkan Video encode on AMD](/quest/m2/vulkan-encode.md) - the AMD backend whose sessions this counts diff --git a/quest/m2/gpu-release.md b/quest/m2/gpu-release.md index 09b6111fa5..1110f689a3 100644 --- a/quest/m2/gpu-release.md +++ b/quest/m2/gpu-release.md @@ -2,20 +2,21 @@ ## Goal -The next breaking release carries the neutral `Surface::Vulkan`, Vulkan -Video encode on AMD, and VA-API import of external Vulkan images on Intel. -Both vendor proofs validate the input surface before publication. +A later release carries Vulkan Video encode on AMD and VA-API import of +external Vulkan images on Intel. Both vendor proofs validate their use of +the neutral `Surface::Vulkan` before multi-vendor support is published. ## Plan -Decided 2026-10-13: ship these changes in the next main-to-release cut. -GPU capacity and health reporting is additive and remains separate. This is -a publication condition; the implementation and vendor proofs gate m1. +Decided 2026-10-15: defer AMD/Intel implementation and publication together. +The next breaking cut makes no new AMD/Intel GPU-input support promise. +The existing surface on main is not evidence that those backends work. +GPU capacity and health reporting remains a separate additive follow-up. ## Required -- [Vulkan Video encode on AMD](/quest/m1/vulkan-encode.md) - AMD proof -- [VA-API Vulkan import](/quest/m1/vaapi-vulkan-import.md) - Intel proof +- [Vulkan Video encode on AMD](/quest/m2/vulkan-encode.md) - AMD proof +- [VA-API Vulkan import](/quest/m2/vaapi-vulkan-import.md) - Intel proof ## Related diff --git a/quest/m1/hop-aligned-import.md b/quest/m2/hop-aligned-import.md similarity index 96% rename from quest/m1/hop-aligned-import.md rename to quest/m2/hop-aligned-import.md index 0d7d1cd349..81e686c579 100644 --- a/quest/m1/hop-aligned-import.md +++ b/quest/m2/hop-aligned-import.md @@ -25,7 +25,7 @@ archive, and transcode) refuse a shared epoch. Decided 2026-10-13 by the maintainer: every importer of an epoch stays live and derives one coordinate from the epoch and its source alone -([Epoch anchor](/quest/m1/epoch-anchor.md)), superseding the same day's plan +([Epoch anchor](/quest/m2/epoch-anchor.md)), superseding the same day's plan where later importers adopted the first one's. This quest numbers groups on that coordinate and proves the pair end to end. A raw PTS or PCR value alone does not supply it, and arrival time chooses only the era. Considered @@ -75,8 +75,8 @@ stopping and restarting; repeat it with `--passthrough`. ## Required -- [Epoch anchor](/quest/m1/epoch-anchor.md) - every importer derives one catalog clock and timestamp era from the epoch -- [Raw TS cuts on the epoch timeline](/quest/m1/ts-passthrough-epoch-cuts.md) - passthrough groups start where every instance cuts +- [Epoch anchor](/quest/m2/epoch-anchor.md) - every importer derives one catalog clock and timestamp era from the epoch +- [Raw TS cuts on the epoch timeline](/quest/m2/ts-passthrough-epoch-cuts.md) - passthrough groups start where every instance cuts ## Related diff --git a/quest/m1/kt-ffi-calls.md b/quest/m2/kt-ffi-calls.md similarity index 86% rename from quest/m1/kt-ffi-calls.md rename to quest/m2/kt-ffi-calls.md index a176380d90..839c83a70d 100644 --- a/quest/m1/kt-ffi-calls.md +++ b/quest/m2/kt-ffi-calls.md @@ -22,15 +22,15 @@ Decided with the maintainer on 2026-10-15: - The async continuation is a plain JNA `Callback` with no `CallbackThreadInitializer`, so each wakeup from a Rust worker attaches that thread to the JVM and detaches it again. -- **Fix in [the fork](/quest/m1/uniffi-fork.md)'s Kotlin generator**, not by +- **Fix in [the fork](/quest/m2/uniffi-fork.md)'s Kotlin generator**, not by patching generated code. Both costs are one quest: same root, same generator, same proof. Prefer a shape that keeps the wrapper's API unchanged. -- If [the BoltFFI spike](/quest/m1/boltffi.md) picks BoltFFI, re-plan this +- If [the BoltFFI spike](/quest/m2/boltffi.md) picks BoltFFI, re-plan this against its Kotlin backend, or delete it if that backend already avoids JNA. Public API: none expected. Wire: none. ## Required -- [Hard fork](/quest/m1/uniffi-fork.md) - the generator this patches +- [Hard fork](/quest/m2/uniffi-fork.md) - the generator this patches diff --git a/quest/m2/latency-ledger.md b/quest/m2/latency-ledger.md index 0dccc383e3..3a03404187 100644 --- a/quest/m2/latency-ledger.md +++ b/quest/m2/latency-ledger.md @@ -39,4 +39,4 @@ probes, which was the right trade to get it running. This quest promotes them. ## Related -- [QoS](/quest/m1/qos/README.md) - relay-side health, the same idea from the other end +- [QoS](/quest/m2/qos/README.md) - relay-side health, the same idea from the other end diff --git a/quest/m2/mobile-capture-android.md b/quest/m2/mobile-capture-android.md index cef04e8d45..96f87cae20 100644 --- a/quest/m2/mobile-capture-android.md +++ b/quest/m2/mobile-capture-android.md @@ -21,7 +21,7 @@ so the mechanism exists. Capture sets the catalog `rotation` from the sensor and display orientation, like browser capture, so a phone held in portrait plays upright. -Promoted from m2 on 2026-10-09 by maintainer priority. +Deferred to m2; mobile capture does not gate the next breaking cut. ## Related diff --git a/quest/m2/mobile-capture-ios.md b/quest/m2/mobile-capture-ios.md index c39c39928c..0e207f44c6 100644 --- a/quest/m2/mobile-capture-ios.md +++ b/quest/m2/mobile-capture-ios.md @@ -26,7 +26,7 @@ same everywhere. Capture sets the catalog `rotation` from the device orientation, like browser capture, so a phone held in portrait plays upright. -Promoted from m2 on 2026-10-09 by maintainer priority. +Deferred to m2; mobile capture does not gate the next breaking cut. ## Related diff --git a/quest/m2/one-port/README.md b/quest/m2/one-port/README.md index 865cf29726..3d78a5fe48 100644 --- a/quest/m2/one-port/README.md +++ b/quest/m2/one-port/README.md @@ -20,21 +20,23 @@ a blocker for this line. ## Plan Deferred to m2 in the 2026-09-30 audit: the consumer is moq.pro's edge, and -STUN's only consumer is P2P, now in m1. SRT is not blocked upstream: +STUN's only consumer is P2P, deferred to m2. SRT is not blocked upstream: [SRT demux](/quest/m2/one-port/srt-demux.md) decided on 2026-09-30 to drive sans-io `srt-protocol` directly instead of waiting on a socket abstraction in srt-tokio. Decided 2026-10-05, from moq.pro's audit: `moq-rtc` taking a fed socket -([rtc-feed](/quest/m2/one-port/rtc-feed.md)) and the shard steering filter join this line, and +([rtc-feed](/quest/m2/one-port/rtc-feed.md)) joins this line, and [the io_uring demux](/quest/m2/uring-demux.md) is planned beside it, all in m2 rather than m3, since moq.pro's edge cutover and its uring rollout wait on them. -Decided 2026-10-13: [UDP demux](/quest/m1/p2p/udp-demux.md) and shard steering moved to m1 under P2P, -because [native pairs](/quest/m1/p2p/native-quic.md) run QUIC and ICE's +Decided 2026-10-15: [UDP demux](/quest/m2/p2p/udp-demux.md) remains +under P2P, now deferred to m2, +because [native pairs](/quest/m2/p2p/native-quic.md) run QUIC and ICE's STUN on one socket through the same demux. The rest of this line builds on -them from m1. +that shared prerequisite in m2. QUIC shard steering landed in #5638; +reuse its filter rather than recreating that completed work. ### Classifying a datagram @@ -70,7 +72,7 @@ decided. ### STUN -Decided 2026-10-08: the public STUN responder is left to P2P (m1), its +Decided 2026-10-08: the public STUN responder is left to P2P (m2), its only consumer. Here, a Binding request carrying USERNAME is an ICE check and goes to WebRTC; any other is dropped and counted. Decided 2026-10-13: the responder binds its own port ([relay STUN](/doc/bin/relay/config.md#stun)), @@ -96,6 +98,6 @@ pre-accepted streams can stand behind. ## Related -- [UDP demux](/quest/m1/p2p/udp-demux.md) - one socket carries QUIC, with a WebRTC hook for embedders; moved to m1 for native P2P +- [UDP demux](/quest/m2/p2p/udp-demux.md) - one socket carries QUIC, with a WebRTC hook for embedders; owned by deferred native P2P in m2 - [One port on the io_uring workers](/quest/m2/uring-demux.md) - the io_uring workers host the UDP demux - [Stream sessions](/quest/m3/uring-tcp/README.md) - the io_uring workers that would host the TCP acceptor later diff --git a/quest/m2/one-port/rtc-feed.md b/quest/m2/one-port/rtc-feed.md index 886cb1d11b..3708dca649 100644 --- a/quest/m2/one-port/rtc-feed.md +++ b/quest/m2/one-port/rtc-feed.md @@ -64,4 +64,4 @@ bound tests keep passing. ## Required -- [UDP demux](/quest/m1/p2p/udp-demux.md) - the WebRTC hook and flow table this feeds from and pins into +- [UDP demux](/quest/m2/p2p/udp-demux.md) - the WebRTC hook and flow table this feeds from and pins into diff --git a/quest/m2/one-port/srt-demux.md b/quest/m2/one-port/srt-demux.md index 49d84db9a4..72b07c5a0c 100644 --- a/quest/m2/one-port/srt-demux.md +++ b/quest/m2/one-port/srt-demux.md @@ -16,7 +16,7 @@ demuxed packets. That is more code, but it removes the dependency on upstream (now rosalyntg/srt-rs) has not been pushed since 2024-05, and its maintainer said they do not actively support it. -The demux side is the flow table from [UDP demux](/quest/m1/p2p/udp-demux.md): +The demux side is the flow table from [UDP demux](/quest/m2/p2p/udp-demux.md): a 4-tuple already pinned to WebRTC (ICE succeeded) is never tested for SRT. An unknown 4-tuple whose full SRT handshake header matches (control bit, type 0, and the SRT magic, not merely `80 00`) pins to SRT provisionally: @@ -35,4 +35,4 @@ with a QUIC client active on the same port; a QUIC short header from a new ## Required -- [UDP demux](/quest/m1/p2p/udp-demux.md) +- [UDP demux](/quest/m2/p2p/udp-demux.md) diff --git a/quest/m2/one-port/tcp-demux.md b/quest/m2/one-port/tcp-demux.md index b13a2de254..c3f8da6275 100644 --- a/quest/m2/one-port/tcp-demux.md +++ b/quest/m2/one-port/tcp-demux.md @@ -54,6 +54,6 @@ right arm. ## Related -- [UDP demux](/quest/m1/p2p/udp-demux.md) - the UDP half +- [UDP demux](/quest/m2/p2p/udp-demux.md) - the UDP half - [mTLS on tls://](/quest/m2/tls-listener-mtls.md) - client-certificate verification on the `tls://` arm this acceptor carries - [WebSocket bitrate caps](/quest/m2/rate-websocket.md) - sizes the kernel buffers of the socket this acceptor boxes diff --git a/quest/m1/p2p/README.md b/quest/m2/p2p/README.md similarity index 84% rename from quest/m1/p2p/README.md rename to quest/m2/p2p/README.md index 8a4c07801c..2e1f0446b5 100644 --- a/quest/m1/p2p/README.md +++ b/quest/m2/p2p/README.md @@ -33,10 +33,17 @@ under "One channel, qmux, ordered". ## Plan -Promoted to m1 in the 2026-10-13 clustering interview: CDN-connected clients -using application-selected shorter or preferred P2P paths are a required -consumer. P2P remains an independently shippable milestone after the shared -routing contracts; no second routing implementation. +Deferred to m2 by the maintainer on 2026-10-15, with signaling, transports, +policy, and validation together. CDN-connected clients using selected peer +paths remain the consumer. Reuse the shared routing contracts; no second +routing implementation. Any incompatible wire additions after lite-07 +finalizes use a later wip/version. + +This epic owns practical P2P setup guidance extending the routing concept: +keep the CDN session while selecting peers, explain no browser transit, and +show application-selected ICE servers including an opt-in relay STUN endpoint +(`stun:relay.example.com:3478`). Link `/bin/relay/config#stun` and the +JS/native P2P reference. Do not imply that direct always beats CDN. ### The relay is the rendezvous and the fallback @@ -65,7 +72,7 @@ off by default, and used only when the application lists it. ### Policy is the application's `Peers` takes the shared origin, a reconnect-safe relay auth handle for -[peer grants](/quest/m1/p2p/peer-grant.md), plus: +[peer grants](/quest/m2/p2p/peer-grant.md), plus: - `enabled`: an explicit on/off signal, off by default; - `prefix`: the reserved prefix, `.p2p/` under the session root by default; @@ -99,7 +106,7 @@ SCTP retransmits it. Decided 2026-10-08: the channel is always ordered, and unordered qmux was dropped. Receiver reassembly would exist only in the Rust qmux, so it could cover native pairs alone, and a native pair runs QUIC, whose streams are independent. The -[harness](/quest/m1/p2p/harness.md) reports the stall so the cost stays +[harness](/quest/m2/p2p/harness.md) reports the stall so the cost stays visible. Channel-per-stream is not planned. moq opens a stream per group, so it churns @@ -118,7 +125,7 @@ Decided 2026-10-13: priority then total cost selects among authorized routes; a direct connection does not always win. A watching tab never re-serves what it receives. Offloading a room of watchers is a local relay's job (`moq-cli --p2p` is the small one), which keeps warm and cold costs and cache-aware routing out of the -protocol. [Peer route policy](/quest/m1/p2p/cost-scopes.md) sets the cost +protocol. [Peer route policy](/quest/m2/p2p/cost-scopes.md) sets the cost knobs and defaults. Target the public `@moq/net` interface regardless of whether its @@ -139,15 +146,15 @@ Backend choice does not change the policy that watching tabs never re-serve. ## Required -- [Peer grants](/quest/m1/p2p/peer-grant.md) - the relay issues a node-bound, asymmetrically signed grant a browser can verify; HS256 keys issue none -- [Signaling and policy](/quest/m1/p2p/signal.md) - opted-in peers find each other under the prefix, the application picks who to dial, and the roster-size gate decides whether STUN is used -- [Native data channel transport](/quest/m1/p2p/webrtc.md) - `moq-tokio` holds a moq-net session with a browser over str0m with a full ICE agent -- [UDP demux](/quest/m1/p2p/udp-demux.md) - one classifier splits QUIC from STUN and WebRTC on the relay's socket and on a native pair's -- [Native pairs over ICE and QUIC](/quest/m1/p2p/native-quic.md) - two native peers connect through ICE, then `moq-quic` over the nominated path with fingerprints pinned from the offer -- [moq-cli joins](/quest/m1/p2p/cli.md) - `--p2p` publishes a roster entry, dials native peers over ICE and QUIC, and serves browsers as a transit hop -- [Peer route policy](/quest/m1/p2p/cost-scopes.md) - application priorities and total cost choose eligible routes, with epoch-aware handover -- [Watch opts in](/quest/m1/p2p/watch.md) - one attribute turns it on in the demo and the watcher migrates to the best eligible route -- [Harness](/quest/m1/p2p/harness.md) - the Playwright harness and the numbers behind every mapping decision +- [Peer grants](/quest/m2/p2p/peer-grant.md) - the relay issues a node-bound, asymmetrically signed grant a browser can verify; HS256 keys issue none +- [Signaling and policy](/quest/m2/p2p/signal.md) - opted-in peers find each other under the prefix, the application picks who to dial, and the roster-size gate decides whether STUN is used +- [Native data channel transport](/quest/m2/p2p/webrtc.md) - `moq-tokio` holds a moq-net session with a browser over str0m with a full ICE agent +- [UDP demux](/quest/m2/p2p/udp-demux.md) - one classifier splits QUIC from STUN and WebRTC on the relay's socket and on a native pair's +- [Native pairs over ICE and QUIC](/quest/m2/p2p/native-quic.md) - two native peers connect through ICE, then `moq-quic` over the nominated path with fingerprints pinned from the offer +- [moq-cli joins](/quest/m2/p2p/cli.md) - `--p2p` publishes a roster entry, dials native peers over ICE and QUIC, and serves browsers as a transit hop +- [Peer route policy](/quest/m2/p2p/cost-scopes.md) - application priorities and total cost choose eligible routes, with epoch-aware handover +- [Watch opts in](/quest/m2/p2p/watch.md) - one attribute turns it on in the demo and the watcher migrates to the best eligible route +- [Harness](/quest/m2/p2p/harness.md) - the Playwright harness and the numbers behind every mapping decision ## Related diff --git a/quest/m1/p2p/cli.md b/quest/m2/p2p/cli.md similarity index 86% rename from quest/m1/p2p/cli.md rename to quest/m2/p2p/cli.md index 1c8bc2d7ad..c68851fbf1 100644 --- a/quest/m1/p2p/cli.md +++ b/quest/m2/p2p/cli.md @@ -4,7 +4,7 @@ `moq-cli --p2p` publishes a roster entry on its relay session, advertises how it can be reached, dials other native peers over -[ICE and QUIC](/quest/m1/p2p/native-quic.md), and accepts browsers over data channels as a transit hop between +[ICE and QUIC](/quest/m2/p2p/native-quic.md), and accepts browsers over data channels as a transit hop between them and the relay. Its policy knobs mirror `@moq/p2p`. ## Plan @@ -19,7 +19,7 @@ surface and per-connection priority/cost mirroring the application API; settle its concrete CLI shape before coding and update all invocations against --help. The roster entry mirrors the `info.json` schema from -[signaling](/quest/m1/p2p/signal.md): the listener's LAN addresses as +[signaling](/quest/m2/p2p/signal.md): the listener's LAN addresses as WebTransport URLs, ordered the way `mdns::Peer::urls` orders them, with the listener's certificate fingerprint. Use signaling's ordered-list shape and dialing order, confirmed in the quest audit, so multi-interface listeners @@ -41,6 +41,6 @@ Docs: a "P2P" section in `doc/bin/cli.md` beside the LAN cluster one. ## Required -- [Native data channel transport](/quest/m1/p2p/webrtc.md) -- [Native pairs over ICE and QUIC](/quest/m1/p2p/native-quic.md) - the native-to-native path -- [Signaling and policy](/quest/m1/p2p/signal.md) - the roster and offer schema this mirrors +- [Native data channel transport](/quest/m2/p2p/webrtc.md) +- [Native pairs over ICE and QUIC](/quest/m2/p2p/native-quic.md) - the native-to-native path +- [Signaling and policy](/quest/m2/p2p/signal.md) - the roster and offer schema this mirrors diff --git a/quest/m1/p2p/cost-scopes.md b/quest/m2/p2p/cost-scopes.md similarity index 95% rename from quest/m1/p2p/cost-scopes.md rename to quest/m2/p2p/cost-scopes.md index 325c958281..248c8db7c4 100644 --- a/quest/m1/p2p/cost-scopes.md +++ b/quest/m2/p2p/cost-scopes.md @@ -44,8 +44,8 @@ in Peers and native configuration. Wire: none beyond shared routing and existing - [JS track handover](/quest/m1/js-group-handover.md) - same-epoch browser migration - [Routes and announces](/quest/m1/cluster-routing/routes.md) - common identity and route ordering -- [Signaling and policy](/quest/m1/p2p/signal.md) - explicit application peer selection -- [moq-cli joins](/quest/m1/p2p/cli.md) - matching native controls +- [Signaling and policy](/quest/m2/p2p/signal.md) - explicit application peer selection +- [moq-cli joins](/quest/m2/p2p/cli.md) - matching native controls ## Related diff --git a/quest/m1/p2p/harness.md b/quest/m2/p2p/harness.md similarity index 96% rename from quest/m1/p2p/harness.md rename to quest/m2/p2p/harness.md index 6ce03a6f13..8397794451 100644 --- a/quest/m1/p2p/harness.md +++ b/quest/m2/p2p/harness.md @@ -51,5 +51,5 @@ points. ## Required -- [moq-cli joins](/quest/m1/p2p/cli.md) -- [Signaling and policy](/quest/m1/p2p/signal.md) +- [moq-cli joins](/quest/m2/p2p/cli.md) +- [Signaling and policy](/quest/m2/p2p/signal.md) diff --git a/quest/m1/p2p/native-quic.md b/quest/m2/p2p/native-quic.md similarity index 84% rename from quest/m1/p2p/native-quic.md rename to quest/m2/p2p/native-quic.md index 81626f6fd3..2b2466e26e 100644 --- a/quest/m1/p2p/native-quic.md +++ b/quest/m2/p2p/native-quic.md @@ -7,7 +7,7 @@ selected by their applications connect directly across a NAT: ICE finds a path through host and server-reflexive candidates, then a `moq-quic` connection runs over the nominated pair, with each side's certificate pinned by the fingerprint the other sent in its offer or answer. A pair with a -browser still uses a [data channel](/quest/m1/p2p/webrtc.md). +browser still uses a [data channel](/quest/m2/p2p/webrtc.md). ## Plan @@ -18,19 +18,19 @@ Decided with the maintainer on 2026-10-13: the relay as rendezvous and fallback. - ICE is str0m's standalone sans-IO agent, the `is` crate (0.11, already in the lockfile through str0m; str0m re-exports it as `str0m::ice`). Gather - candidates through the module [webrtc](/quest/m1/p2p/webrtc.md) lifts out + candidates through the module [webrtc](/quest/m2/p2p/webrtc.md) lifts out of `moq-rtc`, with host candidates, `.local` resolution, and the STUN Binding client; don't copy it. - QUIC is `moq-quic`, fed datagrams from the nominated pair. - Authentication: each side generates a self-signed certificate and sends its SHA-256 fingerprint in its offer or answer, as DTLS fingerprints ride in SDP. Both sides pin it with the existing `tls.fingerprint` check. MoQ - AUTH with a [peer grant](/quest/m1/p2p/peer-grant.md) still runs on top. + AUTH with a [peer grant](/quest/m2/p2p/peer-grant.md) still runs on top. - After nomination the session's socket carries ICE consent checks and - QUIC, split by the [UDP demux](/quest/m1/p2p/udp-demux.md) classifier. + QUIC, split by the [UDP demux](/quest/m2/p2p/udp-demux.md) classifier. Roles are deterministic: the ICE controlling side is the QUIC client, so a -simultaneous dial resolves the same way [signaling](/quest/m1/p2p/signal.md) +simultaneous dial resolves the same way [signaling](/quest/m2/p2p/signal.md) resolves offers (the lower id wins). The offer and answer carry ICE credentials, the fingerprint, and candidates on the pair's `signal` track; settle the field names with signaling's schema, and add a roster field @@ -54,11 +54,11 @@ independent QUIC streams here, so unordered reassembly would serve nobody. Tests: unit tests drive two agents and two `moq-quic` endpoints in memory with mocked time, covering nomination, a wrong fingerprint refused, and a -consent-check failure closing the session. The [harness](/quest/m1/p2p/harness.md) +consent-check failure closing the session. The [harness](/quest/m2/p2p/harness.md) covers LAN and NAT pairs between two `moq-cli --p2p` nodes. ## Required -- [UDP demux](/quest/m1/p2p/udp-demux.md) - the classifier that splits STUN from QUIC -- [Native data channel transport](/quest/m1/p2p/webrtc.md) - the shared ICE gathering module -- [Signaling and policy](/quest/m1/p2p/signal.md) - the offer schema and roster this extends +- [UDP demux](/quest/m2/p2p/udp-demux.md) - the classifier that splits STUN from QUIC +- [Native data channel transport](/quest/m2/p2p/webrtc.md) - the shared ICE gathering module +- [Signaling and policy](/quest/m2/p2p/signal.md) - the offer schema and roster this extends diff --git a/quest/m1/p2p/peer-grant.md b/quest/m2/p2p/peer-grant.md similarity index 98% rename from quest/m1/p2p/peer-grant.md rename to quest/m2/p2p/peer-grant.md index df0a362029..b1eb399cb7 100644 --- a/quest/m1/p2p/peer-grant.md +++ b/quest/m2/p2p/peer-grant.md @@ -50,7 +50,7 @@ AUTH_KEYS, checks `exp`, matches node id against the roster, and requires granted paths. A missing, expired, HMAC-signed, or unproven grant, or one whose `cnf` is not the session's proven key, is not a grant. -[Signaling and policy](/quest/m1/p2p/signal.md) presents the grant in band +[Signaling and policy](/quest/m2/p2p/signal.md) presents the grant in band on each direct session. P2P is its only consumer, so it lives on this line rather than the auth line (decided in the 2026-09-30 audit). diff --git a/quest/m1/p2p/signal.md b/quest/m2/p2p/signal.md similarity index 93% rename from quest/m1/p2p/signal.md rename to quest/m2/p2p/signal.md index 0aa5afab47..c428ec01e7 100644 --- a/quest/m1/p2p/signal.md +++ b/quest/m2/p2p/signal.md @@ -12,7 +12,7 @@ size above which new negotiations stop using them. No new relay behavior. `Peers` in `@moq/p2p` takes the shared origin, a reconnect-safe handle to the relay connection's auth, and the policy knobs from the -[questline README](/quest/m1/p2p/README.md): `enabled`, `prefix`, +[questline README](/quest/m2/p2p/README.md): `enabled`, `prefix`, `iceServers`, `max`, `meta`, plus application-owned selection and per-connection priority/cost. Decided 2026-10-13: only explicitly selected peers are dialed; there is no default dial-all. Prefer a selected set/handle to a callback, @@ -30,13 +30,13 @@ hop list, so no hop chain carries it. Roster: each peer publishes `` with an `info.json` snapshot track: the moq ALPNs it accepts, `webrtc: true`, the presenter public key that -[peer grants](/quest/m1/p2p/peer-grant.md) bind, the application's `meta`, +[peer grants](/quest/m2/p2p/peer-grant.md) bind, the application's `meta`, and for native peers an optional WebTransport entry with an ordered list of URLs and the listener's certificate fingerprint, plus the field -[native pairs](/quest/m1/p2p/native-quic.md) add for ICE+QUIC. +[native pairs](/quest/m2/p2p/native-quic.md) add for ICE+QUIC. Decided in the quest audit: use a list so a native peer can advertise its reachable interfaces without choosing one for every browser. This quest -owns the schema shared with [moq-cli](/quest/m1/p2p/cli.md). +owns the schema shared with [moq-cli](/quest/m2/p2p/cli.md). Pairing follows explicit selection: pair broadcasts exist only for selected pairs. The application may still choose a full mesh, so measure that cost @@ -87,7 +87,7 @@ and short-lived, which the peer presents in band with a proof of possession; the other side verifies the relay's signature, the node id and key against the roster, and that the key is the one the session proved, then serves only the granted paths. Issuance, asymmetric keys, JWKS, and refresh live in -[Peer grants](/quest/m1/p2p/peer-grant.md), and the session's key proof in +[Peer grants](/quest/m2/p2p/peer-grant.md), and the session's key proof in [Identity proof](/quest/m1/cluster-routing/identity.md): HMAC keys cannot be given to browsers without also letting them forge grants, so an HS256-only relay issues nothing. A peer session with no verifiable grant serves nothing; @@ -96,4 +96,4 @@ there is no equal-scope shortcut. ## Required - [Routes](/quest/m1/cluster-routing/routes.md) - the ROUTE_START node id the roster keys on -- [Peer grants](/quest/m1/p2p/peer-grant.md) - the node-bound, asymmetrically signed credential a direct session presents; HS256 keys issue none +- [Peer grants](/quest/m2/p2p/peer-grant.md) - the node-bound, asymmetrically signed credential a direct session presents; HS256 keys issue none diff --git a/quest/m1/p2p/udp-demux.md b/quest/m2/p2p/udp-demux.md similarity index 94% rename from quest/m1/p2p/udp-demux.md rename to quest/m2/p2p/udp-demux.md index 8e01705cc8..5e9d115286 100644 --- a/quest/m1/p2p/udp-demux.md +++ b/quest/m2/p2p/udp-demux.md @@ -10,9 +10,7 @@ splits a native P2P session's socket into ICE's STUN and QUIC. ## Plan -Decided 2026-10-13: moved from the m2 [one-port](/quest/m2/one-port/README.md) -line into m1 whole, with shard steering, -so [native P2P pairs](/quest/m1/p2p/native-quic.md) share one demux with the +Decided 2026-10-15: deferred to m2 with P2P as a whole, so [native P2P pairs](/quest/m2/p2p/native-quic.md) share one demux with the relay rather than a P2P-only classifier (maintainer's choice over splitting the classifier out). A native P2P session's socket uses the same classifier: STUN goes to its ICE agent and QUIC to its endpoint; it has no flow table diff --git a/quest/m1/p2p/watch.md b/quest/m2/p2p/watch.md similarity index 78% rename from quest/m1/p2p/watch.md rename to quest/m2/p2p/watch.md index d77779f666..43655535d3 100644 --- a/quest/m1/p2p/watch.md +++ b/quest/m2/p2p/watch.md @@ -11,10 +11,10 @@ the peer goes away, with no visible interruption. `moq-watch` and `moq-publish` gain a `p2p` attribute that constructs `Peers` on the shared connection's origin and its relay auth handle (see -[Signaling and policy](/quest/m1/p2p/signal.md)), with the demo's ICE servers +[Signaling and policy](/quest/m2/p2p/signal.md)), with the demo's ICE servers and a `max` from the page; `demo/web` exposes the toggle and an explicit application peer-selection policy. Enabling discovery alone must not dial every peer. The route pick is the -origin's, under the rule from [Peer route policy](/quest/m1/p2p/cost-scopes.md): +origin's, under the rule from [Peer route policy](/quest/m2/p2p/cost-scopes.md): application priority then total cost selects among eligible connections. The CDN session remains, but unused CDN payload demand is released. Same-epoch handover resumes; epochless source changes restart. A direct link need not win. @@ -24,6 +24,6 @@ directly, and watcher tabs pulling from a `moq-cli --p2p` hop. ## Required -- [Signaling and policy](/quest/m1/p2p/signal.md) -- [moq-cli joins](/quest/m1/p2p/cli.md) - the native hop the second topology shows -- [Peer route policy](/quest/m1/p2p/cost-scopes.md) +- [Signaling and policy](/quest/m2/p2p/signal.md) +- [moq-cli joins](/quest/m2/p2p/cli.md) - the native hop the second topology shows +- [Peer route policy](/quest/m2/p2p/cost-scopes.md) diff --git a/quest/m1/p2p/webrtc.md b/quest/m2/p2p/webrtc.md similarity index 94% rename from quest/m1/p2p/webrtc.md rename to quest/m2/p2p/webrtc.md index 9daac1e3bb..92506d78bc 100644 --- a/quest/m1/p2p/webrtc.md +++ b/quest/m2/p2p/webrtc.md @@ -39,5 +39,5 @@ candidate is offered and selected. Browser interop is the harness's job. ## Related -- [moq-cli joins](/quest/m1/p2p/cli.md) - the first consumer -- [Native pairs over ICE and QUIC](/quest/m1/p2p/native-quic.md) - reuses the ICE gathering module this lifts out of `moq-rtc` +- [moq-cli joins](/quest/m2/p2p/cli.md) - the first consumer +- [Native pairs over ICE and QUIC](/quest/m2/p2p/native-quic.md) - reuses the ICE gathering module this lifts out of `moq-rtc` diff --git a/quest/m2/pipeline-requests/fetch.md b/quest/m2/pipeline-requests/fetch.md index cfc968a1f2..a276902bff 100644 --- a/quest/m2/pipeline-requests/fetch.md +++ b/quest/m2/pipeline-requests/fetch.md @@ -62,6 +62,6 @@ Public API: none. Wire: none (ordering only). ## Related - [Pipelined SUBSCRIBE](/quest/m2/pipeline-requests/subscribe.md) - the same change for subscriptions -- [moq-transport ranges](/quest/m1/subscribe-ranges/ietf.md) - adjacent code in `ietf/subscriber.rs` and `model/origin.rs`; this lands first and ranges rebases +- [moq-transport ranges](/quest/m1/subscribe-ranges/ietf.md) - adjacent code in `ietf/subscriber.rs` and `model/origin.rs`; ranges remains in m1; this optional m2 optimization adapts to the landed range API - [Retired requests](https://github.com/moq-dev/moq/pull/5294) - staged requests retain their selected instance; retries cannot resolve a replacement by path - [FETCH max-delay](/quest/m1/fetch-max-delay.md) - setup and recovery do not reset or bypass the reader's age budget diff --git a/quest/m1/plan-watch-worker.md b/quest/m2/plan-watch-worker.md similarity index 97% rename from quest/m1/plan-watch-worker.md rename to quest/m2/plan-watch-worker.md index b1ff5e5887..86149835d2 100644 --- a/quest/m1/plan-watch-worker.md +++ b/quest/m2/plan-watch-worker.md @@ -3,7 +3,7 @@ ## Goal A measured decision on how `@moq/watch` moves its heavy lifting off the main -thread, recorded by rewriting [Watch worker](/quest/m1/watch-worker.md). The +thread, recorded by rewriting [Watch worker](/quest/m2/watch-worker.md). The jank harness and the N-player sweep that decided it land and run nightly; the prototypes themselves are discarded. @@ -73,6 +73,6 @@ follow-up wires the capture worklet to the encoder worker with a ## Related -- [Watch worker](/quest/m1/watch-worker.md) - the implementation this rewrites +- [Watch worker](/quest/m2/watch-worker.md) - the implementation this rewrites - [Browser benchmarks](/quest/m2/browser-benchmarks.md) - artifact conventions; may absorb the harness later - [A/V clock](/quest/m1/av-clock.md) - the `Sync` shape the implementation moves; the prototype can pace against today's diff --git a/quest/m2/qos-lag-dashboard.md b/quest/m2/qos-lag-dashboard.md index 789e426b80..78af074f40 100644 --- a/quest/m2/qos-lag-dashboard.md +++ b/quest/m2/qos-lag-dashboard.md @@ -10,9 +10,9 @@ and per node like the existing counters. ## Plan - Read both from the stats totals and [prefix - tracks](/quest/m1/stats-prefix-tracks.md), which land first, + tracks](/quest/m2/stats-prefix-tracks.md), which land first, rather than the `publisher.json` rows they sit on today (decided in the - 2026-10-06 audit; the rows stay for existing readers, 2026-10-12); [viewer lag histogram](/quest/m1/qos/lag-histogram.md) + 2026-10-06 audit; the rows stay for existing readers, 2026-10-12); [viewer lag histogram](/quest/m2/qos/lag-histogram.md) lands them on the egress side of its per-group totals and prefix tracks (decided 2026-10-08). `lag` is a cumulative byte count per bucket keyed by its upper edge (`"50ms"` to `"5s"`, then `"inf"`, empty buckets @@ -34,5 +34,5 @@ Public API: none. Wire: none. ## Required -- [Viewer lag histogram](/quest/m1/qos/lag-histogram.md) - the `lag` histogram and `dropped` counters the dashboard reads -- [Stats prefix tracks](/quest/m1/stats-prefix-tracks.md) - the prefix tracks the dashboard reads lag from, beside the totals +- [Viewer lag histogram](/quest/m2/qos/lag-histogram.md) - the `lag` histogram and `dropped` counters the dashboard reads +- [Stats prefix tracks](/quest/m2/stats-prefix-tracks.md) - the prefix tracks the dashboard reads lag from, beside the totals diff --git a/quest/m2/qos-publisher-timeliness.md b/quest/m2/qos-publisher-timeliness.md index 5425497a31..eda75132bd 100644 --- a/quest/m2/qos-publisher-timeliness.md +++ b/quest/m2/qos-publisher-timeliness.md @@ -11,7 +11,7 @@ cooperation. ## Plan This is the ingress mirror of the egress `lag` histogram ([viewer lag -histogram](/quest/m1/qos/lag-histogram.md), which lands first so this reuses +histogram](/quest/m2/qos/lag-histogram.md), which lands first so this reuses its histogram type, decided 2026-10-08) and needs no acknowledgments: the relay is the receiver, and a frame is measured when its last byte arrives, because a partial frame is not useful. @@ -29,7 +29,7 @@ Document the window length beside the bucket edges. Aggregate as a byte-weighted cumulative histogram of drift on the ingress (`Role::Subscriber`) side of the stats totals and [prefix -tracks](/quest/m1/stats-prefix-tracks.md), which land first, +tracks](/quest/m2/stats-prefix-tracks.md), which land first, rather than the per-path rows (decided in the 2026-10-06 audit), with the same bucket edges and the same monotonic contract as the egress `lag` histogram. Beside it keep two cumulative counters: `timestamp_regressions`, frames whose timestamp is below the @@ -55,8 +55,8 @@ excluded. ## Required -- [Viewer lag histogram](/quest/m1/qos/lag-histogram.md) - the egress histogram whose type and edges this reuses -- [Stats prefix tracks](/quest/m1/stats-prefix-tracks.md) - the prefix tracks the drift histogram lands on, beside the totals +- [Viewer lag histogram](/quest/m2/qos/lag-histogram.md) - the egress histogram whose type and edges this reuses +- [Stats prefix tracks](/quest/m2/stats-prefix-tracks.md) - the prefix tracks the drift histogram lands on, beside the totals ## Related diff --git a/quest/m1/qos/README.md b/quest/m2/qos/README.md similarity index 85% rename from quest/m1/qos/README.md rename to quest/m2/qos/README.md index 70cb95778e..4e41e17563 100644 --- a/quest/m1/qos/README.md +++ b/quest/m2/qos/README.md @@ -2,7 +2,7 @@ ## Goal -Publish the breaking relay QoS lag and dropped-counter API in this release. +Publish the relay QoS lag and dropped-counter API after the next release. The histogram lands on totals and prefix tracks beside the existing maps. Publisher timeliness, client media health, and dashboard presentation remain separate additive work in m2. @@ -34,8 +34,9 @@ and publisher timeliness lives downstream. This line only reports the counters. Decided (2026-10-06 audit): [stats prefix -tracks](/quest/m1/stats-prefix-tracks.md) lands first, since it is -a prerequisite of the breaking lag API in m1; the lag histogram lands on the stats totals and +tracks](/quest/m2/stats-prefix-tracks.md) lands first, since it is +a prerequisite of the lag API; both are deferred to m2 by the maintainer +on 2026-10-15; the lag histogram lands on the stats totals and prefix tracks rather than the per-path `publisher.json` and `subscriber.json` map rows, which stay unchanged for existing readers (2026-10-12). @@ -43,7 +44,7 @@ Rejected: the histogram first, which would make prefix tracks carry the lag histogram and dropped counters across. Decided (2026-10-08): the egress histogram is its own quest, -[viewer lag histogram](/quest/m1/qos/lag-histogram.md), since every other +[viewer lag histogram](/quest/m2/qos/lag-histogram.md), since every other child reads it. The line's PR, #4133, closed without landing (2026-10-09); its code is kept on `wip/4133-lag-histogram`. @@ -54,13 +55,13 @@ on `main`. ## Required -- [Viewer lag histogram](/quest/m1/qos/lag-histogram.md) - the egress `lag` +- [Viewer lag histogram](/quest/m2/qos/lag-histogram.md) - the egress `lag` histogram and `dropped` counters land on the stats totals and prefix tracks ## Related -- [Stats prefix tracks](/quest/m1/stats-prefix-tracks.md) - +- [Stats prefix tracks](/quest/m2/stats-prefix-tracks.md) - lands first beside the map rows; this line's histograms land on the totals and prefix tracks - [Media stats](/quest/m2/stats/README.md) - publishers and viewers report diff --git a/quest/m1/qos/lag-histogram.md b/quest/m2/qos/lag-histogram.md similarity index 95% rename from quest/m1/qos/lag-histogram.md rename to quest/m2/qos/lag-histogram.md index 901e560c4b..86a8510f4e 100644 --- a/quest/m1/qos/lag-histogram.md +++ b/quest/m2/qos/lag-histogram.md @@ -6,7 +6,7 @@ The relay's `moq-stats` egress side reports, per broadcast, how far behind its viewers are according to what the network has acknowledged: a byte-weighted cumulative `lag` histogram and `dropped` counters (duration, bytes, and groups dropped before they were acknowledged), on the stats totals -and [prefix tracks](/quest/m1/stats-prefix-tracks.md). A spliced subscription +and [prefix tracks](/quest/m2/stats-prefix-tracks.md). A spliced subscription stays exact across a route switch, and a closing subscription records its last partial interval. @@ -57,4 +57,4 @@ stats JSON gains the egress fields. ## Required -- [Stats prefix tracks](/quest/m1/stats-prefix-tracks.md) - the prefix tracks the histogram lands on, beside the totals +- [Stats prefix tracks](/quest/m2/stats-prefix-tracks.md) - the prefix tracks the histogram lands on, beside the totals diff --git a/quest/m2/quic-deadline.md b/quest/m2/quic-deadline.md index e0193c031a..66294205c9 100644 --- a/quest/m2/quic-deadline.md +++ b/quest/m2/quic-deadline.md @@ -22,7 +22,9 @@ Implement in `moq-quic`. deadline, estimate the arrival instant as now plus the forward one-way delay. Start with `min_rtt / 2`, corrected by the peer's reported ACK delay; the [receive-timestamps spike](/quest/m3/quic-gcc.md) measures whether - receive timestamps should replace that guess with a measured forward delay. If the estimate is past the deadline, + receive-time differences can improve that estimate, accounting for clock + offset, drift, and uncertainty. They do not alone measure absolute one-way + delay. If the estimate is past the deadline, reset the stream with a dedicated error code and drop its retransmit ranges, including bytes already lost, so flow control is returned in one step. - Proactive tail-loss probe: when the newest in-flight packet carries deadline @@ -57,7 +59,7 @@ Decided in the 2026-09-30 audit: moved to m2. No m1 quest consumes it. ## Related - [QUIC receive-timestamps spike](/quest/m3/quic-gcc.md) - measures whether - receive timestamps give a forward delay worth replacing the half-RTT estimate + receive-time differences improve an explicit delay estimator over half-RTT - [ACK hook](/quest/m2/quic-ack-hook.md) - the same pattern for a moq-net method backed by `moq-quic` - [Discover media headroom](/quest/m2/quic-probe.md) - can reuse retransmission machinery if redundant capacity probes prove worthwhile diff --git a/quest/m2/quic-probe.md b/quest/m2/quic-probe.md index d61d1b7ae3..ca04a0cf42 100644 --- a/quest/m2/quic-probe.md +++ b/quest/m2/quic-probe.md @@ -51,6 +51,6 @@ requires it. - [Viewer up-switch](/quest/m2/viewer-upswitch.md) - the viewer side, which requires this - [Natural media drains](/quest/m2/quic-bbr-natural-drain.md) - separate ProbeRTT policy experiment -- [QUIC receive-timestamps spike](/quest/m3/quic-gcc.md) - measures forward delay, which a later delay-based controller would read headroom from +- [QUIC receive-timestamps spike](/quest/m3/quic-gcc.md) - evaluates delay variation and estimator accuracy for a later delay-based controller - [noq#811](https://github.com/n0-computer/noq/issues/811) - probing while app-limited, proposed to n0 - [noq#476](https://github.com/n0-computer/noq/issues/476) - the earlier bitrate-probing ask diff --git a/quest/m2/release/README.md b/quest/m2/release/README.md index 64a57aa368..41b633d271 100644 --- a/quest/m2/release/README.md +++ b/quest/m2/release/README.md @@ -34,6 +34,7 @@ production stability proven merely because upstream artifacts published. - [Dart publication](/quest/m2/release/dart-publish.md) - the wrapper and native artifacts install from the registry - [Final libmoq stub](/quest/m2/release/libmoq-final-release.md) - publish the migration notice before removing its source - [Retire libmoq](/quest/m2/release/libmoq-retire.md) - cleanup only after that publication +- [Final hand-written C notice](/quest/m2/release/c-final-release.md) - publish the migration notice before source retirement - [Retire hand-written C](/quest/m2/release/c-retire.md) - complete the final old-package notice and source retirement - [Hosted worklet adoption](/quest/m2/release/dogfood-assets.md) - downstream consumers exercise the published assets - [moq.sh deployment](/quest/m2/release/moq-sh-deploy.md) - verify its release-triggered publication diff --git a/quest/m2/release/c-final-release.md b/quest/m2/release/c-final-release.md new file mode 100644 index 0000000000..e1e0ac8087 --- /dev/null +++ b/quest/m2/release/c-final-release.md @@ -0,0 +1,12 @@ +# [XS] Publish the final hand-written C migration notice + +## Goal + +Waiting on publication: the final hand-written `moq-c` 0.7.x release tells +users to migrate to generated `moq-c` 0.8.0. Verify the published registry +artifact contains the notice before deleting the old source. Delete this +condition after publication is verified. + +## Related + +- [Retire hand-written C](/quest/m2/release/c-retire.md) - source removal follows the final notice and generated package publication diff --git a/quest/m2/release/c-retire.md b/quest/m2/release/c-retire.md index b0c0ec768e..3a8333924f 100644 --- a/quest/m2/release/c-retire.md +++ b/quest/m2/release/c-retire.md @@ -14,6 +14,9 @@ it are deleted. `doc/setup/upgrade.md` tells C users how to move. ## Required +- [Final hand-written C notice](/quest/m2/release/c-final-release.md) - published before deleting its source +- [Generated C publication](/quest/m2/release/c-publish.md) - the replacement is available and verified + - [C consumers](/quest/m1/c/consumers.md) - nothing in the repository still calls the hand-written ABI ## Related diff --git a/quest/m1/rendition-preference.md b/quest/m2/rendition-preference.md similarity index 100% rename from quest/m1/rendition-preference.md rename to quest/m2/rendition-preference.md diff --git a/quest/m2/route-health/README.md b/quest/m2/route-health/README.md index da624e396e..c9e41af55a 100644 --- a/quest/m2/route-health/README.md +++ b/quest/m2/route-health/README.md @@ -62,7 +62,7 @@ third route, simultaneous relay decisions, loss/reordering, cancelled demand, and stale/replayed progress. Wire these into CI. Report actual media overlap, control traffic, wakes, switches, and memory across tracks, candidate connections, and downstream readers. Extend the -[P2P harness](/quest/m1/p2p/harness.md) with real browser/native cases: +[P2P harness](/quest/m2/p2p/harness.md) with real browser/native cases: metadata-only standby, stalled payload with advancing control, same-epoch promotion with bounded overlap, verified failback, and epochless exclusion. Add a route-health section to the routing concept page. diff --git a/quest/m2/simulcast-rung-disable.md b/quest/m2/simulcast-rung-disable.md index 3217602b2c..c9315c2e21 100644 --- a/quest/m2/simulcast-rung-disable.md +++ b/quest/m2/simulcast-rung-disable.md @@ -58,6 +58,6 @@ publishers expose for it. Wire: none; `enabled` already exists. ## Related - [Ladder controller](/quest/m3/ladder/controller.md) - the same rung disable for generated transcode ladders -- [Rendition preference](/quest/m1/rendition-preference.md) - viewer selection that drops disabled renditions before ranking the rest +- [Rendition preference](/quest/m2/rendition-preference.md) - viewer selection that drops disabled renditions before ranking the rest - [OBS multitrack](/quest/m2/obs-multitrack.md) - OBS's native multitrack encoders as the renditions this disables - [Viewer up-switch](/quest/m2/viewer-upswitch.md) - the viewer side of moving between renditions as capacity changes diff --git a/quest/m2/starvation-frames.md b/quest/m2/starvation-frames.md index 7c18b843c1..4923e8c779 100644 --- a/quest/m2/starvation-frames.md +++ b/quest/m2/starvation-frames.md @@ -41,9 +41,9 @@ Backends that return unsupported from `poll_acked` keep the group-granularity sampling from the parent quest, so the histogram never disappears over qmux or a browser transport; document which resolution a node offers. -Decided in the 2026-09-30 audit: moved to m2. The group-granularity sampler -from the parent quest answers the m1 question, and this waits on the m2 ACK -hook. +Deferred to m2 with the parent QoS sampler. Group-granularity lag lands +first after the next release; frame-granularity sampling then adds the ACK +hook without making either resolution a release gate. Decided 2026-10-08: the group-granularity sampler is the egress lag histogram from the QoS line, which so far exists only on its line branch, so that quest @@ -60,5 +60,5 @@ unsupported. - [poll_acked on moq-net's send stream](/quest/m2/quic-ack-hook.md) - the hook this samples through -- [Egress lag histogram](/quest/m1/qos/lag-histogram.md) - the +- [Egress lag histogram](/quest/m2/qos/lag-histogram.md) - the group-granularity sampler and starvation histogram this refines diff --git a/quest/m1/stats-prefix-tracks.md b/quest/m2/stats-prefix-tracks.md similarity index 98% rename from quest/m1/stats-prefix-tracks.md rename to quest/m2/stats-prefix-tracks.md index 590a80a3b1..c02cfc6ef3 100644 --- a/quest/m1/stats-prefix-tracks.md +++ b/quest/m2/stats-prefix-tracks.md @@ -94,7 +94,7 @@ and payloads. ## Related -- [Viewer lag histogram](/quest/m1/qos/lag-histogram.md) - lands on the totals +- [Viewer lag histogram](/quest/m2/qos/lag-histogram.md) - lands on the totals and these prefix tracks - [Publisher timeliness at relay ingest](/quest/m2/qos-publisher-timeliness.md) - the drift histogram lands on the same tracks diff --git a/quest/m2/stats/README.md b/quest/m2/stats/README.md index 000355cf3a..d8b7a08ba6 100644 --- a/quest/m2/stats/README.md +++ b/quest/m2/stats/README.md @@ -90,7 +90,7 @@ kind. Gauges are carried but never summed. - **On main.** Every change is additive: optional sections on `#[non_exhaustive]` catalog types, and new types. The line left the - [QoS](/quest/m1/qos/README.md) line, which keeps the relay's moq-stats + [QoS](/quest/m2/qos/README.md) line, which keeps the relay's moq-stats changes. - **No `@moq/stats` package.** Media types live in `@moq/hang`, and the demo dashboard's relay-stats reader stays where it is. @@ -124,7 +124,7 @@ connection view and the dashboard flow. ## Related -- [QoS](/quest/m1/qos/README.md) - the relay's delivery counters; the +- [QoS](/quest/m2/qos/README.md) - the relay's delivery counters; the combined per-broadcast verdict reading both lives downstream - [Encoder feedback](/quest/m3/stats-encoder-feedback.md) - a Rust encoder adapts its bitrate to what its viewers report diff --git a/quest/m2/stats/health.md b/quest/m2/stats/health.md index c487b1fd40..78918b6f52 100644 --- a/quest/m2/stats/health.md +++ b/quest/m2/stats/health.md @@ -67,6 +67,6 @@ Public API: new health types in `hang` and `@moq/hang`. Wire: none. ## Related -- [QoS](/quest/m1/qos/README.md) - the relay's delivery counters, which a combined downstream verdict also reads +- [QoS](/quest/m2/qos/README.md) - the relay's delivery counters, which a combined downstream verdict also reads - [Broadcast epochs](/quest/m0/broadcast-epoch/README.md) - a new epoch is one way a counter reset shows - [TS health counters](/quest/m2/ts-health-stats.md) - container counters kept out of the verdict diff --git a/quest/m2/ts-hitless.md b/quest/m2/ts-hitless.md index c0a3ddcac8..78beaa841d 100644 --- a/quest/m2/ts-hitless.md +++ b/quest/m2/ts-hitless.md @@ -24,7 +24,7 @@ Decided 2026-10-13 by the maintainer: the coordinate the legs share is the broadcast's media timeline, each object's timestamp on its catalog clock, with a constant mux rate. Two exports of one broadcast already read the same objects and timestamps, so this needs no importer -alignment; [same-epoch importers](/quest/m1/hop-aligned-import.md) extend it +alignment; [same-epoch importers](/quest/m2/hop-aligned-import.md) extend it to legs fed by different importers of one epoch. Every byte a `--sync` leg renders from the objects it has (slot, PCR, table, PES header, continuity counter) is a function of those objects, that coordinate, the catalog, and @@ -90,5 +90,5 @@ Public API: `moq export ts --sync`. ## Related - [TS over RTP legs](/quest/m2/ts-rtp-egress.md) - packetizes these slots with matching RTP headers for ST 2022-7 -- [Same-epoch importers](/quest/m1/hop-aligned-import.md) - extends the coordinate to legs fed by different importers +- [Same-epoch importers](/quest/m2/hop-aligned-import.md) - extends the coordinate to legs fed by different importers - [TS passthrough export](/quest/m2/ts-passthrough-export.md) - identical packets for common objects by construction, in its own lane diff --git a/quest/m1/ts-passthrough-epoch-cuts.md b/quest/m2/ts-passthrough-epoch-cuts.md similarity index 94% rename from quest/m1/ts-passthrough-epoch-cuts.md rename to quest/m2/ts-passthrough-epoch-cuts.md index a64636c70f..f969421b4c 100644 --- a/quest/m1/ts-passthrough-epoch-cuts.md +++ b/quest/m2/ts-passthrough-epoch-cuts.md @@ -17,7 +17,7 @@ importer's first PCR. ## Plan Decided 2026-10-13 by the maintainer: every group start is a function of -the content and the [epoch's timeline](/quest/m1/epoch-anchor.md), never of +the content and the [epoch's timeline](/quest/m2/epoch-anchor.md), never of when the importer started. - Fallback cuts fall on a grid of the epoch timeline, so an importer started @@ -60,9 +60,9 @@ Public API: none. ## Required -- [Epoch anchor](/quest/m1/epoch-anchor.md) - the timeline the grid sits on +- [Epoch anchor](/quest/m2/epoch-anchor.md) - the timeline the grid sits on ## Related -- [Same-epoch importers](/quest/m1/hop-aligned-import.md) - group sequences and the end-to-end pair +- [Same-epoch importers](/quest/m2/hop-aligned-import.md) - group sequences and the end-to-end pair - [TS passthrough export](/quest/m2/ts-passthrough-export.md) - identical objects export as identical packets diff --git a/quest/m2/ts-passthrough-export.md b/quest/m2/ts-passthrough-export.md index 8038e109af..20ae151d27 100644 --- a/quest/m2/ts-passthrough-export.md +++ b/quest/m2/ts-passthrough-export.md @@ -90,4 +90,4 @@ Public API: a new export mode; nothing existing breaks. - [T-STD TS export](/quest/m1/tstd/README.md) - the jitter buffer and clock recovery this release shares - [TS validation](/test/ts/README.md) - shared automatic graders and controls - [Loss and recovery](/quest/m1/ts-reliability/recovery.md) - actual missing content and reacquisition proof -- [Same-epoch importers](/quest/m1/hop-aligned-import.md) - identical common objects do not prove independent importers publish identical groups +- [Same-epoch importers](/quest/m2/hop-aligned-import.md) - identical common objects do not prove independent importers publish identical groups diff --git a/quest/m1/uniffi-fork.md b/quest/m2/uniffi-fork.md similarity index 90% rename from quest/m1/uniffi-fork.md rename to quest/m2/uniffi-fork.md index 588bba9bfa..bdf8ddb201 100644 --- a/quest/m1/uniffi-fork.md +++ b/quest/m2/uniffi-fork.md @@ -14,15 +14,15 @@ are unchanged by the move itself. Decided with the maintainer on 2026-10-13, after a review of how moq uses UniFFI: -- **Base**: [the BoltFFI spike](/quest/m1/boltffi.md) decides whether the +- **Base**: [the BoltFFI spike](/quest/m2/boltffi.md) decides whether the fork starts from UniFFI or BoltFFI. The plan below assumes UniFFI. If BoltFFI is chosen, re-plan this quest against it (the repo name, the crate names, porting the Go, Dart, and C++/C generators to BoltFFI) along with the quests that name UniFFI internals: - [Payload bytes](/quest/m1/ffi-bytes.md), - [Poll-native async](/quest/m1/ffi-poll.md), + [Payload bytes](/quest/m2/ffi-bytes.md), + [Poll-native async](/quest/m2/ffi-poll.md), [Zero-copy byte returns](/quest/m2/ffi-bytes-return.md), - [Kotlin calls](/quest/m1/kt-ffi-calls.md), and + [Kotlin calls](/quest/m2/kt-ffi-calls.md), and [Namespaced external types](/quest/m2/uniffi-namespaces.md). - **Why**: every UniFFI minor release breaks external generators (0.31 @@ -68,10 +68,10 @@ Public API: none at the move; moq-ffi's dependency names change. Wire: none. ## Required -- [BoltFFI or UniFFI](/quest/m1/boltffi.md) - picks the base this fork starts from +- [BoltFFI or UniFFI](/quest/m2/boltffi.md) - picks the base this fork starts from ## Related -- [Payload bytes](/quest/m1/ffi-bytes.md) - the first patch the fork carries -- [Kotlin calls](/quest/m1/kt-ffi-calls.md) - a Kotlin generator patch the fork carries +- [Payload bytes](/quest/m2/ffi-bytes.md) - the first patch the fork carries +- [Kotlin calls](/quest/m2/kt-ffi-calls.md) - a Kotlin generator patch the fork carries - [Namespaced external types](/quest/m2/uniffi-namespaces.md) - fixes mozilla/uniffi-rs#3026 in the fork diff --git a/quest/m2/uniffi-namespaces.md b/quest/m2/uniffi-namespaces.md index e9eae1182c..fdf7eee9dc 100644 --- a/quest/m2/uniffi-namespaces.md +++ b/quest/m2/uniffi-namespaces.md @@ -5,7 +5,7 @@ Two crates linked into one library can each export a type with the same name, and a type in one can take the other's: `media.GroupConsumer(group: GroupConsumer)` generates and runs in Kotlin, Swift, Python, Go, Dart, C++, -and C from [moq-dev/uniffi](/quest/m1/uniffi-fork.md). +and C from [moq-dev/uniffi](/quest/m2/uniffi-fork.md). ## Plan @@ -30,4 +30,4 @@ Public API: none in moq until the crate split is re-decided. Wire: none. ## Required -- [Hard fork](/quest/m1/uniffi-fork.md) - the repository the fix lands in +- [Hard fork](/quest/m2/uniffi-fork.md) - the repository the fix lands in diff --git a/quest/m2/uring-demux.md b/quest/m2/uring-demux.md index e2eb02f9a6..63e20e147d 100644 --- a/quest/m2/uring-demux.md +++ b/quest/m2/uring-demux.md @@ -18,13 +18,13 @@ customer feature, and moq.pro already ranks one-port above its uring rollout. Decided 2026-10-08: the UDP demux shrinks to QUIC plus the WebRTC hook, and -the public STUN responder moves to the m1 P2P line, so the ring carries no +the public STUN responder belongs to the deferred m2 P2P line, so the ring carries no STUN responder class either. An ICE Binding request is WebRTC traffic and follows that class. Each worker's endpoint receives a batch and feeds every segment straight to the QUIC endpoint. The classifier and flow table from the -[UDP demux](/quest/m1/p2p/udp-demux.md), split there into a +[UDP demux](/quest/m2/p2p/udp-demux.md), split there into a runtime-agnostic piece, run first on each segment; only QUIC continues into the endpoint. The worker is thread-per-core and `!Send`, while the consumers of the other classes (`moq-rtc`, `moq-srt`) run on @@ -49,7 +49,7 @@ off on the ring, from shard steering. ## Required -- [UDP demux](/quest/m1/p2p/udp-demux.md) - the classifier and flow table this runs on the ring +- [UDP demux](/quest/m2/p2p/udp-demux.md) - the classifier and flow table this runs on the ring - [WebRTC on the shared socket](/quest/m2/one-port/rtc-feed.md) - the fed RTC socket and transmit contract the ring integrates - [SRT on the shared socket](/quest/m2/one-port/srt-demux.md) - the SRT state machines fed through the shared demux diff --git a/quest/m1/vaapi-vulkan-import.md b/quest/m2/vaapi-vulkan-import.md similarity index 96% rename from quest/m1/vaapi-vulkan-import.md rename to quest/m2/vaapi-vulkan-import.md index ade98f2138..38e41d74df 100644 --- a/quest/m1/vaapi-vulkan-import.md +++ b/quest/m2/vaapi-vulkan-import.md @@ -7,7 +7,7 @@ DMA-BUF with a DRM format modifier, from a producer that synchronizes explicitly through a timeline, encodes through VA-API on Intel (iHD) without a CPU round trip. An ignored hardware test proves it on an Arrow Lake iGPU, and `Kind::Auto` picks VA-API for a surface on an Intel device, through the same -seam as [Vulkan encode](/quest/m1/vulkan-encode.md): `encode::Config::input` +seam as [Vulkan encode](/quest/m2/vulkan-encode.md): `encode::Config::input` and a VA-API candidate with `vulkan: true` in `rs/moq-video/src/encode/backend/mod.rs`. @@ -69,7 +69,7 @@ Public API: none; the surface landed in #4975. Wire: none. ## Related -- [Vulkan Video encode on AMD](/quest/m1/vulkan-encode.md) - the AMD half of the same proof +- [Vulkan Video encode on AMD](/quest/m2/vulkan-encode.md) - the AMD half of the same proof - [VAAPI encode and decode](/quest/m2/video-vaapi.md) - H.265 encode on the same backend - [GPU capacity and health](/quest/m2/gpu-health.md) - reports per device keyed by the same render node `dev_t` - [Linux OBS GPU input](/quest/m3/obs-linux-gpu.md) - a DMA-BUF producer that needs the same explicit sync diff --git a/quest/m2/video-vaapi.md b/quest/m2/video-vaapi.md index 94cf6ce584..da73c01322 100644 --- a/quest/m2/video-vaapi.md +++ b/quest/m2/video-vaapi.md @@ -59,4 +59,4 @@ already falls back cleanly, since `Encoder::new` returns `Err` and ## Related -- [VA-API encodes an external Vulkan image](/quest/m1/vaapi-vulkan-import.md) - explicit-sync DMA-BUF input and render-node selection by device, H.264 only +- [VA-API encodes an external Vulkan image](/quest/m2/vaapi-vulkan-import.md) - explicit-sync DMA-BUF input and render-node selection by device, H.264 only diff --git a/quest/m1/vulkan-encode.md b/quest/m2/vulkan-encode.md similarity index 98% rename from quest/m1/vulkan-encode.md rename to quest/m2/vulkan-encode.md index 7ebf9d53dc..7c4b01f34d 100644 --- a/quest/m1/vulkan-encode.md +++ b/quest/m2/vulkan-encode.md @@ -75,5 +75,5 @@ Public API: a new encoder backend and feature. Wire: none. ## Related -- [VA-API encodes an external Vulkan image](/quest/m1/vaapi-vulkan-import.md) - the Intel half of the same proof +- [VA-API encodes an external Vulkan image](/quest/m2/vaapi-vulkan-import.md) - the Intel half of the same proof - [Intra-refresh GOPs](/quest/m2/intra-refresh/README.md) - import and playback of intra-refresh video, which this encoder does not produce diff --git a/quest/m2/watch-data-sync.md b/quest/m2/watch-data-sync.md index dddd2bfec6..5921c62196 100644 --- a/quest/m2/watch-data-sync.md +++ b/quest/m2/watch-data-sync.md @@ -29,4 +29,4 @@ releases it. ## Required - [A/V clock](/quest/m1/av-clock.md) - the playhead the reader releases against -- [Watch worker](/quest/m1/watch-worker.md) - moves `Sync` into a worker, where the reader registers +- [Watch worker](/quest/m2/watch-worker.md) - moves `Sync` into a worker, where the reader registers diff --git a/quest/m2/watch-decode-gate.md b/quest/m2/watch-decode-gate.md index a8b2b769d8..ce3d8012d5 100644 --- a/quest/m2/watch-decode-gate.md +++ b/quest/m2/watch-decode-gate.md @@ -25,7 +25,7 @@ Public API: none. Wire: none. ## Required -- [Watch worker](/quest/m1/watch-worker.md) - moves decode and `Sync` into a worker +- [Watch worker](/quest/m2/watch-worker.md) - moves decode and `Sync` into a worker ## Related diff --git a/quest/m1/watch-worker.md b/quest/m2/watch-worker.md similarity index 92% rename from quest/m1/watch-worker.md rename to quest/m2/watch-worker.md index 91f20c6b13..065c90bf93 100644 --- a/quest/m1/watch-worker.md +++ b/quest/m2/watch-worker.md @@ -11,7 +11,7 @@ instead of falling back to the main thread. ## Plan -[Plan: watch worker](/quest/m1/plan-watch-worker.md) picks between an +[Plan: watch worker](/quest/m2/plan-watch-worker.md) picks between an invisible page-wide worker with on-demand signal handles and application-spawned workers, and rewrites this quest with the result. Until then, the settled constraints in that quest apply here too. @@ -30,7 +30,7 @@ Gate on the plan's jank harness and N-player sweep, both nightly. ## Required -- [Plan: watch worker](/quest/m1/plan-watch-worker.md) - picks the worker model and rewrites this quest +- [Plan: watch worker](/quest/m2/plan-watch-worker.md) - picks the worker model and rewrites this quest - [A/V clock](/quest/m1/av-clock.md) - reshapes `Sync` and the worklet playhead, so the move to the worker happens once ## Related diff --git a/quest/m2/webgpu-hdr.md b/quest/m2/webgpu-hdr.md index 92e02e8130..c858e5ebfd 100644 --- a/quest/m2/webgpu-hdr.md +++ b/quest/m2/webgpu-hdr.md @@ -12,7 +12,7 @@ drawn as if it were SDR as today. Decided 2026-10-08 in the WebGPU renderer `/quest-plan` interview: SDR and wide gamut first in the `@moq/video` WebGPU renderer (`js/video`), HDR here, -both in m1. +HDR and its catalog extension now follow in m2. Facts (2026-10): diff --git a/quest/m2/wire-stats/srt.md b/quest/m2/wire-stats/srt.md index 4670099734..6a0debce67 100644 --- a/quest/m2/wire-stats/srt.md +++ b/quest/m2/wire-stats/srt.md @@ -21,3 +21,7 @@ the same definition as QUIC's `udp_tx`. - Target the shared-socket integration that drives `srt-protocol` directly, not `srt-tokio` socket handling. - Test: an SRT pull's reported tx covers at least the TS payload it sent. + +## Required + +- [Shared SRT socket](/quest/m2/one-port/srt-demux.md) - replaces today's srt-tokio ownership with the direct srt-protocol integration this source targets diff --git a/quest/m3/cut-through/README.md b/quest/m3/cut-through/README.md index 6d429fc457..819b138c7b 100644 --- a/quest/m3/cut-through/README.md +++ b/quest/m3/cut-through/README.md @@ -76,5 +76,5 @@ stats field, which its quest documents inline. ## Related - [Hierarchical stream scheduling](/quest/m1/quic/scheduler.md) - orders streams; offset writes order ranges within one -- [QoS](/quest/m1/qos/README.md) - the loss-delay counter follows its per-broadcast ingress row conventions +- [QoS](/quest/m2/qos/README.md) - the loss-delay counter follows its per-broadcast ingress row conventions - [Frame slots are cached for free](https://github.com/moq-dev/moq/pull/5239) - changes how the cache charges the in-flight frames a cut-through build would hold diff --git a/quest/m3/cut-through/loss-delay.md b/quest/m3/cut-through/loss-delay.md index daed9e3cea..2b2ca92d01 100644 --- a/quest/m3/cut-through/loss-delay.md +++ b/quest/m3/cut-through/loss-delay.md @@ -41,4 +41,4 @@ missing fields. ## Related -- [QoS](/quest/m1/qos/README.md) - the per-broadcast ingress conventions this follows +- [QoS](/quest/m2/qos/README.md) - the per-broadcast ingress conventions this follows diff --git a/quest/m3/emsg.md b/quest/m3/emsg.md index 867d5a7d17..76cce2c187 100644 --- a/quest/m3/emsg.md +++ b/quest/m3/emsg.md @@ -2,7 +2,7 @@ ## Goal -`emsg` boxes survive fMP4 import instead of being silently discarded, and export +`emsg` boxes survive fMP4 import instead of being discarded with a warning, and export reproduces them. In-band ID3, SCTE-35 splice information, and DASH event messages reach a MoQ consumer. This is the first of the four timed-metadata carriers, so it also settles the contract they share: how an event's @@ -34,9 +34,9 @@ The shared contract, recorded here and mirrored in the Hang draft: A new exported envelope shape needs maintainer agreement before the dependents implement against it; land the shared CI fixtures with it. -`rs/moq-mux/src/container/fmp4/` does not mention `emsg` anywhere, so every -event message in a fragmented MP4 is dropped without a log line. That is the -one metadata path a DASH or CMAF ingest is most likely to use. +`rs/moq-mux/src/container/fmp4/import.rs` counts discarded `emsg` boxes +and warns on the first drop. Carriage and export remain unimplemented; +logging the loss does not preserve the metadata. Carry the boxes byte-faithfully: scheme id URI, value, timescale, presentation time, duration, id, and the opaque message payload, for both version 0 and diff --git a/quest/m3/flv-script.md b/quest/m3/flv-script.md index e8c24c8759..0d5058e6a1 100644 --- a/quest/m3/flv-script.md +++ b/quest/m3/flv-script.md @@ -9,10 +9,9 @@ FLV script tags survive RTMP and FLV import instead of being discarded, so Deferred in the 2026-09-30 audit and moved to m3 in the 2026-10-05 audit: no named consumer for timed metadata. -`rs/moq-mux/src/container/flv/import.rs` matches `TAG_SCRIPT => {}` and moves -on, which drops every AMF data message an encoder sends. `onMetaData` is the -one every RTMP publisher emits, and applications routinely push their own cues -through the same channel. +`rs/moq-mux/src/container/flv/import.rs` counts discarded `TAG_SCRIPT` +payloads and warns on the first drop. `onMetaData` and application cues still +need carriage; logging their loss does not implement this quest. Carry raw tag payloads without decoding AMF into a fixed vocabulary. Publish when received on independently sequenced metadata groups, with event time on diff --git a/quest/m3/obs-linux-gpu.md b/quest/m3/obs-linux-gpu.md index 339982fae0..88d88b6f4f 100644 --- a/quest/m3/obs-linux-gpu.md +++ b/quest/m3/obs-linux-gpu.md @@ -23,5 +23,5 @@ the bottleneck. ## Related - [Video hardware validation](/quest/m3/video-hardware.md) - native input and encoder acceptance need hardware evidence -- [VA-API encodes an external Vulkan image](/quest/m1/vaapi-vulkan-import.md) - explicit sync for an external DMA-BUF, and the public `DmaBuf` constructor from #4975 +- [VA-API encodes an external Vulkan image](/quest/m2/vaapi-vulkan-import.md) - explicit sync for an external DMA-BUF, and the public `DmaBuf` constructor from #4975 - [VAAPI encode and decode](/quest/m2/video-vaapi.md) - H.265 and checked-in bindings. The DMA-BUF encoder import this quest needs is already on main (moq-vaapi 0.1.0). diff --git a/quest/m3/quic-gcc.md b/quest/m3/quic-gcc.md index eadd13ce85..d55d9ea173 100644 --- a/quest/m3/quic-gcc.md +++ b/quest/m3/quic-gcc.md @@ -3,9 +3,9 @@ ## Goal A measured, written verdict on the QUIC receive-timestamps extension -(draft-smith-quic-receive-ts) between native `moq-quic` peers: how much more -accurate its per-packet one-way delay is than the half-RTT estimate the -[deadline quest](/quest/m2/quic-deadline.md) starts with, and what its ACK +(draft-ietf-quic-receive-ts) between native `moq-quic` peers: whether its +receive-time differences improve the forward-delay estimate used by the +[deadline quest](/quest/m2/quic-deadline.md), and what its ACK overhead costs on relay egress. A written abandonment is a successful outcome. @@ -22,10 +22,12 @@ as its own quest only if this spike says they are cheap and accurate. frame variant with timestamp ranges, and a way for `congestion::Controller` (or a test hook) to see each acknowledged packet's receive instant. Both ends are ours. -- Compare the forward delay it measures against the half-RTT estimate on the - impaired path profile with asymmetric delay. Report how often the half-RTT - estimate would have kept a hopeless retransmission or reset a deliverable - one. +- Receive timestamps alone measure delay variation, not absolute one-way + delay: the receiver's clock origin is not communicated (the draft's + section 6.1). State clock offset and drift assumptions. Compare an explicit + estimator against known simulator/netem delay and the half-RTT baseline on + asymmetric paths; do not label raw receive timestamps absolute delay. + Report mistaken retransmit/reset decisions and estimator uncertainty. - Measure the ACK overhead the timestamps add on a fanout-shaped relay egress, with and without the ACK-frequency extension reducing ACK rate. diff --git a/quest/m3/unreal.md b/quest/m3/unreal.md index 688d33b2b3..ade838b2be 100644 --- a/quest/m3/unreal.md +++ b/quest/m3/unreal.md @@ -22,8 +22,9 @@ In m3 until an Unreal consumer asks; the C++ package it links already exists. is enough for the verdict, GPU import is not measured here. - Record: editor hot-reload behavior with a static Rust library loaded, the dispatcher shutdown on module unload, and package size. -- Verdict promotes a real `cpp/unreal` plugin quest into m1 or abandons this - with the reasons. +- A positive verdict proposes a plugin quest in the then-current feature + horizon for maintainer prioritization; it does not automatically gate a + release. Otherwise abandon this with the reasons. ## Required diff --git a/quest/m3/video-hardware-access.md b/quest/m3/video-hardware-access.md index 21bd8d8d77..942dd8e6d2 100644 --- a/quest/m3/video-hardware-access.md +++ b/quest/m3/video-hardware-access.md @@ -17,7 +17,7 @@ Check by asking the maintainer which machines are available. As of 2026-10-05 only part is on hand. The maintainer's desktop has an Intel Arrow Lake iGPU (iHD 26.1.2, `renderD128`) and an AMD RX 9070 (RADV, Mesa 26.0.8, `renderD129`), so it has a second render node; that check moved to -[VA-API external images](/quest/m1/vaapi-vulkan-import.md). The +[VA-API external images](/quest/m2/vaapi-vulkan-import.md). The [multi-vendor GPU quests](/quest/m2/gpu-release.md) test on it. Checked 2026-10-08 on that desktop: `vainfo` lists only `VAEntrypointEncSlice` diff --git a/quest/m3/video-hardware.md b/quest/m3/video-hardware.md index 81ce67bdc5..f9ccafc464 100644 --- a/quest/m3/video-hardware.md +++ b/quest/m3/video-hardware.md @@ -16,7 +16,7 @@ V4L2 `VIDIOC_EXPBUF` source is dropped with the export itself (see Decided 2026-10-08: the VAAPI low-power entrypoint check is dropped. Neither Meteor Lake nor the Arrow Lake test host exposes `EncSliceLP`, so no machine on hand reaches that fallback. A render node other than the first is checked -by [VA-API external images](/quest/m1/vaapi-vulkan-import.md). +by [VA-API external images](/quest/m2/vaapi-vulkan-import.md). - **Windows Media Foundation capture**: on-demand open and close, so the camera LED is off when nobody is watching, and NV12 delivery from MJPEG and