Powered by iroh · QUIC peer-to-peer

Video that dials a public key, not an address.

Viroh streams live video over iroh's QUIC network — watch it as colored terminal ASCII or as real video in your browser. End-to-end encrypted, NAT-traversing, with a relay and directory you can host yourself. Pure Rust, no ffmpeg.

640×480 @ 30fps
Motion-JPEG over QUIC
dial by node id
🦀 pure Rust
 /sources/<node-id> — live in the browser
viroh browser player streaming a live 640×480 timecode video over iroh

The browser gateway — real video in a plain <img>, no plugins.

How it works

A sender, a key, and anyone who has it.

Every endpoint is an Ed25519 keypair. The public key is the address you dial — no IP, no port, even across NATs. The connection's TLS is authenticated by that key directly, so reaching an id means you reached exactly that peer.

1

Synthesize or capture

A sender produces 640×480 frames — today a synthetic timecode source, tomorrow a real camera — and encodes each as a JPEG. Motion-JPEG, pure Rust.

2

Stream over iroh

Frames flow over a QUIC uni-stream viroh/mjpeg/1. iroh bootstraps through a relay, then hole-punches to a direct peer-to-peer path when it can.

3

Watch anywhere

Dial the node id from a terminal for colored ASCII, or open /sources/<id> in a browser for real video. Many viewers, one sender.

Why viroh

Built on the network, not on a server farm.

🔑

Dial by public key

The node id is the address. TLS is authenticated by the key itself — no certificate authority, no DNS to trust.

🕳️

NAT traversal built in

Relay-assisted bootstrap, then a silent upgrade to a direct peer-to-peer path when the network allows. Reachable behind NAT by node id alone — no port forwarding.

🖥️

Watch in your terminal

A receiver renders the stream as colored Unicode half-blocks — a legible anti-aliased timecode, right in your shell.

🌐

…or in a browser

Real video in the browser — an MJPEG gateway into a plain <img>, or a direct WebTransport path for lower latency. No transcoding either way.

🛰️

Host the whole network

Run your own iroh relay and discovery directory — point any binary at them with a flag each. Self-hostable end to end, with no dependency on anyone else's infrastructure.

🦀

Pure Rust, no ffmpeg

Encode and decode with jpeg-encoder + zune-jpeg. No C codec libraries, no system dependencies.

Local-first

Watch a device on your LAN. Nothing in the middle.

A sender opens a direct WebTransport door and pins a self-signed certificate by its hash — so a browser on the same network connects straight to the device, with no certificate authority, no DNS, and no cloud in the video path. The fleet is just a rendezvous board: it hands your browser the address and the hash, then gets out of the way.

 a camera on your desk → your browser
# on the device (LAN IP 10.0.0.133) $ viroh-sender --wt --san 10.0.0.133 --register-url https://fleet [wt] certhash: Y1QU5PTQVntMiQmD82MeP82L8spyzaJd84XNYY+Ae3M= reg : registering with fleet (heartbeat 20s) # in the dashboard: one click → browser ↔ device, peer-to-peer. ▸ watch (wt-direct)
Fleet

Run a fleet. Watch it scale, live.

A small web app manages many sender agents on one host — launch from the dashboard or the command line, tail logs, and open a live browser player for any agent. Every viewer is an independent encrypted connection.

 your-host:8443 — viroh fleet
viroh fleet dashboard: a running agent with its node id, watch link, color selector, and live log tail
Open source

Clone it, point it at your own relay and directory, go live.

viroh is pure Rust and self-hostable end to end. Build the binaries, run a sender, and watch it from a terminal or a browser in minutes.