concept-collection / hitandrun-commonview
Generate peer identity per page load
Persisting the key in localStorage made every tab in the same profile the same peer, so they ignored each other. Nothing else is persisted, so a fresh keypair per load is the right model: every tab is its own peer.
Jeremy Magland <jmagland@flatironinstitute.org> committed commit 4711a6c2c508 parent 40dabfd Browse files
3 changed files+9−21
CLAUDE.mdmodified+2−3View file
@@ -60,8 +60,7 @@ revisit `src/engine/engine.ts`.
6060 - Headless engine check (no browser): run the script + uihtml round-trip in
6161 Node against the installed numbl — see the "engine-test" pattern in git
6262 history / ask the user. `executeCode` is platform-agnostic.
63-- Full check: `npm run dev`, open in two different browser **profiles** (same
64- profile = same localStorage key = same peer). Kill the central tab to test
65- failover.
63+- Full check: `npm run dev`, open in two tabs (identity is generated per page
64+ load, so every tab is its own peer). Kill the central tab to test failover.
6665 - `npm run build` type-checks (`tsc -b`) and bundles; the numbl worker chunk
6766 is ~1.5 MB.
README.mdmodified+3−4View file
@@ -50,10 +50,9 @@ npm install # requires numbl >= 0.4.8 on npm (browser-embedding exports)
5050 npm run dev
5151 ```
5252
53-Open the printed URL in **two different browsers or profiles** (two tabs in the
54-same profile share the same localStorage key, so they'd be the *same* peer).
55-Drag the samples slider or press "New region" in either window and watch both
56-update; close the central window and watch the other take over.
53+Open the printed URL in two tabs (identity is per page load, so every tab is
54+its own peer). Drag the samples slider or press "New region" in either window
55+and watch both update; close the central window and watch the other take over.
5756
5857 ## How the engine embedding works
5958
src/p2p/identity.tsmodified+4−14View file
@@ -2,12 +2,12 @@ import * as secp from '@noble/secp256k1'
22
33 // The peer's identity is a secp256k1 / BIP340 (schnorr) keypair.
44 // - The x-only public key (hex) IS the peer ID.
5-// - The private key is persisted in localStorage so the identity survives reloads.
5+// - The key is generated fresh per page load (NOT persisted), so every tab —
6+// even in the same browser profile — is its own peer. Nothing else is
7+// persisted either; a reload is simply a new peer joining.
68 // - The same key signs both nostr events (for relay discovery/signaling) and
79 // every application-level message sent over WebRTC.
810
9-const STORAGE_KEY = 'hitandrun-commonview:privkey'
10-
1111 const toHex = (bytes: Uint8Array): string =>
1212 bytes.reduce((s, b) => s + b.toString(16).padStart(2, '0'), '')
1313
@@ -19,17 +19,7 @@ const fromHex = (hex: string): Uint8Array => {
1919 return out
2020 }
2121
22-const loadOrCreateSecretKey = (): Uint8Array => {
23- const existing = localStorage.getItem(STORAGE_KEY)
24- if (existing && existing.length === 64) {
25- return fromHex(existing)
26- }
27- const {secretKey} = secp.schnorr.keygen()
28- localStorage.setItem(STORAGE_KEY, toHex(secretKey))
29- return secretKey
30-}
31-
32-const secretKey = loadOrCreateSecretKey()
22+const {secretKey} = secp.schnorr.keygen()
3323 const publicKey = secp.schnorr.getPublicKey(secretKey)
3424
3525 /** This peer's ID = its x-only public key, as hex. */