junto
Watch media in sync with friends — no server, no account. Playback coordination travels encrypted over free public nostr relays; the media files stream directly peer-to-peer over WebRTC. Playback happens in mpv, which junto launches and remote-controls.
Stack: Go, WebRTC, nostr, mpv, NIP-44, yt-dlp
GitHub · Live
The problem: Watching something "together" remotely usually means a paid sync service, a browser extension that only works on one streaming site, or counting down "3, 2, 1, play" and hoping. junto does it for arbitrary local files with no server, no account, and no upload to anyone's cloud.
No backend: Coordination rides on free public nostr relays as ephemeral, encrypted events (kind 29888 — relays forward but don't store them). A room is a random 128-bit secret encoded as a jun1… code; from it both sides derive a public room ID for relay filtering and a NIP-44 key that encrypts every message. Relays and lurkers see only ciphertext under an unlinkable room tag, and every event is BIP-340 signature-verified, so a relay can't forge who said what. Each participant uses a throwaway keypair per session.
Sync: Leaderless last-writer-wins. Explicit actions (pause, seek, speed, subtitle, playlist) carry a version timestamp and win everywhere; 2-second heartbeats double as presence and drift beacons. Peers measure clock skew NTP-style on join and compensate, so a wrong system clock doesn't read as drift. Small divergence (~0.05–0.4s) is eased out by briefly nudging mpv's playback rate up to 3% — invisible, and local-only so it never drags the room's speed — while a larger gap triggers a one-way hard seek so clients can't fight. Remote commands are echo-suppressed so they aren't rebroadcast.
Streaming: A joiner doesn't wait for the download — junto runs a tiny local HTTP server and points mpv at it, so playback starts within seconds while bytes arrive in the background. Because media indexes are read on open, junto fetches the index region first: for mp4/mov it walks the box structure to pull the moov atom exactly, wherever it sits; for other containers it grabs the trailing 16 MiB. So files whose index lives at the end start quickly instead of forcing a full download. The download follows the playhead in a bandwidth-adaptive window, and the room pauses only when mpv actually runs dry — never on speculative read-ahead.
File transfer: One persistent WebRTC connection per file. A single non-trickle SDP offer/answer travels through the encrypted room channel, then byte ranges are negotiated on the data channel itself — 16 KiB chunks with backpressure, and a seek redirects the stream to a new offset without rebuilding the connection. Every file is SHA-256-verified on completion; a dropped connection resumes from the bytes already on disk. STUN hole-punching for the direct path; a supplied TURN relay for symmetric-NAT pairs.
Resilience: Dropped relays are silently re-dialed. If the host drops offline and another member has the files locally, the room elects a new host automatically (largest-pubkey eligible peer, agreed with no coordination round), re-announces the playlist, and redirects streaming joiners to it — verification continues across the switch.
Distribution: A single Go binary. One-command install (curl junto.watch/install | sh), a Homebrew tap, a macOS .pkg, or go install. junto doctor diagnoses mpv, relay reachability, NAT traversal, TURN, and yt-dlp/ffmpeg, and prints the fix for whatever fails.
Roadmap: junto is built around one discipline — make the watch party seamless, then deepen that rather than widen it. The two biggest items ahead are a resilient QUIC + DERP transport (the Tailscale model: coordinate over an encrypted relay, punch a direct hole, fall back to the relay for the ~10% of networks that block it — ~90% direct-connect even behind symmetric NAT/CGNAT, and pure Go) and swarm streaming (BLAKE3/Bao-verified per-chunk fetch from any peer, so the host's upload leaves the critical path). Breadth — more players, a native GUI app, live-stream and torrent sources, Windows — is deliberately parked until the core is flawless.
swayam.codes