// pointerDiag — does this node's LOCAL byte set match the CONVERGED pointer set?
//
// The pointer design splits a record in two: the autobase converges a { $oid, size } POINTER (so every
// co-owner agrees WHICH records exist), and the bytes live as content-addressed overlay files. The
// signed repo is built from the BYTES. Nothing ever checked that the two halves agree — and they can
// silently disagree, because ensureLocal returns a local file hit WITHOUT comparing it to the
// pointer's $oid:
//
// async ensureLocal (collection, rkey, oid) {
// const local = await this.get(collection, rkey)
// if (local != null) return local <-- returns superseded bytes; oid never consulted
// ...fetch by oid...
// }
//
// So a node holding an OLD copy of a record resolves it happily, never re-fetches, and signs it as
// truth — while its pointer set looks perfectly converged from every diagnostic surface.
//
// Measured on the live wj62 pair (2026-07-27): both co-owners held the SAME 139 converged pointers,
// both resolved all 139, and still produced DIFFERENT signed repos. They differed on exactly one
// record — app.bsky.actor.profile/self, two different avatar blob refs (6533 vs 5962 bytes) — so the
// signed content FLIPPED on every signer change, republishing the profile with a different avatar
// each time. Finding that required fetching two signed CARs and diffing them by hand, because no
// single-node call could answer "do my bytes match my pointers?". This is that call.
//
// Test 1 is the load-bearing one. It pins WHY this stayed invisible: the stale record still READS
// fine, so every read-path surface looks healthy. (reconcileBytes was equally blind — it skipped any
// record with a local file — but that is now fixed, so the repair is asserted in its own test below
// rather than as a control here.)
//
// Run: bare test/pointer-diag.test.mjs
import '../lib/bare-globals.js'
import test from 'brittle'
import fs from 'bare-fs'
import b4a from 'b4a'
import crypto from 'hypercore-crypto'
import { OverlayRepoStore } from 'atproto-store/lib/overlay-repo-store.js'
import { SwarmDevice } from '../lib/swarm-device.js'
const DID = 'did:plc:pointerdiag0000000000000'
const POST = 'app.bsky.feed.post'
const PROFILE = 'app.bsky.actor.profile'
const tmp = () => { const d = '/tmp/hpp-pdiag-' + Math.floor(Math.random() * 1e9); fs.mkdirSync(d, { recursive: true }); return d }
function memKV () {
const m = new Map()
return {
_m: m,
async set (k, v) { m.set(k, v) },
async del (k) { m.delete(k) },
async get (k) { return m.has(k) ? m.get(k) : null },
async list (prefix = '') { const o = {}; for (const [k, v] of m) if (k.startsWith(prefix)) o[k] = v; return o },
async listNow (prefix = '') { return this.list(prefix) },
async getNow (k) { return this.get(k) },
holdsSigner: () => false
}
}
// Content-addressed transport. hashFile mirrors the real overlay's read-only hash — hyper-overlay's
// streaming hasher is documented as matching crypto.data(buffer), so this is the same oid registerFile
// would assign.
function mkOverlay (net) {
return {
async registerFile (overlayPath, diskPath) {
const bytes = fs.readFileSync(diskPath)
const oid = crypto.data(bytes).toString('hex')
net.set(oid, bytes)
return { contentHash: oid }
},
async fetchFile (oid, { destPath } = {}) {
const bytes = net.get(oid)
if (!bytes) return null
fs.writeFileSync(destPath, bytes)
return { path: destPath }
},
async hashFile (diskPath) {
let bytes
try { bytes = fs.readFileSync(diskPath) } catch { return null }
return { contentHash: crypto.data(bytes).toString('hex'), size: bytes.length }
},
get peerCount () { return net.size ? 1 : 0 }
}
}
// Author a record as the OTHER peer would and return the pointer that converges to us.
async function authorElsewhere (net, collection, rkey, record) {
const store = await new OverlayRepoStore(tmp(), { overlay: mkOverlay(net) }).ready()
const { oid, size } = await store.put(collection, rkey, record)
return { $oid: oid, size }
}
const mkDevice = (sb, store) => new SwarmDevice(sb, { did: DID, privateKey: crypto.randomBytes(32), self: 'peerA', recordStore: store })
test('a STALE local file is reported — the wj62 profile/avatar divergence', async (t) => {
const net = new Map()
const store = await new OverlayRepoStore(tmp(), { overlay: mkOverlay(net), did: DID }).ready()
const sb = memKV()
const device = mkDevice(sb, store)
// We authored the profile with avatar A and hold those bytes locally.
const mine = { $type: PROFILE, displayName: 'wj62', avatar: { ref: { $link: 'bafkreiAAA' }, size: 6533 } }
const { oid: localOid } = await store.put(PROFILE, 'self', mine)
// The co-owner authored it with avatar B; THAT pointer is what converged into the shared autobase.
const theirs = { $type: PROFILE, displayName: 'wj62', avatar: { ref: { $link: 'bafkreiBBB' }, size: 5962 } }
const ptr = await authorElsewhere(net, PROFILE, 'self', theirs)
await sb.set(PROFILE + '/self', ptr)
t.not(ptr.$oid, localOid, 'precondition: the converged pointer names different bytes than we hold')
// WHY IT WAS INVISIBLE (1): the record still reads, so every read-path surface looks healthy.
t.alike(await store.get(PROFILE, 'self'), mine, 'CONTROL: the record resolves — to OUR superseded copy')
const d = await device.pointerDiag()
t.is(d.checked, 1, 'one record checked')
t.is(d.stale, 1, 'the divergence IS reported')
t.is(d.ok, 0, 'and it is not counted as healthy')
t.is(d.entries.length, 1, 'the offending record is named')
t.is(d.entries[0].key, PROFILE + '/self', 'names the exact record — the one wj62 differed on')
t.is(d.entries[0].state, 'stale', 'classified as stale, not missing')
t.is(d.entries[0].pointerOid, ptr.$oid, 'reports the pointer oid')
t.is(d.entries[0].localOid, localOid, 'and the oid our bytes actually hash to')
await store.close()
})
test('a matching local file is OK and is not listed', async (t) => {
const net = new Map()
const store = await new OverlayRepoStore(tmp(), { overlay: mkOverlay(net), did: DID }).ready()
const sb = memKV()
const device = mkDevice(sb, store)
const rec = { $type: POST, text: 'converged', createdAt: '2026-07-27T00:00:00.000Z' }
const { oid, size } = await store.put(POST, '3kaaa', rec)
await sb.set(POST + '/3kaaa', { $oid: oid, size })
const d = await device.pointerDiag()
t.is(d.checked, 1, 'checked the record')
t.is(d.ok, 1, 'local bytes match the pointer')
t.is(d.stale, 0, 'nothing stale')
t.is(d.entries.length, 0, 'a healthy store lists nothing — quiet in steady state')
await store.close()
})
test('a converged pointer with no local bytes is MISSING, not stale (a lagging peer is not corrupt)', async (t) => {
const net = new Map()
const store = await new OverlayRepoStore(tmp(), { overlay: mkOverlay(net), did: DID }).ready()
const sb = memKV()
const device = mkDevice(sb, store)
const ptr = await authorElsewhere(net, POST, '3kbbb', { $type: POST, text: 'not here yet', createdAt: '2026-07-27T00:00:00.000Z' })
await sb.set(POST + '/3kbbb', ptr)
const d = await device.pointerDiag()
t.is(d.missing, 1, 'reported as missing')
t.is(d.stale, 0, 'NOT conflated with a stale file — different fault, different fix')
t.is(d.entries[0].state, 'missing', 'and classified as such')
t.is(d.entries[0].localOid, null, 'with no local oid to report')
// reconcileBytes is the right remedy for THIS one, and it clears it.
await device.reconcileBytes()
const after = await device.pointerDiag()
t.is(after.ok, 1, 'once the bytes are pulled the record is healthy')
t.is(after.missing, 0, 'and nothing is missing')
await store.close()
})
test('reserved keys and tombstones are never treated as records', async (t) => {
const net = new Map()
const store = await new OverlayRepoStore(tmp(), { overlay: mkOverlay(net), did: DID }).ready()
const sb = memKV()
const device = mkDevice(sb, store)
await sb.set('__commit__/3kzzz', { rev: '3kzzz', ops: [] })
await sb.set('__members__', { a: 1 })
await sb.set('__blob__/bafkreiXYZ', { $oid: 'deadbeef', size: 1 })
await sb.set(POST + '/3kdel', { $deleted: true })
const d = await device.pointerDiag()
t.is(d.checked, 0, 'no reserved key was checked as a record')
t.is(d.tombstones, 1, 'the tombstone was counted, not chased')
t.is(d.stale, 0, 'and nothing was misreported as divergent')
await store.close()
})
test('it is a PURE READ — it never writes, fetches, or repairs', async (t) => {
const net = new Map()
const store = await new OverlayRepoStore(tmp(), { overlay: mkOverlay(net), did: DID }).ready()
const sb = memKV()
const device = mkDevice(sb, store)
const mine = { $type: POST, text: 'ours', createdAt: '2026-07-27T00:00:00.000Z' }
await store.put(POST, '3kccc', mine)
const ptr = await authorElsewhere(net, POST, '3kccc', { $type: POST, text: 'theirs', createdAt: '2026-07-27T00:00:01.000Z' })
await sb.set(POST + '/3kccc', ptr)
const before = JSON.stringify([...sb._m.entries()])
await device.pointerDiag()
await device.pointerDiag()
t.is(JSON.stringify([...sb._m.entries()]), before, 'the pointer set is untouched — nothing appended to the autobase')
t.alike(await store.get(POST, '3kccc'), mine, 'the local bytes are untouched — it diagnoses, it does not repair')
t.is((await device.pointerDiag()).stale, 1, 'and it keeps reporting the divergence rather than hiding it')
await store.close()
})
test('no record store (legacy backend) skips cleanly rather than throwing', async (t) => {
const device = new SwarmDevice(memKV(), { did: DID, privateKey: crypto.randomBytes(32), self: 'peerA' })
const d = await device.pointerDiag()
t.is(d.skipped, 'no-record-store', 'a legacy node reports why it cannot answer')
t.absent(d.stale, 'and asserts nothing about staleness')
})
test('reconcileBytes now REPAIRS a stale file (presence is not agreement)', async (t) => {
const net = new Map()
const store = await new OverlayRepoStore(tmp(), { overlay: mkOverlay(net), did: DID }).ready()
const sb = memKV()
const device = mkDevice(sb, store)
const mine = { $type: PROFILE, displayName: 'wj62', avatar: { ref: { $link: 'bafkreiAAA' }, size: 6533 } }
const theirs = { $type: PROFILE, displayName: 'wj62', avatar: { ref: { $link: 'bafkreiBBB' }, size: 5962 } }
await store.put(PROFILE, 'self', mine)
const ptr = await authorElsewhere(net, PROFILE, 'self', theirs)
await sb.set(PROFILE + '/self', ptr)
t.is((await device.pointerDiag()).stale, 1, 'precondition: the local copy is superseded')
const r = await device.reconcileBytes()
t.is(r.stale, 1, 'the stale record is SEEN — it used to be skipped because a file was present')
t.is(r.fetched, 1, 'and repaired')
t.alike(await store.get(PROFILE, 'self'), theirs, 'the authoritative record is now local')
const after = await device.pointerDiag()
t.is(after.stale, 0, 'nothing stale remains')
t.is(after.ok, 1, 'the record now matches the converged pointer')
const second = await device.reconcileBytes()
t.is(second.stale, 0, 'a converged pass reports no staleness')
t.is(second.fetched, 0, 'and fetches nothing — no churn once verified')
await store.close()
})