// DidPeer.getRepo({ since }) — the PARTIAL CAR, over a real replicated firehose (org #34).
//
// The membrane accepted `since` and threw it away: pds/lib/server.js called front.getRepo() with no
// arguments, so it never reached the peer and the identical full CAR came back, byte for byte. The trap
// is not the wasted bytes — it is that a caller passing `since` got correct results BY ACCIDENT and
// would have been silently wrong the day partial CARs landed. This is that day, so the contract has to
// be nailed down here rather than inferred at the membrane.
//
// This runs over a real swarm + a real replicated firehose (not a hand-built one), because the whole
// mechanism is "the front already holds the frames" — a fixture that hands the frames over directly
// would not test the claim that makes this cheap.
//
// Run: node test/get-repo-since.test.mjs
import { mkdtempSync, rmSync } from 'node:fs'
import { join } from 'node:path'
import { tmpdir } from 'node:os'
import createTestnet from '@hyperswarm/testnet'
import Hyperswarm from 'hyperswarm'
import { RepoStore } from 'atproto-store/lib/repo-store.js'
import { RepoManager } from 'atproto-store/lib/repo-manager.js'
import { FirehoseSequencer } from 'atproto-store/lib/sequencer.js'
import { openCorestore } from 'atproto-store/lib/corestore.js'
import { createAtprotoIdentityFromKey } from 'atproto-identity/lib/atproto-identity.js'
import { discoveryTopic } from 'atproto-identity/lib/discovery.js'
import { readCar, listRecords, loadCommit, writeCar } from 'atproto-repo/lib/repo.js'
import { DidPeer } from '../index.js'
const assert = (c, m) => { if (!c) { console.error('FAIL:', m); process.exit(1) } console.log(' ok -', m) }
const id = createAtprotoIdentityFromKey(new Uint8Array(32).fill(9), { handle: 'since.test', pdsUrl: 'https://pds.local' })
const testnet = await createTestnet(3)
// ---- holder: a real repo with a real sequenced firehose ---------------------------------------------
const hDir = mkdtempSync(join(tmpdir(), 'since-holder-'))
const hStore = openCorestore(hDir)
const repo = await new RepoStore(hStore, { name: `atproto-repo:${id.did}` }).ready()
let micros = 1_700_000_000_000_000n
const mgr = await new RepoManager(repo, { did: id.did, privateKey: id.privateKey, clock: () => (micros += 1_000_000n) }).init()
const seq = await new FirehoseSequencer(hStore.get({ name: 'firehose' })).ready()
let t0 = Date.parse('2026-08-07T00:00:00.000Z')
const write = async (rkey, text) => {
const w = await mgr.applyWrite({ action: 'create', collection: 'app.bsky.feed.post', rkey, record: { $type: 'app.bsky.feed.post', text, createdAt: new Date(t0 += 1000).toISOString() } })
await seq.sequenceCommit(w, { time: new Date(t0).toISOString() })
return w
}
const w1 = await write('a1', 'first')
// Snapshot what a caller would hold at w1, NOW — before the repo advances. getRepoFromCommit rebuilds
// the MST from the store's CURRENT records and refuses if it does not match the commit handed in, so
// asking for w1's repo after two more writes correctly throws. (It did, and the refusal was right.)
const priorCar = await readCar((await (await import('atproto-store/lib/read-view.js')).getRepoFromCommit(repo, w1.signedCommit)).carBytes)
const w2 = await write('a2', 'second')
const w3 = await write('a3', 'third')
const holder = new DidPeer(hStore, { did: id.did, verifyKey: id.publicKey })
await holder.provide({ repoKey: repo.core.key, firehoseKey: seq.core.key })
const hSwarm = new Hyperswarm({ bootstrap: testnet.bootstrap })
hSwarm.on('connection', (c) => holder.attach(c))
await hSwarm.join(discoveryTopic(id.did), { server: true, client: false }).flushed()
// ---- reader: a keyless front, exactly as the membrane fronts one ------------------------------------
const rStore = openCorestore(mkdtempSync(join(tmpdir(), 'since-reader-')))
const rSwarm = new Hyperswarm({ bootstrap: testnet.bootstrap })
const front = new DidPeer(rStore, { did: id.did, verifyKey: id.publicKey })
rSwarm.on('connection', (c) => front.attach(c))
await rSwarm.join(discoveryTopic(id.did), { server: false, client: true }).flushed()
assert(!!(await front.ready({ timeout: 20000 })), 'front reached a verified head over the swarm')
// Wait for the firehose to replicate all three frames — the diff is assembled FROM them, so a front that
// has only the head frame would legitimately refuse and the test would prove nothing.
for (let i = 0; i < 100 && front.seq.core.length < 3; i++) await new Promise((r) => setTimeout(r, 100))
assert(front.seq.core.length >= 3, 'all three commit frames replicated to the front (' + front.seq.core.length + ')')
// ---- 1. no `since` -> the FULL CAR, unchanged --------------------------------------------------------
// The regression that matters most: every existing caller passes nothing, and must be byte-identical.
const full = await front.getRepo()
const fullCar = await readCar(full.carBytes)
assert(listRecords(fullCar).length === 3, 'no-since getRepo still returns the complete repo (3 records)')
assert(full.partial === false, 'and declares itself NOT partial')
assert(full.sinceHonoured === null, 'sinceHonoured is null when nothing was asked — not false, which would imply a refusal')
// ---- 2. `since` -> a real partial CAR ----------------------------------------------------------------
const d = await front.getRepo({ since: w1.rev })
assert(d.partial === true, 'getRepo({since}) returns a PARTIAL CAR')
assert(d.sinceHonoured === true, 'and says the parameter was honoured')
assert(d.fromRev === w1.rev && d.toRev === w3.rev, 'reporting the range it covers: ' + d.fromRev + ' -> ' + d.toRev)
const diffCar = await readCar(d.carBytes)
assert(loadCommit(diffCar).cid.equals(loadCommit(fullCar).cid), 'rooted at the SAME head as the full CAR')
// The defining property: it is not self-contained. If this ever stops throwing, we are serving a full
// CAR again and have silently regressed to accept-and-ignore.
let walked = null
try { listRecords(diffCar); walked = 'walked' } catch { walked = 'threw' }
assert(walked === 'threw', 'the partial CAR is NOT self-contained — walking it alone throws')
// ---- 3. it COMPLETES a caller prior state ------------------------------------------------------------
// The only check that proves the omission was sufficient rather than merely plausible.
const union = new Map([...priorCar.blocks, ...diffCar.blocks])
const merged = await readCar(await writeCar(loadCommit(fullCar).cid, [...union.values()]))
const texts = listRecords(merged).map((r) => r.value.text).sort()
assert(JSON.stringify(texts) === JSON.stringify(['first', 'second', 'third']),
'prior state + diff walks to the COMPLETE record set: ' + texts.join(','))
// ---- 4. an unanswerable `since` falls back to the FULL CAR and says so -------------------------------
// Never a 500, never a silent full CAR pretending to be a diff. '2222222222222' is below every real rev
// (the TID alphabet starts at '2', not 'a'), so the firehose provably cannot reach back to it.
const fb = await front.getRepo({ since: '2222222222222' })
assert(fb.partial === false, 'an unanswerable since falls back to a full CAR')
assert(fb.sinceHonoured === false, 'and reports that it was NOT honoured — the anti-silence property')
assert(typeof fb.reason === 'string' && fb.reason.length > 0, 'with a reason: ' + fb.reason)
assert(listRecords(await readCar(fb.carBytes)).length === 3, 'and the fallback CAR is genuinely complete')
// ---- 5. since == head -> an empty diff ---------------------------------------------------------------
const empty = await front.getRepo({ since: w3.rev })
assert(empty.partial === true && empty.empty === true, 'since == head gives an EMPTY diff, not an error')
assert((await readCar(empty.carBytes)).blocks.size === 1, 'carrying the commit block alone')
console.log('\nGET-REPO-SINCE OK: partial CARs are real, self-declaring, and refuse rather than mislead')
await hSwarm.destroy(); await rSwarm.destroy(); await testnet.destroy()
try { rmSync(hDir, { recursive: true, force: true }) } catch {}
process.exit(0)