XGW

An Elixir WebRTC gateway: ICE, DTLS, and SRTP on the wire; plaintext RTP/RTCP (and optional data channels) for your app.

Ported and updated from an earlier Xirsys media gateway implementation; released for public use.

If you have used RTCPeerConnection in the browser, you already know the hard parts -- finding a path (ICE), securing it (DTLS), encrypting media (SRTP). XGW is the server-side half of that peer connection. Your product still owns signaling (offer/answer over WebSocket or similar). XGW takes over once you have SDP.

Home: https://github.com/Lazarus404/xgw

How the pieces fit together (friendly walkthrough): ARCHITECTURE.md.

What problem this solves

Browsers speak encrypted WebRTC. Your media server wants clear packets so it can mix, forward, or inspect them.

XGW sits in the middle:

Browser  -- ICE + DTLS + SRTP -->  XGW  -- plaintext RTP/RTCP -->  your app

Typical hosts are SFUs or media servers -- the same product role as Janus, mediasoup, or LiveKit: they own rooms and signaling, then call into XGW.Peer. Under the hood XGW leans on xsockets for listeners/sockets and xmedialib for STUN/RTP/SRTP/SDP helpers.

Mental model (JS -> XGW)

You already know Rough XGW match
One RTCPeerConnection One XGW.Peer process
setRemoteDescription / answer XGW.Peer.apply_descriptions/4
addIceCandidate / onicecandidate add_remote_candidate/2 and {:xgw, :local_candidate, ...}
Media after connected {:xgw, :rtp, ...} in / SendHandle out
Data channels XGW.Peer.Datachannel + handler messages

Listener = shared media port. Peer = one remote browser on that port.

What you get

Main modules

You will touch these first:

Module Job in plain English
XGW.Listener Shared UDP (+ optional TCP) port; classify packets; route to Peers
XGW.Peer One remote client: ICE -> DTLS -> SRTP -> events to your handler
XGW.Descriptions Parse SDP offer/answer into ICE creds, fingerprints, SSRC maps
XGW.Demux "Is this STUN, DTLS, or media?" from the first byte
XGW.ICE / Lite / Full Find a working network path
XGW.DTLS Secure the path and export SRTP keys
XGW.SRTPSession Encrypt outbound / decrypt inbound media
XGW.Peer.SendHandle Fast outbound RTP after DTLS is up
XGW.Peer.Datachannel Optional data-channel child (keeps Peer free for media)

Full module map and call lifecycle: ARCHITECTURE.md.

ICE-lite vs ICE-full (quick pick)

ICE-lite (default): the browser does most connectivity-check work; XGW answers STUN Bindings and tracks consent. Simple. Fine when the server address is stable and well advertised.

ICE-full (ice_lite: false): XGW runs the full checklist -- pairs, checks, nomination -- and can gather srflx/relay candidates (and trickle them to the browser). Use this for tougher NATs, relay-heavy deploys, or when you need ICE-TCP.

Either mode emits familiar handler messages like {:xgw, :ice_connected, peer} and {:xgw, :dtls_connected, peer} when setup succeeds.

RFCs (the specs we implement)

You do not need to read these to run tests. They are here when you want the official wording:

Dependencies

Local checkouts before remotes catch up:

mix deps.get
mix test

Browser spike

Want to see a real Chrome/Firefox peer talk to XGW without wiring a full SFU?

mix xgw.spike --port 10000 --http-port 8080

Open the printed URL, allow mic if asked, and watch ICE/DTLS/RTP come up. Steps and outcome notes: test/spike/README.md.

ICE-TCP variant:

mix xgw.spike --ice-tcp

Tests

Default suite (unit + integration, no live browser):

mix test

Spike tests under test/spike/ are tagged :external and excluded by default.