Skip to content

fix: m0 interop audit - #6255

Merged
kixelated merged 20 commits into
mainfrom
quest/m0/audit-interop
Oct 23, 2026
Merged

kixelated merged 20 commits into
mainfrom
quest/m0/audit-interop

Conversation

@kixelated

@kixelated kixelated commented Oct 22, 2026 •

Copy link
Copy Markdown
Collaborator

Runs the m0 interop audit over the relays page and the runner's 2026-10-22 nightly, fixes what was ours, reports what was theirs, and schedules the m1 run.

Fixes (one commit each)

  • Rust WebTransport offer (moq-tokio, moq-wasm): stop offering moq-00 as a WebTransport subprotocol. Draft-14 predates WT subprotocols, and moxygen picked it over draft-16. Draft-14 now rides moql and negotiates in SETUP, as js/net already does. Each client maps the ALPN list itself, so moq-net's API is unchanged.
    • Verified live: moxygen over WebTransport (draft-16) passes from this branch and is red on the published release.
  • WT_SESSION_GONE (moq-tokio): a stream reset or stopped with 0x170d7b68 now reads as the session's close reason, not invalid RESET_STREAM. If it beats the close capsule, it reads as WebTransportError::SessionGone rather than a guessed clean close. Unknown capsules log at DEBUG.
  • js/net control writes: every control message (type, length, body) goes out in one write. Message.write and Writer.collect replace four copies of the scratch-writer code.
  • js/net moqt://: a moqt:// or moql:// URL, including a GOAWAY redirect, dials WebTransport at https:// (draft-18 §3.1.3). Before, the CONNECT carried :scheme: moqt and moq-relay dropped it. A failed race now names each transport's error instead of Promise.any's empty message.
  • Smoke failure_reason: leads with the last stage the subscriber reached (not connected, nothing subscribed, no SUBSCRIBE_OK, only headless groups, no objects), then the first error minus capsule teardown noise. The Rust CLI logs SUBSCRIBE_OK only at DEBUG, so a Rust cell says so instead of claiming an OK.
  • Smoke relays: drops the Cloudflare draft-16 extras. Those relays need a provisioned token and close everything else with "scope resolution failed".

Expected red

  • moxygen draft-14 over QUIC (moq CLI) stays red until IETF lazy discovery lands, now promoted to m1 because it owns this cell. moq-net never asks draft-14/15 for the empty namespace, and moxygen announces nothing unasked. A CLI subtree scope fixed the cell but lost catalog-referenced siblings (Codex P1), so it was dropped: moq-dev/moq#6165 made moq-lite ask only for watched prefixes, and moq-mux Source watches the siblings, so lite needs no scope.

Rechecked

Public API and wire impact

  • moq-net: none. The WebTransport subprotocol mapping lives privately in moq-tokio and moq-wasm.
  • Wire: Rust clients offer moql instead of moq-00 in WT-Available-Protocols. The server side is unchanged and still accepts moq-00 from released clients.
  • moq-tokio: adds quic::web::WebTransportError::SessionGone to a #[non_exhaustive] enum. Additive, so no MIGRATE entry.
  • js/net: moqt:/moql: URLs now connect over WebTransport instead of failing. No exported symbols change.

Upstream

The full classification, including what is unsupported, down, or not yet reported, is in the successor quest quest/m1/audit-interop.md.

Decisions (settled by the maintainer)

  1. CLI scope vs sibling broadcasts. Scoping the dialed subscriber to subtree(--broadcast) fixed moxygen draft-14 but lost catalog-referenced siblings, such as a transcode's . source (Codex).
    • (a) keep the subtree; (b) scope to the broadcast's parent; (c) scope only on IETF drafts 14-15; (d) drop the scope and let IETF lazy discovery fix the cell (chosen, done). The scope commit is reverted, and IETF lazy discovery moves from m2 to m1.
  2. Versions::web_protocols() as public API.
    • (a) keep it public; (b) hide the mapping in moq-tokio and moq-wasm with a string filter (chosen, implemented).
  3. JS join filter before draft-20. The quest asked for Next Group Start. Next Group Start would never deliver a write-once catalog without a joining FETCH (the reason for fix(moq-net): serve joining FETCH with its saved object prefix #3562), so it stays unfiltered.
    • (a) wait for JavaScript FETCH (chosen); (b) Next Group Start for every track; (c) a per-track join hint from hang.
  4. Cloudflare draft-16 on the relays page.
    • (a) drop it (chosen, done); (b) provision a token as a CI secret and keep the cells.

Checks

  • just check on 485927d030 (after merging main): the rs (including the moq-wasm wasm32 build and clippy), js, and quest steps pass. The flake step fails building the GitHub runner VM image (unshare: write failed /proc/self/uid_map), a sandbox limit of this machine unrelated to this diff.
  • Targeted tests:
    • cargo test -p moq-tokio --lib web_protocols and quic::web
    • the moq-tokio WebTransport integration tests
    • bun test for js/net: the full suite (2637 tests) passes on 485927d030, which also routes the PUBLISH and SUBSCRIBE_TRACKS writes main added through Message.write
  • just test interop --all: all 119 checks pass on 485927d030 (107 of 107 on deccd90855; the count changed with the cases main added since), including every language against the Rust relay over WebTransport.
  • Smoke: test/smoke just check passes.
  • Live relays lane from deccd90855's moq (before the CLI scope was dropped): run against moxygen, Cloudflare, imquic, MOQtail, aiomoqt, moqx, red5, and moqt-nr.

Follow-ups

  • Answer an H3 CONNECT with a bad :scheme with 400 rather than dropping the stream (moq-tokio Connecting::accept).
  • Recheck the js-web publisher cells after JavaScript FETCH lands.

🤖 Generated with Claude Code

(Written by Claude Opus 5.5)

kixelated and others added 10 commits October 21, 2026 20:50
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…scriber

Draft-14 predates WebTransport subprotocols, so a peer that accepts moq-00
picks draft-14 over a newer draft both support. Offer moql instead and
negotiate in SETUP, as js/net does.

Draft-14 and draft-15 cannot ask for the empty namespace, so the CLI
subscriber discovered nothing from moxygen. Scope it to the consumed
broadcasts, as the HLS and RTC exports scope theirs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
draft-ietf-webtrans-http3 §4.4 resets and stops a closed session's streams
with WT_SESSION_GONE, which surfaced as an invalid RESET_STREAM. Report the
session's close reason instead, and log unknown capsules at DEBUG.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each Writer primitive issued its own stream write, so an IETF control
message went out as a type, a u16 length, and a body. Message.write now
buffers the type and the encoded message and writes them once, as Rust
does, and Message.encode (both IETF and lite) writes its size and body
in one write too. A message that fails to encode still leaves nothing on
the stream, and two writers can no longer interleave inside a message.

Writer.collect replaces the scratch Writers that ietf/message.ts,
lite/message.ts, ietf/object.ts, and lite/subscriber.ts each built.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The first error-looking line was often teardown noise after the timeout.
Lead with the stage (connected, SUBSCRIBE sent, SUBSCRIBE_OK, headless
groups) and keep the first error, minus capsule teardown, as the detail.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Cloudflare's draft-16 relays require a provisioned token in the URL path and
close anything else with "scope resolution failed", so these cells could
never pass. The registry lists every other public relay.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A browser has no raw QUIC, and the WebTransport CONNECT carries the URL's
scheme, so moq-relay refused moqt:// (draft-18 §3.1.3 says to swap in
https). This also covers a GOAWAY redirect to a moqt:// URI. A failed race
now names each transport's error instead of Promise.any's empty message.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated

Copy link
Copy Markdown
Collaborator Author

Quest outcome (m0 interop audit): every red cell on the relays page and in the runner's 2026-10-22 nightly that has a moq.dev side has been classified. Fixes for our side are in this PR. Upstream bugs are filed or already tracked. What is down or unsupported is recorded in quest/m1/audit-interop.md, which schedules the next run.

The PR stays a draft until the maintainer settles the four open decisions in the description. The two that matter most:

  • whether to keep the CLI subscriber scoped to subtree(--broadcast)
  • whether Versions::web_protocols() should be public

just check passes for the rs, js and quest scopes. The flake step failed only because this machine's sandbox does not permit unshare. The cross-language just test interop --all suite was not run.

(Written by Claude Opus 5.5)

kixelated and others added 2 commits October 21, 2026 23:15
Drop the public `Versions::web_protocols()`: the two WebTransport clients map
`moq-00` to `moql` themselves, so moq-net's API is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 20 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 4 included reviews currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 8435b214-6a38-42e9-b194-194cf64e2be4
📥 Commits

Reviewing files that changed from the base of the PR and between 1c72130 and 485927d.

📒 Files selected for processing: 34
  • doc/concept/transport.md
  • js/net/src/connection/accept.ts
  • js/net/src/connection/connect.test.ts
  • js/net/src/connection/connect.ts
  • js/net/src/connection/handshake.ts
  • js/net/src/ietf/adapter.ts
  • js/net/src/ietf/filter.ts
  • js/net/src/ietf/message.test.ts
  • js/net/src/ietf/message.ts
  • js/net/src/ietf/object.ts
  • js/net/src/ietf/publisher.ts
  • js/net/src/ietf/request_stream.ts
  • js/net/src/ietf/subscriber.test.ts
  • js/net/src/ietf/subscriber.ts
  • js/net/src/lite/message.ts
  • js/net/src/lite/subscriber.ts
  • js/net/src/stream.ts
  • quest/m0/README.md
  • quest/m0/audit-interop.md
  • quest/m1/README.md
  • quest/m1/audit-interop.md
  • quest/m1/ietf-lazy-discovery.md
  • quest/m1/interop-coverage/README.md
  • quest/m1/interop-runner-nightly.md
  • quest/m2/README.md
  • quest/m2/keyed-prefix-polling.md
  • rs/moq-tokio/src/quic/backend.rs
  • rs/moq-tokio/src/quic/web/error.rs
  • rs/moq-tokio/src/quic/web/recv.rs
  • rs/moq-tokio/src/quic/web/send.rs
  • rs/moq-tokio/src/quic/web/session.rs
  • rs/moq-wasm/src/transport.rs
  • test/smoke/relays.sh
  • test/smoke/smoke.sh
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-22T22:02:25.153818Z 485927d Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 891582a011

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread test/smoke/smoke.sh
The Rust CLI logs SUBSCRIBE_OK only at DEBUG, so at INFO a Rust cell that
sent SUBSCRIBE and timed out was reported as accepted. Say the OK was not
logged unless a positive marker appears.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 17d3414d1a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread rs/moq-tokio/src/quic/web/error.rs Outdated
kixelated and others added 2 commits October 21, 2026 23:53
A WT_SESSION_GONE reset can beat the CloseWebTransportSession capsule.
Reporting it as `Closed(0, "")` claimed a clean close and hid the real
code; `WebTransportError::SessionGone` says only that the session is gone,
and the recorded reason replaces it once known.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: deccd90855

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread rs/moq-cli/src/main.rs Outdated
kixelated and others added 4 commits October 22, 2026 14:13
moq-lite clients already ask only for watched prefixes (#6165), and
moq-mux Source watches the sibling broadcasts a catalog references, so the
subtree scope only lost those siblings. The moxygen draft-14 cell stays
red until IETF lazy discovery asks for watched prefixes too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The PUBLISH and SUBSCRIBE_TRACKS control writes that main added to
js/net's IETF publisher go through Message.write, so they too go out in
one write.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
It owns the moxygen draft-14 CLI cell now that the CLI does no subscriber
scoping of its own.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated

Copy link
Copy Markdown
Collaborator Author

@codex review

(Written by Claude Opus 5.5)

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Bravo.

Reviewed commit: b84f6114dd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

recv.rs's map_error is now a free function shared with poll_lease, so the
WT_SESSION_GONE mapping moves into it and covers leased reads too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated

Copy link
Copy Markdown
Collaborator Author

@codex review

(Written by Claude Opus 5.5)

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Can't wait for the next one!

Reviewed commit: 485927d030

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@kixelated
kixelated added this pull request to the merge queue Oct 22, 2026
Merged via the queue into main with commit 4e7ddf5 Oct 23, 2026
11 checks passed
@kixelated
kixelated deleted the quest/m0/audit-interop branch October 23, 2026 00:45
kixelated added a commit that referenced this pull request Oct 23, 2026
The union renewal flake was the publisher writing REQUEST_OK's type and body in
two writes: the shared write budget can yield to the host after any write, and
under load the subscriber's FIN landed in that gap, so the publisher FINed
after a bare type and the subscriber closed the session as malformed. #6255
already made every JS message one write; this pins that with a test that
yields after every write, and closes the rest:

- JS: the IETF subscriber no longer sends an update of defaults when its last
  subscriber leaves (the logged priority=255 was that), matching lite and
  Rust, and an update that fails to encode ends the track loudly instead of
  being swallowed.
- Rust: every IETF control message buffers its type with its body, all or
  nothing, via Writer::buffer_message / encode_message, so a failed encode or
  a cancelled write can no longer strand a bare type for the next write or a
  FIN to complete.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EeyiEy5FWz3z44DYZVo7vU
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant