this repo has no description

docs: add 'why' comments to protocol-specific logic

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

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

Changed files
+9 -3
src
+9 -3
src/pds.js
··· 118 encodeHead(parts, 3, bytes.length) 119 parts.push(...bytes) 120 } else if (val instanceof CID) { 121 - // CID - encode with CBOR tag 42 + 0x00 prefix 122 parts.push(0xd8, CBOR_TAG_CID) 123 encodeHead(parts, 2, val.bytes.length + 1) // +1 for 0x00 prefix 124 - parts.push(0x00) // multibase identity prefix 125 parts.push(...val.bytes) 126 } else if (val instanceof Uint8Array) { 127 // Regular byte string ··· 132 for (const item of val) encode(item) 133 } else if (typeof val === 'object') { 134 // DAG-CBOR: sort keys by length first, then lexicographically 135 const keys = Object.keys(val).filter(k => val[k] !== undefined) 136 keys.sort((a, b) => { 137 if (a.length !== b.length) return a.length - b.length ··· 378 ) 379 const sig = new Uint8Array(signature) 380 381 - // Low-S normalization: if S > N/2, replace S with N - S 382 const r = sig.slice(0, 32) 383 const s = sig.slice(32, 64) 384 const sBigInt = bytesToBigInt(s) 385 386 if (sBigInt > P256_N_DIV_2) { 387 const newS = P256_N - sBigInt 388 const newSBytes = bigIntToBytes(newS, 32) ··· 499 } 500 } 501 502 const depth = Math.floor(zeros / 2) 503 keyDepthCache.set(key, depth) 504 return depth
··· 118 encodeHead(parts, 3, bytes.length) 119 parts.push(...bytes) 120 } else if (val instanceof CID) { 121 + // CID links in DAG-CBOR use tag 42 + 0x00 multibase prefix 122 + // The 0x00 prefix indicates "identity" multibase (raw bytes) 123 parts.push(0xd8, CBOR_TAG_CID) 124 encodeHead(parts, 2, val.bytes.length + 1) // +1 for 0x00 prefix 125 + parts.push(0x00) 126 parts.push(...val.bytes) 127 } else if (val instanceof Uint8Array) { 128 // Regular byte string ··· 133 for (const item of val) encode(item) 134 } else if (typeof val === 'object') { 135 // DAG-CBOR: sort keys by length first, then lexicographically 136 + // (differs from standard CBOR which sorts lexicographically only) 137 const keys = Object.keys(val).filter(k => val[k] !== undefined) 138 keys.sort((a, b) => { 139 if (a.length !== b.length) return a.length - b.length ··· 380 ) 381 const sig = new Uint8Array(signature) 382 383 const r = sig.slice(0, 32) 384 const s = sig.slice(32, 64) 385 const sBigInt = bytesToBigInt(s) 386 387 + // Low-S normalization: Bitcoin/ATProto require S <= N/2 to prevent 388 + // signature malleability (two valid signatures for same message) 389 if (sBigInt > P256_N_DIV_2) { 390 const newS = P256_N - sBigInt 391 const newSBytes = bigIntToBytes(newS, 32) ··· 502 } 503 } 504 505 + // MST depth = leading zeros in SHA-256 hash / 2 506 + // This creates a probabilistic tree where ~50% of keys are at depth 0, 507 + // ~25% at depth 1, etc., giving O(log n) lookups 508 const depth = Math.floor(zeros / 2) 509 keyDepthCache.set(key, depth) 510 return depth