// Device-attested anchoring (atproto side): the genesis op must place the ROOT identity in
// rotationKeys (authority/recovery) and the DEVICE key in verificationMethod (repo
// signing), and be signed by the ROOT — never the device. This is the permanent shape
// baked at genesis, so it must be correct before the one irreversible PLC write.
import test from 'brittle'
import { buildAttestedIdentity, verifyAttestedGenesis } from '../lib/device-attestation.js'
import { deriveAtprotoKey } from '../lib/atproto-identity.js'
// two distinct valid BIP39 vectors → distinct secp256k1 keys (root vs device)
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'
test('attested genesis: root=rotationKey, device=verificationMethod, root-signed', (t) => {
const id = buildAttestedIdentity({
rootMnemonic: ROOT, deviceMnemonic: DEVICE,
handle: 'p2p.7rnx.net', pdsUrl: 'https://pds.7rnx.net'
})
const op = id.signedOp
t.ok(id.did.startsWith('did:plc:'), 'did:plc minted from the attested genesis')
t.not(id.deviceDidKey, id.rootDidKey, 'device signing key ≠ root rotation key (separation)')
t.is(op.verificationMethods.atproto, id.deviceDidKey, 'verificationMethod = device signing key')
t.is(op.rotationKeys[0], id.rootDidKey, 'rotationKeys[0] = root (authority / recovery)')
t.ok(verifyAttestedGenesis(op, id.rootPublicKey), 'genesis is signed by the ROOT rotation key')
t.absent(verifyAttestedGenesis(op, id.deviceSigningKey.publicKey), 'genesis is NOT signed by the device key')
t.ok(id.deviceSigningKey.privateKey && id.deviceSigningKey.privateKey.length === 32,
'device holds a usable 32-byte signing key (it signs the repo commits)')
})
test('extra rotation keys form a recovery chain (root + device both authoritative)', (t) => {
const dev = deriveAtprotoKey(DEVICE)
const id = buildAttestedIdentity({
rootMnemonic: ROOT, deviceMnemonic: DEVICE, extraRotationDidKeys: [dev.didKey],
handle: 'p2p.7rnx.net', pdsUrl: 'https://pds.7rnx.net'
})
const rk = id.signedOp.rotationKeys
t.is(rk.length, 2, 'two rotation keys (a chain: root + device)')
t.is(rk[0], id.rootDidKey, 'root has priority (first in the ordered rotation set)')
t.is(rk[1], dev.didKey, 'device is also a rotation key — it can recover if the root is lost')
})
test('the did:plc changes if the rotation set changes (rotation is baked at genesis)', (t) => {
const a = buildAttestedIdentity({ rootMnemonic: ROOT, deviceMnemonic: DEVICE, handle: 'p2p.7rnx.net', pdsUrl: 'https://pds.7rnx.net' })
const dev = deriveAtprotoKey(DEVICE)
const b = buildAttestedIdentity({ rootMnemonic: ROOT, deviceMnemonic: DEVICE, extraRotationDidKeys: [dev.didKey], handle: 'p2p.7rnx.net', pdsUrl: 'https://pds.7rnx.net' })
t.not(a.did, b.did, 'a different rotation set yields a different permanent DID — decide it before --publish')
})