Skip to content

feat(moq-net): moq-lite-05 SETUP message + PATH parameter#1954

Merged
kixelated merged 4 commits into
mainfrom
claude/vigilant-allen-9c6b7f
Jun 30, 2026
Merged

feat(moq-net): moq-lite-05 SETUP message + PATH parameter#1954
kixelated merged 4 commits into
mainfrom
claude/vigilant-allen-9c6b7f

Conversation

@kixelated

@kixelated kixelated commented Jun 30, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds moq-lite-05 SETUP support with a PATH parameter, kept entirely at the MoQ layer.

A raw iroh or raw QUIC session carries no request URI, so every moq-lite/moq-transport client must be able to advertise its path via MoQ regardless of transport — there is no transport-level dependency here. Everything is gated behind the opt-in Lite05Wip version (not in Versions::all() or ALPNS), so this is purely additive: no shipping version's wire format or public API changes.

Wire-protocol change to moq-net would normally target dev; it's on main by request, safe because it only activates under the opt-in moq-lite-05-wip ALPN.

What changed

moq-net

  • lite/setup.rs (new): Setup { path: Option<String> } — a size-prefixed Message wrapping a TLV parameter bag (PATH = 0x2; unknown IDs ignored). The PATH is rejected on both encode and decode unless it begins with /. send_setup opens the unidirectional DataType::Setup (0x1) stream and sends one SETUP then FINs (closing the session if the send fails, so a peer doesn't hang in accept_setup). accept_setup lets the server read the client's SETUP before serving, resetting any data stream that races ahead.
  • lite::Version::has_setup_stream() gates the behavior; lite::start gained our_setup; the subscriber drains the peer's Setup stream.
  • Public, additive API:
    • Client::with_path(impl Into<String>) — normalized to an absolute path.
    • Server::accept_request() -> Request exposing path() -> Option<&str>, with_publish/with_consume/with_stats, ok(), close() (WebTransport-Request style). accept() is a convenience over accept_request().ok().

moq-native (client supplies the path, fully URL-driven)

The request path is derived from the dial URL — no separate API:

  • tcp://h/anycast, moqt://h/anycast, iroh://node/anycast, https://h/anycast — the URL path component.
  • unix:///run/moq.sock?path=anycast — a ?path= query, because a unix:// URL's path is the socket file, not a namespace. ?path= works as an override on any scheme but is only required for unix://.

The path is advertised in the moq SETUP for transports with no request URI of their own (raw QUIC, qmux over TCP/UDS, and raw iroh — its h3 mode carries one, but sending it there is harmless). WebTransport (https/http) and WebSocket (ws/wss) convey the path in their own request, so it's omitted there.

moq-relay (server consumes the path)

The unauthenticated internal listener (qmux over TCP/UDS — the URL-less server path) reads request.path() and scopes its full internal access to that subtree (no path → empty root, as before). WebTransport/WebSocket continue to derive the path from the transport request.

Public API changes

  • Added (non-breaking): moq_net::Client::with_path, moq_net::Server::accept_request, moq_net::Request (path/with_publish/with_consume/with_stats/ok/close).
  • moq_net::Server::accept unchanged for callers (delegates to accept_request().ok()). No new moq-native public methods — connect(url)/reconnect(url) are unchanged.

Test plan

  • moq-net unit tests: SETUP round-trip, unknown-param passthrough, non-absolute-path rejection, accept_request path surfacing, racing-Group skip — 363 lib tests pass.
  • moq-native tests: transport classification + request_path derivation (incl. unix://?path=); lib tests pass.
  • moq-relay builds.
  • clippy + cargo fmt (via nix) clean on the three crates.
  • Not yet e2e-tested on a live relay.

Follow-ups (not in this PR)

  • js/net: moq-lite-05 SETUP-stream send/read only — no path (browsers use WebTransport, which carries it).
  • doc/concept + doc/bin/relay for the internal-listener path scoping.

(Written by Claude Opus 4.8)

Reintroduce the moq-lite Setup stream (removed in draft-03) for
moq-lite-05, carrying a SETUP message with an extensible parameter bag,
and wire the PATH parameter end to end so a client can advertise a
request path and the server can authorize on it before serving.

All of this is gated behind the opt-in `Lite05Wip` version (not in
`Versions::all()` or `ALPNS`), so the change is purely additive and does
not alter any shipping version's wire format or public API. Targets main
per request; kept leaner than the dev implementation (no ProbeLevel,
PeerSetup, or connecting.rs).

moq-net:
- lite/setup.rs: `Setup { path: Option<String> }` (size-prefixed Message
  with a TLV parameter bag, PATH = 0x2; unknown params ignored),
  `send_setup` (opens the uni `DataType::Setup` stream), and
  `accept_setup` (server eagerly reads the client SETUP, resetting any
  data stream that races ahead).
- `lite::Version::has_setup_stream()` gates the new behavior.
- `lite::start` gains an `our_setup` argument; the subscriber drains the
  peer's Setup stream.
- Public, additive API: `Client::with_path()`, and
  `Server::accept_request() -> Request` exposing `path()`,
  `with_publish`/`with_consume`/`with_stats`, `ok()`, and `close()`
  (modeled on the WebTransport Request). `accept()` is now a convenience
  over `accept_request().ok()`.

The path is optional and wire-faithful at moq-net: it is omitted on
URL-carrying bindings, a server never sends one, and `path()` is `None`
when nothing was advertised in band. The `/` default lives in
moq-native, which owns the dial URL.

moq-native: classify each transport by scheme. WebTransport
(https/http), WebSocket (ws/wss), and iroh (h3 WebTransport) carry the
path in their request; the URL-less schemes (raw QUIC moqt/moql, qmux
over tcp/unix) advertise it in the SETUP via `Client::with_path`,
defaulting an empty path to `/`.

moq-relay: the unauthenticated internal listener (qmux over TCP/UDS, the
URL-less server path) now reads the client's in-band path via
`accept_request` and scopes its full internal access to that subtree.
No path means the empty root, as before.

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

@sourcery-ai sourcery-ai 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.

Sorry @kixelated, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e828d28b-25e2-42ee-bde5-b714475f908b

📥 Commits

Reviewing files that changed from the base of the PR and between dff1098 and 3571e93.

📒 Files selected for processing (2)
  • rs/moq-native/src/client.rs
  • rs/moq-native/src/server.rs

Walkthrough

This PR adds moq-lite-05 Setup stream support and request-path propagation. moq-net gains a Setup message, Setup stream send/receive helpers, and version gating for Setup-stream versions. moq-net::Client now carries an optional path and advertises it for Lite-05. Server::accept_request now returns a paused request that exposes the negotiated path before finalization. moq-native selects per-connection client configuration based on transport path handling and updates WebSocket session storage. moq-relay now scopes internal auth to the negotiated request root.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately names the main change: moq-lite-05 SETUP support with a PATH parameter.
Description check ✅ Passed The description is clearly related and matches the PR's moq-lite-05 SETUP and PATH changes.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% 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.
✨ Finishing Touches
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch claude/vigilant-allen-9c6b7f

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.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 7

🤖 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`:
- Around line 286-291: The MoQ SETUP path is incorrectly derived from url.path()
in the client setup flow, which leaks the Unix socket filesystem path and uses
the wrong request root for unix:// URLs. Update the path handling in the
relevant client methods that call with_path (including the setup/connect paths
referenced in this diff) so that Unix socket URLs do not advertise their
filesystem path as the MoQ request path; instead derive a proper request
path/root for SETUP while preserving the existing HTTP-style default behavior
for non-Unix URLs.
- Around line 269-291: The path handling in connect_client currently treats all
iroh URLs as already carrying the request path, which drops the SETUP path for
raw moq ALPN sessions. Update the transport check so only WebTransport-style
iroh sessions bypass with_path(), and keep applying the URL path for raw iroh
connections created via crate::iroh::connect and Session::raw; use the existing
connect_client and transport_carries_path logic to split the iroh case
appropriately.

In `@rs/moq-net/src/client.rs`:
- Around line 60-61: The public builder method client::Setup::with_path
currently stores whatever string it receives, but Setup::path is expected to be
non-empty and slash-prefixed. Update with_path to normalize the provided path
before assigning it to self.path, ensuring the stored value always matches the
documented SETUP path format. Keep the fix localized to with_path so all callers
get the normalized path consistently.

In `@rs/moq-net/src/lite/session.rs`:
- Around line 59-62: The SETUP send path in session::spawn currently only logs
and continues when send_setup fails, which leaves Lite05 peers waiting forever
in accept_setup. Update the async block around send_setup to treat this as a
fatal session setup failure: after the failed send, close/terminate the session
and avoid returning a usable session. Use the existing session, send_setup, and
tracing::warn call site to locate the change, and make sure the failure path
tears down the session consistently.

In `@rs/moq-net/src/lite/setup.rs`:
- Around line 31-33: The PATH handling in the setup request path validation only
checks for empty strings, so non-absolute values like "foo" can still pass
through encode/decode and reach auth scoping unchanged. Update the path
validation in setup.rs (the setup request encoding/decoding flow around the PATH
parameter, including accept_request() handling) to reject any value that does
not start with "/" on both sides of the wire boundary, and add a regression test
covering a non-slash-prefixed PATH value.

In `@rs/moq-net/src/server.rs`:
- Around line 73-75: Move the empty-origin warning out of `accept_request()` in
`Request::accept_request` and into `Request::ok()`, since `accept_request()` now
returns a builder-like `Request` that callers may still populate with
publish/consume origins after checking `path()`. Keep the warning check on
`publish`/`consume`, but run it only when finalizing the request in `ok()` so
scoped accepts don’t emit false “not publishing or consuming anything” logs.

In `@rs/moq-relay/src/internal.rs`:
- Around line 220-221: The unrestricted token is being minted from the raw
request path, which may not match the relay’s canonical root handling. In the
internal request flow around the `request.path()` to `AuthToken::unrestricted`
conversion, normalize the advertised URL path through the same URL-path-to-root
resolver used for authenticated relay requests, and reject invalid roots before
creating the token. Keep the fix local to the path-to-root logic in this code
path so the `root` passed into `AuthToken::unrestricted` is always canonical.
🪄 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: 29a630d3-1fe9-4c7c-9a16-1731f7462c02

📥 Commits

Reviewing files that changed from the base of the PR and between a34b7c3 and eabb6d1.

📒 Files selected for processing (11)
  • rs/moq-native/src/client.rs
  • rs/moq-net/src/client.rs
  • rs/moq-net/src/lite/mod.rs
  • rs/moq-net/src/lite/parameters.rs
  • rs/moq-net/src/lite/session.rs
  • rs/moq-net/src/lite/setup.rs
  • rs/moq-net/src/lite/stream.rs
  • rs/moq-net/src/lite/subscriber.rs
  • rs/moq-net/src/lite/version.rs
  • rs/moq-net/src/server.rs
  • rs/moq-relay/src/internal.rs

Comment thread rs/moq-native/src/client.rs Outdated
Comment thread rs/moq-native/src/client.rs Outdated
Comment thread rs/moq-net/src/client.rs Outdated
Comment thread rs/moq-net/src/lite/session.rs
Comment thread rs/moq-net/src/lite/setup.rs
Comment thread rs/moq-net/src/server.rs Outdated
Comment thread rs/moq-relay/src/internal.rs
Address review feedback on the lite-05 SETUP wire:

- Reject a non-absolute PATH (not beginning with `/`) on both encode and
  decode, not just empty. URL-carrying transports can never produce a
  relative path, and the relay scopes auth from it, so it must not reach
  `accept_request()`. Adds a regression test.
- Close the session when `send_setup` fails. The peer gates serving on
  receiving our SETUP, so logging and continuing would leave it waiting
  in `accept_setup` while this side held a usable session.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
kixelated and others added 2 commits June 30, 2026 04:38
PATH stays a moq-layer concern (the lite-05 SETUP), not a transport one:
a raw iroh or raw QUIC session carries no request URI, so every client
must be able to advertise PATH via MoQ regardless of transport. Drop the
attempt to read it from the transport and fix the two URL-derivation bugs
CodeRabbit flagged.

moq-native:
- Don't advertise a `unix://` URL's path: it's the filesystem socket
  path, not a request namespace. Default it to `/` and add
  `Client::connect_with_path(url, path)` to set the namespace explicitly.
- Treat `iroh` as URL-less so it always advertises the path in band: its
  raw mode carries no request URI (only the HTTP/3 mode does), and
  sending it for an h3 session is harmless.

moq-net:
- `Client::with_path` normalizes to an absolute path (empty -> `/`,
  prepends a leading `/`).
- Move the empty-origin warning from `accept_request` to `Request::ok`,
  so scoped accepts that attach origins after inspecting `path()` don't
  log a false positive.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ect_with_path

Replace `Client::connect_with_path` with a `?path=` query parameter, so a
single `connect(url)` / `reconnect(url)` covers every case and the path
stays fully URL-driven.

- `request_path()` derives the moq SETUP path from the dial URL: a
  `?path=` query wins (the only way to set one on `unix://`, whose URL
  path is the socket file), otherwise the URL path component (`tcp`, raw
  QUIC, `iroh`). A `unix://` URL with no `?path=` has no namespace.
- Removes the awkward `connect_with_path` entry point.

Also box `RequestKind::WebSocket` to match the other variants and silence
the pre-existing large_enum_variant lint.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@kixelated kixelated merged commit 56791da into main Jun 30, 2026
2 checks passed
@kixelated kixelated deleted the claude/vigilant-allen-9c6b7f branch June 30, 2026 16:15
@moq-bot moq-bot Bot mentioned this pull request Jun 30, 2026
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