// `projectRepoCore: false` — the stateless virtual device. THE SERVE-PATH GATE.
//
// atproto-store's derive-vs-project proof already shows the DERIVED CAR is byte-identical to the
// PROJECTED one. What nothing covered until now is what `serveMembrane` itself does when the flag is
// off — and that is the half with the operational risk, because the announce contract is what decides
// whether a consumer will front this peer at all.
//
// Why this matters (measured on tabertron 2026-08-05): the membrane-repo Hyperbee holds a SECOND copy
// of every record that already exists as a content-addressed file — 78 MB of RocksDB against 2.3 MB of
// actual record files. Turning the projection off reclaims it. But serve-membrane.js warns in as many
// words that this is "a COORDINATED change with the consumer", because a peer announcing repoKey:null
// is simply not fronted by a reader that requires it.
//
// So this asserts the three things a flip actually depends on:
// 1. no membrane-repo core is opened (the storage win is real, not just unused)
// 2. the announce carries repoKey:null rather than omitting the field or throwing
// 3. get-repo-car is still registered — the derive-on-read replacement is present, not just intended
//
// Consumer side is verified separately: atproto-did-peer 0.2.10 treats repoKey as optional in
// _openCores/onAnnounce/DidPeerRouter and `get holds()` keys off the FIREHOSE (`!!this.seq`), not the
// repo. All three deployed consumers (atproto-bridge, favela-web, ap-bridge, both hosts) are on 0.2.10.
//
// Run: bare test/project-repo-core-off.test.mjs
import '../lib/bare-globals.js'
import test from 'brittle'
import b4a from 'b4a'
import { serveMembrane } from '../lib/serve-membrane.js'
import { openCorestore } from 'atproto-store/lib/corestore.js'
import fs from 'bare-fs'
const DID = 'did:plc:projectrepocoreoff00000'
const tmp = () => { const d = '/tmp/ar-prc-' + Math.floor(Math.random() * 1e9); fs.mkdirSync(d, { recursive: true }); return d }
// serveMembrane touches only these swarm members. `join` is captured so the announce can be inspected.
function fakeSwarm () {
return {
on () {}, off () {},
keyPair: { publicKey: b4a.alloc(32, 7) },
join () { return { flushed: async () => {} } }
}
}
function fakeDevice (log = []) {
return {
_log: log,
repo: { listRecords: async () => [] },
async firehoseLog () { return this._log }
}
}
test('projectRepoCore:false opens NO membrane-repo core and announces repoKey:null', async (t) => {
const off = await serveMembrane(fakeDevice([]), {
corestore: openCorestore(tmp()), swarm: fakeSwarm(), did: DID, projectRepoCore: false
})
t.is(off.repo, null, 'no membrane-repo RepoStore is opened — this is the 78 MB')
t.is(off.repoKey, null, 'and the announce carries repoKey:null (the field EXISTS, so a consumer can branch on it)')
t.ok(off.firehoseKey, 'the firehose key is still announced — it is what `holds` keys off')
t.ok(off.seq, 'the membrane firehose sequencer is still opened')
})
test('the DEFAULT is unchanged — true still projects, so nothing moves until a DID is flipped', async (t) => {
const on = await serveMembrane(fakeDevice([]), {
corestore: openCorestore(tmp()), swarm: fakeSwarm(), did: DID
})
t.ok(on.repo, 'default (omitted) still opens the membrane-repo core')
t.ok(on.repoKey, 'and still announces a repoKey')
t.not(on.repoKey, null)
})
test('sync() is safe with the projection off — it must not try to project into a null repo', async (t) => {
const off = await serveMembrane(fakeDevice([]), {
corestore: openCorestore(tmp()), swarm: fakeSwarm(), did: DID, projectRepoCore: false
})
// The sign loop calls sync() every signIntervalMs (~2s). If it dereferenced the null repo the daemon
// would crash-loop within seconds of the flip — which is exactly the failure a "safe config flag"
// must not have.
await off.sync()
await off.sync()
t.pass('repeated sync() with no repo core does not throw')
t.is(off.repo, null, 'and still opens no repo core after syncing')
})