Overview
This repository contains the TURN relay component of the ERP/1 CHAT monorepo. It packages the
eturnal STUN/TURN server as an Erlang/OTP application, wired directly into the RTP
signaling plane. When ICE direct-path negotiation fails behind symmetric NATs or enterprise
firewalls, TURN allocates a relay port and forwards DTLS-SRTP media between the browser and
the GStreamer MCU, maintaining call quality without opening arbitrary firewall holes.
Architecture
TURN operates as a transparent relay between the browser's ICE agent and the GStreamer MCU. All allocation, permission, and channel bindings are handled by eturnal internally. The RTP signaling plane instructs browsers to use the TURN relay via ICE candidates returned during SDP negotiation.
Browser ··► UDP/TCP:3478 STUN/TURN ──► eturnal relay ──► priv/gst (MCU)
Browser ··► UDP/TCP:5349 TURNS TLS ──► eturnal relay ──► priv/gst (MCU)
rtp_app ──► rtp_token:issue/2 ──► TURN credential (user:pass) ──► browser ICE config
Three roles in the NAT traversal chain:
- 1. STUN — UDP:3478 / TCP:3478 binding requests for public IP and port discovery.
- 2. TURN relay — allocates a relay address and forwards media when STUN binding fails.
- 3. TURNS (TLS) — TCP:5349 encrypted relay for networks that block plain UDP entirely.
Directory Blueprint
├── config/
│ └── eturnal.yml eturnal configuration: listen ports, realm, credentials
├── src/
│ └── turn_app.erl OTP Application: starts eturnal under supervision
├── lib/
│ └── turn.ex Elixir shim: Bandit integration for health-check HTTP
├── run/
│ └── run.sh Shell helper: launches the node in embedded mode
├── log/
│ └── eturnal.log Runtime log: STUN/TURN allocation events
├── mix.exs Elixir project and dependency manifest
└── rebar.config Rebar3 build configuration
Erlang/OTP Integration
eturnal — RFC 5766 TURN server by ProcessOne, embedded here as an OTP dependency
and started under the application supervisor. All TURN authentication uses short-term credentials
derived from rtp_token so that credential lifetime matches the WebRTC session
lifetime.
rtp_token integration — Each login issues a TURN credential valid for the same
configurable TTL (default 180 s, overridable via {rtp, token_ttl, Seconds}).
The browser receives the credential in the signaling init message and passes it
to RTCPeerConnection as an ICE server entry:
iceServers: [
{ urls: "stun:turn.example.com:3478" },
{ urls: "turn:turn.example.com:3478",
username: "<rtp_token>",
credential: "<hmac-sha1-secret>" }
]
Configuration
Edit config/eturnal.yml to adjust listen addresses, realm, and logging:
eturnal:
listen:
- ip: "0.0.0.0"
port: 3478
transport: udp
- ip: "0.0.0.0"
port: 3478
transport: tcp
- ip: "0.0.0.0"
port: 5349
transport: tls
relay_min_port: 49152
relay_max_port: 65535
secret: "<shared-hmac-secret>"
realm: "turn.example.com"
Configuration and Ports
Port Protocol Purpose
──────────────────────────────────────────────────────────────────
3478 UDP/TCP eturnal STUN/TURN — NAT traversal
5349 UDP/TCP eturnal TURNS — STUN/TURN over TLS
ITU-T Standards Alignment
Standard Description System Mapping
────────────────────────────────────────────────────────────────────────────
RFC 5766 TURN protocol eturnal — TURN relay server
RFC 5389 STUN protocol eturnal — STUN binding
RFC 6062 TCP allocations for TURN eturnal TCP transport
RFC 6347 DTLS 1.2 DTLS-SRTP over TURN relay
RFC 8656 TURN for IPv6 and long-term creds eturnal relay port range
Getting Started
brew install erlang
cd turn
mix deps.get
iex -S mix
╔════════════════════════════════════════════════════════╗
║ ERP/1 : TURN Server / STUN Relay ║
║ STUN : stun://localhost:3478 ║
║ TURN : turn://localhost:3478 ║
║ TURNS : turns://localhost:5349 ║
╚════════════════════════════════════════════════════════╝
Relay ports : 49152–65535 (dynamic allocation)
Realm : turn.synrc.com
Auth : short-term HMAC-SHA1 (rtp_token)