/ concept-collection / commonview
Sign in
concept-collection / commonview
commonview / README.md
47 lines · 2.2 KBPreviewCodeBlameHistoryRaw
1# CommonView
3A minimal peer-to-peer web app. Everyone who opens the page becomes a peer in a
4single shared network. Peers discover each other over **nostr** relays and
5communicate over **WebRTC** (full mesh). It borrows the discovery/signaling
6approach from [trystero](https://github.com/dmotz/trystero)'s nostr strategy,
7but only the parts needed here.
9For now the shared state is just a counter — a stand-in for the richer state
10(including binary blobs) planned later.
12## How it works
14- **Identity** ([src/p2p/identity.ts](src/p2p/identity.ts)) — each peer has a
15 secp256k1 / BIP340 (schnorr) keypair, persisted in `localStorage`. The x-only
16 public key **is** the peer ID. The same key signs every nostr event and every
17 application message.
18- **Discovery/signaling** ([src/p2p/nostr.ts](src/p2p/nostr.ts)) — peers
19 announce on a hashed "root topic" and listen there to find each other. WebRTC
20 offers/answers/ICE are delivered to a per-peer topic. Only ephemeral nostr
21 event kinds are used, so nothing is stored on relays.
22- **WebRTC** ([src/p2p/peer.ts](src/p2p/peer.ts)) — a trimmed data-channel
23 wrapper. Glare is avoided by letting only the peer with the smaller ID make
24 the offer.
25- **Mesh + state** ([src/p2p/network.ts](src/p2p/network.ts)) — every peer
26 connects to every other peer. On connect, peers exchange a self-reported
27 timestamp of when they joined. The **oldest** peer (earliest timestamp, ties
28 broken by ID) is the **central** peer and holds the authoritative state.
29 - Commands (`+`/`−`) are sent to the central peer, which applies them and
30 broadcasts the new state to everyone.
31 - Every message is a signed envelope (`{data, from, sig}`) and is verified on
32 receipt.
33 - If the central peer leaves, the next-oldest peer becomes central and its
34 last-known state becomes the source of truth.
35 - A page reload, or losing and regaining the network, is a **reconnect** (new
36 timestamp). State is never persisted, so once all peers leave it resets.
38## Run
40```
41npm install
42npm run dev
43```
45Open the printed URL in **two different browsers or profiles** (two tabs in the
46same profile share the same `localStorage` key, so they'd be the *same* peer).
47Watch the roster populate and the counter stay in sync.
moveopenescclose