LumyxGet started
Alpha v0.4.1ICE, DTLS, SRTP and RTP forwarding work end to end

Ship real-time video you can actually debug

An open-source WebRTC SFU in Rust. It reads RTCP as packets pass through, so when a call goes bad you see which peer and why.

webinar-useu-west-3 · 6 peersdemo data
peer_idscorertt_mslossstatus
ff104b2c42908.20%degraded
a3f91c0299380.20%live
c27ad93096710.60%live
7e01d4aa98520.30%live
5b9c3e10771181.40%live
91da0f6799440.10%live
jitter_mspacket_loss_ratioroom avg · 30s · 500 ms
-30sjitter_ms > 30now
01

Self-hosting shouldn’t mean three services and a Redis.

Signaling, forwarding and the dashboard run in one process. One command starts it; there is nothing else to deploy or keep in sync.

Proofdocker run
processes1
external dependenciesnone
config to start0 lines
imageghcr.io/lumyx-dev/sfu-core
licenseMIT
02

Stop bolting Grafana onto your SFU.

The forwarder already sees every RTCP report. Lumyx keeps the six numbers that explain a bad call, per peer and per room, and pages Slack or PagerDuty when one crosses its threshold.

Proofper peer · per room · 500 ms
jitter_ms> 30
packet_loss_ratio> 0.02
rtt_ms> 200
nack_ratio> 0.05
freeze_ratio> 0.01
bitrate_kbps< 100
03

Written in Rust, not Go with CGO.

The hot path is Rust end to end. No garbage collector pausing forwarding mid-frame, no FFI boundary to cross for SRTP.

Proofmedia path
media pathRust
GC pausesnone
CGO / FFI boundarynone
measured throughputbenchmark pending
Room topology

See the path every packet takes

One room, drawn from what the forwarder sees. Edge width is bitrate. Pulse speed is round-trip time. The dashed edge is the peer your users are complaining about.

Direct pathTURN relayApproachingBreached
sfu-coreeu-west-3
turn · relay

Configure it once, then stop thinking about it

One TOML file. Clients speak plain JSON over a WebSocket, with no SDK to install.

1# lumyx.toml — read at startup
2[server]
3listen = "0.0.0.0:3000"
4region = "eu-west-3"
5
6[thresholds]
7packet_loss_ratio = 0.02
8rtt_ms = 200
9jitter_ms = 30
10nack_ratio = 0.05
11freeze_ratio = 0.01
12debounce = "30s"
13
14[alerts.slack]
15webhook = "env:SLACK_WEBHOOK_URL"
16min_severity = "warning"
17
18[alerts.pagerduty]
19routing_key = "env:PD_ROUTING_KEY"
20min_severity = "critical"
21
22[remediation]
23audio_only_fallback = true
24simulcast_drop_layer = true
Draft config shape — today the SFU reads environment variables. Thresholds, alert destinations and remediation are on the roadmap.
No GC pausesRust in the media path.
No exporter to writeMetrics come from the forwarder.
No RedisRoom state lives in the process.
No SDK to installPlain JSON over a WebSocket.

Everything in the box

Solid pills ship in v0.4.1. Dashed pills are on the roadmap.

Works todayPlanned
Media
  • Selective forwarding(works today)
  • ICE(works today)
  • DTLS(works today)
  • SRTP(works today)
  • RTP forwarding(works today)
  • Simulcast layers(planned)
  • TURN fallback(planned)
Observability
  • RTCP metrics collector(planned)
  • Six metrics per peer(planned)
  • Per-room rollups(planned)
  • Room topology(planned)
  • Prometheus /metrics(planned)
  • Session replay(planned)
Alerting
  • Thresholds per environment(planned)
  • Debounce(planned)
  • Slack(planned)
  • PagerDuty(planned)
  • Webhooks(planned)
  • Audio-only fallback(planned)
Operations
  • Single process(works today)
  • Docker image(works today)
  • WebSocket signaling(works today)
  • Room manager(works today)
Project status · alpha v0.4.1

What works, and what doesn't yet

No customers yet, no published benchmark. Here is the line between the two, kept in sync with the README.

README · Project status
Works today
ICE, DTLS, SRTPend to end
RTP forwardingselective
Room managerjoin · leave · tracks
WebSocket signalingoffer · answer · ice
Test suite147 tests
Docker imagesfu-core:latest
Planned
Quality metricsjitter · loss · RTT · NACK
Thresholds and alertingSlack · PagerDuty · webhook
Automatic remediationaudio-only · layer drop
Built-in dashboardtopology · replay
Simulcast3 layers
Published benchmarkpending

Find out why your last call was bad

$docker run -p 3000:3000 ghcr.io/lumyx-dev/sfu-core:latest
#then open http://localhost:3000