Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 doc/lib/rs/moq-net.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ What you use it for, beyond what the concept page already describes:
- **Follow restarts.** `consumer.follow(path)` reduces the routes covering a path to the one a request resolves (the most specific) and yields its start, update, restart, and end. A restart says another publisher instance now serves the path: request it again. A broadcast already resolved stays on the old instance until dropped or its route goes.
- **One track per name.** Concurrent subscriptions share one request and producer. The application owns sequence numbers: a producer that replaces an ended one gets `Error::Duplicate` from an append that would reuse a sequence the name already wrote, so it starts with `create_group` at an explicit sequence. Only a new broadcast starts over. Readers of an aborted track resume onto the replacement only when the broadcast announces an epoch; otherwise their subscription ends with the error.
- **Publish only while someone is watching.** `demand()` on a track, group, or broadcast says whether a subscriber is attached, which is how capture and transcode skip work nobody asked for. Holding a broadcast consumer is not demand. Holding a track consumer is, so drop one you are not reading. A shared fetch stays up until its last reader leaves.
- **Use the model without a runtime.** Disable default features on `moq-net` to use its `poll_*` APIs and origin driver with caller-supplied time. The default `async` feature adds async helpers, `time::run`, and both wire sessions.
- **You drive the session, or `moq-tokio` does.** `connect` and `accept` return a session plus a driver that never reads the clock itself. `moq_net::time::run` polls it on tokio or in the browser. `moq-tokio` and `moq-wasm` hide that. A custom transport implements `moq_net::transport::poll`.
- **A session caps what its peer can hold.** `session::Limits` (via `Client::with_limits` and `Server::with_limits`) bounds announces and subscriptions per session; the defaults suit a relay mesh, so lower them for untrusted peers. Past either, the session closes with `TOO_MANY_REQUESTS`. On moq-transport drafts 14 to 16 they also size the request-ID window, which every request counts against, so very low limits can starve it. Peer-declared lengths are capped before they are buffered.
- **Authorization is in band.** On moq-lite 07 (`moq-lite-07-wip`, opt-in) and on moq-transport draft-17+ with the [MoQ Auth extension](/draft/moq-auth), `session.auth()` presents tokens without reconnecting and watches the grant the peer sent back. A server verifies tokens itself by taking `handshake.auth().requests()` before `ok()`. `authorize(&grant)` narrows or widens a live session on any version: what falls outside resets with `Unauthorized` and the session stays up. A client that publishes outside its grant closes the session, naming the path.
Expand Down
1 change: 0 additions & 1 deletion quest/m1/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ above them.
- [Untimed decisions](/quest/m1/untimed-decisions.md) - the maintainer decides whether moq-archive keeps refusing untimed tracks, and whether a malformed FETCH object ends its track
- [FFI shape](/quest/m1/ffi-shape/README.md) - the bindings mirror Rust's layers: net at the root, then media, json, flate, audio, and video namespaces built from the handle below
- [Publishing never invents a timestamp](/quest/m1/publish-timestamp.md) - no Rust or binding publish API fills in a timestamp; an untimed payload goes out untimed
- [moq-net's async feature](/quest/m1/net-async-feature.md) - async helpers and both sessions sit behind a default `async` feature, and CI builds moq-net without it; Controlled time's net migration requires it
- [Bump web-transport-iroh for the capsule close](/quest/m1/iroh-capsule-bump.md) - condition: moq-dev/web-transport#419 ships in a release, then moq's iroh HTTP/3 client reports a peer's close capsule
- [Early drops](/quest/m1/subscribe-drop-early.md) - Rust and JS publishers drop a sequence the moment they give it up, and forward an upstream drop
- [SUBSCRIBE_DROP](/quest/m1/subscribe-drop.md) - every stream group in a lite subscription arrives or is dropped by name, and lite-07 drops its stream count for it
Expand Down
24 changes: 0 additions & 24 deletions quest/m1/net-async-feature.md

This file was deleted.

1 change: 0 additions & 1 deletion quest/m1/time/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,4 +108,3 @@ cleanup and the global lint ratchet belong to

- [More tests under load](/quest/m1/test-flakes-2/README.md) - the per-flake fixes this line makes systemic
- [Rust-backed @moq/net](/quest/m2/rs2ts/README.md) - Rust and JS clocks must agree across either browser backend
- [The async feature](/quest/m1/net-async-feature.md) - the core keeps explicit instants; ambient time stays behind `async`
9 changes: 2 additions & 7 deletions quest/m1/time/net.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,8 @@ arm timers only through `moq-clock`:

Breaking; it lands on `main` after the crate (see [the line's README](/quest/m1/time/README.md)).
The ambient functions sit behind moq-net's `async` feature; the core takes
explicit instants. Decided 2026-10-10 from review: this waits for
[the async feature](/quest/m1/net-async-feature.md), so the
migration lands gated rather than exposing ambient time to the sans-IO core.
explicit instants. The migration preserves that boundary rather than exposing
ambient time to the sans-IO core.
Coordinate clock origin, units, and controlled-time behavior with the
[browser backend](/quest/m2/rs2ts/README.md) if it has landed. Source-level
`Instant` mappings depend on the selected backend; WASM retains Rust time types.
Expand Down Expand Up @@ -60,7 +59,3 @@ instant has no general conversion back: `time::Instant`, `moq_mux::Clock`,
and moq-mux's `flush`/`update` family among them (`container::Producer`,
`catalog::Estimator`, `rate::Control`, the codec and import producers). The
PR enumerates each break and migrates its in-tree callers. Wire: none.

## Required

- [The async feature](/quest/m1/net-async-feature.md) - the feature the ambient functions sit behind
5 changes: 5 additions & 0 deletions rs/justfile
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ c-tests:

# Compile the workspace at the feature extremes.
features:
just rs net-features
cargo check --locked --workspace {{ no_fuzz }} --all-targets --no-default-features
cargo hack check --locked -p moq-tokio --each-feature --exclude-features _transport --features aws-lc-rs
cargo check --locked -p moq-relay --no-default-features
Expand All @@ -220,6 +221,10 @@ features:
RUSTDOCFLAGS="-D warnings" cargo doc --locked --workspace {{ no_fuzz }} --all-features --no-deps
bash sh/rs/nextest.sh cargo nextest run --locked --workspace {{ no_fuzz }} --all-targets --all-features

# Compile and test the explicit-time model without async helpers or sessions.
net-features:
bash sh/rs/net-features.sh

# Compile the media crates at supported feature boundaries and prove expensive
# native/render dependencies are absent when their features are excluded.
media-features:
Expand Down
21 changes: 19 additions & 2 deletions rs/moq-net/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,19 @@ categories = ["multimedia", "network-programming", "web-programming"]
ignored = ["getrandom"]

[features]
default = ["async"]
# Runtime-backed helpers and Lite/IETF sessions; explicit-time model drivers stay available.
async = ["dep:futures", "dep:web-async"]

# Exposes the wire codecs to the `fuzz/` harness and the announce, codec, varint, and frame benches,
# and session views of an origin to the viewers bench, through the hidden `fuzz` module. Off by
# default and hidden from the docs.
fuzz = []
fuzz = ["async"]

[dependencies]
arrayvec = "0.7"
bytes = { workspace = true }
futures = { workspace = true }
futures = { workspace = true, optional = true }
kio = { workspace = true }
moq-pattern = { workspace = true, features = ["serde"] }
num_enum = { workspace = true }
Expand All @@ -34,10 +38,15 @@ slab = { workspace = true }
thiserror = { workspace = true }
tracing = { workspace = true }
uuid = { workspace = true, features = ["js"] }
web-async = { workspace = true, optional = true }

# The browser instant shim is needed even by explicit-time drivers.
[target.'cfg(target_family = "wasm")'.dependencies]
web-async = { workspace = true }

[dev-dependencies]
criterion = { workspace = true }
futures = { workspace = true }
# The tests' executor, with simulated time.
moq-net-sim = { path = "sim" }
serde_json = { workspace = true }
Expand Down Expand Up @@ -71,6 +80,7 @@ required-features = ["fuzz"]
[[bench]]
name = "origin"
harness = false
required-features = ["async"]

# Reaches the crate-private session view of an origin through the hidden `fuzz` module.
[[bench]]
Expand Down Expand Up @@ -105,26 +115,32 @@ required-features = ["fuzz"]
[[bench]]
name = "group"
harness = false
required-features = ["async"]

[[bench]]
name = "track"
harness = false
required-features = ["async"]

[[bench]]
name = "priority"
harness = false
required-features = ["async"]

[[bench]]
name = "stats"
harness = false
required-features = ["async"]

[[bench]]
name = "session"
harness = false
required-features = ["async"]

[[bench]]
name = "admission"
harness = false
required-features = ["async"]

[[bench]]
name = "request-ids"
Expand All @@ -140,3 +156,4 @@ required-features = ["fuzz"]
[[bench]]
name = "auth"
harness = false
required-features = ["async"]
6 changes: 6 additions & 0 deletions rs/moq-net/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ Live media is built on top of this layer using something like [hang](https://git
- **Groups**: A sequential collection of frames, usually starting with a keyframe.
- **Frame**: A timed chunk of data.

The default `async` feature includes Lite/IETF sessions, async convenience methods,
and the announcement `Stream` adapter.
Use `default-features = false` for the model's `poll_*` APIs and origin driver,
polled with explicit time without a native runtime. This excludes sessions and
`time::run`; the origin's core serving logic remains available.

## Examples

- [Publishing a chat track](https://github.com/moq-dev/moq/blob/main/rs/moq-tokio/examples/chat.rs)
Expand Down
2 changes: 1 addition & 1 deletion rs/moq-net/benches/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -813,7 +813,7 @@ impl ChurnRoom {
for _ in 0..shape.sessions {
let mut viewer = cluster.join(0).await;
viewer.watch(&path(0), &held, None).await;
broadcasts.push(viewer.origin.consume().routed_broadcast(&path(0)).await.unwrap());
broadcasts.push(viewer.origin.consume().routed_broadcast(path(0)).await.unwrap());
viewers.push(viewer);
}
Self {
Expand Down
Loading
Loading