End-to-end encrypted

Private live streaming,
with maximum privacy protection

It all runs in your browser โ€” there's no app to install. Your video is encrypted on your device before it ever leaves, so:

๐Ÿ“กThe relay carries your video but can't decode it โ€” it only ever handles encrypted data.
๐Ÿ”—The broker just connects you to a relay. Your video never passes through it, and it keeps no record of your stream's content.

No accounts. No server-side list of who's streaming.

Broadcast Read the code

How a stream travels โ€” and what's exchanged

Two browsers, one broker, a relay fleet. Follow the numbers. Notice that the content key lives only in the share link and is derived on each device โ€” nothing in the middle can decrypt your video.

Broker ยท tinymoq.com gates connections ยท serves public salts ๐Ÿ”’ content-blind โ€” never sees your key โ‘ข you send the share link watch.html?node=โ€ฆ&o=โ€ฆ #k=the content key (secret) broadcaster browser โ‘  mint node id โ€” Ed25519 public key โ‘ก mint #k= โ€” 32 random bytes โ‘ค CK = HKDF(#k=, salts) โ‘ฅ encode โ†’ AES-256-GCM encrypt camera โ†’ encode โ†’ encrypt โ†’ send viewer browser โ‘ฆ read node ยท origin ยท #k= from link โ‘ฉ CK = HKDF(#k=, salts) โ‘ช decrypt โ†’ decode โ†’ play receive โ†’ decrypt โ†’ decode โ†’ play Origin relay encrypted ยท can't decode Edge relay encrypted ยท can't decode โ‘ฃ assign publish (Bearer pk_) ยท PUT salt โ† origin EID + publish JWT โ‘งโ‘จ get salt ยท assign watch โ† edge relay + subscribe JWT โ‘ฅ WebTransport ?jwt iroh pull โ‘ช WebTransport ?jwt your video travels encrypted end to end ยท only the codec & size are visible
  1. 1Identity. The browser mints an Ed25519 keypair; the public key is the stream's name.
  2. 2Content key seed. 32 random bytes (#k=) that will live only in the link.
  3. 3Share. You hand someone the link โ€” that (and only that) lets them watch.
  4. 4Assign + salt. The broker gives a gated relay + short-lived token and public salts.
  5. 5Derive key. CK = HKDF(#k=, salts) โ€” computed in the browser, sent nowhere.
  6. 6Encrypt + publish. Each frame is AES-256-GCM encrypted, then sent to the origin relay.
  7. 7Open link. The viewer reads the node, origin, and #k= from the URL.
  8. 8Salt + edge. The viewer fetches the salts and a gated edge relay from the broker.
  9. 10Derive same key. Same inputs โ†’ same CK, independently, in the browser.
  10. 11Decrypt + play. Ciphertext in, video out โ€” only here and on the broadcaster.

The values being exchanged

Everything in the path is one of these. Only one of them is a secret.

pk_โ€ฆ

Public publishable key. Identifies a tenant for quotas/limits; can mint relay tokens but can't decrypt. Ships in the page.

node id

An Ed25519 public key (base32). The stream's identity and the relay track name. Public.

origin EID

Which relay holds the origin, so a viewer's edge knows where to pull from. Routing only; public.

salts + epoch

Public HKDF inputs (a global kill-switch salt โ€– a per-stream salt). Rotating one re-keys the stream.

JWT

A short-lived, per-broadcast relay token that authorizes the connection (publish or subscribe scope). Not a content key.

#k= โ†’ CK

The secret. 32 bytes in the link fragment (never sent to a server) โ†’ AES-256-GCM key via HKDF. Held only by the two browsers.

Who can see what

The design makes every party in the middle either content-blind or removable.

PartyCan seeNever sees
Broker (tinymoq.com)node id, tenant pk_, public salts, coarse geo; mints tokensyour #k=, the key CK, your video & audio
Relay fleeta connection token, encrypted (unreadable) frames, the catalog (codec/resolution)your #k=, the key CK, your video & audio
Someone with the linkeverything โ€” the link carries #k=โ€” (share it carefully)
Someone without the linkat most scrambled, unreadable dataanything decryptable

Small enough to read

Each page loads exactly two pieces of JavaScript โ€” on purpose.

Our whole client

earthseed.js โ€” one unminified, documented file that runs in the browser as-is (no build step). It does capture, encode, encrypt, decrypt, decode and render. What you read is what runs.

The transport

@moq/net (Media over QUIC), loaded directly from its published package at a pinned version. We don't vendor or modify it โ€” verify it upstream.