Skip to content

feat: add TURN-TCP support with server-driven config#112

Merged
nagar-decart merged 7 commits intomainfrom
nagar-decart/sdk-turn-tcp
Apr 7, 2026
Merged

feat: add TURN-TCP support with server-driven config#112
nagar-decart merged 7 commits intomainfrom
nagar-decart/sdk-turn-tcp

Conversation

@nagar-decart
Copy link
Copy Markdown
Contributor

@nagar-decart nagar-decart commented Apr 5, 2026

Summary

  • Add TURN-over-TCP support for WebRTC fallback in restricted networks where UDP is blocked
  • SDK accepts server-driven turn_config via WebSocket signaling, giving full server-side control over TURN rollout
  • Add iceServers option to connect() for manual/internal testing without server-side changes
  • Remove hardcoded TURN from ICE_SERVERS — TURN config is now either server-driven or explicitly passed
  • Add Playwright E2E tests and vitest config for local k8s validation

Changes

  • webrtc-connection.ts: Handle turn_config message, merge server-driven + explicit ICE servers into PC config
  • webrtc-manager.ts / client.ts: Thread iceServers option from connect API to connection layer
  • types.ts: Add TurnConfigMessage type
  • e2e-turn-tcp.test.ts: E2E tests for relay-only and dual-mode paths (relay test skipped pending server-side aioice fix)
  • vitest.config.e2e-turn-tcp.ts: Vitest config for TURN-TCP tests

Test plan

  • pnpm typecheck clean
  • pnpm build succeeds
  • pnpm test — 141 unit tests pass
  • pnpm test:e2e:turn-tcp — dual-mode test passes against local k8s
  • Relay-only test blocked on server-side aioice TURN candidate generation

🤖 Generated with Claude Code


Note

Medium Risk
Changes WebRTC connection setup by introducing server-provided TURN configuration and new ICE options, which can affect connectivity and handshake timing across environments. Added tests reduce risk but the new signaling path and timing waits could introduce regressions in connection establishment.

Overview
Adds TURN/TURN-TCP support to realtime connections by letting connect() accept optional iceServers and an iceTransportPolicy hint (forwarded as a query param) and by consuming a new WebSocket turn_config signaling message.

Updates the WebRTC connection flow to merge default/manual ICE servers with server-provided TURN servers, optionally wait briefly for TURN config before creating the peer connection, and reset TURN state on cleanup.

Introduces Playwright-based E2E coverage for TURN scenarios via a new test:e2e:turn-tcp Vitest config/script, and excludes the new E2E test from the default unit test run.

Reviewed by Cursor Bugbot for commit 9bebe2d. Bugbot is set up for automated code reviews on this repo. Configure here.

Add TURN-over-TCP ICE server alongside existing STUN for networks
where UDP is blocked. ICE naturally prefers direct UDP and falls back
to TURN-TCP relay via coturn.

Includes Playwright E2E tests verifying both relay-only and dual-mode
paths against local k8s slim-bit-invert.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Handle `turn_config` WebSocket message from server to receive TURN
  servers dynamically (for production server-side control)
- Add `iceServers` option to connect() for manual/internal testing
- Remove hardcoded TURN from ICE_SERVERS (now server-driven or explicit)
- Skip relay-only E2E test until server-side aioice TURN allocation
  is verified

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@nagar-decart nagar-decart changed the title feat: add TURN-TCP support and E2E tests feat: add TURN-TCP support with server-driven config Apr 6, 2026
When the server sends turn_config after the PeerConnection is already
created (race: bouncer acks Phase 2 locally before upstream pump
delivers turn_config), update the PC's ICE servers via
setConfiguration() and trigger an ICE restart so TURN candidates
are gathered.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
nagar-decart and others added 2 commits April 6, 2026 17:04
When turn_config triggers an ICE restart, the answer to the original
offer may arrive after the new offer resets signaling state to stable.
Guard setRemoteDescription(answer) with a signalingState check to
drop stale answers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace ICE restart approach with a Phase 2.5 wait: after Phase 2
completes, wait up to 2s for turn_config to arrive before creating
the PeerConnection. This ensures TURN servers are included from the
start — one offer, one answer, no re-negotiation.

Also reverts the stale answer guard (no longer needed without ICE
restart).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 9348290. Configure here.

nagar-decart and others added 2 commits April 6, 2026 22:07
Forwards ice_transport_policy query param to the server, which filters
TURN URLs by transport (tcp/udp/all) and configures server-side ICE
accordingly.

Usage: client.realtime.connect(stream, { model, iceTransportPolicy: "tcp" })

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Only wait for turn_config in Phase 2.5 when iceTransportPolicy is
  set (server expected to send TURN config). Connections without TURN
  have zero added latency.
- Clean up turnConfig event handler after Promise.race resolves,
  preventing leaked handlers on reconnection cycles.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@nagar-decart nagar-decart merged commit 39b9f09 into main Apr 7, 2026
3 of 4 checks passed
@nagar-decart nagar-decart deleted the nagar-decart/sdk-turn-tcp branch April 7, 2026 09:19
nagar-decart added a commit that referenced this pull request Apr 7, 2026
nagar-decart added a commit that referenced this pull request Apr 7, 2026
…115)

Reverts #112. Merged by mistake — the SDK PR needs to land
together with the API PR (DecartAI/api#971).

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Changes the realtime/WebRTC connection setup by removing TURN server
configuration and related signaling handling, which can affect
connectivity in restricted NAT/firewall environments. Also deletes
associated E2E coverage, increasing the chance of regressions going
unnoticed.
> 
> **Overview**
> Reverts the previously added TURN-TCP support in the realtime SDK by
removing `iceServers`/`iceTransportPolicy` connect options and dropping
the `turn_config` signaling message path.
> 
> WebRTC peer connections now always use a fixed STUN-only `iceServers`
list, and the websocket URL no longer includes the
`ice_transport_policy` query param. The TURN-TCP E2E test/config and the
`test:e2e:turn-tcp` script are removed.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
5f31ec2. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant