// Migration daemon: adopt an EXISTING published DID + its signing key, import a repo CAR, and
// serve it self-custody over Hyperswarm (discoverable by DID). First run adopts + imports;
// later runs reload the vault. Used to move a custodial account (e.g. wj62/p2p.7rnx.net) onto
// a self-custody peer without changing the DID.
//
// PEER_STORAGE=<dir> ADOPT_DID=did:plc:… ADOPT_DID_KEY=did:key:… ADOPT_DEVICE_PRIV_HEX=…
// ADOPT_HANDLE=… ADOPT_PDS_URL=… ADOPT_CAR=<path-to-repo.car> node bin/peer-migrate.js
import Hyperswarm from 'hyperswarm'
import { existsSync, readFileSync } from 'node:fs'
import { join } from 'node:path'
import { Peer } from '../lib/peer.js'
import { loadOrCreateSwarmKeyPair } from '../lib/swarm-keypair.js'
import { readCar, listRecords } from 'atproto-repo/lib/repo.js'
const storage = process.env.PEER_STORAGE
if (!storage) { console.error('PEER_STORAGE required'); process.exit(1) }
let peer
if (existsSync(join(storage, 'peer-identity.json'))) {
peer = await Peer.load({ storage })
console.log(JSON.stringify({ msg: 'reloaded adopted peer', did: peer.did }))
} else {
peer = await Peer.adopt({
storage,
did: process.env.ADOPT_DID,
handle: process.env.ADOPT_HANDLE,
privateKey: process.env.ADOPT_DEVICE_PRIV_HEX,
didKey: process.env.ADOPT_DID_KEY,
pdsUrl: process.env.ADOPT_PDS_URL
})
if (process.env.ADOPT_CAR) {
const car = await readCar(new Uint8Array(readFileSync(process.env.ADOPT_CAR)))
const recs = await listRecords(car)
const parsed = recs.map((r) => {
if (r.collection && r.rkey != null) return { collection: r.collection, rkey: r.rkey, record: r.value }
const [collection, ...rest] = String(r.key).split('/')
return { collection, rkey: rest.join('/'), record: r.value }
})
await peer.importRecords(parsed)
console.log(JSON.stringify({ msg: 'imported repo', count: parsed.length, collections: [...new Set(parsed.map((p) => p.collection))] }))
}
}
const swarm = new Hyperswarm({ keyPair: loadOrCreateSwarmKeyPair(storage) })
const serving = peer.serve(swarm)
await serving.ready()
console.log(JSON.stringify({
msg: 'adopted peer serving', did: peer.did, handle: peer.handle, didKey: peer.didKey,
serverKey: serving.serverKey, discoveryTopic: serving.discoveryTopic,
repoKey: peer.repoKeyHex, firehoseKey: peer.firehoseKeyHex, firehoseLen: peer.seq.length
}))
const shutdown = async () => { try { serving.close(); await swarm.destroy(); await peer.close() } catch {}; process.exit(0) }
process.on('SIGINT', shutdown)
process.on('SIGTERM', shutdown)