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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions quest/m0/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ Each lands its fixes in its own PR and plans only what needs a decision.
- [qmux 0.5.3](/quest/m0/qmux-0.5.3.md) - crates.io publishes qmux 0.5.3 with the builder session config the release backport of #5609 needs
- [Release shutdown backport](/quest/m0/ffi-shutdown-backport.md) - `release` publishes a non-breaking moq-ffi with an async `drain()` and qmux sessions on the QUIC stream limit
- [Release fixed delay](/quest/m0/release-fixed-delay.md) - a fixed latency on `release`, which moq.dev runs, ignores the catalog's jitter and delay as `main` already does
- [HLS admission backport](/quest/m0/hls-admission-backport.md) - `release` refuses the hostile HLS playlists `main` does, without breaking `import::Config::client`
- [Adopt quest resolve](/quest/m0/quest-resolve.md) - local merges resolve concurrent quest link-list edits once kixelated/quest ships its merge driver
- [CI cost and latency](/quest/m0/ci/README.md) - cloud CI runs only in the merge queue as one `just ci` pass, expensive checks run nightly and gate `release`, caches move to R2, and runners are chosen last
- [MIGRATE.md at the cut](/quest/m0/migrate-at-cut.md) - PRs stop conflicting on `MIGRATE.md`: breaking PRs carry their migration note in the PR, and the breaking cut writes the guides
1 change: 1 addition & 0 deletions quest/m0/ci/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,5 @@ The historical samples locate candidates; they are not controlled benchmarks.
- [Bump to the manifest fix](/quest/m0/ci/mbx-bump.md) - condition: an mbx release contains jdx/mr-boxington#798; then bump the Rust cache's pin
- [Verify the R2 Rust cache](/quest/m0/ci/mbx-r2-verify.md) - measure the hosted Nightly jobs' hits, restore time, and expiry once the bump lands
- [Verify the Nix cache](/quest/m0/ci/nix-cache-verify.md) - confirm CI and releases use `nix.moq.dev` and retire Cachix
- [The gate builds a Nix package](/quest/m0/ci/nix-package-gate.md) - a change that breaks `nix build .#moq-relay` or the Dockerfile fails before `main`
- [First shared FFI release](/quest/m0/ci/nightly-ffi-reuse.md) - condition: the first `moq-ffi-v*` tag that runs `Release FFI`, and a `Shipping sizes` run; verify the wrappers and size keys
25 changes: 25 additions & 0 deletions quest/m0/ci/nix-package-gate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# [S] The gate builds a Nix package

## Goal

A change that breaks `nix build .#<package>` or the root `Dockerfile` fails
before it reaches `main`.

## Plan

[moq-dev/moq#6159](https://github.com/moq-dev/moq/pull/6159) broke every
crane package build (kio's real manifest inherited `workspace.lints`, which
crane's dummy root drops) and nothing noticed:
the merge queue's `nix flake check` builds no packages, Docker builds only on
tags, and Nightly had not run since. [moq-dev/moq#6586](https://github.com/moq-dev/moq/pull/6586)
fixed the build.

Build one crane package, `moq-relay`, where a change to `nix/`, `Cargo.toml`,
`Cargo.lock`, or a crate's manifest can break it. Measure its cost on the
cache first: if it fits the merge gate, run it there only for those paths;
otherwise run it in Nightly next to the Docker dry-run, so a break is caught
within a day.

Decided 2026-10-23 (maintainer): track this as m0 CI work.

Public API: none. Wire: none.
26 changes: 26 additions & 0 deletions quest/m0/hls-admission-backport.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# [S] Backport HLS import admission to release

## Goal

`release`, which moq.dev runs, refuses the same hostile HLS playlists `main`
does: the origin-admission and redirect-hop checks from
[HLS admission](/quest/m1/release-audit/hls-admission.md) ship in a
non-breaking `moq-hls` patch release.

## Plan

`main` drops `import::Config::client` while adding the checks. `release`
still has `pub client: Option<Client>` (`rs/moq-hls/src/import.rs`), so the
backport keeps that field and documents that a caller-supplied client owns
its own redirect handling; the checks apply to the client moq-hls builds.

Decided 2026-10-23 (maintainer): backport once the checks have been validated
on `main`, following the shape of
[the shutdown backport](/quest/m0/ffi-shutdown-backport.md): cut from
`release`, no breaking change, a patch release.

Public API: none on `release`. Wire: none.

## Required

- [HLS admission](/quest/m1/release-audit/hls-admission.md) - the checks land and are validated on `main` first
14 changes: 14 additions & 0 deletions quest/m1/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,22 @@ tracks, so per-group stats stop costing cluster announces.
- [Epoch correctness](/quest/m0/broadcast-epoch/README.md) - publisher identity, duplicate suppression, and catalog pinning gate the breaking cut
- [moq-congestion](/quest/m1/congestion/README.md) - one QUIC congestion-control crate with a transport-independent trait, run by `moq-quic` and published for noq
- [Release audit findings](/quest/m1/release-audit/README.md) - resolve newly identified resource, parser, playback, and interoperability risks before the cut
- [SUBSCRIBE_TRACKS follows credit](/quest/m1/listing-credit.md) - a listing holds state in proportion to what the peer reads, not to the catalog
- [Cancelled moq-transport requests stop](/quest/m1/ietf-request-cancel.md) - a cancelled request releases its state even when the peer withholds credit
- [SUBSCRIBE setup sees a cancel](/quest/m1/ietf-setup-cancel.md) - a subscriber that cancels mid-setup releases upstream demand and its slot at once
- [JS qmux stream credit](/quest/m1/js-qmux-credit.md) - credit returns when both halves close, so `@moq/net` drops its caps
- [Every close records its cause](/quest/m1/close-cause.md) - protocol-violation closes and abandoned SETUP replies record why
- [Empty segments refused](/quest/m1/empty-segments.md) - `room//cam` fails loud instead of naming `room/cam`
- [Readers follow restarts](/quest/m1/sink-restart-follow.md) - every moq-cli and moq-gst reader, moqsrc and `moq play` included, follows instances through `announced_broadcast` and an epochless `Restart` where its output can mark it; otherwise it stays on the old instance and fails once that route dies
- [Readers follow a moved rendition](/quest/m1/rendition-move-follow.md) - moq-rtc, moq-ffi, moq-gst and `moq play` follow a rendition the catalog moves to a new track
- [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
- [Cluster routing](/quest/m1/cluster-routing/README.md) - prove shared routing contracts, implement per-origin routes, and connect outbound local clusters
- [Stats groups as tracks](/quest/m1/stats-group-tracks.md) - one stable node broadcast per stats producer, with each group a produced track listed by TRACKS
- [No reused track names](/quest/m1/name-reuse.md) - no first-party publisher re-creates a finished track under its old name
- [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
- [io_uring MTU matches tokio](/quest/m1/uring-mtu.md) - a 1200-byte floor and `quic.mtu_discovery` honored, so the ring stops black-holing smaller paths
- [io_uring listen extensions](/quest/m1/uring-listen-extensions.md) - ring listeners offer the configured `[listen] extensions`
- [Rust request credit](/quest/m1/rs-request-credit.md) - a moq-net request fails at once without stream credit instead of waiting
- [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
- [Exact timestamps in every binding](/quest/m1/binding-timestamps.md) - bindings carry `{value, scale}` timestamps instead of lossy microseconds
Expand All @@ -88,6 +97,7 @@ tracks, so per-group stats stop costing cluster announces.
- [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 under the relay conformance profile, except cases that use a subgroup other than 0
- [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
- [Subscribe ranges](/quest/m1/subscribe-ranges/README.md) - a lite-07 SUBSCRIBE asks for past and live ranges with absolute or relative ends, in either order, and replaces FETCH; merged ranges never starve a subscriber; relays fill misses by range, including over moq-transport
- [Updates reach fills](/quest/m1/fill-updates.md) - a reader's new priority, delay, or order reaches fills already queued or taken
- [A widened max age re-offers cached groups](/quest/m1/resolved-demand.md) - a warm relay serves a reader with a wider max age the cached groups a cold relay would, with no wire change
- [Drop set](/quest/m1/drop-set.md) - each cursor judges missing groups against its own budget, so a narrow reader drops promptly and a widening re-offers groups already reported dropped on lite-07
- [Broadcast archives](/quest/m1/archive/README.md) - ANNOUNCE, TRACKS, and a SUBSCRIBE per track record every track under a prefix to any object_store, a host serves a stored broadcast back, and the catalog drops its archive pointer for an opt-in `hls` field
Expand All @@ -97,6 +107,7 @@ tracks, so per-group stats stop costing cluster announces.
- [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
- [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
- [More tests under load](/quest/m1/test-flakes-2/README.md) - the second round of load-only failures, one quest per flake, fixed at the cause
- [JS interop tone](/quest/m1/interop-js-tone.md) - the `js ->` cells pass the audio check on every draft again, after the 2026-10-23 regression
- [A smaller, safer API than release](/quest/m1/api-audit/README.md) - every surface added since release has a consumer or is gone, overlapping operations are collapsed, names mirror across Rust, JS, and the bindings, and every break has its MIGRATE entry
- [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
Expand All @@ -115,15 +126,18 @@ tracks, so per-group stats stop costing cluster announces.
- [Buffered playback subscribes in ascending order](/quest/m1/buffered-ascending.md) - a buffered `<moq-watch>` asks for the oldest group first, so a voice reply's opening arrives first, and audio's head hold shrinks to the floor
- [Buffered video poster](/quest/m1/buffered-video-poster.md) - buffered startup paints the first video frame at once and starts A/V together at a shared floor
- [qmux local lifecycle](/quest/m1/qmux-local-lifecycle.md) - an abort blocked on send credit resets at once, and a close behind a backed-up socket still sends APPLICATION_CLOSE
- [JS reload config](/quest/m1/js-reload-config.md) - `@moq/net` reload takes Rust's `healthy` backoff and no ignored `extensions`
- [JS release preflight](/quest/m1/js-publish-preflight.md) - `release.ts` refuses a package whose `@moq/*` dependency is not on npm, so no dependent publishes uninstallable
- [Stream sockets disable Nagle](/quest/m1/tcp-nodelay.md) - every qmux TCP, TLS, and WebSocket socket, accepted or dialed, sets `TCP_NODELAY`
- [TS fidelity and recovery](/quest/m1/ts-reliability/README.md) - both modes have explicit profiles, automated validation, loss tests, and a dedicated guide
- [Encoders declare no reordering](/quest/m1/encoder-reorder.md) - moq-video encoders publish `reorder: 0`
- [Rust resume checks compression](/quest/m1/resume-compress.md) - a same-epoch route copy with a different `compress` is refused instead of corrupting framing
- [WebGPU on Safari](/quest/m1/webgpu-safari.md) - the WebGPU renderer is verified on Safari 26 for macOS and iOS
- [Track live signal](/quest/m1/track-live.md) - a reader can wait for its track's feed to be live, so a player skips old cached groups while a fresh upstream subscription starts
- [Producers drop the phantom catalog type](/quest/m1/catalog-phantom.md) - moq-video, moq-audio and moq-mux producers lose an `E: CatalogExt` parameter that only `PhantomData` holds
- [JSON encoder options without a downcast](/quest/m1/json-encoder-options.md) - moq-mux's `json_snapshot` takes its `deltas` switch through a typed path any custom catalog entry can use, not an `Any` downcast
- [CI housekeeping](/quest/m1/ci-housekeeping.md) - validate queue nextest at 24 threads and remove the actionlint queue ignore after its release
- [Python docs stubs](/quest/m1/py-docs-stubs.md) - `just py docs` rebuilds its autosummary stubs, so a renamed symbol cannot break it
- [Interop AAC](/quest/m1/interop-aac.md) - the Rust interop publisher adds an AAC tone and every subscriber checks it beside Opus
- [Interop pre-skip](/quest/m1/interop-preskip.md) - the interop tone check anchors phase to timestamps, so a misapplied pre-skip fails
- [moq-net internals](/quest/m1/net-internals.md) - no per-group session clones, no transport type parameter in lite and IETF internals, and the `Local` check measured
Expand Down
2 changes: 2 additions & 0 deletions quest/m1/api-audit/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,9 @@ Rejected in planning:
- [Derived origins borrow](/quest/m1/api-audit/derived-origin-close.md) - closing a JS `scope()` or `withPreference()` handle releases only that handle
- [Wrapper config names](/quest/m1/api-audit/wrapper-config.md) - Python, Go, and Dart client options use the FFI's names and nesting
- [One decode read](/quest/m1/api-audit/decode-read.md) - audio and video decoders yield the same `Frame`, `Gap`, `End` read as moq-mux containers
- [Bindings read gaps and ends](/quest/m1/api-audit/decode-read-bindings.md) - moq-ffi and every wrapper surface `Gap` and `End` instead of discarding them
- [Media surface trim](/quest/m1/api-audit/media-trim.md) - media crate items with no consumer go private, and Rust/JS media names agree
- [One frame rate name](/quest/m1/api-audit/frame-rate.md) - core `moq_video::Rate` becomes `FrameRate`, as the bindings name it
- [CLI and gst flags](/quest/m1/api-audit/cli-flags.md) - one flag name per meaning across CLI subcommands and gst elements, and `linger` means one thing
- [qmux shape](/quest/m1/api-audit/qmux-shape.md) - qmux binds framing at connect, shares flow-control names with moq-tokio, and JS drops the legacy wire
- [Transport config](/quest/m1/api-audit/transport-config.md) - moq-tokio and moq-quic configs have one meaning for unset, enums for modes, no order-dependent setters, and no leftover features
Expand Down
31 changes: 31 additions & 0 deletions quest/m1/api-audit/decode-read-bindings.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# [L] Bindings read decoder gaps and ends

## Goal

Every binding reads the same audio and video decode stream as Rust:
frames, a gap where groups were skipped, and the end. moq-ffi, the generated
C and C++, Python, Swift, Kotlin, Dart, and Go stop discarding `Gap` and
`End`.

## Plan

[One decode read](/quest/m1/api-audit/decode-read.md) gives moq-audio and
moq-video `decode::Read { Frame, Gap, End }`, but moq-ffi (`audio.rs`,
`video.rs`), the hand-written moq-c, and the interop audio client still
hand over frames only.

Open for this quest: the binding shape. Options are a tagged record per read
(mirrors Rust, one call), or frames plus a separate gap and end signal
(smaller change for wrappers). Recommend the tagged record, mirroring Rust
names, and ask the maintainer before implementing.

Breaking in moq-ffi and every wrapper: `MIGRATE.md` entries per package, and
the Cross-Package Sync table's docs (`doc/lib/*`). Run `just test interop --all`.

Decided 2026-10-23 (maintainer): tracked under the API audit.

Public API: breaking, every binding. Wire: none.

## Required

- [One decode read](/quest/m1/api-audit/decode-read.md) - the Rust `decode::Read` this exposes
23 changes: 23 additions & 0 deletions quest/m1/api-audit/frame-rate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# [S] One frame rate name

## Goal

Rust's `moq_video::Rate` is `moq_video::FrameRate`, the name the bindings
already use (`moq_video_frame_rate` in C, `VideoFrameRate` in C++), so the
type has one name across Rust, JS, and the bindings.

## Plan

[Binding encoder config](/quest/m1/binding-encoder-config.md) names the
binding type `FrameRate` and leaves the core `Rate`
(`rs/moq-video/src/rate.rs`) as is. Rename the core type and its uses in
moq-video, moq-transcode, moq-cli, moq-boy, moq-c, and moq-ffi (about 137
references), with `MIGRATE.md` entries for each published crate.

Decided 2026-10-23 (maintainer): rename the core type, not the bindings.

Public API: breaking rename in moq-video. Wire: none.

## Required

- [Binding encoder config](/quest/m1/binding-encoder-config.md) - introduces the binding `FrameRate`
1 change: 1 addition & 0 deletions quest/m1/auth/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ because `main` assigned 0x3A to NOT_FETCHABLE first.
AUTH streams: a session starts on the token its SETUP or URL carried and its
AUTH streams add the rest
- [Binding auth polish](/quest/m1/auth/bindings-polish.md) - OBS reconnects with the presented token, one FFI auth handle, Swift grant sequences
- [Re-present a lost token](/quest/m1/auth/token-represent.md) - a token whose AUTH stream ended without a verdict is presented again on the live session

## Related

Expand Down
21 changes: 21 additions & 0 deletions quest/m1/auth/token-represent.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# [M] A lost AUTH stream re-presents its token

## Goal

When a session's AUTH stream ends without a verdict, the shared auth handle
presents its token again on the same session, with backoff, on lite and
moq-transport. The session never runs on without a token it was given.

## Plan

After [Auth outlives its session](/quest/m1/auth/issuer.md), a token whose
AUTH stream is lost waits for the next session. The moq-transport presenter
has no retry loop at all. Retry on the live session with the connection's
backoff, stop on a verdict or session end, and test both wires with mocked
time.

Public API: none expected. Wire: none.

## Required

- [Auth outlives its session](/quest/m1/auth/issuer.md) - the shared handle that presents the token
4 changes: 3 additions & 1 deletion quest/m1/c/consumers.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ build. Nothing in the repository calls the hand-written ABI any more.
## Plan

- Port the interop client first; it is the smallest real consumer and proves
the callback and dispatcher shape end to end.
the callback and dispatcher shape end to end. It skips a disabled audio
rendition through the generated record's `enabled`, as js-native does; the
hand-written ABI has only `moq_consume_video_enabled` (decided 2026-10-23).
- Rewrite `doc/lib/c` around the generated API, including a sample that runs
callbacks on the host's own loop through `moq_set_dispatcher`.

Expand Down
27 changes: 27 additions & 0 deletions quest/m1/close-cause.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# [S] Every session close records its cause

## Goal

A session closed for a protocol violation, or abandoned at shutdown before
its SETUP reply, records why, over every transport, instead of a clean end
or nothing.

## Plan

[One clean close](/quest/m1/api-audit/clean-close.md) adds the session's close
cause, but some closes bypass it and close the transport directly:
`lite::error::control` (`rs/moq-net/src/lite/error.rs`),
`request_update::too_many` (`rs/moq-net/src/ietf/request_update.rs`), and the
IETF dispatcher's protocol-violation close. Over QUIC those still record a
clean end. Route them through the close cause.

The relay's shutdown drain drops `request.ok()` at its deadline
(`until_deadline` in `rs/moq-relay/src/connection.rs`, the `select` in
`rs/moq-relay/src/uring.rs`), so an abandoned SETUP reply records nothing.
Record it as the handshake deadline already does.

Public API: none. Wire: none.

## Required

- [One clean close](/quest/m1/api-audit/clean-close.md) - introduces the close cause
Loading
Loading