3.9 KiB
iroh-live-relay: Browser Bridging
Overview
The relay server bridges iroh P2P streams to browser clients via WebTransport. Browsers cannot speak iroh's QUIC protocol directly, so the relay accepts WebTransport connections and either serves locally-published broadcasts or pulls them from remote iroh publishers on demand.
Architecture:
iroh-live publish --(iroh P2P)--> iroh-live-relay <--(WebTransport)-- browser
browser --(WebTransport)--> iroh-live-relay --(iroh P2P)--> iroh-live subscribe
Components
RelayConfig (CLI Configuration)
pub struct RelayConfig {
pub bind: SocketAddr, // QUIC/WebTransport bind (default: [::]:4443)
pub http_bind: SocketAddr, // HTTP static files bind (default: same as bind)
}
Flattenable into a clap CLI via #[command(flatten)].
run(config) — Main Server Loop
The main entry point. Sets up:
-
QUIC/WebTransport server — Uses
moq-native::ServerConfigwith:- QUIC backend:
noq(a custom QUIC implementation) - iroh endpoint integration
- Self-signed TLS certificates (dev mode) for
localhost - Max streams:
moq_relay::DEFAULT_MAX_STREAMS
- QUIC backend:
-
iroh endpoint — Binds an iroh endpoint for P2P connectivity, prints its ID
-
moq-relay Cluster — The broadcast routing engine. Manages broadcast lifecycle: when all subscribers disconnect, the broadcast is removed.
-
HTTP server — Axum router serving:
GET /certificate.sha256— TLS fingerprint for dev modeGET /— Web viewer landing pageGET /{path}— Static file serving with CORS- Embedded via
include_dir!fromweb/dist/
-
Pull mode — If iroh endpoint is available, creates a
PullStatefor on-demand remote broadcast fetching -
Connection loop — Accepts incoming connections, parses the URL path as a
LiveTicket, and if valid, triggers a pull before running the connection
PullState — On-Demand Remote Fetching
When a browser connects with a broadcast name that is a valid LiveTicket, the relay:
- Checks if the broadcast already exists in the cluster (fast path)
- If not, connects to the remote publisher via iroh-live's
Moq::connect() - Subscribes to the remote broadcast
- Publishes the consumer into the local cluster under the ticket string as the name
- Spawns a keepalive task that holds the session until it closes
Concurrency: Duplicate concurrent pulls for the same ticket are deduplicated using a HashMap<String, Arc<Notify>>. Waiters block on the Notify until the first connector finishes.
pub(crate) struct PullState {
live: iroh_live::Live,
cluster: Cluster,
connecting: Arc<Mutex<HashMap<String, Arc<Notify>>>>>,
}
Web Viewer
The relay embeds a SolidJS + TypeScript web application compiled by Vite. It uses:
@moq/watch— Web component for watching streams via WebCodecs@moq/publish— Web component for publishing from browser camera/mic- WebTransport — For QUIC connectivity from the browser
Watch URLs: https://relay:4443/?name=<BROADCAST_OR_TICKET>
Data Directory
The relay persists data to $IROH_LIVE_RELAY_DATA (or the platform default). This includes:
- iroh secret key (
iroh_secret_key) — ensures endpoint ID stability across restarts - TLS certificates
TLS and Certificates
Currently self-signed only. ACME/Let's Encrypt is planned but not implemented. In dev mode, browsers need --ignore-certificate-errors or the relay's fingerprint (served at /certificate.sha256) for WebTransport to work.
Error Handling
No authentication is implemented yet. The relay accepts all connections. MoQ supports token-based authentication which could be added.
CLI Binary
// iroh-live-relay/src/main.rs
#[derive(Parser)]
struct Cli {
#[command(flatten)]
relay: RelayConfig,
}
Must call rustls::crypto::aws_lc_rs::default_provider().install_default() before run().