Skip to main content
MoqTransport connects a PipecatClient to a Pipecat bot over Media over QUIC — either through a relay both sides dial, or directly to a bot serving its own socket. It publishes the microphone as an Opus broadcast and consumes the bot’s, discovering codec and sample rate from the bot’s catalog at connect time rather than pinning them in code. RTVI messages travel in both directions over a dedicated JSON stream track, so this is a full RTVI transport rather than an audio-only path. The transport automatically deduplicates messages across reconnects, preventing redelivery of messages such as client-ready when either side replays its log. Connection uses WebTransport with a WebSocket fallback (raced by @moq/net), with auto-reconnect via Connection.Reload. Once the session reaches ready, relay reconnects happen in the background without changing the state — messages sent during a reconnect are queued and replayed to the bot when it resubscribes.

Installation

Usage

Basic Setup

With the development runner

The Pipecat development runner returns everything the transport needs from POST /start, under a moq key — the relay URL, the certificate hash in serve mode, the namespace, and the participant ids. Pass it straight through:
Constructing the transport by hand, take care with clientId and botId. The defaults here are client0 and bot0, while a Pipecat bot publishes under response and listens on request. With defaults on both sides the two never find each other — so set clientId: "request" and botId: "response", or take the values from /start as above.

Self-signed development relay

A bot in serve mode mints its own certificate and reports the fingerprint. Pin it rather than disabling verification:

API Reference

Constructor Options

string
required
Full URL of the MoQ peer, e.g. https://relay.example.com:4080/moq.
string
default:"client0"
This client’s participant id. The client publishes under <namespace>/<clientId>.
string
default:"bot0"
The bot’s participant id. The client subscribes to <namespace>/<botId>.
string
default:"pipecat"
Top-level namespace, analogous to a room name.
WebTransportHash[]
Certificate hashes to pin, for a self-signed development relay. Same shape as WebTransport’s own serverCertificateHashes.
string
default:"transcript.json.z"
Track name for the bidirectional RTVI message channel. Discovered by convention rather than through the catalog, so it has to match the bot’s.
number
default:"80"
Jitter buffer floor, in milliseconds. Lower is more interactive and drops more on a poor network; higher is smoother at the cost of delay.
number | 'real-time'
default:"30000"
Ceiling for buffered playback. A bot writes TTS audio faster than real time with future-dated timestamps, and the player buffers it rather than skipping ahead — this caps how much may build up. An interruption flushes it early. Pass "real-time" to collapse to the floor instead, which only makes sense against a genuinely live publisher.
number
default:"48000"
Rate the client publishes its microphone at. One of Opus’s supported rates: 8000, 12000, 16000, 24000, or 48000. The bot reads this from the catalog and resamples, so exact agreement isn’t required.

Broadcast paths

Each side publishes on one path and subscribes to the other’s:
Audio track names within a broadcast come from the bot’s catalog, so they aren’t configured here. The transcript track is the exception — it’s a JSON stream rather than a media rendition, so the catalog doesn’t describe it and both sides agree on the name by convention.

Transcript records

Each transcript track is a single group that a subscriber reads from its first record, so a reconnect on either side, including the automatic one from @moq/net, replays the whole log. Every record the transport sends is the RTVI message plus seq, its position in the connection’s log, and epoch, an id minted per connection. Incoming records at or below the last seq accepted for the bot’s current epoch are dropped, a different epoch is a new bot whose count starts over, and both fields are removed before the message reaches PipecatClient. A record without seq is delivered unchanged, so a bot that predates the fields keeps working. The bot side of the same format is described with the server transport; acceptTranscriptRecord and the TranscriptRecord type are exported for other implementations of the stream. Each side also sends a session-ending marker on its transcript before it leaves. The bot’s tracks ending after the marker is a hangup and the transport disconnects. Ending without it may be a relay between the peers failing, which looks the same on the wire, so the transport redials: the bot either reappears on the new session or is never announced on it. disconnect() sends the marker from this side, so the bot ends the call at once.

Events

MoqTransport reports connection state through the standard PipecatClient callbacks — there are no MoQ-specific events.

More Information

Server-side MoQ transport

The bot half of the pair, including serve and relay modes

Choosing a transport

How MoQ compares to the other client transports