// Probe (room-state P3 — LIVE router wiring): verify router._stateBaseProviders
// against a REAL keet daemon with a REAL keet room. Single daemon = creator
// path. Confirms the integration the in-process tests (injected providers)
// couldn't: getMembers from a real room, selfKey from getIdentity, and attest
// via real keet-core, all wired through router._getStateBase → StateBase.
//
// Run: bare test/probe-router-room-live.js (slow — keet-core boot + room create)
// PASS = providers resolve correctly AND state.set mints a writable gamestate
// base whose write reads back.
import fs from 'bare-fs'
import path from 'bare-path'
import os from 'bare-os'
import { startDaemon } from '../index.js'
const baseDir = path.join(os.tmpdir(), 'probe-router-room-' + Date.now())
fs.mkdirSync(baseDir, { recursive: true })
const socketPath = path.join(baseDir, 'daemon.sock')
let exitCode = 1
let daemon = null
const sleep = (ms) => new Promise(r => setTimeout(r, ms))
try {
console.log('[live] startDaemon + vault.setup…')
daemon = await startDaemon({ storage: baseDir, socketPath })
const router = daemon.router
await router._vaultSetup({ password: 'probe-router-pw' })
console.log('[live] create keet room…')
const room = await router._roomCreate({ title: 'gamestate-e2e-' + Math.random().toString(36).slice(2, 8) })
const roomId = typeof room === 'string' ? room : (room && (room.roomId || room.id || room.key))
console.log('[live] roomId:', roomId)
if (!roomId) throw new Error('no roomId from createRoom: ' + JSON.stringify(room))
// members can take a beat to materialize after create
let members = []
for (let i = 0; i < 20; i++) {
members = await router._members({ roomId }).catch(() => [])
if (members.length) break
await sleep(1000)
}
console.log('[live] members:', JSON.stringify(members))
// confirm the room's shared secret is reachable (drives deterministic v2 key)
try {
const rk = await router.core.backendAPI.api.core.getRoomKeys(roomId)
console.log('[live] getRoomKeys fields:', rk ? Object.keys(rk).join(',') : null)
} catch (e) { console.log('[live] getRoomKeys err:', e.message) }
// verify the members-only encryptionKey accessor (for base encryption, like keet)
try {
const idEnc = (await import('hypercore-id-encoding')).default
const rmKey = idEnc.decode(roomId)
const rm = router.core.internalOpenExistingRoom(rmKey)
const ek = rm && rm.encryptionKey
console.log('[live] room.encryptionKey:', ek ? ('len=' + ek.length + ' ' + Buffer.from(ek).toString('hex').slice(0, 12) + '…') : ek)
try { router.core.internalReleaseRoom(rm) } catch {}
} catch (e) { console.log('[live] room.encryptionKey err:', e.message) }
console.log('[live] build router providers for the room…')
const p = await router._stateBaseProviders(roomId)
console.log('[live] providers.roomSeed present (→ deterministic v2):', !!p.roomSeed)
const selfHex = p.selfKey ? Buffer.from(p.selfKey).toString('hex') : null
console.log('[live] providers.selfKey:', selfHex ? selfHex.slice(0, 16) + '…' : null)
const pm = await p.getMembers()
console.log('[live] providers.getMembers():', JSON.stringify(pm.map(m => ({ key: m.key.slice(0, 12) + '…', role: m.role }))))
// self must be a member, as admin (creator), keyed by identity pubkey
const selfMember = pm.find(m => m.key === selfHex)
console.log('[live] self in members as admin?', !!selfMember, selfMember && ('role=' + selfMember.role))
// attest/verifyAttest round-trip via the REAL providers
const testData = Buffer.concat([Buffer.alloc(32, 3), Buffer.alloc(32, 4)])
const proof = await p.attest(testData)
const verOk = proof ? p.verifyAttest(proof, testData, p.selfKey) : false
console.log('[live] providers.attest→verify round-trip:', verOk, ' proofBytes=', proof && proof.length)
// Drive state.set through the real router path → creator mints, write lands.
console.log('[live] state.set via router (creator path)…')
await router._stateSet({ roomId, key: 'world:e2e', value: 'hello-e2e' })
let info = await router._stateInfo({ roomId })
console.log('[live] state.info:', JSON.stringify(info))
const got = await router._stateGet({ roomId, key: 'world:e2e' }).catch(() => null)
console.log('[live] state.get world:e2e:', JSON.stringify(got))
// _stateGet wraps StateBase.get (itself {value,ts,_by}) → got.value.value is the leaf
const leaf = got && got.value && (got.value.value !== undefined ? got.value.value : got.value)
// D1c: when _getStateBase opened this room's StateBase it should have SEEDED
// the local membership copy (__members__) from the real keet room, so apply
// can gate optimistic admission (D1a/D1b, spec game-state.md §3.0/§4). The
// creator is writable, so the seed runs; read the projected self role back.
const sb = await router._getStateBase(roomId)
const projectedSelfRole = await sb.getProjectedRole(selfHex)
console.log('[live] D1c projected self role:', projectedSelfRole, '(expect', selfMember && selfMember.role, ')')
const providersOk = !!selfHex && !!selfMember && selfMember.role === 1 && verOk
const statePathOk = info && info.writable === true && !!info.stateBaseKey && leaf === 'hello-e2e'
const projectionOk = projectedSelfRole === (selfMember && selfMember.role)
if (providersOk && statePathOk && projectionOk) {
console.log('\n✓ LIVE router wiring works: real getMembers/selfKey/attest + state.set minted a writable base, the write reads back, AND D1c seeded projected membership (__members__)')
exitCode = 0
} else {
console.error('\n✗ providersOk=' + providersOk + ' statePathOk=' + statePathOk + ' projectionOk=' + projectionOk)
}
} catch (err) {
console.error('\n✗ FAIL:', err.message)
if (err.stack) console.error(err.stack)
} finally {
await Promise.race([
(async () => { try { await daemon?.core?.close() } catch {} })(),
new Promise(r => setTimeout(r, 5000))
])
try { fs.rmSync(baseDir, { recursive: true, force: true }) } catch {}
}
console.log(exitCode === 0 ? '\nPASS' : '\nFAIL')
Bare.exit(exitCode)