# blax-webrtc-sfu — the Rust data plane The sidecar binary behind `Blax\WebRtc\Media\RustMediaEngine`: **backend-hosted rooms**. PHP/Laravel owns the control plane (rooms, auth, signaling orchestration, billing hooks); this process owns everything per-packet. - **ICE** (Interactive Connectivity Establishment — finds a working network path to each browser), **DTLS** (the UDP TLS handshake that negotiates keys) and **SRTP** (the encrypted media packets) are terminated here via [str0m](https://github.com/algesten/str0m), a sans-IO Rust WebRTC stack. - **SFU forwarding** (Selective Forwarding Unit): each peer's Opus audio is received once and forwarded to the other peers of its room **without decoding** — one loop thread, one shared UDP socket, `rtc.accepts()` demultiplexing. - **Recording**: per-peer Ogg/Opus written straight from the depayloaded stream — playable by ffmpeg/browsers, no transcoding, no client cooperation. - **Negotiation**: the initial offer/answer arrives via the control socket (from PHP signaling). Renegotiation as peers join rides each browser's WebRTC **data channel** directly — PHP never relays it (see str0m's `chat` example, which this follows). ## Control protocol (PHP ⇄ sidecar) JSON-lines over a Unix socket. Requests carry an `id` echoed by the reply; notices are pushed without one. ``` → {"id":1,"cmd":"add_peer","room":"lobby","peer":"alice","offer":"v=0…","record":"/rec/alice.ogg"} ← {"id":1,"ok":true,"answer":"v=0…"} → {"id":2,"cmd":"mute_peer","room":"lobby","peer":"alice","muted":true} → {"id":3,"cmd":"record_start","room":"lobby","peer":"alice","path":"/rec/a.ogg"} (record_stop too) → {"id":4,"cmd":"remove_peer","room":"lobby","peer":"alice"} → {"id":5,"cmd":"stats"} ← rooms → peers → {connected, muted, seconds} → {"id":6,"cmd":"ping"} / {"id":7,"cmd":"shutdown"} ← {"event":"peer_connected","room":"lobby","peer":"alice"} ← {"event":"peer_left","room":"lobby","peer":"alice","seconds":42.5} ``` ## Build & test ```bash cargo build && cargo test # 7 tests incl. an end-to-end control-socket run ./build-release.sh # static musl release + .sha256 into dist/ ``` On NixOS: `nix-shell -p gcc pkg-config openssl perl gnumake musl --run ./build-release.sh`. ## Releasing `BinaryManager::VERSION` (PHP) pins the release tag it downloads from. To ship: build `dist/` artifacts (linux-x86_64 at minimum), attach both files to the Forgejo release with that tag, and bump `VERSION` when the protocol changes. PHP resolves binaries in this order: `webrtc.sfu.binary` override → a local `target/{release,debug}` build (dev) → the downloaded install. ## CLI ``` blax-webrtc-sfu --socket /run/sfu.sock --udp-ip 0.0.0.0 --udp-port 41000 --public-ip 203.0.113.9 ``` `--udp-port 0` (default) picks an ephemeral port — fine on one host, pin it in production and publish it (UDP) on the container. `--public-ip` is what browsers are told to reach in the ICE host candidate; it defaults to the default-route interface address.