diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 08faaad5ac..fd37b476be 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -92,6 +92,12 @@ jobs: if: ${{ !cancelled() }} run: nix develop --command bun js/net/bench/frames.ts + # Times one varint encode and decode per wire format. No threshold: it only + # has to keep running, and its numbers are the baseline for the generated codec. + - name: JS varint benchmark + if: ${{ !cancelled() }} + run: nix develop --command bun js/net/bench/varint.ts + # Fails if publishing a group costs more as the track retains more groups, # which means the latency guard or the cache went back to scanning them all. - name: JS track retention benchmark diff --git a/Cargo.lock b/Cargo.lock index c5b2f64b6b..d83041b540 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4630,6 +4630,7 @@ dependencies = [ "serde", "serde_json", "serde_with", + "socket2 0.6.5", "sysinfo", "tempfile", "thiserror 2.0.21", @@ -4804,6 +4805,7 @@ dependencies = [ "serde", "serde_with", "sha2", + "socket2 0.6.5", "subtle", "tempfile", "thiserror 2.0.21", diff --git a/doc/bin/cli.md b/doc/bin/cli.md index 287ae9445f..4c58d42b09 100644 --- a/doc/bin/cli.md +++ b/doc/bin/cli.md @@ -225,9 +225,9 @@ zero-based `frame` and padded standard base64. `` is the literal track name. `/fetch` splits its path on the last `/`, so the two agree only for names without one. Fetch only dials `--connect`, and -refuses a listener or cluster flag. It gives up after 30 seconds, as `/fetch` -does, and exits non-zero when the broadcast or group is not found (before -writing anything), the relay refuses, or the deadline passes. +refuses any listener, cluster, auth, or `--hop` flag. It gives up after 30 +seconds, as `/fetch` does, and exits non-zero when the broadcast or group is not +found (before writing anything), the relay refuses, or the deadline passes. ## Multiple stages diff --git a/doc/bin/relay/cluster.md b/doc/bin/relay/cluster.md index f4c3466a5d..d313dd825a 100644 --- a/doc/bin/relay/cluster.md +++ b/doc/bin/relay/cluster.md @@ -49,18 +49,18 @@ link costs 1, which reproduces plain hop counting. Each relay adds the price of the link an announcement arrived on before forwarding it, so a route's cost is the sum of what it crossed. -Wildcard advertisements are forwarded and costed the same way as an exact-path +Prefix advertisements are forwarded and costed the same way as an exact-path route: each hop appends its identity, adds the link price, and passes the -claim on. An advertisement must be contained by one of the publisher's granted -prefixes (`grant/**`); an over-wide pattern is refused rather than clamped. +claim on. An advertised prefix must overlap the publisher's grant, or it is +refused. A prefix wider than the grant is accepted, but it only routes requests +for paths the grant covers. -Routing prefers the most specific pattern, then a fully identified hop list +Routing prefers the longest covering prefix, then a fully identified hop list over one that holds a 0 (an anonymous hop) at any depth, then the lowest cost, then the shortest hop list, breaking any remaining tie toward the newest announcement so a reconnecting publisher isn't outranked by the session it replaced. An assigned identity for an anonymous peer is local selection state -and is never written into the hop list. Resolving a non-prefix pattern into a -subscription is not implemented yet. +and is never written into the hop list. ```toml [cluster] diff --git a/doc/bin/relay/config.md b/doc/bin/relay/config.md index d5febea5b8..6cec124121 100644 --- a/doc/bin/relay/config.md +++ b/doc/bin/relay/config.md @@ -77,8 +77,8 @@ io_uring = false # Drive them with io_uring instead of tokio ``` Packets are steered by connection ID, so a client that migrates stays with its -worker. The group shares one port, including an ephemeral (zero) port: the -first worker binds it and the rest join that port. Use an explicit port unless +worker. The group shares one port, including an ephemeral (zero) port, which is +resolved once and joined by every worker. Use an explicit port unless something reads the bound address at startup. `workers` needs the `noq` feature and real certificate files rather than `tls.generate`. A build without QUIC rejects `workers` instead of diff --git a/doc/lib/go/index.md b/doc/lib/go/index.md index 20a7c9d590..97ff40f178 100644 --- a/doc/lib/go/index.md +++ b/doc/lib/go/index.md @@ -95,10 +95,10 @@ Paths with a `.`-prefixed segment below the prefix are [hidden](/concept/moq-lit `Hidden: true`. An `OriginProducer` from `moq.NewOriginProducer` has no `Close`: its origin -ends when the garbage collector reaches the last producer, and every consumer -and `OriginDynamic` made from it then fails with `moq.ErrClosed`. Keep the -producer reachable (a field on a long-lived struct, or `runtime.KeepAlive`) for -as long as the origin should serve. +ends when the garbage collector reaches the last owner (each producer, published +broadcast, and `OriginDynamic`), and every consumer made from it then fails with +`moq.ErrClosed`. Keep an owner reachable (a field on a long-lived struct, or +`runtime.KeepAlive`) for as long as the origin should serve. Every call that can block takes a `context.Context` first. Cancelling it returns `ctx.Err()` promptly and tears the in-flight native work down, so a diff --git a/doc/lib/js/hang.md b/doc/lib/js/hang.md index 07d9325f5b..3487eeb136 100644 --- a/doc/lib/js/hang.md +++ b/doc/lib/js/hang.md @@ -22,7 +22,8 @@ import * as Container from "@moq/hang/container"; `Catalog.watch(broadcast)` iterates validated catalog roots. It throws `Catalog.TooManyRenditions` for an update above the 64 rendition limit, and `Catalog.EscapingBroadcast` for a `broadcast` reference that walks above the -handle's `path`. +handle's `path`. Run the same checks on a catalog from another source with +`Catalog.checkRenditions(root)` and `Catalog.checkResolvable(root, base)`. `Hang.Timeline.Consumer.subscribe(broadcast, root.archive)` reads segment `push`, `pop`, and `skip` events when a root advertises an archive. diff --git a/doc/lib/js/publish.md b/doc/lib/js/publish.md index d475c613da..0a0f392ee9 100644 --- a/doc/lib/js/publish.md +++ b/doc/lib/js/publish.md @@ -31,7 +31,7 @@ WebCodecs, writes the catalog, and publishes a hang broadcast. | `source` | `camera`, `screen`, or `file`. | | `muted`, `invisible` | Disable audio or video capture. | | `preview` | What the nested element shows: the raw `source` (default), a decoded copy of the `encoded` stream to see what viewers get, or `none`. | -| `announce` | When to advertise: once a `source` is live (default), `always`, or `never`. A camera source waits for every enabled track. The broadcast is created while connected either way, but nobody can see or subscribe to it until it is announced. | +| `announce` | When to advertise: once a `source` is live (default), `always`, or `never`. A camera source waits for every enabled track, and `source` waits until each captured track's config resolves or fails, so the first catalog lists every rendition. The broadcast is created while connected either way, but nobody can see or subscribe to it until it is announced. | A nested `