// ★ The doorbell, OVER THE WIRE — the half two other files say is unproven.
//
// `feed-base.js` on knock(): "⚠ Requires holding their base, which requires a transport. Untested
// end to end for that reason — the authorisation rules it relies on are proven in knock.test.js,
// the wire is not."
//
// `doorbell-peer.test.js`: "The optimistic append itself is NOT exercised here… Appended by MY OWN
// writer, because there is no transport to carry a stranger's optimistic append."
//
// So every part of the doorbell is proven except the one that makes it a doorbell: that somebody who
// is NOT a writer on your feed can put a row in it from another machine. That is the claim here, and
// it runs over the same in-memory duplex pair replicate.test.js uses — the wiring under test is "can
// a stranger's append cross", and a swarm would add DHT timing to a test that is not about that.
//
// ⛔ WHY IT MATTERS NOW. An interaction lives in its author's feed, so if you do not hold that feed
// you never learn of it, and the doorbell is the only answer the model has for a stranger. If this
// append does not cross, that answer does not exist — and "the like never showed up" is exactly the
// symptom, indistinguishable from a dozen other causes.
import test from 'brittle'
import fs from '#node:fs'
import os from '#node:os'
import b4a from 'b4a'
import { join } from '#node:path'
import Corestore from 'corestore'
import { RecordStore } from '../lib/record-store.js'
import { Feed } from '../lib/feed-base.js'
import { attest, identity } from './helpers.js'
// ⛔ A knock proves which identity is knocking (feed.js applyKnock): the binder ties the knocking
// writer core to the stranger's identity root.
const STRANGER = await (await identity()).device()
const asStranger = (writerKeyHex) => STRANGER.bind(writerKeyHex)
const tmp = () => fs.mkdtempSync(join(os.tmpdir(), 'favela-bell-wire-'))
const hex = (k) => b4a.toString(k, 'hex')
function pipe (a, b) {
const s1 = a.replicate(true)
const s2 = b.replicate(false)
s1.pipe(s2).pipe(s1)
return () => { s1.destroy(); s2.destroy() }
}
// Wait for the CONDITION, never for a duration — a fixed sleep passes on a fast machine and flakes
// on a loaded one, which is how a suite starts lying.
async function until (fn, { timeout = 15000 } = {}) {
const deadline = Date.now() + timeout
while (Date.now() < deadline) {
const v = await fn()
if (v) return v
await new Promise((r) => setTimeout(r, 25))
}
return null
}
async function mkFeed (store, key, records, opts = {}) {
const f = new Feed(store, key, { store: new RecordStore(records), ...opts })
await f.ready()
return f
}
async function owned (records) {
const f = await mkFeed(new Corestore(tmp()), null, records, { optimistic: true })
await f.addDevice(await attest(f))
await f.update()
return f
}
test('★★★ a stranger who is NOT a writer rings the doorbell from another machine', async (t) => {
const records = tmp()
const alice = await owned(records)
const bob = await owned(tmp())
// Bob opens ALICE's feed by key. He is a reader of her log and a writer of nothing in it.
const aliceOnBob = await mkFeed(new Corestore(tmp()), alice.key, records, { optimistic: true })
const unpipe = pipe(alice, aliceOnBob)
t.teardown(async () => { unpipe(); await alice.close(); await bob.close(); await aliceOnBob.close() })
await until(async () => { await aliceOnBob.update(); return (await aliceOnBob.devices()).length > 0 })
t.absent(aliceOnBob.writable, '⛔ he is NOT a writer on her feed — that is the whole premise')
await aliceOnBob.knock(bob.key, asStranger)
const knocks = await until(async () => {
await alice.update()
const k = await alice.knocks()
return k.length ? k : null
})
t.ok(knocks, 'the knock crossed the wire and landed in her feed')
t.is(knocks && knocks[0].feed, hex(bob.key), 'and it names the feed that rang, not the device')
})
test('⛔ and it grants nothing — a knock is not admission', async (t) => {
const records = tmp()
const alice = await owned(records)
const bob = await owned(tmp())
const before = await alice.devices()
const aliceOnBob = await mkFeed(new Corestore(tmp()), alice.key, records, { optimistic: true })
const unpipe = pipe(alice, aliceOnBob)
t.teardown(async () => { unpipe(); await alice.close(); await bob.close(); await aliceOnBob.close() })
await until(async () => { await aliceOnBob.update(); return (await aliceOnBob.devices()).length > 0 })
await aliceOnBob.knock(bob.key, asStranger)
await until(async () => { await alice.update(); return (await alice.knocks()).length })
t.alike(await alice.devices(), before, "her writer set is untouched — the thing a doorbell must never change")
// ⛔ `writable` IS TRUE HERE, AND IT DOES NOT MEAN WHAT IT LOOKS LIKE. An optimistic append creates
// a local writer core, so autobase reports the base as writable from that moment — "I have a core I
// can append to", not "my appends are admitted". Asserting on it would test the wrong thing and
// pass for the wrong reason. The property that matters is whether a RECORD lands, so write one.
//
// ★ Two places can refuse it, and either one satisfies the property. On autobee 2.1.1 the append
// was accepted locally and refused by apply. From 2.12.1 autobee refuses it AT THE SOURCE: once the
// owner has acked the knock, the knocker's writer is acked-but-not-added (`isRemoved`), and a
// non-optimistic append from it throws `Not writable` (autobee index.js `append`). Measured
// 2026-10-12. So the throw is a refusal, not a test failure, and the read-back below still runs.
const { POST } = await import('../lib/peer.js')
let refusedLocally = false
try {
await aliceOnBob.put(POST, 'intruder', { frontmatter: { createdAt: '2026-09-15T00:00:00Z' }, body: 'not mine to write' })
} catch (err) {
if (!/Not writable/.test(err.message)) throw err
refusedLocally = true
}
await aliceOnBob.update()
t.comment(refusedLocally ? 'refused at append (autobee ≥2.12)' : 'accepted locally, left to apply to refuse')
// Give it every chance to arrive before concluding it did not — a race that passes by being fast
// is not a refusal.
const landed = await until(async () => {
await alice.update()
return (await alice.get(POST, 'intruder')) ? true : null
}, { timeout: 3000 })
t.absent(landed, '⛔ his record is REFUSED — a doorbell grants no write, however writable the base claims to be')
})