// GOVERNANCE — proof-gated multi-writer admission (block-until-admitted golden rule) + roster
// projection. A joiner proves membership, is admitted as a plain writer, writes a record, and both
// co-owners converge to the IDENTICAL projected head. A non-member is rejected (its write THROWS
// rather than being silently rolled back).
import test from 'brittle'
import { mkdtempSync, rmSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import crypto from 'hypercore-crypto'
import { deriveAtprotoKey, createAtprotoIdentity } from 'atproto-identity'
import { AtprotoBase } from '../lib/base.js'
const MNEMONIC = 'test test test test test test test test test test test junk'
const ID = createAtprotoIdentity(MNEMONIC, { handle: 'x.7rnx.net', pdsUrl: 'https://pds.7rnx.net' })
const KEY = deriveAtprotoKey(MNEMONIC)
const post = (text) => ({ $type: 'app.bsky.feed.post', text, createdAt: '2026-07-23T00:00:00.000Z', langs: ['en'] })
const hex = (b) => b.toString('hex')
const sleep = (ms) => new Promise((r) => setTimeout(r, ms))
function mk (opts) {
const dir = mkdtempSync(join(tmpdir(), 'gov-'))
const base = new AtprotoBase({ path: dir, did: ID.did, privateKey: KEY.privateKey, ackInterval: 100, ...opts })
return { base, dir, close: async () => { await base.close(); rmSync(dir, { recursive: true, force: true }) } }
}
const pipe = (a, b) => { const s1 = a.base.base.replicate(true); const s2 = b.base.base.replicate(false); s1.pipe(s2).pipe(s1); return () => { try { s1.destroy() } catch {} ; try { s2.destroy() } catch {} } }
async function settle (...ws) { for (let i = 0; i < 15; i++) { for (const w of ws) { try { await w.base.base.update() } catch {} } await sleep(60) } }
test('governance: a member joiner is admitted (block-until-admitted) → writes → identical head', async (t) => {
const creatorId = crypto.keyPair()
const joinerId = crypto.keyPair()
const roster = [{ key: hex(creatorId.publicKey), role: 1 }, { key: hex(joinerId.publicKey), role: 3 }]
const C = mk({ identity: creatorId, members: roster })
await C.base.ready()
const J = mk({ bootstrap: C.base.base.key, identity: joinerId })
await J.base.ready()
const unpipe = pipe(C, J)
t.teardown(async () => { unpipe(); await C.close(); await J.close() })
await C.base.projectMembers(roster) // land __members__ before the joiner rings
await settle(C, J)
// Joiner writes — ensureWritable rings the CONTENT-FREE doorbell and blocks until admitted.
await J.base.putRecord('app.bsky.feed.post', '3joiner', post('hello from the joiner'))
t.ok(J.base.writable, 'joiner was admitted as a writer (block-until-admitted)')
await settle(C, J)
await C.base.settled()
const recsC = await C.base.indexedRecords()
t.ok(recsC.find((r) => r.key.endsWith('3joiner')), "joiner's record replicated into the creator's shared view")
await C.base.settled(); await J.base.settled()
const hC = await C.base.head()
const hJ = await J.base.head()
t.is(hC.commitCid.toString(), hJ.commitCid.toString(), 'identical projected head across co-owners after admission (deterministic, no signer election)')
})
test('governance: a NON-member is rejected — write throws, never silently rolled back', async (t) => {
const creatorId = crypto.keyPair()
const outsiderId = crypto.keyPair() // NOT in the roster
const roster = [{ key: hex(creatorId.publicKey), role: 1 }]
const C = mk({ identity: creatorId, members: roster })
await C.base.ready()
const X = mk({ bootstrap: C.base.base.key, identity: outsiderId })
await X.base.ready()
const unpipe = pipe(C, X)
t.teardown(async () => { unpipe(); await C.close(); await X.close() })
await C.base.projectMembers(roster)
await settle(C, X)
await t.exception(() => X.base.ensureWritable({ timeoutMs: 3000 }), 'non-member ensureWritable THROWS (not admitted) rather than allowing a silent-loss write')
t.absent(X.base.writable, 'outsider never became writable')
})