Libraries
Two primary implementations and six bindings, all speaking the same wire protocol. A publisher in Python is consumable by a subscriber in Swift.
| Language | Package | Best for |
|---|---|---|
moq-net, hang, and friends on crates.io | Servers, CLIs, native apps, anything that needs the full stack including hardware codecs. | |
@moq/* on npm | Browsers (WebTransport + WebCodecs) and Node/Bun/Deno. | |
Moq via SwiftPM | iOS, iPadOS, macOS. | |
dev.moq:moq on Maven Central | Android and the JVM. | |
moq-rs on PyPI | Scripts, ML pipelines, voice agents. | |
moq.dev/moq | Go services and tooling. | |
moq on pub.dev | Flutter apps. | |
moq-c | C/C++ and any language with a C FFI. |
How they relate
Rust is the reference implementation; every server-side tool is built on it. TypeScript is a from-scratch browser implementation. The other six wrap the Rust core: Python, Kotlin, Swift, Go, and Dart are generated from one UniFFI crate (moq-ffi) and then wrapped in an idiomatic layer, while C gets a hand-written stable ABI (moq-c). A feature added to the core lands in all of them together.
What every binding can do
The wrapped bindings share one feature set, so the language pages only show how it looks in that language:
- Connect to a relay with TLS options (system roots, custom CA, fingerprint pinning, mTLS) and a JWT in the URL, or serve sessions yourself and accept or reject each request by path.
- Reconnect automatically with backoff when the transport drops, with
status/epochreporting each (re)connect and backoff tunable down to retrying forever. The peer's inbound QUIC stream limit is configurable for subscribe-heavy clients. - Discover broadcasts by prefix, wait for a specific one, or request one by path, including a path a prefix claim serves on demand. Advertise an exact path with
create_broadcastthenannounce/unannounce(a broadcast is invisible to local consumers and peers alike until announced), or claim a path prefix withdynamic(prefix, route). - Publish and subscribe to media with the hang catalog filled in from the bitstream, plus raw pixels or PCM in and out with the codec running inside the binding (VideoToolbox, Media Foundation, NVENC, openh264, Opus). A publisher follows the connection's send estimate through
session.bandwidth(): reserve a share for an app-owned encoder, or pass the handle when encoding so the built-in video encoder follows the grant. - Connection health.
stats()snapshots RTT, send/receive estimates, and byte/packet counters.bandwidth()divides that send estimate among tracks sharing the connection. - Raw tracks of arbitrary bytes with timestamps, sparse or replayed groups, per-subscriber priority and max age, and best-effort datagrams.
- JSON tracks in snapshot mode (latest value, merge-patch deltas, optional compression) or stream mode (append log).
- Fetch a single group by sequence from the cache, decoded through the container or raw.
- Serve on demand: accept track and broadcast requests as they arrive instead of publishing up front.
- Catalog extensions: write your own section next to
videoandaudio, and read others' back. - Routes: see which relays a broadcast came through, and advertise a cost as a standby publisher: warm
costfor what pulling costs today, undiscountedcoldfor what it costs with nothing cached (omitcoldto price both alike). - Errors distinguish auth rejection (don't retry) from shutdown (expected) from transport failure. A protocol error carries the peer's session or stream code, a known kind when recognized, and keeps an application or unknown code without loss.
Dart is the exception on codecs: its published binaries carry no encoder or decoder, so it moves already-encoded frames.