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/game4.git
cd seance
What it is
A party game you play with your feet. Gather 1–16 people in a home, agree
which real room is the Study, the Parlour and the
Cellar, and open one shared link on every phone. Each time the bell tolls
you have ~20 seconds to physically walk to a room and tap it. A ghost sweeps
one room — and it favours wherever the crowd stood last bell. Everyone caught
loses a candle; lose all three and you fade. Last lit candle wins the séance.
Every bell, one player is secretly the medium: their phone
alone names a room the spirit will shun — truthfully. They can share it,
keep it, or lie about it out loud. Where you actually stand is the
honor system; the walking is the point, not the enforcement. Installable PWA,
works from an iOS home screen.
How it works
No game server. Browsers are
js-libp2p peers: they dial a shared
circuit-relay-v2 peer over WSS, discover each other through gossipsub,
and upgrade to direct WebRTC — the circuit is signaling plus a
rate-limited fallback path. The room secret lives in the URL fragment
(#…) and never leaves the browser.
Rooms are cryptographic. Discovery is app-wide, so a
room is enforced by a room-scoped protocol id
(/seance/2/<HKDF(secret)> — wrong rooms fail protocol
negotiation) plus a mutual sealed hello: one AES-256-GCM envelope keyed
from the room secret and bound to both noise-authenticated peer ids.
After the hello, every pair's frames ride libp2p's noise encryption,
end-to-end even when routed through the relay. The p2p layer
(client/src/net/) is copied from
neon-grid.
One peer conducts. The lowest peer id runs the bell
timers and the ghost's dice, collects secret room picks, and broadcasts
tiny phase frames; everyone renders from them. Séances are seat-indexed so
guests joining or leaving can't corrupt one in flight. If the conductor's
phone dies the séance is voided and the next-lowest id takes over; late
joiners get a snapshot and watch from the veil until the next séance.
Hosting without an origin server. The client is a static
Vite build served as plain files — there is no backend anywhere. This page
you're reading works the same way.
What's in the repo
client/src/game/ — the game
TypeScript, no framework: typed wire protocol, host-authoritative séance
logic (bells, crowd-hungry ghost, medium hints, candle elimination),
WebAudio bell tolls, candlelit DOM/CSS scenes.
client/src/net/ — the p2p layer
js-libp2p assembly copied from neon-grid: relay bootstrap (identity
fetched from /.well-known/ap2p with a compiled-in fallback),
room crypto, the sealed-hello membership gate, and one long-lived
varint-framed stream per pair. Only the protocol id is game-specific.
deploy/ — the publish pipeline
publish-repo.sh builds this very site: landing page + bare
mirror exploded to loose objects (static file servers and git's dumb-HTTP protocol
agree on "every path must exist"), with an anonymity gate that refuses to
publish identity-bearing bytes.
Run it yourself
cd client && npm install && npm run dev # :4173
# open two tabs on the same #room URL and haunt yourself (keys 1/2/3 pick rooms)
Deploy: npm run build, then publish client/dist/ as
static files anywhere — IPFS, a CDN, a folder behind nginx. It needs nothing but
a signaling relay it's allowed to talk to (see src/game/config.ts).
Guarantees & limits, honestly
The relay operator sees connection metadata, peer ids, and (until the
WebRTC upgrade lands) opaque noise ciphertext and its sizes/timing. Not
content, not names, not who the ghost took.
Anyone with the room link can join and play — capability model. The
16-guest cap is enforced by the conductor (the mesh is O(n²); ~12 is the
practical ceiling).
The conductor is one of the players; a cheating conductor could load the
ghost's dice. Between friends that's the same trust as letting someone
shuffle the deck. Other players' picks can't be forged.
Your tapped room is taken on faith — the game can't see your feet.
That's a feature: the sprint to the cellar is the game.
MIT licensed. Built on js-libp2p, WebRTC and WebCrypto.
SÉANCE
un joc de salon bântuit pentru o casă, șaisprezece telefoane și trei
încăperi adevărate — 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/game4.git
cd seance
Ce este
Un joc de petrecere pe care îl joci cu picioarele. Adună 1–16 oameni într-o
casă, stabiliți care încăpere e Biroul, care Salonul și care
Pivnița, și deschideți același link pe fiecare telefon. De fiecare dată
când bate clopotul ai ~20 de secunde să mergi fizic într-o încăpere și să o
atingi. O fantomă mătură o încăpere — și o atrage locul unde a stat mulțimea la
clopotul trecut. Toți cei prinși pierd o lumânare; le pierzi pe toate trei și te
stingi. Ultima lumânare aprinsă câștigă ședința.
La fiecare clopot, un jucător e în secret mediumul: doar
telefonul lui numește o încăpere pe care spiritul o va ocoli — adevărat. Poate
să spună, să tacă sau să mintă cu voce tare. Unde stai de fapt rămâne
pe cuvânt de onoare; mersul e miza, nu controlul. PWA instalabil, merge de pe
ecranul de start al unui iPhone.
Cum funcționează
Fără server de joc. Browserele sunt peers
js-libp2p: sună un peer circuit-relay-v2
comun prin WSS, se descoperă prin gossipsub și trec pe WebRTC direct —
circuitul e semnalizare plus o cale de rezervă cu limită de trafic.
Secretul camerei stă în fragmentul URL-ului (#…) și nu pleacă
niciodată din browser.
Camerele sunt criptografice. Descoperirea e la nivel de
aplicație, așa că o cameră e impusă printr-un id de protocol derivat din
cameră (/seance/2/<HKDF(secret)> — camerele greșite pică
la negocierea protocolului) plus un hello sigilat reciproc: un plic
AES-256-GCM cu cheia din secretul camerei, legat de ambele peer id-uri
autentificate prin noise. După hello, cadrele fiecărei perechi merg pe
criptarea noise a lui libp2p, cap-la-cap chiar și prin releu. Stratul p2p
(client/src/net/) e copiat din
neon-grid.
Un peer conduce. Cel mai mic peer id ține ceasul
clopotelor și zarurile fantomei, strânge alegerile secrete de încăperi și
difuzează cadre de fază minuscule; restul doar randează. Ședințele sunt
indexate pe scaune, așa că oaspeții care vin sau pleacă nu pot corupe una
în desfășurare. Dacă telefonul maestrului moare, ședința se anulează și
preia următorul id; cine intră târziu primește un snapshot și privește de
dincolo de văl până la ședința următoare.
Găzduire fără server de origine. Clientul e un build Vite
static servit ca simple fișiere — nu există backend nicăieri. Pagina pe care
o citești funcționează la fel.
Ce e în repo
client/src/game/ — jocul
TypeScript, fără framework: protocol de rețea tipat, logică de ședință
autoritativă la gazdă (clopote, fantomă atrasă de aglomerație, șoapte pentru
medium, eliminare pe lumânări), dangăte WebAudio, scene DOM/CSS la lumina
lumânării.
client/src/net/ — stratul p2p
Asamblaj js-libp2p copiat din neon-grid: bootstrap de releu (identitatea
citită din /.well-known/ap2p cu rezervă compilată), criptografia
camerei, poarta de apartenență cu hello sigilat, și un singur stream
persistent cu cadre varint per pereche. Doar id-ul de protocol e specific
jocului.
deploy/ — conducta de publicare
publish-repo.sh construiește chiar situl ăsta: pagină de
prezentare + oglindă bare explodată în loose objects (serverele de fișiere
statice și dumb-HTTP-ul lui git cad de acord că „orice cale trebuie să
existe"), cu o poartă de anonimat care refuză să publice octeți care poartă
identitate.
Rulează-l singur
cd client && npm install && npm run dev # :4173
# deschide două taburi pe același URL #cameră și bântuie-te singur (tastele 1/2/3 aleg încăperile)
Deploy: npm run build, apoi publică client/dist/ ca
fișiere statice oriunde — IPFS, un CDN, un director în spatele lui nginx. N-are
nevoie decât de un releu de semnalizare cu care are voie să vorbească (vezi
src/game/config.ts).
Garanții și limite, pe bune
Operatorul releului vede metadate de conexiune, peer id-uri și (până se
face trecerea pe WebRTC) ciphertext noise opac cu dimensiunile și ritmul
lui. Nu conținut, nu nume, nu pe cine a luat fantoma.
Oricine are linkul camerei poate intra și juca — model de capabilitate.
Plafonul de 16 oaspeți îl impune maestrul (mesh-ul e O(n²); ~12 e plafonul
practic).
Maestrul e unul dintre jucători; un maestru care trișează poate măslui
zarurile fantomei. Între prieteni e aceeași încredere ca atunci când lași
pe cineva să amestece cărțile. Alegerile celorlalți nu pot fi falsificate.
Încăperea atinsă e luată pe cuvânt — jocul nu-ți vede picioarele. E o
calitate: sprintul spre pivniță e chiar jocul.
Licență MIT. Construit pe js-libp2p, WebRTC și WebCrypto.
SÉANCE
kísértetjárta szalonjáték egy házra, tizenhat telefonra és három
igazi szobára — 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/game4.git
cd seance
Mi ez
Egy partijáték, amit a lábaddal játszol. Gyűjts össze 1–16 embert egy házban,
beszéljétek meg, melyik igazi szoba a dolgozószoba, melyik a
szalon és melyik a pince, aztán nyissátok meg ugyanazt a linket
minden telefonon. Ahányszor megkondul a harang, ~20 másodperced van ténylegesen
átsétálni egy szobába és rákoppintani. Egy szellem végigsöpör egy szobán — és
oda húz, ahol a tömeg állt az előző harangszónál. Akit elkap, elveszít egy
gyertyát; ha mindhármat elveszted, elenyészel. Az utolsó égő gyertya nyeri a
szeánszot.
Minden harangszónál egy játékos titokban a médium: egyedül
az ő telefonja nevez meg egy szobát, amit a szellem elkerül — igazat mondva.
Elmondhatja, magában tarthatja, vagy hazudhat róla fennhangon. Hogy
valójában hol állsz, becsületbeli ügy; a séta a lényeg, nem az
ellenőrzés. Telepíthető PWA, iOS kezdőképernyőről is megy.
Hogyan működik
Nincs játékszerver. A böngészők
js-libp2p peerek: egy közös circuit-relay-v2
peert hívnak WSS-en, gossipsubbal fedezik fel egymást, majd közvetlen
WebRTC-re váltanak — a circuit jelzésre való, plusz egy korlátozott sávú
tartalék útvonal. A szoba titka az URL fragmentben él (#…),
és soha nem hagyja el a böngészőt.
A szobák kriptográfiaiak. A felfedezés app-szintű, ezért
a szobát egy szoba-hatókörű protokoll-id kényszeríti ki
(/seance/2/<HKDF(secret)> — rossz szobával megbukik a
protokoll-egyeztetés), plusz egy kölcsönös lepecsételt hello: egyetlen
AES-256-GCM boríték a szoba titkából származtatott kulccsal, mindkét
noise-hitelesített peer id-hez kötve. A hello után minden pár keretei a
libp2p noise-titkosításán mennek, végponttól végpontig, a relén át is. A
p2p réteg (client/src/net/) a
neon-gridből van másolva.
Az egyik peer vezényel. A legkisebb peer id kezeli a
harang időzítőit és a szellem kockáit, begyűjti a titkos szobaválasztásokat,
és apró fázis-kereteket sugároz; a többiek ezekből rajzolnak. A szeánszok
szék-indexeltek, így az érkező-távozó vendégek nem ronthatnak el egy
folyamatban lévőt. Ha a mester telefonja kimúlik, a szeánsz érvénytelen, és
a következő legkisebb id veszi át; a későn érkezők pillanatképet kapnak, és
a fátyol mögül néznek a következő szeánszig.
Hosztolás origin-szerver nélkül. A kliens statikus Vite
build, sima fájlokként kiszolgálva — nincs sehol backend. Ez az oldal is
pontosan így működik.
A neon-gridből másolt js-libp2p összeállítás: relé-bootstrap (az identitás
a /.well-known/ap2p-ből jön, beforgatott tartalékkal),
szoba-kriptográfia, a lepecsételt hello tagsági kapu, és páronként egyetlen
hosszú életű, varint-keretezett stream. Csak a protokoll-id játékspecifikus.
deploy/ — a publikáló futószalag
A publish-repo.sh építi ezt az oldalt: nyitólap + bare tükör
loose objectekre bontva (a statikus fájlszerverek és a git dumb-HTTP-je
egyetértenek abban, hogy „minden útvonalnak léteznie kell"), és egy
anonimitás-kapu, amely nem enged identitáshordozó bájtokat publikálni.
Futtasd magad
cd client && npm install && npm run dev # :4173
# nyiss két fület ugyanarra a #szoba URL-re, és kísértsd magad (az 1/2/3 billentyű szobát választ)
Telepítés: npm run build, aztán tedd ki a
client/dist/-et statikus fájlokként bárhová — IPFS, CDN, egy mappa
nginx mögött. Semmi másra nincs szüksége, csak egy jelző-relére, amivel
beszélhet (lásd src/game/config.ts).
Garanciák és korlátok, őszintén
A relé üzemeltetője kapcsolati metaadatokat, peer id-kat és (amíg a
WebRTC-váltás meg nem történik) átlátszatlan noise-ciphertextet lát, annak
méretével és ütemével. Tartalmat, neveket, a szellem áldozatát nem.
Bárki, akinél ott a szoba linkje, beléphet és játszhat — capability
modell. A 16 vendéges plafont a mester tartatja be (a mesh O(n²); ~12 a
gyakorlati plafon).
A mester az egyik játékos; egy csaló mester cinkelheti a szellem
kockáit. Barátok közt ez ugyanaz a bizalom, mint amikor hagyod, hogy valaki
megkeverje a paklit. A többiek választásait nem lehet hamisítani.
A rákoppintott szobát elhisszük — a játék nem látja a lábadat. Ez nem
hiba: a sprint a pincéig maga a játék.
MIT licenc. js-libp2p-re, WebRTC-re és WebCryptóra épül.