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
3 changes: 3 additions & 0 deletions quest/m1/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,15 @@ transport, benchmark tooling); worktrees isolate commits, not semantics.
- [Close codes](/quest/m1/close-codes.md) - a client sees the peer's application close code over WebSocket and raw QUIC, like WebTransport
- [Raw stream codes](/quest/m1/raw-stream-codes.md) - raw QUIC stream resets and stops carry the application's code, not an HTTP/3-mapped one
- [Live in apps](/quest/m1/announce-live-apps.md) - the demo and `@moq/room` show "no broadcasts" from the `live` marker, which waits for the first session on page load
- [Watch refusal](/quest/m1/watch-refusal.md) - `<moq-watch>` shows an origin refusal as an error instead of sitting offline
- [kio waiter overflow](/quest/m1/kio-waiter-lost.md) - a retained `Waiter` past 8 lists stops adding a duplicate entry to lists it already recorded
- [Capture re-anchor](/quest/m1/capture-reanchor.md) - a repeating or restarting device clock never rewinds native capture during a fast backlog drain
- [Splice edge cases](/quest/m1/splice-edges.md) - an unstamped successor, a pruned segment's boundary group, and a warm head during a takeover are each handled correctly
- [Track tail hardening](/quest/m1/track-tail-hardening.md) - Rust and JS wait out a track's tail by the same rules, with the known hang, count, truncation, grace, and memory holes closed
- [Session death parity](/quest/m1/session-death.md) - a local close ends tracks cleanly in both languages, and JS group readers see the session's error on session death
- [moqsrc stop](/quest/m1/moqsrc-stop.md) - moqsrc's stop blocks until its session ends, without deadlocking on a blocked pad push
- [More tests under load](/quest/m1/test-flakes-2.md) - the second round of load-only failures, fixed at the cause
- [Auth outage clock](/quest/m1/auth-outage-clock.md) - the relay and moq-auth outage tests run on a paused clock again and assert both bounds of `expires`
- [Interop contention](/quest/m1/interop-contention.md) - two `just test interop --all` matrices pass side by side, and `just test harness` runs from a clean checkout
- [UnknownSession log flood](/quest/m1/unknown-session-logs.md) - streams reset before their WebTransport header stop being reported as UnknownSession at WARN
- [Merge queue](/quest/m1/merge-queue.md) - the required checks run on `merge_group`, so a stale green check can no longer break main
Expand All @@ -54,6 +56,7 @@ transport, benchmark tooling); worktrees isolate commits, not semantics.
- [Capture control](/quest/m1/capture-control.md) - on dev, `encode::Capture` replaces `CaptureOptions` without a `clock` field (it reads the catalog's), an unsupported `cut()` errors, and dropping the last `Control` cancels in-flight opens
- [Video surface](/quest/m1/video-surface.md) - on dev, moq-ffi's `native` becomes `surface`, refused on platforms with no surface
- [HLS discontinuity sequence](/quest/m1/hls-discontinuity-sequence.md) - on dev, `Segment::discontinuity` is the absolute sequence, so every cursor agrees
- [Auth client CA](/quest/m1/relay-auth-client-ca.md) - on dev, `auth::Config::validate` and `init` take the client-CA flag, so no caller can skip the check
- [RTMP TLS only](/quest/m1/rtmp-tls-only.md) - an RTMP listener configured for TLS can refuse plaintext instead of sniffing and serving it
- [HLS linger](/quest/m1/hls-linger.md) - `moq_hls::Server` serves an ended broadcast for its playlist window plus grace, so the moq.pro edge drops its own pool
- [Remove live()](/quest/m1/remove-live.md) - on dev, importers publish stream timestamps verbatim, the catalog clock maps them to wall time, and an encoder restart becomes a new epoch
Expand Down
63 changes: 63 additions & 0 deletions quest/m1/auth-outage-clock.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# [M] Auth outage tests on a paused clock

## Goal

The moq-relay and moq-auth outage tests run on tokio's paused clock again and
assert both bounds: a session (or grant) survives an auth outage until its
`expires`, and closes at `expires`, not later. No wall-clock sleeps, no
widened timeouts, and no dependence on how fast the OS delivers loopback.

## Plan

- The tests: `an_outage_keeps_the_session_until_expires` in
`rs/moq-relay/tests/auth_lifetime.rs`
([#4244](https://github.com/moq-dev/moq/pull/4244)) and
`an_outage_keeps_the_grant_until_expires` in `rs/moq-auth/src/client.rs`
([#4291](https://github.com/moq-dev/moq/pull/4291)). Both moved to the real
clock because a paused clock auto-advances while the runtime waits on a
real socket, so a virtual timer fired before macOS delivered loopback. That
swapped one violation of "unit tests mock time" for another, and #4244
dropped the upper bound. Read both PR descriptions: they list what was
tried and why it failed (restoring a listener probe, pausing after setup,
waiting on the log).
- The race is real sockets under virtual time, so fix it by taking the
sockets out of these tests. Look at what the codebase already offers
before building anything: `rs/moq-net/tests/support/mock.rs` (an in-memory
session pair), `moq_relay::auth::Auth::embedded` with its `Admissions`
(decides leases in-process), and the lease driver in moq-auth, which could
be exercised against an in-process answer source instead of HTTP. If the
relay's `Connection` or moq-auth's `Client` cannot take such a transport,
prefer the small seam that lets them over a test-only shim.
- Decide where each assertion belongs. The outage semantics (a 503 keeps the
grant until `expires`) are moq-auth's; the relay test may only need to show
that a lease reaching `expires` closes the session as `Expired` and reports
`end`. Don't keep two tests proving the same thing.
- Measure against tokio's clock, not `SystemTime`: the grant still carries a
wall-clock `expires`, so pin how it maps onto the paused clock.
- Other paused-clock tests touch real sockets and would share the hazard
once a timeout lands on their path. #4291's audit named
`a_grant_within_clock_skew_stays_live` (moq-auth) and
`fixed_addresses_keep_tls_name_and_request_host` (moq-tokio websocket);
moq-auth's `clock_server` helper exists only to keep axum on the paused
clock. Move those onto the same seam if it is cheap.
- Prove it: loop the tests with every core loaded, on macOS if available,
and mutate the deadline both ways (close early, close late) to see each
bound fail.

Public API: none unless a transport seam is needed; report it if so. Wire:
none.

## Related

- [More tests under load](/quest/m1/test-flakes-2.md) - the same rule
applied to other load-only failures
- [moq-shaper virtual time](/quest/m1/shaper-virtual-time.md) - the same
paused-clock-versus-real-socket fight in moq-shaper
- [#4280](https://github.com/moq-dev/moq/pull/4280) - moq-archive and
moq-hls tests poll with real-clock sleeps, on the archive track-timeline
line
- [#4281](https://github.com/moq-dev/moq/pull/4281) - OBS `WaitFor` polling,
on the C++ line
- [Nightly 2026-09-26](https://github.com/moq-dev/moq/actions/runs/36240326747/job/108399481809) -
the macOS relay tarball job failed this test with "publisher connect
timeout", before #4244 landed
37 changes: 37 additions & 0 deletions quest/m1/relay-auth-client-ca.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# [S] moq-relay auth validate takes the client-CA flag

## Goal

On dev, no caller of `moq_relay::auth::Config` can start with `--auth-public`
rules alongside a listener TLS client CA by forgetting a check.
`Config::validate` takes the client-CA flag, `init` requires it too, and
`validate_client_ca` is gone. The `moq` CLI fails loud on an invalid auth
config instead of quietly refusing every session.

## Plan

- [#4364](https://github.com/moq-dev/moq/pull/4364) adds an additive
`Config::validate_client_ca(&self, client_ca: bool)` that `Relay::load` and
the CLI must each remember to call; the CLI missing the original check is
the bug it fixes. Fold it into `validate(&self, client_ca: bool)` so every
caller has to answer, and have `init` take the same answer so a caller that
skips `validate` still cannot start. `init` today only receives the
outbound auth TLS, so the listener's client-CA answer is a new input; its
shape (a bool, or the listener TLS config) is open. Prefer whatever makes
the wrong call unrepresentable.
- `spawn_server` in `rs/moq-cli/src/main.rs` maps any `auth.validate()` error
to `Auth::refuse`. That fallback is only right for a LAN-only mesh with no
auth configured, which `MoqSide::validate` permits and whose peers admit
through the cluster. Make that case explicit and let any other error stop
startup.
- Update every caller, the tests #4364 added in both `moq-cli` and
`moq-relay`, and `doc/bin/relay/auth.md` or `doc/lib/rs` wherever they name
the methods.

Public API: breaks `moq_relay::auth::Config::validate` and `init`, removes
`validate_client_ca`, so this targets `dev`. Wire: none.

## Required

- #4364 merged to `main`
- `dev` has merged `main` after #4364 lands
38 changes: 38 additions & 0 deletions quest/m1/watch-refusal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# [S] Watch shows a refusal

## Goal

When the origin refuses the broadcast `<moq-watch>` asks for, the player shows
that refusal as an error instead of sitting offline as if nothing was
published yet. Refusal stays terminal, as
[#4230](https://github.com/moq-dev/moq/pull/4230) made it in `@moq/net` to
match Rust moq-net: the player never re-asks a handler that already said no.

## Plan

- The gap is Codex's P1 on #4230
([r4109909244](https://github.com/moq-dev/moq/pull/4230#discussion_r4109909244)):
`js/watch/src/broadcast.ts` only watches `request.active`, so after a
`dynamic()` handler refuses, the request closes with an error that nobody
reads and `active` stays `undefined` forever.
- Observe `Requesting.closed` and carry the error into the broadcast's
state. Whether that is a new `"error"` status, a separate error signal, or
both is open; mirror how the element already surfaces other terminal
states, such as the unsupported indicator. Keep the error's message so the
UI can say why. `unroutable` is also true for a path nothing serves yet, so
it cannot tell a refusal from offline.
- What clears the error is part of the design: a fresh request (a new
`name` or origin, or re-enabling) should be the only way back. No retry
loop.
- Cover both the announced and unannounced paths in `#runBroadcast`.
- Show it in the UI, and update `demo/web` if it consumes the status. Add a
test in `js/watch` where a `dynamic()` handler refuses and the broadcast
reports the error.
- Update `doc/` wherever the watch status values are documented.

Public API: likely additive (a new status value or error signal on the watch
broadcast and element). Wire: none.
Comment on lines +33 to +34

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Classify an error status as a breaking API change

If this quest chooses the proposed new "error" status, it widens the publicly exposed Broadcast.out.status union in the published @moq/watch package, which can break consumers with exhaustive switches or assignments. That option must target dev; only adding a separate optional error signal is additive, so the quest should distinguish the two instead of labeling both “likely additive.”

AGENTS.md reference: AGENTS.md:L59-L61

Useful? React with 👍 / 👎.


## Related

- [#4230](https://github.com/moq-dev/moq/pull/4230) - made JS refusals terminal
Loading