The shape
How your data moves: MoQT tracks
Everything you publish reduces to MoQT tracks, and this is why you never manage delivery yourself. You name a track by (namespace, name), attach a capability tag, and write frames. The relay fans the track out to every subscriber that matched the namespace + capability. You don’t know who’s subscribed; a subscriber doesn’t know who you are. Add or drop consumers at any time — your publish code never changes.| Track kind | Carries | Wire model |
|---|---|---|
| Audio | Continuous voice (Opus / PCM / G.711) | subgroup stream |
| Video | Encoded video; group per keyframe | subgroup stream |
| Frame | Opaque binary with per-frame priority | subgroup OR datagram (datagram=true) |
| Text | Reliable ordered messages | subgroup stream |
The relay mesh
The relay is what makes your fan-out scale without you tuning anything. It’s the same binary as the engine, with the built-in relay role enabled, and it runs shard-per-core: one shard per core, each shard binds the same UDP port withSO_REUSEPORT
and an eBPF program dispatches incoming QUIC packets to the shard that
owns the connection’s destination CID. Each shard:
- accepts QUIC handshakes (ECDSA P-256)
- maintains its share of MoQT sessions
- forwards published frames to subscribers without leaving the shard
- gates each publish/subscribe through the namespace auth hook (JWT verify with namespace-scoped claims)
relay.telequick.dev and
by the per-edge POP code (relay-us.telequick.dev,
relay-uk.telequick.dev, …).
Two ports, two stacks
The relay runs two QUIC stacks on two ports rather than multiplexing on one:| Port | Stack | Carries |
|---|---|---|
| 443 | QUIC · MoQT | MoQT (audio / video / frame / text tracks) |
| 4433 | QUIC · HTTP/3 | HTTP/3 + WebTransport (REST, MCP, signalling) |
Why every language behaves the same
Whatever language you build in, you get identical wire behavior and identical latency — because there’s one core underneath all of them. Two things follow from that. Identical wire envelopes. The relay only ever sees the same MoQT framing regardless of which SDK published, so a message you send from Python looks the same as one from Rust. There’s no per-language parser to keep in sync — only the C++ core, which every SDK imports via FFI / WASM. Audio without a copy. µ-law / A-law to 16-bit PCM conversion is done in SIMD inside the core, so your voice path stays fast everywhere. Browser (WASM) and Node / Python / Go / Rust / Java / .NET (native FFI) call into the same code path with the same latency profile.What happens when you connect
You callconnect once; here’s what the SDK does for you underneath.
- Connect. The SDK dials MoQT on
relay.telequick.dev:443over QUIC (or WebTransport on browser). Your tenant token is presented as the first MoQT envelope. - Handshake. The relay’s namespace_auth hook verifies the token and stamps a namespace scope on the session.
- Publish / subscribe. The SDK opens MoQT publish and subscribe requests on demand. The relay routes by namespace and capability.
- Auto-reconnect. You don’t write reconnect logic. If the link drops, the SDK reconnects with capped exponential backoff and re-establishes every publication and subscription transparently. Your application code never sees it.
Where your SDK runs
The same core ships everywhere you deploy, so you get the same behavior on every runtime.- Browsers. The TypeScript SDK speaks MoQT directly over native WebTransport (no FFI, no custom framing). The C++ core is compiled to WebAssembly via Emscripten for audio APM + framing fast paths.
- Native runtimes. The SDK loads
telequick_core_ffi.so/.dylib/.dllvia the language’s native loader: JNI (Java), P/Invoke (.NET), CGO (Go),libloading(Rust),ctypes(Python). - Unity. The .NET runtime SDK plus a
com.telequick.transportUPM package exposesINetworkInterfaceover the games modality — drop-in for Unity Netcode for GameObjects / Entities. See Netcode (Unity).
Direct-media (voice)
When you run a server-side AI call (default_app=AI_BIDIRECTIONAL_STREAM),
your audio takes the shortest path: the
voice path uses direct-media between the carrier and the agent
runtime. The gateway negotiates SIP signalling with the carrier, then
publishes the SDP answer pointing at the agent runtime’s RTP socket;
RTP flows straight from the carrier to the runtime. The gateway is
signalling-only on that path.
For SIP/RTP-only calls (no AI bridge), the gateway still terminates RTP
and runs a local VAD. So the same call_sid can take either RTP
path depending on default_app.
Legacy RPC (still supported)
The original control plane was a method-id RPC envelope over QUIC:TeleQuickClient (dial, originate_bulk, hangup, barge,
push_audio, …) used this surface. It’s kept for backwards compat;
new code should use the Voice modality. See
Envelope Format for the full wire detail.
Code generation
This is why the SDKs stay in lockstep for you across languages: method IDs and DTO definitions in every language come from one IDL (api/telequick.json), compiled by a shared IDL code generator. The
modality clients’ wire formats are generated from that same
IDL, so a new modality method only needs an IDL edit plus a compiler run —
and it lands in every language at once.