// The receiving half of the doorbell, at the Peer surface.
//
// ⚠ The optimistic append itself is not exercised HERE — that is doorbell-wire.test.js, which since
// 2026-09-15 proves it across a replication pipe. What this file proves is everything the wire
// carries into — that a knock lands as a request, that accepting one follows the knocker
// rather than admitting them, and that nothing about it touches the feed's writer set.
import test from 'brittle'
import fs from '#node:fs'
import os from '#node:os'
import { join } from '#node:path'
import Corestore from 'corestore'
import { RecordStore } from '../lib/record-store.js'
import { Feed } from '../lib/feed-base.js'
import { Peer } from '../lib/peer.js'
import { KNOCKS } from '../lib/feed.js'
import { attest, identity } from './helpers.js'
// ⛔ A knock must prove which identity is knocking (feed.js applyKnock). These tests append it through
// MY writer as a stand-in for a stranger's, so it carries a proof binding that writer to a separate
// identity: the knock is then keyed by that identity's root, and still names THEIR feed.
const KNOCKER_ID = await identity()
const KNOCKER = await KNOCKER_ID.device()
const knockOp = (me, them) => Buffer.from(JSON.stringify({ t: 'knock', feed: them.key.toString('hex'), proof: KNOCKER.bind(me.feed.deviceKey) }), 'utf8')
const tmp = () => fs.mkdtempSync(join(os.tmpdir(), 'favela-bell-'))
async function mkFeed (optimistic = true) {
const f = new Feed(new Corestore(tmp()), null, { store: new RecordStore(tmp()), optimistic })
await f.ready()
await f.addDevice(await attest(f))
await f.update()
return f
}
test('a knock appears at the Peer surface and accepting it FOLLOWS, never admits', async (t) => {
const me = await new Peer(await mkFeed()).ready()
const them = await mkFeed()
t.teardown(() => Promise.all([me.close(), them.close()]))
// ⚠ Appended by MY OWN writer, because there is no transport to carry a stranger's optimistic
// block. So this exercises the doorbell's HANDLING — where a knock lands, what accepting it does —
// and not its authorisation. That a stranger may knock and may do nothing else is proven at the
// unit level in knock.test.js, against a foreign device key.
await me.feed.base.append(knockOp(me, them))
await me.update()
const knocks = await me.knocks()
t.is(knocks.length, 1, 'the knock is waiting')
t.is(knocks[0].feed, them.key.toString('hex'))
const before = await me.feed.devices()
await me.admitKnock(them.key, them)
await me.update()
t.alike(await me.following(), [them.key.toString('hex')], 'accepting means I follow them')
t.alike(await me.feed.devices(), before, '⛔ and my writer set is unchanged')
t.alike(await me.knocks(), [], 'the knock is cleared')
})
test('dismissing clears without following', async (t) => {
const me = await new Peer(await mkFeed()).ready()
const them = await mkFeed()
t.teardown(() => Promise.all([me.close(), them.close()]))
await me.feed.base.append(knockOp(me, them))
await me.update()
await me.dismissKnock(them.key)
await me.update()
t.alike(await me.knocks(), [], 'gone')
t.alike(await me.following(), [], 'and not followed')
})
test('knocks never leak into the timeline or the record set', async (t) => {
const me = await new Peer(await mkFeed()).ready()
const them = await mkFeed()
t.teardown(() => Promise.all([me.close(), them.close()]))
await me.feed.base.append(knockOp(me, them))
await me.update()
t.alike(await me.timeline(), [], 'no posts')
t.alike(await me.feed.list(), [], 'reserved namespaces are not records')
t.absent((await me.feed.list()).some((r) => r.key.startsWith(KNOCKS)))
})