OverviewFilesCommitsIssuesDocsReleasesPeers

favela-node / test / community.test.mjs

96 lines · javascript ref:
// favela-node — the co-owned community, end to end over a local testnet:
//   - FOUNDER mints the community DID + boots its co-owned repo + creates the linked keyless CommunityRoom.
//   - a CO-ADMIN (attested to the community root) boots the SAME DID's repo and CO-SIGNS a feed.generator
//     record; it converges to the founder (one shared, co-owned repo — the multiswarm-PDS pattern).
//   - a KEYLESS reader reads the CommunityRoom's community DID + roster holding no key.
// This proves spec/lens-export.md §3 (two substrates) + §8 phase 1 (DID + linkage). Run: bare test/community.test.mjs
import '../lib/bare-globals.js' // FIRST — shim TextEncoder/process for @noble/* at module load (Bare)
import Corestore from 'corestore'
import b4a from 'b4a'
import createTestnet from '@hyperswarm/testnet'
import { deviceKeetKeyPair } from 'atproto-room/lib/membership.js'
import { RepoBase } from 'atproto-room/lib/repo-base.js'
import { CommunityRoom } from 'favela-community'
import { createCommunity, attestAdmin, bootCommunityAdmin } from '../index.js'

const assert = (c, m) => { if (!c) { console.error('FAIL:', m); process.exit(1) } console.log('  ok -', m) }
const sleep = (ms) => new Promise((r) => setTimeout(r, ms))
async function until (fn, ms = 30000) { const end = Date.now() + ms; while (Date.now() < end) { try { if (await fn()) return true } catch {} await sleep(500) } return false }
const stamp = Date.now()
const tmp = (s) => `data/fn-${s}-${stamp}`

// community root + two admin devices attested to it (the founder holds the root; admins get bundles)
const ROOT = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about'
const FOUNDER_DEV = 'legal winner thank year wave sausage worth useful legal winner thank yellow'
const ADMIN2_DEV = 'zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong'
const GEN = 'app.bsky.feed.generator'

const testnet = await createTestnet(3)
const swarmOpts = { bootstrap: testnet.bootstrap }

// admin2's device pubkey (known up front so the founder seeds it into the roster at create)
const admin2Kp = await deviceKeetKeyPair(ADMIN2_DEV)

// ── FOUNDER: mint the community DID + co-owned repo + linked keyless CommunityRoom ──
const roomStore = new Corestore(tmp('room'))
const c = await createCommunity({
  rootMnemonic: ROOT,
  founderDeviceMnemonic: FOUNDER_DEV,
  adminDeviceKeys: [admin2Kp.publicKey], // admit admin2 into the roster now
  name: 'Cat Lovers',
  roomStore,
  repoStorage: tmp('founder-repo'),
  handle: 'catlovers.test',
  swarmOpts
})
assert(/^did:plc:/.test(c.did), 'community minted an atproto did:plc')
assert((await c.room.communityDid()) === c.did, 'the linked CommunityRoom records the community DID (meta:did)')
assert(c.members.length === 2, 'roster seeded with founder + admin2 (two equal admins)')

// founder adds a member to the roster (the room's write layer — mods manage membership)
await c.room.addMember('did:plc:member2222222222222222222')

// ── CO-ADMIN: founder attests admin2, admin2 boots the SAME DID's repo ──
const bundle = await attestAdmin({ rootMnemonic: ROOT, deviceMnemonic: ADMIN2_DEV })
assert(bundle.deviceProof && bundle.devicePublicKey === b4a.toString(admin2Kp.publicKey, 'hex'), 'founder attested admin2 (root→device proof)')
const admin2 = await bootCommunityAdmin({
  storage: tmp('admin2-repo'),
  did: c.did,
  signingKey: c.signingKey,
  rootPub: c.rootPub,
  deviceMnemonic: ADMIN2_DEV,
  deviceProof: bundle.deviceProof,
  members: c.members,
  swarmOpts
})

// admin2 CO-SIGNS a feed.generator record into the community repo (poll until attestation admits it)
const genRecord = { $type: GEN, did: c.did, displayName: 'Cat Lovers', createdAt: '2026-07-15T00:00:00.000Z' }
let admitted = false
for (let i = 0; i < 40 && !admitted; i++) {
  try { await admin2.repo.putRecord(GEN, 'catlovers', genRecord) } catch {}
  await admin2.sb.set('__ping__', { i }).catch(() => {})
  await sleep(1500)
  admitted = await admin2.sb.effectiveWritable().catch(() => false)
}
assert(admitted, 'CO-ADMIN admin2 was admitted as a writer via attestation (co-owned repo)')

// convergence: the FOUNDER sees admin2's feed.generator record — one shared, co-owned community repo
const founderRepo = new RepoBase(c.sb, { did: c.did })
const seen = await until(async () => { await c.sb.base.update().catch(() => {}); const r = await founderRepo.getRecord(GEN, 'catlovers'); return r && r.did === c.did ? r : null })
assert(seen, 'FOUNDER sees admin2\'s co-signed app.bsky.feed.generator record (multiswarm-PDS: co-owned repo)')

// ── KEYLESS reader: the CommunityRoom DID + roster, holding no key (favela-web's path) ──
const readerStore = new Corestore(tmp('reader'))
const reader = await CommunityRoom.openReadonly(readerStore, c.room.key)
const rs1 = reader.base.replicate(true); const rs2 = c.room.base.replicate(false)
rs1.on('error', () => {}); rs2.on('error', () => {}); rs1.pipe(rs2).pipe(rs1)
assert(await until(async () => (await reader.communityDid()) === c.did && (await reader.members()).includes('did:plc:member2222222222222222222')), 'KEYLESS reader reads the community DID + member roster (no key held)')
assert(!reader.writable, 'the keyless reader holds no key')

console.log('\nFAVELA-NODE OK: community = co-owned DID (two admins co-sign the repo) + linked keyless CommunityRoom roster')
try { rs1.destroy(); rs2.destroy() } catch {}
await c.sb.close(); await admin2.sb.close(); await c.room.close(); await reader.close()
try { await roomStore.close(); await readerStore.close() } catch {}
await testnet.destroy()