// The identity binding — one BIP39 mnemonic, both worlds.
//
// mnemonic ─┬─ keet-identity-key ──► Ed25519 identity key (Hypercore / our P2P)
// └─ this repo ──► secp256k1 + did:plc (atproto / bsky)
//
// Verified prerequisite: keet's bip39-mnemonic and our @scure/bip39 derive the
// IDENTICAL standard BIP39 seed, so the two derivations agree on one phrase (see
// the seed-equality check). This module produces the durable binding record that
// hyper-identity's `atproto` provider persists.
import IdentityKey from 'keet-identity-key'
import { createAtprotoIdentity, deriveAtprotoKey, ATPROTO_SIGNING_PATH } from './atproto-identity.js'
function toHex (bytes) { return Buffer.from(bytes).toString('hex') }
// mnemonic -> { keet Ed25519 identity pubkey, atproto secp256k1/did:plc }.
export async function deriveIdentity (mnemonic, { handle, pdsUrl, path = ATPROTO_SIGNING_PATH }) {
const idk = await IdentityKey.from({ mnemonic })
const ed25519PublicKey = Buffer.from(idk.identityPublicKey) // copy before clear()
idk.clear() // zero the secret material we don't retain
const atproto = createAtprotoIdentity(mnemonic, { handle, pdsUrl, path })
return {
ed25519PublicKey, // keet identity (Hypercore world)
secp256k1PublicKey: atproto.publicKey, // atproto signing (bsky world)
didKey: atproto.didKey, // did:key:zQ3s…
did: atproto.did, // did:plc:…
handle,
pdsUrl,
signedOp: atproto.signedOp
}
}
// JSON-safe binding record for the hyper-identity `atproto` provider / registry.
export async function bindingRecord (mnemonic, opts) {
const id = await deriveIdentity(mnemonic, opts)
return {
provider: 'atproto',
did: id.did,
handle: id.handle,
pdsUrl: id.pdsUrl,
didKey: id.didKey,
ed25519PublicKey: toHex(id.ed25519PublicKey),
secp256k1PublicKey: toHex(id.secp256k1PublicKey)
}
}
export { deriveAtprotoKey }