Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
6dcf832
rename announce flow to publish_namespace for draft-16 alignment
itzmanish May 6, 2026
68e71b7
implement draft-16 KVP delta encoding and namespace validation
itzmanish May 6, 2026
8a24653
update setup and ALPN for draft-16
itzmanish May 7, 2026
b280222
add REQUEST_OK/REQUEST_ERROR/REQUEST_UPDATE and enforce control messa…
itzmanish May 11, 2026
037dcb3
enforce draft-16 request ID flow control
itzmanish May 11, 2026
bdb45a9
update PUBLISH_NAMESPACE flow for draft-16
itzmanish May 11, 2026
130bb1c
update SUBSCRIBE flow for draft-16
itzmanish May 11, 2026
e5d2a2e
update subscription termination flow for draft-16
itzmanish May 11, 2026
34a8278
update TRACK_STATUS flow for draft-16
itzmanish May 11, 2026
455d423
remove ObjectDoesNotExist (0x1) from ObjectStatus for draft-16
itzmanish May 11, 2026
b5fcb45
return REQUEST_ERROR NOT_SUPPORTED for unimplemented requests
itzmanish May 11, 2026
0cbc6fc
update relay and transport descriptions for draft-16
itzmanish May 11, 2026
5c49801
harden draft-16 control and data paths
itzmanish May 13, 2026
da00a61
fix: opencode suggestions
itzmanish May 13, 2026
76a1d3e
fix publish namespace clean close
itzmanish May 14, 2026
adec802
remove legacy draft message stubs
itzmanish May 15, 2026
9de3e30
harden setup and request-id flow control
itzmanish May 18, 2026
87091ee
fix small nits
itzmanish May 18, 2026
8fad2c3
set default max request id to 0 if not provided in client setup
itzmanish May 18, 2026
b9f43f6
fix: add missing subscribe option in subscribe namespace message
itzmanish May 19, 2026
79b0209
Merge remote-tracking branch 'manish/main' into mpandit/RT-469
itzmanish May 29, 2026
2bbb259
Implement draft-16 subscription parameters and TrackName type
itzmanish May 26, 2026
1787db6
fix: clean up unsubscribe state
itzmanish May 27, 2026
64a505b
use correct loglevel for logs for subgroups
itzmanish May 27, 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
32 changes: 32 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Repository Guidance

## Scope And Constraints

- This repository targets MoQT `draft-ietf-moq-transport-16`.
- Core protocol code lives in `moq-transport`; relay behavior lives in `moq-relay-ietf`.
- `moq-relay-ietf` is production critical, so prefer small, tested changes over broad refactors.
- Avoid breaking public APIs, wire behavior, CLI flags, and operator workflows unless explicitly requested.
- Do not add `unsafe` code unless there is a clear performance or FFI need and the safety invariant is documented at the call site.
- Do not commit or directly edit `docs/draft-16.txt` unless explicitly requested; it is local protocol reference material.
- Avoid `unwrap()`, `expect()`, `panic!`, `todo!`, `unimplemented!`, and `dbg!` in production paths. Propagate errors or convert lock poisoning to an internal error instead.

## Commands

- Run `cargo fmt --all` after Rust changes.
- Run `cargo test -p moq-transport` for protocol changes.
- Run `cargo test -p moq-relay-ietf` or `cargo build -p moq-relay-ietf` for relay-only changes.
- Run `cargo build --workspace` when shared APIs, relay behavior, or test-client behavior changes.
- Consider `cargo clippy --workspace --all-targets` before larger changes or when review asks for lint coverage.
- When tests fail, diagnose from compiler/test output first and keep fixes scoped to the failing behavior.

## Draft-16 Terminology

- Use `PUBLISH_NAMESPACE` terminology in protocol code and docs; older docs and comments may call this Announce.
- `PublishedNamespace` represents an inbound `PUBLISH_NAMESPACE` received by a subscriber.
- `TrackNamespacePrefix` is for `SUBSCRIBE_NAMESPACE` prefixes and may be empty; `TrackNamespace` is full and non-empty.
- `REQUEST_OK`, `REQUEST_ERROR`, and `REQUEST_UPDATE` are shared draft-16 request response/update messages.

## External Contributions

- Treat public GitHub mirror and community PRs with heightened scrutiny for protocol correctness, resource bounds, panic-free production paths, and safe peer-facing errors.
- Be careful with compatibility: do not rename public CLI flags or remove behavior solely for terminology cleanup without an explicit migration decision.
52 changes: 27 additions & 25 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ members = [
resolver = "2"

[workspace.dependencies]
web-transport = "0.10"
web-transport = "0.10.4"
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

An implementation of the Media over QUIC Transport (MoQT) protocol for live media delivery over QUIC, as specified by the IETF MoQ working group.

This codebase was originally created by [Luke Curley (@kixelated)](https://github.com/kixelated). [Mike English (@englishm)](https://github.com/englishm) contributed to early design and has maintained this IETF-aligned fork. The project is now maintained by Cloudflare. The implementation targets [draft-ietf-moq-transport-14](https://datatracker.ietf.org/doc/draft-ietf-moq-transport/14/).
This codebase was originally created by [Luke Curley (@kixelated)](https://github.com/kixelated). [Mike English (@englishm)](https://github.com/englishm) contributed to early design and has maintained this IETF-aligned fork. The project is now maintained by Cloudflare. The implementation targets [draft-ietf-moq-transport-16](https://datatracker.ietf.org/doc/draft-ietf-moq-transport/16/).

## Protocol Support

The `main` branch targets **draft-14** of the MoQT specification. For draft-07 compatibility (used in [Cloudflare's current production deployment](https://developers.cloudflare.com/moq/)), see the [`draft-ietf-moq-transport-07`](https://github.com/cloudflare/moq-rs/tree/draft-ietf-moq-transport-07) branch.
The `main` branch targets **draft-16** of the MoQT specification. For draft-07 compatibility (used in [Cloudflare's current production deployment](https://developers.cloudflare.com/moq/)), see the [`draft-ietf-moq-transport-07`](https://github.com/cloudflare/moq-rs/tree/draft-ietf-moq-transport-07) branch.

### What's Included

Expand Down
22 changes: 11 additions & 11 deletions deploy/MLOG_SETUP.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ curl https://interop-relay.cloudflare.mediaoverquic.com:443/mlog/22c73802597dcd9
The output is JSON-SEQ: each record starts with ASCII Record Separator (`0x1e`) and contains a JSON object. The first record is a header; subsequent records are events:

```json
{"time":0.179,"name":"moqt:control_message_parsed","data":{"message_type":"client_setup","supported_versions":["DRAFT_14"],...}}
{"time":0.216,"name":"moqt:control_message_created","data":{"message_type":"server_setup","selected_version":"DRAFT_14",...}}
{"time":0.179,"name":"moqt:control_message_parsed","data":{"message_type":"client_setup","supported_versions":["DRAFT_16"],...}}
{"time":0.216,"name":"moqt:control_message_created","data":{"message_type":"server_setup","selected_version":"DRAFT_16",...}}
```

- `control_message_parsed` = the relay **received** a message from your client
Expand Down Expand Up @@ -74,12 +74,12 @@ cargo run --bin moq-test-client -- \
"stream_id": 0,
"message_type": "client_setup",
"number_of_supported_versions": 1,
"supported_versions": ["DRAFT_14"],
"supported_versions": ["DRAFT_16"],
"parameters": [["2", "100"]]
}
}
```
The relay parsed your CLIENT_SETUP. It offered version DRAFT_14. The `parameters` array contains SETUP parameters as `[id, value]` pairs (here, PATH with max length 100).
The relay parsed your CLIENT_SETUP. It offered version DRAFT_16. The `parameters` array contains SETUP parameters as `[id, value]` pairs (here, PATH with max length 100).

```json
{
Expand All @@ -89,26 +89,26 @@ The relay parsed your CLIENT_SETUP. It offered version DRAFT_14. The `parameters
"event_type": "control_message_created",
"stream_id": 0,
"message_type": "server_setup",
"selected_version": "DRAFT_14",
"selected_version": "DRAFT_16",
"parameters": [["2", "100"]]
}
}
```
The relay responded with SERVER_SETUP, selecting DRAFT_14. The handshake is complete.
The relay responded with SERVER_SETUP, selecting DRAFT_16. The handshake is complete.

**What to look for:**
- If you see `client_setup` parsed but no `server_setup` created, the relay rejected your version or parameters
- If you see nothing at all, your connection didn't reach the MoQ layer (check QUIC connectivity)

### Example 2: Publishing a namespace (`announce-only`)
### Example 2: Publishing a namespace (`publish-namespace-only`)

After SETUP, announce a namespace and verify the relay accepts it.

```bash
cargo run --bin moq-test-client -- \
--relay https://interop-relay.cloudflare.mediaoverquic.com:443 \
--tls-disable-verify \
--test announce-only
--test publish-namespace-only
```

**mlog output** (after the SETUP exchange):
Expand Down Expand Up @@ -148,21 +148,21 @@ The relay accepted the namespace with PUBLISH_NAMESPACE_OK. Your client is now r
- `publish_namespace_ok` created confirms it was accepted
- The `request_id` ties the response to the request

### Example 3: Full publish-subscribe flow (`announce-subscribe`)
### Example 3: Full publish-subscribe flow (`publish-namespace-subscribe`)

This test uses two connections: a publisher and a subscriber. The test client reports both Connection IDs:

```bash
cargo run --bin moq-test-client -- \
--relay https://interop-relay.cloudflare.mediaoverquic.com:443 \
--tls-disable-verify \
--test announce-subscribe
--test publish-namespace-subscribe
```

**TAP output:**

```
ok 1 - announce-subscribe
ok 1 - publish-namespace-subscribe
---
duration_ms: 3436
publisher_connection_id: 71d4b5eb1a807779af03331c330d5fa9
Expand Down
4 changes: 2 additions & 2 deletions moq-clock-ietf/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ async fn main() -> anyhow::Result<()> {
tokio::select! {
res = session.run() => res.context("session error")?,
res = clock_publisher.run() => res.context("clock error")?,
res = publisher.announce(tracks_reader) => res.context("failed to serve tracks")?,
res = publisher.publish_namespace(tracks_reader) => res.context("failed to serve tracks")?,
}
} else {
tracing::info!("publishing clock via streams");
Expand All @@ -82,7 +82,7 @@ async fn main() -> anyhow::Result<()> {
tokio::select! {
res = session.run() => res.context("session error")?,
res = clock_publisher.run() => res.context("clock error")?,
res = publisher.announce(tracks_reader) => res.context("failed to serve tracks")?,
res = publisher.publish_namespace(tracks_reader) => res.context("failed to serve tracks")?,
}
}
} else {
Expand Down
2 changes: 1 addition & 1 deletion moq-native-ietf/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ categories = ["multimedia", "network-programming", "web-programming"]
[dependencies]
moq-transport = { path = "../moq-transport", version = "0.14" }
web-transport = { workspace = true }
web-transport-quinn = { version = "0.11", default-features = false, features = ["ring"] }
web-transport-quinn = { version = "0.11.8", default-features = false, features = ["ring"] }

rustls = { version = "0.23", features = ["ring"] }
rustls-pemfile = "2"
Expand Down
26 changes: 21 additions & 5 deletions moq-native-ietf/src/quic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -426,9 +426,18 @@ impl Server {
.await
.context("failed to receive WebTransport request")?;

let moqt_protocol = std::str::from_utf8(moq_transport::setup::ALPN)
.context("invalid MoQT ALPN")?
.to_string();
let response = if request.protocols.contains(&moqt_protocol) {
web_transport_quinn::proto::ConnectResponse::OK.with_protocol(moqt_protocol)
} else {
web_transport_quinn::proto::ConnectResponse::OK
};

// Accept the CONNECT request.
let session = request
.ok()
.respond(response)
.await
.context("failed to respond to WebTransport request")?;
(session, Transport::WebTransport)
Expand Down Expand Up @@ -552,10 +561,17 @@ impl Client {
.to_string();

let (session, transport) = match url.scheme() {
"https" => (
web_transport_quinn::Session::connect(connection, url.clone()).await?,
Transport::WebTransport,
),
"https" => {
let moqt_protocol = std::str::from_utf8(moq_transport::setup::ALPN)
.context("invalid MoQT ALPN")?
.to_string();
let request = web_transport_quinn::proto::ConnectRequest::new(url.clone())
.with_protocol(moqt_protocol);
(
web_transport_quinn::Session::connect(connection, request).await?,
Transport::WebTransport,
)
}
"moqt" => (
web_transport_quinn::Session::raw(
connection,
Expand Down
2 changes: 1 addition & 1 deletion moq-pub/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ async fn main() -> anyhow::Result<()> {
res = run_media(media) => {
res.context("media error")?
},
res = publisher.announce(reader) => res.context("publisher error")?,
res = publisher.publish_namespace(reader) => res.context("publisher error")?,
}

Ok(())
Expand Down
2 changes: 1 addition & 1 deletion moq-pub/src/media.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ impl Media {
let mut selection_params = moq_catalog::SelectionParam::default();

let mut track = moq_catalog::Track {
init_track: Some(self.init.name.clone()),
init_track: Some(self.init.name.to_string()),
name: name.clone(),
namespace: Some(self.broadcast.namespace.to_utf8_path()),
packaging: Some(moq_catalog::TrackPackaging::Cmaf),
Expand Down
4 changes: 2 additions & 2 deletions moq-relay-ietf/src/bin/moq-relay-ietf/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ pub struct Cli {
#[arg(long)]
pub mlog_dir: Option<PathBuf>,

/// Forward all announces to the provided server for authentication/routing.
/// If not provided, the relay accepts every unique announce.
/// Forward all PUBLISH_NAMESPACE messages to the provided server for auth/routing.
/// If not provided, the relay accepts every unique namespace publish.
#[arg(long)]
pub announce: Option<Url>,

Expand Down
Loading
Loading