Networking & Transports
iOS transport layer — Local (Bonjour), WebRTC, and Relay transport implementations.
The iOS app manages three transport layers for connecting to the desktop, orchestrated by DeviceTransportManager.
Transport Protocol
All transports conform to the Transport protocol:
protocol Transport {
var isConnected: Bool { get }
func connect()
func disconnect()
func send(_ data: Data)
var onReceive: ((Data) -> Void)? { get set }
var onStatusChange: ((TransportStatus) -> Void)? { get set }
}Transport Priority
- Local (Bonjour) —
LocalTransport.swift - WebRTC —
WebRTCTransport.swift - Relay —
RelayClient.swift
The app sends via the best available transport but receives from all active transports.
Local Transport (Bonjour)
LocalTransport.swift handles direct LAN connections:
- Discovery: Uses
NWBrowserto find_termpod._tcpservices on the local network - Resolution:
NWConnectionresolves Bonjour endpoint to IP + port - Connection: Establishes WebSocket connection (
ws://) - Auth: Sends auth secret (obtained via relay Device WS) as first message
- Subscription: Sends
subscribe_sessionfor each session to view
Important: Must strip %en0 interface scope from the resolved IP before building the WebSocket URL.
WebRTC Transport
WebRTCTransport.swift handles peer-to-peer connections via DataChannel:
- Uses
livekit/webrtc-xcframework(LKRTC* prefix classes) - Gated behind
canImport(LiveKitWebRTC)for conditional compilation - Signaling flows through the relay Device WS:
- Desktop sends
webrtc_offervia Device WS - iOS creates answer, sends
webrtc_answerback - ICE candidates exchanged via
webrtc_icemessages - DataChannel opens for multiplexed binary data + JSON control
- Desktop sends
ICE Servers:
- Google STUN:
stun:stun.l.google.com:19302,stun:stun1.l.google.com:19302 - Cloudflare STUN:
stun:stun.cloudflare.com:3478 - Optional Cloudflare TURN (Pro tier): Fetched from
GET /turn-credentials
Relay Transport
RelayClient.swift handles WebSocket connections through the Cloudflare relay:
- Connects to the Device WS endpoint (
/devices/:deviceId/ws?token=JWT) - Handles auth handshake and hello exchange
- Receives multiplexed binary frames for subscribed sessions
- Always connected for control plane (signaling, session management)
- Terminal data forwarding requires Pro subscription on hosted relay
DeviceTransportManager
DeviceTransportManager.swift orchestrates all three transports:
- Spawns all transports concurrently on connection
- Each transport reports status independently
- Selects best connected transport for sending (by priority)
- Routes incoming data from all transports to the connection handler
- Handles transport failures and fallback
ReconnectionManager
ReconnectionManager.swift implements exponential backoff:
- Starts with short delay, increases exponentially
- Caps at a maximum interval
- Resets on successful connection
- Handles transport-specific reconnection logic
Connection Flow
App Launch
│
├── Auth (JWT from Keychain)
│
├── DeviceTransportManager.connect()
│ ├── LocalTransport → Bonjour browse
│ ├── WebRTCTransport → signaling via Device WS
│ └── RelayClient → Device WS connect
│
├── Best transport selected
│
├── E2E Key Exchange (ECDH via CryptoService)
│
└── Encrypted streaming begins