Files
siprouter/CLAUDE.md
Juergen Kunz f3e1c96872 initial commit — SIP B2BUA + WebRTC bridge with Rust codec engine
Full-featured SIP router with multi-provider trunking, browser softphone
via WebRTC, real-time Opus/G.722/PCM transcoding in Rust, RNNoise ML
noise suppression, Kokoro neural TTS announcements, and a Lit-based
web dashboard with live call monitoring and REST API.
2026-04-09 23:03:55 +00:00

2.2 KiB

Project Notes

Architecture: Hub Model (Call as Centerpiece)

All call logic lives in ts/call/. The Call is the central entity with N legs.

Key Files

  • ts/call/call-manager.ts — singleton registry, factory methods, SIP routing
  • ts/call/call.ts — the hub: owns legs, media forwarding
  • ts/call/sip-leg.ts — SIP device/provider connection (wraps SipDialog)
  • ts/call/webrtc-leg.ts — browser WebRTC connection (wraps werift PeerConnection)
  • ts/call/rtp-port-pool.ts — unified RTP port pool
  • ts/sipproxy.ts — thin bootstrap wiring everything together
  • ts/webrtcbridge.ts — browser device registration (signaling only)

WebRTC Browser Call Flow (Critical)

The browser call flow has a specific signaling order that MUST be followed:

  1. POST /api/call with browser deviceId → CallManager creates Call, saves pending state, notifies browser via webrtc-incoming
  2. Browser sends webrtc-offer (with its own sessionId) → CallManager creates a standalone WebRtcLeg (NOT attached to any call yet)
  3. Browser sends webrtc-accept (with callId + sessionId) → CallManager links the standalone WebRtcLeg to the Call, then starts the SIP provider leg

The WebRtcLeg CANNOT be created at call creation time because the browser's session ID is unknown until the webrtc-offer arrives.

WebRTC Audio Return Channel (Critical)

The SIP→browser audio path works through the Call hub:

  1. Provider sends RTP to SipLeg's socket
  2. SipLeg's onRtpReceived fires → Call hub's forwardRtp
  3. Call hub calls webrtcLeg.sendRtp(data) → which calls forwardToBrowser()
  4. forwardToBrowser transcodes (G.722→Opus) and sends via sender.sendRtp() (WebRTC PeerConnection)

WebRtcLeg.sendRtp() MUST feed into forwardToBrowser() (the WebRTC PeerConnection path), NOT send to a UDP address. This was a bug that caused one-way audio.

The browser→SIP direction works independently: ontrack.onReceiveRtpforwardToSip() → transcodes → sends directly to provider's media endpoint via UDP.

SIP Protocol Library

ts/sip/ is a zero-dependency SIP protocol library. Do not add transport or timer logic there — it's purely data-level (parse/build/mutate/serialize).