// (A) admission: a peer proves control of its keet ROOM writer key (manifest + sig);
// the indexer derives the device key, verifies the sig, and resolves device→member→role.
import { StateBaseV3 } from '../lib/state-base-v3.js'
import createTestnet from 'hyperdht/testnet'
import Corestore from 'corestore'
import Autobase from 'autobase'
import crypto from 'hypercore-crypto'
import b4a from 'b4a'
import fs from 'bare-fs'
import path from 'bare-path'
import os from 'bare-os'
const ROOT = path.join(os.tmpdir(), 'sbadm-' + Bare.pid)
try { fs.rmSync(ROOT, { recursive: true, force: true }) } catch {}
const hex = (b) => b4a.toString(b, 'hex')
let pass = 0, fail = 0
const ok = (c, m) => { if (c) { pass++; console.log(' ok ' + m) } else { fail++; console.log(' FAIL ' + m) } }
const sleep = (ms) => new Promise(r => setTimeout(r, ms))
// a "keet room writer" = keypair + manifest + deviceKey (deviceKey = Hypercore.key(manifest))
async function keetWriter (dir) {
const store = new Corestore(dir)
const base = new Autobase(store, null, { apply: async () => {}, open: (s) => s.get('v'), valueEncoding: 'json' })
await base.ready()
return { keyPair: base.local.keyPair, manifest: base.local.manifest, deviceKey: base.local.key }
}
async function main () {
const testnet = await createTestnet(3)
const bootstrap = testnet.bootstrap
const roomSeed = crypto.randomBytes(32)
const wA = await keetWriter(path.join(ROOT, 'wA'))
const wD = await keetWriter(path.join(ROOT, 'wD'))
const wE = await keetWriter(path.join(ROOT, 'wE')) // NOT a member
// member keys (keet per-room member identities), lex pick creator
const mA = crypto.randomBytes(32), mD = crypto.randomBytes(32)
// roster A projects: member -> role + that member's keet writer device keys
const roster = [
{ key: hex(mA), role: 1, devices: [hex(wA.deviceKey)] },
{ key: hex(mD), role: 3, devices: [hex(wD.deviceKey)] }
]
const amCreatorKey = (hex(mA) < hex(mD)) ? mA : mD // lex-lowest admin = creator; mA is the only admin
const opt = (selfKey, w) => ({
roomSeed, encryptionKey: roomSeed, network: true, swarmOpts: { bootstrap },
selfKey, getMembers: async () => roster, roomWriterKeyPair: w.keyPair, roomWriterManifest: w.manifest
})
// A = admin/creator
const A = new StateBaseV3(path.join(ROOT, 'A'), crypto.randomBytes(32), opt(mA, wA))
await A.ready()
ok(A.writable, '1. A (admin) opens writable = creator')
await A.projectMembers(roster)
// D = peer member, joins A's room
const D = new StateBaseV3(path.join(ROOT, 'D'), A._roomKey, opt(mD, wD))
await D.ready()
ok(!D.writable, '2. D (peer) opens NOT writable')
// D writes -> optimistic manifest proof -> A admits via device->member->role
await D.set('world:players:D', { pos: [1, 2, 3] })
const deadline = Date.now() + 30000
while (Date.now() < deadline) {
if (A.writable) { try { await A.set('__hb__', Date.now()) } catch {} }
await A.base.update().catch(() => {}); await D.base.update().catch(() => {})
await sleep(200)
const got = await A.list('world:players:')
if (got['world:players:D']) break
}
const aSees = await A.list('world:players:')
ok(aSees['world:players:D'] != null, '3. D ADMITTED via keet-writer manifest proof (write in A view)')
// NEGATIVE: E is not in the roster -> admission must be denied
const E = new StateBaseV3(path.join(ROOT, 'E'), A._roomKey, opt(crypto.randomBytes(32), wE))
await E.ready()
await E.set('world:players:E', { pos: [9, 9, 9] })
const d2 = Date.now() + 12000
while (Date.now() < d2) { await A.base.update().catch(() => {}); await E.base.update().catch(() => {}); await sleep(200) }
const aSees2 = await A.list('world:players:')
ok(aSees2['world:players:E'] == null, '4. E (NOT a member) DENIED — write never lands in A view')
console.log('\n[sb-admission] pass=' + pass + ' fail=' + fail)
Bare.exit(fail ? 1 : 0)
}
main().catch(e => { console.error('THREW', e && e.stack ? e.stack : e); Bare.exit(2) })