OverviewFilesCommitsIssuesDocsReleasesPeers

atproto-identity / lib / binding.js

48 lines · javascript ref:
// 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 }