Installing the Web Relay

shiloh-web-relay is the browser gateway. It subscribes to shiloh-mixer as three UDP relay clients (one for each feed: main, monitor, cue), encodes each feed to Opus, and serves them over WebRTC using the WHEP protocol. Any browser that supports WebRTC can tune in with sub-500 ms latency — no plugin required.

Architecture

shiloh-mixer  ──UDP 5005──▶  shiloh-web-relay (relay_sub)
                                  │  Opus encoder (20 ms frames, 128 kbps VBR)
                                  ▼
                             axum HTTP server :8890
                                  │  POST /whep/main   → WebRTC PeerConnection
                                  │  POST /whep/monitor
                                  └  POST /whep/cue
                                  ▼
                             Browser  (WHEP client, <audio> element)

Prerequisites

  • Linux (same host as shiloh-mixer, or a host with UDP access to port 5005 on the mixer)
  • Rust toolchain (rustup)
  • The Opus shared library: sudo apt install libopus-dev

1. Build the binary

cd ~/shiloh-broadcaster
cargo build --release -p shiloh-web-relay

Optionally install to ~/.local/bin:

ln -sfn "$(pwd)/target/release/shiloh-web-relay" ~/.local/bin/shiloh-web-relay

2. Run

shiloh-web-relay \
    --mixer   127.0.0.1:5005 \
    --control 127.0.0.1:19997 \
    --http    0.0.0.0:8890
Flag Default Description
--mixer HOST:PORT 127.0.0.1:5005 Mixer relay UDP address
--control HOST:PORT 127.0.0.1:19997 Mixer control plane (assigns feeds)
--http ADDR:PORT 127.0.0.1:8890 HTTP listen address for WHEP
--name-prefix PREFIX web-relay Prefix for the three relay client names (web-relay-main, web-relay-monitor, web-relay-cue)
--announce-ip IP[,IP] (auto) Public IP(s) for ICE candidates — required behind NAT
--ice-network-types udp4 (all) Restrict ICE transport types; udp4 disables IPv6 and TCP candidates

On startup you should see:

INFO shiloh_web_relay: shiloh-web-relay: mixer=127.0.0.1:5005 control=127.0.0.1:19997 http=0.0.0.0:8890 prefix=web-relay
INFO shiloh_web_relay::relay_sub: registered "web-relay-main" → session=0xaabbccdd
INFO shiloh_web_relay::relay_sub: registered "web-relay-monitor" → session=0xaabbccde
INFO shiloh_web_relay::relay_sub: registered "web-relay-cue" → session=0xaabbccdf
INFO shiloh_web_relay: WHEP server listening on http://0.0.0.0:8890

3. Test in a browser

Open http://server-ip:8890/ in a browser. The built-in index page lets you pick a feed (main / monitor / cue) and start listening. WebRTC setup takes 1–3 seconds.

Alternatively, hit the WHEP endpoint directly:

# POST an SDP offer to start a session (WHEP protocol)
curl -s -X POST http://server-ip:8890/whep/main \
    -H "Content-Type: application/sdp" \
    --data-binary @offer.sdp

4. Reverse proxy with nginx

To serve the WHEP endpoint under your main domain (e.g. https://listen.example.com/webrtc/):

server {
    listen 443 ssl;
    server_name listen.example.com;

    # ... ssl_certificate, ssl_certificate_key, etc.

    location /webrtc/ {
        proxy_pass         http://127.0.0.1:8890/;
        proxy_http_version 1.1;
        proxy_set_header   Host              $host;
        proxy_set_header   X-Real-IP         $remote_addr;
        proxy_set_header   X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Proto $scheme;

        # WHEP uses chunked responses; disable buffering.
        proxy_buffering    off;
        proxy_read_timeout 60s;
    }
}

NOTE: If the web relay is behind NAT, pass --announce-ip <public-ip> so the ICE candidates in the SDP answer contain a reachable address. Without this, browsers outside the LAN cannot complete the WebRTC handshake.

5. systemd user unit

Create ~/.config/systemd/user/shiloh-web-relay.service:

[Unit]
Description=shiloh-web-relay — WebRTC/WHEP browser egress
After=shiloh-mixer.service network-online.target
Wants=network-online.target

[Service]
Type=simple
ExecStart=%h/.local/bin/shiloh-web-relay \
    --mixer   127.0.0.1:5005 \
    --control 127.0.0.1:19997 \
    --http    0.0.0.0:8890
Restart=on-failure
RestartSec=5
Environment=RUST_LOG=info

[Install]
WantedBy=default.target
systemctl --user daemon-reload
systemctl --user enable shiloh-web-relay.service
systemctl --user start  shiloh-web-relay.service

Troubleshooting

Symptom Likely cause
Browser says “ICE failed” Behind NAT without --announce-ip; or firewall blocking UDP
No audio after connection Mixer not streaming to this relay yet (check feed assignment in mixer_web UI)
bind 0.0.0.0:8890 fails Port already in use; check `ss -tlnp
Three clients registering but mixer rejects Mixer [ingest] and relay allow-lists are separate — relay clients do not need [[ingest.sender]] entries