laravel-webrtc/rust
Blax Software aca88c3dbc feat: Rust SFU data plane — backend-hosted rooms, plug-and-play sidecar binary
rust/ becomes a standalone static binary (blax-webrtc-sfu, tokio-free
std-thread loop on str0m): terminates ICE/DTLS/SRTP, forwards Opus between
room peers without decoding, records per-peer Ogg/Opus (ffprobe-verified),
server-side mute/kick, stats + peer_connected/peer_left usage events, all
driven by PHP over a JSON-lines Unix control socket. Renegotiation rides the
browser data channel (str0m chat model) — signaling only carries the initial
offer/answer.

PHP side: RustMediaEngine (MediaEngine contract + room-aware API) is
plug-and-play — first use downloads the sha256-verified prebuilt binary
(BinaryManager, RoadRunner pattern) and spawns it detached, flock-guarded
(SidecarSupervisor). webrtc:install / webrtc:sidecar provided for explicit
control; config gains the webrtc.sfu block. Replaces the ext-php-rs
Str0mMediaEngine seam.

Tests: 61 PHP (incl. a real PHP<->Rust E2E that spawns the sidecar from
PHPUnit) + 7 Rust (incl. an end-to-end control-socket integration test).
build-release.sh produces the static-musl release + .sha256 (verified:
static-pie, 9.2MB, runs).

Card #1083 (rel #1051 #1060 #1064).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 11:29:26 +02:00
..
src feat: Rust SFU data plane — backend-hosted rooms, plug-and-play sidecar binary 2026-07-10 11:29:26 +02:00
tests feat: Rust SFU data plane — backend-hosted rooms, plug-and-play sidecar binary 2026-07-10 11:29:26 +02:00
Cargo.lock feat: Rust SFU data plane — backend-hosted rooms, plug-and-play sidecar binary 2026-07-10 11:29:26 +02:00
Cargo.toml feat: Rust SFU data plane — backend-hosted rooms, plug-and-play sidecar binary 2026-07-10 11:29:26 +02:00
README.md feat: Rust SFU data plane — backend-hosted rooms, plug-and-play sidecar binary 2026-07-10 11:29:26 +02:00
build-release.sh feat: Rust SFU data plane — backend-hosted rooms, plug-and-play sidecar binary 2026-07-10 11:29:26 +02:00

README.md

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, 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

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.