Files

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:

  1. QUIC/WebTransport server — Uses moq-native::ServerConfig with:

    • 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
  2. iroh endpoint — Binds an iroh endpoint for P2P connectivity, prints its ID

  3. moq-relay Cluster — The broadcast routing engine. Manages broadcast lifecycle: when all subscribers disconnect, the broadcast is removed.

  4. HTTP server — Axum router serving:

    • GET /certificate.sha256 — TLS fingerprint for dev mode
    • GET / — Web viewer landing page
    • GET /{path} — Static file serving with CORS
    • Embedded via include_dir! from web/dist/
  5. Pull mode — If iroh endpoint is available, creates a PullState for on-demand remote broadcast fetching

  6. 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:

  1. Checks if the broadcast already exists in the cluster (fast path)
  2. If not, connects to the remote publisher via iroh-live's Moq::connect()
  3. Subscribes to the remote broadcast
  4. Publishes the consumer into the local cluster under the ticket string as the name
  5. 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().