this repo has no description sites.wisp.place/zzstoatzz.io/pds-message-poc
pds messaging
at main 116 lines 3.7 kB view raw
1/** 2 * Script to create a labeler DID on plc.directory 3 * 4 * Run with: node scripts/create-did.js <labeler-endpoint> 5 * Example: node scripts/create-did.js https://spam-labeler.nate-8fe.workers.dev 6 */ 7 8import { secp256k1 } from '@noble/curves/secp256k1'; 9import { sha256 } from '@noble/hashes/sha256'; 10import * as cbor from '@ipld/dag-cbor'; 11import { base32 } from 'multiformats/bases/base32'; 12import * as uint8arrays from 'uint8arrays'; 13 14const PLC_DIRECTORY = 'https://plc.directory'; 15 16// Generate a new secp256k1 keypair 17function generateKeypair() { 18 const privateKey = secp256k1.utils.randomPrivateKey(); 19 const publicKey = secp256k1.getPublicKey(privateKey, true); // compressed 20 return { privateKey, publicKey }; 21} 22 23// Convert public key to did:key format for secp256k1 24function publicKeyToDidKey(publicKey) { 25 // secp256k1 multicodec prefix is 0xe7 0x01 26 const multicodec = new Uint8Array([0xe7, 0x01, ...publicKey]); 27 const encoded = uint8arrays.toString(multicodec, 'base58btc'); 28 return `did:key:z${encoded}`; 29} 30 31// Sign an operation with secp256k1 32async function signOperation(unsigned, privateKey) { 33 const bytes = cbor.encode(unsigned); 34 const hash = sha256(bytes); 35 const sig = secp256k1.sign(hash, privateKey, { lowS: true }); 36 const sigBytes = sig.toCompactRawBytes(); 37 const sigB64 = uint8arrays.toString(sigBytes, 'base64url'); 38 return { ...unsigned, sig: sigB64 }; 39} 40 41// Compute DID from genesis operation 42async function didForCreateOp(op) { 43 const bytes = cbor.encode(op); 44 const hash = sha256(bytes); 45 const hashB32 = uint8arrays.toString(hash, 'base32'); 46 const truncated = hashB32.slice(0, 24); 47 return `did:plc:${truncated}`; 48} 49 50async function main() { 51 const endpoint = process.argv[2]; 52 if (!endpoint) { 53 console.error('Usage: node scripts/create-did.js <labeler-endpoint>'); 54 console.error('Example: node scripts/create-did.js https://spam-labeler.nate-8fe.workers.dev'); 55 process.exit(1); 56 } 57 58 console.log('Generating secp256k1 keypair...'); 59 const { privateKey, publicKey } = generateKeypair(); 60 const didKey = publicKeyToDidKey(publicKey); 61 62 console.log('Creating labeler DID operation...'); 63 const unsigned = { 64 type: 'plc_operation', 65 alsoKnownAs: [], 66 rotationKeys: [didKey], 67 verificationMethods: { 68 atproto_label: didKey, 69 }, 70 services: { 71 atproto_labeler: { 72 type: 'AtprotoLabeler', 73 endpoint: endpoint, 74 }, 75 }, 76 prev: null, 77 }; 78 79 const signed = await signOperation(unsigned, privateKey); 80 const did = await didForCreateOp(signed); 81 82 console.log('\n=== LABELER CREDENTIALS ===\n'); 83 console.log('DID:', did); 84 console.log('Private Key (hex):', uint8arrays.toString(privateKey, 'hex')); 85 console.log('Public Key (did:key):', didKey); 86 console.log('\nEndpoint:', endpoint); 87 88 console.log('\n=== SUBMITTING TO PLC DIRECTORY ===\n'); 89 90 try { 91 const res = await fetch(`${PLC_DIRECTORY}/${did}`, { 92 method: 'POST', 93 headers: { 'Content-Type': 'application/json' }, 94 body: JSON.stringify(signed), 95 }); 96 97 if (res.ok) { 98 console.log('SUCCESS! DID created on plc.directory'); 99 console.log('\nVerify at:', `${PLC_DIRECTORY}/${did}`); 100 101 console.log('\n=== NEXT STEPS ===\n'); 102 console.log('1. Add to wrangler.toml:'); 103 console.log(` LABELER_DID = "${did}"`); 104 console.log('\n2. Set the secret:'); 105 console.log(` wrangler secret put SIGNING_KEY`); 106 console.log(` (paste the private key hex when prompted)`); 107 } else { 108 const text = await res.text(); 109 console.error('Failed to create DID:', res.status, text); 110 } 111 } catch (err) { 112 console.error('Error:', err.message); 113 } 114} 115 116main();