OverviewFilesCommitsIssuesDocsReleasesPeers

favela-node commits / ee04b5f

favela-node: loser-club mint + PLC-submit scripts (community DID go-live)
scripts/mint-loser-club.mjs — mint the loser-club community did:plc (handle loser-club.7rnx.net, roster = p2p.7rnx.net + ashatron.7rnx.net), persist to ~/.config/loser-club, save secrets, print the genesis op. scripts/submit-loser-club-plc.mjs — re-derive deterministically, verify the DID, submit the genesis to plc.directory (irreversible), confirm via GET /:did/data. DONE: did:plc:wqnwulql7ndcz6xmtvaovl6u is LIVE on plc.directory with #bsky_fg (BskyFeedGenerator) -> pds.7rnx.net, #atproto_labeler, #atproto_label. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ee04b5fff60a4cad4faf3c930da481e4d1cdf33f
by plankatron <fleeky@gmail.com> · 2026-07-15T16:40:57.000Z

Changed files (4) +110 -4

package-lock.json +16 -4
……@@ -1,18 +1,19 @@
11{
22 "name": "favela-node",
3 "version": "0.1.0",
3 "version": "0.1.2",
44 "lockfileVersion": 3,
55 "requires": true,
66 "packages": {
77 "": {
88 "name": "favela-node",
9 "version": "0.1.0",
9 "version": "0.1.2",
1010 "dependencies": {
1111 "atproto-identity": "file:../atproto-identity",
1212 "atproto-room": "file:../atproto-room",
1313 "b4a": "^1.8.1",
1414 "bare-path": "^3.0.0",
1515 "bare-process": "^4.0.0",
16 "bip39-mnemonic": "^2.5.0",
1617 "corestore": "^7.4.0",
1718 "favela-community": "file:../favela-community",
1819 "hypercore-crypto": "^3.4.0",
……@@ -23,7 +24,7 @@
2324 }
2425 },
2526 "../atproto-identity": {
26 "version": "0.0.1",
27 "version": "0.0.2",
2728 "hasInstallScript": true,
2829 "dependencies": {
2930 "@ipld/dag-cbor": "^9.2.1",
……@@ -72,7 +73,7 @@
7273 }
7374 },
7475 "../favela-community": {
75 "version": "0.1.3",
76 "version": "0.1.4",
7677 "dependencies": {
7778 "autobase": "^7.28.0",
7879 "b4a": "^1.8.1",
……@@ -425,6 +426,17 @@
425426 "integrity": "sha512-6RjV/3mSZORlMdpUaQ6rUSpG637cZm0//E54YYGtQg1c1O+AbZP8UTdJ/TchsDZcTVLmyWZcseBfp2HBeXUXOQ==",
426427 "license": "MIT"
427428 },
429 "node_modules/bip39-mnemonic": {
430 "version": "2.5.0",
431 "resolved": "https://registry.npmjs.org/bip39-mnemonic/-/bip39-mnemonic-2.5.0.tgz",
432 "integrity": "sha512-hgCxFHN129ta0bHl9VPykoqCPagBUr2oaXjfSbfeXAPe+y/byxwjPFFEFTMNO/1T/GSoMP1cOXHa2ZaprHG1kg==",
433 "license": "Apache-2.0",
434 "dependencies": {
435 "b4a": "^1.6.6",
436 "nanoassert": "^2.0.0",
437 "sodium-universal": "^5.0.1"
438 }
439 },
428440 "node_modules/bits-to-bytes": {
429441 "version": "1.3.0",
430442 "resolved": "https://registry.npmjs.org/bits-to-bytes/-/bits-to-bytes-1.3.0.tgz",
package.json +1 -0
……@@ -13,6 +13,7 @@
1313 "b4a": "^1.8.1",
1414 "bare-path": "^3.0.0",
1515 "bare-process": "^4.0.0",
16 "bip39-mnemonic": "^2.5.0",
1617 "corestore": "^7.4.0",
1718 "favela-community": "file:../favela-community",
1819 "hypercore-crypto": "^3.4.0",
scripts/mint-loser-club.mjs +67 -0
……@@ -0,0 +1,67 @@
1// Mint the loser-club community DID (step 1, LOCAL — no PLC submit here). Creates a co-owned community
2// did:plc (handle loser-club.7rnx.net) with a keyless CommunityRoom whose roster = p2p.7rnx.net + ashatron.
3// Persists to ~/.config/loser-club and saves the secrets + prints the genesis op for the PLC submit.
4// Run once: bare scripts/mint-loser-club.mjs
5import '../lib/bare-globals.js'
6import process from 'bare-process'
7import fs from 'bare-fs'
8import Corestore from 'corestore'
9import { generateMnemonic } from 'bip39-mnemonic'
10import { createCommunity } from '../index.js'
11
12const HOME = process.env.HOME
13const DIR = HOME + '/.config/loser-club'
14const SECRETS = DIR + '/secrets.json'
15if (fs.existsSync(SECRETS)) { console.error('refusing to overwrite existing', SECRETS, '(loser-club already minted)'); process.exit(1) }
16fs.mkdirSync(DIR + '/room', { recursive: true })
17fs.mkdirSync(DIR + '/repo', { recursive: true })
18
19const WJ62 = 'did:plc:wj62ebbusln6gxo5azo6ogzo' // p2p.7rnx.net
20const ASHATRON = 'did:plc:e2jevhuurrhlm36ufk7fklpg' // ashatron.7rnx.net
21const MEMBRANE = 'https://pds.7rnx.net'
22
23const rootMnemonic = generateMnemonic()
24const founderDeviceMnemonic = generateMnemonic()
25
26const roomStore = new Corestore(DIR + '/room')
27const c = await createCommunity({
28 rootMnemonic,
29 founderDeviceMnemonic,
30 handle: 'loser-club.7rnx.net',
31 name: 'loser-club',
32 feedGenEndpoint: MEMBRANE,
33 labelerEndpoint: MEMBRANE,
34 roomStore,
35 repoStorage: DIR + '/repo',
36 network: false
37})
38await c.room.addMember(WJ62)
39await c.room.addMember(ASHATRON)
40
41const secrets = {
42 did: c.did,
43 handle: 'loser-club.7rnx.net',
44 rootMnemonic,
45 founderDeviceMnemonic,
46 signingKey: c.signingKey,
47 roomKey: c.room.key,
48 members: [WJ62, ASHATRON],
49 createdAt: null // stamped by the operator; Date.now() is unavailable under bare here
50}
51fs.writeFileSync(SECRETS, JSON.stringify(secrets, null, 2))
52try { fs.chmodSync(SECRETS, 0o600) } catch {}
53
54console.log('=== loser-club minted (LOCAL — not yet PLC-submitted) ===')
55console.log('DID: ' + c.did)
56console.log('handle: loser-club.7rnx.net')
57console.log('roomKey: ' + c.room.key)
58console.log('members: ' + (await c.room.members()).join(', '))
59console.log('secrets: ' + SECRETS + ' (root mnemonic + keys — KEEP SAFE)')
60console.log('\n--- genesis op (submit this to plc.directory: POST https://plc.directory/<did>) ---')
61console.log(JSON.stringify(c.identity.signedOp))
62console.log('--- end genesis op ---')
63
64await c.sb.close()
65await c.room.close()
66try { await roomStore.close() } catch {}
67process.exit(0)
scripts/submit-loser-club-plc.mjs +26 -0
……@@ -0,0 +1,26 @@
1// Step 2 — SUBMIT loser-club's genesis op to plc.directory. ⚠️ IRREVERSIBLE (a permanent append to the
2// global PLC op-log). Re-derives the identity deterministically from the saved root mnemonic, verifies the
3// DID matches the minted one, validates the op (checkGenesisOp), POSTs, and confirms via GET /:did/data.
4// Run under node (global fetch): node scripts/submit-loser-club-plc.mjs
5import fs from 'node:fs'
6import { createSwarmIdentity } from 'atproto-identity'
7import { PlcDirectory } from 'atproto-identity/lib/plc-directory.js'
8
9const SECRETS = process.env.HOME + '/.config/loser-club/secrets.json'
10const s = JSON.parse(fs.readFileSync(SECRETS, 'utf8'))
11const MEMBRANE = 'https://pds.7rnx.net'
12
13const id = createSwarmIdentity(s.rootMnemonic, { handle: s.handle, pdsUrl: MEMBRANE, feedGenEndpoint: MEMBRANE, labelerEndpoint: MEMBRANE })
14if (id.did !== s.did) { console.error('DID mismatch — re-derived', id.did, 'vs saved', s.did, '— REFUSING to submit'); process.exit(1) }
15console.log('re-derived DID matches:', id.did)
16
17const plc = new PlcDirectory({}) // uses global fetch (node)
18console.log('submitting genesis op to plc.directory …')
19try {
20 const data = await plc.submitGenesis({ did: id.did, signedOp: id.signedOp, confirm: true })
21 console.log('✓ SUBMITTED + confirmed. PLC data:')
22 console.log(JSON.stringify(data))
23} catch (e) {
24 console.error('submit FAILED:', e.message)
25 process.exit(1)
26}