this repo has no description
1#!/usr/bin/env node 2 3/** 4 * PDS Setup Script 5 * 6 * Registers a did:plc, initializes the PDS, and notifies the relay. 7 * Zero dependencies - uses Node.js built-ins only. 8 * 9 * Usage: node scripts/setup.js --handle alice --pds https://your-pds.workers.dev 10 */ 11 12import { webcrypto } from 'crypto' 13 14// === ARGUMENT PARSING === 15 16function parseArgs() { 17 const args = process.argv.slice(2) 18 const opts = { 19 handle: null, 20 pds: null, 21 plcUrl: 'https://plc.directory', 22 relayUrl: 'https://bsky.network' 23 } 24 25 for (let i = 0; i < args.length; i++) { 26 if (args[i] === '--handle' && args[i + 1]) { 27 opts.handle = args[++i] 28 } else if (args[i] === '--pds' && args[i + 1]) { 29 opts.pds = args[++i] 30 } else if (args[i] === '--plc-url' && args[i + 1]) { 31 opts.plcUrl = args[++i] 32 } else if (args[i] === '--relay-url' && args[i + 1]) { 33 opts.relayUrl = args[++i] 34 } 35 } 36 37 if (!opts.handle || !opts.pds) { 38 console.error('Usage: node scripts/setup.js --handle <handle> --pds <pds-url>') 39 console.error('') 40 console.error('Options:') 41 console.error(' --handle Handle name (e.g., "alice")') 42 console.error(' --pds PDS URL (e.g., "https://atproto-pds.chad-53c.workers.dev")') 43 console.error(' --plc-url PLC directory URL (default: https://plc.directory)') 44 console.error(' --relay-url Relay URL (default: https://bsky.network)') 45 process.exit(1) 46 } 47 48 return opts 49} 50 51// === KEY GENERATION === 52 53async 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 78function 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 89function 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 99function 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) 106const P256_MULTICODEC = new Uint8Array([0x80, 0x24]) 107 108function 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 117function 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 151// === MAIN === 152 153async function main() { 154 const opts = parseArgs() 155 156 console.log('PDS Federation Setup') 157 console.log('====================') 158 console.log(`Handle: ${opts.handle}`) 159 console.log(`PDS: ${opts.pds}`) 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)}`) 168 console.log('') 169 170 // TODO: Register DID:PLC 171 // TODO: Initialize PDS 172 // TODO: Notify relay 173} 174 175main().catch(err => { 176 console.error('Error:', err.message) 177 process.exit(1) 178})