OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-proto-peer / test / peer-init.test.js

84 lines · javascript ref:
// Init UX: a fresh peer onboards with the atproto-correct self-custody identity — a
// DETERMINISTIC, root-derived genesis pins the DID (reproducible, device-INDEPENDENT), then a
// POST-genesis rotation attests the per-device SIGNING key. ROTATION authority = the root
// (recovery seed, NOT stored on the peer); SIGNING key = the device key the peer holds. Persists
// to a vault and RELOADS the same identity across restarts. (P0b — atproto-base/DESIGN.md §4a.)

import test from 'brittle'
import { mkdtempSync, rmSync, existsSync, readFileSync } from 'bare-fs'
import { tmpdir } from 'bare-os'
import { join } from 'bare-path'
import { Peer } from '../lib/peer.js'
import { verifyCommit } from 'atproto-repo/lib/commit.js'
import { decodeDidKey } from 'atproto-identity/lib/did-key.js'
import { verifyAttestedGenesis } from 'atproto-identity/lib/device-attestation.js'
import { createAtprotoIdentity, verifyOp } from 'atproto-identity'
import { opCid, unsignedOp } from 'atproto-identity/lib/did-plc.js'

const ROOT = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about'
const DEVICE = 'legal winner thank year wave sausage worth useful legal winner thank yellow'
const OPTS = { handle: 'alice.7rnx.net', pdsUrl: 'https://pds.7rnx.net' }

test('fresh self-custody init: deterministic genesis + device rotation, persisted + reloads', async (t) => {
  const dir = mkdtempSync(join(tmpdir(), 'peer-init-'))
  t.teardown(() => rmSync(dir, { recursive: true, force: true }))

  const peer = await Peer.init({ storage: dir, rootMnemonic: ROOT, deviceMnemonic: DEVICE, ...OPTS })

  // custody: the peer holds a 32-byte DEVICE signing key + a did:plc + BOTH ops to publish
  t.is(peer.privateKey.length, 32, 'peer holds its own device signing key')
  t.ok(peer.did.startsWith('did:plc:'), 'minted a did:plc')
  t.ok(peer.genesisOp && peer.genesisOp.sig, 'holds a signed genesis op')
  t.ok(peer.rotationOp && peer.rotationOp.sig, 'holds a signed rotation op (device attestation)')

  // THE FIX: the DID is the DETERMINISTIC root-derived genesis — reproducible + device-independent
  t.is(peer.did, createAtprotoIdentity(ROOT, OPTS).did, 'DID = f(root,handle,pds), NOT f(device key)')
  t.is(peer.genesisOp.verificationMethods.atproto, peer.rootDidKey, 'genesis vm = ROOT-derived key (deterministic)')

  // device attestation lives in the ROTATION, not the genesis
  const { pubkey: rootPub } = decodeDidKey(peer.rootDidKey)
  t.ok(verifyAttestedGenesis(peer.genesisOp, rootPub), 'genesis signed by the root')
  t.is(peer.rotationOp.verificationMethods.atproto, peer.didKey, 'rotation vm = the DEVICE signing key')
  t.is(peer.rotationOp.prev, opCid(peer.genesisOp), 'rotation chains onto the genesis (prev)')
  t.ok(verifyOp(unsignedOp(peer.rotationOp), peer.rotationOp.sig, rootPub), 'rotation signed by the root')
  t.ok(peer.rotationKeys.includes(peer.rootDidKey), 'root is a rotation (recovery) key')
  t.not(peer.didKey, peer.rootDidKey, 'signing key is separated from the rotation key')

  // the vault persists the DEVICE key + both ops but NOT the root seed
  const vault = JSON.parse(readFileSync(join(dir, 'peer-identity.json'), 'utf8'))
  t.ok(vault.devicePrivateKey && vault.genesisOp && vault.rotationOp, 'vault stores device key + genesis + rotation')
  t.absent(vault.rootMnemonic || vault.rootPrivateKey, 'vault does NOT store the root seed')

  // author a record — signed by the device key (the DID doc vm after rotation)
  const w = await peer.write({ collection: 'app.bsky.feed.post', rkey: '3aaa', record: { $type: 'app.bsky.feed.post', text: 'onboarded', createdAt: '2026-07-03T00:00:00.000Z' } })
  const { pubkey: devPub } = decodeDidKey(peer.didKey)
  t.ok(verifyCommit(w.signedCommit, devPub), 'commit signed by the device key')
  await peer.close()

  // durability: reload from the vault -> SAME identity + repo (no phantom identity on restart)
  const reopened = await Peer.load({ storage: dir })
  t.is(reopened.did, peer.did, 'reload yields the same DID')
  t.is(reopened.didKey, peer.didKey, 'same device signing key after reload')
  t.is(reopened.rotationOp.prev, opCid(reopened.genesisOp), 'reloaded rotation still chains to genesis')
  t.is((await reopened.store.records()).length, 1, 'repo persisted across reload')
  await reopened.close()

  // init refuses to clobber an existing identity (never destroy custody)
  await t.exception(Peer.init({ storage: dir, rootMnemonic: ROOT, deviceMnemonic: DEVICE, handle: 'x', pdsUrl: 'y' }),
    'init refuses to overwrite an existing vault')
  t.ok(existsSync(join(dir, 'peer-identity.json')), 'existing vault untouched')
})

test('DID is device-independent: a different device key yields the SAME DID', async (t) => {
  const d1 = mkdtempSync(join(tmpdir(), 'peer-a-'))
  const d2 = mkdtempSync(join(tmpdir(), 'peer-b-'))
  t.teardown(() => { rmSync(d1, { recursive: true, force: true }); rmSync(d2, { recursive: true, force: true }) })

  // same root+handle+pds, DIFFERENT device (one from mnemonic, one auto-minted)
  const a = await Peer.init({ storage: d1, rootMnemonic: ROOT, deviceMnemonic: DEVICE, ...OPTS })
  const b = await Peer.init({ storage: d2, rootMnemonic: ROOT, ...OPTS }) // auto-mint a fresh device key
  t.is(a.did, b.did, 'two devices under ONE reproducible DID (the multi-device precondition)')
  t.not(a.didKey, b.didKey, 'but each holds its OWN device signing key')
  await a.close(); await b.close()
})