---
url: https://doc.moq.dev/lib.md
description: MoQ libraries for Rust, TypeScript, C, Python, Kotlin, Swift, Go, and Dart
---

# 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 |
| --- | --- | --- |
|  [Rust](/lib/rs/) | `moq-net`, `hang`, and friends on crates.io | Servers, CLIs, native apps, anything that needs the full stack including hardware codecs. |
|  [TypeScript](/lib/js/) | `@moq/*` on npm | Browsers (WebTransport + WebCodecs) and Node/Bun/Deno. |
|  [Swift](/lib/swift/) | `Moq` via SwiftPM | iOS, iPadOS, macOS. |
|  [Kotlin](/lib/kt/) | `dev.moq:moq` on Maven Central | Android and the JVM. |
|  [Python](/lib/py/) | `moq-rs` on PyPI | Scripts, ML pipelines, voice agents. |
|  [Go](/lib/go/) | `moq.dev/moq` | Go services and tooling. |
|  [Dart](/lib/dart/) | `moq` on pub.dev | Flutter apps. |
|  [C](/lib/c/) | `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](https://mozilla.github.io/uniffi-rs/) 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`/`epoch` reporting 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_broadcast` then `announce` / `unannounce` (a broadcast is invisible to local consumers and peers alike until announced), or claim a path prefix with `dynamic(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 `video` and `audio`, and read others' back.
* **Routes**: see which relays a broadcast came through, and advertise a cost as a standby publisher: warm `cost` for what pulling costs today, undiscounted `cold` for what it costs with nothing cached (omit `cold` to 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.
