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
2 changes: 1 addition & 1 deletion .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ jobs:
recipe: pub check-bbb
uncached: true
- name: Browser backend baseline
recipe: test wasm-baseline
recipe: test wasm-baseline --uniffi
chromium: true
artifact: wasm-backend-baseline
artifact-path: target/test
Expand Down
7 changes: 4 additions & 3 deletions bench/ffi-gen/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,8 @@ BoltFFI's JS glue is its generated web module plus `@boltffi/runtime`, unminifie
`.github/workflows/platform.yml` (`macos-latest`, beside `just swift check`). The
harness has no Swift runner yet.
- **UniFFI's JS glue.** ubrn, UniFFI's JS generator, does not support 0.32 yet, so only
its `.wasm` is sized. Browser per-call cost is
[#5478](https://github.com/moq-dev/moq/pull/5478)'s job.
its `.wasm` is sized. The browser comparison, including its per-call cost, is
[`test/wasm/generator.md`](/test/wasm/generator.md).
- **The full moq-ffi through BoltFFI.** BoltFFI cannot bind moq-ffi without the API
changes listed below, so its numbers come from the slice alone.
- Go, Dart, C++, and C, which BoltFFI lacks or has only in part. Startup and memory.
Expand Down Expand Up @@ -233,4 +233,5 @@ bulk copies), not from its core. A UniFFI fork can adopt each one backend at a t
using these numbers as targets, while keeping all eight targets working throughout.

Pick BoltFFI instead if call overhead outweighs everything else and writing the Go and
C++ backends is acceptable. The browser half is decided separately in #5478.
C++ backends is acceptable. The browser half was decided separately: UniFFI over ubrn
([#5838](https://github.com/moq-dev/moq/pull/5838), [`test/wasm/generator.md`](/test/wasm/generator.md)).
6 changes: 6 additions & 0 deletions bun.lock

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

1 change: 1 addition & 0 deletions quest/m1/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ lands before the cut.
- [Close tail drain](/quest/m1/close-tail-drain.md) - after a peer close, subscribers read the group streams their transport already delivered instead of aborting them, so a finished tail arrives on every version
- [Publisher linger](/quest/m1/publisher-linger.md) - every finishing publisher keeps its broadcast up for the media max-age so subscribers can drain it
- [Exporters follow a rendition to a new track](/quest/m1/export-rendition-rebind.md) - the fMP4, TS, MKV and FLV exporters rebuild a rendition whose resolved `track` or `broadcast` changes under the same ID
- [lite-06 max sequence](/quest/m1/lite06-max-sequence.md) - group sequence 2^62-1 reaches a lite-06 subscriber or is refused loudly, instead of vanishing

- [C++ and OBS package proof](/quest/m1/cpp-package-check.md) - the installed artifacts pass before their first release

Expand Down
23 changes: 23 additions & 0 deletions quest/m1/lite06-max-sequence.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# [S] lite-06 drops group sequence 2^62-1 silently

## Goal

A group whose sequence is 2^62-1, the largest QUIC varint, either reaches a moq-lite-06
subscriber through the relay or is refused loudly where it cannot be carried. Today the
publisher accepts it and the subscriber waits forever, while moq-lite-02 and
moq-transport-19 deliver it and 2^53+1 crosses all three.

## Plan

Found by the browser binding spike (#5838): `test/wasm/uniffi/cases.ts` publishes the
ladder 5, 2^32, 2^53-1, 2^53+1, 2^62-1 from one moq-ffi session to another through each
relay flavour. On lite-06 it records 2^62-1 as a known gap, and the case fails once the
gap closes.

Reproduce natively first, with a moq-net test over the memory transport and then through
moq-relay, to find whether the publisher, the relay, or the subscriber drops it. Fail
loud if the value is illegal on lite-06; otherwise deliver it. Land a regression test
with the fix, then remove the browser case's known-gap branch.

Public API: none expected. Wire: none if the value is legal; otherwise the refusal
matches the published draft.
4 changes: 4 additions & 0 deletions quest/m2/ffi-bytes.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ wrapper (Python, Go, Kotlin, Swift, Dart, and C++).

Decided with the maintainer on 2026-10-13:

- **The browser too.** Since 2026-10-17 (#5838) the browser binds moq-ffi through
UniFFI over ubrn, and pays the same per-byte path: about 7 ns per byte written and
8 ns read at 128 KiB in Chromium, against BoltFFI's 0.5 and 1.3. Re-measure with
`test/wasm/uniffi/cases.ts`'s per-call timings once the fork's core changes.
- **Benchmark first.** `just bench-ffi` ([bench/README.md](/bench/README.md))
measures every wrapper before and after. Add a Rust microbenchmark of
UniFFI's lift and lower for `Vec<u8>` here, beside the patch it proves.
Expand Down
10 changes: 9 additions & 1 deletion quest/m2/rs2ts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@ for a source change. A batched boundary is an optimization candidate, not a
reason to require a full sans-IO rewrite. Controlled time remains independently
useful; source-level `Instant` mappings depend on the chosen backend.

Decided with the maintainer on 2026-10-17
([#5838](https://github.com/moq-dev/moq/pull/5838)): the WASM binding is moq-ffi
generated by UniFFI over ubrn's `wasm2` player. It won over hand-written moq-wasm and
BoltFFI on maintenance (one surface for every language, no API break) and parity. Its
measured costs, size and payload copies, have their own quests. The report is
[`test/wasm/generator.md`](/test/wasm/generator.md).

Evidence from [#5337](https://github.com/moq-dev/moq/issues/5337) motivates
reopening the options, not choosing a winner. Its attachment reports the
original async Rust consumer passing nine browser tests in both WASM and
Expand All @@ -61,8 +68,9 @@ any necessary breaks. Wire: no changes intended.

## Required

- [Binding generator](/quest/m2/rs2ts/generator.md) - the maintainer picks UniFFI- or BoltFFI-generated moq-ffi, or hand-written moq-wasm, from measurements
- [Complete WASM bindings](/quest/m2/rs2ts/wasm-bindings.md) - Rust publishing and consumption cover the browser protocol and model surface
- [Net-only moq-ffi](/quest/m2/rs2ts/net-only.md) - the browser `.wasm` drops the media layer, near moq-wasm's size
- [ubrn reads UniFFI 0.32](/quest/m2/rs2ts/ubrn-032.md) - waiting on upstream, so the browser TypeScript generates without the measurement pin
- [Adopt WASM in @moq/net](/quest/m2/rs2ts/wasm-adopt.md) - the TypeScript interface uses Rust, with measured tradeoffs accepted before replacement

## Related
Expand Down
103 changes: 0 additions & 103 deletions quest/m2/rs2ts/generator.md

This file was deleted.

30 changes: 30 additions & 0 deletions quest/m2/rs2ts/net-only.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# [M] A net-only moq-ffi build for the browser

## Goal

The browser's moq-ffi `.wasm` carries only the network surface `@moq/net` wraps, so its
shipped size lands near moq-wasm's (about 740 KB gzip on 2026-10-16) instead of the full
build's 1.70 MB, stripped. Measured with `bun test/wasm/uniffi/size.ts` and the
baseline sweep's asset sizes.

## Plan

Decided with the maintainer on 2026-10-17 (#5838): the browser binds moq-ffi through
UniFFI, and size was the choice's largest cost. The report
(`test/wasm/generator.md`) attributes about 30% of moq-ffi's code to the media layer:
mp4\_atom, moq\_mux, `hang`'s regex crates, and the JSON crates. A slice that consumes and
publishes over WebTransport landed within 3% of moq-wasm under UniFFI, so the generator
itself adds little.

Use a cargo feature on moq-ffi, not a new crate: #5460 rejected per-layer crate splits.
The UniFFI exports the linker keeps are the ones the feature compiles in. Toggling
`default-features` changes nothing on wasm32, because `audio` and `video` are already
native-only. Also strip the `name` section when staging, worth about 235 KB gzip.

Public API: a new moq-ffi cargo feature; the bound surface is unchanged where it is
enabled. Wire: none.

## Related

- [Adopt WASM in @moq/net](/quest/m2/rs2ts/wasm-adopt.md) - weighs the shipped size
- [Bindings size profile](/quest/m2/ffi-size-profile.md) - the native builds' size question
30 changes: 30 additions & 0 deletions quest/m2/rs2ts/ubrn-032.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# [XS] Waiting: ubrn generates UniFFI 0.32 TypeScript

## Goal

uniffi-bindgen-react-native (ubrn) ships a release whose `wasm2` flavour reads UniFFI
0.32 metadata, so moq-ffi's browser TypeScript generates from the published crate
without `test/wasm/uniffi/pin.patch`. Check: whether
[ubrn#468](https://github.com/jhugman/uniffi-bindgen-react-native/pull/468) has merged and
a release after 0.31.0-6 lists it. Delete this quest once the pin is gone.

## Plan

Decided with the maintainer on 2026-10-17 (#5838): the browser binds moq-ffi through
UniFFI over ubrn's `wasm2` player. ubrn pins UniFFI 0.31; moq-ffi is on 0.32, and the
harness pins a scratch checkout to 0.31 for measurement only.

If ubrn has not moved by the time adoption is otherwise ready, carry ubrn's TypeScript
generator in [the UniFFI fork](/quest/m2/uniffi-fork.md) instead, and delete this quest.

When raising it upstream, also report two costs the spike measured. Posting outside
`moq-dev` and `kixelated` needs the maintainer's approval.

- `@ubjs/core` arms and clears a `setTimeout` keep-alive on every async call, which only
Node.js needs.
- The generated `*Like` return types omit `uniffiDestroy`, so freeing a returned handle
early needs a cast to the class.

## Related

- [Adopt WASM in @moq/net](/quest/m2/rs2ts/wasm-adopt.md) - ships the generated TypeScript this unblocks
5 changes: 5 additions & 0 deletions quest/m2/rs2ts/wasm-adopt.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,15 @@ Update watch, publish, hang, room, demos, and other affected consumers together.
in existing API docs and package READMEs, together with regeneration.
- Retain baseline revisions and shared tests after removing the handwritten
implementation. Keep the WASM backend while the JS-only stage is evaluated.
- Delete `rs/moq-wasm` with the handwritten code: on 2026-10-17 (#5838) the
maintainer chose UniFFI-generated moq-ffi as the browser binding, so moq-wasm
is only the baseline's reference until then.

Public API: `@moq/net` remains the package; enumerate any approved signature,
initialization, lifetime, or deployment changes. Wire: none.

## Required

- [Complete WASM bindings](/quest/m2/rs2ts/wasm-bindings.md) - the complete Rust-backed candidate and parity coverage
- [ubrn reads UniFFI 0.32](/quest/m2/rs2ts/ubrn-032.md) - the TypeScript generates from the published moq-ffi, without the measurement pin
- [Net-only moq-ffi](/quest/m2/rs2ts/net-only.md) - the browser `.wasm` drops the media layer, near moq-wasm's size
18 changes: 12 additions & 6 deletions quest/m2/rs2ts/wasm-bindings.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,18 @@ close/error behavior, cancellation, and backpressure. Validate supported
versions and transports with the browser fixtures and `just test interop --all`.
Wire new tests into CI, at least nightly.

Complete the binding the [generator quest](/quest/m2/rs2ts/generator.md)
chose: `moq-ffi` through UniFFI or BoltFFI, or the consume-only `moq-wasm`. Measure boundary calls, Promise allocation,
Decided with the maintainer on 2026-10-17 (#5838, report in
[`test/wasm/generator.md`](/test/wasm/generator.md)): complete `moq-ffi` through UniFFI
over ubrn's `wasm2` player, not the hand-written `moq-wasm` and not BoltFFI. This quest
completes moq-ffi's browser parity and generates its TypeScript in CI, extending
`test/wasm/uniffi/` (`just test wasm-baseline --uniffi`, nightly). `moq-wasm`'s deletion
moves to [adoption](/quest/m2/rs2ts/wasm-adopt.md). Until
[ubrn reads UniFFI 0.32](/quest/m2/rs2ts/ubrn-032.md), generation keeps applying
`test/wasm/uniffi/pin.patch` to a scratch checkout.

Gaps the spike found against `@moq/net` and moq-wasm: moq-ffi exposes no negotiated
version and has no WebSocket fallback on wasm, and payload copies cost about 7 to 8 ns
per byte (see [Payload bytes](/quest/m2/ffi-bytes.md)). Measure boundary calls, Promise allocation,
payload copies, and retained memory. Compare a batched poll/event-drain
boundary if these dominate, including latency and backpressure effects;
do not require a sans-IO rewrite or impose batching latency to win throughput.
Expand All @@ -34,7 +44,3 @@ Update the bindings' README and existing API docs for the surface added.

Public API: extends the unpublished browser bindings; any changes needed to
`@moq/net` are proposed in the adoption quest. Wire: none.

## Required

- [Binding generator](/quest/m2/rs2ts/generator.md) - which binding this completes
2 changes: 1 addition & 1 deletion test/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@
"noEmit": true
},
"include": ["**/*", "../js/common/worklet.d.ts"],
"exclude": ["**/node_modules", "**/dist", "**/target"]
"exclude": ["**/node_modules", "**/dist", "**/target", "wasm/uniffi"]
}
12 changes: 12 additions & 0 deletions test/wasm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,3 +172,15 @@ WASM-to-JS measurements are separate, unverified results here. Full network
sequences above 2^53 need a native publisher and a JS model that can represent
them before adoption. WASM frame timestamps and complete publish/backpressure
API parity remain explicit gaps too.

## Generated bindings

```bash
just test wasm-baseline --uniffi
```

`--uniffi` adds moq-ffi, bound by UniFFI over ubrn's `wasm2` player, as a third
baseline backend. It also runs parity cases and per-call timings in `uniffi/` against
every relay, once normally and once under a strict CSP. It builds from a scratch
checkout, since ubrn needs a UniFFI pin that does not land.
[generator.md](generator.md) is the report that chose this binding.
Loading
Loading