MoQ Streaming with Fishjam
How Media over QUIC (MoQ) works in Fishjam
What is MoQ?
Media over QUIC (MoQ) is a new internet standard for live media delivery, designed from the ground up for scalable, low-latency streaming to large audiences.
Unlike WebRTC — which was primarily built for interactive, peer-to-peer conferencing — MoQ is optimized for the one-to-many broadcast model: one publisher, potentially thousands of simultaneous subscribers, all receiving the stream with minimal delay.
A few properties make MoQ stand out:
- Built on QUIC. QUIC is a modern transport protocol that eliminates head-of-line blocking, recovers from packet loss more gracefully than TCP, and establishes connections faster. For live video, this means more resilient delivery at low latency.
- Standardized negotiation. Because MoQ defines a common signaling and subscription protocol, any MoQ-compliant client can connect to any MoQ-compliant relay — you are not locked into a proprietary stack.
- Relay-based architecture. The relay is a first-class part of the MoQ protocol, not an add-on. Because relaying is built into the protocol's design, scaling delivery to large audiences is a native capability.
Fishjam also supports WebRTC-based livestreaming (WHIP/WHEP). See Livestreams for that approach.
How MoQ Works in Fishjam
Fishjam provides a MoQ relay that publishers push media to and subscribers pull media from.
Publisher → Fishjam MoQ Relay → Subscriber(s)
The relay is responsible for distributing the stream: it receives media from the publisher once and fans it out to every subscriber.
The publish/subscribe model
MoQ uses a publish/subscribe model:
- A publisher connects to the relay, announces a stream under a specific path, and starts sending media.
- Subscribers connect to the relay, discover announced paths, and receive the media.
The relay manages the flow between them. Neither side needs a direct connection to the other.
Paths
Every stream is identified by a path — a slash-separated string like my-room/alice-camera. Paths are used in two distinct ways:
- Addressing — a subscriber consumes an exact path to receive that specific stream (e.g.
my-room/alice-camera). - Discovery — a subscriber watches a prefix (e.g.
my-room) to learn which streams are currently live under it. This returns a live feed of announced paths — each of which must then be consumed individually. This is how you can display all participants in a room without knowing their paths in advance.
Note that consuming an exact path and discovering a prefix are separate operations. Consuming my-room directly would fail unless a publisher is broadcasting at that exact path.
Path Scoping
Every connection goes to relay.fishjam.io/<fishjam-id>. Your Fishjam ID is automatically used as the token's root namespace by the Fishjam Server — you never include it in publishPath or subscribePath; it is set for you. All paths you specify are relative to that root.
Path matching is prefix-based: a path of "stream-name" permits any broadcast whose full path starts with stream-name/, not just the exact string "stream-name".
Publisher paths
The publishPath you set determines how much freedom the broadcaster has when naming their broadcast:
- Broad path (
publishPath: "stream-name") — the client can publish as any sub-path understream-name, such asstream-name/aliceorstream-name/bob-camera. The client chooses its own identity; the relay only enforces the prefix. - Specific path (
publishPath: "stream-name/alice") — the client can only publish asstream-name/alice. If the broadcaster tries to usestream-name/bob, the relay rejects the announcement. This is how you enforce a broadcaster's identity from the server side.
Use the broad form when clients self-identify (e.g., users pick their own stream name). Use the specific form when your backend assigns identities (e.g., you issue a per-user token for a managed conference).
Subscriber paths
The subscribePath works the same way: it is a prefix that limits which broadcasts the subscriber can consume and discover.
- Broad path (
subscribePath: "stream-name") — the subscriber can consume any broadcast understream-name/and will surface all publishers in that namespace as they come and go. - Specific path (
subscribePath: "stream-name/alice") — the subscriber can only receive fromstream-name/alice. Broadcasts atstream-name/bobare invisible to this client.
Example: a multi-publisher room
A typical room setup uses a combination of both patterns:
- The backend issues each broadcaster a specific publisher token —
publishPath: "my-room/<user-id>"— so each user can only occupy their own slot. - The backend issues viewers a broad subscriber token —
subscribePath: "my-room"— so they discover and consume every broadcast in the room. - When a new publisher joins or leaves, the viewer is informed by the relay
Access Control: MoQ Tokens
Access to the relay is controlled by MoQ tokens — short-lived JWTs that are path-scoped:
| Token type | Grants | Typical recipient |
|---|---|---|
| Publisher token | Write access to a specific path | Streamer |
| Subscriber token | Read access to a path or namespace | Viewer |
A token is attached to the relay URL as a query parameter (?jwt=<token>). The relay validates the token and enforces its scope before allowing any media to flow.
Keeping publisher and subscriber tokens separate ensures that a viewer can never accidentally publish to the stream, and a publisher cannot subscribe to paths it does not own.
Getting Tokens
There are two ways to obtain MoQ tokens, depending on where you are in the development lifecycle.
Sandbox API (prototyping)
The Sandbox API is a ready-made backend provided by Fishjam for development and prototyping. It issues tokens without requiring you to build your own server, so you can start streaming immediately.
To get a publisher token, call:
GET https://fishjam.io/api/v1/connect/{FISHJAM_ID}/room-manager/moq/{PUBLISHER-PATH}/publisher
To get a subscriber token, call:
GET https://fishjam.io/api/v1/connect/{FISHJAM_ID}/room-manager/moq/{SUBSCRIBER-PATH}/subscriber
The Sandbox API is not intended for production — it has no authentication and is only available in the Sandbox environment. See What is the Sandbox API? for more context.
Fishjam Server SDK (production)
In production, your backend generates tokens using the Fishjam Server SDK. This gives you full control over who can publish and who can subscribe.
The SDK's createMoqToken method accepts either a publishPath or a subscribePath:
publishPath— issues a publisher token scoped to that path.subscribePath— issues a subscriber token scoped to that path or namespace prefix.
Your backend then delivers each token to the appropriate client (publisher or viewer), which uses it to connect to the relay.
See the MoQ Streaming tutorial for working code examples of both approaches.
See also
- MoQ Streaming tutorial — step-by-step guide to publishing and subscribing
- What is the Sandbox API? — when and why to use the Sandbox API
- Security & Token Model — broader overview of Fishjam's token system
- Livestreams — WebRTC-based livestreaming with WHIP/WHEP