Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
024b5e9
quest: claim quest/m1/p2p/webrtc
kixelated Oct 16, 2026
d1b5654
feat(tokio): WebRTC data channel transport over str0m
kixelated Oct 16, 2026
19dcd22
quest: finish m1/p2p/webrtc
kixelated Oct 16, 2026
381b7e1
Merge main into quest/m1/p2p/webrtc
kixelated Oct 16, 2026
963a001
Merge remote-tracking branch 'origin/main' into worktree-agent-a5d146…
kixelated Oct 16, 2026
ace5625
refactor(tokio): split webrtc::Peer into typed Offerer and Answerer
kixelated Oct 16, 2026
bda48c9
fix(tokio): drain str0m after every mutation and bound data channel r…
kixelated Oct 16, 2026
6d9631e
Merge remote-tracking branch 'origin/main' into worktree-agent-a5d146…
kixelated Oct 16, 2026
d0a315c
Merge remote-tracking branch 'origin/main' into pr-5702-webrtc
kixelated Oct 16, 2026
9e6fe3d
refactor(ice): move the shared ICE agent into a moq-ice crate
kixelated Oct 16, 2026
a244d57
fix(ice): bound the send backlog and hold an offer's ICE deadline
kixelated Oct 16, 2026
6eb1649
refactor(ice): bundle an agent's socket, candidates, and inbound queue
kixelated Oct 17, 2026
67a7448
Merge remote-tracking branch 'origin/main' into pr-5702
kixelated Oct 17, 2026
06276d3
chore(rtc): sort moq-rtc dependencies
kixelated Oct 17, 2026
c0111e2
fix(webrtc): end reads on zero-cost records and gate IPv4 on dual-stack
kixelated Oct 17, 2026
4b96283
fix(webrtc): reject STUN hosts with characters a DNS name can't hold
kixelated Oct 17, 2026
8cfea80
Merge remote-tracking branch 'origin/main' into iterate-5702
kixelated Oct 17, 2026
5a99847
fix(ice): hold the ICE deadline until gathering ends
kixelated Oct 17, 2026
9c59474
fix(tokio): give a webrtc-only build the qmux send-group impls
kixelated Oct 17, 2026
39e9ab3
fix(relay): map WebRTC requests into relay authentication
kixelated Oct 17, 2026
6de08b3
fix(webrtc): hold the answerer to the signaled protocol
kixelated Oct 17, 2026
313b214
Merge remote-tracking branch 'origin/main' into iterate-5702
kixelated Oct 17, 2026
fb5c4bf
Merge remote-tracking branch 'origin/main' into quest/m1/p2p/webrtc
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
1 change: 1 addition & 0 deletions .github/workflows/ts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ on:
# Workspace dependencies of moq-net, moq-tokio and moq-mux on the TS path.
- "rs/kio/**"
- "rs/moq-flate/**"
- "rs/moq-ice/**"
- "rs/moq-msf/**"
- "rs/moq-pattern/**"
- "rs/moq-sock/**"
Expand Down
16 changes: 16 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ members = [
"rs/moq-flate",
"rs/moq-gst",
"rs/moq-hls",
"rs/moq-ice",
"rs/moq-json",
"rs/moq-loc",
"rs/moq-msf",
Expand Down Expand Up @@ -61,6 +62,7 @@ default-members = [
# "rs/moq-gst", # requires GStreamer
"rs/moq-flate",
"rs/moq-hls",
"rs/moq-ice",
"rs/moq-json",
"rs/moq-loc",
"rs/moq-msf",
Expand Down Expand Up @@ -133,6 +135,8 @@ hmac = "0.13"
humantime = "2.3"
humantime-serde = "1.1.1"
hyper-util = { version = "0.1.21", features = ["tokio"] }
# Interface addresses for WebRTC host candidates (moq-tokio's `webrtc` feature).
if-addrs = "0.15"
io-uring = "0.7.15"
jsonwebtoken = "11"
kio = { version = "0.6.2", path = "rs/kio" }
Expand All @@ -153,6 +157,7 @@ moq-clock = { version = "0.11.0", path = "rs/moq-clock" }
moq-clock-macros = { version = "0.1.0", path = "rs/moq-clock/macros" }
moq-flate = { version = "0.2.0", path = "rs/moq-flate" }
moq-hls = { version = "0.5.11", path = "rs/moq-hls", default-features = false }
moq-ice = { version = "0.1.0", path = "rs/moq-ice" }
moq-json = { version = "0.5.8", path = "rs/moq-json" }
moq-loc = { version = "0.2.19", path = "rs/moq-loc" }
moq-msf = { version = "0.5.1", path = "rs/moq-msf" }
Expand Down Expand Up @@ -227,6 +232,10 @@ serde_with = "3"
sha2 = "0.11"
slab = "0.4.12"
socket2 = "0.6"
# Sans-IO WebRTC. The default `examples` feature pulls in a web server, and aws-lc-rs
# is the provider rustls already brings.
str0m = { version = "0.24.1", default-features = false, features = ["aws-lc-rs"] }
stun-types = "2.0.3"
subtle = "2.6"
tempfile = "3"
thiserror = "2"
Expand Down
6 changes: 6 additions & 0 deletions doc/concept/transport.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,12 @@ records, with no datagrams. The application supplies signaling and ICE and
keeps ownership of the peer connection; closing the transport closes only its
channel. SCTP ordering means one lost packet can hold back every stream.

Native peers use `moq-tokio`'s `webrtc` feature, which runs its own ICE agent:
host candidates on every interface, server-reflexive candidates from the STUN
servers it is given, and mDNS resolution of a browser's `.local` candidates.
The application still carries the offer, answer, and candidates; the offering
peer connects as the MoQ client and the answering peer accepts.

## iroh (peer-to-peer, experimental)

[iroh](https://www.iroh.computer/) swaps "dial a hostname" for "dial a public
Expand Down
1 change: 1 addition & 0 deletions doc/lib/rs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ The reference implementation. Every crate is on
| [moq-pattern](https://docs.rs/moq-pattern) | Path patterns such as `room/**`. Re-exported by moq-net and moq-auth. |
| [moq-tokio](https://docs.rs/moq-tokio) | Connects and listens over QUIC, WebSocket, or iroh, from config or CLI flags. |
| [moq-sock](https://docs.rs/moq-sock) | Socket binding and CPU pinning for thread-per-core listeners. |
| [moq-ice](https://docs.rs/moq-ice) | The ICE agent behind native WebRTC: drives a str0m connection over UDP, with a STUN client. |
| [moq-uring](https://docs.rs/moq-uring) | Experimental Linux io\_uring server for moq-lite. |
| [hang](/lib/rs/hang) | The media layer: catalog, containers, ordered frame delivery. |
| [moq-mux](/lib/rs/moq-mux) | Import and export fMP4/CMAF, MPEG-TS, Matroska, FLV, and Annex-B. |
Expand Down
1 change: 0 additions & 1 deletion quest/m2/p2p/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,6 @@ Backend choice does not change the policy that watching tabs never re-serve.

- [Peer grants](/quest/m2/p2p/peer-grant.md) - the relay issues a node-bound, asymmetrically signed grant a browser can verify; HS256 keys issue none
- [Signaling and policy](/quest/m2/p2p/signal.md) - opted-in peers find each other under the prefix, the application picks who to dial, and the roster-size gate decides whether STUN is used
- [Native data channel transport](/quest/m2/p2p/webrtc.md) - `moq-tokio` holds a moq-net session with a browser over str0m with a full ICE agent
- [UDP demux](/quest/m2/p2p/udp-demux.md) - one classifier splits QUIC from STUN and WebRTC on the relay's socket and on a native pair's
- [Native pairs over ICE and QUIC](/quest/m2/p2p/native-quic.md) - two native peers connect through ICE, then `moq-quic` over the nominated path with fingerprints pinned from the offer
- [moq-cli joins](/quest/m2/p2p/cli.md) - `--p2p` publishes a roster entry, dials native peers over ICE and QUIC, and serves browsers as a transit hop
Expand Down
1 change: 0 additions & 1 deletion quest/m2/p2p/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,5 @@ Docs: a "P2P" section in `doc/bin/cli.md` beside the LAN cluster one.

## Required

- [Native data channel transport](/quest/m2/p2p/webrtc.md)
- [Native pairs over ICE and QUIC](/quest/m2/p2p/native-quic.md) - the native-to-native path
- [Signaling and policy](/quest/m2/p2p/signal.md) - the roster and offer schema this mirrors
10 changes: 5 additions & 5 deletions quest/m2/p2p/native-quic.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ selected by their applications connect directly across a NAT: ICE finds a
path through host and server-reflexive candidates, then a `moq-quic`
connection runs over the nominated pair, with each side's certificate pinned
by the fingerprint the other sent in its offer or answer. A pair with a
browser still uses a [data channel](/quest/m2/p2p/webrtc.md).
browser still uses a data channel (`moq_tokio::webrtc`).

## Plan

Expand All @@ -18,9 +18,10 @@ Decided with the maintainer on 2026-10-13:
the relay as rendezvous and fallback.
- ICE is str0m's standalone sans-IO agent, the `is` crate (0.11, already in
the lockfile through str0m; str0m re-exports it as `str0m::ice`). Gather
candidates through the module [webrtc](/quest/m2/p2p/webrtc.md) lifts out
of `moq-rtc`, with host candidates, `.local` resolution, and the STUN
Binding client; don't copy it.
candidates with `moq_tokio::webrtc`'s host gathering, `.local` resolution,
and the STUN Binding client in `moq-ice`; don't copy them. The first two
are private to the data channel transport today, so move them into
`moq-ice` rather than export them from `moq-tokio`.
- QUIC is `moq-quic`, fed datagrams from the nominated pair.
- Authentication: each side generates a self-signed certificate and sends
its SHA-256 fingerprint in its offer or answer, as DTLS fingerprints ride
Expand Down Expand Up @@ -60,5 +61,4 @@ covers LAN and NAT pairs between two `moq-cli --p2p` nodes.
## Required

- [UDP demux](/quest/m2/p2p/udp-demux.md) - the classifier that splits STUN from QUIC
- [Native data channel transport](/quest/m2/p2p/webrtc.md) - the shared ICE gathering module
- [Signaling and policy](/quest/m2/p2p/signal.md) - the offer schema and roster this extends
43 changes: 0 additions & 43 deletions quest/m2/p2p/webrtc.md

This file was deleted.

2 changes: 1 addition & 1 deletion rs/moq-auth/src/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ pub enum Transport {
Rtmp,
/// An SRT gateway session.
Srt,
/// A WebRTC gateway session, including WHIP and WHEP.
/// A WebRTC session: a WHIP or WHEP gateway, or MoQ over a data channel.
WebRtc,
/// A WebTransport connection over HTTP/3.
WebTransport,
Expand Down
4 changes: 3 additions & 1 deletion rs/moq-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ path = "src/main.rs"
doc = false

[features]
default = ["cluster-lan", "quic", "websocket", "nvidia", "s3", "gcs", "azure"]
default = ["cluster-lan", "quic", "websocket", "webrtc", "nvidia", "s3", "gcs", "azure"]
# Experimental transport with a heavy dependency tree, enabled only on request.
iroh = ["moq-relay/iroh", "moq-tokio/iroh"]
# LAN discovery and meshing (`--cluster-lan`).
Expand All @@ -38,6 +38,8 @@ quic = ["moq-tokio/quic"]
# congestion-control testing.
qlog = ["moq-tokio/qlog"]
websocket = ["moq-tokio/websocket"]
# Native WebRTC data channels, for holding sessions with browser peers.
webrtc = ["moq-tokio/webrtc"]
# The cloud object stores `export archive` and `import archive` reach (`s3://`,
# `gs://`, `az://`). Local `file://` URLs need none of them.
s3 = ["object_store/aws"]
Expand Down
12 changes: 12 additions & 0 deletions rs/moq-ice/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added

- First release: the ICE agent shared by `moq-tokio`'s WebRTC data channel transport and `moq-rtc`'s WHIP/WHEP gateway, with a STUN Binding client and per-peer wire counters.
23 changes: 23 additions & 0 deletions rs/moq-ice/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[package]
name = "moq-ice"
description = "Media over QUIC - drives a str0m WebRTC connection's ICE over a tokio UDP socket"
authors = ["Luke Curley"]
repository = "https://github.com/moq-dev/moq"
license = "MIT OR Apache-2.0"

version = "0.1.0"
edition = "2024"
rust-version.workspace = true

keywords = ["webrtc", "ice", "stun", "str0m", "moq"]
categories = ["network-programming", "multimedia"]

[dependencies]
str0m = { workspace = true }
stun-types = { workspace = true }
thiserror = { workspace = true }
tokio = { workspace = true, features = ["macros", "net", "rt", "sync", "time"] }
tracing = { workspace = true }

[dev-dependencies]
tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
12 changes: 12 additions & 0 deletions rs/moq-ice/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# moq-ice

Drives a [str0m](https://github.com/algesten/str0m) WebRTC connection over a
tokio UDP socket: the run loop that feeds it datagrams and timeouts, a STUN
Binding client for server-reflexive candidates, and per-peer wire counters.

str0m is sans-IO and requires every mutation to be followed by draining its
output. `Agent::rtc_mut` hands out a guard that drains when dropped, so callers
can't forget.

This is infrastructure, not an entry point: `moq-tokio`'s `webrtc` feature
builds MoQ data channels on it, and `moq-rtc` builds its WHIP/WHEP gateway.
Loading
Loading