OverviewFilesCommitsIssuesDocsReleasesPeers

atproto-room / test / signer-presence-channel.test.mjs

93 lines · javascript ref:
// attachPresence (S1 wiring) — proves the presence handshake over a REAL encrypted connection (a
// SecretStream pair = exactly a hyperswarm connection). Two peers exchange member keys, both elect the
// lowest-key signer, and the survivor fails over (after grace) when the holder disconnects — appending
// nothing to any log. Run: `node test/signer-presence-channel.test.mjs`
import test from 'brittle'
import SecretStream from '@hyperswarm/secret-stream'
import { SignerPresence } from '../lib/signer-presence.js'
import { attachPresence } from '../lib/signer-presence-channel.js'

const A = 'aaaa000000000000000000000000000000000000000000000000000000000000'
const B = 'bbbb000000000000000000000000000000000000000000000000000000000000'
const sleep = (ms) => new Promise((r) => setTimeout(r, ms))
const waitFor = async (fn, timeout = 3000) => { const end = Date.now() + timeout; while (Date.now() < end) { if (fn()) return true; await sleep(20) } return false }

test('presence handshake over a real connection: elect lowest-key signer, fail over on disconnect', async (t) => {
  const grace = 400
  const spA = new SignerPresence({ self: A, graceMs: grace })
  const spB = new SignerPresence({ self: B, graceMs: grace })

  const a = new SecretStream(true)
  const b = new SecretStream(false)
  a.rawStream.pipe(b.rawStream).pipe(a.rawStream) // cross-pipe = a connected pair

  const detachA = attachPresence(a, { selfKey: A, presence: spA })
  const detachB = attachPresence(b, { selfKey: B, presence: spB })
  t.teardown(() => { detachA(); detachB(); try { a.destroy() } catch {} try { b.destroy() } catch {} })

  // member keys exchanged over the wire → both compute the same signer (A, lowest)
  t.ok(await waitFor(() => spB.effective().includes(A) && spA.effective().includes(B)), 'peers exchanged member keys over the connection')
  t.is(spA.currentSigner(), A, 'A (lowest key) is elected signer')
  t.is(spB.currentSigner(), A, 'B independently agrees A is the signer')
  t.ok(spA.holds(), 'A holds the signer role')
  t.absent(spB.holds(), 'B does not hold while A is present')

  // A disconnects → B's connection closes → B fails over, but only AFTER the grace window
  a.destroy()
  await sleep(150) // close fires + A marked absent, but 150ms < grace(400) → still in grace
  t.absent(spB.holds(), 'immediately after disconnect B stays in grace (no premature/thrashing failover)')
  await sleep(grace) // now past grace
  t.ok(spB.holds(), 'after grace, B fails over to signer')
  t.is(spB.currentSigner(), B, 'B is now the sole signer')
})

// ⛔ A SILENT NO-OP MUST BE VISIBLE. 2026-10-14: loser-club's two co-owners each saw ONLY themselves present
// (each elected itself → two signers) while wj62's presence over the same connections worked. A second attach of
// the same (protocol, id) on a mux is a no-op by design — if a stale StateBase holds the channel, the live one's
// attach does nothing and nothing says so. The handle now reports whether it attached, and every received
// announce is reported, so "channel taken" and "peer never sends" are told apart by a read.
test('attach reports whether it got the channel; a duplicate (protocol, id) reports false; receipts are reported', async (t) => {
  const spA = new SignerPresence({ self: A })
  const spB = new SignerPresence({ self: B })
  const a = new SecretStream(true)
  const b = new SecretStream(false)
  a.rawStream.pipe(b.rawStream).pipe(a.rawStream)
  const id = Buffer.alloc(32, 7)
  const gotA = []
  const first = attachPresence(a, { selfKey: A, presence: spA, id, onReceive: (k) => gotA.push(k) })
  const dup = attachPresence(a, { selfKey: A, presence: spA, id })
  const other = attachPresence(b, { selfKey: B, presence: spB, id })
  t.teardown(() => { first(); dup(); other(); try { a.destroy() } catch {} try { b.destroy() } catch {} })
  t.is(first.attached, true, 'the first attach got the channel')
  t.is(dup.attached, false, 'a second attach of the same (protocol, id) on this mux is a no-op — and says so')
  t.is(other.attached, true)
  t.ok(await waitFor(() => gotA.includes(B)), 'the peer\'s announce is reported to onReceive')
})

// ⛔ A LATE ATTACH MUST STILL PAIR. 2026-10-14, loser-club: tabertron attached presence when the connection opened;
// stortron's loser-club booted LATER (resync side store) and attached after. Protomux rejects a remote open that
// has no local channel and no pair() handler (_requestSession → _rejectSession), so tabertron's channel closed;
// stortron's later open then hit a peer with no channel and no handler and was rejected too. Both counted
// `attached`, both received 0, both elected themselves → two signers. The first side must accept a later open.
test('late attach: the side that attached first accepts the peer\'s later open (rejected first open is recovered)', async (t) => {
  const spA = new SignerPresence({ self: A })
  const spB = new SignerPresence({ self: B })
  const a = new SecretStream(true)
  const b = new SecretStream(false)
  a.rawStream.pipe(b.rawStream).pipe(a.rawStream)
  const id = Buffer.alloc(32, 9)
  // As in production: B's stream ALREADY carries a mux (replication + another DID's presence — wj62), so A's
  // open for this DID is processed on arrival and, with no channel and no pair handler there, rejected.
  const otherA = attachPresence(a, { selfKey: A, presence: new SignerPresence({ self: A }), id: Buffer.alloc(32, 8) })
  const otherB = attachPresence(b, { selfKey: B, presence: new SignerPresence({ self: B }), id: Buffer.alloc(32, 8) })
  t.teardown(() => { otherA(); otherB() })
  await sleep(200)
  const detachA = attachPresence(a, { selfKey: A, presence: spA, id })
  await sleep(300) // B has NO channel and NO pair handler for this id yet → B's mux rejects A's open
  const detachB = attachPresence(b, { selfKey: B, presence: spB, id }) // B boots late
  t.teardown(() => { detachA(); detachB(); try { a.destroy() } catch {} try { b.destroy() } catch {} })
  t.ok(await waitFor(() => spA.effective().includes(B) && spB.effective().includes(A)), 'both see each other after a late attach')
  t.is(spA.currentSigner(), A)
  t.is(spB.currentSigner(), A, 'and agree on one signer')
})