// READ-ONLY forensics: replicate each DID's firehose with a KEYLESS reader (own throwaway
// corestore, no writes, daemon untouched) and audit every #commit op. The signature we are
// hunting is delete/create OSCILLATION on the same path — a lagging co-owner deleting a record
// the caught-up co-owner then re-creates. A genuine user delete is terminal.
import './lib/bare-globals.js'
import fs from 'bare-fs'
import b4a from 'b4a'
import Hyperswarm from 'hyperswarm'
import { secp256k1 } from '@noble/curves/secp256k1'
import { DidPeerRouter } from 'atproto-did-peer'
import { openCorestore } from 'atproto-store/lib/corestore.js'
import { decodeCommitFrame } from 'atproto-store/lib/firehose.js'
import { discoveryTopic } from 'atproto-identity/lib/discovery.js'
const DIR = '/tmp/claude-1000/-storage-projects-code-hypercore-kb/6cf0ccb9-db69-4cc4-8094-9b7f3d26480b/scratchpad/delete-audit'
try { fs.rmSync(DIR, { recursive: true }) } catch {}
const cfg = JSON.parse(fs.readFileSync('/home/fleeky/.config/hyper-proto-peer/config.json', 'utf8'))
const store = openCorestore(DIR)
const swarm = new Hyperswarm()
const router = new DidPeerRouter(store, { swarm })
const sleep = (ms) => new Promise((r) => setTimeout(r, ms))
for (const d of (cfg.dids || [])) {
const verifyKey = b4a.from(secp256k1.getPublicKey(b4a.from(d.signingKey, 'hex'), true))
router.add(d.did, { verifyKey, noRepo: true, onError: () => {} })
swarm.join(discoveryTopic(d.did), { client: true, server: true })
}
await swarm.flush().catch(() => {})
for (const d of (cfg.dids || [])) {
const tag = (d.handle || d.did).padEnd(22)
let front
try { front = await router.front(d.did, { timeout: 45000 }) } catch (e) { console.log(tag, 'NO FRONT:', e.message); continue }
try { await Promise.race([front.seq.core.update({ wait: true }), sleep(15000)]) } catch {}
const total = front.seq && front.seq.core ? front.seq.core.length : 0
const seen = [] // [{action, path}] in frame order
let n = 0
try {
for await (const { bytes } of front.subscribeRepos(0)) {
const dec = decodeCommitFrame(bytes)
if (dec && dec.body && dec.body.ops) for (const o of dec.body.ops) seen.push({ action: o.action, path: o.path, rev: dec.body.rev })
if (++n >= total) break
}
} catch (e) { console.log(tag, 'read stopped:', e.message) }
const dels = seen.filter((o) => o.action === 'delete')
// oscillation = a path that is CREATED after having been DELETED
const osc = []
const state = new Map()
for (const o of seen) {
if (o.action === 'delete') state.set(o.path, o.rev)
else if (state.has(o.path)) { osc.push({ path: o.path, deletedAt: state.get(o.path), recreatedAt: o.rev }); state.delete(o.path) }
}
console.log('\n%s frames=%d ops=%d deletes=%d', tag, n, seen.length, dels.length)
for (const o of dels) console.log(' delete %s @rev %s', o.path, o.rev)
console.log(' OSCILLATIONS (delete->recreate, the spurious-delete signature):', osc.length)
for (const o of osc) console.log(' !! %s deleted@%s recreated@%s', o.path, o.deletedAt, o.recreatedAt)
}
try { await swarm.destroy() } catch {}
try { await store.close() } catch {}