← The Kibitz Engine · deep dive
Video rooms on a LAN with no internet, no accounts, and nothing to install on the guests' side. Run one tiny program on one device on the local network — the LAN hub — and everyone else just opens kibitz.chat in a browser on that same Wi-Fi, and they're in a call. The audio and video flow phone-to-phone across the LAN; the hub only helps the browsers find each other.
Not the TURN relay. Kibitz has two unrelated things. The internet TURN relay forwards an online call's encrypted media when a direct connection is blocked (see architecture.md §3). The LAN hub described here (Offline mode) replaces the internet entirely for a same-Wi-Fi call. The hub ships as the kibitz-offline project (a small static Go binary, MIT, renamed from "kibitz-relay").
A small always-on relay box on the LAN replaces the cloud signaling broker. Every browser connects to it, beacons its presence, and exchanges WebRTC handshakes through it; once the mesh forms, all content (audio, video, chat, co-browse) flows directly between browsers — the hub never carries it.
The relay is deliberately dumb — it assigns each browser an id and routes
{to, payload} frames between them, and nothing else (the contract the web client
talks to in galaxyHub.ts:3-13, send/broadcast :128-135). All call semantics
(presence, roster, media signaling) live in the web layer as frame payloads, so the
box itself almost never needs updating. It ships as a single static Go binary (a Pi
appliance, an Android app, or any laptop).
It reaches the browsers through one clever trick — a permanent identity: a
fixed UDP port, fixed ICE credentials, and a fixed DTLS certificate it persists
across restarts. That entire half of the WebRTC handshake packs into one short
blob that becomes the ?galaxy=… in the join link / QR. The web app reconstructs
the hub's side of the handshake locally from that blob, so there is zero
per-session signaling (galaxySignal.ts). Scan once, connect forever.
The blob arrives once via ?galaxy=… and persists in localStorage — open the
relay's link once on its Wi-Fi (or while online), and Kibitz works on that LAN
forever after; ?galaxy=off clears it (galaxyHub.ts:15-39).
Where the online room elects a migratory coordinator over the broker, the LAN
room needs none — the relay is an always-on box, so peers just self-assemble
(lanRoom.ts:1-23). Each browser, on the hub, periodically broadcasts a
presence beacon (present: name / cam / avatar); every peer builds the roster
locally from those beacons and reaps the silent ones (BEACON_MS = 2500,
REAP_MS = 8000). A newcomer's hi frame prompts everyone to re-announce so it
appears at once; bye is a clean leave. Identity = the hub id, stable for the
session — which is all a LAN call needs.
Media is a pairwise WebRTC mesh whose offer/answer/ICE are trickled over the
hub (the hub routes handshakes; media itself flows directly between browsers,
LAN-only, iceServers: [] — lanMesh.ts:1-17). It implements the same
VoiceMesh interface and the same battle-tested no-churn rules as the online mesh
(smaller id initiates each pair; every link carries audio + a placeholder video
lane from the start; camera toggles are replaceVideoTrack swaps, never a
re-dial). LAN is the best-case mesh environment — gigabit, ~0 ms RTT, no upload
caps — so 6 tiles run better in one house than online across town.
Content does not go through the hub. Chat, co-browse, pay, and ink ride a
peer-to-peer RTCDataChannel on the lanMesh connections, exactly like the online
data mesh (lanRoom.ts:21-22, lanMesh.ts).
RoomLink interface
(lanRoom.ts is the LAN twin of room.ts); the entire call UI
(Tile / CallSurface / widget panel, chat pane); the QR scanner (BarcodeDetector?galaxy=… blob.?galaxy=… blob codec.?galaxy=… blob.getUserMedia's HTTPS requirement). Then scan the hub's QR..local hostname so a Pi without a static IP is still
reachable by name (--advertise overrides the advertised LAN address).--state), so the same QR works
across restarts.These survive from the original design and still hold:
disconnected states in seconds with no
signaling — covers most real-world drops for free. Because the hub is always on,
a peer can also just re-handshake through it (unlike a broker-less scheme).An earlier design (2026-06-07, not built) replaced the hub with QR codes
exchanged directly between phones — no relay box at all. Each pair did a mutual QR
"kiss" (two QRs carrying the ~100-byte variable part of each side's SDP), the first
device acted as a founder that relayed joiner↔joiner signaling over data channels,
and the mesh assembled from N−1 kisses. It was dropped in favor of the LAN hub
above: the hub needs no per-pair ceremony, no minimal-SDP template codec (which
drifts across browser versions), and supports auto-reconnect — at the cost of one
small always-on box. The privacy properties are the same (a content-blind
coordination point caught by the safety code). Kept here only as design history.