decentralised message store
at main 3.4 kB view raw
1import { __DEV__, SERVER_PORT, SERVICE_DID } from "@/lib/env"; 2import { 3 didWebSchema, 4 type DidDocument, 5 type DidWeb, 6 type VerificationMethod, 7} from "@/lib/types/atproto"; 8import { Secp256k1PrivateKeyExportable } from "@atcute/crypto"; 9import { toString as uint8arraysToString } from "uint8arrays"; 10 11export interface ServiceKeys { 12 atproto: Secp256k1PrivateKeyExportable; 13 service: Secp256k1PrivateKeyExportable; 14} 15 16export interface CreateDidWebDocResult { 17 didDoc: DidDocument; 18 keys: ServiceKeys; 19} 20 21const buildDidWebDoc = async ( 22 didWeb: DidWeb, 23): Promise<CreateDidWebDocResult> => { 24 const atprotoKey = await Secp256k1PrivateKeyExportable.createKeypair(); 25 const serviceKey = await Secp256k1PrivateKeyExportable.createKeypair(); 26 27 const atprotoMultikey = encodeMultikey( 28 await atprotoKey.exportPublicKey("raw"), 29 ); 30 const serviceMultikey = encodeMultikey( 31 await atprotoKey.exportPublicKey("raw"), 32 ); 33 34 const { domain, serviceEndpoint } = extractInfoFromDidWeb(didWeb); 35 36 const verificationMethod: Array<VerificationMethod> = [ 37 { 38 id: `${didWeb}#atproto`, 39 type: "Multikey", 40 controller: didWeb, 41 publicKeyMultibase: atprotoMultikey, 42 }, 43 ]; 44 45 const didDoc: DidDocument = { 46 "@context": [ 47 "https://www.w3.org/ns/did/v1", 48 "https://w3id.org/security/multikey/v1", 49 "https://w3id.org/security/suites/secp256k1-2019/v1", 50 ], 51 id: didWeb, 52 verificationMethod, 53 }; 54 55 if (serviceEndpoint) { 56 const serviceEndpointType = "GemstoneShard"; 57 58 const serviceEndpointUrl = `https://${domain}/`; 59 60 // @ts-expect-error we are already adding the verificationMethod array above when we create didDoc. 61 didDoc.verificationMethod.push({ 62 id: `${didWeb}#${serviceEndpoint}`, 63 type: "Multikey", 64 controller: didWeb, 65 publicKeyMultibase: serviceMultikey, 66 }); 67 68 didDoc.service = [ 69 { 70 id: `${didWeb}#${serviceEndpoint}`, 71 type: serviceEndpointType, 72 serviceEndpoint: serviceEndpointUrl, 73 }, 74 ]; 75 } 76 77 return { 78 didDoc, 79 keys: { 80 atproto: atprotoKey, 81 service: serviceKey, 82 }, 83 }; 84}; 85 86const encodeMultikey = (publicKeyBytes: Uint8Array) => { 87 // For secp256k1 (K-256), prefix with 0xE701 88 const prefixed = new Uint8Array(publicKeyBytes.length + 2); 89 prefixed[0] = 0xe7; 90 prefixed[1] = 0x01; 91 prefixed.set(publicKeyBytes, 2); 92 93 // Base58-btc encode with 'z' prefix 94 const value = uint8arraysToString(prefixed, "base58btc"); 95 96 return "z" + value; 97}; 98 99const extractInfoFromDidWeb = (didWeb: DidWeb) => { 100 const fragments = didWeb.split("#"); 101 return { 102 domain: fragments[0].replace("did:web:", ""), 103 serviceEndpoint: fragments[1] as string | undefined, 104 }; 105}; 106 107const createDidWebDoc = async () => { 108 let did = SERVICE_DID; 109 if (__DEV__) { 110 did = `${did}%3A${SERVER_PORT.toString()}`; 111 } 112 const { success: isDidWeb, data: didWeb } = didWebSchema.safeParse(did); 113 if (!isDidWeb) return; 114 const { didDoc } = await buildDidWebDoc(didWeb); 115 return didDoc; 116}; 117 118export const didDoc = await createDidWebDoc();