// Per-room swarm.join layer (Keet activateRoom — issue #34/#37).
//
// Each org/repo is its own autobase ("room"); a peer rendezvous for a
// SPECIFIC room by joining that room's discoveryKey on the shared swarm,
// NOT the owner's daemon identity topic. This test forces the two
// daemons onto DIFFERENT identity topics (distinct raw identities), so
// the ONLY way the peer can reach the owner is via the per-room topic.
// Proves: (1) per-room rendezvous works across daemons that share no
// daemon topic; (2) peer-mode scope is isolated — a room never joined is
// unreachable.
import test from 'brittle'
import process from '#node:process'
import { mkdtempSync } from '#node:fs'
import { join } from '#node:path'
import { tmpdir } from '#node:os'
import b4a from 'b4a'
import createTestnet from '@hyperswarm/testnet'
import { startPearGit } from '../index.js'
let n = 0
const tmpStorage = () => mkdtempSync(join(tmpdir(), 'pgv3-prsj-' + (n++) + '-'))
const tmpSock = () => join(mkdtempSync(join(tmpdir(), 'pgv3-prsj-sock-' + (n++) + '-')), 'svc.sock')
async function waitFor (pred, ms = 12000) {
const t0 = Date.now()
while (Date.now() - t0 < ms) {
try { if (await pred()) return true } catch {}
await new Promise((r) => setTimeout(r, 50))
}
return false
}
test('per-room swarm.join: peer rendezvous via the ORG topic only (different daemon topics, no leak)', async (t) => {
// Each daemon gets its OWN raw identity → its OWN daemon topic.
process.env.PGV3_USE_RAW_IDENTITY = '1'
const testnet = await createTestnet(3, t.teardown)
const nodeA = await startPearGit({
storage: tmpStorage(), uds: tmpSock(), http: { port: 0 },
swarm: { swarm: { bootstrap: testnet.bootstrap } }, deploy: false, sync: false
})
t.teardown(async () => { try { await nodeA.close() } catch {} })
const nodeB = await startPearGit({
storage: tmpStorage(), uds: tmpSock(), http: { port: 0 },
swarm: { swarm: { bootstrap: testnet.bootstrap } }, deploy: false, sync: false
})
t.teardown(async () => { try { await nodeB.close() } catch {} })
// Sanity: the daemons are NOT on a shared identity topic. Without the
// per-room join, the peer could never find the owner.
t.not(
b4a.toString(nodeA.svc.swarm.topic, 'hex'),
b4a.toString(nodeB.svc.swarm.topic, 'hex'),
'daemons are on DIFFERENT identity topics'
)
// A owns alpha + beta; announce each room on the shared swarm.
await nodeA.orgs.create('alpha', 'group', { displayName: 'Alpha' })
await nodeA.orgs.create('beta', 'group', { displayName: 'Beta' })
nodeA.svc.activateRoom(nodeA.orgs.getOrgRoom('alpha').discoveryKey)
nodeA.svc.activateRoom(nodeA.orgs.getOrgRoom('beta').discoveryKey)
const { invitation } = await nodeA.orgs.createOrgInvite('alpha', { role: 'reader', swarm: nodeA.svc.swarm.swarm })
// B pairs into ONLY alpha over the swarm: mount it + join alpha's room topic.
const joined = await nodeB.orgs.joinOrg(invitation, { swarm: nodeB.svc.swarm.swarm })
t.is(joined.name, 'alpha', 'peer joined alpha')
nodeB.svc.activateRoom(nodeB.orgs.getOrgRoom('alpha').discoveryKey)
// B replicates alpha purely via the per-room rendezvous.
const sawAlpha = await waitFor(async () => {
await nodeB.orgs.sync()
const o = await nodeB.orgs.get('alpha')
return !!(o && o.displayName === 'Alpha')
})
t.ok(sawAlpha, 'peer replicated alpha via the org room topic (no shared daemon topic)')
// Isolation: B never joined/mounted beta → structurally unreachable.
t.absent(await nodeB.orgs.get('beta'), 'beta not visible (never joined its room topic)')
t.alike(nodeB.orgs.listOrgKeys().map((o) => o.name), ['alpha'], 'peer tracks only alpha')
})