๐ sueta
a complete, working stack for serverless p2p end-to-end-encrypted web apps โ this page is also the git repo.
try the live chat browse the code clone the repoClone
This site serves the repository over git's dumb-HTTP protocol as plain static files (it's hosted on IPFS โ there is no git server here, or any server at all):
git clone https://git-chat.ardegazu.ro/sueta.git
cd sueta
What you get
client/src/lib/ โ the p2p core
Reusable, app-agnostic TypeScript: WebRTC full-mesh with perfect negotiation, E2E crypto (HKDF + AES-GCM, structurally nonce-safe), blind signaling client, TURN credentials, persistent Ed25519 identities with emoji fingerprints.
server/ โ one Go binary
Blind WebSocket relay (routes only ciphertext), coturn REST credential minter, built-in Let's Encrypt. No database, no state, no logs of content.
client/src/app/ โ the chat
Installable PWA: rooms as capability links, threads, reactions, EXIF-stripped image sharing, p2p audio/video room calls (DTLS-SRTP), peer-to-peer history sync + on-device persistence, local notifications, numbered releases with an in-app update banner.
docs/ โ the contracts
PROTOCOL.md โ crypto & wire spec. SIGNAL-SERVER.md โ ops runbook and how to attach new apps/domains to one relay.
How it works (the short version)
- The room secret never leaves the browser. It lives in the URL fragment (
#โฆ). HKDF derives a one-way room id (rendezvous only) and per-sender AES-256-GCM keys. - The relay is blind. Peers meet through a ~500-line WebSocket relay that forwards opaque encrypted blobs. It cannot read offers, names, or messages โ verified by tests that grep every frame for plaintext.
- Everything else is browser โ browser. WebRTC datachannels (DTLS + app-layer AES-GCM), full mesh up to ~12 peers, STUN/TURN for NAT traversal with ephemeral HMAC credentials.
- History lives in the members. Devices persist their rooms locally (IndexedDB) and replay history to newcomers over the encrypted channels. No server stores a byte.
- Identity without accounts. One Ed25519 seed per device (password-manager-sized), session binding signatures, emoji fingerprints, trust-on-first-use with key-change warnings.
- Hosting without an origin server. The app is a static build pinned to IPFS, served through an HTTPS tunnel that is deliberately never connected โ its "offline page" is the site. This page you're reading works the same way.
Run it yourself
# dev: relay + app
cd server && go run . --dev # :8080
cd client && npm install && npm run dev # :5173, proxies /ws to :8080
# tests (real Chromium โ WebRTC needs real UDP)
cd server && go test ./...
cd client && node e2e/mesh.e2e.mjs
To deploy your own server side, one command on any Ubuntu/Debian box: sudo deploy/install-server.sh signal.yourdomain.com โ installs coturn + the relay from source, secrets, systemd, firewall. Host the client build anywhere static โ IPFS, a CDN, a folder behind nginx. Full steps in docs/SIGNAL-SERVER.md.
Build your own p2p app on this
The chat is just one consumer of the core. A new app is ~40 lines of bootstrap:
- pick an app salt (e.g.
"myapp.example.com/v1") โ it namespaces your rooms so apps can share one relay without collisions, - copy
client/src/lib/(zero app-specific code in it), - point it at your relay โ or reuse an existing one; the relay never learns what app it's carrying,
- define your own payloads and broadcast them โ they're sealed per-sender automatically.
The recipe with code is in client/src/lib/README.md. Shared whiteboards, turn-based games, collaborative editors, file drops โ anything that fits "small group, real-time, private" fits this stack.
Guarantees & limits, honestly
- The relay operator (or anyone on the wire) sees: connection metadata, opaque room ids, ciphertext sizes and timing. Not content, not names, not who-is-who inside a room.
- Anyone with the room link is a full member โ capability model. Guard links like keys.
- Identity verification is human: compare the four emoji out-of-band, like reading safety numbers aloud. No servers means no authority that can vouch for anyone.
- Full mesh scales to ~a dozen peers; beyond that you'd want an SFU, which is no longer serverless.
- History survives as long as any member's device kept it; a room everyone truly abandons is forgotten. That's the design, not a bug.