Skip to content

moq-net: map MoQ versions to required qmux versions#1513

Open
kixelated wants to merge 10 commits into
devfrom
claude/gifted-mcnulty-7eb608
Open

moq-net: map MoQ versions to required qmux versions#1513
kixelated wants to merge 10 commits into
devfrom
claude/gifted-mcnulty-7eb608

Conversation

@kixelated
Copy link
Copy Markdown
Collaborator

Summary

  • The MoQ WG decided qmux's draft version is tied to the moq-transport version: moq-transport-18 must ride on qmux-01, moq-transport-14..17 on qmux-00. moq-lite is unconstrained.
  • Add the spec mapping to moq-net: QmuxVersion enum, Version::qmux_versions, Version::accepts_qmux, and Versions::qmux_alpns (ordered dedup'd (qmux_version, app_alpn) pairs for building the Sec-WebSocket-Protocol list).
  • Existing moq-lite versions (Lite01..Lite04) advertise both qmux-01 and qmux-00 for back-compat. Future moq-lite versions should pin to a single qmux version like moq-transport. The match arms are listed by variant rather than wildcard so any new Lite variant fails to compile until a deliberate choice is made.

Why this lives in moq-net

The composition of (qmux_version, app_alpn) pairs is policy that depends on the moq-transport spec, not on qmux. qmux today (0.0.7) and after moq-dev/web-transport#226 cross-products qmux versions × app protocols internally, which would advertise illegal pairs like qmux-00.moqt-18 or qmux-01.moqt-17. Keeping the mapping here means moq-native can build exactly the right pairs and feed them through whatever opaque-protocol path qmux exposes (or, if needed, prompt a small with_raw_protocols addition upstream).

What's not in this PR

  • Wiring through moq-native's websocket client/server and moq-relay's sub-protocol list. That's blocked on the qmux 0.0.8 release that ships PR 226 with an API for explicit pairs.
  • Bumping the qmux workspace dep from 0.0.7.

Test plan

  • cargo test -p moq-net --lib version:: — 7 new tests pinning the table and per-version mappings, plus the unchanged 327 existing tests, all pass.
  • cargo clippy -p moq-net --all-targets -- -D warnings clean.
  • cargo fmt -p moq-net -- --check clean.
  • cargo build -p moq-native -p moq-relay -p moq-cli — downstream crates still compile against the new surface (it's purely additive).

(Written by Claude)

The MoQ WG decided qmux's draft version is tied to the moq-transport
version. moq-transport-18 requires qmux-01; moq-transport-14..17 stay
on qmux-00. moq-lite is unconstrained: existing Lite01..Lite04 advertise
both for back-compat, and future moq-lite versions should pin to a
single qmux version like moq-transport does.

Add a local QmuxVersion enum, Version::qmux_versions (the spec mapping),
Version::accepts_qmux (server-side validation), and Versions::qmux_alpns
(ordered, dedup'd (qmux_version, app_alpn) pairs for building the
Sec-WebSocket-Protocol list).

Lite arms are listed by variant rather than wildcard so adding a new
Lite variant fails to compile until someone makes a deliberate choice.

Wiring through moq-native (and the qmux 0.0.8 bump) follows in a
separate change once upstream PR moq-dev/web-transport#226 lands and
exposes an API for explicit (version, protocol) pairs instead of the
default cross-product.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 26, 2026

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

This PR introduces qmux version support to the MoQ protocol library. A new QmuxVersion enum represents qmux draft versions (00 and 01). Version gains qmux_versions() and accepts_qmux() to enumerate and validate allowed qmux drafts per MoQ version. QMUX_ALPNS and QMUX_ALPN_STRINGS provide ordered advertisement pairs/strings. The native client and listener now advertise and negotiate qmux subprotocols via Sec-WebSocket-Protocol and map the negotiated value to a qmux::Version; the relay pins qmux sessions to the negotiated version.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: adding a mapping between MoQ versions and their required qmux versions to moq-net.
Description check ✅ Passed The description is directly related to the changeset, providing clear context about the MoQ WG spec mapping, the new abstractions in moq-net, and rationale for the design decisions.
Docstring Coverage ✅ Passed Docstring coverage is 89.29% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch claude/gifted-mcnulty-7eb608

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 and usage tips.

kixelated and others added 2 commits May 26, 2026 12:42
CI's rustfmt collapses single-expr match arms; local fmt didn't flag it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
qmux 0.1 pins a single Version per Client/Server. To keep advertising
qmux-01.* and qmux-00.* on the same WebSocket connection (which moq-lite
needs for back-compat), moq-native now owns the multi-version
Sec-WebSocket-Protocol composition:

- moq-net exposes Versions::qmux_alpn_strings() that formats the
  ordered (QmuxVersion, app_alpn) pairs as "qmux-XX.app".
- moq-native::websocket::connect builds the request itself and hands
  the upgraded socket to qmux::ws::Bare with the version inferred from
  the negotiated header.
- WebSocketListener does the accept handshake the same way: pick the
  first server-supported pair, then wrap with Bare pinned to that
  version.
- moq-relay::serve_ws lets axum filter on the same string list, reads
  socket.protocol() before adapter conversion, and passes the resolved
  qmux::Version through to handle_socket.

Cargo.toml temporarily points qmux at the local qmux-01 worktree and
adds a [patch.crates-io] entry for web-transport-{trait,proto} so the
whole web-transport family shares one crate instance. Drop both once
qmux 0.1.0 ships on crates.io.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
Cargo.toml (1)

55-71: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Remove machine-local absolute path dependencies from Cargo.toml.

Cargo.toml currently depends on unpublished crates via absolute /Users/kixelated/... worktree paths (qmux at line 55; [patch.crates-io] for web-transport-trait and web-transport-proto at lines 70–71). This will fail for anyone outside that filesystem (CI included). Replace with a shareable source: published crates.io versions, git + rev, or a path relative to the repo (or vendored sources) so the build is reproducible.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Cargo.toml` around lines 55 - 71, The Cargo.toml contains machine-local
absolute path dependencies (qmux and the [patch.crates-io] entries for
web-transport-trait and web-transport-proto) which are not portable; update the
qmux dependency and the web-transport-trait/web-transport-proto patch entries to
use a shareable source (replace the absolute path for the qmux dependency with
either the published crates.io version, a git = "<repo-url>" + rev = "<commit>"
reference, or a relative path inside the repo), and similarly change the
[patch.crates-io] entries for web-transport-trait and web-transport-proto to use
crates.io versions, git+rev, or relative paths so CI and other developers can
build reproducibly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@rs/moq-native/src/websocket.rs`:
- Around line 181-183: bind_with_alpns currently only checks for empty alpns but
later accept_socket expects every negotiated ALPN to map to a known
qmux::Version; validate the provided alpns at bind time by iterating over the
alpns Vec<String> in bind_with_alpns and ensuring each entry maps to a supported
qmux::Version (use the same mapping logic as accept_socket or
qmux::Version::from_str/parse equivalent), and return an error (anyhow::Error)
if any unknown/unsupported subprotocol is present so the API fails
deterministically rather than allowing an unsupported ALPN through the
handshake.

---

Outside diff comments:
In `@Cargo.toml`:
- Around line 55-71: The Cargo.toml contains machine-local absolute path
dependencies (qmux and the [patch.crates-io] entries for web-transport-trait and
web-transport-proto) which are not portable; update the qmux dependency and the
web-transport-trait/web-transport-proto patch entries to use a shareable source
(replace the absolute path for the qmux dependency with either the published
crates.io version, a git = "<repo-url>" + rev = "<commit>" reference, or a
relative path inside the repo), and similarly change the [patch.crates-io]
entries for web-transport-trait and web-transport-proto to use crates.io
versions, git+rev, or relative paths so CI and other developers can build
reproducibly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 091c7ed2-b8bc-4bf2-97b6-108dbf348c1d

📥 Commits

Reviewing files that changed from the base of the PR and between 8999f96 and 32bd4da.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (5)
  • Cargo.toml
  • rs/moq-native/src/client.rs
  • rs/moq-native/src/websocket.rs
  • rs/moq-net/src/version.rs
  • rs/moq-relay/src/websocket.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • rs/moq-net/src/version.rs

Comment thread rs/moq-native/src/websocket.rs Outdated
@kixelated kixelated marked this pull request as draft May 27, 2026 21:22
@kixelated
Copy link
Copy Markdown
Collaborator Author

Converting to draft — CI is intentionally red while this is blocked on qmux 0.1.0 publishing.

Cargo.toml currently links qmux against a local worktree path that CI can't resolve. Once qmux 0.1.0 ships on crates.io (or the qmux-01 branch is pushed somewhere I can reference by git URL), I'll bump the dep and drop the [patch.crates-io] block, and the check will go green.

(Written by Claude)

kixelated and others added 3 commits May 27, 2026 14:54
A bad subprotocol string would otherwise pass the WebSocket handshake
(server happily echoes it back) and only fail later when accept_socket
tried to map it to a qmux::Version. Reject up front so the listener
errors at startup instead of every connection.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The qmux-01 branch is now pushed to moq-dev/web-transport, so CI can
fetch it. Replaces the absolute /Users/... paths with git deps on the
same branch (qmux 0.1.0 + web-transport-{trait,proto} 0.3.5 / 0.6.0).

Still temporary. Drop the git source and the [patch.crates-io] block
once qmux 0.1.0 publishes to crates.io.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@kixelated kixelated marked this pull request as ready for review May 27, 2026 22:09
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Cargo.toml`:
- Line 55: Replace the floating git branch pins with the exact commit rev so the
manifest is immutable: change the qmux dependency entry that currently has
branch = "claude/update-qmux-draft-01-sTTN0" to use rev =
"fd1289acf7e2c5599745059c8ec37c1dba204d18", and do the same for the patch
entries for web-transport-trait and web-transport-proto (replace their branch
fields with the same rev value); after updating Cargo.toml, update the lockfile
by running cargo update -p qmux --precise
fd1289acf7e2c5599745059c8ec37c1dba204d18 (or run cargo generate-lockfile) to
ensure Cargo.lock and the manifest are consistent.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ab0609e6-261b-48f1-8ed9-4c71c8d493dd

📥 Commits

Reviewing files that changed from the base of the PR and between 32bd4da and 1b8551a.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (3)
  • Cargo.toml
  • rs/moq-native/src/websocket.rs
  • rs/moq-relay/src/websocket.rs
🚧 Files skipped from review as they are similar to previous changes (2)
  • rs/moq-relay/src/websocket.rs
  • rs/moq-native/src/websocket.rs

Comment thread Cargo.toml Outdated
Comment thread rs/moq-net/src/version.rs Outdated
Comment thread rs/moq-net/src/version.rs Outdated
Comment thread rs/moq-native/src/client.rs Outdated
Comment thread rs/moq-relay/src/websocket.rs Outdated
kixelated and others added 2 commits May 27, 2026 15:55
Per the review on PR #1513:

- moq-net: replace `Versions::qmux_alpns()` / `qmux_alpn_strings()` (the
  per-instance Vec-returning methods) with module-level consts
  `QMUX_ALPNS: &[(QmuxVersion, &str)]` and
  `QMUX_ALPN_STRINGS: &[&str]`. The permutation table is small and
  baked in; no need to recompute. A unit test pins it against the typed
  list to catch drift.
- moq-net: add `Lite05Wip` arm to `Version::qmux_versions` (pinned to
  qmux-01, opt-in only — excluded from QMUX_ALPNS just like main keeps
  it out of ALPNS / Versions::all). This is the merge-from-main fix
  for the CI break introduced by #1518.
- moq-native (client): drop the unused `versions` field and switch the
  websocket helper to take `&[(QmuxVersion, &str)]` (typed pair list).
  Recover the qmux version after the handshake by index lookup in the
  pair list rather than re-parsing the prefix.
- moq-relay: bundle handle_socket's seven arguments into a `Handler`
  struct with a `run()` method. axum filters on
  `QMUX_ALPN_STRINGS.iter().copied()`; the negotiated subprotocol is
  also resolved by index lookup so the relay no longer has its own
  prefix-parsing helper.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@rs/moq-native/src/client.rs`:
- Line 280: The WebSocket client construction is advertising the full QMUX_ALPNS
instead of honoring the configured Versions subset; change the call sites that
construct moq_net::Client (currently using
moq_net::Client::new().with_versions(config.versions()) and where QMUX_ALPNS is
used) to first derive the offered ALPN/QUIC-version pairs from the configured
Versions (ClientConfig::versions or Versions::... helper) and pass that filtered
list into the WebSocket-path client setup so the same filtered pair list is used
for both normal and WebSocket paths (reuse the filtered list rather than
unconditionally using moq_net::QMUX_ALPNS).

In `@rs/moq-native/src/websocket.rs`:
- Around line 199-205: bind_with_alpns currently accepts arbitrary app strings
and defers validation until handshake, which can panic when
HeaderValue::from_str is later called; validate the formatted ALPN strings at
bind time by iterating over the results of pair_to_alpn (the same vector
assigned to formatted) and attempting HeaderValue::from_str(picked) for each
entry, returning an anyhow::Error if any fail; apply the same pre-validation for
the other bind path that builds ALPNs (the similar block around the 257-263
region) so invalid Sec-WebSocket-Protocol values are rejected during
configuration rather than causing a runtime panic during handshake.

In `@rs/moq-net/src/version.rs`:
- Around line 205-213: The qmux_versions() match currently treats only
Ietf::Draft18 as the "new" QMux01, which will break forward compatibility;
update the match in qmux_versions so that all future IETF drafts default to
QmuxVersion::QMux01 and explicitly list older drafts (Draft14–Draft17) as the
exception mapping to Qmux00. Concretely, change the Ietf arm so older variants
(I::Draft14 | I::Draft15 | I::Draft16 | I::Draft17) return
&[QMuxVersion::QMux00] and use a catch-all Ietf pattern (e.g., I::Draft18 or
I::_ ) that returns &[QMuxVersion::QMux01]; keep the Lite arms unchanged. Ensure
patterns reference qmux_versions and ietf::Version variants (Draft14..Draft18)
so the match remains exhaustive and future drafts inherit QMux01.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 08726d88-b713-46fb-9025-73d1ce243473

📥 Commits

Reviewing files that changed from the base of the PR and between 1b8551a and 622155f.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (4)
  • rs/moq-native/src/client.rs
  • rs/moq-native/src/websocket.rs
  • rs/moq-net/src/version.rs
  • rs/moq-relay/src/websocket.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • rs/moq-relay/src/websocket.rs

Ok(Self {
moq: moq_net::Client::new().with_versions(versions.clone()),
versions,
moq: moq_net::Client::new().with_versions(config.versions()),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Don't bypass ClientConfig.version on WebSocket paths.

These call sites now advertise moq_net::QMUX_ALPNS unconditionally. That ignores the configured versions() subset for WebSocket, so --client-version moq-lite-02 still offers qmux-01.moq-lite-04, qmux-01.moqt-18, etc., and the server can negotiate one of them before moq_net::Client gets a chance to enforce the subset. Please derive the offered pair list from the configured Versions and reuse that filtered list here.

Also applies to: 392-393, 423-424, 453-454, 472-473

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rs/moq-native/src/client.rs` at line 280, The WebSocket client construction
is advertising the full QMUX_ALPNS instead of honoring the configured Versions
subset; change the call sites that construct moq_net::Client (currently using
moq_net::Client::new().with_versions(config.versions()) and where QMUX_ALPNS is
used) to first derive the offered ALPN/QUIC-version pairs from the configured
Versions (ClientConfig::versions or Versions::... helper) and pass that filtered
list into the WebSocket-path client setup so the same filtered pair list is used
for both normal and WebSocket paths (reuse the filtered list rather than
unconditionally using moq_net::QMUX_ALPNS).

Comment on lines +199 to +205
pub async fn bind_with_alpns(
addr: net::SocketAddr,
alpns: &'static [(QmuxVersion, &'static str)],
) -> anyhow::Result<Self> {
anyhow::ensure!(!alpns.is_empty(), "no WebSocket subprotocols to accept");
let listener = tokio::net::TcpListener::bind(addr).await?;
let server = qmux::Server::new().with_protocols(alpns);
Ok(Self { listener, server })
let formatted = alpns.iter().map(|(qv, app)| pair_to_alpn(*qv, app)).collect();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Reject invalid custom subprotocol strings at bind time.

bind_with_alpns accepts arbitrary app strings, but the handshake path later does HeaderValue::from_str(picked).expect(...). A custom pair that formats to an invalid Sec-WebSocket-Protocol value will panic the listener during handshake instead of failing configuration early.

🛡️ Proposed fix
 	pub async fn bind_with_alpns(
 		addr: net::SocketAddr,
 		alpns: &'static [(QmuxVersion, &'static str)],
 	) -> anyhow::Result<Self> {
 		anyhow::ensure!(!alpns.is_empty(), "no WebSocket subprotocols to accept");
-		let listener = tokio::net::TcpListener::bind(addr).await?;
-		let formatted = alpns.iter().map(|(qv, app)| pair_to_alpn(*qv, app)).collect();
+		let formatted: Vec<String> = alpns.iter().map(|(qv, app)| pair_to_alpn(*qv, app)).collect();
+		for protocol in &formatted {
+			tungstenite::http::HeaderValue::from_str(protocol)
+				.with_context(|| format!("invalid WebSocket subprotocol: {protocol}"))?;
+		}
+		let listener = tokio::net::TcpListener::bind(addr).await?;
 		Ok(Self {
 			listener,
 			pairs: alpns,
 			formatted: Arc::new(formatted),
 		})

Also applies to: 257-263

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rs/moq-native/src/websocket.rs` around lines 199 - 205, bind_with_alpns
currently accepts arbitrary app strings and defers validation until handshake,
which can panic when HeaderValue::from_str is later called; validate the
formatted ALPN strings at bind time by iterating over the results of
pair_to_alpn (the same vector assigned to formatted) and attempting
HeaderValue::from_str(picked) for each entry, returning an anyhow::Error if any
fail; apply the same pre-validation for the other bind path that builds ALPNs
(the similar block around the 257-263 region) so invalid Sec-WebSocket-Protocol
values are rejected during configuration rather than causing a runtime panic
during handshake.

Comment thread rs/moq-net/src/version.rs
Comment on lines +205 to +213
pub fn qmux_versions(&self) -> &'static [QmuxVersion] {
use ietf::Version as I;
use lite::Version as L;
match self {
Self::Ietf(I::Draft18) => &[QmuxVersion::QMux01],
Self::Ietf(I::Draft14 | I::Draft15 | I::Draft16 | I::Draft17) => &[QmuxVersion::QMux00],
Self::Lite(L::Lite01 | L::Lite02 | L::Lite03 | L::Lite04) => &[QmuxVersion::QMux01, QmuxVersion::QMux00],
Self::Lite(L::Lite05Wip) => &[QmuxVersion::QMux01],
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Default new IETF drafts to qmux-01 here.

This hard-codes Draft18 as the only "new" case, so the next IETF draft will not inherit the newest qmux behavior automatically.

♻️ Proposed fix
 		match self {
-			Self::Ietf(I::Draft18) => &[QmuxVersion::QMux01],
 			Self::Ietf(I::Draft14 | I::Draft15 | I::Draft16 | I::Draft17) => &[QmuxVersion::QMux00],
+			Self::Ietf(_) => &[QmuxVersion::QMux01],
 			Self::Lite(L::Lite01 | L::Lite02 | L::Lite03 | L::Lite04) => &[QmuxVersion::QMux01, QmuxVersion::QMux00],
 			Self::Lite(L::Lite05Wip) => &[QmuxVersion::QMux01],
 		}

As per coding guidelines, "When matching on Version enums in Rust, default to the newest draft behavior so future versions default forward. Explicitly list older versions as the exception case."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rs/moq-net/src/version.rs` around lines 205 - 213, The qmux_versions() match
currently treats only Ietf::Draft18 as the "new" QMux01, which will break
forward compatibility; update the match in qmux_versions so that all future IETF
drafts default to QmuxVersion::QMux01 and explicitly list older drafts
(Draft14–Draft17) as the exception mapping to Qmux00. Concretely, change the
Ietf arm so older variants (I::Draft14 | I::Draft15 | I::Draft16 | I::Draft17)
return &[QMuxVersion::QMux00] and use a catch-all Ietf pattern (e.g., I::Draft18
or I::_ ) that returns &[QMuxVersion::QMux01]; keep the Lite arms unchanged.
Ensure patterns reference qmux_versions and ietf::Version variants
(Draft14..Draft18) so the match remains exhaustive and future drafts inherit
QMux01.

Now that qmux 0.1.0 has shipped, drop the temporary git/path deps:

- Cargo.toml: qmux switches from the local-path/git dep on the qmux-01
  branch back to a normal crates.io version pin. The [patch.crates-io]
  block for web-transport-{trait,proto} is gone too; ^0.3.4 and ^0.6
  now resolve cleanly to the published 0.3.5 / 0.6.0.
- js/net: bump @moq/qmux to ^0.1.0 and update the WebSocket fallback in
  connect.ts to the new API. qmux 0.1.0 pins a single QMux version per
  Session, so the polyfill now constructs `new Session(url, { version:
  "qmux-01", protocols: [...] })` and offers the QMux01-compatible app
  protocols (moq-lite-04/03/01-02, moqt-18). Older moq-transport drafts
  that require QMux00 reach the relay via native WebTransport instead.

bun.lock will refresh on the next install once npm has fully propagated
@moq/qmux 0.1.0; not committed here because frozen-lockfile would fail.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@kixelated kixelated changed the base branch from main to dev May 28, 2026 16:37
@moq/qmux@0.1.0 is now live on npm; this picks it up so
`bun install --frozen-lockfile` passes again. js/net typechecks (`bun
run check`) against the new SessionOptions surface.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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