Skip to content

Relay Overview

bilbycast-relay is a stateless opaque per-path forwarder for NAT traversal between bilbycast-edge nodes behind NAT, with an optional stateful viewer-distribution role alongside it. As a forwarder it pairs the two ends of each path by tunnel ID and forwards [tunnel_id][ciphertext] verbatim between them. It cannot read tunnel payloads (end-to-end ChaCha20-Poly1305 encryption between edges), and it never terminates or combines the streams it carries. Statelessness is a property of that role: the viewer-distribution role below keeps per-viewer WHEP sessions in memory and an on-disk segment store it re-adopts on restart.

It forwards three logically distinct path types, all the same opaque way:

  • QUIC tunnels — TCP streams + UDP datagrams over QUIC / TLS 1.3 (:4433).
  • Native SRT / RIST over relay — plain UDP, no QUIC (:4434), so SRT/RIST keep their own ARQ + congestion control without QUIC’s overhead.
  • Individual bond legs — a relayed multi-path bond leg is just a native plain-UDP tunnel. Bond aggregation, cross-leg ARQ, FEC, and reordering run end-to-end edge↔edge; the relay forwards each leg opaquely. There is no “bond bridge” — the relay does not terminate or combine bonds.

A relay can also run as a viewer-distribution node — reaching browser viewers directly with a WHEP SFU (sub-second WebRTC) plus an LL-HLS/CMAF origin (CDN-scale), with no external streaming server and no ports opened on the edge. The opaque forwarding described above is unaffected either way.

Default-off describes the build, not the box. The Cargo feature is off by default, so a plain forwarder build carries no distribution surface at all — but the shipped -distribution artefact comes up with the role enabled and listening on :4485 (TCP — browser-facing HTTP signaling + origin) and :4486 (UDP/QUIC ingest), both dual-stack, unless relay.json says "distribution": { "enabled": false }. install-relay.sh installs that variant by default; pass --variant default for the lean forwarder. The same tarball also carries a second binary, bilbycast-portal — the DVR viewer portal — installed only with --with-portal and deliberately left stopped until you issue it a manager service token.

  • Zero-config startup — Runs with no config file, self-signed TLS certificate auto-generated
  • QUIC/TLS 1.3 — QUIC-tunnel traffic encrypted in transit
  • Native-UDP carrier — Plain-UDP data plane (:4434, on by default) for native SRT/RIST and bond legs over relay; both ends dial out, so a bond can work with both ends behind NAT
  • End-to-end encryption — ChaCha20-Poly1305 between edges; relay sees only ciphertext
  • Optional API auth — Bearer token authentication for REST API
  • Optional tunnel auth — Per-tunnel HMAC-SHA256 bind tokens managed via manager
  • Manager integration — Optional WebSocket connection for centralized monitoring
  • Lock-free design — DashMap registries, AtomicU64 stats, zero Mutex usage
  • Viewer distribution (optional) — WHEP SFU + LL-HLS origin to reach browser viewers natively; the Cargo feature is default-off, but the -distribution release variant ships it enabled on :4485 / :4486 unless relay.json disables it
Layer Mechanism
Transport TLS 1.3 via QUIC
End-to-end ChaCha20-Poly1305 between edges
REST API Optional Bearer token
Tunnel binding Optional HMAC-SHA256 bind tokens
ALPN bilbycast-relay protocol enforced
Endpoint Description
GET /health Health check (always public)
GET /metrics Prometheus metrics
GET /api/v1/tunnels List active QUIC tunnels
GET /api/v1/udp-sessions List active native-UDP relay sessions
GET /api/v1/edges List connected edges
GET /api/v1/stats Bandwidth, throughput, peaks, uptime

Two administrative teardown routes exist for orphan cleanup when the manager is unavailable: DELETE /api/v1/tunnels/{id} and DELETE /api/v1/udp-sessions/{id}. Both are fail-closed — they return 403 unless api_token is configured.

Terminal window
# Zero-config start
cargo build --release
./target/release/bilbycast-relay
# With config
./target/release/bilbycast-relay -c relay.json

Default ports: QUIC on 0.0.0.0:4433, the native-UDP relay on 0.0.0.0:4434, REST API on 0.0.0.0:4480. When edges live outside the relay’s host, also set public_quic_addr (and public_udp_addr if you use the native-UDP carrier) in the config — or pass --public-quic-addr / --public-udp-addr — to the hostname or public IP edges will dial. That’s what gets advertised to the manager and pre-populates the tunnel-creation dropdown.

See Architecture for the full design and connection lifecycle.