OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-proto-peer / test / reconcile-bytes.test.mjs

178 lines · javascript ref:
// Peer-level BYTE convergence — the regression guard for "co-owned on paper, single-homed in practice".
//
// The autobase guarantees POINTER convergence: every co-owner agrees on WHICH records exist. Byte
// convergence had no equivalent. The only path that resolves record bytes is sign()'s
// listRecords({now:true}), and sign() returns at the ELECTION GATE (holdsSigner) BEFORE reaching it:
//
//     sign()  ... if (!force && !this.sb.holdsSigner(...)) return null      <-- non-signer exits here
//                 const records = await this.repo.listRecords(null, {now:true})   <-- bytes resolved HERE
//
// So a co-owner that is not the current signer never fetched a single record byte. Its byte set grew
// only from what it authored locally while its pointer set stayed correct and agreeing — both nodes
// report a healthy, identical record set while silently diverging. Measured on the live wj62 pair:
// 125 vs 124 posts, each holding one the other lacked, and a record unresolved for 75+ hours that
// appeared the instant anything called a byte-resolving read.
//
// Test 1 is the load-bearing one: it asserts the NON-SIGNER converges. It fails without
// reconcileBytes, because sign() is the only thing that would have pulled and it returns early.
//
// Run: bare test/reconcile-bytes.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:reconcilebytes00000000'
const tmp = () => { const d = '/tmp/hpp-recon-' + Math.floor(Math.random() * 1e9); fs.mkdirSync(d, { recursive: true }); return d }

// A substrate holding POINTERS only — exactly what replication delivers to a co-owner: it converges
// which records exist, and nothing about their bytes.
function memKV (holdsSigner) {
  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: () => holdsSigner
  }
}

// A content-addressed transport shared by the "peers": bytes are fetchable by oid from anywhere,
// which is the one property a real overlay provides and the one this feature depends on.
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 }
    },
    get peerCount () { return net.size ? 1 : 0 }
  }
}

const REC = { $type: 'app.bsky.feed.post', text: 'a co-owner must be able to hold this', createdAt: '2026-07-26T00:00:00.000Z' }

// Seed `net` with a record's bytes (as the authoring peer would) and return its pointer.
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 }
}

test('a NON-SIGNER converges record bytes (it never reaches the code path that pulls)', async (t) => {
  const net = new Map()
  const ptr = await authorElsewhere(net, 'app.bsky.feed.post', '3kaaa', REC)

  const root = tmp()
  const store = await new OverlayRepoStore(root, { overlay: mkOverlay(net), did: DID }).ready()
  const sb = memKV(false) // <- NOT the elected signer
  const device = new SwarmDevice(sb, { did: DID, privateKey: crypto.randomBytes(32), self: 'peerB', recordStore: store })

  // Replication delivered the POINTER; the bytes are not here.
  await sb.set('app.bsky.feed.post/3kaaa', ptr)
  t.absent(fs.existsSync(root + '/records/app.bsky.feed.post/3kaaa.dagcbor'), 'precondition: pointer converged, bytes absent')

  // CONTROL: sign() is the only thing that would have pulled, and it bails at the election gate.
  t.is(await device.sign(), null, 'CONTROL: a non-signer signs nothing — so it never resolves bytes')
  t.absent(fs.existsSync(root + '/records/app.bsky.feed.post/3kaaa.dagcbor'), 'and the bytes are STILL absent after a full sign pass')

  const r = await device.reconcileBytes()
  t.is(r.missing, 1, 'reconcile saw one pointer without local bytes')
  t.is(r.fetched, 1, 'and pulled it')
  t.ok(fs.existsSync(root + '/records/app.bsky.feed.post/3kaaa.dagcbor'), 'the NON-SIGNER now holds the bytes')
  t.alike(await store.get('app.bsky.feed.post', '3kaaa'), REC, 'byte-exact record')

  await store.close()
})

test('reconcile is idempotent and quiet once converged', async (t) => {
  const net = new Map()
  const ptr = await authorElsewhere(net, 'app.bsky.feed.post', '3kbbb', REC)
  const store = await new OverlayRepoStore(tmp(), { overlay: mkOverlay(net), did: DID }).ready()
  const sb = memKV(false)
  const device = new SwarmDevice(sb, { did: DID, privateKey: crypto.randomBytes(32), self: 'peerB', recordStore: store })
  await sb.set('app.bsky.feed.post/3kbbb', ptr)

  t.is((await device.reconcileBytes()).fetched, 1, 'first pass pulls')
  const second = await device.reconcileBytes()
  t.is(second.fetched, 0, 'second pass pulls nothing')
  t.is(second.missing, 0, 'nothing is missing')
  t.is(second.checked, 1, 'but it still checked the record')

  await store.close()
})

test('reconcile ignores reserved keys and tombstones (never chases bytes that cannot exist)', async (t) => {
  const net = new Map()
  const store = await new OverlayRepoStore(tmp(), { overlay: mkOverlay(net), did: DID }).ready()
  const sb = memKV(false)
  const device = new SwarmDevice(sb, { did: DID, privateKey: crypto.randomBytes(32), self: 'peerB', recordStore: store })

  await sb.set('__commit__/3kzzz', { rev: '3kzzz', ops: [] })  // firehose frame — not a record
  await sb.set('__members__', { a: 1 })                         // control key
  await sb.set('app.bsky.feed.post/3kdel', { $deleted: true })  // tombstone — deliberately no bytes

  const r = await device.reconcileBytes()
  t.is(r.checked, 0, 'no reserved key or tombstone was treated as a record')
  t.is(r.missing, 0, 'so nothing was reported missing')
  t.is(r.failed, 0, 'and nothing was uselessly fetched')

  await store.close()
})

test('a pass is BOUNDED and reports `more`, so a backlog cannot block signing', async (t) => {
  const net = new Map()
  const store = await new OverlayRepoStore(tmp(), { overlay: mkOverlay(net), did: DID }).ready()
  const sb = memKV(false)
  const device = new SwarmDevice(sb, { did: DID, privateKey: crypto.randomBytes(32), self: 'peerB', recordStore: store })

  for (let i = 0; i < 10; i++) {
    const ptr = await authorElsewhere(net, 'app.bsky.feed.post', 'r' + i, { ...REC, text: 'post ' + i })
    await sb.set('app.bsky.feed.post/r' + i, ptr)
  }

  const first = await device.reconcileBytes({ limit: 4 })
  t.is(first.fetched, 4, 'a bounded pass pulls at most `limit`')
  t.ok(first.more, 'and says there is more to do')

  let guard = 0
  while ((await device.reconcileBytes({ limit: 4 })).more && guard++ < 10) { /* drain */ }
  const done = await device.reconcileBytes({ limit: 4 })
  t.is(done.missing, 0, 'successive passes converge the whole backlog')
  t.is(done.checked, 10, 'all ten records accounted for')

  await store.close()
})

test('an unfetchable pointer fails without wedging the pass (no peer has the bytes)', async (t) => {
  const net = new Map()
  const store = await new OverlayRepoStore(tmp(), { overlay: mkOverlay(net), did: DID }).ready()
  const sb = memKV(false)
  const device = new SwarmDevice(sb, { did: DID, privateKey: crypto.randomBytes(32), self: 'peerB', recordStore: store })

  const good = await authorElsewhere(net, 'app.bsky.feed.post', 'ok', REC)
  await sb.set('app.bsky.feed.post/ok', good)
  await sb.set('app.bsky.feed.post/gone', { $oid: b4a.toString(crypto.randomBytes(32), 'hex'), size: 9 }) // nobody has it

  const r = await device.reconcileBytes()
  t.is(r.fetched, 1, 'the resolvable record was pulled')
  t.is(r.failed, 1, 'the unresolvable one was counted, not thrown')
  t.ok(await store.get('app.bsky.feed.post', 'ok'), 'and the good record is present')

  await store.close()
})