Skip to content

Architecture

The bridge is a small, stateless-per-call relay. It holds two WebSockets per call - the StandIn media bridge on one side, a Cartesia Line agent stream on the other - and mostly copies strings between them.

flowchart LR
    Teams["Microsoft Teams<br/>voice call"]
    StandIn["StandIn media bridge<br/>(hosted)<br/>joins the call, speaks<br/>the wire protocol"]
    Bridge["this bridge (Python)<br/>verifies HMAC, terminates<br/>the wire protocol,<br/>one WS per call"]
    Line["Cartesia Line agent<br/>your code on Cartesia's<br/>platform, one stream per call"]

    Teams <--> StandIn
    StandIn -- "HMAC WebSocket, 16 kHz" --> Bridge
    Bridge -- "Line WebSocket, pcm_16000" --> Line
    Bridge -. "relay audio verbatim,<br/>map clear to barge-in,<br/>forward context, run governors" .-> Line

The StandIn media bridge handles everything about Teams itself and exposes each call as a single WebSocket carrying audio.frame (PCM 16 kHz) and control messages. This bridge has no idea what is on the other end of the Teams call - it only speaks the wire protocol. The Line agent’s brain is your code on Cartesia’s platform - this bridge only speaks the Line WebSocket API.

The StandIn wire is base64 PCM 16 kHz, 16-bit, mono. The Line stream is pinned to pcm_16000, and agent audio returns over the same single-format stream config - so the hot path relays the base64 string itself: caller audio is re-wrapped into media_input without decoding, agent audio from media_output goes onto the wire untouched, and frame timestamps are computed from string length + padding (no decode). This is stronger than the sibling bridges’ copy-only property (they at least re-frame base64 to binary).

One caveat, verified defensively rather than by documentation: Cartesia’s docs imply but do not state that media_output mirrors config.input_format. The session logs the first agent frame’s byte length and warns when it cannot be 16-bit PCM, so a format surprise is visible in the logs on the first live call.

flowchart TD
    A["upgrade + HMAC verify<br/>401 on bad / replayed / stale signature<br/>409 if callId already live"]
    B["session.start<br/>callId cross-checked vs the URL<br/>10s pre-start timeout if it never arrives"]
    C["mint per-call access token, connect Line<br/>send start: pcm_16000, overrides, caller metadata<br/>audio buffered until the server ack (10s bound)"]
    D["relay<br/>audio.frame &lt;-&gt; media events, verbatim payloads<br/>clear -&gt; assistant.cancel<br/>participants / speaker / recording -&gt; custom events<br/>dtmf native"]
    E["teardown<br/>worker close, agent close, session.end,<br/>governor, or drain<br/>closes BOTH sockets once, clears timers,<br/>records call duration, de-registers the call"]

    A --> B --> C --> D --> E

Ordering contract: the start event is the first message on the Line socket, and no caller audio flows until the server acks (buffered up to ~5 s, bounded, then flushed oldest-first; a 10 s ack timeout ends the call rather than leaving it silent). The ack also triggers one call_context snapshot so the agent code learns the initial participant count and recording state.

Barge-in: Line sends clear when playback should be flushed (the caller barged in); the bridge mirrors it to the Teams side with assistant.cancel. There is deliberately no ghost filter here: clear arrives in-band on the same ordered socket, so any media_output after it is genuinely new speech - unlike the Deepgram sibling, where the barge-in signal races pipelined TTS frames.

ModuleResponsibility
server.pyaiohttp server + WS upgrade, HMAC validation, connection guards (caps, replay, pre-start, dup-callId 409), session registry, drain
session.pyOne call: the StandIn WS ⇄ Line WS relay, ack gate, context events, governors, goodbye, flush-aware teardown
cartesia.pyLine socket (token mint, start/ack, media events, keepalive pings), start builder, Sonic TTS for the goodbye
protocol.pyWire message parsing (JSON, camelCase, discriminated on type) + PCM duration helper
hmac_auth.pyHMAC-SHA256("{timestampMs}.{callId}") sign/verify (constant-time, never raises on malformed input), header names, freshness
config.pyEnv config, fail-loud numeric parsing, *.cartesia.ai allowlist
cli.pyCLI entry point, .env loader, SIGTERM/SIGINT drain, friendly startup errors
log.py / metrics.pyMinimal leveled logger; Prometheus counters + call-duration histogram
LayerProtection
Upgrade authHMAC-SHA256("{timestampMs}.{callId}"), constant-time compare, fails closed when the secret is unset
ReplaySingle-use (callId, ts, sig) guard within a 60 s freshness window (a zero window fails loud at startup)
Duplicate callA second live connection for the same callId is rejected (409) - no second billed agent stream
DoSMax connections (64), per-IP cap (default = total cap), 2 MB inbound frame cap, 1 MB outbound backpressure cap (hot-path audio only - control frames and goodbye audio always pass), 10 s pre-start timeout, 90 s dead-peer window
Key hygieneCARTESIA_API_KEY is used only over HTTPS to *.cartesia.ai (token mint + TTS); each agent socket authenticates with a short-lived, agent-scoped access token
Input boundsWorker frames are validated per type (an audio.frame without a payload, a participants without a count, a dtmf without a digit are dropped, not relayed malformed)
Crash safetyEvery async entry point is guarded so a single malformed frame from either peer cannot take the process down
ShutdownThe CLI’s signal drain (or your server.drain() / server.close()) ends live calls gracefully, letting an in-progress goodbye finish; native TLS pins a 1.2 minimum