This site serves the repository over git's dumb-HTTP protocol as plain static files (plain files — there is no git server here, or any server at all):
git clone https://git.ardegazu.ro/chat.git
cd sueta
What you get
client/src/lib/ — the p2p core
Reusable, app-agnostic TypeScript on libp2p: browser⇄browser WebRTC with a circuit-relay bootstrap, an end-to-end-encrypted replicated log (OrbitDB) with a deterministic per-room address, E2E crypto (HKDF + AES-GCM, structurally nonce-safe), persistent Ed25519 identities signing every log entry.
deploy/relay/ — ~100 lines of relay
A libp2p circuit relay + gossipsub peer discovery over websockets. It carries encrypted handshakes only — room traffic flows member⇄member and never touches it. Dev server and self-host example in one file.
client/src/app/ — the chat
Installable PWA: rooms as capability links, threads, reactions (removals replicate too), EXIF-stripped image sharing over encrypted bitswap blocks, p2p audio/video room calls (DTLS-SRTP), offline-capable replicated history, local notifications, numbered releases with an in-app update banner.
docs/ — the contracts
PROTOCOL.md — crypto & protocol spec (v2). RELAY.md — the relay: dev usage and self-hosting.
How it works (the short version)
The room secret never leaves the browser. It lives in the URL fragment (#…). HKDF derives every network-visible identifier one-way — the gossip topic, the log address, all AES-256-GCM keys.
The relay is blind — more than before. Browsers meet through a libp2p circuit relay that carries noise-encrypted handshakes only; room traffic runs member⇄member and never touches it. Verified by tests that grep every relay frame for plaintext.
Everything else is browser ⇄ browser. Direct WebRTC (noise + app-layer AES-GCM), full mesh up to ~12 peers, STUN/TURN for NAT traversal with ephemeral HMAC credentials.
The room IS a replicated encrypted log. Messages, threads, reactions and images are entries in an append-only OrbitDB log, sealed end-to-end before they become content-addressed blocks. Devices replicate it to each other — reopen offline and your copy is there; join late and members' devices feed you everything, images included. 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 served as plain files — there is no backend anywhere. This page you're reading works the same way.
Run it yourself
# dev: relay + app
cd deploy/relay && npm install && npm run dev # :9090, deterministic dev PeerId
cd client && npm install && npm run dev # :5173
# tests (real Chromium — WebRTC needs real UDP; each spawns its own relay)
cd client && node e2e/mesh.e2e.mjs
To self-host the network side: run deploy/relay/ (a ~100-line Node script) behind any TLS proxy, plus coturn for TURN, and point the client build at them with VITE_RELAY_MULTIADDR / VITE_TURN_CREDS_URL. Any willing libp2p circuit relay works — it holds no state and learns nothing. Host the client build anywhere static — IPFS, a CDN, a folder behind nginx. Full steps in docs/RELAY.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 any willing libp2p relay — the relay never learns what app it's carrying,
broadcast live payloads (sealed per-sender automatically) — and, if your app wants durable shared state, append to the room's encrypted replicated log and fold entries into your UI.
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, ephemeral per-session peer ids, opaque topic strings, encrypted handshake frames. Not content, not names, not who-is-who inside a room — and, new in v2, not even the room traffic itself, which flows member⇄member. Other clients of a shared relay see your ephemeral peer id exist (unlinkable across page loads), nothing more.
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.
🔒 sueta
un stack complet, funcțional, pentru aplicații web p2p criptate cap-la-cap, fără servere — pagina asta e și repo-ul git.
Situl servește repository-ul prin protocolul dumb-HTTP al lui git, ca simple fișiere statice (chiar simple fișiere — nu există aici niciun server git, de fapt niciun server):
git clone https://git.ardegazu.ro/chat.git
cd sueta
Ce primești
client/src/lib/ — nucleul p2p
TypeScript reutilizabil, agnostic de aplicație, pe libp2p: WebRTC browser⇄browser cu bootstrap prin circuit relay, un log replicat criptat cap-la-cap (OrbitDB) cu adresă deterministă per cameră, criptografie E2E (HKDF + AES-GCM, structural sigură la nonce-uri), identități Ed25519 persistente care semnează fiecare intrare din log.
deploy/relay/ — ~100 de linii de releu
Un circuit relay libp2p + descoperire de peers prin gossipsub, peste websockets. Poartă doar handshake-uri criptate — traficul camerei curge membru⇄membru și nu-l atinge niciodată. Server de dev și exemplu de self-hosting într-un singur fișier.
client/src/app/ — chatul
PWA instalabil: camere ca linkuri-capabilitate, fire de discuție, reacții (și ștergerile se replică), poze cu EXIF-ul scos, partajate ca blocuri bitswap criptate, apeluri audio/video p2p în cameră (DTLS-SRTP), istoric replicat care merge offline, notificări locale, versiuni numerotate cu banner de actualizare în aplicație.
docs/ — contractele
PROTOCOL.md — specificația cripto și de protocol (v2). RELAY.md — releul: folosire în dev și self-hosting.
Cum funcționează (versiunea scurtă)
Secretul camerei nu pleacă niciodată din browser. Stă în fragmentul URL-ului (#…). HKDF derivă într-un singur sens fiecare identificator vizibil în rețea — topicul de gossip, adresa logului, toate cheile AES-256-GCM.
Releul e orb — mai orb ca înainte. Browserele se întâlnesc printr-un circuit relay libp2p care poartă doar handshake-uri criptate noise; traficul camerei curge membru⇄membru și nu-l atinge niciodată. Verificat de teste care caută plaintext în fiecare cadru trecut prin releu.
Restul e browser ⇄ browser. WebRTC direct (noise + AES-GCM la nivel de aplicație), mesh complet până la ~12 peers, STUN/TURN pentru traversarea NAT-urilor, cu credențiale HMAC efemere.
Camera ESTE un log criptat replicat. Mesajele, firele, reacțiile și pozele sunt intrări într-un log OrbitDB doar-adaugă, sigilate cap-la-cap înainte să devină blocuri adresate după conținut. Dispozitivele și-l replică între ele — redeschide offline și copia ta e acolo; intră târziu și dispozitivele membrilor îți dau tot, cu poze cu tot. Niciun server nu stochează un octet.
Identitate fără conturi. Un seed Ed25519 per dispozitiv (cât să încapă în managerul de parole), semnături de legare a sesiunii, amprente emoji, trust-on-first-use cu avertismente la schimbarea cheii.
Găzduire fără server de origine. Aplicația e un build static servit ca simple fișiere — nu există backend nicăieri. Pagina pe care o citești funcționează la fel.
Rulează-l singur
# dev: releu + aplicație
cd deploy/relay && npm install && npm run dev # :9090, PeerId de dev determinist
cd client && npm install && npm run dev # :5173
# teste (Chromium adevărat — WebRTC are nevoie de UDP adevărat; fiecare își pornește propriul releu)
cd client && node e2e/mesh.e2e.mjs
Ca să-ți self-hostezi partea de rețea: rulează deploy/relay/ (un script Node de ~100 de linii) în spatele oricărui proxy TLS, plus coturn pentru TURN, și îndreaptă buildul clientului spre ele cu VITE_RELAY_MULTIADDR / VITE_TURN_CREDS_URL. Merge orice circuit relay libp2p binevoitor — nu ține stare și nu află nimic. Găzduiește buildul clientului oriunde static — IPFS, un CDN, un director în spatele lui nginx. Pașii compleți în docs/RELAY.md.
Construiește-ți propria aplicație p2p pe asta
Chatul e doar un consumator al nucleului. O aplicație nouă înseamnă ~40 de linii de bootstrap:
alege un app salt (de ex. "myapp.example.com/v1") — pune camerele tale într-un namespace, ca mai multe aplicații să împartă un releu fără coliziuni,
copiază client/src/lib/ (zero cod specific aplicației în el),
îndreaptă-l spre orice releu libp2p binevoitor — releul nu află niciodată ce aplicație poartă,
difuzează payloaduri live (sigilate automat per expeditor) — iar dacă aplicația vrea stare comună durabilă, adaugă în logul criptat replicat al camerei și împăturește intrările în UI.
Rețeta cu cod e în client/src/lib/README.md. Table albe comune, jocuri pe ture, editoare colaborative, schimb de fișiere — orice încape în „grup mic, timp real, privat" încape în stackul ăsta.
Garanții și limite, pe bune
Operatorul releului (sau oricine de pe fir) vede: metadate de conexiune, peer id-uri efemere per sesiune, șiruri opace de topic, cadre criptate de handshake. Nu conținut, nu nume, nu cine-i cine într-o cameră — și, nou în v2, nici măcar traficul camerei, care curge membru⇄membru. Alți clienți ai unui releu partajat văd că peer id-ul tău efemer există (de nelegat între încărcări de pagină), nimic mai mult.
Oricine are linkul camerei e membru cu drepturi depline — model de capabilitate. Păzește linkurile ca pe niște chei.
Verificarea identității e umană: compari cele patru emoji pe alt canal, cum ai citi cu voce tare numerele de siguranță. Fără servere înseamnă fără vreo autoritate care să garanteze pentru cineva.
Mesh-ul complet ține până la vreo duzină de peers; peste, ai vrea un SFU, care nu mai e fără server.
Istoricul supraviețuiește cât timp l-a păstrat dispozitivul vreunui membru; o cameră părăsită de-adevăratelea de toți e uitată. Așa e proiectat, nu e un bug.
🔒 sueta
teljes, működő stack szerver nélküli, végponttól végpontig titkosított p2p webappokhoz — ez az oldal egyben a git repó is.
Az oldal a repót a git dumb-HTTP protokollján át szolgálja ki, sima statikus fájlokként (tényleg sima fájlok — nincs itt git szerver, sőt semmilyen szerver):
git clone https://git.ardegazu.ro/chat.git
cd sueta
Egy libp2p circuit relay + gossipsub peer-felfedezés websocketeken. Csak titkosított kézfogásokat visz — a szoba forgalma tag⇄tag megy, és sosem érinti. Dev szerver és self-host példa egyetlen fájlban.
client/src/app/ — a chat
Telepíthető PWA: szobák capability-linkekként, szálak, reakciók (a törlés is replikálódik), EXIF-től megfosztott képmegosztás titkosított bitswap-blokkokon, p2p hang-/videóhívás a szobában (DTLS-SRTP), offline is működő replikált előzmények, helyi értesítések, számozott kiadások app-beli frissítési sávval.
docs/ — a szerződések
PROTOCOL.md — kripto- és protokollspecifikáció (v2). RELAY.md — a relé: dev használat és self-hosting.
Hogyan működik (a rövid változat)
A szoba titka soha nem hagyja el a böngészőt. Az URL fragmentben él (#…). Minden hálózaton látható azonosítót HKDF származtat egyirányúan — a gossip topicot, a log címét, az összes AES-256-GCM kulcsot.
A relé vak — jobban, mint valaha. A böngészők egy libp2p circuit relay-n át találkoznak, amely csak noise-titkosított kézfogásokat visz; a szoba forgalma tag⇄tag megy, és sosem érinti. Tesztek igazolják, amelyek minden relékeretben plaintextet keresnek.
Minden más böngésző ⇄ böngésző. Közvetlen WebRTC (noise + app-rétegű AES-GCM), teljes mesh ~12 peerig, STUN/TURN a NAT-átjáráshoz efemer HMAC hitelesítéssel.
A szoba MAGA egy replikált, titkosított log. Az üzenetek, szálak, reakciók és képek egy csak-hozzáfűzős OrbitDB log bejegyzései, végponttól végpontig lepecsételve, mielőtt tartalomcímzett blokkokká válnának. Az eszközök egymás közt replikálják — nyisd meg offline, és a másolatod ott van; érkezz későn, és a tagok eszközei mindent odaadnak, képekkel együtt. Egyetlen szerver sem tárol egyetlen bájtot sem.
Identitás fiókok nélkül. Eszközönként egy Ed25519 seed (jelszókezelő-méretű), munkamenet-kötő aláírások, emoji-ujjlenyomatok, trust-on-first-use kulcsváltási figyelmeztetésekkel.
Hosztolás origin-szerver nélkül. Az app statikus build, sima fájlokként kiszolgálva — nincs sehol backend. Ez az oldal is pontosan így működik.
Futtasd magad
# dev: relé + app
cd deploy/relay && npm install && npm run dev # :9090, determinisztikus dev PeerId
cd client && npm install && npm run dev # :5173
# tesztek (igazi Chromium — a WebRTC-nek igazi UDP kell; mindegyik saját relét indít)
cd client && node e2e/mesh.e2e.mjs
A hálózati oldal self-hostolásához: futtasd a deploy/relay/-t (~100 soros Node szkript) bármilyen TLS proxy mögött, mellé coturnt a TURN-höz, és irányítsd rájuk a kliens buildet a VITE_RELAY_MULTIADDR / VITE_TURN_CREDS_URL változókkal. Bármilyen készséges libp2p circuit relay megteszi — nem tart állapotot és nem tud meg semmit. A kliens buildet bárhová kiteheted statikusan — IPFS, CDN, egy mappa nginx mögött. A teljes lépéssor a docs/RELAY.md-ben.
Építsd rá a saját p2p appod
A chat csak egy fogyasztója a magnak. Egy új app ~40 sor bootstrap:
válassz egy app saltot (pl. "myapp.example.com/v1") — ez ad névteret a szobáidnak, hogy több app osztozhasson egy relén ütközés nélkül,
másold be a client/src/lib/-et (nulla app-specifikus kód van benne),
irányítsd bármilyen készséges libp2p relére — a relé sosem tudja meg, milyen appot visz,
sugározz élő payloadokat (automatikusan feladónként lepecsételve) — és ha az appnak tartós közös állapot kell, fűzz hozzá a szoba titkosított replikált logjához, és hajtogasd a bejegyzéseket a UI-ba.
A recept kóddal a client/src/lib/README.md-ben van. Közös rajztáblák, körökre osztott játékok, kollaboratív szerkesztők, fájl-drop — bármi, amire ráillik a „kis csapat, valós idő, privát", ráillik erre a stackre.
Garanciák és korlátok, őszintén
A relé üzemeltetője (vagy bárki a vonalon) ezt látja: kapcsolati metaadatok, munkamenetenkénti efemer peer id-k, átlátszatlan topic-sztringek, titkosított kézfogás-keretek. Tartalmat nem, neveket nem, azt sem, ki kicsoda egy szobában — és, ami a v2 újdonsága, magát a szobaforgalmat sem, az tag⇄tag megy. Egy közös relé más kliensei annyit látnak, hogy az efemer peer id-d létezik (oldalbetöltések közt összekapcsolhatatlan), semmi többet.
Bárki, akinél ott a szoba linkje, teljes jogú tag — capability modell. Őrizd a linkeket, mint a kulcsokat.
Az identitás ellenőrzése emberi: a négy emojit más csatornán hasonlítod össze, mintha biztonsági számokat olvasnál fel. Ha nincs szerver, nincs hatóság sem, amely bárkiért kezeskedne.
A teljes mesh nagyjából egy tucat peerig skálázódik; afölött SFU kellene, az viszont már nem szerver nélküli.
Az előzmények addig élnek, amíg bármely tag eszköze megőrizte őket; a mindenki által tényleg elhagyott szoba feledésbe merül. Ez a terv, nem hiba.