// Proof-of-fix test for the device-invite ↔ IdentityRegistry contract.
//
// Hypothesis (already empirically confirmed by device-invite-writer-mismatch.test.js):
// the source pre-computes the target's autobase writer key from newDeviceKp;
// for that prediction to hold, the target's IdentityRegistry must open
// the autobase with `keyPair: newDeviceKp` AND `bootstrap: source's autobase root`.
//
// This test models the canonical end-to-end flow using only autobase + corestore +
// Hypercore.key — no pear-git-v3 wrappers, no daemon, no Hyperswarm. If this passes,
// the fix is mechanically sound; we then apply it to lib/pear-git.js's
// IdentityRegistry construction (mirrors only).
//
// Flow modelled:
// 1. PRIMARY creates Autobase with keyPair=primaryKp. autobase.key = root.
// 2. SOURCE generates newDeviceKp for a NEW device (mirror). Computes
// pre = Hypercore.key({signers:[{publicKey: newDeviceKp.publicKey}]}).
// 3. PRIMARY (= source's daemon) appends an addWriter op for `pre`.
// 4. MIRROR opens its own corestore + Autobase with bootstrap=primary.base.key
// AND keyPair=newDeviceKp. mirror.base.local.key MUST equal `pre`.
// 5. Replicate corestores. Mirror should become writable. Mirror appends.
// 6. Primary applies mirror's op into its view. signedLength advances.
import test from 'brittle'
import Autobase from 'autobase'
import Corestore from 'corestore'
import Hypercore from 'hypercore'
import crypto from 'hypercore-crypto'
import b4a from 'b4a'
import os from '#node:os'
import path from '#node:path'
import fs from '#node:fs'
import { replicateAndSync } from 'autobase-test-helpers'
const TMP = fs.mkdtempSync(path.join(os.tmpdir(), 'dvi-fix-proof-'))
let n = 0
const tmp = () => path.join(TMP, 'store-' + (n++))
async function apply (nodes, view, host) {
for (const node of nodes) {
const v = node.value
if (v && v.type === 'add-writer') {
await host.addWriter(b4a.from(v.writerKey, 'hex'), { indexer: false })
} else {
await view.append(v)
}
}
}
const baseOpts = {
apply,
open: (s) => s.get('view', { valueEncoding: 'json' }),
valueEncoding: 'json',
optimistic: true,
ackInterval: 1000,
ackThreshold: 4
}
test('source pre-compute + mirror keyPair = mirror becomes writable cleanly', async (t) => {
// ── PRIMARY (source) ─────────────────────────────────────────
const primaryKp = crypto.keyPair() // = hyperident master keypair on source
const primaryStore = new Corestore(tmp())
await primaryStore.ready()
const primary = new Autobase(primaryStore, null, { ...baseOpts, keyPair: primaryKp })
await primary.ready()
await primary.append({ msg: 'primary boot' })
await primary.update()
// ── DEVICE INVITE (source side) ──────────────────────────────
// Identical to lib/device-linker.js createInvite:
const newDeviceKp = crypto.keyPair()
const predictedWriterKey = Hypercore.key({ version: 1, signers: [{ publicKey: newDeviceKp.publicKey }] })
// Source addWriters predicted key — identical to lib/handlers/devices.js:102.
await primary.append({ type: 'add-writer', writerKey: b4a.toString(predictedWriterKey, 'hex') })
await primary.update()
// ── MIRROR opens with bootstrap=primary.key + keyPair=newDeviceKp ──
const mirrorStore = new Corestore(tmp())
await mirrorStore.ready()
const mirror = new Autobase(mirrorStore, primary.key, { ...baseOpts, keyPair: newDeviceKp })
await mirror.ready()
// PRIMARY pre-computed predictedWriterKey based on newDeviceKp.publicKey.
// MIRROR's actual local.key must equal predictedWriterKey for the addWriter
// to register the right writer.
console.log(' predicted (source addWritered): ', b4a.toString(predictedWriterKey, 'hex').slice(0, 16) + '…')
console.log(' mirror.local.key (actual): ', b4a.toString(mirror.local.key, 'hex').slice(0, 16) + '…')
t.ok(b4a.equals(mirror.local.key, predictedWriterKey),
'mirror.local.key === predictedWriterKey (DeviceLinker prediction holds when target uses keyPair=newDeviceKp)')
// ── Replicate primary <-> mirror via in-process stream pipe ─
const sa = primaryStore.replicate(true)
const sb = mirrorStore.replicate(false)
sa.on('error', () => {}); sb.on('error', () => {})
sa.pipe(sb).pipe(sa)
await replicateAndSync([primary, mirror])
t.ok(mirror.writable, 'mirror.writable === true after sync (addWriter was for the right key)')
// ── Mirror appends, primary sees it ──────────────────────────
await mirror.append({ msg: 'mirror boot, can write' })
await mirror.update()
await replicateAndSync([primary, mirror])
let seenMirrorMsg = false
for (let i = 0; i < primary.view.length; i++) {
const v = await primary.view.get(i)
if (v && v.msg === 'mirror boot, can write') { seenMirrorMsg = true; break }
}
t.ok(seenMirrorMsg, 'primary.view contains mirror\'s op (cross-machine write linearized)')
console.log(' primary.signedLength:', primary.signedLength)
console.log(' mirror.signedLength: ', mirror.signedLength)
t.ok(primary.signedLength > 0 && mirror.signedLength > 0, 'both peers have advanced signedLength')
await primary.close(); await primaryStore.close()
await mirror.close(); await mirrorStore.close()
})
test('CONTROL: same setup but mirror opened WITHOUT keyPair → mirror is NOT writable (reproduces today\'s prod bug)', async (t) => {
const primaryKp = crypto.keyPair()
const primaryStore = new Corestore(tmp())
await primaryStore.ready()
const primary = new Autobase(primaryStore, null, { ...baseOpts, keyPair: primaryKp })
await primary.ready()
await primary.append({ msg: 'primary boot' })
await primary.update()
const newDeviceKp = crypto.keyPair()
const predictedWriterKey = Hypercore.key({ version: 1, signers: [{ publicKey: newDeviceKp.publicKey }] })
await primary.append({ type: 'add-writer', writerKey: b4a.toString(predictedWriterKey, 'hex') })
await primary.update()
// Mirror opens WITHOUT keyPair — exactly today's post-revert IdentityRegistry.
const mirrorStore = new Corestore(tmp())
await mirrorStore.ready()
const mirror = new Autobase(mirrorStore, primary.key, { ...baseOpts /* no keyPair */ })
await mirror.ready()
const sa = primaryStore.replicate(true)
const sb = mirrorStore.replicate(false)
sa.on('error', () => {}); sb.on('error', () => {})
sa.pipe(sb).pipe(sa)
await replicateAndSync([primary, mirror])
console.log(' mirror.local.key: ', b4a.toString(mirror.local.key, 'hex').slice(0, 16) + '…')
console.log(' predicted (source addWritered): ', b4a.toString(predictedWriterKey, 'hex').slice(0, 16) + '…')
t.absent(b4a.equals(mirror.local.key, predictedWriterKey),
'mirror.local.key !== predictedWriterKey (today\'s bug: source addWritered the wrong key)')
t.absent(mirror.writable,
'mirror NOT writable — exactly the prod symptom: stortron returns "Not writable" on mutating RPCs')
await primary.close(); await primaryStore.close()
await mirror.close(); await mirrorStore.close()
})