Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
a62b26d
quest: claim quest/m1/quic/fork/switch
kixelated Oct 16, 2026
590d323
feat(moq-tokio): import quinn's async layer and web-transport-moq ver…
kixelated Oct 16, 2026
e8195bd
feat(quic)!: run moq-tokio and moq-uring on moq-quic
kixelated Oct 16, 2026
018fd5c
quest: claim quest/m1/quic/peer-limits
kixelated Oct 16, 2026
866b13d
fix(moq-tokio): log a CONNECT stream cut off by the close at debug
kixelated Oct 16, 2026
88a6fae
docs: point fork and advisory guidance at moq-quic
kixelated Oct 16, 2026
2d50267
docs: format the upgrade note
kixelated Oct 16, 2026
29d1c68
feat(relay): widen QUIC limits for cluster peers
kixelated Oct 16, 2026
c2993ca
quest: finish quest/m1/quic/fork/switch
kixelated Oct 16, 2026
fcd8ea4
Merge origin/main into quest/m1/quic/fork/switch
kixelated Oct 16, 2026
e111abf
test(moq-tokio): gate the timed helper with its only caller
kixelated Oct 16, 2026
7e000d4
refactor(relay): nest peer QUIC limits under [peer.quic]
kixelated Oct 16, 2026
5b7bff2
Merge origin/main into quest/m1/quic/fork/switch
kixelated Oct 16, 2026
43193f9
Merge remote-tracking branch 'origin/main' into quest-switch-alias
kixelated Oct 16, 2026
188b08c
fix(tokio): mark quic::web and quic::driver error enums non_exhaustive
kixelated Oct 16, 2026
7d379d1
Merge quest/m1/quic/fork/switch into quest/m1/quic/peer-limits
kixelated Oct 16, 2026
5d21b4f
fix(tokio): count in-flight accepts in wait_all_draining
kixelated Oct 16, 2026
21cac2b
fix(tokio): wake draining waiters when accept empties the queue
kixelated Oct 16, 2026
fb4e809
Merge quest/m1/quic/fork/switch into quest/m1/quic/peer-limits
kixelated Oct 16, 2026
c1f4cfd
fix(tokio): surface non-WouldBlock receive errors, cap moq_quic logs
kixelated Oct 16, 2026
60d9aeb
Merge quest/m1/quic/fork/switch into quest/m1/quic/peer-limits
kixelated Oct 16, 2026
be42bf2
Merge main into quest/m1/quic/peer-limits
kixelated Oct 16, 2026
6f35168
fix(relay): widen every cluster dial and make zero peer windows unrep…
kixelated Oct 16, 2026
e9de2c1
Merge main into quest/m1/quic/peer-limits
kixelated Oct 16, 2026
ee6fd8e
fix(tokio): build with_quic_limits without the quic feature
kixelated Oct 16, 2026
344c9d8
fix(relay): make a zero peer stream limit unrepresentable
kixelated Oct 16, 2026
2ec0f45
Merge main into quest/m1/quic/peer-limits
kixelated Oct 16, 2026
867dd01
refactor(relay): build every cluster dial from the connect template
kixelated Oct 16, 2026
0589fcf
Merge branch 'main' of github.com:moq-dev/moq into worktree-agent-a3f…
kixelated Oct 16, 2026
03270fe
fix(relay): wait for peer stream credit in the limits probe
kixelated Oct 16, 2026
8335ba2
Merge branch 'main' of github.com:moq-dev/moq into worktree-agent-a3f…
kixelated Oct 16, 2026
506c3d0
fix(relay): refuse a peer receive window past a QUIC varint at load
kixelated Oct 16, 2026
ff5565c
Merge branch 'main' of github.com:moq-dev/moq into worktree-agent-a3f…
kixelated Oct 16, 2026
ba90290
fix(relay): refuse unadvertisable QUIC limits and keep the static dia…
kixelated Oct 16, 2026
78b373c
Merge branch 'main' of github.com:moq-dev/moq into worktree-agent-a3f…
kixelated Oct 16, 2026
7c120e4
fix(cli): build the dial client only for --connect
kixelated Oct 16, 2026
d23be12
fix(cli): refuse a fixed --connect-bind shared by --connect and clust…
kixelated Oct 16, 2026
2644fe5
Merge branch 'main' of github.com:moq-dev/moq into worktree-agent-a3f…
kixelated Oct 16, 2026
f1f4b1e
test(relay): cover peer QUIC limits on the tokio worker shards
kixelated Oct 16, 2026
7bde915
Merge branch 'main' of github.com:moq-dev/moq into worktree-agent-a3f…
kixelated Oct 16, 2026
32113e5
test(relay): link peer_limits into the integration binary
kixelated Oct 16, 2026
94cdbf1
fix(relay): close the cluster's peer dials before the runtime stops
kixelated Oct 16, 2026
7d6d263
Merge remote-tracking branch 'origin/main' into worktree-agent-a3f79e…
kixelated Oct 16, 2026
b76291d
fix(relay): stop every cluster dial task on close
kixelated Oct 17, 2026
685fbbd
Merge remote-tracking branch 'origin/main' into worktree-agent-a3f79e…
kixelated Oct 17, 2026
6de8b24
Merge branch 'main' of github.com:moq-dev/moq into worktree-agent-a3f…
kixelated Oct 17, 2026
d3e8ecc
fix(relay): make every Cluster::close await one shared shutdown
kixelated Oct 17, 2026
fc2e9b3
Merge branch 'main' of github.com:moq-dev/moq into worktree-agent-a3f…
kixelated Oct 17, 2026
36a4e44
fix(cli): dial --connect only after the listener starts
kixelated Oct 17, 2026
c5a40ab
Merge branch 'main' of github.com:moq-dev/moq into worktree-agent-a3f…
kixelated Oct 17, 2026
cb5e07d
fix(relay): bind the cluster dial client only after start can no long…
kixelated Oct 17, 2026
d63a1c4
Merge branch 'main' of github.com:moq-dev/moq into worktree-agent-a3f…
kixelated Oct 17, 2026
7a4a6bb
fix(relay): close the cluster's dials whenever Started::run returns
kixelated Oct 17, 2026
86d7426
docs(relay): note that a cancelled Started::run cannot await its closes
kixelated Oct 17, 2026
b817fc3
Merge branch 'main' of github.com:moq-dev/moq into worktree-agent-a3f…
kixelated Oct 17, 2026
56d89f3
fix(cli): check the --connect-bind collision only when QUIC binds it
kixelated Oct 17, 2026
a5abb70
Merge branch 'main' of github.com:moq-dev/moq into worktree-agent-a3f…
kixelated Oct 17, 2026
497a4ec
fix(relay): abort every cluster dial when its dial map drops
kixelated Oct 17, 2026
e09b430
docs(cli): scope --connect-bind sharing to WAN dials and name MOQ_CON…
kixelated Oct 17, 2026
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
6 changes: 5 additions & 1 deletion doc/bin/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,11 @@ moq --cluster-lan --cluster-lan-secret /etc/moq/cluster.key import capture
MoQ process and `moq-relay` on the LAN, with no other configuration. Without
`--cluster-lan-secret`, anyone who can reach the listener joins, so leave it
unset only on networks you trust. `--cluster-connect` is a MoQ side on its own,
so it needs no `--connect`.
so it needs no `--connect`. WAN cluster dials (`--cluster-connect`,
`--cluster-connect-api`) share `--connect-bind` with `--connect`, and each binds
it, while LAN dials take an ephemeral port. So a fixed `--connect-bind` port with
both `--connect` and a WAN cluster peer is refused at startup: use port 0, or drop
`--connect` (unset `MOQ_CONNECT` if it supplies the URL).

## Auth

Expand Down
13 changes: 13 additions & 0 deletions doc/bin/relay/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,21 @@ fetches = 10000 # Independent simultaneous FETCH requests.
announces = 100000
subscriptions = 10000
fetches = 10000

[peer.quic]
max_streams = 100000 # Concurrent QUIC streams per direction.
receive_window = 671088640 # Connection-wide receive window (640 MiB).
send_window = 671088640 # Unacknowledged outgoing data (640 MiB).
```

`[peer.quic]` takes only `max_streams`, `receive_window`, and `send_window`;
any other `[quic]` key is refused. They replace the `[quic]` values for a
peer's QUIC connection once its grant is known, before the SETUP reply. Links
the relay dials get them from the handshake on. A link carrying a whole cluster is then
not throttled by `[quic]` limits sized for one viewer, for example
`--peer-quic-max-streams=200000`. Peers over WebSocket, TCP, or iroh keep
their transport's own limits.

These are the defaults. For example, `--peer-subscriptions=50000` raises the
subscription cap on cluster links. `--session-fetches` and `--peer-fetches`
set the independent FETCH caps; concurrent callers of the same group each
Expand Down
2 changes: 0 additions & 2 deletions quest/m1/quic/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,6 @@ cache limits remain separate; bitrate pacing composes with this memory cap.
tail grace
- [Schedule datagrams by subscription priority](/quest/m1/quic/datagram-priority.md) -
datagrams join their subscription's send group instead of preempting every stream
- [Relay peers get wider limits](/quest/m1/quic/peer-limits.md) - MAX_STREAMS
and MAX_DATA are raised after SETUP identifies a cluster peer
- [A FIN below received data is a final size error](/quest/m1/quic/final-size-below-received.md) - a shrunken final size closes with FINAL_SIZE_ERROR instead of blocking the reader forever
- [A stateless reset keeps a closed connection's reason](/quest/m1/quic/close-reason-after-reset.md) - a reset after a local transport error neither replaces the reason nor reports a second loss
- [0-RTT streams adopt a raised stream window](/quest/m1/quic/zero-rtt-window-raise.md) - a server's larger stream window reaches the client's 0-RTT streams, so they no longer stall
Expand Down
46 changes: 0 additions & 46 deletions quest/m1/quic/peer-limits.md

This file was deleted.

1 change: 0 additions & 1 deletion quest/m1/quic/receive-memory.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,4 +107,3 @@ separate guide quest is needed.
- [Unbounded groups](/quest/m2/unbounded-groups/README.md) - removes the frame cap; frame payload credit becomes per segment
- [Scope track priority](/quest/m1/track-priority-scope.md) - broadcast fairness domains and subscription priority semantics
- [QUIC bitrate caps](/quest/m2/rate-quic.md) - pace credit grants inside the independently bounded memory allowance
- [Relay peers get wider limits](/quest/m1/quic/peer-limits.md) - client and peer windows must both fit supported units
15 changes: 8 additions & 7 deletions quest/m2/rate-quic.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,18 +49,20 @@ backpressure the peer cannot ignore, rather than metering and closing.
window for an uncapped session, paced credit for a capped one. A lowered
rate (revalidation, a union shrinking) withholds future grants against
outstanding credit rather than shrinking assembly memory below a legal
message. Memory-window reductions retain the shrink-as-debt semantics
described in [peer limits](/quest/m1/quic/peer-limits.md). Overshoot is
message. Memory-window reductions retain `moq-quic`'s shrink-as-debt
semantics. Overshoot is
bounded by credit outstanding at the change and the test asserts that bound.
- **Egress: cap the pacer.** The send rate is `min(controller rate, cap)`,
so a subscriber below the broadcast's bitrate gets MoQ's normal group
skipping, not a growing queue.
- **Runtime, per connection.** The cap is known only after auth (the token
rides the CONNECT URL or arrives in band), so it is set on a live
connection and reset on revalidation or a union change. Reuse the runtime
limits seam [peer limits](/quest/m1/quic/peer-limits.md) adds
(`set_limits(Limits)` on moq-net's transport trait, implemented by the
moq-tokio and moq-uring adapters), extended with the two rates.
connection and reset on revalidation or a union change. Extend the
runtime limits seam the relay widens cluster peers through
(`moq_tokio::server::Request::set_quic_limits` and
`moq_uring::quic::web::Session::set_limits`, each taking its backend's
`quic::Limits`) with the two rates, and reach it from the live session
so revalidation can change it after SETUP.
- **Unions take the highest cap.** When in-band auth unions several grants
on one session, each direction's cap is the highest among them, and an
uncapped grant lifts it, matching the union's permissive meaning. Decided
Expand Down Expand Up @@ -89,5 +91,4 @@ that a one-second rate window can also be the complete assembly allowance.

## Required

- [Peer limits](/quest/m1/quic/peer-limits.md) - adds the runtime `set_limits(Limits)` seam and shrink-as-debt behavior this extends
- [Bound message assembly](/quest/m1/quic/receive-memory.md) - separate retained memory from paced outstanding credit
1 change: 0 additions & 1 deletion quest/m2/transport-adapter-dedup.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,3 @@ Public API: possibly a feature-gated module. Wire: none.
- [poll_acked on moq-net's send stream](/quest/m2/quic-ack-hook.md) - a backend-specific method the moq-tokio and moq-uring adapters keep
- [Per-stream deadlines](/quest/m2/quic-deadline.md) - adds `set_deadline`, also kept out of the shared adapter
- [QUIC caps](/quest/m2/rate-quic.md) - extends `set_limits` with the bitrate caps
- [Peer limits](/quest/m1/quic/peer-limits.md) - adds the `set_limits` seam
2 changes: 1 addition & 1 deletion rs/moq-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ doc = false
[features]
default = ["cluster-lan", "quic", "websocket", "nvidia", "s3", "gcs", "azure"]
# Experimental transport with a heavy dependency tree, enabled only on request.
iroh = ["moq-tokio/iroh"]
iroh = ["moq-relay/iroh", "moq-tokio/iroh"]
# LAN discovery and meshing (`--cluster-lan`).
cluster-lan = ["moq-relay/cluster-lan"]
jemalloc = ["moq-tokio/jemalloc"]
Expand Down
54 changes: 54 additions & 0 deletions rs/moq-cli/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,15 @@ impl MoqSide {
self.client.url.is_some() || self.serves() || self.cluster_dials(),
"a MoQ side is required: pass --connect <url> to dial a relay, a --listen option to self-host, --cluster-lan to mesh over the LAN, or --cluster-connect to join a cluster"
);
// The `--connect` client and the cluster's WAN dials each bind
// `--connect-bind` for QUIC, so a fixed port would collide halfway through startup.
#[cfg(feature = "quic")]
anyhow::ensure!(
!(self.client.url.is_some()
&& self.cluster_dials()
&& self.client.bind.is_some_and(|bind| bind.port() != 0)),
Comment thread
kixelated marked this conversation as resolved.
Resolved
"--connect-bind with a fixed port cannot serve both --connect and --cluster-connect / --cluster-connect-api, which each bind it: use port 0, or drop --connect (unset MOQ_CONNECT if it supplies the URL)"
);
#[cfg(feature = "cluster-lan")]
{
self.cluster.lan.validate()?;
Expand Down Expand Up @@ -2315,6 +2324,51 @@ mod tests {
parse(&[]).unserved().expect("nothing to refuse");
}

/// `--connect` and a WAN cluster peer each bind `--connect-bind` for QUIC, so a
/// fixed port is refused before anything binds; port 0 or either side alone is
/// fine, and a build without QUIC binds nothing to collide.
#[test]
fn a_fixed_connect_bind_serves_one_dialer() {
let parse = |flags: &[&str]| {
let args = ["moq"].iter().chain(flags).chain(&["import", "ts"]).copied();
Invocation::try_parse_from(args).expect("parse")
};
for peer in [
["--cluster-connect", "https://relay.example"],
["--cluster-connect-api", "https://api.example/peers"],
] {
let both = [
&["--connect", "https://relay.example", "--connect-bind", "127.0.0.1:4443"][..],
&peer,
]
.concat();
let result = parse(&both).moq.validate();
if cfg!(feature = "quic") {
let err = result.unwrap_err().to_string();
assert!(err.contains("--connect-bind") && err.contains(peer[0]), "{err}");
} else {
result.expect("without QUIC nothing binds the fixed port");
}

let ephemeral = [
&["--connect", "https://relay.example", "--connect-bind", "127.0.0.1:0"][..],
&peer,
]
.concat();
parse(&ephemeral).moq.validate().expect("an ephemeral bind serves both");

let cluster_only = [&["--connect-bind", "127.0.0.1:4443"][..], &peer].concat();
parse(&cluster_only)
.moq
.validate()
.expect("the cluster alone owns a fixed bind");
}
parse(&["--connect", "https://relay.example", "--connect-bind", "127.0.0.1:4443"])
.moq
.validate()
.expect("--connect alone owns a fixed bind");
}

/// `--cluster-connect` / `--cluster-connect-api` attach the process as a
/// cluster peer, so they are a MoQ side on their own. `--cluster-node` is
/// identity, not an attachment.
Expand Down
Loading
Loading