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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion quest/m0/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ remain and no release waits on them.

- [Self-hosted CI](/quest/m0/self-hosted-ci.md) - same-repo Check and Test run on a self-hosted NixOS runner with a main-written local cache, behind a `CI_RUNNER` kill switch
- [CI host](/quest/m0/ci-host.md) - the maintainer brings up the spare desktop as the `moq-ci` and `moq-gpu` runner host
- [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, before Seattle
- [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, before Seattle, then draft-20+ FETCH
- [TS duration fidelity](/quest/m0/ts-duration-fidelity.md) - Interop's TS compliance captures the whole round-tripped stream again, fixing a regression that turns Interop red on main
- [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
Expand Down
13 changes: 8 additions & 5 deletions quest/m0/release-22/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,12 @@ Decided 2026-10-08:
- Offer the reporter the `release` head for their rig once the children
land, but don't gate the cut on their result.

The backports have landed, including moq-noq 1.3.5 in #5170 (refreshed in the
2026-10-10 audit). This README's remaining work: bump `@moq/net` on
`release` (`/bump`), merge the release-plz PR it regenerates for the
backports (0.17.3; 0.17.2 already shipped, and #4944 predates the
backports), and send the reporter the branch.
The backports shipped in moq-relay 0.17.3 (#5119) and `@moq/net` 0.4.3
(refreshed 2026-10-12). Draft-22 interop on 2026-10-12 then found FETCH still
refused on draft-20+ sessions. This README's remaining work: once the child
lands, cut the next 0.17 patch and `@moq/net` with it (`/bump`, then the
release-plz PR), and tell the testers.

## Required

- [Draft-20+ FETCH](/quest/m0/release-22/fetch.md) - backport #4971 so a draft-22 FETCH is served, not refused
17 changes: 17 additions & 0 deletions quest/m0/release-22/fetch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# [S] Draft-20+ FETCH on the 0.17 release line

## Goal

A 0.17.x moq-relay serves single-group FETCH on draft-20 and later sessions,
instead of refusing with "FETCH not supported on draft-20" on draft-22.

## Plan

Reported from draft-22 interop on 2026-10-12 against moq-relay 0.17.3.
`main` already serves it ([#4971](https://github.com/moq-dev/moq/pull/4971),
"serve draft-20 FETCH within one group"); `release` still refuses on
`Filter::is_draft20(version)`.

Decided 2026-10-12: backport #4971 onto `release` as its own PR
(CONTRIBUTING), including its tests, then cut the next 0.17 patch with the
release-plz PR. Mirror the `@moq/net` side if #4971 touched `js/net`.
2 changes: 2 additions & 0 deletions quest/m1/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ folded into Dropped sources.
- [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
- [IETF group ingest refuses loudly](/quest/m1/ietf-ingest-refusal.md) - a subgroup other than 0 or a datagram past object 0 aborts the track, and a peer's malformed group is refused rather than dropped at debug
- [Draft 17-18 withdraw by reset](/quest/m1/ietf-withdraw-reset.md) - withdrawing a PUBLISH_NAMESPACE on drafts 17 and 18 resets the request instead of FIN-ing it
- [Relay session limits](/quest/m1/relay-session-limits.md) - moq-relay sets per-session request limits, tighter for clients than peers, and the bindings name a refused request
- [Churn with held subscriptions](/quest/m1/session-churn-held.md) - opening and closing a request costs the same with 1 or 1,024 held subscriptions
- [Catalog track identity](/quest/m1/catalog-tracks.md) - a track's codec and description never change for its name; resolution changes in band below ceilings fixed at creation, and anything else mints a new rendition or epoch
Expand Down
52 changes: 52 additions & 0 deletions quest/m1/ietf-ingest-refusal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# [S] IETF group ingest refuses loudly

## Goal

A moq-transport publisher whose groups moq-net cannot represent gets a loud
refusal instead of a silently truncated track. A subgroup ID other than 0
or a datagram past object 0 aborts the track with Unsupported, logged at
warn. A peer's malformed group is refused with STOP_SENDING instead of being
dropped at debug while the handler returns `Ok(())`.

## Plan

Subgroups and object ID gaps stay unsupported on purpose
([#4926](https://github.com/moq-dev/moq/issues/4926), declined). Interop on
2026-10-12 (a LOC publisher at draft-18, one GOP per group and one object per
frame, and a draft-22 test suite) found that the refusal is quiet. In
`rs/moq-net/src/ietf/subscriber.rs`:

- `recv_group` refuses a subgroup other than 0 with Unsupported, which
resets only that stream, so subgroup 0 is forwarded as if whole.
- `recv_datagram` drops a datagram past object 0 at debug and returns
`Ok(())`. Object 0 is forwarded with `end_of_group` set, so viewers get
truncated groups and no error.
- The catch-all `Err(err)` arm after `GroupIngest` logs at debug and returns
`Ok(())`. Gaps no longer reach it (#5233 returns Unsupported), but a
malformed object extension block (`Decode`), a FIN before the declared
payload ends (`WrongSize`), a second stream for a live group
(`Duplicate`), and an oversized frame or group (`FrameTooLarge`,
`GroupTooLarge`) still do.

Decided 2026-10-12:

- **Abort the track** on the first subgroup other than 0 or datagram past
object 0, with Unsupported at warn. The subscriber sees an error, not
partial media.
- **Narrow the catch-all.** Route the peer's faults (`Decode`, `WrongSize`,
`Duplicate`, `FrameTooLarge`, `GroupTooLarge`) to the refusing arm so the dispatcher sends STOP_SENDING.
Peer resets, transport errors and Cancel stay at debug.
- One PR for both, since they share the error routing.
- **Docs.** Record in the deliberate deviations of
`doc/concept/standard.md` that per-subgroup and per-datagram publisher
priority is rewritten to the track's (the model's `group::Info` has no
priority), beside the existing dropped-properties entry, and that groups
past subgroup 0 or object 0 abort the track.
- One regression test per case through the IETF dispatch loop, each failing
without its fix.

## Related

- [#5371](https://github.com/moq-dev/moq/issues/5371) - closed: its gap case was fixed by #5233, and it pointed at the catch-all this narrows

- [Malformed moq-transport input](/quest/m2/ietf-malformed-close.md) - session-level malformed control input
27 changes: 27 additions & 0 deletions quest/m1/ietf-withdraw-reset.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# [XS] Draft 17-18 withdraw by reset

## Goal

On drafts 17 and 18, moq-net withdraws a PUBLISH_NAMESPACE by resetting the
request stream and sending STOP_SENDING, as drafts 19 and later already do,
so quiche-moq no longer closes the session on withdrawal.

## Plan

Reported from interop on 2026-10-12. The withdraw path in
`rs/moq-net/src/ietf/publisher.rs` writes PUBLISH_NAMESPACE_DONE then FIN on
drafts 14-16, a plain FIN on 17-18, and aborts both directions on 19+.
Draft-17 §3.3.1 and draft-18 §3.3.2 both say a request is cancelled by
resetting or STOP_SENDING, and that a PUBLISH_NAMESPACE is withdrawn by
cancelling it. FIN is allowed only for SUBSCRIBE_NAMESPACE and
SUBSCRIBE_TRACKS.

Decided 2026-10-12:

- Abort both directions on every version after 16. Drafts 14-16 keep
PUBLISH_NAMESPACE_DONE.
- Keep `fin_cancels` accepting a peer FIN as cancellation on 14-18: lenient
on receive.
- Extend `namespace_requester_fin_is_version_gated` (or a sibling) to assert
a reset on 17 and 18.
- Mirror in `js/net/src/ietf` if it has the same gap.
3 changes: 2 additions & 1 deletion quest/m1/stats/rust.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
`rs/moq-audio/src/playback` is already counted privately.
- `moq-cli` folds the counters into `hang::stats::Snapshot` on the stats
interval and writes it through `moq_json::snapshot`, with the `.z` sibling.
A TS import flattens `ts::stats::Snapshot` in as `mpegts`. `transport` comes from
A TS import flattens `ts::stats::Snapshot` in as `mpegts`, and a TS
export flattens its egress counters into `.echo` the same way. `transport` comes from
the connection's `ConnectionStats`.
- Feedback: after reading a catalog with an `echo` section, the player
resolves the echo path against the broadcast, appends `<name>.echo`, and
Expand Down
17 changes: 12 additions & 5 deletions quest/m1/stats/schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ and the hang draft specs both. Nothing produces them yet.
way `Catalog<E>` takes `ts::Ext`. `Track` holds sent frames, sent bytes,
keyframes, skipped frames, and the target bitrate as a gauge.
- `rs/hang/src/echo.rs`:
`Snapshot { transport, renditions: BTreeMap<String, Track> }`. `Track`
`Snapshot<E = ()> { transport, renditions: BTreeMap<String, Track>, #[serde(flatten)] ext: E }`,
the same flatten as `stats::Snapshot`, so a TS export folds its
`mpegts` egress counters in (decided 2026-10-12, from
[#5360](https://github.com/moq-dev/moq/issues/5360)). `Track`
holds:
- received frames and bytes, decoded, late, and decode errors;
- stalls, stalled duration, and underruns;
Expand All @@ -51,10 +54,14 @@ and the hang draft specs both. Nothing produces them yet.
schemas, and the `.echo` convention: one broadcast per soliciting catalog,
under the catalog's echo path, announced after the viewer reads the
catalog. Validate with `just drafts check`.
- Open: the MSF catalog conversion in `rs/moq-mux` copies only the media
sections and `ext`, so `stats` and `echo` drop silently under
`--catalog-format msf`. Candidates: carry them through MSF with a
round-trip test, or refuse `--stats` and `--echo` with MSF.
- **MSF carries neither section yet** (decided 2026-10-12). The MSF
conversion in `rs/moq-mux` copies only the media sections and `ext`. A
viewer given `--echo` with `--catalog-format msf` refuses, since it would
never see an `echo` section to answer. An import has no catalog-format
flag and always emits the MSF catalog beside the hang one, so that catalog
omits `stats` and `echo` the way it omits `json` and `binary`; hang
readers still get them. Carrying them through MSF `publishTracks` is
[MSF feedback tracks](/quest/m3/msf-publish-tracks.md).
- Docs: `doc/concept/hang.md`, and a media section in `doc/concept/stats.md`.
- Tests:
- fixtures round-trip in both languages;
Expand Down
2 changes: 2 additions & 0 deletions quest/m1/tstd/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ nightly.

- [Send-ahead within the delay](/quest/m1/tstd/send-ahead.md) - total lag is `--delay`, send-ahead included, with a 1 s default
- [PSI runs](/quest/m1/tstd/psi-runs.md) - the PAT and PMT are deduplicated and paced so the system buffers never overflow
- [Video at its full leak](/quest/m1/tstd/video-leak.md) - a leak-bound video PID is admitted at its full Rbx, not about 3 % below it
- [Mux overhead](/quest/m1/tstd/mux-overhead.md) - the PCR rides in the video's adaptation field and audio PES carry several frames, so a full CBR source re-muxes at its own rate

## Related

Expand Down
41 changes: 41 additions & 0 deletions quest/m1/tstd/mux-overhead.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# [M] TS export fits a full source at its own rate

## Goal

`moq export ts` spends no more packets than the source on clocks and audio
framing, so a CBR source that fills its multiplex with no null packets
re-muxes at its own `--mux-rate` instead of falling behind.

## Plan

Reported in [#5362](https://github.com/moq-dev/moq/issues/5362) point 3 and
verified against `rs/moq-mux/src/container/ts/export.rs`: with points 1 and 2
fixed, the export of a 25 Mb/s broadcast feed averages 25.017 Mb/s of
non-null packets. Against the source it spends about 40 packets/s on
separate PCR packets (the source carries its PCR in video packets) and about
60 on one audio frame per PES (the source packs about twelve MP2 and nine
AC-3 frames per PES).

Decided 2026-10-12:

- **PCR in the PCR PID's adaptation field.** When the clock is due in a slot
that sends a PCR-PID packet, carry the PCR in that packet's adaptation
field, sampled at its byte position as today. Fall back to the standalone
`pcr_packet` only when the slot has none. The 25 ms grid stays; the
comment at `lay` explains why the PCR is not sampled from the DTS.
- **Several audio frames per PES.** Pack consecutive frames of an audio PID
into one PES while the PES fits the PID's B, timed by the first frame's
PTS. This applies to every audio stream the export frames (AAC, MP2, AC-3,
E-AC-3), not only DVB AC-3.
- Land after [#5363](https://github.com/moq-dev/moq/pull/5363), which reworks
the DVB AC-3 framing this packs.
- Test: a generated CBR source with no nulls, at the source's own
`--mux-rate`, exports for the whole clip and passes the strict T-STD check.

## Closes

- [#5362](https://github.com/moq-dev/moq/issues/5362) - a full-rate CBR contribution feed exports at its own rate

## Related

- [TS export admits video at its full leak](/quest/m1/tstd/video-leak.md) - #5362 point 2
31 changes: 31 additions & 0 deletions quest/m1/tstd/video-leak.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# [S] TS export admits video at its full leak

## Goal

`moq export ts` admits a leak-bound video PID at its full Rbx rate, so a
source that fills its level's leak (no HRD, 1080i25 High@4.0 at Rbx 24 Mb/s)
exports without falling behind and missing a decode deadline.

## Plan

Reported in [#5362](https://github.com/moq-dev/moq/issues/5362) point 2 and
verified against `rs/moq-mux/src/container/ts/`: `Buffer::per_slot`
(`schedule.rs`) floors the rate to whole 188-byte packets per 25 ms slot and
subtracts one packet of slack, and `admit` charges each slot's clock packet to
the PCR PID, which is the video. Together that admits about 2.9 % below what
Rbx carries in payload bytes. The slack's only rationale is a comment about
bunching at slot edges; no commit or quest calls it a deliberate margin.

Decided 2026-10-12:

- **Exact accounting.** Cap a leak-bound PID at the lesser of Rx in packets
and Rbx in payload bytes, since Rbx governs payload into EB while TB drains
at Rx. Carry fractional packets from slot to slot and drop the one-packet
slack. A standalone PCR packet still enters the video's transport buffer,
so it counts against the Rx packet budget but not the Rbx payload budget.
- Test: a generated source whose video fills Rbx on average exports for the
whole clip and passes the strict T-STD check that `just test ts --hrd` runs.

## Related

- [Lower TS mux overhead](/quest/m1/tstd/mux-overhead.md) - #5362 point 3, which closes the issue
20 changes: 12 additions & 8 deletions quest/m2/ts-health-stats.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Goal

`moq import ts --stats` and `moq export ts --stats` publish the `ts::stats`
`moq import ts --stats` and `moq export ts --echo` publish the `ts::stats`
counters so a dashboard reads the TR 101 290 counters and per-PID liveness
beside the media and delivery counters, with no new transport. No health
roll-up in-tree: green, amber or red per priority is the consumer's reading of
Expand All @@ -17,12 +17,15 @@ Decided while planning [#1838](https://github.com/moq-dev/moq/issues/1838):
import flattens `ts::stats::Snapshot` into the publisher's stats snapshot under the
catalog's `mpegts` key, the counters stay owned by moq-mux beside
`ts::Ext`, and `hang` stays TS-free.
- Open: an export is a viewer, which has no stats track and reports only
through a soliciting catalog's `.echo` feedback, so where `moq export ts
--stats` publishes its egress rows is unsettled. Candidates: extend the
solicited `.echo` snapshot with a flattened container section, give viewers
their own stats surface, or narrow this quest to import. Leaning toward
narrowing, since the other two widen the settled stats design.
- **The export reports through `.echo`** (decided 2026-10-12, from
[#5360](https://github.com/moq-dev/moq/issues/5360)). An export is a viewer,
so `moq export ts --echo <name>` folds `ts::stats::Snapshot` into its
solicited `.echo` snapshot under `mpegts`, through the `E` flatten the
[media stats schema](/quest/m1/stats/schema.md) gives `echo::Snapshot`.
The egress is where an operator otherwise cannot see per-PID outages: a
57 s video hole passed every TR 101 290 P1 check in the reporter's lab,
and only per-PID access-unit counts caught it. There is no export
`--stats` flag.
- **Counters sum, gauges do not.** Every check is a cumulative counter, which
keeps `.z` deltas small and lets an aggregate sum; the PID's `quiet` is a gauge and
merges newest-wins.
Expand All @@ -32,7 +35,8 @@ Decided while planning [#1838](https://github.com/moq-dev/moq/issues/1838):
monitoring point (ingest grades the feed, egress grades our muxer, and
neither is the groomed wire), the value domain of the PCR checks, and that
the per-PID rows cover `PID_error`; `doc/bin/cli.md` the flags.
- Test: publish a TS clip with `--stats`, read it back, and assert every ETSI
- Test: publish a TS clip with `--stats` and export it with `--echo` to a
soliciting catalog, read both back, and assert every ETSI
counter reads zero on a clean clip while every elementary stream's
access-unit count advances.

Expand Down
1 change: 1 addition & 0 deletions quest/m3/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ m4's upstream waits in here and moved m2 work with no consumer.
- [fMP4 emsg](/quest/m3/emsg.md) - settles the shared framing and missing-data semantics before this section adopts them
- [ID3 catalog section](/quest/m3/id3.md) - timed ID3 as a first-class container-neutral catalog section
- [FLV script tags](/quest/m3/flv-script.md) - onMetaData and AMF data messages survive RTMP and FLV import
- [MSF feedback tracks](/quest/m3/msf-publish-tracks.md) - `--stats` and `--echo` work under `--catalog-format msf` through `publishTracks`, once a consumer asks
- [MSFTS convergence](/quest/m3/msfts-convergence.md) - the demultiplexed TS lane converges on MSFTS where they differ, once an MSFTS implementer engages
- [#2279](/quest/m3/2279-hang-typed-scte-35-ad-cue-signaling-carried-opaquely.md) - hang: SCTE-35 cues arrive immediately on an independent metadata track, optionally associated with a rendition
- [CEA-608/708](/quest/m3/captions-cea.md) - captions carried inside video SEI become a real text rendition at import
Expand Down
24 changes: 24 additions & 0 deletions quest/m3/msf-publish-tracks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# [M] MSF feedback tracks

## Goal

`--stats` and `--echo` work under `--catalog-format msf`: a soliciting
catalog's `echo` maps onto an MSF `publishTracks` entry (draft-ietf-moq-msf
§5.1.5), and a viewer publishes the hang `.echo` snapshot there. Start once a
consumer asks.

## Plan

Raised in [#5360](https://github.com/moq-dev/moq/issues/5360). Until then the
[media stats schema](/quest/m1/stats/schema.md) leaves both sections out of
the MSF catalog and refuses a viewer's `--echo` under `--catalog-format msf`
(decided 2026-10-12).

Open: how the entry names the hang payload, since MSF fixes its packaging
values in the draft; and whether to add a CMCD v2 Event Mode encoding
(draft-wilaw-moq-cmcd-event-timeline) of the overlapping fields (stalls,
dropped frames, latency, throughput, errors) for consumers that ingest CMCD.

## Required

- [Media stats schema](/quest/m1/stats/schema.md) - the `stats` and `echo` sections this maps
Loading