MoqtClient. Every SDK ships it, with method names in that
language’s idiomatic case. You publish and subscribe the same way in
each SDK. The relay handles connect, backoff, and reconnect for you.
Track kinds
A track is addressed by a namespace + name (e.g.
robot/turtlebot4-001
odom). A track also carries a capability string. A capability is an open-ended routing intent ("asr","tts","ros.telemetry","media.passthrough"). The relay / gateway routes the track to whatever registered that capability. You route on intent, not on media kind.
Connect
connect returns immediately and dials in the background. If the link
drops, the client auto-reconnects with capped exponential backoff.
It re-establishes every publication and subscription on each
reconnect. Your code does nothing. on_state reports the lifecycle:
You may publish or subscribe before the session is up. The client
queues the calls and replays them after connection. Also, the relay
holds a subscribe for a namespace that is not yet announced. Thus a
robot can subscribe to its command track before any controller connects.
Publish & subscribe a frame track
Frame tracks carry opaque binary objects with a per-frame priority. They are the correct fit for robot telemetry and game state. The example below shows a publisher that streams a track and a subscriber that receives it. The relay fans the track out.Keep the subscription handle alive for as long as you want frames. In
garbage-collected languages (Python/Java/C#), the handle owns the native
callback. If the handle is collected, the engine calls into freed
memory. The publication handle owns the track. Drop the handle (or call
close) to stop the publication.Audio tracks
Audio tracks have the same shape, with codec metadata instead of a priority. The methods arepublish_audio(ns, name, capability, sample_rate, channels, frame_ms)
and subscribe_audio(ns, name, on_frame). on_frame(ts_us, bytes)
delivers one decoded object. Use audio tracks for live voice between an
SDK client and an agent. The capability (“asr”, “tts”, …) routes the
track to the correct module.
Namespacing & routing
- Telemetry vs commands: give command (inbound) tracks a distinct
namespace from telemetry. For example, publish telemetry under
robot/<id>and subscribe to commands underrobot/<id>/ctl. If both sharerobot/<id>, the relay can route a command subscription to the robot’s own telemetry announce. - Capability is the routing key. Two publishers may use different capabilities on the same namespace. Subscribers and modules select by intent.
- Fan-out is free. The relay copies each object to every subscriber. The publisher opens one stream per group, regardless of subscriber count.
How it works
You get one publish/subscribe API in every SDK, but two runtimes sit behind it. The C++, Python, and Go SDKs share one engine via theclutchcall_moqt_ffi native library. The TypeScript SDK is a
standalone WebTransport implementation with its own queueing, late-join,
and reconnect behaviour. Treat the two families as separate conformance
surfaces. A result on one does not automatically carry to the other,
until they are held to a common behaviour contract. The
publish/subscribe APIs match, but queue depth, filter handling, and
unsubscribe lifecycle can differ.
See the per-language SDK reference for the full method list, and
Architecture for how the relay mesh fans tracks out.
