A zero-dependency AT Protocol Personal Data Server written in JavaScript
atproto pds

feat: add P-256 key generation to setup script

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

Changed files
+111 -7
scripts
+111 -7
scripts/setup.js
··· 48 48 return opts 49 49 } 50 50 51 + // === KEY GENERATION === 52 + 53 + async function generateP256Keypair() { 54 + const keyPair = await webcrypto.subtle.generateKey( 55 + { name: 'ECDSA', namedCurve: 'P-256' }, 56 + true, 57 + ['sign', 'verify'] 58 + ) 59 + 60 + // Export private key as raw 32 bytes 61 + const privateJwk = await webcrypto.subtle.exportKey('jwk', keyPair.privateKey) 62 + const privateBytes = base64UrlDecode(privateJwk.d) 63 + 64 + // Export public key as uncompressed point (65 bytes) 65 + const publicRaw = await webcrypto.subtle.exportKey('raw', keyPair.publicKey) 66 + const publicBytes = new Uint8Array(publicRaw) 67 + 68 + // Compress public key to 33 bytes 69 + const compressedPublic = compressPublicKey(publicBytes) 70 + 71 + return { 72 + privateKey: privateBytes, 73 + publicKey: compressedPublic, 74 + cryptoKey: keyPair.privateKey 75 + } 76 + } 77 + 78 + function compressPublicKey(uncompressed) { 79 + // uncompressed is 65 bytes: 0x04 + x(32) + y(32) 80 + const x = uncompressed.slice(1, 33) 81 + const y = uncompressed.slice(33, 65) 82 + const prefix = (y[31] & 1) === 0 ? 0x02 : 0x03 83 + const compressed = new Uint8Array(33) 84 + compressed[0] = prefix 85 + compressed.set(x, 1) 86 + return compressed 87 + } 88 + 89 + function base64UrlDecode(str) { 90 + const base64 = str.replace(/-/g, '+').replace(/_/g, '/') 91 + const binary = atob(base64) 92 + const bytes = new Uint8Array(binary.length) 93 + for (let i = 0; i < binary.length; i++) { 94 + bytes[i] = binary.charCodeAt(i) 95 + } 96 + return bytes 97 + } 98 + 99 + function bytesToHex(bytes) { 100 + return Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('') 101 + } 102 + 103 + // === DID:KEY ENCODING === 104 + 105 + // Multicodec prefix for P-256 public key (0x1200) 106 + const P256_MULTICODEC = new Uint8Array([0x80, 0x24]) 107 + 108 + function publicKeyToDidKey(compressedPublicKey) { 109 + // did:key format: "did:key:" + multibase(base58btc) of multicodec + key 110 + const keyWithCodec = new Uint8Array(P256_MULTICODEC.length + compressedPublicKey.length) 111 + keyWithCodec.set(P256_MULTICODEC) 112 + keyWithCodec.set(compressedPublicKey, P256_MULTICODEC.length) 113 + 114 + return 'did:key:z' + base58btcEncode(keyWithCodec) 115 + } 116 + 117 + function base58btcEncode(bytes) { 118 + const ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' 119 + 120 + // Count leading zeros 121 + let zeros = 0 122 + for (const b of bytes) { 123 + if (b === 0) zeros++ 124 + else break 125 + } 126 + 127 + // Convert to base58 128 + const digits = [0] 129 + for (const byte of bytes) { 130 + let carry = byte 131 + for (let i = 0; i < digits.length; i++) { 132 + carry += digits[i] << 8 133 + digits[i] = carry % 58 134 + carry = (carry / 58) | 0 135 + } 136 + while (carry > 0) { 137 + digits.push(carry % 58) 138 + carry = (carry / 58) | 0 139 + } 140 + } 141 + 142 + // Convert to string 143 + let result = '1'.repeat(zeros) 144 + for (let i = digits.length - 1; i >= 0; i--) { 145 + result += ALPHABET[digits[i]] 146 + } 147 + 148 + return result 149 + } 150 + 51 151 // === MAIN === 52 152 53 153 async function main() { ··· 57 157 console.log('====================') 58 158 console.log(`Handle: ${opts.handle}`) 59 159 console.log(`PDS: ${opts.pds}`) 60 - console.log(`PLC: ${opts.plcUrl}`) 61 - console.log(`Relay: ${opts.relayUrl}`) 160 + console.log('') 161 + 162 + // Step 1: Generate keypair 163 + console.log('Generating P-256 keypair...') 164 + const keyPair = await generateP256Keypair() 165 + const didKey = publicKeyToDidKey(keyPair.publicKey) 166 + console.log(` did:key: ${didKey}`) 167 + console.log(` Private key: ${bytesToHex(keyPair.privateKey)}`) 62 168 console.log('') 63 169 64 - // TODO: Implement in subsequent tasks 65 - console.log('TODO: Generate keypair') 66 - console.log('TODO: Register DID:PLC') 67 - console.log('TODO: Initialize PDS') 68 - console.log('TODO: Notify relay') 170 + // TODO: Register DID:PLC 171 + // TODO: Initialize PDS 172 + // TODO: Notify relay 69 173 } 70 174 71 175 main().catch(err => {