| … | … | @@ -0,0 +1 @@ |
| 1 | node_modules/ |
| … | … | @@ -0,0 +1,98 @@ |
| 1 | // Seed-anchored atproto identity — the secp256k1 / did:plc half. | |
| 2 | // | |
| 3 | // One BIP39 mnemonic anchors both worlds: | |
| 4 | // mnemonic -> Ed25519 (keet-identity-key) -> Hypercore log signing [NEXT increment] | |
| 5 | // mnemonic -> secp256k1 (this module) -> did:plc + atproto commits | |
| 6 | // | |
| 7 | // This module owns the atproto side: deterministic secp256k1 derivation, did:key | |
| 8 | // encoding, and building/signing a did:plc genesis operation — entirely offline. | |
| 9 | // It never submits to the PLC directory (the op-log is permanent; don't churn). | |
| 10 | ||
| 11 | import { mnemonicToSeedSync } from '@scure/bip39' | |
| 12 | import { HDKey } from '@scure/bip32' | |
| 13 | import { secp256k1 } from '@noble/curves/secp256k1' | |
| 14 | import { sha256 } from '@noble/hashes/sha2' | |
| 15 | import { base64url } from 'multiformats/bases/base64' | |
| 16 | import { encodeDidKey, CURVE_SECP256K1 } from './did-key.js' | |
| 17 | import { encodeOp, unsignedOp, didForCreateOp } from './did-plc.js' | |
| 18 | ||
| 19 | // Our fixed derivation path. No BIP44 coin type is registered for atproto, so this | |
| 20 | // is a project convention — the *mnemonic* is the recovery anchor; the path is fixed | |
| 21 | // by this constant so the same phrase always yields the same atproto key. | |
| 22 | export const ATPROTO_SIGNING_PATH = "m/44'/0'/0'/0/0" | |
| 23 | ||
| 24 | // raw base64url (no multibase prefix, no padding) — atproto's sig encoding. | |
| 25 | function sigToBase64url (bytes) { | |
| 26 | return base64url.encode(bytes).slice(1) // drop multibase 'u' | |
| 27 | } | |
| 28 | export function base64urlToBytes (str) { | |
| 29 | return base64url.decode('u' + str) | |
| 30 | } | |
| 31 | ||
| 32 | // mnemonic -> deterministic secp256k1 keypair for atproto signing. | |
| 33 | export function deriveAtprotoKey (mnemonic, path = ATPROTO_SIGNING_PATH) { | |
| 34 | const seed = mnemonicToSeedSync(mnemonic) | |
| 35 | const node = HDKey.fromMasterSeed(seed).derive(path) | |
| 36 | if (!node.privateKey) throw new Error('derivation produced no private key') | |
| 37 | return keyFromPrivate(node.privateKey) | |
| 38 | } | |
| 39 | ||
| 40 | // raw 32-byte secp256k1 private key -> { privateKey, publicKey, didKey }. | |
| 41 | export function keyFromPrivate (privateKey) { | |
| 42 | const publicKey = secp256k1.getPublicKey(privateKey, true) // 33-byte compressed | |
| 43 | return { privateKey, publicKey, didKey: encodeDidKey(CURVE_SECP256K1, publicKey) } | |
| 44 | } | |
| 45 | ||
| 46 | // A fresh random atproto signing keypair (server-minted accounts with no seed anchor). | |
| 47 | export function generateAtprotoKey () { | |
| 48 | return keyFromPrivate(secp256k1.utils.randomPrivateKey()) | |
| 49 | } | |
| 50 | ||
| 51 | // ECDSA sign the sha256 of DAG-CBOR(unsignedOp), low-S, compact -> base64url. | |
| 52 | export function signOp (unsigned, privateKey) { | |
| 53 | const hash = sha256(encodeOp(unsigned)) | |
| 54 | const sig = secp256k1.sign(hash, privateKey, { lowS: true }) | |
| 55 | return sigToBase64url(sig.toCompactRawBytes()) | |
| 56 | } | |
| 57 | ||
| 58 | // verify a signature string against an unsigned op + did:key-encoded pubkey bytes. | |
| 59 | export function verifyOp (unsigned, sigB64url, publicKey) { | |
| 60 | const hash = sha256(encodeOp(unsigned)) | |
| 61 | return secp256k1.verify(base64urlToBytes(sigB64url), hash, publicKey, { lowS: true }) | |
| 62 | } | |
| 63 | ||
| 64 | // Build an UNSIGNED did:plc genesis op. rotationKey defaults to the signing key | |
| 65 | // (single-key setup); pass a distinct rotation didKey for key-separation. | |
| 66 | export function buildGenesisOp ({ signingDidKey, rotationDidKeys, handle, pdsUrl }) { | |
| 67 | return { | |
| 68 | type: 'plc_operation', | |
| 69 | rotationKeys: rotationDidKeys && rotationDidKeys.length ? rotationDidKeys : [signingDidKey], | |
| 70 | verificationMethods: { atproto: signingDidKey }, | |
| 71 | alsoKnownAs: [`at://${handle}`], | |
| 72 | services: { | |
| 73 | atproto_pds: { type: 'AtprotoPersonalDataServer', endpoint: pdsUrl } | |
| 74 | }, | |
| 75 | prev: null | |
| 76 | } | |
| 77 | } | |
| 78 | ||
| 79 | // key { privateKey, publicKey, didKey } -> signed genesis op + did:plc. | |
| 80 | export function identityFromKey (key, { handle, pdsUrl }) { | |
| 81 | const unsigned = buildGenesisOp({ signingDidKey: key.didKey, handle, pdsUrl }) | |
| 82 | const sig = signOp(unsigned, key.privateKey) | |
| 83 | const signedOp = { ...unsigned, sig } | |
| 84 | const did = didForCreateOp(signedOp) | |
| 85 | return { did, didKey: key.didKey, publicKey: key.publicKey, privateKey: key.privateKey, signedOp } | |
| 86 | } | |
| 87 | ||
| 88 | // Full offline flow: mnemonic -> signed genesis op + did:plc. | |
| 89 | export function createAtprotoIdentity (mnemonic, { handle, pdsUrl, path = ATPROTO_SIGNING_PATH }) { | |
| 90 | return identityFromKey(deriveAtprotoKey(mnemonic, path), { handle, pdsUrl }) | |
| 91 | } | |
| 92 | ||
| 93 | // Same, but from a raw private key (server-minted / bring-your-own accounts). | |
| 94 | export function createAtprotoIdentityFromKey (privateKey, { handle, pdsUrl }) { | |
| 95 | return identityFromKey(keyFromPrivate(privateKey), { handle, pdsUrl }) | |
| 96 | } | |
| 97 | ||
| 98 | export { unsignedOp, didForCreateOp } |
| … | … | @@ -0,0 +1,42 @@ |
| 1 | // Compose the full device-attested atproto anchor — both worlds, from one root + one | |
| 2 | // device. See docs/ATPROTO-P2P-BRIDGE.md (device attestation) and §13 (purity firewall). | |
| 3 | // | |
| 4 | // atproto (federation-visible): genesis with rotationKeys=[root secp256k1] + | |
| 5 | // verificationMethod=[device secp256k1], signed by root. | |
| 6 | // keet (P2P-verifiable): root → device(Ed25519) → vouches-for(did). | |
| 7 | // | |
| 8 | // The ROOT box (holds rootMnemonic) signs the genesis + attests the device. The DEVICE box | |
| 9 | // only ever needs `deviceSigningKey` (secp256k1) to run the PDS. In production the device | |
| 10 | // derives its own keys and sends only PUBLIC keys to the root — use buildAttestedIdentity | |
| 11 | // with `deviceDidKey` for that path; this helper takes both mnemonics for the end-to-end | |
| 12 | // (dev / single-operator) case. | |
| 13 | ||
| 14 | import { buildAttestedIdentity } from './device-attestation.js' | |
| 15 | import { deviceKeetKeyPair, attestDeviceChain, attestAtprotoBinding } from './device-chain.js' | |
| 16 | ||
| 17 | export async function buildAttestedAnchor ({ | |
| 18 | rootMnemonic, rootSeed, | |
| 19 | deviceMnemonic, deviceSeed, devicePath, | |
| 20 | handle, pdsUrl, | |
| 21 | extraRotationDidKeys = [] | |
| 22 | }) { | |
| 23 | // atproto side — the permanent, PLC-bound genesis | |
| 24 | const atp = buildAttestedIdentity({ rootMnemonic, deviceMnemonic, devicePath, handle, pdsUrl, extraRotationDidKeys }) | |
| 25 | ||
| 26 | // keet side — the P2P attestation binding the DID to the root identity | |
| 27 | const deviceKeet = await deviceKeetKeyPair(deviceMnemonic, deviceSeed) | |
| 28 | const chain = await attestDeviceChain({ rootMnemonic, rootSeed, devicePublicKey: deviceKeet.publicKey }) | |
| 29 | const keetProof = attestAtprotoBinding({ deviceKeyPair: deviceKeet, did: atp.did, proof: chain }) | |
| 30 | ||
| 31 | return { | |
| 32 | did: atp.did, | |
| 33 | handle, | |
| 34 | pdsUrl, | |
| 35 | signedOp: atp.signedOp, // → the one irreversible PLC write publishes this | |
| 36 | rootDidKey: atp.rootDidKey, // secp256k1 rotation (authority/recovery — root box) | |
| 37 | deviceDidKey: atp.deviceDidKey, // secp256k1 signing (verificationMethod — device box) | |
| 38 | deviceSigningKey: atp.deviceSigningKey, // { privateKey, … } — all the PDS needs to sign commits | |
| 39 | deviceKeetPublicKey: deviceKeet.publicKey, // Ed25519 device identity (P2P) | |
| 40 | keetProof // P2P-verifiable: chain resolves to root, device vouches for did | |
| 41 | } | |
| 42 | } |
| … | … | @@ -0,0 +1,47 @@ |
| 1 | // The identity binding — one BIP39 mnemonic, both worlds. | |
| 2 | // | |
| 3 | // mnemonic ─┬─ keet-identity-key ──► Ed25519 identity key (Hypercore / our P2P) | |
| 4 | // └─ this repo ──► secp256k1 + did:plc (atproto / bsky) | |
| 5 | // | |
| 6 | // Verified prerequisite: keet's bip39-mnemonic and our @scure/bip39 derive the | |
| 7 | // IDENTICAL standard BIP39 seed, so the two derivations agree on one phrase (see | |
| 8 | // the seed-equality check). This module produces the durable binding record that | |
| 9 | // hyper-identity's `atproto` provider persists. | |
| 10 | ||
| 11 | import IdentityKey from 'keet-identity-key' | |
| 12 | import { createAtprotoIdentity, deriveAtprotoKey, ATPROTO_SIGNING_PATH } from './atproto-identity.js' | |
| 13 | ||
| 14 | function toHex (bytes) { return Buffer.from(bytes).toString('hex') } | |
| 15 | ||
| 16 | // mnemonic -> { keet Ed25519 identity pubkey, atproto secp256k1/did:plc }. | |
| 17 | export async function deriveIdentity (mnemonic, { handle, pdsUrl, path = ATPROTO_SIGNING_PATH }) { | |
| 18 | const idk = await IdentityKey.from({ mnemonic }) | |
| 19 | const ed25519PublicKey = Buffer.from(idk.identityPublicKey) // copy before clear() | |
| 20 | idk.clear() // zero the secret material we don't retain | |
| 21 | const atproto = createAtprotoIdentity(mnemonic, { handle, pdsUrl, path }) | |
| 22 | return { | |
| 23 | ed25519PublicKey, // keet identity (Hypercore world) | |
| 24 | secp256k1PublicKey: atproto.publicKey, // atproto signing (bsky world) | |
| 25 | didKey: atproto.didKey, // did:key:zQ3s… | |
| 26 | did: atproto.did, // did:plc:… | |
| 27 | handle, | |
| 28 | pdsUrl, | |
| 29 | signedOp: atproto.signedOp | |
| 30 | } | |
| 31 | } | |
| 32 | ||
| 33 | // JSON-safe binding record for the hyper-identity `atproto` provider / registry. | |
| 34 | export async function bindingRecord (mnemonic, opts) { | |
| 35 | const id = await deriveIdentity(mnemonic, opts) | |
| 36 | return { | |
| 37 | provider: 'atproto', | |
| 38 | did: id.did, | |
| 39 | handle: id.handle, | |
| 40 | pdsUrl: id.pdsUrl, | |
| 41 | didKey: id.didKey, | |
| 42 | ed25519PublicKey: toHex(id.ed25519PublicKey), | |
| 43 | secp256k1PublicKey: toHex(id.secp256k1PublicKey) | |
| 44 | } | |
| 45 | } | |
| 46 | ||
| 47 | export { deriveAtprotoKey } |
| … | … | @@ -0,0 +1,76 @@ |
| 1 | // Device-attested atproto anchoring. | |
| 2 | // | |
| 3 | // The atproto identity separates two roles (this is atproto's native design, and it maps | |
| 4 | // onto a device chain — see docs/ATPROTO-P2P-BRIDGE.md §device-attestation): | |
| 5 | // | |
| 6 | // rotationKeys = authority / recovery keys (the ROOT identity, kept off the | |
| 7 | // serving box). Any rotationKey can sign a PLC op to rotate the | |
| 8 | // signing key, change the handle, or move the PDS. | |
| 9 | // verificationMethod = the repo SIGNING key (a per-DEVICE key — the box that runs the | |
| 10 | // PDS holds only this). Signs every commit. | |
| 11 | // | |
| 12 | // So the genesis op is signed by the ROOT rotation key and authorizes a DEVICE signing | |
| 13 | // key. The device never holds the root seed; if the device is lost, the root rotates to a | |
| 14 | // new device and the DID/handle/repos persist. did:plc = hash(genesis), so the rotation | |
| 15 | // set is baked at genesis — this MUST be right before the one irreversible PLC write. | |
| 16 | // | |
| 17 | // This module owns the atproto (secp256k1 / PLC) side. The parallel keet-identity-key | |
| 18 | // Ed25519 device chain (our P2P-verifiable attestation) lives in device-chain.js; the two | |
| 19 | // run in parallel off the same device, bound by device-chain.js's attestData over the DID. | |
| 20 | ||
| 21 | import { deriveAtprotoKey, keyFromPrivate, buildGenesisOp, signOp, verifyOp } from './atproto-identity.js' | |
| 22 | import { unsignedOp, didForCreateOp } from './did-plc.js' | |
| 23 | ||
| 24 | // Resolve a { privateKey, publicKey, didKey } from a mnemonic or a raw secp256k1 key. | |
| 25 | function resolveKey (mnemonic, key, path) { | |
| 26 | if (key) return keyFromPrivate(key) | |
| 27 | if (mnemonic) return deriveAtprotoKey(mnemonic, path) | |
| 28 | throw new Error('device-attestation: need a mnemonic or a key') | |
| 29 | } | |
| 30 | ||
| 31 | // Build a device-attested did:plc: | |
| 32 | // rotationKeys = [root, ...extraRotationDidKeys] (root signs the genesis) | |
| 33 | // verificationMethod = the device signing key | |
| 34 | // Pass root by { rootMnemonic } or { rootKey } (secp256k1 private); device by | |
| 35 | // { deviceMnemonic, devicePath } or { deviceKey }. Optionally list additional rotation | |
| 36 | // did:keys (e.g. the device itself, or a second recovery device) for a real key chain. | |
| 37 | export function buildAttestedIdentity ({ | |
| 38 | rootMnemonic, rootKey, | |
| 39 | deviceMnemonic, deviceKey, deviceDidKey, devicePath, | |
| 40 | handle, pdsUrl, | |
| 41 | extraRotationDidKeys = [] | |
| 42 | }) { | |
| 43 | const root = resolveKey(rootMnemonic, rootKey) | |
| 44 | ||
| 45 | // Device: the REAL flow passes only the device's PUBLIC did:key — the device private key | |
| 46 | // never leaves the device box, and the root just authorizes the pubkey into the genesis. | |
| 47 | // The dev/test flow may pass a device mnemonic/key so the full signing key is available. | |
| 48 | let deviceSigningKey = null | |
| 49 | let deviceKeyId = deviceDidKey | |
| 50 | if (!deviceKeyId) { | |
| 51 | deviceSigningKey = resolveKey(deviceMnemonic, deviceKey, devicePath) | |
| 52 | deviceKeyId = deviceSigningKey.didKey | |
| 53 | } | |
| 54 | ||
| 55 | const rotationDidKeys = [root.didKey, ...extraRotationDidKeys] | |
| 56 | const unsigned = buildGenesisOp({ signingDidKey: deviceKeyId, rotationDidKeys, handle, pdsUrl }) | |
| 57 | const sig = signOp(unsigned, root.privateKey) // signed by the ROOT rotation key | |
| 58 | const signedOp = { ...unsigned, sig } | |
| 59 | const did = didForCreateOp(signedOp) | |
| 60 | ||
| 61 | return { | |
| 62 | did, | |
| 63 | signedOp, | |
| 64 | rootDidKey: root.didKey, | |
| 65 | rootPublicKey: root.publicKey, | |
| 66 | deviceDidKey: deviceKeyId, | |
| 67 | deviceSigningKey, // { privateKey, publicKey, didKey } when available; null in the pubkey-only root flow | |
| 68 | rotationKeys: rotationDidKeys | |
| 69 | } | |
| 70 | } | |
| 71 | ||
| 72 | // Verify an attested genesis: it must be signed by (one of) its rotation keys — i.e. by | |
| 73 | // the ROOT, not the device. Pass the root's secp256k1 public key. | |
| 74 | export function verifyAttestedGenesis (signedOp, rootPublicKey) { | |
| 75 | return verifyOp(unsignedOp(signedOp), signedOp.sig, rootPublicKey) | |
| 76 | } |
| … | … | @@ -0,0 +1,45 @@ |
| 1 | // keet-identity-key device chain — the P2P-verifiable half of device attestation. | |
| 2 | // | |
| 3 | // Runs in PARALLEL to the atproto PLC authority (device-attestation.js). atproto's | |
| 4 | // rotationKeys/PLC-op-log carry the FEDERATION-visible authority (secp256k1); this keet | |
| 5 | // chain carries the authority OUR P2P world verifies (Ed25519). Both anchor on the same | |
| 6 | // root identity and vouch for the same device. | |
| 7 | // | |
| 8 | // Shape (from keet-identity-key): a proof is `{ identity, epoch, chain: [{publicKey, | |
| 9 | // signature}], data? }`. `bootstrap` attests a device pubkey under the root identity; | |
| 10 | // `attestData` lets the (leaf) device sign arbitrary data — here, the atproto DID — so a | |
| 11 | // verifier learns "root → device → this device vouches for did:plc:…". `verify` walks the | |
| 12 | // chain back to the identity and checks the data signature. | |
| 13 | ||
| 14 | import IdentityKey from 'keet-identity-key' | |
| 15 | import b4a from 'b4a' | |
| 16 | ||
| 17 | // The device's keet-world Ed25519 keypair (derived from the device seed) — distinct from | |
| 18 | // its secp256k1 atproto signing key. This is the device's identity in the keet chain. | |
| 19 | export async function deviceKeetKeyPair (mnemonic, seed) { | |
| 20 | const idk = await IdentityKey.from({ mnemonic, seed }) | |
| 21 | return idk.identityKeyPair // { publicKey, secretKey } | |
| 22 | } | |
| 23 | ||
| 24 | // The root identity's Ed25519 public key — the anchor every chain must resolve to. | |
| 25 | export async function rootIdentityPublicKey (mnemonic, seed) { | |
| 26 | const idk = await IdentityKey.from({ mnemonic, seed }) | |
| 27 | return b4a.from(idk.identityPublicKey) | |
| 28 | } | |
| 29 | ||
| 30 | // Root identity attests a device's Ed25519 public key → an encoded proof chain. | |
| 31 | export async function attestDeviceChain ({ rootMnemonic, rootSeed, devicePublicKey }) { | |
| 32 | return IdentityKey.bootstrap({ mnemonic: rootMnemonic, seed: rootSeed }, devicePublicKey) | |
| 33 | } | |
| 34 | ||
| 35 | // The attested (leaf) device vouches for the atproto DID by signing it as attested data, | |
| 36 | // extending the proof with `proof.data`. Result: root → device → vouches-for(did). | |
| 37 | export function attestAtprotoBinding ({ deviceKeyPair, did, proof }) { | |
| 38 | return IdentityKey.attestData(b4a.from(did), deviceKeyPair, proof) | |
| 39 | } | |
| 40 | ||
| 41 | // Verify the full binding: the chain resolves to the root identity AND the device vouched | |
| 42 | // for exactly this DID. Returns { receipt, identityPublicKey, devicePublicKey } or null. | |
| 43 | export function verifyAtprotoBinding ({ proof, did, expectedIdentity, expectedDevice }) { | |
| 44 | return IdentityKey.verify(proof, b4a.from(did), { expectedIdentity, expectedDevice }) | |
| 45 | } |
| … | … | @@ -0,0 +1,61 @@ |
| 1 | // did:key encoding/decoding for the two curves atproto accepts. | |
| 2 | // | |
| 3 | // did:key:z<base58btc( <multicodec-varint> ++ <compressed-pubkey> )> | |
| 4 | // | |
| 5 | // Multicodec key-type prefixes (unsigned-varint): | |
| 6 | // secp256k1-pub = 0xe7 -> varint [0xe7, 0x01] -> did:key:zQ3s... | |
| 7 | // p256-pub = 0x1200 -> varint [0x80, 0x24] -> did:key:zDna... | |
| 8 | // | |
| 9 | // The pubkey is the 33-byte SEC1 compressed point (0x02/0x03 ++ x). This matches | |
| 10 | // @atproto/crypto's formatDidKey. Validated by the P-256 golden round-trip test. | |
| 11 | ||
| 12 | import { base58btc } from 'multiformats/bases/base58' | |
| 13 | ||
| 14 | export const CURVE_SECP256K1 = 'secp256k1' | |
| 15 | export const CURVE_P256 = 'p256' | |
| 16 | ||
| 17 | const MULTICODEC = { | |
| 18 | [CURVE_SECP256K1]: 0xe7, | |
| 19 | [CURVE_P256]: 0x1200 | |
| 20 | } | |
| 21 | const BY_CODE = { 0xe7: CURVE_SECP256K1, 0x1200: CURVE_P256 } | |
| 22 | ||
| 23 | function varintEncode (n) { | |
| 24 | const out = [] | |
| 25 | while (n >= 0x80) { out.push((n & 0x7f) | 0x80); n >>>= 7 } | |
| 26 | out.push(n) | |
| 27 | return Uint8Array.from(out) | |
| 28 | } | |
| 29 | ||
| 30 | function varintDecode (bytes) { | |
| 31 | let n = 0, shift = 0, i = 0 | |
| 32 | for (;;) { | |
| 33 | const b = bytes[i++] | |
| 34 | n |= (b & 0x7f) << shift | |
| 35 | if ((b & 0x80) === 0) break | |
| 36 | shift += 7 | |
| 37 | } | |
| 38 | return { code: n >>> 0, length: i } | |
| 39 | } | |
| 40 | ||
| 41 | // compressed pubkey bytes (33) -> did:key string | |
| 42 | export function encodeDidKey (curve, compressedPubkey) { | |
| 43 | const code = MULTICODEC[curve] | |
| 44 | if (code === undefined) throw new Error(`unsupported curve: ${curve}`) | |
| 45 | const prefix = varintEncode(code) | |
| 46 | const bytes = new Uint8Array(prefix.length + compressedPubkey.length) | |
| 47 | bytes.set(prefix, 0) | |
| 48 | bytes.set(compressedPubkey, prefix.length) | |
| 49 | return `did:key:${base58btc.encode(bytes)}` // base58btc.encode already prepends 'z' | |
| 50 | } | |
| 51 | ||
| 52 | // did:key string -> { curve, pubkey (compressed bytes) } | |
| 53 | export function decodeDidKey (didKey) { | |
| 54 | if (!didKey.startsWith('did:key:')) throw new Error('not a did:key') | |
| 55 | const mb = didKey.slice('did:key:'.length) | |
| 56 | const bytes = base58btc.decode(mb) // expects leading 'z' | |
| 57 | const { code, length } = varintDecode(bytes) | |
| 58 | const curve = BY_CODE[code] | |
| 59 | if (!curve) throw new Error(`unknown multicodec 0x${code.toString(16)}`) | |
| 60 | return { curve, pubkey: bytes.slice(length) } | |
| 61 | } |
| … | … | @@ -0,0 +1,43 @@ |
| 1 | // did:plc computation — byte-exact. | |
| 2 | // | |
| 3 | // A did:plc is derived from its *genesis* operation: | |
| 4 | // did = "did:plc:" + base32( sha256( dag-cbor(signedGenesisOp) ) ).slice(0, 24) | |
| 5 | // | |
| 6 | // The hash is over the SIGNED op (the `sig` field is included). DAG-CBOR gives a | |
| 7 | // canonical byte layout (sorted map keys, definite lengths), so the DID is a pure | |
| 8 | // function of the op. base32 here is RFC-4648 lowercase, NO padding, and NO | |
| 9 | // multibase prefix (did-method-plc uses raw base32, not multiformats' 'b'-prefixed | |
| 10 | // multibase form) — see the golden-vector test. | |
| 11 | // | |
| 12 | // Reference: https://github.com/did-method-plc/did-method-plc (didForCreateOp) | |
| 13 | ||
| 14 | import * as dagCbor from '@ipld/dag-cbor' | |
| 15 | import { sha256 } from '@noble/hashes/sha2' | |
| 16 | import { base32 } from 'multiformats/bases/base32' | |
| 17 | ||
| 18 | // multiformats' base32.encode returns a multibase string prefixed with 'b'. | |
| 19 | // did:plc uses the raw base32 alphabet output, so strip the leading multibase char. | |
| 20 | function rawBase32 (bytes) { | |
| 21 | const multibase = base32.encode(bytes) // 'b' + <rfc4648-lower-nopad> | |
| 22 | return multibase.slice(1) | |
| 23 | } | |
| 24 | ||
| 25 | // Canonical DAG-CBOR bytes for an operation object. | |
| 26 | export function encodeOp (op) { | |
| 27 | return dagCbor.encode(op) | |
| 28 | } | |
| 29 | ||
| 30 | // Compute the did:plc for a signed genesis operation. | |
| 31 | export function didForCreateOp (signedOp, truncate = 24) { | |
| 32 | const bytes = encodeOp(signedOp) | |
| 33 | const hash = sha256(bytes) | |
| 34 | const b32 = rawBase32(hash) | |
| 35 | return `did:plc:${b32.slice(0, truncate)}` | |
| 36 | } | |
| 37 | ||
| 38 | // The unsigned operation is everything except `sig`. did:plc signs the DAG-CBOR of | |
| 39 | // the unsigned op; the signature is then attached and the DID hashes the signed form. | |
| 40 | export function unsignedOp (op) { | |
| 41 | const { sig, ...rest } = op // eslint-disable-line no-unused-vars | |
| 42 | return rest | |
| 43 | } |
| … | … | @@ -0,0 +1,1146 @@ |
| 1 | { | |
| 2 | "name": "atproto-identity", | |
| 3 | "version": "0.0.1", | |
| 4 | "lockfileVersion": 3, | |
| 5 | "requires": true, | |
| 6 | "packages": { | |
| 7 | "": { | |
| 8 | "name": "atproto-identity", | |
| 9 | "version": "0.0.1", | |
| 10 | "hasInstallScript": true, | |
| 11 | "dependencies": { | |
| 12 | "@ipld/dag-cbor": "^9.2.1", | |
| 13 | "@noble/curves": "^1.6.0", | |
| 14 | "@noble/hashes": "^1.5.0", | |
| 15 | "@scure/bip32": "^1.5.0", | |
| 16 | "@scure/bip39": "^1.4.0", | |
| 17 | "keet-identity-key": "^3.2.0", | |
| 18 | "multiformats": "^13.3.0" | |
| 19 | }, | |
| 20 | "devDependencies": { | |
| 21 | "brittle": "^4.0.2" | |
| 22 | } | |
| 23 | }, | |
| 24 | "node_modules/@ipld/dag-cbor": { | |
| 25 | "version": "9.2.7", | |
| 26 | "resolved": "https://registry.npmjs.org/@ipld/dag-cbor/-/dag-cbor-9.2.7.tgz", | |
| 27 | "integrity": "sha512-ZmfXmElRWATr+hoUTSAOr6HUcjVhOcNHDqgczc76qte2DHHFEK0ZhNzUcdTDQhF/VSIvf2ioaRTRLWwLc83sNw==", | |
| 28 | "license": "Apache-2.0 OR MIT", | |
| 29 | "dependencies": { | |
| 30 | "cborg": "^5.0.1", | |
| 31 | "multiformats": "^13.1.0" | |
| 32 | } | |
| 33 | }, | |
| 34 | "node_modules/@jridgewell/resolve-uri": { | |
| 35 | "version": "3.1.2", | |
| 36 | "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", | |
| 37 | "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", | |
| 38 | "dev": true, | |
| 39 | "license": "MIT", | |
| 40 | "engines": { | |
| 41 | "node": ">=6.0.0" | |
| 42 | } | |
| 43 | }, | |
| 44 | "node_modules/@jridgewell/sourcemap-codec": { | |
| 45 | "version": "1.5.5", | |
| 46 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", | |
| 47 | "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", | |
| 48 | "dev": true, | |
| 49 | "license": "MIT" | |
| 50 | }, | |
| 51 | "node_modules/@jridgewell/trace-mapping": { | |
| 52 | "version": "0.3.31", | |
| 53 | "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", | |
| 54 | "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", | |
| 55 | "dev": true, | |
| 56 | "license": "MIT", | |
| 57 | "dependencies": { | |
| 58 | "@jridgewell/resolve-uri": "^3.1.0", | |
| 59 | "@jridgewell/sourcemap-codec": "^1.4.14" | |
| 60 | } | |
| 61 | }, | |
| 62 | "node_modules/@noble/curves": { | |
| 63 | "version": "1.9.7", | |
| 64 | "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.7.tgz", | |
| 65 | "integrity": "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==", | |
| 66 | "license": "MIT", | |
| 67 | "dependencies": { | |
| 68 | "@noble/hashes": "1.8.0" | |
| 69 | }, | |
| 70 | "engines": { | |
| 71 | "node": "^14.21.3 || >=16" | |
| 72 | }, | |
| 73 | "funding": { | |
| 74 | "url": "https://paulmillr.com/funding/" | |
| 75 | } | |
| 76 | }, | |
| 77 | "node_modules/@noble/hashes": { | |
| 78 | "version": "1.8.0", | |
| 79 | "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", | |
| 80 | "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", | |
| 81 | "license": "MIT", | |
| 82 | "engines": { | |
| 83 | "node": "^14.21.3 || >=16" | |
| 84 | }, | |
| 85 | "funding": { | |
| 86 | "url": "https://paulmillr.com/funding/" | |
| 87 | } | |
| 88 | }, | |
| 89 | "node_modules/@scure/base": { | |
| 90 | "version": "1.2.6", | |
| 91 | "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.6.tgz", | |
| 92 | "integrity": "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==", | |
| 93 | "license": "MIT", | |
| 94 | "funding": { | |
| 95 | "url": "https://paulmillr.com/funding/" | |
| 96 | } | |
| 97 | }, | |
| 98 | "node_modules/@scure/bip32": { | |
| 99 | "version": "1.7.0", | |
| 100 | "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.7.0.tgz", | |
| 101 | "integrity": "sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw==", | |
| 102 | "license": "MIT", | |
| 103 | "dependencies": { | |
| 104 | "@noble/curves": "~1.9.0", | |
| 105 | "@noble/hashes": "~1.8.0", | |
| 106 | "@scure/base": "~1.2.5" | |
| 107 | }, | |
| 108 | "funding": { | |
| 109 | "url": "https://paulmillr.com/funding/" | |
| 110 | } | |
| 111 | }, | |
| 112 | "node_modules/@scure/bip39": { | |
| 113 | "version": "1.6.0", | |
| 114 | "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.6.0.tgz", | |
| 115 | "integrity": "sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==", | |
| 116 | "license": "MIT", | |
| 117 | "dependencies": { | |
| 118 | "@noble/hashes": "~1.8.0", | |
| 119 | "@scure/base": "~1.2.5" | |
| 120 | }, | |
| 121 | "funding": { | |
| 122 | "url": "https://paulmillr.com/funding/" | |
| 123 | } | |
| 124 | }, | |
| 125 | "node_modules/@types/istanbul-lib-coverage": { | |
| 126 | "version": "2.0.6", | |
| 127 | "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", | |
| 128 | "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", | |
| 129 | "dev": true, | |
| 130 | "license": "MIT" | |
| 131 | }, | |
| 132 | "node_modules/b4a": { | |
| 133 | "version": "1.8.1", | |
| 134 | "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.1.tgz", | |
| 135 | "integrity": "sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw==", | |
| 136 | "license": "Apache-2.0", | |
| 137 | "peerDependencies": { | |
| 138 | "react-native-b4a": "*" | |
| 139 | }, | |
| 140 | "peerDependenciesMeta": { | |
| 141 | "react-native-b4a": { | |
| 142 | "optional": true | |
| 143 | } | |
| 144 | } | |
| 145 | }, | |
| 146 | "node_modules/bare-abort": { | |
| 147 | "version": "2.0.13", | |
| 148 | "resolved": "https://registry.npmjs.org/bare-abort/-/bare-abort-2.0.13.tgz", | |
| 149 | "integrity": "sha512-zdc8l88eB11Jsz5rDd6sCAgv2kUFXgdrZWoMlgU6JMkfAi1/uuGFC3IEHswKbIRQTk5H3T5CMuechsXYxiaHlQ==", | |
| 150 | "dev": true, | |
| 151 | "license": "Apache-2.0" | |
| 152 | }, | |
| 153 | "node_modules/bare-addon-resolve": { | |
| 154 | "version": "1.10.0", | |
| 155 | "resolved": "https://registry.npmjs.org/bare-addon-resolve/-/bare-addon-resolve-1.10.0.tgz", | |
| 156 | "integrity": "sha512-sSd0jieRJlDaODOzj0oe0RjFVC1QI0ZIjGIdPkbrTXsdVVtENg14c+lHHAhHwmWCZ2nQlMhy8jA3Y5LYPc/isA==", | |
| 157 | "license": "Apache-2.0", | |
| 158 | "dependencies": { | |
| 159 | "bare-module-resolve": "^1.10.0", | |
| 160 | "bare-semver": "^1.0.0" | |
| 161 | }, | |
| 162 | "peerDependencies": { | |
| 163 | "bare-url": "*" | |
| 164 | }, | |
| 165 | "peerDependenciesMeta": { | |
| 166 | "bare-url": { | |
| 167 | "optional": true | |
| 168 | } | |
| 169 | } | |
| 170 | }, | |
| 171 | "node_modules/bare-ansi-escapes": { | |
| 172 | "version": "2.2.3", | |
| 173 | "resolved": "https://registry.npmjs.org/bare-ansi-escapes/-/bare-ansi-escapes-2.2.3.tgz", | |
| 174 | "integrity": "sha512-02ES4/E2RbrtZSnHJ9LntBhYkLA6lPpSEeP8iqS3MccBIVhVBlEmruF1I7HZqx5Q8aiTeYfQVeqmrU9YO2yYoQ==", | |
| 175 | "license": "Apache-2.0", | |
| 176 | "dependencies": { | |
| 177 | "bare-stream": "^2.6.5" | |
| 178 | }, | |
| 179 | "peerDependencies": { | |
| 180 | "bare-buffer": "*" | |
| 181 | }, | |
| 182 | "peerDependenciesMeta": { | |
| 183 | "bare-buffer": { | |
| 184 | "optional": true | |
| 185 | } | |
| 186 | } | |
| 187 | }, | |
| 188 | "node_modules/bare-assert": { | |
| 189 | "version": "1.2.0", | |
| 190 | "resolved": "https://registry.npmjs.org/bare-assert/-/bare-assert-1.2.0.tgz", | |
| 191 | "integrity": "sha512-c6uvgvTJBspTDxtVnPgrBKmLgcpW3Fp72NVKDLg6oT4QjQbhGtvrkHMhGYMK1sh4vjBHOBmuUalyt9hSzV37fQ==", | |
| 192 | "license": "Apache-2.0", | |
| 193 | "dependencies": { | |
| 194 | "bare-inspect": "^3.1.2" | |
| 195 | } | |
| 196 | }, | |
| 197 | "node_modules/bare-buffer": { | |
| 198 | "version": "3.6.1", | |
| 199 | "resolved": "https://registry.npmjs.org/bare-buffer/-/bare-buffer-3.6.1.tgz", | |
| 200 | "integrity": "sha512-8mzp60t6jdSD3cEGmyxAV1YrAN2+mnxiUvBPQHJJ4WNk0hSVLJGSIEolRj09ZP8yt/+oSj2N2f5kDVW8297n4A==", | |
| 201 | "devOptional": true, | |
| 202 | "license": "Apache-2.0", | |
| 203 | "engines": { | |
| 204 | "bare": ">=1.20.0" | |
| 205 | } | |
| 206 | }, | |
| 207 | "node_modules/bare-bundle": { | |
| 208 | "version": "1.10.0", | |
| 209 | "resolved": "https://registry.npmjs.org/bare-bundle/-/bare-bundle-1.10.0.tgz", | |
| 210 | "integrity": "sha512-4LVlnJAHr00Hh6Vu6ZUJS38rcEtJT3b3vChXSsBsJ2mk1TN0lQ+gzd+Dw5L0aV7uqDZv84smuwW+O02X7PfDlw==", | |
| 211 | "dev": true, | |
| 212 | "license": "Apache-2.0", | |
| 213 | "peerDependencies": { | |
| 214 | "bare-buffer": "*", | |
| 215 | "bare-url": "*" | |
| 216 | }, | |
| 217 | "peerDependenciesMeta": { | |
| 218 | "bare-buffer": { | |
| 219 | "optional": true | |
| 220 | }, | |
| 221 | "bare-url": { | |
| 222 | "optional": true | |
| 223 | } | |
| 224 | } | |
| 225 | }, | |
| 226 | "node_modules/bare-channel": { | |
| 227 | "version": "5.2.4", | |
| 228 | "resolved": "https://registry.npmjs.org/bare-channel/-/bare-channel-5.2.4.tgz", | |
| 229 | "integrity": "sha512-enkaOtvXUiZsLBSbataxV/QBjehCOK+SUTd2JQWUYW2iIOufpPu9jyZ9bqJqkEquktrK/rpEMVEmKsPzoci/sA==", | |
| 230 | "dev": true, | |
| 231 | "license": "Apache-2.0", | |
| 232 | "dependencies": { | |
| 233 | "bare-events": "^2.0.0", | |
| 234 | "bare-stream": "^2.7.0", | |
| 235 | "bare-structured-clone": "^1.4.0" | |
| 236 | }, | |
| 237 | "engines": { | |
| 238 | "bare": ">=1.7.0" | |
| 239 | } | |
| 240 | }, | |
| 241 | "node_modules/bare-cov": { | |
| 242 | "version": "1.2.2", | |
| 243 | "resolved": "https://registry.npmjs.org/bare-cov/-/bare-cov-1.2.2.tgz", | |
| 244 | "integrity": "sha512-d/b4HdL5ohZDwBvDegJeSkmo1X2MGlm22jXzuY2D7wl2W1+7nk/b7+HMogwgUa+zi8rVVRJQKHCHPHYBJpvlvw==", | |
| 245 | "dev": true, | |
| 246 | "license": "Apache-2.0", | |
| 247 | "dependencies": { | |
| 248 | "bare-fs": "^4.1.2", | |
| 249 | "bare-inspector": "^6.0.1", | |
| 250 | "bare-path": "^3.0.0", | |
| 251 | "bare-process": "^4.2.1", | |
| 252 | "bare-url": "^2.1.5", | |
| 253 | "bare-v8-to-istanbul": "^1.0.2", | |
| 254 | "picomatch": "^4.0.2", | |
| 255 | "which-runtime": "^1.2.1" | |
| 256 | } | |
| 257 | }, | |
| 258 | "node_modules/bare-crypto": { | |
| 259 | "version": "1.15.3", | |
| 260 | "resolved": "https://registry.npmjs.org/bare-crypto/-/bare-crypto-1.15.3.tgz", | |
| 261 | "integrity": "sha512-macV9lbyJTsLPRXJkBtz8ivTGEo3LCyJInLT9IB/PWJ7pRXwvHs/FP4bx/fWw+HZkiepIYCAV2cuU5CR92XWCw==", | |
| 262 | "dev": true, | |
| 263 | "license": "Apache-2.0", | |
| 264 | "dependencies": { | |
| 265 | "bare-assert": "^1.2.0", | |
| 266 | "bare-stream": "^2.6.3" | |
| 267 | }, | |
| 268 | "peerDependencies": { | |
| 269 | "bare-buffer": "*" | |
| 270 | }, | |
| 271 | "peerDependenciesMeta": { | |
| 272 | "bare-buffer": { | |
| 273 | "optional": true | |
| 274 | } | |
| 275 | } | |
| 276 | }, | |
| 277 | "node_modules/bare-debug-log": { | |
| 278 | "version": "2.0.0", | |
| 279 | "resolved": "https://registry.npmjs.org/bare-debug-log/-/bare-debug-log-2.0.0.tgz", | |
| 280 | "integrity": "sha512-Vi42PkMQsNV9PUpx2Gl1hikshx5O9FzMJ6o9Nnopseg7qLBBK7Nl31d0RHcfwLEAfmcPApytpc0ZFfq68u22FQ==", | |
| 281 | "dev": true, | |
| 282 | "license": "Apache-2.0", | |
| 283 | "dependencies": { | |
| 284 | "bare-os": "^3.0.1" | |
| 285 | } | |
| 286 | }, | |
| 287 | "node_modules/bare-dns": { | |
| 288 | "version": "2.1.4", | |
| 289 | "resolved": "https://registry.npmjs.org/bare-dns/-/bare-dns-2.1.4.tgz", | |
| 290 | "integrity": "sha512-abwjHmpWqSRNB7V5615QxPH92L71AVzFm/kKTs8VYiNTAi2xVdonpv0BjJ0hwXLwomoW+xsSOPjW6PZPO14asg==", | |
| 291 | "dev": true, | |
| 292 | "license": "Apache-2.0", | |
| 293 | "engines": { | |
| 294 | "bare": ">=1.7.0" | |
| 295 | } | |
| 296 | }, | |
| 297 | "node_modules/bare-encoding": { | |
| 298 | "version": "1.0.3", | |
| 299 | "resolved": "https://registry.npmjs.org/bare-encoding/-/bare-encoding-1.0.3.tgz", | |
| 300 | "integrity": "sha512-Kqf+t/azs13lUeyK4Tb7ha4wdLRXKWCXQ8w1rVmt7KtoPCPdHD/Xwt7LBIsCSwwGglrcmblo5VOLa5avkJqULA==", | |
| 301 | "dev": true, | |
| 302 | "license": "Apache-2.0", | |
| 303 | "peerDependencies": { | |
| 304 | "bare-buffer": "*" | |
| 305 | }, | |
| 306 | "peerDependenciesMeta": { | |
| 307 | "bare-buffer": { | |
| 308 | "optional": true | |
| 309 | } | |
| 310 | } | |
| 311 | }, | |
| 312 | "node_modules/bare-env": { | |
| 313 | "version": "3.0.0", | |
| 314 | "resolved": "https://registry.npmjs.org/bare-env/-/bare-env-3.0.0.tgz", | |
| 315 | "integrity": "sha512-0u964P5ZLAxTi+lW4Kjp7YRJQ5gZr9ycYOtjLxsSrupgMz3sn5Z9n4SH/JIifHwvadsf1brA2JAjP+9IOWwTiw==", | |
| 316 | "dev": true, | |
| 317 | "license": "Apache-2.0", | |
| 318 | "dependencies": { | |
| 319 | "bare-os": "^3.0.1" | |
| 320 | } | |
| 321 | }, | |
| 322 | "node_modules/bare-events": { | |
| 323 | "version": "2.9.1", | |
| 324 | "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.9.1.tgz", | |
| 325 | "integrity": "sha512-Z0oHEHAFDZkffN8Qc39zNZjQlMDkPJRyyyZieU1VH7u8c5S+qHZ2S8ixdKIAxEjfHO7FJxXmJWgteOghVanIsg==", | |
| 326 | "license": "Apache-2.0", | |
| 327 | "peerDependencies": { | |
| 328 | "bare-abort-controller": "*" | |
| 329 | }, | |
| 330 | "peerDependenciesMeta": { | |
| 331 | "bare-abort-controller": { | |
| 332 | "optional": true | |
| 333 | } | |
| 334 | } | |
| 335 | }, | |
| 336 | "node_modules/bare-format": { | |
| 337 | "version": "1.0.2", | |
| 338 | "resolved": "https://registry.npmjs.org/bare-format/-/bare-format-1.0.2.tgz", | |
| 339 | "integrity": "sha512-GswdhnOnP9QtwRbrf4wLApw5widkaLMsLe2XOs35fQD2YfEN1ApoGka+cZ7PfvzxMgfYXmMhj/2OGlVn5/Dxgw==", | |
| 340 | "dev": true, | |
| 341 | "license": "Apache-2.0", | |
| 342 | "dependencies": { | |
| 343 | "bare-inspect": "^3.0.0" | |
| 344 | } | |
| 345 | }, | |
| 346 | "node_modules/bare-fs": { | |
| 347 | "version": "4.7.3", | |
| 348 | "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.7.3.tgz", | |
| 349 | "integrity": "sha512-xRgplks8SvcKkdlv2M6Z2LZmRsmqd+x0nXXGXeMEjwdibj1HSDrlnqBRLeYdMvsgCox7Bq0e+DHwfczOfsn6IA==", | |
| 350 | "dev": true, | |
| 351 | "license": "Apache-2.0", | |
| 352 | "dependencies": { | |
| 353 | "bare-events": "^2.5.4", | |
| 354 | "bare-path": "^3.0.0", | |
| 355 | "bare-stream": "^2.6.4", | |
| 356 | "bare-url": "^2.2.2", | |
| 357 | "fast-fifo": "^1.3.2" | |
| 358 | }, | |
| 359 | "engines": { | |
| 360 | "bare": ">=1.16.0" | |
| 361 | }, | |
| 362 | "peerDependencies": { | |
| 363 | "bare-buffer": "*" | |
| 364 | }, | |
| 365 | "peerDependenciesMeta": { | |
| 366 | "bare-buffer": { | |
| 367 | "optional": true | |
| 368 | } | |
| 369 | } | |
| 370 | }, | |
| 371 | "node_modules/bare-hrtime": { | |
| 372 | "version": "2.1.1", | |
| 373 | "resolved": "https://registry.npmjs.org/bare-hrtime/-/bare-hrtime-2.1.1.tgz", | |
| 374 | "integrity": "sha512-VMb3tHo05gsnbu3OXTmkDiwTjMlOsbQmKoysKqKEyR09m77TuDrYFbj3Q5GGk10dAKsUHrnXmwCaeJqzVpB5ZA==", | |
| 375 | "dev": true, | |
| 376 | "license": "Apache-2.0" | |
| 377 | }, | |
| 378 | "node_modules/bare-http-parser": { | |
| 379 | "version": "1.1.5", | |
| 380 | "resolved": "https://registry.npmjs.org/bare-http-parser/-/bare-http-parser-1.1.5.tgz", | |
| 381 | "integrity": "sha512-sPaDetLbWRmth04JmuTCvw/hoNdWJvYUJN8n1tYdGW3HM0mMnCvK2f0oIxE6HK7iOq+WlsRzEMg1LT/b0wGbLQ==", | |
| 382 | "dev": true, | |
| 383 | "license": "Apache-2.0" | |
| 384 | }, | |
| 385 | "node_modules/bare-http1": { | |
| 386 | "version": "4.5.7", | |
| 387 | "resolved": "https://registry.npmjs.org/bare-http1/-/bare-http1-4.5.7.tgz", | |
| 388 | "integrity": "sha512-PRuzs9ywt4vUvrC3mnHhQyaQfLYzdFy8XKOH0oKeKmYfyYYUqXBkdbJE2csESLBxJEbKDBjxPGLU+Qa0l1ds4A==", | |
| 389 | "dev": true, | |
| 390 | "license": "Apache-2.0", | |
| 391 | "dependencies": { | |
| 392 | "bare-events": "^2.6.0", | |
| 393 | "bare-http-parser": "^1.1.1", | |
| 394 | "bare-stream": "^2.10.0", | |
| 395 | "bare-tcp": "^2.2.0" | |
| 396 | }, | |
| 397 | "peerDependencies": { | |
| 398 | "bare-buffer": "*", | |
| 399 | "bare-url": "*" | |
| 400 | }, | |
| 401 | "peerDependenciesMeta": { | |
| 402 | "bare-buffer": { | |
| 403 | "optional": true | |
| 404 | }, | |
| 405 | "bare-url": { | |
| 406 | "optional": true | |
| 407 | } | |
| 408 | } | |
| 409 | }, | |
| 410 | "node_modules/bare-https": { | |
| 411 | "version": "3.0.0", | |
| 412 | "resolved": "https://registry.npmjs.org/bare-https/-/bare-https-3.0.0.tgz", | |
| 413 | "integrity": "sha512-W1GRSCzn+xXKf5bMcPs/hg6Ga1bxPqb7owGfS+tvlBQfPe5Q2STcanRuKZrgU60v5uKrhXH5cgWwM+DLqvXZgQ==", | |
| 414 | "dev": true, | |
| 415 | "license": "Apache-2.0", | |
| 416 | "dependencies": { | |
| 417 | "bare-http1": "^4.4.0", | |
| 418 | "bare-tcp": "^2.2.0", | |
| 419 | "bare-tls": "^3.0.0" | |
| 420 | } | |
| 421 | }, | |
| 422 | "node_modules/bare-inspect": { | |
| 423 | "version": "3.1.4", | |
| 424 | "resolved": "https://registry.npmjs.org/bare-inspect/-/bare-inspect-3.1.4.tgz", | |
| 425 | "integrity": "sha512-jfW5KRA84o3REpI6Vr4nbvMn+hqVAw8GU1mMdRwUsY5yJovQamxYeKGVKGqdzs+8ZbG4jRzGUXP/3Ji/DnqfPg==", | |
| 426 | "license": "Apache-2.0", | |
| 427 | "dependencies": { | |
| 428 | "bare-ansi-escapes": "^2.1.0", | |
| 429 | "bare-type": "^1.0.0" | |
| 430 | }, | |
| 431 | "engines": { | |
| 432 | "bare": ">=1.18.0" | |
| 433 | } | |
| 434 | }, | |
| 435 | "node_modules/bare-inspector": { | |
| 436 | "version": "6.1.0", | |
| 437 | "resolved": "https://registry.npmjs.org/bare-inspector/-/bare-inspector-6.1.0.tgz", | |
| 438 | "integrity": "sha512-PRxmZ4gF+K3TLzGubgRFvzdECybTCSKackgNsAdd4e7SdvICxMkGj+p5iX7bSKYSmgDnxgCR+2Z6UXmWykKhvw==", | |
| 439 | "dev": true, | |
| 440 | "license": "Apache-2.0", | |
| 441 | "dependencies": { | |
| 442 | "bare-events": "^2.1.0", | |
| 443 | "bare-http1": "^4.0.0", | |
| 444 | "bare-stream": "^2.0.0", | |
| 445 | "bare-url": "^2.0.0", | |
| 446 | "bare-ws": "^3.0.0" | |
| 447 | }, | |
| 448 | "engines": { | |
| 449 | "bare": ">=1.29.0" | |
| 450 | }, | |
| 451 | "peerDependencies": { | |
| 452 | "bare-tcp": "*" | |
| 453 | }, | |
| 454 | "peerDependenciesMeta": { | |
| 455 | "bare-tcp": { | |
| 456 | "optional": true | |
| 457 | } | |
| 458 | } | |
| 459 | }, | |
| 460 | "node_modules/bare-module": { | |
| 461 | "version": "6.4.0", | |
| 462 | "resolved": "https://registry.npmjs.org/bare-module/-/bare-module-6.4.0.tgz", | |
| 463 | "integrity": "sha512-Yn4V5g5EqGQL4LYUOmt7fjKzj2JPWyJOqE3lPoeZwfUH5rk4CKUfZj6JhDwbzhBYCqqmUgjgQ5aY8cihAPILLA==", | |
| 464 | "dev": true, | |
| 465 | "license": "Apache-2.0", | |
| 466 | "dependencies": { | |
| 467 | "bare-bundle": "^1.3.0", | |
| 468 | "bare-module-lexer": "^1.0.0", | |
| 469 | "bare-module-resolve": "^1.8.0", | |
| 470 | "bare-path": "^3.0.0", | |
| 471 | "bare-type-stripper": "^0.1.2", | |
| 472 | "bare-url": "^2.0.1" | |
| 473 | }, | |
| 474 | "engines": { | |
| 475 | "bare": ">=1.29.4" | |
| 476 | }, | |
| 477 | "peerDependencies": { | |
| 478 | "bare-buffer": "*" | |
| 479 | }, | |
| 480 | "peerDependenciesMeta": { | |
| 481 | "bare-buffer": { | |
| 482 | "optional": true | |
| 483 | } | |
| 484 | } | |
| 485 | }, | |
| 486 | "node_modules/bare-module-lexer": { | |
| 487 | "version": "1.6.0", | |
| 488 | "resolved": "https://registry.npmjs.org/bare-module-lexer/-/bare-module-lexer-1.6.0.tgz", | |
| 489 | "integrity": "sha512-uVDdbh6EnypLg5K4Gz5GkFloXiJu0QN5S1JNiKdKbiLoyTjUqIXKnHR/8fb2QejDT/+5BNlnqlbkAAg1iEZSFA==", | |
| 490 | "dev": true, | |
| 491 | "license": "Apache-2.0", | |
| 492 | "dependencies": { | |
| 493 | "require-addon": "^1.0.2" | |
| 494 | }, | |
| 495 | "peerDependencies": { | |
| 496 | "bare-buffer": "*" | |
| 497 | }, | |
| 498 | "peerDependenciesMeta": { | |
| 499 | "bare-buffer": { | |
| 500 | "optional": true | |
| 501 | } | |
| 502 | } | |
| 503 | }, | |
| 504 | "node_modules/bare-module-resolve": { | |
| 505 | "version": "1.12.2", | |
| 506 | "resolved": "https://registry.npmjs.org/bare-module-resolve/-/bare-module-resolve-1.12.2.tgz", | |
| 507 | "integrity": "sha512-j+hiD5k99qec4KjJvYsI67q5AOBifmy9JG3oeMVxTmvrhn2sIdp8StrUvZu4YNgwTpO+NhniQG16N1ETDe1k5w==", | |
| 508 | "license": "Apache-2.0", | |
| 509 | "dependencies": { | |
| 510 | "bare-semver": "^1.0.0" | |
| 511 | }, | |
| 512 | "peerDependencies": { | |
| 513 | "bare-url": "*" | |
| 514 | }, | |
| 515 | "peerDependenciesMeta": { | |
| 516 | "bare-url": { | |
| 517 | "optional": true | |
| 518 | } | |
| 519 | } | |
| 520 | }, | |
| 521 | "node_modules/bare-net": { | |
| 522 | "version": "2.3.2", | |
| 523 | "resolved": "https://registry.npmjs.org/bare-net/-/bare-net-2.3.2.tgz", | |
| 524 | "integrity": "sha512-I+yz+pqbYsBkxDsnu5vkKvy7RSNY9CcAvu2jZT6PsmdXJQG1i3dmD5V7xc3334OVp2absgtUEYLmmuNFlphBzg==", | |
| 525 | "dev": true, | |
| 526 | "license": "Apache-2.0", | |
| 527 | "dependencies": { | |
| 528 | "bare-events": "^2.2.2", | |
| 529 | "bare-pipe": "^4.0.0", | |
| 530 | "bare-stream": "^2.0.0", | |
| 531 | "bare-tcp": "^2.0.0" | |
| 532 | } | |
| 533 | }, | |
| 534 | "node_modules/bare-os": { | |
| 535 | "version": "3.9.3", | |
| 536 | "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.9.3.tgz", | |
| 537 | "integrity": "sha512-fF4Q7QsyKVF5Rj0qvI8BgUNjqzC2JvQlpTaPLjVJVxYVUX5Zr9un+y3w1HmA4nNKdFmRBT8z/WmrjvXzXVerKQ==", | |
| 538 | "devOptional": true, | |
| 539 | "license": "Apache-2.0", | |
| 540 | "engines": { | |
| 541 | "bare": ">=1.14.0" | |
| 542 | } | |
| 543 | }, | |
| 544 | "node_modules/bare-path": { | |
| 545 | "version": "3.0.1", | |
| 546 | "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.1.tgz", | |
| 547 | "integrity": "sha512-ghj2DSK/2e99a1anTVPCV4m4YIYtrbXhfM7V3D7XZLOTsybnYyaJloymGqssQc8l/or0UoDyRtNQkmkEF/ysgQ==", | |
| 548 | "devOptional": true, | |
| 549 | "license": "Apache-2.0", | |
| 550 | "dependencies": { | |
| 551 | "bare-os": "^3.0.1" | |
| 552 | } | |
| 553 | }, | |
| 554 | "node_modules/bare-pipe": { | |
| 555 | "version": "4.2.2", | |
| 556 | "resolved": "https://registry.npmjs.org/bare-pipe/-/bare-pipe-4.2.2.tgz", | |
| 557 | "integrity": "sha512-Wkf6//CmKS8inHrkgi3X/xNMeaGfs2I0ayLM0B8cbPB4DxYHcoes6Ng+muCBfXNIJ9qmEjwDDJjAUz7nRxxjgQ==", | |
| 558 | "dev": true, | |
| 559 | "license": "Apache-2.0", | |
| 560 | "peer": true, | |
| 561 | "dependencies": { | |
| 562 | "bare-events": "^2.0.0", | |
| 563 | "bare-stream": "^2.0.0" | |
| 564 | }, | |
| 565 | "engines": { | |
| 566 | "bare": ">=1.16.0" | |
| 567 | } | |
| 568 | }, | |
| 569 | "node_modules/bare-posix": { | |
| 570 | "version": "1.0.1", | |
| 571 | "resolved": "https://registry.npmjs.org/bare-posix/-/bare-posix-1.0.1.tgz", | |
| 572 | "integrity": "sha512-b4quA8dyRVvX0aFIAIiev5zFATHwIUGr42Gmt+CqiBNg/bdsNkrj4bbkeotMOMiljJS4DCME5kywJO/TFwKo1A==", | |
| 573 | "dev": true, | |
| 574 | "license": "Apache-2.0" | |
| 575 | }, | |
| 576 | "node_modules/bare-process": { | |
| 577 | "version": "4.5.0", | |
| 578 | "resolved": "https://registry.npmjs.org/bare-process/-/bare-process-4.5.0.tgz", | |
| 579 | "integrity": "sha512-NTtYDiwleJjWWNg4+yzS6B/RovL/LLPHVvoOM+18W+dYAXCmky0zFfN66hqqBbrlGAlb52/QgKrdzPR3x1QyHA==", | |
| 580 | "dev": true, | |
| 581 | "license": "Apache-2.0", | |
| 582 | "dependencies": { | |
| 583 | "bare-abort": "^2.0.13", | |
| 584 | "bare-env": "^3.0.0", | |
| 585 | "bare-events": "^2.3.1", | |
| 586 | "bare-hrtime": "^2.0.0", | |
| 587 | "bare-os": "^3.7.1", | |
| 588 | "bare-posix": "^1.0.1", | |
| 589 | "bare-signals": "^4.0.0", | |
| 590 | "bare-stdio": "^1.0.1" | |
| 591 | } | |
| 592 | }, | |
| 593 | "node_modules/bare-semver": { | |
| 594 | "version": "1.1.0", | |
| 595 | "resolved": "https://registry.npmjs.org/bare-semver/-/bare-semver-1.1.0.tgz", | |
| 596 | "integrity": "sha512-1Hw5qJ7hXdVt3uPUqjeFTuxyvBUJauvz5A1I2jk8gzjZMHp04n//6nV9MDbG9CMw78JHY2lGV0w6s//LrASm2w==", | |
| 597 | "license": "Apache-2.0" | |
| 598 | }, | |
| 599 | "node_modules/bare-signals": { | |
| 600 | "version": "4.2.0", | |
| 601 | "resolved": "https://registry.npmjs.org/bare-signals/-/bare-signals-4.2.0.tgz", | |
| 602 | "integrity": "sha512-fNHMOdQIlYuTvMB3Oh9Apk99hLKn351+Ir8vz+khiPTcOqIyGG4uWWjdLTzxWdYGsA0eT+We3y0K74hjj2nq7A==", | |
| 603 | "dev": true, | |
| 604 | "license": "Apache-2.0", | |
| 605 | "dependencies": { | |
| 606 | "bare-events": "^2.5.3", | |
| 607 | "bare-os": "^3.3.1" | |
| 608 | }, | |
| 609 | "engines": { | |
| 610 | "bare": ">=1.7.0" | |
| 611 | } | |
| 612 | }, | |
| 613 | "node_modules/bare-stdio": { | |
| 614 | "version": "1.0.2", | |
| 615 | "resolved": "https://registry.npmjs.org/bare-stdio/-/bare-stdio-1.0.2.tgz", | |
| 616 | "integrity": "sha512-3WJDqtvVGP4f+j68kyEC05umOYNwKJ1xG+YAXL8yZ605WgNqiRhVaFq+mVIhBt2eKNp7pa5vCQdhOt1pNh79SA==", | |
| 617 | "dev": true, | |
| 618 | "license": "Apache-2.0", | |
| 619 | "dependencies": { | |
| 620 | "bare-fs": "^4.5.2", | |
| 621 | "bare-pipe": "^4.1.5", | |
| 622 | "bare-tty": "^5.0.3" | |
| 623 | } | |
| 624 | }, | |
| 625 | "node_modules/bare-stream": { | |
| 626 | "version": "2.13.3", | |
| 627 | "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.13.3.tgz", | |
| 628 | "integrity": "sha512-Kc+brLqvEqGkjyfiwJmImAOqLZL7OsoLKuavx+hJjgVV3nLTOjloJyPMFxjUPerGGHrNH0fLU06jjykMLWrERQ==", | |
| 629 | "license": "Apache-2.0", | |
| 630 | "dependencies": { | |
| 631 | "b4a": "^1.8.1", | |
| 632 | "streamx": "^2.25.0", | |
| 633 | "teex": "^1.0.1" | |
| 634 | }, | |
| 635 | "peerDependencies": { | |
| 636 | "bare-abort-controller": "*", | |
| 637 | "bare-buffer": "*", | |
| 638 | "bare-events": "*" | |
| 639 | }, | |
| 640 | "peerDependenciesMeta": { | |
| 641 | "bare-abort-controller": { | |
| 642 | "optional": true | |
| 643 | }, | |
| 644 | "bare-buffer": { | |
| 645 | "optional": true | |
| 646 | }, | |
| 647 | "bare-events": { | |
| 648 | "optional": true | |
| 649 | } | |
| 650 | } | |
| 651 | }, | |
| 652 | "node_modules/bare-structured-clone": { | |
| 653 | "version": "1.6.0", | |
| 654 | "resolved": "https://registry.npmjs.org/bare-structured-clone/-/bare-structured-clone-1.6.0.tgz", | |
| 655 | "integrity": "sha512-AZjEERyqF7kAuudlmgrweT2KwwWM0LsGG4Gx/bWyFwJrKU7E3wNG8c09z2DIrdw93p3vDtfOX8fyHOcXfy5m+g==", | |
| 656 | "dev": true, | |
| 657 | "license": "Apache-2.0", | |
| 658 | "dependencies": { | |
| 659 | "bare-buffer": "^3.6.0", | |
| 660 | "bare-type": "^1.1.0", | |
| 661 | "bare-url": "^2.4.0", | |
| 662 | "compact-encoding": "^3.0.1" | |
| 663 | }, | |
| 664 | "engines": { | |
| 665 | "bare": ">=1.2.0" | |
| 666 | } | |
| 667 | }, | |
| 668 | "node_modules/bare-stylize": { | |
| 669 | "version": "0.0.1", | |
| 670 | "resolved": "https://registry.npmjs.org/bare-stylize/-/bare-stylize-0.0.1.tgz", | |
| 671 | "integrity": "sha512-l3MjmIl476bWijYWf3RbE+osl4iuXSOMudzp0vAqzIK7gPgn/+G3oAxp8Oin9CFF911KBP0LO9kts8Ci8mGZaQ==", | |
| 672 | "dev": true, | |
| 673 | "license": "Apache-2.0", | |
| 674 | "dependencies": { | |
| 675 | "bare-ansi-escapes": "^2.2.3", | |
| 676 | "bare-process": "^4.2.1" | |
| 677 | } | |
| 678 | }, | |
| 679 | "node_modules/bare-subprocess": { | |
| 680 | "version": "5.2.3", | |
| 681 | "resolved": "https://registry.npmjs.org/bare-subprocess/-/bare-subprocess-5.2.3.tgz", | |
| 682 | "integrity": "sha512-07wwswlV7M3sC9IykbZRZ/jHAkrXFWVLqdBWGv1y0ojCimtRD9hGwxdHmR5FUFmDUZLNsBmTYJNQqgio5+A85Q==", | |
| 683 | "dev": true, | |
| 684 | "license": "Apache-2.0", | |
| 685 | "dependencies": { | |
| 686 | "bare-env": "^3.0.0", | |
| 687 | "bare-events": "^2.5.4", | |
| 688 | "bare-os": "^3.0.1", | |
| 689 | "bare-pipe": "^4.0.0", | |
| 690 | "bare-url": "^2.2.2" | |
| 691 | }, | |
| 692 | "engines": { | |
| 693 | "bare": ">=1.7.0" | |
| 694 | }, | |
| 695 | "peerDependencies": { | |
| 696 | "bare-buffer": "*" | |
| 697 | }, | |
| 698 | "peerDependenciesMeta": { | |
| 699 | "bare-buffer": { | |
| 700 | "optional": true | |
| 701 | } | |
| 702 | } | |
| 703 | }, | |
| 704 | "node_modules/bare-tcp": { | |
| 705 | "version": "2.5.1", | |
| 706 | "resolved": "https://registry.npmjs.org/bare-tcp/-/bare-tcp-2.5.1.tgz", | |
| 707 | "integrity": "sha512-N32OosAegmGzksr/huxIYLU/uaUARte6utO+vAu/EPQztMM7iAC55VeQSfzr2/XBJoz2OIETMvmU7/sdfiOGjw==", | |
| 708 | "dev": true, | |
| 709 | "license": "Apache-2.0", | |
| 710 | "dependencies": { | |
| 711 | "bare-dns": "^2.0.4", | |
| 712 | "bare-events": "^2.5.4", | |
| 713 | "bare-stream": "^2.6.4" | |
| 714 | }, | |
| 715 | "engines": { | |
| 716 | "bare": ">=1.16.0" | |
| 717 | }, | |
| 718 | "peerDependencies": { | |
| 719 | "bare-pipe": "*" | |
| 720 | }, | |
| 721 | "peerDependenciesMeta": { | |
| 722 | "bare-pipe": { | |
| 723 | "optional": true | |
| 724 | } | |
| 725 | } | |
| 726 | }, | |
| 727 | "node_modules/bare-tls": { | |
| 728 | "version": "3.1.7", | |
| 729 | "resolved": "https://registry.npmjs.org/bare-tls/-/bare-tls-3.1.7.tgz", | |
| 730 | "integrity": "sha512-AMw8tJlb3LhzAmhgXRcjDrTlNxR3gXXyj6G8eU9iwvCFtiUBD8MxAW7bwunA1gXDukgo40A970jX0APc2jMU7A==", | |
| 731 | "dev": true, | |
| 732 | "license": "Apache-2.0", | |
| 733 | "dependencies": { | |
| 734 | "bare-net": "^2.0.1", | |
| 735 | "bare-stream": "^2.6.4" | |
| 736 | }, | |
| 737 | "engines": { | |
| 738 | "bare": ">=1.7.0" | |
| 739 | } | |
| 740 | }, | |
| 741 | "node_modules/bare-tty": { | |
| 742 | "version": "5.1.1", | |
| 743 | "resolved": "https://registry.npmjs.org/bare-tty/-/bare-tty-5.1.1.tgz", | |
| 744 | "integrity": "sha512-cXCMAlcLCFTCWwCtXBIhDuXa4hutTEbrHHIK5pvSSb4BLaSGJ9tvxVN3mvGOvXXqONS7l04mTiP+eIUlZKI92g==", | |
| 745 | "dev": true, | |
| 746 | "license": "Apache-2.0", | |
| 747 | "dependencies": { | |
| 748 | "bare-events": "^2.2.0", | |
| 749 | "bare-signals": "^4.0.0", | |
| 750 | "bare-stream": "^2.0.0" | |
| 751 | }, | |
| 752 | "engines": { | |
| 753 | "bare": ">=1.16.0" | |
| 754 | } | |
| 755 | }, | |
| 756 | "node_modules/bare-type": { | |
| 757 | "version": "1.1.0", | |
| 758 | "resolved": "https://registry.npmjs.org/bare-type/-/bare-type-1.1.0.tgz", | |
| 759 | "integrity": "sha512-LdtnnEEYldOc87Dr4GpsKnStStZk3zfgoEMXy8yvEZkXrcCv9RtYDrUYWFsBQHtaB0s1EUWmcvS6XmEZYIj3Bw==", | |
| 760 | "license": "Apache-2.0", | |
| 761 | "engines": { | |
| 762 | "bare": ">=1.2.0" | |
| 763 | } | |
| 764 | }, | |
| 765 | "node_modules/bare-type-stripper": { | |
| 766 | "version": "0.1.4", | |
| 767 | "resolved": "https://registry.npmjs.org/bare-type-stripper/-/bare-type-stripper-0.1.4.tgz", | |
| 768 | "integrity": "sha512-FdZhp9XEnQpj8AWFmIft/sVUyKS9XSmB6PhcxBHhuEDxxZM5Kkt8+kFS7eEpLXR7TkaRkNpSENoGH/8lpAmtkA==", | |
| 769 | "dev": true, | |
| 770 | "license": "Apache-2.0", | |
| 771 | "dependencies": { | |
| 772 | "require-addon": "^1.0.2" | |
| 773 | }, | |
| 774 | "peerDependencies": { | |
| 775 | "bare-buffer": "*" | |
| 776 | }, | |
| 777 | "peerDependenciesMeta": { | |
| 778 | "bare-buffer": { | |
| 779 | "optional": true | |
| 780 | } | |
| 781 | } | |
| 782 | }, | |
| 783 | "node_modules/bare-url": { | |
| 784 | "version": "2.4.5", | |
| 785 | "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.4.5.tgz", | |
| 786 | "integrity": "sha512-K+y9xF1tN+CdPu4qWwr0QiK1Al07eFPGYK5M2pDXcmHdMdgC/tT/bpmMe1hrmRHaidKLkXrC+cRNYf3XVDUhSQ==", | |
| 787 | "devOptional": true, | |
| 788 | "license": "Apache-2.0", | |
| 789 | "dependencies": { | |
| 790 | "bare-path": "^3.0.0" | |
| 791 | } | |
| 792 | }, | |
| 793 | "node_modules/bare-utils": { | |
| 794 | "version": "1.6.0", | |
| 795 | "resolved": "https://registry.npmjs.org/bare-utils/-/bare-utils-1.6.0.tgz", | |
| 796 | "integrity": "sha512-WhQEIkkAxkSnW7u1QgrI0AfNm5JpMruETXeYsb5qnkBJ0TTfNKygZmsh6rkoHBANaV+C/7Jed7bJP9OmEHG7rQ==", | |
| 797 | "dev": true, | |
| 798 | "license": "Apache-2.0", | |
| 799 | "dependencies": { | |
| 800 | "bare-debug-log": "^2.0.0", | |
| 801 | "bare-encoding": "^1.0.0", | |
| 802 | "bare-format": "^1.0.0", | |
| 803 | "bare-inspect": "^3.0.0", | |
| 804 | "bare-stylize": "^0.0.1", | |
| 805 | "bare-type": "^1.0.6" | |
| 806 | } | |
| 807 | }, | |
| 808 | "node_modules/bare-v8-to-istanbul": { | |
| 809 | "version": "1.0.2", | |
| 810 | "resolved": "https://registry.npmjs.org/bare-v8-to-istanbul/-/bare-v8-to-istanbul-1.0.2.tgz", | |
| 811 | "integrity": "sha512-CM0wPgyQtXTQ7h4q8NjbTZ/FH5he0Jkiwgpq0lFZZA5tkVZzmQ/9ul4R7E+cNlGZJIZlQZ9v4CwbgVaFG3zOAA==", | |
| 812 | "dev": true, | |
| 813 | "license": "Apache-2.0", | |
| 814 | "dependencies": { | |
| 815 | "bare-assert": "^1.0.2", | |
| 816 | "bare-fs": "^4.1.2", | |
| 817 | "bare-module": "^6.1.2", | |
| 818 | "bare-path": "^3.0.0", | |
| 819 | "bare-process": "^4.2.0", | |
| 820 | "bare-url": "^2.1.5", | |
| 821 | "bare-utils": "^1.2.0", | |
| 822 | "v8-to-istanbul": "^9.3.0", | |
| 823 | "which-runtime": "^1.2.1" | |
| 824 | } | |
| 825 | }, | |
| 826 | "node_modules/bare-ws": { | |
| 827 | "version": "3.0.0", | |
| 828 | "resolved": "https://registry.npmjs.org/bare-ws/-/bare-ws-3.0.0.tgz", | |
| 829 | "integrity": "sha512-q2v0UIQ0cFQBXQMp+0FRaoSk1EoYgOhzO7yio0TqBV6Rkfot97mbBYxb1ssw5faVCisVaFxQYY8113SMLYQBow==", | |
| 830 | "dev": true, | |
| 831 | "license": "Apache-2.0", | |
| 832 | "dependencies": { | |
| 833 | "bare-crypto": "^1.2.0", | |
| 834 | "bare-events": "^2.3.1", | |
| 835 | "bare-http1": "^4.0.0", | |
| 836 | "bare-https": "^3.0.0", | |
| 837 | "bare-stream": "^2.1.2" | |
| 838 | }, | |
| 839 | "peerDependencies": { | |
| 840 | "bare-buffer": "*", | |
| 841 | "bare-url": "*" | |
| 842 | }, | |
| 843 | "peerDependenciesMeta": { | |
| 844 | "bare-buffer": { | |
| 845 | "optional": true | |
| 846 | }, | |
| 847 | "bare-url": { | |
| 848 | "optional": true | |
| 849 | } | |
| 850 | } | |
| 851 | }, | |
| 852 | "node_modules/bip39-mnemonic": { | |
| 853 | "version": "2.5.0", | |
| 854 | "resolved": "https://registry.npmjs.org/bip39-mnemonic/-/bip39-mnemonic-2.5.0.tgz", | |
| 855 | "integrity": "sha512-hgCxFHN129ta0bHl9VPykoqCPagBUr2oaXjfSbfeXAPe+y/byxwjPFFEFTMNO/1T/GSoMP1cOXHa2ZaprHG1kg==", | |
| 856 | "license": "Apache-2.0", | |
| 857 | "dependencies": { | |
| 858 | "b4a": "^1.6.6", | |
| 859 | "nanoassert": "^2.0.0", | |
| 860 | "sodium-universal": "^5.0.1" | |
| 861 | } | |
| 862 | }, | |
| 863 | "node_modules/brittle": { | |
| 864 | "version": "4.0.2", | |
| 865 | "resolved": "https://registry.npmjs.org/brittle/-/brittle-4.0.2.tgz", | |
| 866 | "integrity": "sha512-frnh7gB76uiSmFAf4+uIsaKjygMEvzUbN/GhxS+30WKz0pd/COBVuj+O9TmW9t0kIzJTafv8xquV6DriW316Vw==", | |
| 867 | "dev": true, | |
| 868 | "license": "Apache-2.0", | |
| 869 | "dependencies": { | |
| 870 | "b4a": "^1.8.0", | |
| 871 | "bare-assert": "^1.2.0", | |
| 872 | "bare-buffer": "^3.6.0", | |
| 873 | "bare-channel": "^5.2.3", | |
| 874 | "bare-cov": "^1.2.1", | |
| 875 | "bare-fs": "^4.5.5", | |
| 876 | "bare-os": "^3.7.1", | |
| 877 | "bare-path": "^3.0.0", | |
| 878 | "bare-process": "^4.3.0", | |
| 879 | "bare-subprocess": "^5.2.2", | |
| 880 | "bare-url": "^2.3.2", | |
| 881 | "error-stack-parser": "^2.1.4", | |
| 882 | "globbie": "^1.0.3", | |
| 883 | "paparam": "^1.10.0", | |
| 884 | "same-object": "^1.0.2", | |
| 885 | "streamx": "^2.25.0", | |
| 886 | "test-tmp": "^1.4.0", | |
| 887 | "tmatch": "^5.0.0", | |
| 888 | "which-runtime": "^1.3.2" | |
| 889 | }, | |
| 890 | "bin": { | |
| 891 | "brittle": "advise.js", | |
| 892 | "brittle-bare": "brittle-bare.js", | |
| 893 | "brittle-make-test": "make-test.js", | |
| 894 | "brittle-node": "brittle-node.js" | |
| 895 | } | |
| 896 | }, | |
| 897 | "node_modules/cborg": { | |
| 898 | "version": "5.1.6", | |
| 899 | "resolved": "https://registry.npmjs.org/cborg/-/cborg-5.1.6.tgz", | |
| 900 | "integrity": "sha512-apu/mOSODJ98JXBz3cbiv/gAFX4oaCZQQ53+8b4IPjc1wylYxd8eJis87QP7ic4AOE4O02j02jjfUgVZUJIIvA==", | |
| 901 | "license": "Apache-2.0", | |
| 902 | "bin": { | |
| 903 | "cborg": "lib/bin.js" | |
| 904 | } | |
| 905 | }, | |
| 906 | "node_modules/compact-encoding": { | |
| 907 | "version": "3.3.0", | |
| 908 | "resolved": "https://registry.npmjs.org/compact-encoding/-/compact-encoding-3.3.0.tgz", | |
| 909 | "integrity": "sha512-e64XyzlBvTRJ3iScuU/U2w25Dglkm7fhrRbrGaHIwuTlNnzjRKMaQBCVl7mJRvZg7wI5a9wX1IVTXCuaAANNkw==", | |
| 910 | "license": "Apache-2.0", | |
| 911 | "dependencies": { | |
| 912 | "b4a": "^1.3.0" | |
| 913 | } | |
| 914 | }, | |
| 915 | "node_modules/convert-source-map": { | |
| 916 | "version": "2.0.0", | |
| 917 | "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", | |
| 918 | "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", | |
| 919 | "dev": true, | |
| 920 | "license": "MIT" | |
| 921 | }, | |
| 922 | "node_modules/error-stack-parser": { | |
| 923 | "version": "2.1.4", | |
| 924 | "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", | |
| 925 | "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", | |
| 926 | "dev": true, | |
| 927 | "license": "MIT", | |
| 928 | "dependencies": { | |
| 929 | "stackframe": "^1.3.4" | |
| 930 | } | |
| 931 | }, | |
| 932 | "node_modules/events-universal": { | |
| 933 | "version": "1.0.1", | |
| 934 | "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", | |
| 935 | "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", | |
| 936 | "license": "Apache-2.0", | |
| 937 | "dependencies": { | |
| 938 | "bare-events": "^2.7.0" | |
| 939 | } | |
| 940 | }, | |
| 941 | "node_modules/fast-fifo": { | |
| 942 | "version": "1.3.2", | |
| 943 | "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", | |
| 944 | "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", | |
| 945 | "license": "MIT" | |
| 946 | }, | |
| 947 | "node_modules/globbie": { | |
| 948 | "version": "1.0.3", | |
| 949 | "resolved": "https://registry.npmjs.org/globbie/-/globbie-1.0.3.tgz", | |
| 950 | "integrity": "sha512-hcryJmKcftf82xfBWTWxuUITWIIoYO3ec104V17SMHGFl6VLbm1d1Ju9LX9L+jyJTwICpemX/dmPI/HGYoKr1A==", | |
| 951 | "dev": true, | |
| 952 | "license": "Apache-2.0", | |
| 953 | "dependencies": { | |
| 954 | "bare-fs": "^4.1.2", | |
| 955 | "bare-path": "^3.0.0", | |
| 956 | "bare-process": "^4.2.1", | |
| 957 | "picomatch": "^4.0.2" | |
| 958 | } | |
| 959 | }, | |
| 960 | "node_modules/keet-identity-key": { | |
| 961 | "version": "3.2.0", | |
| 962 | "resolved": "https://registry.npmjs.org/keet-identity-key/-/keet-identity-key-3.2.0.tgz", | |
| 963 | "integrity": "sha512-EMM+zOnDsdg/tOZ9gv+UlWjFYvMrTu+XYd2sNQ3+x5mGMxw5RjXvdXt/lh2OnGs04IemxKnHjk/hOMKjmYzhew==", | |
| 964 | "license": "Apache-2.0", | |
| 965 | "dependencies": { | |
| 966 | "b4a": "^1.6.4", | |
| 967 | "bip39-mnemonic": "^2.2.0", | |
| 968 | "compact-encoding": "^3.0.0", | |
| 969 | "nanoassert": "^2.0.0", | |
| 970 | "sodium-hmac": "^2.0.1", | |
| 971 | "sodium-universal": "^5.0.1" | |
| 972 | } | |
| 973 | }, | |
| 974 | "node_modules/multiformats": { | |
| 975 | "version": "13.4.2", | |
| 976 | "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-13.4.2.tgz", | |
| 977 | "integrity": "sha512-eh6eHCrRi1+POZ3dA+Dq1C6jhP1GNtr9CRINMb67OKzqW9I5DUuZM/3jLPlzhgpGeiNUlEGEbkCYChXMCc/8DQ==", | |
| 978 | "license": "Apache-2.0 OR MIT" | |
| 979 | }, | |
| 980 | "node_modules/nanoassert": { | |
| 981 | "version": "2.0.0", | |
| 982 | "resolved": "https://registry.npmjs.org/nanoassert/-/nanoassert-2.0.0.tgz", | |
| 983 | "integrity": "sha512-7vO7n28+aYO4J+8w96AzhmU8G+Y/xpPDJz/se19ICsqj/momRbb9mh9ZUtkoJ5X3nTnPdhEJyc0qnM6yAsHBaA==", | |
| 984 | "license": "ISC" | |
| 985 | }, | |
| 986 | "node_modules/paparam": { | |
| 987 | "version": "1.10.1", | |
| 988 | "resolved": "https://registry.npmjs.org/paparam/-/paparam-1.10.1.tgz", | |
| 989 | "integrity": "sha512-viyQI64VIja0Za3njIzhoEP8ZVkgPowhZPuG0E96NwBfYJ6ZIyrrlhWGtFPkdN7eYLl2L8CTWL+wla0evm1KKQ==", | |
| 990 | "dev": true, | |
| 991 | "license": "Apache-2.0" | |
| 992 | }, | |
| 993 | "node_modules/picomatch": { | |
| 994 | "version": "4.0.5", | |
| 995 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", | |
| 996 | "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", | |
| 997 | "dev": true, | |
| 998 | "license": "MIT", | |
| 999 | "engines": { | |
| 1000 | "node": ">=12" | |
| 1001 | }, | |
| 1002 | "funding": { | |
| 1003 | "url": "https://github.com/sponsors/jonschlinkert" | |
| 1004 | } | |
| 1005 | }, | |
| 1006 | "node_modules/require-addon": { | |
| 1007 | "version": "1.2.0", | |
| 1008 | "resolved": "https://registry.npmjs.org/require-addon/-/require-addon-1.2.0.tgz", | |
| 1009 | "integrity": "sha512-VNPDZlYgIYQwWp9jMTzljx+k0ZtatKlcvOhktZ/anNPI3dQ9NXk7cq2U4iJ1wd9IrytRnYhyEocFWbkdPb+MYA==", | |
| 1010 | "license": "Apache-2.0", | |
| 1011 | "dependencies": { | |
| 1012 | "bare-addon-resolve": "^1.3.0" | |
| 1013 | }, | |
| 1014 | "engines": { | |
| 1015 | "bare": ">=1.10.0" | |
| 1016 | } | |
| 1017 | }, | |
| 1018 | "node_modules/same-object": { | |
| 1019 | "version": "1.0.2", | |
| 1020 | "resolved": "https://registry.npmjs.org/same-object/-/same-object-1.0.2.tgz", | |
| 1021 | "integrity": "sha512-csHWhvUsLbIOHDM/nP+KHWM+BLPsIzWkFa8HbzaI0G7BqKXgx+7FJpKTGgLXyz5amfdY2OVBcmXTqYOMEk04og==", | |
| 1022 | "dev": true, | |
| 1023 | "license": "MIT" | |
| 1024 | }, | |
| 1025 | "node_modules/sodium-hmac": { | |
| 1026 | "version": "2.1.0", | |
| 1027 | "resolved": "https://registry.npmjs.org/sodium-hmac/-/sodium-hmac-2.1.0.tgz", | |
| 1028 | "integrity": "sha512-1g7d7v+/0g/XXSX9sp0++X1QB6M1knhomnsFiN7B42b+rV0VhQeyiRzoWtp7vYGLkzmLY77tWNnCef3juGRwfQ==", | |
| 1029 | "license": "Apache-2.0", | |
| 1030 | "dependencies": { | |
| 1031 | "b4a": "^1.6.6", | |
| 1032 | "sodium-universal": "^5.0.1" | |
| 1033 | } | |
| 1034 | }, | |
| 1035 | "node_modules/sodium-native": { | |
| 1036 | "version": "5.1.0", | |
| 1037 | "resolved": "https://registry.npmjs.org/sodium-native/-/sodium-native-5.1.0.tgz", | |
| 1038 | "integrity": "sha512-3RxgyWyJlhTsABPnJVpCI5CoTDANZTqqFrEPqr+kjfnRaBihpVtMUE3yTF40ukdoB1APXeoBNKF3MzZAIHg39g==", | |
| 1039 | "license": "MIT", | |
| 1040 | "dependencies": { | |
| 1041 | "bare-assert": "^1.2.0", | |
| 1042 | "require-addon": "^1.1.0", | |
| 1043 | "which-runtime": "^1.2.1" | |
| 1044 | }, | |
| 1045 | "engines": { | |
| 1046 | "bare": ">=1.16.0" | |
| 1047 | } | |
| 1048 | }, | |
| 1049 | "node_modules/sodium-universal": { | |
| 1050 | "version": "5.0.1", | |
| 1051 | "resolved": "https://registry.npmjs.org/sodium-universal/-/sodium-universal-5.0.1.tgz", | |
| 1052 | "integrity": "sha512-rv+aH+tnKB5H0MAc2UadHShLMslpJsc4wjdnHRtiSIEYpOetCgu8MS4ExQRia+GL/MK3uuCyZPeEsi+J3h+Q+Q==", | |
| 1053 | "license": "MIT", | |
| 1054 | "dependencies": { | |
| 1055 | "sodium-native": "^5.0.1" | |
| 1056 | }, | |
| 1057 | "peerDependencies": { | |
| 1058 | "sodium-javascript": "~0.8.0" | |
| 1059 | }, | |
| 1060 | "peerDependenciesMeta": { | |
| 1061 | "sodium-javascript": { | |
| 1062 | "optional": true | |
| 1063 | } | |
| 1064 | } | |
| 1065 | }, | |
| 1066 | "node_modules/stackframe": { | |
| 1067 | "version": "1.3.4", | |
| 1068 | "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", | |
| 1069 | "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==", | |
| 1070 | "dev": true, | |
| 1071 | "license": "MIT" | |
| 1072 | }, | |
| 1073 | "node_modules/streamx": { | |
| 1074 | "version": "2.28.0", | |
| 1075 | "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.28.0.tgz", | |
| 1076 | "integrity": "sha512-1Yowhzjf0ivGMrTIkY9hav5TxobO9qIVqUE41fiCGMGgc3CLlf4MY+9AHmZqBWgDTue0fY9zWjYFVyf6Diuobw==", | |
| 1077 | "license": "MIT", | |
| 1078 | "dependencies": { | |
| 1079 | "events-universal": "^1.0.0", | |
| 1080 | "fast-fifo": "^1.3.2", | |
| 1081 | "text-decoder": "^1.1.0" | |
| 1082 | } | |
| 1083 | }, | |
| 1084 | "node_modules/teex": { | |
| 1085 | "version": "1.0.1", | |
| 1086 | "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz", | |
| 1087 | "integrity": "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==", | |
| 1088 | "license": "MIT", | |
| 1089 | "dependencies": { | |
| 1090 | "streamx": "^2.12.5" | |
| 1091 | } | |
| 1092 | }, | |
| 1093 | "node_modules/test-tmp": { | |
| 1094 | "version": "1.4.0", | |
| 1095 | "resolved": "https://registry.npmjs.org/test-tmp/-/test-tmp-1.4.0.tgz", | |
| 1096 | "integrity": "sha512-GVggxGg+jXqP2Wbju50JVLo+9E+nIOPPyWqgr63EbOnNItIKu1cEbJpTWAJeflnyGqXOtcMI7ijHRp88GUkfDA==", | |
| 1097 | "dev": true, | |
| 1098 | "license": "MIT", | |
| 1099 | "dependencies": { | |
| 1100 | "bare-fs": "^4.0.1", | |
| 1101 | "bare-os": "^3.3.0", | |
| 1102 | "bare-path": "^3.0.0" | |
| 1103 | } | |
| 1104 | }, | |
| 1105 | "node_modules/text-decoder": { | |
| 1106 | "version": "1.2.7", | |
| 1107 | "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz", | |
| 1108 | "integrity": "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==", | |
| 1109 | "license": "Apache-2.0", | |
| 1110 | "dependencies": { | |
| 1111 | "b4a": "^1.6.4" | |
| 1112 | } | |
| 1113 | }, | |
| 1114 | "node_modules/tmatch": { | |
| 1115 | "version": "5.0.0", | |
| 1116 | "resolved": "https://registry.npmjs.org/tmatch/-/tmatch-5.0.0.tgz", | |
| 1117 | "integrity": "sha512-Ib9OtBkpHn07tXP04SlN1SYRxFgTk6wSM2EBmjjxug4u5RXPRVLkdFJSS1PmrQidaSB8Lru9nRtViQBsbxzE5Q==", | |
| 1118 | "dev": true, | |
| 1119 | "license": "ISC", | |
| 1120 | "engines": { | |
| 1121 | "node": ">=8" | |
| 1122 | } | |
| 1123 | }, | |
| 1124 | "node_modules/v8-to-istanbul": { | |
| 1125 | "version": "9.3.0", | |
| 1126 | "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", | |
| 1127 | "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", | |
| 1128 | "dev": true, | |
| 1129 | "license": "ISC", | |
| 1130 | "dependencies": { | |
| 1131 | "@jridgewell/trace-mapping": "^0.3.12", | |
| 1132 | "@types/istanbul-lib-coverage": "^2.0.1", | |
| 1133 | "convert-source-map": "^2.0.0" | |
| 1134 | }, | |
| 1135 | "engines": { | |
| 1136 | "node": ">=10.12.0" | |
| 1137 | } | |
| 1138 | }, | |
| 1139 | "node_modules/which-runtime": { | |
| 1140 | "version": "1.4.0", | |
| 1141 | "resolved": "https://registry.npmjs.org/which-runtime/-/which-runtime-1.4.0.tgz", | |
| 1142 | "integrity": "sha512-0ugbP4CJW4e2D20jvEcC4973dCgIaHI4Rw1PT+26U9zEve7FyYdWAIwUnoeOYvoCfn+wXHoHTKb1KhkYlb60Pw==", | |
| 1143 | "license": "Apache-2.0" | |
| 1144 | } | |
| 1145 | } | |
| 1146 | } |
| … | … | @@ -0,0 +1,23 @@ |
| 1 | { | |
| 2 | "name": "atproto-identity", | |
| 3 | "version": "0.0.1", | |
| 4 | "description": "Seed-anchored atproto identity: one BIP39 mnemonic -> Ed25519 (Hypercore) + secp256k1 (did:plc). Offline did:plc genesis + verification.", | |
| 5 | "type": "module", | |
| 6 | "main": "lib/atproto-identity.js", | |
| 7 | "scripts": { | |
| 8 | "test": "brittle-node test/*.test.js", | |
| 9 | "postinstall": "node scripts/bare-deps.mjs" | |
| 10 | }, | |
| 11 | "dependencies": { | |
| 12 | "@ipld/dag-cbor": "^9.2.1", | |
| 13 | "@noble/curves": "^1.6.0", | |
| 14 | "@noble/hashes": "^1.5.0", | |
| 15 | "@scure/bip32": "^1.5.0", | |
| 16 | "@scure/bip39": "^1.4.0", | |
| 17 | "keet-identity-key": "^3.2.0", | |
| 18 | "multiformats": "^13.3.0" | |
| 19 | }, | |
| 20 | "devDependencies": { | |
| 21 | "brittle": "^4.0.2" | |
| 22 | } | |
| 23 | } |
| … | … | @@ -0,0 +1,53 @@ |
| 1 | // Bare dep compat: add a "bare" export condition to @noble/hashes' ./crypto so it resolves | |
| 2 | // to the bare-safe crypto.js (globalThis.crypto ?? undefined) instead of cryptoNode.js | |
| 3 | // (`import 'node:crypto'`, which Bare can't load). @noble/curves + @scure/* import | |
| 4 | // @noble/hashes, so this one condition unblocks the whole crypto tree. We sign | |
| 5 | // deterministically (RFC6979), so the shim degrading to undefined is never called. | |
| 6 | // | |
| 7 | // node_modules is reinstall-wiped, so this runs on `postinstall` (see the package.json of | |
| 8 | // each package that depends on @noble). Idempotent. Run manually with explicit roots | |
| 9 | // (`node scripts/bare-deps.mjs identity repo pds`) to patch the whole dev tree at once; | |
| 10 | // with no args it patches the CURRENT package (process.cwd()) — the postinstall case. | |
| 11 | ||
| 12 | import { readdirSync, readFileSync, writeFileSync, statSync } from 'node:fs' | |
| 13 | import { join } from 'node:path' | |
| 14 | ||
| 15 | function patch (pjPath) { | |
| 16 | let j | |
| 17 | try { j = JSON.parse(readFileSync(pjPath, 'utf8')) } catch { return false } | |
| 18 | const exp = j.exports && j.exports['./crypto'] | |
| 19 | if (exp && typeof exp === 'object' && !exp.bare) { | |
| 20 | j.exports['./crypto'] = { bare: './esm/crypto.js', ...exp } // "bare" first → matched before "node" | |
| 21 | writeFileSync(pjPath, JSON.stringify(j, null, 2) + '\n') | |
| 22 | return true | |
| 23 | } | |
| 24 | return false | |
| 25 | } | |
| 26 | ||
| 27 | const isDir = (p) => { try { return statSync(p).isDirectory() } catch { return false } } | |
| 28 | ||
| 29 | // Walk a node_modules dir: patch any @noble/hashes found, recurse into nested node_modules. | |
| 30 | function walk (nm, found) { | |
| 31 | let entries | |
| 32 | try { entries = readdirSync(nm, { withFileTypes: true }) } catch { return } | |
| 33 | for (const e of entries) { | |
| 34 | if (!e.isDirectory()) continue | |
| 35 | const dir = join(nm, e.name) | |
| 36 | if (e.name === '@noble') { | |
| 37 | const pj = join(dir, 'hashes', 'package.json') | |
| 38 | if (patch(pj)) found.push(pj) | |
| 39 | // scoped pkgs may carry their own nested node_modules | |
| 40 | for (const p of readdirSync(dir)) { const nn = join(dir, p, 'node_modules'); if (isDir(nn)) walk(nn, found) } | |
| 41 | } else if (e.name.startsWith('@')) { | |
| 42 | for (const p of readdirSync(dir)) { const nn = join(dir, p, 'node_modules'); if (isDir(nn)) walk(nn, found) } | |
| 43 | } else { | |
| 44 | const nn = join(dir, 'node_modules'); if (isDir(nn)) walk(nn, found) | |
| 45 | } | |
| 46 | } | |
| 47 | } | |
| 48 | ||
| 49 | const roots = process.argv.slice(2) | |
| 50 | if (roots.length === 0) roots.push(process.cwd()) | |
| 51 | const found = [] | |
| 52 | for (const r of roots) { const nm = join(r, 'node_modules'); if (isDir(nm)) walk(nm, found) } | |
| 53 | console.log(found.length ? `bare-deps: patched ${found.length} @noble/hashes` : 'bare-deps: nothing to patch') |
| … | … | @@ -0,0 +1,42 @@ |
| 1 | // The composed device-attested anchor: one root + one device → both the atproto genesis | |
| 2 | // (federation authority) and the keet proof (P2P authority), consistent. Plus the | |
| 3 | // real-flow correctness point: the root builds the identical DID from only the device's | |
| 4 | // PUBLIC key. | |
| 5 | ||
| 6 | import test from 'brittle' | |
| 7 | import b4a from 'b4a' | |
| 8 | import { buildAttestedAnchor } from '../lib/attested-anchor.js' | |
| 9 | import { buildAttestedIdentity, verifyAttestedGenesis } from '../lib/device-attestation.js' | |
| 10 | import { deriveAtprotoKey } from '../lib/atproto-identity.js' | |
| 11 | import { verifyAtprotoBinding, rootIdentityPublicKey } from '../lib/device-chain.js' | |
| 12 | ||
| 13 | const ROOT = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about' | |
| 14 | const DEVICE = 'legal winner thank year wave sausage worth useful legal winner thank yellow' | |
| 15 | ||
| 16 | test('buildAttestedAnchor: atproto genesis + keet proof agree on one root and one device', async (t) => { | |
| 17 | const a = await buildAttestedAnchor({ rootMnemonic: ROOT, deviceMnemonic: DEVICE, handle: 'p2p.7rnx.net', pdsUrl: 'https://pds.7rnx.net' }) | |
| 18 | ||
| 19 | // atproto side | |
| 20 | const rootPk = deriveAtprotoKey(ROOT).publicKey | |
| 21 | t.ok(a.did.startsWith('did:plc:'), 'did:plc minted') | |
| 22 | t.is(a.signedOp.verificationMethods.atproto, a.deviceDidKey, 'verificationMethod = device signing key') | |
| 23 | t.is(a.signedOp.rotationKeys[0], a.rootDidKey, 'rotationKeys[0] = root') | |
| 24 | t.ok(verifyAttestedGenesis(a.signedOp, rootPk), 'genesis signed by root') | |
| 25 | t.ok(a.deviceSigningKey && a.deviceSigningKey.privateKey.length === 32, 'device signing key available to the PDS') | |
| 26 | ||
| 27 | // keet side — the proof resolves to the same root and vouches for this DID | |
| 28 | const keetRoot = await rootIdentityPublicKey(ROOT) | |
| 29 | const res = verifyAtprotoBinding({ proof: a.keetProof, did: a.did }) | |
| 30 | t.ok(res && b4a.equals(res.identityPublicKey, keetRoot), 'keet proof resolves to the ROOT identity') | |
| 31 | t.ok(res && b4a.equals(res.devicePublicKey, a.deviceKeetPublicKey), 'keet proof leaf = this device') | |
| 32 | }) | |
| 33 | ||
| 34 | test('root builds the SAME did:plc from only the device public key (device key never leaves the device)', (t) => { | |
| 35 | const dev = deriveAtprotoKey(DEVICE) | |
| 36 | const full = buildAttestedIdentity({ rootMnemonic: ROOT, deviceMnemonic: DEVICE, handle: 'p2p.7rnx.net', pdsUrl: 'https://pds.7rnx.net' }) | |
| 37 | const pubOnly = buildAttestedIdentity({ rootMnemonic: ROOT, deviceDidKey: dev.didKey, handle: 'p2p.7rnx.net', pdsUrl: 'https://pds.7rnx.net' }) | |
| 38 | ||
| 39 | t.is(pubOnly.did, full.did, 'pubkey-only genesis yields the identical permanent DID') | |
| 40 | t.is(pubOnly.deviceSigningKey, null, 'root does NOT hold the device signing key in the pubkey-only flow') | |
| 41 | t.is(pubOnly.deviceDidKey, dev.didKey, 'verificationMethod is the device pubkey the root was handed') | |
| 42 | }) |
| … | … | @@ -0,0 +1,43 @@ |
| 1 | // The binding: one mnemonic -> keet Ed25519 identity + atproto secp256k1/did:plc. | |
| 2 | // Async (keet-identity-key is async). Confirms both halves come from one phrase and | |
| 3 | // are mutually consistent + deterministic. | |
| 4 | ||
| 5 | import test from 'brittle' | |
| 6 | import { deriveIdentity, bindingRecord, deriveAtprotoKey } from '../lib/binding.js' | |
| 7 | ||
| 8 | const MNEMONIC = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about' | |
| 9 | // keet's Ed25519 identity pubkey for this phrase (captured from keet-identity-key). | |
| 10 | const KEET_ED25519 = 'de1ab94806ced05c64c0098de00ffadae052351b9fbdb6be4d645123ac9d8798' | |
| 11 | const OPTS = { handle: 'alice.7rnx.net', pdsUrl: 'https://pds.7rnx.net' } | |
| 12 | ||
| 13 | test('mnemonic binds keet Ed25519 identity to atproto secp256k1/did:plc', async (t) => { | |
| 14 | const id = await deriveIdentity(MNEMONIC, OPTS) | |
| 15 | ||
| 16 | // Ed25519 half comes from keet-identity-key, for this exact phrase. | |
| 17 | t.ok(id.ed25519PublicKey.length === 32 && Buffer.from(id.ed25519PublicKey).toString('hex') === KEET_ED25519, | |
| 18 | 'ed25519 identity key is keet-derived (32 bytes, matches)') | |
| 19 | ||
| 20 | // secp256k1 half is consistent with the standalone atproto derivation (same seed). | |
| 21 | const standalone = deriveAtprotoKey(MNEMONIC) | |
| 22 | t.ok(Buffer.from(id.secp256k1PublicKey).equals(Buffer.from(standalone.publicKey)), | |
| 23 | 'secp256k1 half matches standalone deriveAtprotoKey') | |
| 24 | t.ok(id.didKey.startsWith('did:key:zQ3s'), 'atproto did:key is secp256k1 (zQ3s…)') | |
| 25 | t.ok(/^did:plc:[a-z2-7]{24}$/.test(id.did), 'did:plc has the expected shape') | |
| 26 | ||
| 27 | // One phrase -> both keys, deterministically. | |
| 28 | const id2 = await deriveIdentity(MNEMONIC, OPTS) | |
| 29 | t.is(id.did, id2.did, 'same mnemonic -> same did:plc') | |
| 30 | t.ok(Buffer.from(id.ed25519PublicKey).equals(Buffer.from(id2.ed25519PublicKey)), 'same mnemonic -> same ed25519') | |
| 31 | t.ok(!Buffer.from(id.ed25519PublicKey).equals(Buffer.from(id.secp256k1PublicKey)), | |
| 32 | 'the two keys differ (distinct curves/roles)') | |
| 33 | ||
| 34 | // JSON-safe binding record for the hyper-identity provider. | |
| 35 | const rec = await bindingRecord(MNEMONIC, OPTS) | |
| 36 | t.is(JSON.stringify(rec), JSON.stringify(JSON.parse(JSON.stringify(rec))), 'binding record is JSON round-trippable') | |
| 37 | t.is(rec.provider, 'atproto', 'record: provider=atproto') | |
| 38 | t.is(rec.ed25519PublicKey.length, 64, 'record: ed25519 hex is 64 chars') | |
| 39 | t.is(rec.secp256k1PublicKey.length, 66, 'record: secp256k1 hex is 66 chars (compressed)') | |
| 40 | t.ok(rec.did === id.did && rec.handle === OPTS.handle, 'record: did + handle carried') | |
| 41 | t.comment(`binding: ${rec.did} handle: ${rec.handle}`) | |
| 42 | t.comment(`ed25519(keet): ${rec.ed25519PublicKey.slice(0, 16)}… secp256k1(atproto): ${rec.secp256k1PublicKey.slice(0, 16)}…`) | |
| 43 | }) |
| … | … | @@ -0,0 +1,56 @@ |
| 1 | // Derivation + signing, all offline. Covers: | |
| 2 | // - did:key codec validated against the golden P-256 values (round-trip) | |
| 3 | // - deterministic secp256k1 derivation (same mnemonic -> same key/did) | |
| 4 | // - genesis sign -> verify round-trip, and tamper-detection | |
| 5 | // - full mnemonic -> did:plc flow is stable | |
| 6 | ||
| 7 | import test from 'brittle' | |
| 8 | import { decodeDidKey, encodeDidKey } from '../lib/did-key.js' | |
| 9 | import { | |
| 10 | deriveAtprotoKey, buildGenesisOp, signOp, verifyOp, | |
| 11 | createAtprotoIdentity, unsignedOp, didForCreateOp | |
| 12 | } from '../lib/atproto-identity.js' | |
| 13 | ||
| 14 | const b4a = (a, b) => Buffer.from(a).equals(Buffer.from(b)) | |
| 15 | ||
| 16 | // A fixed, well-known BIP39 test mnemonic (NOT a real identity). | |
| 17 | const MNEMONIC = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about' | |
| 18 | ||
| 19 | test('did:key codec round-trips golden P-256 values', (t) => { | |
| 20 | // did:key codec — golden P-256 values from millipds must round-trip byte-exact. | |
| 21 | for (const golden of [ | |
| 22 | 'did:key:zDnaeWUTD9gF8QMQ9WDh5t12ojkohiKGWUCrCnAGvu1CSJpyj', | |
| 23 | 'did:key:zDnaepXNQybJv5o8DM5AdmbpnedfApGv58q2UiDyhMgP5SFHx' | |
| 24 | ]) { | |
| 25 | const { curve, pubkey } = decodeDidKey(golden) | |
| 26 | t.is(encodeDidKey(curve, pubkey), golden, `did:key P-256 round-trips (${golden.slice(0, 20)}…)`) | |
| 27 | } | |
| 28 | }) | |
| 29 | ||
| 30 | test('deterministic secp256k1 derivation', (t) => { | |
| 31 | const k1 = deriveAtprotoKey(MNEMONIC) | |
| 32 | const k2 = deriveAtprotoKey(MNEMONIC) | |
| 33 | t.ok(b4a(k1.publicKey, k2.publicKey), 'same mnemonic -> same secp256k1 pubkey') | |
| 34 | t.ok(k1.didKey.startsWith('did:key:zQ3s'), 'derives a secp256k1 did:key (zQ3s…)') | |
| 35 | t.ok(b4a(decodeDidKey(k1.didKey).pubkey, k1.publicKey), 'did:key decodes back to the same pubkey') | |
| 36 | t.ok(!b4a(deriveAtprotoKey(MNEMONIC, "m/44'/0'/0'/0/1").publicKey, k1.publicKey), 'different path -> different key') | |
| 37 | }) | |
| 38 | ||
| 39 | test('genesis sign -> verify round-trip + tamper detection', (t) => { | |
| 40 | const k1 = deriveAtprotoKey(MNEMONIC) | |
| 41 | const unsigned = buildGenesisOp({ signingDidKey: k1.didKey, handle: 'alice.7rnx.net', pdsUrl: 'https://pds.7rnx.net' }) | |
| 42 | const sig = signOp(unsigned, k1.privateKey) | |
| 43 | t.ok(verifyOp(unsigned, sig, k1.publicKey), 'own signature verifies') | |
| 44 | const tampered = { ...unsigned, alsoKnownAs: ['at://mallory.7rnx.net'] } | |
| 45 | t.ok(!verifyOp(tampered, sig, k1.publicKey), 'tampered op fails verification') | |
| 46 | }) | |
| 47 | ||
| 48 | test('full flow is deterministic and self-consistent', (t) => { | |
| 49 | const idA = createAtprotoIdentity(MNEMONIC, { handle: 'alice.7rnx.net', pdsUrl: 'https://pds.7rnx.net' }) | |
| 50 | const idB = createAtprotoIdentity(MNEMONIC, { handle: 'alice.7rnx.net', pdsUrl: 'https://pds.7rnx.net' }) | |
| 51 | t.is(idA.did, idB.did, 'same mnemonic+params -> same did:plc') | |
| 52 | t.ok(/^did:plc:[a-z2-7]{24}$/.test(idA.did), 'did:plc has the expected shape') | |
| 53 | t.is(didForCreateOp(idA.signedOp), idA.did, 'did recomputes from the signed op') | |
| 54 | t.ok(verifyOp(unsignedOp(idA.signedOp), idA.signedOp.sig, idA.publicKey), 'signed op verifies end-to-end') | |
| 55 | t.comment(`derived did: ${idA.did} handle: alice.7rnx.net`) | |
| 56 | }) |
| … | … | @@ -0,0 +1,48 @@ |
| 1 | // Device-attested anchoring (atproto side): the genesis op must place the ROOT identity in | |
| 2 | // rotationKeys (authority/recovery) and the DEVICE key in verificationMethod (repo | |
| 3 | // signing), and be signed by the ROOT — never the device. This is the permanent shape | |
| 4 | // baked at genesis, so it must be correct before the one irreversible PLC write. | |
| 5 | ||
| 6 | import test from 'brittle' | |
| 7 | import { buildAttestedIdentity, verifyAttestedGenesis } from '../lib/device-attestation.js' | |
| 8 | import { deriveAtprotoKey } from '../lib/atproto-identity.js' | |
| 9 | ||
| 10 | // two distinct valid BIP39 vectors → distinct secp256k1 keys (root vs device) | |
| 11 | const ROOT = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about' | |
| 12 | const DEVICE = 'legal winner thank year wave sausage worth useful legal winner thank yellow' | |
| 13 | ||
| 14 | test('attested genesis: root=rotationKey, device=verificationMethod, root-signed', (t) => { | |
| 15 | const id = buildAttestedIdentity({ | |
| 16 | rootMnemonic: ROOT, deviceMnemonic: DEVICE, | |
| 17 | handle: 'p2p.7rnx.net', pdsUrl: 'https://pds.7rnx.net' | |
| 18 | }) | |
| 19 | const op = id.signedOp | |
| 20 | ||
| 21 | t.ok(id.did.startsWith('did:plc:'), 'did:plc minted from the attested genesis') | |
| 22 | t.not(id.deviceDidKey, id.rootDidKey, 'device signing key ≠ root rotation key (separation)') | |
| 23 | t.is(op.verificationMethods.atproto, id.deviceDidKey, 'verificationMethod = device signing key') | |
| 24 | t.is(op.rotationKeys[0], id.rootDidKey, 'rotationKeys[0] = root (authority / recovery)') | |
| 25 | t.ok(verifyAttestedGenesis(op, id.rootPublicKey), 'genesis is signed by the ROOT rotation key') | |
| 26 | t.absent(verifyAttestedGenesis(op, id.deviceSigningKey.publicKey), 'genesis is NOT signed by the device key') | |
| 27 | t.ok(id.deviceSigningKey.privateKey && id.deviceSigningKey.privateKey.length === 32, | |
| 28 | 'device holds a usable 32-byte signing key (it signs the repo commits)') | |
| 29 | }) | |
| 30 | ||
| 31 | test('extra rotation keys form a recovery chain (root + device both authoritative)', (t) => { | |
| 32 | const dev = deriveAtprotoKey(DEVICE) | |
| 33 | const id = buildAttestedIdentity({ | |
| 34 | rootMnemonic: ROOT, deviceMnemonic: DEVICE, extraRotationDidKeys: [dev.didKey], | |
| 35 | handle: 'p2p.7rnx.net', pdsUrl: 'https://pds.7rnx.net' | |
| 36 | }) | |
| 37 | const rk = id.signedOp.rotationKeys | |
| 38 | t.is(rk.length, 2, 'two rotation keys (a chain: root + device)') | |
| 39 | t.is(rk[0], id.rootDidKey, 'root has priority (first in the ordered rotation set)') | |
| 40 | t.is(rk[1], dev.didKey, 'device is also a rotation key — it can recover if the root is lost') | |
| 41 | }) | |
| 42 | ||
| 43 | test('the did:plc changes if the rotation set changes (rotation is baked at genesis)', (t) => { | |
| 44 | const a = buildAttestedIdentity({ rootMnemonic: ROOT, deviceMnemonic: DEVICE, handle: 'p2p.7rnx.net', pdsUrl: 'https://pds.7rnx.net' }) | |
| 45 | const dev = deriveAtprotoKey(DEVICE) | |
| 46 | const b = buildAttestedIdentity({ rootMnemonic: ROOT, deviceMnemonic: DEVICE, extraRotationDidKeys: [dev.didKey], handle: 'p2p.7rnx.net', pdsUrl: 'https://pds.7rnx.net' }) | |
| 47 | t.not(a.did, b.did, 'a different rotation set yields a different permanent DID — decide it before --publish') | |
| 48 | }) |
| … | … | @@ -0,0 +1,39 @@ |
| 1 | // keet device chain (Ed25519, P2P side): root identity attests a device, the device | |
| 2 | // vouches for the atproto DID, and a verifier resolves the chain back to the root — the | |
| 3 | // attestation OUR P2P world checks, parallel to atproto's PLC authority. | |
| 4 | ||
| 5 | import test from 'brittle' | |
| 6 | import b4a from 'b4a' | |
| 7 | import { | |
| 8 | deviceKeetKeyPair, rootIdentityPublicKey, | |
| 9 | attestDeviceChain, attestAtprotoBinding, verifyAtprotoBinding | |
| 10 | } from '../lib/device-chain.js' | |
| 11 | ||
| 12 | const ROOT = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about' | |
| 13 | const DEVICE = 'legal winner thank year wave sausage worth useful legal winner thank yellow' | |
| 14 | const DID = 'did:plc:waj4hyx6pr5zdwaccrcbsfti' | |
| 15 | ||
| 16 | test('device chain binds the atproto DID to the root identity', async (t) => { | |
| 17 | const deviceKp = await deviceKeetKeyPair(DEVICE) | |
| 18 | const chain = await attestDeviceChain({ rootMnemonic: ROOT, devicePublicKey: deviceKp.publicKey }) | |
| 19 | const bound = attestAtprotoBinding({ deviceKeyPair: deviceKp, did: DID, proof: chain }) | |
| 20 | ||
| 21 | const res = verifyAtprotoBinding({ proof: bound, did: DID }) | |
| 22 | const rootPk = await rootIdentityPublicKey(ROOT) | |
| 23 | ||
| 24 | t.ok(res, 'binding proof verifies') | |
| 25 | t.ok(res && b4a.equals(res.identityPublicKey, rootPk), 'chain resolves to the ROOT identity') | |
| 26 | t.ok(res && b4a.equals(res.devicePublicKey, deviceKp.publicKey), 'the device is the attested leaf') | |
| 27 | }) | |
| 28 | ||
| 29 | test('binding is rejected for a different DID or a wrong expected device', async (t) => { | |
| 30 | const deviceKp = await deviceKeetKeyPair(DEVICE) | |
| 31 | const chain = await attestDeviceChain({ rootMnemonic: ROOT, devicePublicKey: deviceKp.publicKey }) | |
| 32 | const bound = attestAtprotoBinding({ deviceKeyPair: deviceKp, did: DID, proof: chain }) | |
| 33 | ||
| 34 | t.absent(verifyAtprotoBinding({ proof: bound, did: 'did:plc:someoneelsesdifferentdid' }), | |
| 35 | 'a different DID → rejected (device signed the real DID)') | |
| 36 | const otherKp = await deviceKeetKeyPair(ROOT) | |
| 37 | t.absent(verifyAtprotoBinding({ proof: bound, did: DID, expectedDevice: otherKp.publicKey }), | |
| 38 | 'a wrong expected device → rejected') | |
| 39 | }) |
| … | … | @@ -0,0 +1,38 @@ |
| 1 | // Golden-vector test: prove our did:plc computation is byte-exact against a DID | |
| 2 | // that millipds actually minted and that lives on the real PLC directory. | |
| 3 | // | |
| 4 | // did:plc:bh2hiuoeb34g4lif62zj2vx7 (handle bridgetest.7rnx.net) | |
| 5 | // | |
| 6 | // If dag-cbor canonicalization, sha256, base32, or truncation are off by a byte, | |
| 7 | // this fails. Reference IS the spec (verification-first). | |
| 8 | ||
| 9 | import test from 'brittle' | |
| 10 | import { readFileSync } from 'node:fs' | |
| 11 | import { fileURLToPath } from 'node:url' | |
| 12 | import { dirname, join } from 'node:path' | |
| 13 | import { didForCreateOp } from '../lib/did-plc.js' | |
| 14 | ||
| 15 | const here = dirname(fileURLToPath(import.meta.url)) | |
| 16 | const golden = JSON.parse(readFileSync(join(here, 'vectors', 'bridgetest-genesis.json'), 'utf8')) | |
| 17 | ||
| 18 | const EXPECTED = 'did:plc:bh2hiuoeb34g4lif62zj2vx7' | |
| 19 | ||
| 20 | test('did:plc is byte-exact against millipds golden genesis op', (t) => { | |
| 21 | const did = didForCreateOp(golden) | |
| 22 | t.is(did, EXPECTED, 'did:plc from millipds golden genesis op') | |
| 23 | ||
| 24 | // The DID is a pure function of the op — recomputation is stable. | |
| 25 | t.is(didForCreateOp(golden), did, 'recomputation is deterministic') | |
| 26 | ||
| 27 | // Reordering the JSON keys must NOT change the DID (DAG-CBOR canonicalizes). | |
| 28 | const reordered = { | |
| 29 | sig: golden.sig, | |
| 30 | prev: golden.prev, | |
| 31 | services: golden.services, | |
| 32 | alsoKnownAs: golden.alsoKnownAs, | |
| 33 | verificationMethods: golden.verificationMethods, | |
| 34 | rotationKeys: golden.rotationKeys, | |
| 35 | type: golden.type | |
| 36 | } | |
| 37 | t.is(didForCreateOp(reordered), EXPECTED, 'DID invariant under key reordering') | |
| 38 | }) |
| … | … | @@ -0,0 +1,16 @@ |
| 1 | { | |
| 2 | "type": "plc_operation", | |
| 3 | "rotationKeys": ["did:key:zDnaeWUTD9gF8QMQ9WDh5t12ojkohiKGWUCrCnAGvu1CSJpyj"], | |
| 4 | "verificationMethods": { | |
| 5 | "atproto": "did:key:zDnaepXNQybJv5o8DM5AdmbpnedfApGv58q2UiDyhMgP5SFHx" | |
| 6 | }, | |
| 7 | "alsoKnownAs": ["at://bridgetest.7rnx.net"], | |
| 8 | "services": { | |
| 9 | "atproto_pds": { | |
| 10 | "type": "AtprotoPersonalDataServer", | |
| 11 | "endpoint": "https://pds.7rnx.net" | |
| 12 | } | |
| 13 | }, | |
| 14 | "prev": null, | |
| 15 | "sig": "fN81wwBBSdc7VjydAmv_1aM5qn747LyVtzxj0IVWBOlRgxJ7gSTwgsJVgRBzHiUsu_zIRIiUMMYheMp3RFhYcw" | |
| 16 | } |