@ghostlytools/cli 1.0.3 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/README.md +1 -1
  2. package/dist/assets/bark_ffi_wasm_bg-EY2uc0Bx.js.map +1 -1
  3. package/dist/assets/bitcoindevkit_bg-B-iLWrmX.js.map +1 -1
  4. package/dist/assets/{fileBytes-CpP9Vj7s.js → fileBytes-BQ84L9Fe.js} +2 -2
  5. package/dist/assets/{fileBytes-CpP9Vj7s.js.map → fileBytes-BQ84L9Fe.js.map} +1 -1
  6. package/dist/assets/{fileBytes-Br_MpeNT.js → fileBytes-DxiJHmTk.js} +3 -3
  7. package/dist/assets/fileBytes-DxiJHmTk.js.map +1 -0
  8. package/dist/assets/{fileBytesIdb-vubSXPoO.js → fileBytesIdb-DQkQ10PS.js} +3 -3
  9. package/dist/assets/{fileBytesIdb-vubSXPoO.js.map → fileBytesIdb-DQkQ10PS.js.map} +1 -1
  10. package/dist/assets/{fileBytesOpfs-6iSa6u2K.js → fileBytesOpfs-BFfR_68b.js} +3 -3
  11. package/dist/assets/{fileBytesOpfs-6iSa6u2K.js.map → fileBytesOpfs-BFfR_68b.js.map} +1 -1
  12. package/dist/assets/{ghostly_iroh_web-CPa_YEfb.js → ghostly_iroh_web-Dvzh3Y6E.js} +32 -32
  13. package/dist/assets/{ghostly_iroh_web-CPa_YEfb.js.map → ghostly_iroh_web-Dvzh3Y6E.js.map} +1 -1
  14. package/dist/assets/ghostly_iroh_web_bg.wasm +0 -0
  15. package/dist/assets/{hyperdhtRelay-VM9STKLd.js → hyperdhtRelay-CVLCuqVr.js} +3 -3
  16. package/dist/assets/{hyperdhtRelay-VM9STKLd.js.map → hyperdhtRelay-CVLCuqVr.js.map} +1 -1
  17. package/dist/assets/{idb-DIN_Miwb.js → idb-Co2lObpU.js} +4 -4
  18. package/dist/assets/idb-Co2lObpU.js.map +1 -0
  19. package/dist/assets/{mainline-CcO2f8jy.js → mainline-CKFpwXcB.js} +2 -2
  20. package/dist/assets/{mainline-CcO2f8jy.js.map → mainline-CKFpwXcB.js.map} +1 -1
  21. package/dist/assets/{restoredRows-tVxgyG23.js → restoredRows-C1O9FfA4.js} +38 -3
  22. package/dist/assets/restoredRows-C1O9FfA4.js.map +1 -0
  23. package/dist/assets/{server-BnNXYzR9.js → server-vGoe9MET.js} +9460 -784
  24. package/dist/assets/server-vGoe9MET.js.map +1 -0
  25. package/dist/assets/{src-7sdQpCoi.js → src-CJdPTl7Q.js} +4216 -1305
  26. package/dist/assets/src-CJdPTl7Q.js.map +1 -0
  27. package/dist/ghostly.mjs +374 -45
  28. package/dist/ghostly.mjs.map +1 -1
  29. package/package.json +2 -1
  30. package/dist/assets/fileBytes-Br_MpeNT.js.map +0 -1
  31. package/dist/assets/idb-DIN_Miwb.js.map +0 -1
  32. package/dist/assets/restoredRows-tVxgyG23.js.map +0 -1
  33. package/dist/assets/server-BnNXYzR9.js.map +0 -1
  34. package/dist/assets/src-7sdQpCoi.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"src-7sdQpCoi.js","names":[],"sources":["../../../core/src/bytes.ts","../../../core/src/text.ts","../../../core/src/avatar.ts","../../../core/src/crypto.ts","../../../core/src/identity.ts","../../../core/src/dns.ts","../../../core/src/pkarr.ts","../../../core/src/transport.ts","../../../core/src/relayBreaker.ts","../../../core/src/linkTrace.ts","../../../core/src/relay.ts","../../../core/src/invite.ts","../../../core/src/services.ts","../../../core/src/records.ts","../../../core/src/callSignal.ts","../../../core/src/signal.ts","../../../core/src/frames.ts","../../../core/src/http.ts","../../../core/src/link.ts","../../../core/src/directPath.ts","../../../core/src/datalink.ts","../../../core/src/groupCrypto.ts","../../../core/src/groupCommits.ts","../../../core/src/messageTime.ts","../../../core/src/statusCards.ts","../../../core/src/replies.ts","../../../core/src/reactions.ts","../../../core/src/forwards.ts","../../../core/src/linkPreview.ts","../../../core/src/pairedEdits.ts","../../../core/src/dhtDelivery.ts","../../../core/src/video.ts","../../../core/src/voice.ts","../../../core/src/files.ts","../../../core/src/image.ts","../../../core/src/pairedFiles.ts","../../../core/src/pairedTransports.ts","../../../core/src/transportSwitch.ts","../../../core/src/pubkyRing.ts","../../../core/src/peerProofs.ts","../../../core/src/identityTimeline.ts","../../../core/src/identityProofs.ts","../../../core/src/capsRecord.ts","../../../core/src/pairedSignal.ts","../../../core/src/storeForward.ts","../../../core/src/pairedSession.ts","../../../core/src/pairedHttp.ts","../../../core/src/pairedCapabilities.ts","../../../core/src/webPush.ts","../../../core/src/pairedWake.ts","../../../core/src/pins.ts","../../../core/src/pairedTyping.ts","../../../core/src/chatFiles.ts","../../../core/src/pairedCalls.ts","../../../core/src/communityRendezvous.ts","../../../core/src/groupHubs.ts","../../../core/src/pairingProgress.ts","../../../core/src/ghostlink.ts","../../../core/src/payments.ts","../../../core/src/bolt11.ts","../../../core/src/engineErrors.ts","../../../core/src/lnurl.ts","../../../core/src/domainProofs.ts","../../../core/src/pkdns.ts","../../../core/src/pubkyProofs.ts","../../../core/src/bitcoinAddress.ts","../../../core/src/sparkAddress.ts","../../../core/src/paymentIntent.ts","../../../core/src/didDht.ts","../../../core/src/did.ts","../../../core/src/atprotoRepo.ts","../../../core/src/atprotoIdentity.ts","../../../core/src/bitcoinScript.ts","../../../core/src/bip322.ts","../../../core/src/bitcoinMessage.ts","../../../core/src/sshsig.ts","../../../core/src/groupMeta.ts","../../../core/src/groupMentions.ts","../../../core/src/groupEdits.ts","../../../core/src/groupSession.ts","../../../core/src/groupWake.ts","../../../core/src/groupEntry.ts","../../../core/src/groupCommunity.ts","../../../core/src/groupTyping.ts"],"sourcesContent":["const textEncoder = new TextEncoder();\nconst textDecoder = new TextDecoder(\"utf-8\", { fatal: true });\n\nexport function utf8Encode(str: string): Uint8Array {\n return textEncoder.encode(str);\n}\n\nexport function utf8Decode(bytes: Uint8Array): string {\n return textDecoder.decode(bytes);\n}\n\nexport function concatBytes(...parts: Uint8Array[]): Uint8Array {\n let length = 0;\n for (const p of parts) length += p.length;\n const out = new Uint8Array(length);\n let offset = 0;\n for (const p of parts) {\n out.set(p, offset);\n offset += p.length;\n }\n return out;\n}\n\nexport function bytesEqual(a: Uint8Array, b: Uint8Array): boolean {\n if (a.length !== b.length) return false;\n let diff = 0;\n for (let i = 0; i < a.length; i++) diff |= a[i] ^ b[i];\n return diff === 0;\n}\n\nexport function toBase64(bytes: Uint8Array): string {\n let binary = \"\";\n const CHUNK = 0x8000;\n for (let i = 0; i < bytes.length; i += CHUNK) {\n binary += String.fromCharCode(...bytes.subarray(i, i + CHUNK));\n }\n return btoa(binary);\n}\n\nexport function fromBase64(str: string): Uint8Array {\n const binary = atob(str);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);\n return bytes;\n}\n\nexport function toBase64Url(bytes: Uint8Array): string {\n return toBase64(bytes).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\n\nexport function fromBase64Url(str: string): Uint8Array {\n let b = str.replace(/-/g, \"+\").replace(/_/g, \"/\");\n while (b.length % 4) b += \"=\";\n return fromBase64(b);\n}\n\nconst Z32_ALPHABET = \"ybndrfg8ejkmcpqxot1uwisza345h769\";\n\nexport function toZ32(bytes: Uint8Array): string {\n let out = \"\";\n let bits = 0;\n let value = 0;\n for (const byte of bytes) {\n value = (value << 8) | byte;\n bits += 8;\n while (bits >= 5) {\n out += Z32_ALPHABET[(value >>> (bits - 5)) & 31];\n bits -= 5;\n }\n value &= (1 << bits) - 1;\n }\n if (bits > 0) out += Z32_ALPHABET[(value << (5 - bits)) & 31];\n return out;\n}\n\nexport function fromZ32(str: string): Uint8Array {\n const out = new Uint8Array(Math.floor((str.length * 5) / 8));\n let bits = 0;\n let value = 0;\n let index = 0;\n for (const char of str) {\n const v = Z32_ALPHABET.indexOf(char);\n if (v < 0) throw new Error(`Invalid z-base-32 character: ${char}`);\n value = (value << 5) | v;\n bits += 5;\n if (bits >= 8) {\n out[index++] = (value >>> (bits - 8)) & 0xff;\n bits -= 8;\n }\n value &= (1 << bits) - 1;\n }\n return out;\n}\n\nexport function randomBytes(length: number): Uint8Array {\n const bytes = new Uint8Array(length);\n crypto.getRandomValues(bytes);\n return bytes;\n}\n","/**\n * Display text a peer chooses: nicknames and service names. It is shown next\n * to the peer's messages and in the chat header, so it must not be able to\n * read as something it is not, and it must not be able to grow without bound.\n */\n\n/** Longest nickname kept from a peer. The `hello` frame has always cut here. */\nexport const MAX_NICK_LENGTH = 64;\n\n/**\n * Invisible and direction-changing characters. Unlike a file name, display\n * text may legitimately hold emoji and Indic or Arabic joiners, so the two\n * joiners stay and the rest of Cf goes: bidi overrides and isolates reorder\n * what follows them, and the others hide text or let two different names\n * render identically.\n */\nconst HIDDEN = /[\\p{Cc}\\p{Cf}\\p{Zl}\\p{Zp}]/gu;\n/** Zero-width non-joiner and zero-width joiner. */\nconst KEEP = /[\\u200c\\u200d]/;\n\n/** Undefined when nothing visible is left, so callers can fall back. */\nexport function sanitizeDisplayText(text: string, max: number): string | undefined {\n const visible = text.replace(HIDDEN, (char) => (KEEP.test(char) ? char : \"\"));\n const clean = [...visible].slice(0, max).join(\"\").trim();\n return clean || undefined;\n}\n\n/** A nickname as it arrived from a peer, ready to show. */\nexport function sanitizeNick(nick: unknown): string | undefined {\n return typeof nick === \"string\" ? sanitizeDisplayText(nick, MAX_NICK_LENGTH) : undefined;\n}\n","/**\n * A profile picture as it travels between paired peers: a small square JPEG in a data URL. The picture\n * a contact sends is shown next to their chats, so it is checked here before anything draws it: the\n * format, the size, and the dimensions its header declares (a tiny file can claim a huge image).\n */\n\n/** Longest data URL kept: a 128×128 JPEG is a few KB; this leaves room and no more. */\nexport const MAX_AVATAR_LENGTH = 48_000;\n/** Largest side a picture may declare. What Ghostly sends is 128. */\nexport const MAX_AVATAR_SIDE = 512;\nconst PREFIX = \"data:image/jpeg;base64,\";\n\n/** Width and height from a JPEG's frame header, or undefined when there is none before the image data. */\nexport function jpegSize(bytes: Uint8Array): { width: number; height: number } | undefined {\n if (bytes.length < 4 || bytes[0] !== 0xff || bytes[1] !== 0xd8) return;\n let i = 2;\n while (i + 4 <= bytes.length) {\n if (bytes[i] !== 0xff) return;\n const marker = bytes[i + 1];\n i += 2;\n if (marker === 0xd8 || (marker >= 0xd0 && marker <= 0xd7) || marker === 0x01) continue;\n if (marker === 0xd9 || marker === 0xda) return;\n const size = (bytes[i] << 8) | bytes[i + 1];\n if (size < 2 || i + size > bytes.length) return;\n // Start-of-frame markers (baseline, progressive, …), not DHT/JPG/DAC.\n if (marker >= 0xc0 && marker <= 0xcf && marker !== 0xc4 && marker !== 0xc8 && marker !== 0xcc && size >= 7)\n return { height: (bytes[i + 3] << 8) | bytes[i + 4], width: (bytes[i + 5] << 8) | bytes[i + 6] };\n i += size;\n }\n}\n\n/**\n * The picture if it is one Ghostly will show, `null` for \"no picture\" (an empty string), and undefined\n * for anything else, which is ignored.\n */\nexport function sanitizeAvatar(value: unknown): string | null | undefined {\n if (value === \"\" || value === null) return null;\n if (typeof value !== \"string\" || value.length > MAX_AVATAR_LENGTH || !value.startsWith(PREFIX)) return;\n const data = value.slice(PREFIX.length);\n if (!/^[A-Za-z0-9+/]+={0,2}$/.test(data) || data.length % 4 !== 0) return;\n let binary: string;\n try { binary = atob(data); } catch { return; }\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);\n const size = jpegSize(bytes);\n if (!size || size.width < 1 || size.height < 1 || size.width > MAX_AVATAR_SIDE || size.height > MAX_AVATAR_SIDE) return;\n return value;\n}\n","import { xsalsa20poly1305 } from \"@noble/ciphers/salsa.js\";\nimport { concatBytes, fromBase64, randomBytes, toBase64, utf8Decode, utf8Encode } from \"./bytes\";\n\nconst NONCE_LENGTH = 24;\nconst KEY_LENGTH = 32;\n\nexport function generateEncryptionKey(): Uint8Array {\n return randomBytes(KEY_LENGTH);\n}\n\n/**\n * NaCl secretbox (XSalsa20-Poly1305). Wire format matches the Rust\n * implementation in `apps/desktop/src/crypto.rs`: base64(nonce || box).\n */\nexport function encrypt(plaintext: string, key: Uint8Array): string {\n if (key.length !== KEY_LENGTH) throw new Error(\"Invalid key length: expected 32 bytes\");\n const nonce = randomBytes(NONCE_LENGTH);\n const box = xsalsa20poly1305(key, nonce).encrypt(utf8Encode(plaintext));\n return toBase64(concatBytes(nonce, box));\n}\n\n/** The length of what `encrypt` returns for a plaintext of this many UTF-8 bytes: base64 of nonce, box and 16-byte tag. */\nexport function sealedLength(plaintextBytes: number): number {\n return 4 * Math.ceil((NONCE_LENGTH + 16 + plaintextBytes) / 3);\n}\n\nexport function decrypt(encoded: string, key: Uint8Array): string {\n if (key.length !== KEY_LENGTH) throw new Error(\"Invalid key length: expected 32 bytes\");\n const combined = fromBase64(encoded);\n if (combined.length < NONCE_LENGTH + 1) throw new Error(\"Invalid ciphertext: too short\");\n const nonce = combined.subarray(0, NONCE_LENGTH);\n const box = combined.subarray(NONCE_LENGTH);\n return utf8Decode(xsalsa20poly1305(key, nonce).decrypt(box));\n}\n\nexport function tryDecrypt(encoded: string, key: Uint8Array): string | null {\n try {\n return decrypt(encoded, key);\n } catch {\n return null;\n }\n}\n","import { ed25519 } from \"@noble/curves/ed25519.js\";\nimport { fromBase64Url, fromZ32, randomBytes, toBase64Url, toZ32 } from \"./bytes\";\n\n/**\n * A Ghostly identity is an Ed25519 keypair. The public key, z-base-32 encoded,\n * is the peer's Pkarr address. Today every link (chat) uses its own identity,\n * exactly like Ghostly Desktop.\n */\nexport interface Identity {\n seed: Uint8Array;\n seedB64: string;\n publicKey: Uint8Array;\n pubKeyZ32: string;\n}\n\nexport function identityFromSeed(seed: Uint8Array): Identity {\n if (seed.length !== 32) throw new Error(\"Seed must be exactly 32 bytes\");\n const publicKey = ed25519.getPublicKey(seed);\n return {\n seed,\n seedB64: toBase64Url(seed),\n publicKey,\n pubKeyZ32: toZ32(publicKey),\n };\n}\n\nexport function identityFromSeedB64(seedB64: string): Identity {\n return identityFromSeed(fromBase64Url(seedB64));\n}\n\nexport function createIdentity(): Identity {\n return identityFromSeed(randomBytes(32));\n}\n\nexport function publicKeyFromZ32(z32: string): Uint8Array {\n if (z32.length !== 52) throw new Error(\"Invalid public key: expected 52 z-base-32 characters\");\n const bytes = fromZ32(z32);\n if (bytes.length !== 32) throw new Error(\"Invalid public key length\");\n return bytes;\n}\n\nexport function sign(message: Uint8Array, seed: Uint8Array): Uint8Array {\n return ed25519.sign(message, seed);\n}\n\nexport function verify(signature: Uint8Array, message: Uint8Array, publicKey: Uint8Array): boolean {\n try {\n return ed25519.verify(signature, message, publicKey);\n } catch {\n return false;\n }\n}\n","import { concatBytes, utf8Decode, utf8Encode } from \"./bytes\";\n\n/**\n * Minimal DNS wire codec for Pkarr packets. Ghostly publishes TXT records (and,\n * for a did:dht document that names its gateways, NS). Decoding skips every\n * other record type and understands name compression, which the Rust client\n * (simple-dns) emits. `decodeDnsAnswers` hands out any record type raw, for\n * readers of other people's records (Pubky's `_pubky` and homeserver records, pkdns.ts).\n */\nexport interface TxtRecord {\n /** Fully qualified name, e.g. `_msgs.<z32 public key>` */\n name: string;\n value: string;\n ttl: number;\n}\n\n/** A name server record: `host` is the server's name. */\nexport interface NsRecord {\n type: \"NS\";\n name: string;\n host: string;\n ttl: number;\n}\n\nconst TYPE_NS = 2;\nconst TYPE_TXT = 16;\nconst CLASS_IN = 1;\nconst FLAGS_REPLY = 0x8000;\nconst FLAG_AUTHORITATIVE = 0x0400;\nconst MAX_CHARACTER_STRING = 255;\nconst MAX_POINTER_OFFSET = 0x3fff;\n\nclass Writer {\n private chunks: Uint8Array[] = [];\n length = 0;\n\n push(bytes: Uint8Array): void {\n this.chunks.push(bytes);\n this.length += bytes.length;\n }\n\n u8(v: number): void {\n this.push(Uint8Array.of(v & 0xff));\n }\n\n u16(v: number): void {\n this.push(Uint8Array.of((v >>> 8) & 0xff, v & 0xff));\n }\n\n u32(v: number): void {\n this.push(Uint8Array.of((v >>> 24) & 0xff, (v >>> 16) & 0xff, (v >>> 8) & 0xff, v & 0xff));\n }\n\n bytes(): Uint8Array {\n return concatBytes(...this.chunks);\n }\n}\n\nfunction writeName(writer: Writer, name: string, suffixOffsets: Map<string, number>): void {\n const labels = name.split(\".\").filter((l) => l.length > 0);\n for (let i = 0; i < labels.length; i++) {\n const suffix = labels.slice(i).join(\".\");\n const pointer = suffixOffsets.get(suffix);\n if (pointer !== undefined) {\n writer.u16(0xc000 | pointer);\n return;\n }\n if (writer.length <= MAX_POINTER_OFFSET) suffixOffsets.set(suffix, writer.length);\n const label = utf8Encode(labels[i]);\n if (label.length > 63) throw new Error(`DNS label too long: ${labels[i]}`);\n writer.u8(label.length);\n writer.push(label);\n }\n writer.u8(0);\n}\n\n/** `authoritative` sets the AA flag, which a did:dht packet must carry. */\nexport function encodeTxtPacket(records: (TxtRecord | NsRecord)[], options: { authoritative?: boolean } = {}): Uint8Array {\n const writer = new Writer();\n writer.u16(0);\n writer.u16(options.authoritative ? FLAGS_REPLY | FLAG_AUTHORITATIVE : FLAGS_REPLY);\n writer.u16(0);\n writer.u16(records.length);\n writer.u16(0);\n writer.u16(0);\n\n const suffixOffsets = new Map<string, number>();\n for (const record of records) {\n writeName(writer, record.name, suffixOffsets);\n if (\"host\" in record) {\n writer.u16(TYPE_NS);\n writer.u16(CLASS_IN);\n writer.u32(record.ttl);\n // The length comes first, so the host is written on its own (uncompressed) and measured.\n const host = new Writer();\n writeName(host, record.host, new Map());\n writer.u16(host.length);\n writer.push(host.bytes());\n continue;\n }\n writer.u16(TYPE_TXT);\n writer.u16(CLASS_IN);\n writer.u32(record.ttl);\n\n const value = utf8Encode(record.value);\n const strings: Uint8Array[] = [];\n for (let i = 0; i < value.length || i === 0; i += MAX_CHARACTER_STRING) {\n const chunk = value.subarray(i, i + MAX_CHARACTER_STRING);\n strings.push(Uint8Array.of(chunk.length), chunk);\n }\n const rdata = concatBytes(...strings);\n writer.u16(rdata.length);\n writer.push(rdata);\n }\n return writer.bytes();\n}\n\n/** A name at `start` (compressed or not), and where the bytes after it begin. Labels are joined with \".\", no trailing dot. */\nexport function readDnsName(data: Uint8Array, start: number): { name: string; next: number } {\n const labels: string[] = [];\n let pos = start;\n let next = -1;\n let jumps = 0;\n\n for (;;) {\n if (pos >= data.length) throw new Error(\"DNS name out of bounds\");\n const len = data[pos];\n if ((len & 0xc0) === 0xc0) {\n if (pos + 1 >= data.length) throw new Error(\"DNS pointer out of bounds\");\n if (next < 0) next = pos + 2;\n pos = ((len & 0x3f) << 8) | data[pos + 1];\n if (++jumps > 32) throw new Error(\"DNS name has too many compression pointers\");\n continue;\n }\n if (len & 0xc0) throw new Error(\"Unsupported DNS label type\");\n pos += 1;\n if (len === 0) break;\n if (pos + len > data.length) throw new Error(\"DNS label out of bounds\");\n labels.push(utf8Decode(data.subarray(pos, pos + len)));\n pos += len;\n }\n\n return { name: labels.join(\".\"), next: next < 0 ? pos : next };\n}\n\nexport function decodeTxtPacket(data: Uint8Array): TxtRecord[] {\n if (data.length < 12) throw new Error(\"DNS packet too short\");\n const view = new DataView(data.buffer, data.byteOffset, data.byteLength);\n const questions = view.getUint16(4);\n const answers = view.getUint16(6);\n\n let pos = 12;\n for (let i = 0; i < questions; i++) {\n pos = readDnsName(data, pos).next + 4;\n }\n\n const records: TxtRecord[] = [];\n for (let i = 0; i < answers; i++) {\n const { name, next } = readDnsName(data, pos);\n pos = next;\n if (pos + 10 > data.length) throw new Error(\"DNS record out of bounds\");\n const type = view.getUint16(pos);\n const ttl = view.getUint32(pos + 4);\n const rdLength = view.getUint16(pos + 8);\n pos += 10;\n const end = pos + rdLength;\n if (end > data.length) throw new Error(\"DNS rdata out of bounds\");\n\n if (type === TYPE_TXT) {\n const parts: Uint8Array[] = [];\n let p = pos;\n while (p < end) {\n const len = data[p];\n if (p + 1 + len > end) throw new Error(\"DNS character-string out of bounds\");\n parts.push(data.subarray(p + 1, p + 1 + len));\n p += 1 + len;\n }\n try {\n records.push({ name, value: utf8Decode(concatBytes(...parts)), ttl });\n } catch {\n // not valid UTF-8, not one of ours\n }\n }\n pos = end;\n }\n return records;\n}\n\n/** One answer record, undecoded: its rdata starts at `offset` in the packet (names in it may point anywhere before). */\nexport interface RawDnsAnswer {\n name: string;\n type: number;\n ttl: number;\n /** Where the rdata starts in the packet. */\n offset: number;\n length: number;\n}\n\n/** Every answer record of a packet, of any type. Names are decompressed; the rdata is left to the caller. */\nexport function decodeDnsAnswers(data: Uint8Array): RawDnsAnswer[] {\n if (data.length < 12) throw new Error(\"DNS packet too short\");\n const view = new DataView(data.buffer, data.byteOffset, data.byteLength);\n const questions = view.getUint16(4);\n const answers = view.getUint16(6);\n let pos = 12;\n for (let i = 0; i < questions; i++) pos = readDnsName(data, pos).next + 4;\n const records: RawDnsAnswer[] = [];\n for (let i = 0; i < answers; i++) {\n const { name, next } = readDnsName(data, pos);\n pos = next;\n if (pos + 10 > data.length) throw new Error(\"DNS record out of bounds\");\n const type = view.getUint16(pos);\n const ttl = view.getUint32(pos + 4);\n const length = view.getUint16(pos + 8);\n pos += 10;\n if (pos + length > data.length) throw new Error(\"DNS rdata out of bounds\");\n records.push({ name, type, ttl, offset: pos, length });\n pos += length;\n }\n return records;\n}\n","import { concatBytes, utf8Encode } from \"./bytes\";\nimport { decodeTxtPacket, encodeTxtPacket, type TxtRecord } from \"./dns\";\nimport { publicKeyFromZ32, sign, verify, type Identity } from \"./identity\";\n\n/** Mainline DHT (BEP44) caps the value of a mutable item at 1000 bytes. */\nexport const MAX_DNS_PACKET_BYTES = 1000;\nconst SIGNATURE_LENGTH = 64;\nconst TIMESTAMP_LENGTH = 8;\n/** The largest relay payload: a 64-byte signature, an 8-byte timestamp and the largest DNS packet (1072 bytes). */\nexport const RELAY_PAYLOAD_MAX_BYTES = SIGNATURE_LENGTH + TIMESTAMP_LENGTH + MAX_DNS_PACKET_BYTES;\n\n/** A label relative to the publisher's key, e.g. `_msgs`. */\nexport interface GhostRecord {\n label: string;\n value: string;\n ttl?: number;\n}\n\nexport interface SignedPacket {\n pubKeyZ32: string;\n /** Microseconds since the UNIX epoch; doubles as the BEP44 sequence number. */\n timestampMicros: bigint;\n records: GhostRecord[];\n}\n\nexport class PacketTooLargeError extends Error {\n constructor(public readonly size: number) {\n super(`DNS packet is too large, expected max ${MAX_DNS_PACKET_BYTES} bytes but got: ${size}`);\n }\n}\n\nconst DEFAULT_TTL = 300;\n\nfunction signable(timestampMicros: bigint, dnsPacket: Uint8Array): Uint8Array {\n return concatBytes(utf8Encode(`3:seqi${timestampMicros}e1:v${dnsPacket.length}:`), dnsPacket);\n}\n\nfunction toTxtRecords(pubKeyZ32: string, records: GhostRecord[]): TxtRecord[] {\n return records.map((r) => ({\n name: `${r.label}.${pubKeyZ32}`,\n value: r.value,\n ttl: r.ttl ?? DEFAULT_TTL,\n }));\n}\n\n/** Size of the encoded DNS packet these records would produce. */\nexport function measureRecords(pubKeyZ32: string, records: GhostRecord[]): number {\n return encodeTxtPacket(toTxtRecords(pubKeyZ32, records)).length;\n}\n\n/**\n * Builds the payload Pkarr relays accept on `PUT /<z32 public key>`:\n * `<64 bytes signature><8 bytes big-endian timestamp (µs)><encoded DNS packet>`\n */\nexport function createRelayPayload(\n identity: Identity,\n records: GhostRecord[],\n timestampMicros: bigint = BigInt(Date.now()) * 1000n,\n): Uint8Array {\n return signRelayPayload(identity, encodeTxtPacket(toTxtRecords(identity.pubKeyZ32, records)), timestampMicros);\n}\n\n/**\n * Signs a DNS packet exactly as given (its own names and flags) into a relay payload. `seq` is the\n * BEP44 sequence number: Ghostly's records use microseconds, a did:dht document seconds.\n */\nexport function signRelayPayload(identity: Identity, dnsPacket: Uint8Array, seq: bigint): Uint8Array {\n if (dnsPacket.length > MAX_DNS_PACKET_BYTES) throw new PacketTooLargeError(dnsPacket.length);\n\n const signature = sign(signable(seq, dnsPacket), identity.seed);\n const timestamp = new Uint8Array(TIMESTAMP_LENGTH);\n new DataView(timestamp.buffer).setBigUint64(0, seq);\n return concatBytes(signature, timestamp, dnsPacket);\n}\n\n/** Checks a relay payload's size and signature. Returns its sequence number and DNS packet, not decoded. */\nexport function openRelayPayload(pubKeyZ32: string, payload: Uint8Array): { seq: bigint; dnsPacket: Uint8Array } {\n if (payload.length < SIGNATURE_LENGTH + TIMESTAMP_LENGTH + 12) {\n throw new Error(\"Relay payload too short\");\n }\n if (payload.length > SIGNATURE_LENGTH + TIMESTAMP_LENGTH + MAX_DNS_PACKET_BYTES) {\n throw new PacketTooLargeError(payload.length);\n }\n const publicKey = publicKeyFromZ32(pubKeyZ32);\n const signature = payload.subarray(0, SIGNATURE_LENGTH);\n const seq = new DataView(\n payload.buffer,\n payload.byteOffset + SIGNATURE_LENGTH,\n TIMESTAMP_LENGTH,\n ).getBigUint64(0);\n const dnsPacket = payload.subarray(SIGNATURE_LENGTH + TIMESTAMP_LENGTH);\n\n if (!verify(signature, signable(seq, dnsPacket), publicKey)) {\n throw new Error(\"Invalid signature\");\n }\n return { seq, dnsPacket };\n}\n\n/** Verifies and decodes a relay payload. Throws if the signature does not match. */\nexport function parseRelayPayload(pubKeyZ32: string, payload: Uint8Array): SignedPacket {\n const { seq: timestampMicros, dnsPacket } = openRelayPayload(pubKeyZ32, payload);\n const suffix = `.${pubKeyZ32}`;\n const records = decodeTxtPacket(dnsPacket).map((r) => ({\n label: r.name.endsWith(suffix) ? r.name.slice(0, -suffix.length) : r.name,\n value: r.value,\n ttl: r.ttl,\n }));\n return { pubKeyZ32, timestampMicros, records };\n}\n\n/** How far ahead of this clock a packet's time may be and still count as now: clocks drift, not by this much. */\nexport const PKARR_FUTURE_SKEW_MS = 10 * 60_000;\n\n/**\n * The newer of two signed packets of one key. A packet dated further ahead than `PKARR_FUTURE_SKEW_MS` never wins over\n * one dated now or before: whoever could sign it once (a key several people hold, a clock far off) must not keep every\n * later packet from being taken. Between two of the same kind, the later time wins; on a tie, `known` stays.\n */\nexport function newerPacket(known: SignedPacket | null | undefined, packet: SignedPacket | null | undefined, now = Date.now()): SignedPacket | null {\n if (!known) return packet ?? null;\n if (!packet) return known;\n const limit = BigInt(now + PKARR_FUTURE_SKEW_MS) * 1000n;\n const knownAhead = known.timestampMicros > limit, packetAhead = packet.timestampMicros > limit;\n if (knownAhead !== packetAhead) return knownAhead ? packet : known;\n return packet.timestampMicros > known.timestampMicros ? packet : known;\n}\n","import type { DiscoveryStatus } from \"./relayBreaker\";\nimport type { Identity } from \"./identity\";\nimport type { GhostRecord, SignedPacket } from \"./pkarr\";\n\n/**\n * How a Ghostly peer reaches Pkarr. Desktop talks to the Mainline DHT directly\n * through the Rust client; browsers cannot open UDP sockets, so they go through\n * Pkarr relays. Both end up in the same DHT and see each other's packets.\n */\n/**\n * `background`: a request that can wait (a periodic look at a shared record, not a link's\n * signaling). A transport with a request budget spends only part of it on these, so they never\n * starve the links; one without ignores it.\n */\n/**\n * `urgent`: a signal is due any moment (a link polling fast). A transport with more than one relay may\n * ask a second one when the first had nothing new: relays behind one name do not all serve a fresh\n * packet at the same moment.\n */\n/**\n * `group`: a group's request (its edges' signaling, a knock, a community's shared records). A transport with a\n * request budget keeps the last part of each minute for a 1:1 chat it had to hold back; one without ignores it.\n */\n/** What changed in how discovery goes: a relay tripped (left alone), or one answered again. */\nexport type DiscoveryChange = \"tripped\" | \"recovered\";\n\n/**\n * `door`: a community door reading its knock bell. A background read, but not held to the small share while a link\n * signals (`BACKGROUND_WHILE_SIGNALING`): the door admitting one person opens links that signal, and the next person's\n * knock would wait for them (2026-09-28).\n *\n * `signal`: a link's WebRTC signaling, what its contact waits for: the write of a new offer or answer, or a read for\n * the answer while this side's offer is out, or the reads of a DHT-only contact's mailbox once it shows it is leaving,\n * up to three while it still says DHT only (the live link waits on them). A 1:1 chat's may go a little over the relay's minute\n * (`SIGNALING_ALLOWANCE_SHARE` in relay.ts).\n */\nexport interface PkarrRequestOptions { background?: boolean; urgent?: boolean; group?: boolean; door?: boolean; signal?: boolean }\n\n/**\n * `transport` with `extra` added to every request's options: a group's edges say `group` so. The optional methods\n * stay optional (a caller reads `configure` as \"the DHT is reached directly\").\n */\nexport function withRequestOptions(transport: PkarrTransport, extra: PkarrRequestOptions): PkarrTransport {\n const wrapped: PkarrTransport = {\n publish: (identity, records, options) => transport.publish(identity, records, { ...options, ...extra }),\n resolve: (pubKeyZ32, options) => transport.resolve(pubKeyZ32, { ...options, ...extra }),\n describe: () => transport.describe(),\n };\n if (transport.publishPayload) wrapped.publishPayload = (pubKeyZ32, payload, options) => transport.publishPayload!(pubKeyZ32, payload, { ...options, ...extra });\n if (transport.discovery) wrapped.discovery = () => transport.discovery!();\n if (transport.subscribe) wrapped.subscribe = (listener) => transport.subscribe!(listener);\n if (transport.networkChanged) wrapped.networkChanged = () => transport.networkChanged!();\n if (transport.readAnsweredAt) wrapped.readAnsweredAt = (pubKeyZ32) => transport.readAnsweredAt!(pubKeyZ32);\n if (transport.onServerTime) wrapped.onServerTime = (listener) => transport.onServerTime!(listener);\n if (transport.configure) wrapped.configure = (options) => transport.configure!(options);\n return wrapped;\n}\n\n/**\n * A request the transport held back to stay within a request budget, its own or a relay's rate limit: nothing went\n * out, and the same request goes through once the budget frees one, in about `retryInMs`. A wait, never a failure:\n * whoever publishes tries again then, and says nothing is wrong meanwhile.\n */\nexport class DiscoveryBudgetError extends Error {\n readonly code = \"discovery-budget\";\n constructor(readonly retryInMs: number, message = \"Discovery request budget reached; retry shortly\") {\n super(message);\n this.name = \"DiscoveryBudgetError\";\n }\n}\n\n/** Whether `error` is a request the budget held back (`DiscoveryBudgetError`), also from another copy of this module. */\nexport function isDiscoveryBudgetError(error: unknown): error is DiscoveryBudgetError {\n return error instanceof DiscoveryBudgetError || (typeof error === \"object\" && error !== null && (error as { code?: unknown }).code === \"discovery-budget\"\n && typeof (error as { retryInMs?: unknown }).retryInMs === \"number\");\n}\n\n/** When to try again after the budget held a request back: when it frees one, but never sooner than `min` nor later than `max`. */\nexport function budgetRetryMs(error: DiscoveryBudgetError, min: number, max: number): number {\n return Math.min(max, Math.max(min, Number.isFinite(error.retryInMs) ? error.retryInMs : max));\n}\n\n/**\n * A server's own time: the `Date` header (ms, to the second) of its answer to a request that went out at `sent` and\n * came back at `received`, both by this device's clock. `source` names the server (a relay's origin), never a key.\n */\nexport interface ServerTime { source: string; date: number; sent: number; received: number }\n\nexport interface PkarrTransport {\n publish(identity: Identity, records: GhostRecord[], options?: PkarrRequestOptions): Promise<void>;\n /**\n * Puts a relay payload signed elsewhere, byte for byte (a did:dht document: its own record names and\n * a sequence number in seconds). A transport without it cannot publish one.\n */\n publishPayload?(pubKeyZ32: string, payload: Uint8Array, options?: PkarrRequestOptions): Promise<void>;\n /** Most recent packet known for this key, or null when nothing is published. */\n resolve(pubKeyZ32: string, options?: PkarrRequestOptions): Promise<SignedPacket | null>;\n describe(): { protocol: string; relays: string[] };\n /** How reads go and how each relay is doing, for the connection panel; absent where nothing is known. */\n discovery?(): DiscoveryStatus;\n /**\n * Called when that changes in a way worth showing (a relay trips or recovers), with `\"recovered\"` when a relay\n * answered again after failing: links then look and publish at once rather than at their pace. Returns the unsubscribe.\n */\n subscribe?(listener: (change?: DiscoveryChange) => void): () => void;\n /** The device changed networks (back online): what was learnt about failing relays is forgotten, and links look again. */\n networkChanged?(): void;\n /**\n * When the network last answered a read of this key (this clock), whatever it answered. A `resolve` may hand back a\n * copy it kept (the budget held the read, every relay was resting, the same key was read a moment ago): a caller\n * that reasons from \"the record did not have this at my last read\" asks this to know the read was one. Absent\n * where the transport cannot say; such a caller then takes no read for one.\n */\n readAnsweredAt?(pubKeyZ32: string): number | undefined;\n /**\n * Called with each time a server gave (`ServerTime`), where the transport can read one: what `ClockWatch` holds this\n * device's clock against. Returns the unsubscribe.\n */\n onServerTime?(listener: (time: ServerTime) => void): () => void;\n /**\n * Where the DHT is reached directly (Desktop): the relays from Settings, and whether reads may use them too\n * (`readRelays`, \"Also use Pkarr relays\"). Writes go to them either way, so browser contacts see this peer's packets.\n */\n configure?(options: { relays: string[]; readRelays: boolean }): void;\n}\n","/**\n * A circuit breaker per Pkarr relay. A relay that failed `threshold` times in a row (no answer, a server error,\n * its own rate limit, 429, or an answer too slow to use, `SLOW_MS` in relay.ts) is left alone for a while: a minute the first time, twice as long each time it\n * trips again, five minutes at most. When the wait is over, one request goes to it (the probe); an answer\n * closes the breaker, a failure opens it again for longer. The relays still closed take its turns meanwhile.\n *\n * Only what the relay did counts: a request this client held back for its own budget is not a failure, and an\n * answer the protocol expects (404, 409, 412, 428) is a relay working as it should.\n */\n\n/** Failures in a row that trip a relay's breaker. */\nexport const BREAKER_THRESHOLD = 3;\n/** How long a relay is left alone the first time its breaker trips; doubled on each trip in a row. */\nexport const BREAKER_BASE_MS = 60_000;\n/** The longest a relay is left alone. */\nexport const BREAKER_MAX_MS = 5 * 60_000;\n/**\n * While every relay is left alone for failing, one of them is asked this often anyway: with nothing else to try, a\n * long wait only delays noticing that they answer again (a link stayed down minutes after the relays came back).\n */\nexport const BREAKER_ALL_DOWN_PROBE_MS = 15_000;\n\n/** Why a request to a relay failed: its rate limit (`throttled`), or anything else (`error`). */\nexport type RelayFailure = \"throttled\" | \"error\";\n\n/** A relay as the connection panel shows it: `ok`, `throttled` (its rate limit) or `failing`, until when. */\nexport interface RelayHealth {\n relay: string;\n state: \"ok\" | \"throttled\" | \"failing\";\n /** When the relay is asked again (ms since the epoch), while it is left alone. */\n until?: number;\n /** What went wrong last, in a few words; never a key. */\n reason?: string;\n}\n\n/**\n * How this client reaches Pkarr right now, for the connection panel: the path its last read took (\"dht\", or\n * the relay that answered), and the health of each relay it uses (none where it reads the DHT alone).\n */\nexport interface DiscoveryStatus {\n path: { via: \"dht\" } | { via: \"relay\"; relay: string } | null;\n relays: RelayHealth[];\n}\n\nexport interface RelayBreakerOptions {\n threshold?: number;\n baseMs?: number;\n maxMs?: number;\n /** `BREAKER_ALL_DOWN_PROBE_MS`. */\n allDownProbeMs?: number;\n now?: () => number;\n /** Told each time a relay trips: which, why, and for how long. */\n onTrip?: (relay: string, reason: string, forMs: number) => void;\n /** Told when a tripped relay answered its probe. */\n onRecover?: (relay: string) => void;\n}\n\ninterface Circuit {\n failures: number;\n /** Trips in a row, without an answer in between: the next wait doubles with each. */\n trips: number;\n /** Left alone until then (0: closed). */\n openUntil: number;\n /** The one request allowed once the wait is over is out. */\n probing: boolean;\n kind?: RelayFailure;\n reason?: string;\n}\n\nexport class RelayBreaker {\n private readonly circuits = new Map<string, Circuit>();\n private readonly threshold: number;\n private readonly baseMs: number;\n private readonly maxMs: number;\n private readonly allDownProbeMs: number;\n private readonly now: () => number;\n /** When a relay was last asked because every relay was left alone. */\n private lastAllDownProbe = -Infinity;\n\n constructor(private readonly options: RelayBreakerOptions = {}) {\n this.threshold = options.threshold ?? BREAKER_THRESHOLD;\n this.baseMs = options.baseMs ?? BREAKER_BASE_MS;\n this.maxMs = options.maxMs ?? BREAKER_MAX_MS;\n this.allDownProbeMs = options.allDownProbeMs ?? BREAKER_ALL_DOWN_PROBE_MS;\n this.now = options.now ?? Date.now;\n }\n\n private circuit(relay: string): Circuit {\n let circuit = this.circuits.get(relay);\n if (!circuit) this.circuits.set(relay, circuit = { failures: 0, trips: 0, openUntil: 0, probing: false });\n return circuit;\n }\n\n /** How long the relay is still left alone, in ms; 0 when a request may go (closed, or its probe is due). */\n blockedFor(relay: string): number {\n const circuit = this.circuits.get(relay);\n if (!circuit || circuit.openUntil === 0) return 0;\n const left = circuit.openUntil - this.now();\n if (left > 0) return left;\n // The wait is over: one probe at a time, the others wait for its answer.\n return circuit.probing ? this.baseMs : 0;\n }\n\n /** Why the relay is left alone, while it is: its rate limit or failures. */\n blockedKind(relay: string): RelayFailure | undefined {\n return this.blockedFor(relay) > 0 ? this.circuits.get(relay)?.kind : undefined;\n }\n\n /** A request is going to the relay now: its probe, when the wait is over. Call only when `blockedFor` is 0. */\n begin(relay: string): void {\n const circuit = this.circuits.get(relay);\n if (circuit && circuit.openUntil !== 0 && circuit.openUntil <= this.now()) circuit.probing = true;\n }\n\n /**\n * When every relay of `relays` is left alone for failing (none for its rate limit, none with its probe due or out),\n * the one whose wait ends first may be asked now, once every `BREAKER_ALL_DOWN_PROBE_MS`. Returns it, or null. The\n * caller marks the request with `beginAllDown` when it really goes (a request its budget holds back is no probe).\n */\n allDownProbe(relays: string[]): string | null {\n const now = this.now();\n if (!relays.length || now - this.lastAllDownProbe < this.allDownProbeMs) return null;\n let pick: string | null = null, soonest = Infinity;\n for (const relay of relays) {\n const circuit = this.circuits.get(relay);\n if (!circuit || circuit.openUntil <= now || circuit.probing || circuit.kind !== \"error\") return null;\n if (circuit.openUntil < soonest) { soonest = circuit.openUntil; pick = relay; }\n }\n return pick;\n }\n\n /** The probe `allDownProbe` named is going now: its answer closes the breaker, a failure trips it again. */\n beginAllDown(relay: string): void {\n const circuit = this.circuits.get(relay);\n if (circuit) circuit.probing = true;\n this.lastAllDownProbe = this.now();\n }\n\n /**\n * A request that went to the relay was dropped before it answered, by this client (another relay answered first), or\n * its answer says nothing either way: a probe among them is no longer out, and the next request may probe again.\n */\n cancel(relay: string): void {\n const circuit = this.circuits.get(relay);\n if (circuit) circuit.probing = false;\n }\n\n /** The relay answered: whatever was wrong is over. */\n success(relay: string): void {\n const circuit = this.circuits.get(relay);\n if (!circuit) return;\n const recovered = circuit.openUntil !== 0;\n this.circuits.delete(relay);\n if (recovered) this.options.onRecover?.(relay);\n }\n\n /** The relay failed a request; a probe that fails trips it again at once. */\n failure(relay: string, kind: RelayFailure, reason: string): void {\n const circuit = this.circuit(relay);\n circuit.failures++;\n circuit.kind = kind;\n circuit.reason = reason;\n if (circuit.probing || circuit.failures >= this.threshold) this.trip(relay, circuit);\n }\n\n private trip(relay: string, circuit: Circuit): void {\n // What just failed was an answer of sorts: the next early probe (`allDownProbe`) is a full interval away.\n this.lastAllDownProbe = Math.max(this.lastAllDownProbe, this.now());\n const forMs = Math.min(this.baseMs * 2 ** circuit.trips, this.maxMs);\n circuit.openUntil = this.now() + forMs;\n circuit.trips++;\n circuit.failures = 0;\n circuit.probing = false;\n this.options.onTrip?.(relay, circuit.reason ?? \"\", forMs);\n }\n\n /** How long until a relay left alone is asked again: its wait, or the next probe while every relay of `relays` is left alone. */\n askedAgainIn(relay: string, relays: string[]): number {\n const wait = this.blockedFor(relay);\n if (!relays.every((r) => this.blockedFor(r) > 0)) return wait;\n return Math.min(wait, Math.max(0, this.lastAllDownProbe + this.allDownProbeMs - this.now()));\n }\n\n /** A new network: every relay starts fresh. */\n reset(): void {\n this.circuits.clear();\n this.lastAllDownProbe = -Infinity;\n }\n\n /** Each relay's health, in the order given. */\n health(relays: string[]): RelayHealth[] {\n return relays.map((relay) => {\n const circuit = this.circuits.get(relay);\n if (!circuit || circuit.openUntil === 0 || this.blockedFor(relay) === 0) return { relay, state: \"ok\" };\n return { relay, state: circuit.kind === \"throttled\" ? \"throttled\" : \"failing\", until: Math.max(circuit.openUntil, this.now()), ...(circuit.reason && { reason: circuit.reason }) };\n });\n }\n}\n","/**\n * Timestamps along a paired link's way to a live connection, for measuring where the time goes.\n * Silent unless `globalThis.__ghostlyLinkTrace` is set (the e2e measurements set it): each step is\n * then one `console.debug` line, `[ghostly:link] {\"t\":…,\"me\":…,\"step\":…}`, read back by the test.\n * A host may also install a sink (the desktop app writes the lines to its log file), which gets every\n * line whether or not the global is set.\n */\ntype LinkTraceSink = (line: string) => void;\nlet sink: LinkTraceSink | null = null;\n\n/** Where the lines go besides the console; `null` removes it. */\nexport function setLinkTraceSink(next: LinkTraceSink | null): void {\n sink = next;\n}\n\nexport function traceLink(me: string, step: string, detail?: Record<string, unknown>): void {\n const console_ = (globalThis as { __ghostlyLinkTrace?: boolean }).__ghostlyLinkTrace;\n if (!console_ && !sink) return;\n const line = JSON.stringify({ t: Date.now(), me: me.slice(0, 6), step, ...detail });\n if (console_) console.debug(`[ghostly:link] ${line}`);\n try { sink?.(line); } catch { /* a sink that fails must not fail the link */ }\n}\n","import type { Identity } from \"./identity\";\nimport { createRelayPayload, newerPacket, openRelayPayload, parseRelayPayload, RELAY_PAYLOAD_MAX_BYTES, type GhostRecord, type SignedPacket } from \"./pkarr\";\nimport { RelayBreaker, type DiscoveryStatus, type RelayBreakerOptions, type RelayFailure } from \"./relayBreaker\";\nimport { DiscoveryBudgetError, isDiscoveryBudgetError, type DiscoveryChange, type PkarrRequestOptions, type PkarrTransport, type ServerTime } from \"./transport\";\nimport { traceLink } from \"./linkTrace\";\n\n/** Who asks, for the budget: a 1:1 chat's link comes before a group's, and both before background looks. */\ninterface Asker { background: boolean; group: boolean; urgent: boolean; door: boolean; write: boolean; signal: boolean }\nconst asker = (options: PkarrRequestOptions, write: boolean): Asker =>\n ({ background: !!options.background, group: !!options.group, urgent: !!options.urgent, door: !!options.door, write, signal: !!options.signal });\n/** A 1:1 chat's request: neither a group's nor a background one. */\nconst isChat = (who: Asker): boolean => !who.background && !who.group;\n/** A 1:1 chat's offer or answer, or its read for the answer to its offer: it may use the allowance (`SIGNALING_ALLOWANCE_SHARE`). */\nconst isChatSignal = (who: Asker): boolean => who.signal && isChat(who);\n/** A group's edge looking fast for a signal (`GROUP_BURST_MS`). */\nconst isGroupUrgentRead = (who: Asker): boolean => who.group && who.urgent && !who.write && !who.background;\n/** A write that goes first once the budget frees a request (see `WRITE_FIRST_MS`): a chat's or a group's, never a background one. */\nconst firstWriter = (who: Asker): \"chat\" | \"group\" | null => (!who.write || who.background ? null : who.group ? \"group\" : \"chat\");\n\n/**\n * Public Pkarr relays. They are generic Pkarr infrastructure (an HTTP bridge to\n * the Mainline DHT), not a Ghostly backend: they only ever see signed,\n * encrypted packets. Every client uses this list: the browser clients read and\n * write through it, Ghostly Desktop writes to it (so browser contacts see its\n * packets) and reads from it only when \"Also use Pkarr relays\" is on. Adding a\n * relay is one line here; see docs/RELAYS.md for how one is chosen.\n */\nexport const DEFAULT_RELAYS = [\"https://pkarr.pubky.org\", \"https://pkarr.pubky.app\"];\n/**\n * Relay lists that were the defaults once: a profile that still has one gets today's defaults. When a relay\n * joins `DEFAULT_RELAYS`, the list as it was goes here, so profiles on the old defaults move with it.\n */\nexport const PREVIOUS_DEFAULT_RELAYS: string[][] = [];\n\n/** The defaults for `relays` when it is one of the old default lists, `relays` itself otherwise. */\nexport function currentRelays(relays: string[]): string[] {\n const same = (a: string[], b: string[]) => a.length === b.length && a.every((relay, i) => relay === b[i]);\n return PREVIOUS_DEFAULT_RELAYS.some((old) => same(old, relays)) ? [...DEFAULT_RELAYS] : relays;\n}\n\n/**\n * Requests this client allows itself per relay and minute. Relays limit by IP\n * (120 a minute when this was written) and one address is often shared by\n * several peers: two browser profiles, a tab and an extension, a household.\n */\nexport const REQUESTS_PER_MINUTE = 30;\n/**\n * Of those, what background requests (`{ background: true }`: periodic looks at shared records, as a\n * community group's hub makes) may spend, counted on their own: the rest is kept for links, whose\n * signaling cannot wait, and a burst of signaling does not hold the background ones back afterwards.\n */\nexport const BACKGROUND_REQUESTS_PER_MINUTE = 20;\n/**\n * A link's write the budget refused (its presence, its offer, its answer) goes first when the minute\n * frees a request: reads on that relay wait this long after the refusal. A link that polls fast for a\n * peer while its offer waits would otherwise take every request the minute frees, and the offer (the\n * only thing that peer is waiting for) would go out only once the polling slowed down, half a minute later.\n */\nexport const WRITE_FIRST_MS = 5_000;\n/**\n * Once a 1:1 chat needs the relays (the budget refused one of its requests there, or it read urgently anywhere: it polls\n * fast for a signal due any moment), group requests leave it the last this many of each minute, for a minute. Groups\n * that spend the whole budget spend it over the minute, so the chat then takes the next request that frees, within\n * seconds, and has room for what follows (the receipt). Waiting for a refusal was too late when a contact went away\n * from a chat and from groups at once: the chat and the groups' edges all watched fast for it, the edges spent both\n * relays' minute in 40 s, and the chat read the contact's offer half a minute late (2026-09-27). A chat with nothing\n * to wait for leaves groups the whole budget: a community door alone costs about 22 requests a minute, and a reserve\n * kept for any chat starved a new group's edges. The reserve is per chat: a chat that reads its contact at a slow pace\n * again (it is live) gives its part back at once. Kept for the minute, a daemon back after a restart with its chats\n * live in 18 s held a group's edge that had just missed its read for 48 s more (bug hunt r6a, 2026-09-29).\n */\nexport const CHAT_RESERVE = 10;\n/**\n * A 1:1 chat's signaling (a new offer or answer, and its reads for the answer to its offer, `signal`) may go over a\n * relay's minute by this fraction of it: 6 on a relay of 30, 12 on one of 60, 1 on relay.pkarr.org's 5 (it allows 10),\n * and never more than that in any minute; its reads leave the last of those to a write. Everything else still stops at\n * the limit. The minute before a chat leaves DHT only can be spent by then (the pairing, the texts over the DHT), and\n * its offer or answer, the one thing the contact waits for, then waited 40 s for requests to age out while the\n * contact's attempt gave up: the matrix's extension chat stayed \"On DHT · retrying live\" 46 times in 120 (2026-09-30).\n */\nexport const SIGNALING_ALLOWANCE_SHARE = 1 / 5;\n/**\n * Groups' urgent reads (their edges looking fast for a signal) take at most a quarter of a relay's minute in any this\n * long, so that they never spend it in a burst. An app back after a restart has every edge offering and looking fast at\n * once; the members at the other end answer only once they notice its old sessions went (about 20 s with\n * node-datachannel). On the default relays the edges spent the groups' share of the minute in those seconds, and read\n * nothing for 40 s more: a private group's edges were live again 75 to 110 s after a restart (bug hunt r5a, 2026-09-29).\n */\nexport const GROUP_BURST_MS = 15_000;\n/** …and never fewer than one link polling fast (every 2 s) reads in that time: one edge alone is never held back. */\nexport const GROUP_BURST_ONE_LINK = 8;\n/**\n * While a link polls fast (an `urgent` read: its peer, or the peer's offer or answer, is due any moment) within this\n * long, background requests (a community's periodic looks) are held to `BACKGROUND_WHILE_SIGNALING` a minute on each\n * relay rather than their usual share. A fast poll reads every 2 s, one relay after the other.\n */\nexport const SIGNALING_WINDOW_MS = 10_000;\n/**\n * Background requests a minute on each relay while a link signals: one look every 12 s. Next to a community's\n * lookups (20 a minute), a new group edge on one relay got 10 and read every 6 s, and its offer and answer missed each\n * other's fast windows (2026-09-27). Not none: fast windows can follow one another, and a hub still reads its knocks.\n */\nexport const BACKGROUND_WHILE_SIGNALING = 5;\n/**\n * A read of a key the relays answered this recently is answered from that answer, with no request. Several parts of\n * a chat read the contact's keys (the link's poll, its capabilities, DHT delivery), and each tends to read again\n * within a few dozen milliseconds of its last read, when a pace changes or a signal goes out: nothing new can be there\n * yet, and a pairing spent a sixth of its requests on those reads (2026-09-27). A publish under the key forgets it.\n */\nexport const FRESH_READ_MS = 500;\n/** A relay that missed a key's newest packet gets it (`catchUpLater`) no sooner than this, and tries again this often. */\nconst CATCH_UP_MIN_MS = 1_000;\nconst CATCH_UP_RETRY_MS = 5_000;\n/** A catch-up put waits behind every link's request: a background write. */\nconst CATCH_UP_ASKER: Asker = { background: true, group: false, urgent: false, door: false, write: true, signal: false };\n/** Keys whose last relay is remembered for their next read (`resolve`): a profile's links and lookups, with room. */\nconst READ_TURNS_KEPT = 512;\n/** A relay that fails at the network level is left alone for this long. */\nconst NETWORK_ERROR_COOLDOWN_MS = 20_000;\n/**\n * Relays whose limit per address is far from the others': this client's share of theirs, a minute, in place of\n * `REQUESTS_PER_MINUTE`. relay.pkarr.org (not a default, see docs/RELAYS.md) allows 10 (`x-ratelimit-limit`), which a\n * browser cannot read without CORS exposing it. pkarr.pubky.app allows 1000 (pkarr.pubky.org, on the same address,\n * 50): 60 of ours leaves room for many peers behind one address, and a page that pairs three contacts in a minute,\n * about ten requests a relay each, still has requests to spare there (2026-09-28). Writes go to every relay and\n * count once one took them; reads go to a relay with requests left.\n */\nexport const RELAY_REQUESTS_PER_MINUTE: Record<string, number> = { \"https://relay.pkarr.org\": 5, \"https://pkarr.pubky.app\": 60 };\n/**\n * A read asks the next relay too when the one it asked has not answered within this long, and takes the first good\n * answer. Measured on 2026-10-03 from a home connection: a relay hands over a packet it holds in 0.25 s (0.7 s on a new\n * connection), and one that has to fetch the packet from the DHT first takes 2.7 s. Reads used to wait for a relay's\n * answer before asking the next one, up to the 10 s timeout: a relay that answered slowly or not at all (pkarr.pubky.org\n * on 2026-10-03) made every read of a pairing that slow, and the pairing never finished.\n */\nexport const HEDGE_MS = 1_500;\n/**\n * The hedge delay for a key whose last read found nothing (404): a relay takes 3 to 5 s to say so (it asks the DHT\n * every time), and a hedge at `HEDGE_MS` would double every poll of a key nobody has published yet.\n */\nexport const HEDGE_MISSING_MS = 6_000;\n/**\n * A packet that took longer than this to arrive, or a hedge lost to a relay that answered with the packet, is slow: it\n * counts against the relay's breaker like a failure (three in a row trip it, logged as \"slow\"), while another relay is\n * there to ask. A 404 says nothing either way: every relay is slow to give one.\n */\nexport const SLOW_MS = 3_000;\n/** A relay found slow goes after the others in every read for this long, unless it answers fast meanwhile. */\nexport const SLOW_DEMOTE_MS = 60_000;\n\nexport interface RelayTransportOptions {\n relays?: string[];\n timeoutMs?: number;\n fetch?: typeof fetch;\n /**\n * Requests allowed per relay and minute (`REQUESTS_PER_MINUTE`); background ones get a share of it.\n * `Infinity` for relays of one's own, with no limit to stay under (a test's relay in the same process).\n */\n requestsPerMinute?: number;\n /** How long a read answers the next ones of the same key (`FRESH_READ_MS`); 0 makes every read a request. */\n freshReadMs?: number;\n /** `HEDGE_MS`; `Infinity` asks the next relay only once the one asked failed. */\n hedgeMs?: number;\n /** The circuit breaker's settings (tests shorten its waits); trips are logged with `log`. */\n breaker?: RelayBreakerOptions;\n /** Where a relay that trips or recovers is reported; never with a key. `console.info` by default. */\n log?: (line: string) => void;\n}\n\nexport function normalizeRelayUrl(input: string): string | null {\n try {\n const url = new URL(input.trim());\n if (url.protocol !== \"https:\" && url.protocol !== \"http:\") return null;\n return `${url.origin}${url.pathname.replace(/\\/+$/, \"\")}`;\n } catch {\n return null;\n }\n}\n\nexport class RelayTransport implements PkarrTransport {\n private relays: string[];\n private readonly timeoutMs: number;\n private readonly fetchFn: typeof fetch;\n private readonly timeListeners = new Set<(time: ServerTime) => void>();\n private readonly lastTimestamp = new Map<string, bigint>();\n private readonly newest = new Map<string, SignedPacket>();\n /**\n * The latest time of any packet read under a key, whoever dated it: the next packet this client puts there is dated\n * past it (`nextTimestamp`). A key is not always one writer's with one clock: a group's lobby, beacon and knock\n * records are written by every member, an inviter warms its contact's key with an empty packet, and this device's\n * own clock may have been set back since it last wrote. A relay and the DHT keep only the packet dated latest, so\n * one dated by this clock alone, when it is behind, was refused (409) or silently never read, until this clock\n * caught up with the other one: minutes, or an hour.\n */\n private readonly seenTimestamp = new Map<string, bigint>();\n /**\n * The last packet this client put under a key that a relay took. A read the budget holds back answers the newer of\n * it and the last one read: a hub that wrote its own beacon entry since it last read the beacon must not see that\n * entry as old as the one it replaced (it would stop counting itself at its community's door until its next write).\n */\n private readonly written = new Map<string, SignedPacket>();\n private readonly coolingDown = new Map<string, number>();\n private readonly networkCooldown = new Map<string, number>();\n private cursor = 0;\n /** Per key, the relay that answered its last read: the next read of the key starts at the one after it (`resolve`). */\n private readonly lastReadFrom = new Map<string, string>();\n private readonly spent = new Map<string, number[]>();\n private readonly spentBackground = new Map<string, number[]>();\n /** Groups' urgent reads on each relay in the last `GROUP_BURST_MS`. */\n private readonly spentGroupUrgent = new Map<string, number[]>();\n /** When a link's write was last refused on each relay (`chat <relay>`, `group <relay>`), while it waits for the budget. */\n private readonly writeWaiting = new Map<string, number>();\n /**\n * Per key a 1:1 chat asks for: when it last read urgently (for the next minute, groups leave it `CHAT_RESERVE` on\n * every relay) and when the budget last refused it on each relay (the same, on that relay). A read of the key at a\n * slow pace (the chat is live, or back in the background) drops it: `CHAT_RESERVE`.\n */\n private readonly chatNeeds = new Map<string, { urgentAt: number; refused: Map<string, number> }>();\n /** When a link (a chat's or a group's) last asked urgently: background requests yield for `SIGNALING_WINDOW_MS`. */\n private urgentAt = -Infinity;\n /** Timestamp of the last packet sent to each relay, per key: the compare-and-swap value for the next one. */\n private readonly lastPut = new Map<string, bigint>();\n /** Per relay and key, the newest packet that relay held back for its budget while another relay took it (`catchUpLater`). */\n private readonly catchUp = new Map<string, { relay: string; key: string; payload: Uint8Array; timestamp: bigint }>();\n private catchUpTimer: ReturnType<typeof setTimeout> | null = null;\n /** When the relays last answered a read of each key, for `FRESH_READ_MS`. */\n private readonly readAt = new Map<string, number>();\n private readonly freshReadMs: number;\n private readonly perMinute: number;\n /** `requestsPerMinute` was given: it bounds every relay, one with its own share (`RELAY_REQUESTS_PER_MINUTE`) too. */\n private readonly perMinuteGiven: boolean;\n private readonly backgroundPerMinute: number;\n private readonly backgroundWhileSignaling: number;\n private readonly breaker: RelayBreaker;\n /** The relay the last read was answered by. */\n private lastRelay: string | null = null;\n private readonly hedgeMs: number;\n /** Until when each relay found slow (`SLOW_MS`) goes after the others in a read (`readOrder`). */\n private readonly slowUntil = new Map<string, number>();\n /** Keys whose last read was answered 404: their next read hedges later (`HEDGE_MISSING_MS`). */\n private readonly missing = new Set<string>();\n private readonly listeners = new Set<(change?: DiscoveryChange) => void>();\n\n constructor(options: RelayTransportOptions = {}) {\n this.relays = [];\n this.setRelays(options.relays ?? DEFAULT_RELAYS);\n this.timeoutMs = options.timeoutMs ?? 10_000;\n this.fetchFn = options.fetch ?? ((...args) => fetch(...args));\n this.perMinute = options.requestsPerMinute ?? REQUESTS_PER_MINUTE;\n this.freshReadMs = options.freshReadMs ?? FRESH_READ_MS;\n this.hedgeMs = options.hedgeMs ?? HEDGE_MS;\n this.perMinuteGiven = options.requestsPerMinute !== undefined;\n this.backgroundPerMinute = this.perMinute === REQUESTS_PER_MINUTE ? BACKGROUND_REQUESTS_PER_MINUTE : Math.ceil(this.perMinute * 2 / 3);\n this.backgroundWhileSignaling = this.perMinute === REQUESTS_PER_MINUTE ? BACKGROUND_WHILE_SIGNALING : Math.ceil(this.perMinute / 6);\n const log = options.log ?? ((line: string) => console.info(`[ghostly:relay] ${line}`));\n this.breaker = new RelayBreaker({\n ...options.breaker,\n onTrip: (relay, reason, forMs) => {\n log(`${new URL(relay).host} tripped (${reason}); left alone for ${Math.round(forMs / 1000)} s`);\n options.breaker?.onTrip?.(relay, reason, forMs);\n this.changed(\"tripped\");\n },\n onRecover: (relay) => {\n log(`${new URL(relay).host} answered again`);\n options.breaker?.onRecover?.(relay);\n this.changed(\"recovered\");\n },\n });\n }\n\n /** How reads go and how each relay is doing, for the connection panel. */\n discovery(): DiscoveryStatus {\n const relays = this.breaker.health(this.relays).map((health) => {\n const limited = this.rateLimitedFor(health.relay);\n return health.state === \"ok\" && limited > 0 ? { ...health, state: \"throttled\" as const, until: Date.now() + limited, reason: \"rate limited (429)\" } : health;\n });\n return { path: this.lastRelay ? { via: \"relay\", relay: this.lastRelay } : null, relays };\n }\n\n /** Back online on some network: relays that failed (or rate limited this address) on the old one are asked again. */\n networkChanged(): void {\n this.breaker.reset();\n this.networkCooldown.clear();\n this.coolingDown.clear();\n this.slowUntil.clear();\n this.changed(\"recovered\");\n }\n\n /** Called when a relay trips or recovers. */\n subscribe(listener: (change?: DiscoveryChange) => void): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n private changed(change: DiscoveryChange): void {\n for (const listener of this.listeners) try { listener(change); } catch { /* a listener that fails must not fail a request */ }\n }\n\n /** What the relay did, for its breaker. */\n private answered(relay: string, failure?: { kind: RelayFailure; reason: string }, method?: \"GET\" | \"PUT\"): void {\n if (failure) { this.breaker.failure(relay, failure.kind, failure.reason); return; }\n // It answered this kind of request: an earlier failure of it no longer keeps the next one back.\n if (method) this.networkCooldown.delete(`${method} ${relay}`);\n this.breaker.success(relay);\n }\n\n setRelays(relays: string[]): void {\n const normalized = relays\n .map(normalizeRelayUrl)\n .filter((r): r is string => r !== null);\n this.relays = [...new Set(normalized)];\n }\n\n describe(): { protocol: string; relays: string[] } {\n return { protocol: \"Pkarr relays (HTTP) → Mainline DHT (BEP44)\", relays: [...this.relays] };\n }\n\n /**\n * Publishes to every relay. Peers may be configured with different relay\n * sets; writing everywhere keeps the overlap warm, the DHT covers the rest.\n * Returns once one relay took the packet: the others finish in the background.\n */\n async publish(identity: Identity, records: GhostRecord[], options: PkarrRequestOptions = {}): Promise<void> {\n if (this.relays.length === 0) throw new Error(\"No Pkarr relays configured\");\n await this.publishDated(identity, records, (key, payload, timestamp, conflict) => this.putEverywhere(key, payload, timestamp, options, conflict), options);\n }\n\n /**\n * The time the next packet under a key is dated: this clock's, and past every packet this client put or read there.\n * BEP44 sequence numbers must strictly increase, and the clock that dated the last one may not be this one's.\n */\n nextTimestamp(pubKeyZ32: string): bigint {\n const now = BigInt(Date.now()) * 1000n;\n const last = this.lastTimestamp.get(pubKeyZ32) ?? 0n, seen = this.seenTimestamp.get(pubKeyZ32) ?? 0n;\n const floor = last > seen ? last : seen;\n const timestamp = now > floor ? now : floor + 1n;\n this.lastTimestamp.set(pubKeyZ32, timestamp);\n return timestamp;\n }\n\n /**\n * Signs the records, dated by `nextTimestamp`, and hands the payload to `put` (the relays; the CLI adds the DHT).\n * `put` calls `conflict` for a relay that answered 409: it holds a packet under this key dated at or after this one,\n * which this client has not read (an inviter's warm packet under the key it gave its contact, from a clock ahead of\n * this one). That packet is read from that relay, and the records go again dated just past it, once. Without this\n * the packet was refused until this device's clock passed the other one's, however long that took, and on a relay\n * that did take it, readers still preferred the later-dated empty packet of another relay.\n */\n async publishDated(identity: Identity, records: GhostRecord[],\n put: (pubKeyZ32: string, payload: Uint8Array, timestamp: bigint, conflict: (relay: string) => void) => Promise<void>,\n options: PkarrRequestOptions = {}, again = false): Promise<void> {\n const key = identity.pubKeyZ32, timestamp = this.nextTimestamp(key);\n let redated: Promise<boolean> | undefined;\n const conflict = (relay: string) => {\n if (again || redated) return;\n redated = (async () => {\n const held = await this.heldAt(relay, key, asker(options, true));\n // Not dated later than this one: the relay was still putting another packet. The caller tries again, as before.\n if (held === null || held < timestamp) return false;\n traceLink(key, \"publish-redated\", { aheadMs: Number((held - timestamp) / 1000n), relay });\n // A later publication of this key took over meanwhile: it is dated past what was just read.\n if (this.lastTimestamp.get(key) !== timestamp) return true;\n await this.publishDated(identity, records, put, options, true);\n return true;\n })();\n redated.catch(() => {});\n };\n try { await put(key, createRelayPayload(identity, records, timestamp), timestamp, conflict); }\n catch (error) {\n // Refused everywhere, and somewhere for a later-dated packet: what the publication dated past it did is the answer.\n const pending = redated as Promise<boolean> | undefined;\n if (!pending || !(await pending)) throw error;\n }\n }\n\n /** One relay's packet for a key, read now: its time, kept for `nextTimestamp`. Null when it has none or does not answer. */\n private async heldAt(relay: string, pubKeyZ32: string, who: Asker): Promise<bigint | null> {\n if (!this.take(relay, who, pubKeyZ32)) return null;\n try {\n let payload: Uint8Array | undefined;\n const response = await this.request(`${relay}/${pubKeyZ32}`, { method: \"GET\" }, async (r, signal) => {\n if (r.ok && r.status !== 404) payload = await readRelayBody(r, signal);\n });\n if (!response.ok || !payload) return null;\n const packet = parseRelayPayload(pubKeyZ32, payload);\n this.sawTimestamp(pubKeyZ32, packet.timestampMicros);\n return packet.timestampMicros;\n } catch { return null; }\n }\n\n /** A packet under this key was read (on a relay, or by the CLI on the DHT): its time, for `nextTimestamp`. */\n sawTimestamp(pubKeyZ32: string, timestamp: bigint): void {\n if (timestamp > (this.seenTimestamp.get(pubKeyZ32) ?? 0n)) this.seenTimestamp.set(pubKeyZ32, timestamp);\n }\n\n /** Puts a payload signed elsewhere (a did:dht document), byte for byte, on every relay. `conflict`: as `publishDated` gives it. */\n async publishPayload(pubKeyZ32: string, payload: Uint8Array, options: PkarrRequestOptions = {}, conflict?: (relay: string) => void): Promise<void> {\n if (this.relays.length === 0) throw new Error(\"No Pkarr relays configured\");\n const { seq } = openRelayPayload(pubKeyZ32, payload);\n await this.putEverywhere(pubKeyZ32, payload, seq, options, conflict);\n }\n\n /**\n * Puts the packet on every relay and settles as soon as one of them took it. A relay that is slow to\n * answer (one that stores the packet and answers the PUT only at the timeout) must not hold a link's\n * presence, offer or answer back: the rest finish in the background, their outcomes still counted.\n * When no relay takes it, this waits for all of them to say why.\n */\n private putEverywhere(pubKeyZ32: string, payload: Uint8Array, timestamp: bigint, options: PkarrRequestOptions, conflict?: (relay: string) => void): Promise<void> {\n // A record read, changed and written back (a lobby, a knock record) is read from the relays the next time.\n this.readAt.delete(pubKeyZ32);\n // Published now: its next read is no longer one of a key nobody has (`HEDGE_MISSING_MS`).\n this.missing.delete(pubKeyZ32);\n // A newer packet goes everywhere now: none waiting for a relay that refused an older one.\n for (const relay of this.relays) this.catchUp.delete(`${relay} ${pubKeyZ32}`);\n const waitingBefore = new Map(this.writeWaiting);\n const writer = firstWriter(asker(options, true));\n // Out on one relay: the link will not try again, so a relay that refused it has no write to wait for.\n const noWriteWaits = () => {\n if (!writer) return;\n for (const relay of this.relays) {\n const key = `${writer} ${relay}`;\n if (this.writeWaiting.get(key) !== waitingBefore.get(key)) this.writeWaiting.delete(key);\n }\n };\n // Every relay left alone for failing: one of them is asked anyway, now and then (`allDownProbe`).\n const probe = this.breaker.allDownProbe(this.relays);\n const puts = this.relays.map(async (relay) => {\n const slot = `${relay} ${pubKeyZ32}`;\n const previous = this.lastPut.get(slot);\n this.lastPut.set(slot, timestamp);\n\n // A relay refuses (428) to replace a packet whose DHT put is still in flight, unless told which\n // packet is being replaced. Links publish in bursts (a message, its ack, a signal), so say so.\n let response = await this.put(relay, pubKeyZ32, payload, previous, asker(options, true), relay === probe);\n // 412: the relay never got `previous` (it was busy, or restarted). This client's packet is the one to replace, so insist.\n if (response.status === 412) response = await this.put(relay, pubKeyZ32, payload, undefined, asker(options, true));\n // 409: this relay holds a packet under the key dated at or after this one (`publishDated` reads it and dates past it).\n if (response.status === 409) conflict?.(relay);\n // The relay's own rate limit: this packet did not go in, and goes once the relay says so.\n if (response.status === 429) throw new DiscoveryBudgetError(this.coolDown(relay, response), `${relay} responded 429; retry shortly`);\n if (!response.ok) throw new Error(`${relay} responded ${response.status}`);\n });\n const settled = Promise.allSettled(puts);\n\n return new Promise<void>((resolve, reject) => {\n let accepted = false;\n for (const put of puts) {\n put.then(() => {\n if (accepted) return;\n accepted = true;\n noWriteWaits();\n try { this.written.set(pubKeyZ32, parseRelayPayload(pubKeyZ32, payload)); } catch { /* not a packet a read would take either */ }\n resolve();\n }, () => { /* read from `settled` */ });\n }\n void settled.then((results) => {\n // A relay that finished after the first one took the packet may have been refused on its retry since.\n if (accepted) {\n noWriteWaits();\n // Its budget (or its rate limit) held it back there: that relay gets it once it frees a request (`catchUp`).\n results.forEach((r, i) => { if (r.status === \"rejected\" && isDiscoveryBudgetError(r.reason)) this.catchUpLater(this.relays[i], pubKeyZ32, payload, timestamp); });\n return;\n }\n // Every relay held it back for its budget (this client's, or the relay's rate limit): a wait for the first\n // of them to free a request, not a failure.\n const held = results.map((r) => (r.status === \"rejected\" && isDiscoveryBudgetError(r.reason) ? r.reason : null));\n const reasons = results.map((r) => (r.status === \"rejected\" ? String(r.reason) : \"\")).join(\"; \");\n if (held.every((e) => e !== null)) reject(new DiscoveryBudgetError(Math.min(...held.map((e) => e!.retryInMs)), `Publish held back on every relay: ${reasons}`));\n else reject(new Error(`Publish failed on every relay: ${reasons}`));\n });\n });\n }\n\n /**\n * Public relays rate limit by IP (120 requests a minute at the time of\n * writing) and several peers may share one address, so a poll costs one\n * request: one relay is asked, the next one when this one fails, or when it\n * has not answered within `HEDGE_MS` (a hedge: the first good answer wins and\n * the other request is dropped). The newest validly signed packet seen so far\n * wins, which also covers a relay that is still serving an older cached copy.\n *\n * In turn per key: a key's next read starts at the relay after the one that answered its last read. A relay that\n * does not have a packet yet (a publish that reached only the other relay, one rate limited there) answers a key's\n * reads at most once in a row. With one turn for all keys, a link that read its peer between two other reads landed\n * on the same relay every time: a restarted member's offer, only on `pkarr.pubky.app`, went unread for 12 to 60 s\n * while the member at the other end read `pkarr.pubky.org` (bug hunt r5a, 2026-09-29). A relay found slow lately\n * (`SLOW_DEMOTE_MS`) goes after the others, whatever the turn.\n *\n * A hedge spends a second request, so it goes only where the budget has requests to spare (`hedgeRoom`), never for a\n * background read (nobody waits on it), and later for a key whose last read found nothing (`HEDGE_MISSING_MS`).\n */\n async resolve(pubKeyZ32: string, options: PkarrRequestOptions = {}): Promise<SignedPacket | null> {\n if (this.relays.length === 0) throw new Error(\"No Pkarr relays configured\");\n // A chat reading its contact at a slow pace has nothing due: the reserve kept for it lapses (`CHAT_RESERVE`).\n if (options.background && !options.urgent && !options.group) this.chatNeeds.delete(pubKeyZ32);\n const readAt = this.readAt.get(pubKeyZ32);\n if (readAt !== undefined && Date.now() - readAt < this.freshReadMs) return this.newest.get(pubKeyZ32) ?? null;\n\n const order = this.readOrder(pubKeyZ32);\n // Every relay left alone for failing: one of them is asked anyway, now and then (`allDownProbe`).\n const probe = this.breaker.allDownProbe(this.relays);\n // The soonest a relay passed over for its budget takes a request again, and whether one was down instead.\n let budgetWait = Infinity, down = false;\n const who = asker(options, false);\n const tried = new Set<string>();\n let hedgeAfter = options.background ? Infinity : this.missing.has(pubKeyZ32) ? Math.max(this.hedgeMs, HEDGE_MISSING_MS) : this.hedgeMs;\n\n /**\n * The next relay to ask, its request taken from its budget, or null. A hedge (`hedge`) goes only to a relay with\n * requests to spare; one passed over for that is still there if the relay asked fails.\n */\n const next = (hedge: boolean): string | null => {\n for (const relay of order) {\n if (tried.has(relay)) continue;\n if (relay !== probe && this.networkCoolingDown(relay, \"GET\")) { tried.add(relay); down = true; continue; }\n const limited = this.rateLimitedFor(relay);\n if (limited > 0) { tried.add(relay); budgetWait = Math.min(budgetWait, limited); continue; }\n // Its breaker is open: a relay throttling us is a wait, one failing is down. The others take its turn.\n const blocked = relay === probe ? 0 : this.breaker.blockedFor(relay);\n if (blocked > 0) {\n tried.add(relay);\n if (this.breaker.blockedKind(relay) === \"throttled\") budgetWait = Math.min(budgetWait, blocked); else down = true;\n continue;\n }\n // Asked before `take`, which notes a refusal (a chat's need, a waiting write): a hedge not sent is no refusal.\n if (hedge && (this.heldFor(relay, who) > 0 || !this.hedgeRoom(relay))) continue;\n tried.add(relay);\n if (!this.take(relay, who, pubKeyZ32)) { budgetWait = Math.min(budgetWait, this.heldFor(relay, who)); continue; }\n if (relay === probe) this.breaker.beginAllDown(relay); else this.breaker.begin(relay);\n return relay;\n }\n return null;\n };\n\n const asked = new Map<string, ReadAttempt>();\n let lastAsked = 0;\n const ask = (hedge: boolean): boolean => {\n const relay = next(hedge);\n if (!relay) return false;\n asked.set(relay, this.readFromRelay(relay, pubKeyZ32));\n lastAsked = Date.now();\n return true;\n };\n ask(false);\n\n let winner: { attempt: ReadAttempt; outcome: Extract<ReadOutcome, { kind: \"packet\" | \"missing\" }> } | null = null;\n while (asked.size > 0 && !winner) {\n const hedgeIn = hedgeAfter - (Date.now() - lastAsked);\n if (hedgeIn <= 0) {\n // No relay to spare a request for a hedge: this read waits for the relays it asked.\n if (!ask(true)) hedgeAfter = Infinity;\n continue;\n }\n let timer: ReturnType<typeof setTimeout> | undefined;\n const hedgeDue = hedgeIn === Infinity ? null : new Promise<null>((resolve) => { timer = setTimeout(() => resolve(null), hedgeIn); });\n const settled = await Promise.race([\n ...[...asked.values()].map((attempt) => attempt.done.then((outcome) => ({ attempt, outcome }))),\n ...(hedgeDue ? [hedgeDue] : []),\n ]);\n clearTimeout(timer);\n if (!settled) continue;\n const { attempt, outcome } = settled;\n asked.delete(attempt.relay);\n const relay = attempt.relay;\n if (outcome.kind === \"packet\" || outcome.kind === \"missing\") { winner = { attempt, outcome }; break; }\n if (outcome.kind === \"throttled\") {\n budgetWait = Math.min(budgetWait, this.coolDown(relay, outcome.response));\n this.answered(relay, { kind: \"throttled\", reason: \"rate limited (429)\" });\n } else if (outcome.kind === \"failed\") {\n // In a browser a rate-limited answer often arrives without CORS headers and\n // surfaces as a network error. Back off this operation and try the next relay.\n // Unlike an observable 429, this does not establish a relay-wide limit.\n this.networkCooldown.set(`GET ${relay}`, Date.now() + NETWORK_ERROR_COOLDOWN_MS);\n this.answered(relay, { kind: \"error\", reason: outcome.reason });\n down = true;\n }\n // A relay that failed is replaced at once, with no hedge delay.\n ask(false);\n }\n\n if (winner) {\n const { attempt, outcome } = winner, relay = attempt.relay;\n const took = Date.now() - attempt.started;\n if (outcome.kind === \"packet\") {\n this.sawTimestamp(pubKeyZ32, outcome.packet.timestampMicros);\n this.newest.set(pubKeyZ32, newerPacket(this.newest.get(pubKeyZ32), outcome.packet)!);\n this.missing.delete(pubKeyZ32);\n } else {\n this.missing.delete(pubKeyZ32);\n this.missing.add(pubKeyZ32);\n if (this.missing.size > READ_TURNS_KEPT) this.missing.delete(this.missing.values().next().value!);\n }\n if (took <= SLOW_MS) {\n this.answered(relay, undefined, \"GET\");\n this.slowUntil.delete(relay);\n } else {\n // It answered this kind of request: an earlier failure of it no longer keeps the next one back.\n this.networkCooldown.delete(`GET ${relay}`);\n // A slow packet counts against the relay; a slow 404 says nothing (every relay takes seconds to give one).\n if (outcome.kind === \"packet\") this.slow(relay);\n else this.breaker.cancel(relay);\n }\n // The relays still asked are dropped, after the winner's own outcome (a probe that answered is a relay to ask\n // again). One asked no later than the winner, which a relay beat with the packet, is slow.\n for (const loser of asked.values()) {\n loser.controller.abort();\n if (outcome.kind === \"packet\" && loser.started <= attempt.started) this.slow(loser.relay);\n else this.breaker.cancel(loser.relay);\n }\n this.lastRelay = relay;\n this.answeredRead(pubKeyZ32);\n this.readFrom(pubKeyZ32, relay);\n return this.newest.get(pubKeyZ32) ?? null;\n }\n\n const resting = this.relays.every((r) => this.isCoolingDown(r, \"GET\") || this.breaker.blockedFor(r) > 0 || this.heldFor(r, who) > 0);\n // Holding back is not an outage: report what is already known, this client's own writes included while only the\n // budget held the read (a relay that failed it says nothing of them: an inviter's placeholder under its contact's\n // key would hide the outage)…\n const known = down ? this.newest.get(pubKeyZ32) : newerPacket(this.newest.get(pubKeyZ32), this.written.get(pubKeyZ32));\n if (resting && known) return known;\n // …or, knowing nothing yet, that the read waits for the budget.\n if (!down && budgetWait < Infinity) throw new DiscoveryBudgetError(budgetWait);\n throw new Error(\"No Pkarr relay reachable\");\n }\n\n /** The order a key's read asks the relays in: in turn per key (see `resolve`), relays found slow lately last. */\n private readOrder(pubKeyZ32: string): string[] {\n const last = this.lastReadFrom.get(pubKeyZ32), lastAt = last === undefined ? -1 : this.relays.indexOf(last);\n const start = lastAt >= 0 ? lastAt + 1 : this.cursor++;\n const turn = this.relays.map((_, i) => this.relays[(start + i) % this.relays.length]);\n const now = Date.now(), slow = (relay: string) => (this.slowUntil.get(relay) ?? 0) > now;\n return [...turn.filter((relay) => !slow(relay)), ...turn.filter(slow)];\n }\n\n /** One GET of a key at one relay, which `controller` drops. Never rejects: what happened is the outcome. */\n private readFromRelay(relay: string, pubKeyZ32: string): ReadAttempt {\n const controller = new AbortController(), started = Date.now();\n const done = (async (): Promise<ReadOutcome> => {\n let status = 0;\n try {\n let payload: Uint8Array | undefined;\n const response = await this.request(`${relay}/${pubKeyZ32}`, { method: \"GET\" }, async (r, signal) => {\n status = r.status;\n if (r.ok && r.status !== 404) payload = await readRelayBody(r, signal);\n }, controller.signal);\n status = response.status;\n if (response.status === 429) return { kind: \"throttled\", response };\n if (response.status === 404) return { kind: \"missing\" };\n if (!response.ok) throw new Error(`${relay} responded ${response.status}`);\n return { kind: \"packet\", packet: parseRelayPayload(pubKeyZ32, payload!) };\n } catch {\n if (controller.signal.aborted) return { kind: \"cancelled\" };\n return { kind: \"failed\", reason: status >= 200 && status < 300 ? \"invalid packet\" : status ? `HTTP ${status}` : \"no answer\" };\n }\n })();\n return { relay, controller, started, done };\n }\n\n /**\n * The relay was slow (`SLOW_MS`): it goes after the others for a while, and counts it against its breaker while\n * another relay is there to ask. With none, a slow answer beats no answer: it counts as one.\n */\n private slow(relay: string): void {\n this.slowUntil.set(relay, Date.now() + SLOW_DEMOTE_MS);\n const another = this.relays.some((r) => r !== relay && this.breaker.blockedFor(r) === 0 && !this.isCoolingDown(r, \"GET\"));\n if (another) this.breaker.failure(relay, \"error\", \"slow\");\n else this.breaker.success(relay);\n }\n\n /**\n * A hedge may go to this relay: its minute has more requests left than what groups leave a chat (`reserveOf`), so\n * a second request for a read that already went out never takes one a pairing needs.\n */\n private hedgeRoom(relay: string): boolean {\n const now = Date.now();\n const recent = (this.spent.get(relay) ?? []).filter((at) => now - at < 60_000).length;\n return recent < this.limitOf(relay) - this.reserveOf(relay);\n }\n\n readAnsweredAt(pubKeyZ32: string): number | undefined { return this.readAt.get(pubKeyZ32); }\n\n /** The relays answered a read of this key now; answers older than `FRESH_READ_MS` are dropped as the list grows. */\n private answeredRead(pubKeyZ32: string): void {\n const now = Date.now();\n this.readAt.delete(pubKeyZ32);\n this.readAt.set(pubKeyZ32, now);\n // Oldest first: drop the stale ones from the front.\n if (this.readAt.size > 64) for (const [key, at] of this.readAt) { if (now - at < this.freshReadMs) break; this.readAt.delete(key); }\n }\n\n /** The relay that answered a key's read, for its next one; the keys read longest ago go past `READ_TURNS_KEPT`. */\n private readFrom(pubKeyZ32: string, relay: string): void {\n this.lastReadFrom.delete(pubKeyZ32);\n this.lastReadFrom.set(pubKeyZ32, relay);\n if (this.lastReadFrom.size > READ_TURNS_KEPT) this.lastReadFrom.delete(this.lastReadFrom.keys().next().value!);\n }\n\n /**\n * A relay whose budget (this client's, or its own rate limit) held a packet back that another relay took: it gets\n * that packet when it frees a request, as a background write. Only the newest per key and relay; a newer packet put\n * everywhere drops it. Without this, that relay kept the key's older packet for good, and an app that restarted and\n * read it first found a contact's offer long answered, answered it again, and waited on it (bug hunt r7a).\n */\n private catchUpLater(relay: string, key: string, payload: Uint8Array, timestamp: bigint): void {\n const slot = `${relay} ${key}`;\n // A newer packet went to that relay (or was tried) since.\n if ((this.lastPut.get(slot) ?? 0n) > timestamp) return;\n const pending = this.catchUp.get(slot);\n if (pending && pending.timestamp >= timestamp) return;\n this.catchUp.set(slot, { relay, key, payload, timestamp });\n this.scheduleCatchUp(CATCH_UP_MIN_MS);\n }\n\n private scheduleCatchUp(inMs: number): void {\n if (this.catchUpTimer || this.catchUp.size === 0) return;\n this.catchUpTimer = setTimeout(() => { this.catchUpTimer = null; void this.runCatchUp(); }, inMs);\n }\n\n private async runCatchUp(): Promise<void> {\n let next = Infinity;\n for (const [slot, item] of [...this.catchUp]) {\n if (this.catchUp.get(slot) !== item) continue;\n const wait = Math.max(this.heldFor(item.relay, CATCH_UP_ASKER), this.rateLimitedFor(item.relay), this.breaker.blockedFor(item.relay));\n if (wait > 0) { next = Math.min(next, wait); continue; }\n this.catchUp.delete(slot);\n try {\n const response = await this.put(item.relay, item.key, item.payload, undefined, CATCH_UP_ASKER);\n // The relay still putting an earlier packet (428) or rate limiting (429): again later, unless a newer one came.\n if ((response.status === 428 || response.status === 429) && !this.catchUp.has(slot)) { this.catchUp.set(slot, item); next = Math.min(next, CATCH_UP_RETRY_MS); }\n } catch (error) {\n if (isDiscoveryBudgetError(error) && !this.catchUp.has(slot)) { this.catchUp.set(slot, item); next = Math.min(next, error.retryInMs); }\n }\n }\n if (this.catchUp.size) this.scheduleCatchUp(Math.max(CATCH_UP_MIN_MS, next === Infinity ? CATCH_UP_RETRY_MS : next));\n }\n\n private async put(relay: string, pubKeyZ32: string, payload: Uint8Array, replaces: bigint | undefined, who: Asker, probe = false): Promise<Response> {\n if (!probe && this.networkCoolingDown(relay, \"PUT\")) throw new Error(\"Discovery relay is cooling down; retry shortly\");\n const limited = this.rateLimitedFor(relay);\n if (limited > 0) throw new DiscoveryBudgetError(limited, \"Discovery relay is cooling down after a 429; retry shortly\");\n const blocked = probe ? 0 : this.breaker.blockedFor(relay);\n if (blocked > 0) {\n if (this.breaker.blockedKind(relay) === \"throttled\") throw new DiscoveryBudgetError(blocked, \"Discovery relay is throttling this address; retry shortly\");\n throw new Error(`${relay} is left alone after failing; asked again in ${Math.max(1, Math.ceil(this.breaker.askedAgainIn(relay, this.relays) / 1000))} s`);\n }\n if (!this.take(relay, who, pubKeyZ32)) throw new DiscoveryBudgetError(this.heldFor(relay, who));\n if (probe) this.breaker.beginAllDown(relay); else this.breaker.begin(relay);\n let response: Response;\n try { response = await this.request(`${relay}/${pubKeyZ32}`, {\n method: \"PUT\",\n body: payload as BodyInit,\n headers: replaces === undefined ? undefined : { \"If-Match\": replaces.toString() },\n }); } catch (error) {\n this.networkCooldown.set(`PUT ${relay}`, Date.now() + NETWORK_ERROR_COOLDOWN_MS);\n this.answered(relay, { kind: \"error\", reason: \"no answer\" });\n throw error;\n }\n // 409, 412 and 428 are the relay working as it should; its rate limit and its own errors count against it.\n if (response.status === 429) this.answered(relay, { kind: \"throttled\", reason: \"rate limited (429)\" });\n else if (response.status >= 500) this.answered(relay, { kind: \"error\", reason: `HTTP ${response.status}` });\n else this.answered(relay, undefined, \"PUT\");\n return response;\n }\n\n /**\n * Discovery reads and writes share a bounded per-relay request budget; background requests only part of it, and\n * groups leave a chat the budget refused its reserve. A link's write the budget refused goes before any read\n * once a request is free again.\n */\n private take(relay: string, who: Asker, key: string): boolean {\n const now = Date.now();\n const writer = firstWriter(who);\n // Asked, whether or not it gets a request now: the link is signaling, and a chat's needs its reserve from now on.\n if (who.urgent && !who.background) {\n this.urgentAt = now;\n if (isChat(who)) this.chatNeed(key).urgentAt = now;\n }\n if (this.heldFor(relay, who, now) > 0) {\n if (isChat(who)) this.chatNeed(key).refused.set(relay, now);\n if (writer) this.writeWaiting.set(`${writer} ${relay}`, now);\n return false;\n }\n if (writer) this.writeWaiting.delete(`${writer} ${relay}`);\n this.spent.get(relay)!.push(now);\n if (who.background) this.spentBackground.get(relay)!.push(now);\n if (isGroupUrgentRead(who)) this.spentGroupUrgent.get(relay)!.push(now);\n return true;\n }\n\n /**\n * How long until this relay's budget takes this request, 0 when it would now: the oldest request of the minute over\n * a limit ages out, a chat's reserve lapses, or a waiting write has had its turn. Keeps the lists to the minute, oldest first.\n *\n * Who goes first: a chat's refused write holds back everything but chat writes; a group's refused write holds back\n * group reads and background requests, never a chat's. Background requests yield to a link that signals. A chat's\n * signaling goes over the limit by its allowance (`SIGNALING_ALLOWANCE_SHARE`).\n */\n private heldFor(relay: string, who: Asker, now = Date.now()): number {\n const recent = (this.spent.get(relay) ?? []).filter((at) => now - at < 60_000);\n const recentBackground = (this.spentBackground.get(relay) ?? []).filter((at) => now - at < 60_000);\n this.spent.set(relay, recent);\n this.spentBackground.set(relay, recentBackground);\n // Until the `limit`-th newest request of the minute ages out; 0 while fewer than `limit` were made.\n const over = (list: number[], limit: number) => (list.length >= limit ? list[list.length - limit] + 60_000 - now : 0);\n const limit = this.limitOf(relay);\n // A chat's signaling may go over the minute by its allowance; its reads leave the last of it to a write.\n const allowance = isChatSignal(who) ? Math.max(0, Math.floor(limit * SIGNALING_ALLOWANCE_SHARE) - (who.write ? 0 : 1)) : 0;\n let wait = over(recent, limit + allowance);\n if (who.background) {\n wait = Math.max(wait, over(recentBackground, this.backgroundPerMinute));\n // A link signaling: background takes a smaller share until it stops, or until enough of its own age out. A\n // community door's bell does not: the links it signals for are often the ones its last admission opened.\n const signaling = this.urgentAt + SIGNALING_WINDOW_MS - now;\n if (signaling > 0 && !who.door) wait = Math.max(wait, Math.min(signaling, over(recentBackground, this.backgroundWhileSignaling)));\n }\n // Groups' urgent reads: their share of the minute spread over it (`GROUP_BURST_MS`).\n const burst = (this.spentGroupUrgent.get(relay) ?? []).filter((at) => now - at < GROUP_BURST_MS);\n this.spentGroupUrgent.set(relay, burst);\n if (isGroupUrgentRead(who)) {\n const most = Math.max(GROUP_BURST_ONE_LINK, Math.floor(limit * GROUP_BURST_MS / 60_000));\n if (burst.length >= most) wait = Math.max(wait, burst[burst.length - most] + GROUP_BURST_MS - now);\n }\n if (who.group) {\n const reserved = this.chatReservedFor(relay, now);\n if (reserved > 0) wait = Math.max(wait, Math.min(reserved, over(recent, limit - this.reserveOf(relay))));\n }\n const writer = firstWriter(who);\n const waiting = (lane: \"chat\" | \"group\") => {\n const at = this.writeWaiting.get(`${lane} ${relay}`);\n return at !== undefined && now - at < WRITE_FIRST_MS ? at + WRITE_FIRST_MS - now : 0;\n };\n if (writer !== \"chat\") wait = Math.max(wait, waiting(\"chat\"));\n if ((writer === null && who.group) || who.background) wait = Math.max(wait, waiting(\"group\"));\n return Math.max(wait, 0);\n }\n\n private chatNeed(key: string): { urgentAt: number; refused: Map<string, number> } {\n let need = this.chatNeeds.get(key);\n if (!need) this.chatNeeds.set(key, need = { urgentAt: -Infinity, refused: new Map() });\n return need;\n }\n\n /** How long groups still leave a chat its reserve on this relay (`CHAT_RESERVE`); a chat's need a minute old goes. */\n private chatReservedFor(relay: string, now: number): number {\n let until = -Infinity;\n for (const [key, need] of this.chatNeeds) {\n const last = Math.max(need.urgentAt, ...need.refused.values());\n if (now - last >= 60_000) { this.chatNeeds.delete(key); continue; }\n until = Math.max(until, need.urgentAt, need.refused.get(relay) ?? -Infinity);\n }\n return until + 60_000 - now;\n }\n\n /** Requests allowed to this relay a minute: this client's budget, or the relay's own share (`RELAY_REQUESTS_PER_MINUTE`). */\n private limitOf(relay: string): number {\n const share = RELAY_REQUESTS_PER_MINUTE[relay];\n if (share === undefined) return this.perMinute;\n return this.perMinuteGiven ? Math.min(this.perMinute, share) : share;\n }\n\n /** What groups leave a chat on this relay: `CHAT_RESERVE`, or a third of a smaller budget. */\n private reserveOf(relay: string): number {\n return Math.min(CHAT_RESERVE, Math.floor(this.limitOf(relay) / 3));\n }\n\n private isCoolingDown(relay: string, method: \"GET\" | \"PUT\"): boolean {\n return this.rateLimitedFor(relay) > 0 || this.networkCoolingDown(relay, method);\n }\n\n /** Left alone after failing at the network level. */\n private networkCoolingDown(relay: string, method: \"GET\" | \"PUT\"): boolean {\n return (this.networkCooldown.get(`${method} ${relay}`) ?? 0) > Date.now();\n }\n\n /** How much longer the relay asked (429) to be left alone; 0 when it did not. */\n private rateLimitedFor(relay: string): number {\n return Math.max(0, (this.coolingDown.get(relay) ?? 0) - Date.now());\n }\n\n /** Leaves the relay alone for as long as its 429 asks; returns that, in ms. */\n private coolDown(relay: string, response: Response): number {\n const retryAfter = Number.parseInt(response.headers.get(\"retry-after\") ?? \"\", 10);\n const seconds = Number.isFinite(retryAfter) ? Math.min(Math.max(retryAfter, 1), 120) : 15;\n this.coolingDown.set(relay, Date.now() + seconds * 1000);\n return seconds * 1000;\n }\n\n /**\n * One request under one time limit. `read` runs before the timer is cleared, so a body read there is bounded in\n * time as well: a relay that sends its headers and then trickles, or never ends, is given up on like one that\n * never answered.\n */\n /**\n * The relay's own time, when its answer says it and this runtime may read it: Node and the Desktop can, a browser\n * only when the relay exposes the header to other origins. Held against this device's clock (`ClockWatch`).\n */\n private serverTime(url: string, response: Response, sent: number): void {\n if (!this.timeListeners.size) return;\n let date = NaN;\n try {\n // An answer a cache kept says when it was made, not when it was served (`Age` says how long ago): not a clock.\n if (Number(response.headers.get(\"age\") ?? 0) > 0) return;\n date = Date.parse(response.headers.get(\"date\") ?? \"\");\n } catch { /* an answer with no headers to read */ }\n if (!Number.isFinite(date)) return;\n const time: ServerTime = { source: new URL(url).origin, date, sent, received: Date.now() };\n for (const listener of this.timeListeners) try { listener(time); } catch { /* a listener that fails must not fail a request */ }\n }\n\n onServerTime(listener: (time: ServerTime) => void): () => void {\n this.timeListeners.add(listener);\n return () => this.timeListeners.delete(listener);\n }\n\n /** `cancel`: this client drops the request (a hedged read another relay answered first). */\n private async request(url: string, init: RequestInit, read?: (response: Response, signal: AbortSignal) => Promise<void>, cancel?: AbortSignal): Promise<Response> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\n const drop = () => controller.abort();\n cancel?.addEventListener(\"abort\", drop, { once: true });\n try {\n // Relays answer with `cache-control: max-age=300`; polling needs fresh data.\n const sent = Date.now();\n const response = await this.fetchFn(url, { ...init, cache: \"no-store\", signal: controller.signal });\n this.serverTime(url, response, sent);\n await read?.(response, controller.signal);\n return response;\n } finally {\n clearTimeout(timer);\n cancel?.removeEventListener(\"abort\", drop);\n }\n }\n}\n\n/** What one relay did with a read (`readFromRelay`); `cancelled`: this client dropped it. */\ntype ReadOutcome =\n | { kind: \"packet\"; packet: SignedPacket }\n | { kind: \"missing\" }\n | { kind: \"throttled\"; response: Response }\n | { kind: \"failed\"; reason: string }\n | { kind: \"cancelled\" };\ninterface ReadAttempt { relay: string; controller: AbortController; started: number; done: Promise<ReadOutcome> }\n\n/**\n * A relay's answer body, at most RELAY_PAYLOAD_MAX_BYTES: refused on a larger `content-length` before anything is\n * read, and otherwise streamed with a running count, so a relay (a default one or one the person added, working or\n * compromised) cannot fill memory with a huge or endless body. Stops when `signal` aborts.\n */\nexport async function readRelayBody(response: Response, signal: AbortSignal, max = RELAY_PAYLOAD_MAX_BYTES): Promise<Uint8Array> {\n const tooLarge = () => new Error(`Relay answer is larger than ${max} bytes`);\n if (Number(response.headers.get(\"content-length\")) > max) { void response.body?.cancel().catch(() => {}); throw tooLarge(); }\n if (!response.body) return new Uint8Array(0);\n const reader = response.body.getReader();\n // The fetch aborts its own stream; this also stops a body that is not tied to the request's signal.\n const stop = () => { void reader.cancel().catch(() => {}); };\n signal.addEventListener(\"abort\", stop, { once: true });\n try {\n const chunks: Uint8Array[] = [];\n let length = 0;\n for (;;) {\n if (signal.aborted) throw new Error(\"Relay answer timed out\");\n const { done, value } = await reader.read();\n if (signal.aborted) throw new Error(\"Relay answer timed out\");\n if (done) break;\n length += value.byteLength;\n if (length > max) { stop(); throw tooLarge(); }\n chunks.push(value);\n }\n const body = new Uint8Array(length);\n let at = 0;\n for (const chunk of chunks) { body.set(chunk, at); at += chunk.byteLength; }\n return body;\n } finally {\n signal.removeEventListener(\"abort\", stop);\n }\n}\n","import { bech32, bech32m } from \"@scure/base\";\nimport { generateEncryptionKey } from \"./crypto\";\nimport { createIdentity, identityFromSeedB64 } from \"./identity\";\nimport { fromBase64Url, fromZ32, toBase64Url, toZ32 } from \"./bytes\";\n\n/**\n * A link between two peers: my identity, the peer's public key and the shared\n * secretbox key. The slash forms are the invite formats of Ghostly 0.4\n * (`<seed>/<peer public key>/<encryption key>`) and of pre-0.5 builds\n * (`pair1/…`, `pair2d/…`); a current app reads them and makes only `ghostly1…`\n * codes (WISP 801).\n */\nexport interface LinkParams {\n profile?: \"paired-chat/1\";\n deliveryMode?: \"stream\" | \"dht\";\n seedB64: string;\n peerPubKeyZ32: string;\n encKeyB64: string;\n /**\n * The inviter's participation public key, carried by a `ghostly1` code: the\n * joiner pins the inviter from it before either path answers.\n */\n peerParticipationKeyZ32?: string;\n /**\n * The inviter's own participation seed, on the inviter's side only (the one\n * whose public key its invite carries). Never part of a code.\n */\n participationSeedB64?: string;\n}\n\n/** The human-readable part of every invite code: codes read `ghostly1…`. */\nexport const INVITE_HRP = \"ghostly\";\n/** The format version this build makes; the first data symbol (`p`). */\nexport const INVITE_VERSION = 1;\n/** Decoders refuse anything longer before computing a checksum (WISP 801 lifts bech32's 90). */\nexport const INVITE_MAX_LENGTH = 1023;\n/** The canonical link host (Q11): the code travels in the fragment, never sent to a server. */\nexport const INVITE_LINK_ORIGIN = \"https://ghostly.tools\";\nconst V1_BYTES = 128;\n\n/** Why a code was refused, each with the one message the UI shows for it. */\nexport type InviteRefusal = \"typo\" | \"update\" | \"not-ghostly\" | \"damaged\";\n\n/** What reading a pasted or scanned invite gives: the chat's parameters, or why not. */\nexport type InviteReading =\n | { ok: true; params: LinkParams; format: \"ghostly1\" | \"pair1\" | \"pair2d\" | \"legacy\" }\n | { ok: false; reason: InviteRefusal; detail?: string };\n\n/** The `ghostly1…` code of a new chat. `params` must carry the inviter's participation key. */\nexport function encodeGhostlyInvite(params: LinkParams): string {\n if (!params.peerParticipationKeyZ32) throw new Error(\"A ghostly1 invite needs the inviter's participation key\");\n const payload = new Uint8Array(V1_BYTES);\n const fields = [fromBase64Url(params.seedB64), fromZ32(params.peerPubKeyZ32), fromBase64Url(params.encKeyB64), fromZ32(params.peerParticipationKeyZ32)];\n fields.forEach((field, index) => {\n if (field.length !== 32) throw new Error(\"Every field of a ghostly1 invite is 32 bytes\");\n payload.set(field, index * 32);\n });\n return bech32m.encode(INVITE_HRP, [INVITE_VERSION, ...bech32m.toWords(payload)], false);\n}\n\n/**\n * The code for these parameters: `ghostly1…` when they carry the inviter's\n * participation key (every invite a current app makes), else the slash form\n * they were read from, so a stored older code round-trips.\n */\nexport function encodeInviteCode(params: LinkParams): string {\n if (params.peerParticipationKeyZ32) return encodeGhostlyInvite(params);\n return `${params.profile ? (params.deliveryMode === \"dht\" ? \"pair2d/\" : \"pair1/\") : \"\"}${params.seedB64}/${params.peerPubKeyZ32}/${params.encKeyB64}`;\n}\n\n/** `https://ghostly.tools/#ghostly1…`: the form an invite is shared in. */\nexport function inviteLink(code: string, origin = INVITE_LINK_ORIGIN): string {\n return `${origin.replace(/\\/+$/, \"\")}/#${code}`;\n}\n\n/** What goes in a QR code: upper case, so the code fits the alphanumeric mode. Older codes stay as they are. */\nexport function inviteQrText(code: string): string {\n return /^ghostly1/i.test(code) ? code.toUpperCase() : code;\n}\n\n/**\n * The QR segments of an invite's link, in capitals: `HTTPS://GHOSTLY.TOOLS/`\n * and the code in alphanumeric mode, the `#` between them in byte mode (QR\n * version 9 at level M). An encoder that takes a list of strings picks each\n * one's mode; one string with the `#` in it would be all bytes. Older codes\n * stay one segment, as they are.\n */\nexport function inviteQrSegments(code: string, origin = INVITE_LINK_ORIGIN): string[] {\n if (!/^ghostly1/i.test(code)) return [code];\n return [`${origin.replace(/\\/+$/, \"\")}/`.toUpperCase(), \"#\", code.toUpperCase()];\n}\n\n/** The code inside whatever was pasted or scanned: after the last `#`, without a leading `chat/`. */\nfunction inviteBody(input: string): string {\n return input.trim().replace(/^.*#/, \"\").replace(/^\\/?chat\\//, \"\");\n}\n\nfunction readGhostly(code: string): InviteReading {\n if (code.length > INVITE_MAX_LENGTH) return { ok: false, reason: \"not-ghostly\", detail: \"Longer than 1,023 characters\" };\n const decoded = bech32m.decodeUnsafe(code, INVITE_MAX_LENGTH);\n if (!decoded) {\n // A valid bech32 (not bech32m) checksum is another kind of string, not a typo.\n if (bech32.decodeUnsafe(code, INVITE_MAX_LENGTH)) return { ok: false, reason: \"not-ghostly\", detail: \"bech32, not bech32m\" };\n return { ok: false, reason: \"typo\" };\n }\n if (decoded.prefix !== INVITE_HRP) return { ok: false, reason: \"typo\" };\n const [version, ...words] = decoded.words;\n if (version === undefined || version === 0) return { ok: false, reason: \"not-ghostly\", detail: \"Version 0 is reserved\" };\n if (version !== INVITE_VERSION) return { ok: false, reason: \"update\", detail: `Version ${version}` };\n const payload = bech32m.fromWordsUnsafe(words);\n if (!payload) return { ok: false, reason: \"damaged\", detail: \"Padding\" };\n if (payload.length !== V1_BYTES) return { ok: false, reason: \"damaged\", detail: `${payload.length} bytes, not ${V1_BYTES}` };\n const field = (index: number) => payload.slice(index * 32, index * 32 + 32);\n return {\n ok: true,\n format: \"ghostly1\",\n params: {\n profile: \"paired-chat/1\",\n seedB64: toBase64Url(field(0)),\n peerPubKeyZ32: toZ32(field(1)),\n encKeyB64: toBase64Url(field(2)),\n peerParticipationKeyZ32: toZ32(field(3)),\n },\n };\n}\n\nconst B64_KEY = /^[A-Za-z0-9_-]{43}$/;\nconst Z32_KEY = /^[ybndrfg8ejkmcpqxot1uwisza345h769]{52}$/;\n\nfunction readSlashForm(code: string): InviteReading {\n const dht = code.startsWith(\"pair2d/\");\n const profile = (dht || code.startsWith(\"pair1/\")) ? \"paired-chat/1\" as const : undefined;\n const parts = (profile ? code.slice(dht ? 7 : 6) : code).split(\"/\");\n if (parts.length !== 3) {\n // A string of another kind (`npub1…`, `lnbc1…`, a word): not a Ghostly invite. A known prefix that lost a part: damaged.\n return profile ? { ok: false, reason: \"damaged\", detail: \"Three fields expected\" } : { ok: false, reason: \"not-ghostly\" };\n }\n const [seedB64, peerPubKeyZ32, encKeyB64] = parts;\n const bad = !B64_KEY.test(seedB64) ? \"The seed\" : !Z32_KEY.test(peerPubKeyZ32) ? \"The contact's key\" : !B64_KEY.test(encKeyB64) ? \"The shared key\" : null;\n if (bad) return { ok: false, reason: \"damaged\", detail: `${bad} is malformed` };\n return {\n ok: true,\n format: profile ? (dht ? \"pair2d\" : \"pair1\") : \"legacy\",\n params: { seedB64, peerPubKeyZ32, encKeyB64, ...(profile ? { profile, ...(dht ? { deliveryMode: \"dht\" as const } : {}) } : {}) },\n };\n}\n\n/**\n * Reads an invite by the rules of WISP 801: a `ghostly1…` code in any case, bare\n * or in a link (`https://ghostly.tools/#…`, `app.ghostly.tools/#…`), or one of\n * the older slash forms. A refused code is never retried as another format.\n */\nexport function readInviteCode(input: string): InviteReading {\n const reading = readInviteForm(input);\n if (reading.ok) return reading;\n // Copied from the end of a sentence (\"…#ghostly1…).\", \"…!\"): read once more without that punctuation, taken only\n // when what is left reads. Otherwise the first refusal stands, so a typo is still a typo.\n const bare = input.trim().replace(/[.,)!]+$/, \"\");\n if (bare !== input.trim()) {\n const again = readInviteForm(bare);\n if (again.ok) return again;\n }\n return reading;\n}\n\nfunction readInviteForm(input: string): InviteReading {\n const code = inviteBody(input);\n if (!code) return { ok: false, reason: \"not-ghostly\" };\n if (/^ghostly1/i.test(code)) return readGhostly(code.toLowerCase());\n return readSlashForm(code);\n}\n\n/** The parameters of an invite, or null when it is refused (`readInviteCode` says why). */\nexport function decodeInviteCode(input: string): LinkParams | null {\n const reading = readInviteCode(input);\n return reading.ok ? reading.params : null;\n}\n\n/** Creates both ends of a link: keep `mine`, hand `invite` to the peer. */\nexport function createLink(): { mine: LinkParams; invite: LinkParams } {\n const a = createIdentity();\n const b = createIdentity();\n const encKeyB64 = toBase64Url(generateEncryptionKey());\n return {\n mine: { seedB64: a.seedB64, peerPubKeyZ32: b.pubKeyZ32, encKeyB64 },\n invite: { seedB64: b.seedB64, peerPubKeyZ32: a.pubKeyZ32, encKeyB64 },\n };\n}\n\n/**\n * A new chat, the way a current app makes one: `mine` keeps this side's\n * participation seed, `invite` carries its public key, and `inviteCode` is the\n * `ghostly1…` code to hand over.\n */\nexport function createChatInvite(): { mine: LinkParams; invite: LinkParams; inviteCode: string } {\n const { mine, invite } = createLink();\n const participation = createIdentity();\n const pairedMine: LinkParams = { ...mine, profile: \"paired-chat/1\", participationSeedB64: participation.seedB64 };\n const pairedInvite: LinkParams = { ...invite, profile: \"paired-chat/1\", peerParticipationKeyZ32: participation.pubKeyZ32 };\n return { mine: pairedMine, invite: pairedInvite, inviteCode: encodeGhostlyInvite(pairedInvite) };\n}\n\n/** A chat this side already has, as `inviteOwnership` reads it: its own seed and the contact's link key. */\nexport interface KnownLink {\n seedB64: string;\n peerPubKeyZ32: string;\n}\n\n/**\n * Whose invite a code is, among the chats a profile already has: `own` when this profile made it (the\n * invite's seed derives the contact key one of its chats waits for, which holds for every format, since\n * an inviter keeps the contact's public key), `joined` when this profile already joined by it (the\n * invite's seed is a chat's own), `new` otherwise. Another profile's chats are never passed, so its\n * invites read as new: two profiles on one app may chat with each other.\n */\nexport type InviteOwnership<T extends KnownLink> = { kind: \"own\" | \"joined\"; link: T } | { kind: \"new\" };\n\nexport function inviteOwnership<T extends KnownLink>(invite: Pick<LinkParams, \"seedB64\">, links: Iterable<T>): InviteOwnership<T> {\n const known = [...links];\n const joined = known.find((link) => link.seedB64 === invite.seedB64);\n if (joined) return { kind: \"joined\", link: joined };\n const inviteKey = identityFromSeedB64(invite.seedB64).pubKeyZ32;\n const own = known.find((link) => link.peerPubKeyZ32 === inviteKey);\n return own ? { kind: \"own\", link: own } : { kind: \"new\" };\n}\n\nconst OWN_INVITE_PREFIX = \"own-invite\";\n\n/**\n * A join refused because the invite is this profile's own (WISP 801 Q9). The message carries the reason\n * and the chat that owns the invite, so a client on the other side of an RPC (which gets the message and\n * nothing else) reads them back with `ownInviteRefusal`.\n */\nexport class OwnInviteError extends Error {\n readonly reason = OWN_INVITE_PREFIX;\n constructor(readonly linkId: string | null) {\n super(`${OWN_INVITE_PREFIX}${linkId ? ` ${linkId}` : \"\"}: This is your own invite. Share it with a contact; they join with it.`);\n this.name = \"OwnInviteError\";\n }\n}\n\n/** The refusal an error (or an error's message, as an RPC hands it over) carries, or null when it is another error. */\nexport function ownInviteRefusal(error: unknown): { linkId: string | null } | null {\n if (error instanceof OwnInviteError) return { linkId: error.linkId };\n const message = error instanceof Error ? error.message : typeof error === \"string\" ? error : \"\";\n const match = message.match(/^own-invite(?: (\\S+))?: /);\n return match ? { linkId: match[1] ?? null } : null;\n}\n","/**\n * Ephemeral services a peer offers while it is online. Chat, voice and video\n * are the services Ghostly always had; `http` exposes a local web application.\n * Unknown types are preserved so future services do not need a new protocol.\n */\nimport { sanitizeDisplayText } from \"./text\";\n\nexport type BuiltinServiceType = \"chat\" | \"voice\" | \"video\" | \"http\";\n\nexport interface ServiceAd {\n /** Stable identifier, unique per peer. Remote peers address services by id only. */\n id: string;\n type: BuiltinServiceType | (string & {});\n name?: string;\n /** Wire protocol spoken over the data link, e.g. `ghostly-http/1`. */\n proto?: string;\n meta?: Record<string, string>;\n}\n\nexport const SERVICES_VERSION = 1;\nexport const HTTP_SERVICE_PROTO = \"ghostly-http/1\";\nexport const MAX_SERVICES = 16;\nconst MAX_NAME_LENGTH = 48;\nconst MAX_META_ENTRIES = 8;\nconst MAX_META_LENGTH = 64;\nconst SERVICE_ID_PATTERN = /^[a-z0-9][a-z0-9-]{0,31}$/;\n\n/** What a peer that predates `_svc` offers implicitly. */\nexport const LEGACY_SERVICES: ServiceAd[] = [\n { id: \"chat\", type: \"chat\" },\n { id: \"voice\", type: \"voice\" },\n { id: \"video\", type: \"video\" },\n];\n\nexport function isValidServiceId(id: string): boolean {\n return SERVICE_ID_PATTERN.test(id);\n}\n\nexport function serviceIdFromName(name: string, taken: Iterable<string> = []): string {\n const used = new Set(taken);\n const base =\n name\n .toLowerCase()\n .normalize(\"NFKD\")\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .slice(0, 24) || \"service\";\n let id = base;\n for (let n = 2; used.has(id) || LEGACY_SERVICES.some((s) => s.id === id); n++) id = `${base}-${n}`;\n return id;\n}\n\ninterface WireService {\n i: string;\n t: string;\n n?: string;\n p?: string;\n m?: Record<string, string>;\n}\n\ninterface WireServices {\n v: number;\n s: (string | WireService)[];\n}\n\n/** Compact JSON; a service whose id equals its type and has no metadata is a bare string. */\nexport function servicesToWire(services: ServiceAd[]): WireServices {\n return {\n v: SERVICES_VERSION,\n s: services.map((s) => {\n if (s.id === s.type && !s.name && !s.proto && !s.meta) return s.id;\n const out: WireService = { i: s.id, t: s.type };\n if (s.name) out.n = s.name;\n if (s.proto) out.p = s.proto;\n if (s.meta && Object.keys(s.meta).length > 0) out.m = s.meta;\n return out;\n }),\n };\n}\n\nexport function encodeServices(services: ServiceAd[]): string {\n return JSON.stringify(servicesToWire(services));\n}\n\nfunction cleanString(value: unknown, max: number): string | undefined {\n // A service name is a button the user presses to run a contact's app, so it\n // must not be able to read as another service's.\n return typeof value === \"string\" ? sanitizeDisplayText(value, max) : undefined;\n}\n\n/** Parses an untrusted advertisement. Invalid entries are dropped, never thrown. */\nexport function decodeServices(json: string): ServiceAd[] | null {\n let wire: unknown;\n try {\n wire = JSON.parse(json);\n } catch {\n return null;\n }\n return servicesFromWire(wire);\n}\n\nexport function servicesFromWire(wire: unknown): ServiceAd[] | null {\n if (!wire || typeof wire !== \"object\") return null;\n const { v, s } = wire as Partial<WireServices>;\n if (typeof v !== \"number\" || v < 1 || !Array.isArray(s)) return null;\n\n const services: ServiceAd[] = [];\n const seen = new Set<string>();\n for (const entry of s.slice(0, MAX_SERVICES)) {\n let ad: ServiceAd | null = null;\n if (typeof entry === \"string\") {\n ad = { id: entry, type: entry };\n } else if (entry && typeof entry === \"object\") {\n const e = entry as Partial<WireService>;\n if (typeof e.i === \"string\" && typeof e.t === \"string\") {\n ad = { id: e.i, type: e.t };\n const name = cleanString(e.n, MAX_NAME_LENGTH);\n const proto = cleanString(e.p, MAX_META_LENGTH);\n if (name) ad.name = name;\n if (proto) ad.proto = proto;\n if (e.m && typeof e.m === \"object\") {\n const meta: Record<string, string> = {};\n for (const [k, val] of Object.entries(e.m).slice(0, MAX_META_ENTRIES)) {\n const key = cleanString(k, MAX_META_LENGTH);\n const value = cleanString(val, MAX_META_LENGTH);\n if (key && value) meta[key] = value;\n }\n if (Object.keys(meta).length > 0) ad.meta = meta;\n }\n }\n }\n if (!ad || !isValidServiceId(ad.id) || !SERVICE_ID_PATTERN.test(ad.type) || seen.has(ad.id)) continue;\n seen.add(ad.id);\n services.push(ad);\n }\n return services;\n}\n","import { encrypt, tryDecrypt } from \"./crypto\";\nimport { MAX_DNS_PACKET_BYTES, measureRecords, type GhostRecord, type SignedPacket } from \"./pkarr\";\nimport { decodeServices, encodeServices, type ServiceAd } from \"./services\";\nimport { sanitizeNick } from \"./text\";\n\n/**\n * TXT labels a peer publishes under its own key. Every value except `_ts` and\n * `_ack` is secretbox-encrypted with the link key.\n *\n * `_msgs`, `_ts`, `_ack`, `_nick` and `_call` are the original Ghost protocol.\n * `_svc` and `_rtc` are additive: clients that predate them ignore unknown\n * labels, so old and new peers keep chatting and calling each other.\n */\nexport const LABEL = {\n msgs: \"_msgs\",\n ts: \"_ts\",\n ack: \"_ack\",\n nick: \"_nick\",\n call: \"_call\",\n /** Service advertisement, see `services.ts`. */\n svc: \"_svc\",\n /** Signaling for the WebRTC data link, see `signal.ts`. */\n rtc: \"_rtc\",\n /**\n * A group link's layer-1 transports and how to dial its native ones (`encodePacketTransports` in `capsRecord.ts`):\n * published only by an app with no WebRTC and by a member answering one, so two apps that have WebRTC never send it.\n */\n tr: \"_tr\",\n} as const;\n\nexport const RECORD_TTL = 300;\n/** Same ceiling the Rust implementation applies to the encrypted message batch. */\nexport const MAX_MSGS_PAYLOAD_B64 = 800;\nconst MAX_SINGLE_MESSAGE_CHARS = 400;\n\nexport interface CompactMessage {\n t: number;\n m: string;\n}\n\nexport interface OutgoingLinkState {\n messages: CompactMessage[];\n ackTimestamp: number;\n nick?: string;\n callSignal?: string | null;\n rtcSignal?: string | null;\n /** `undefined` publishes no `_svc` at all, which is what a legacy peer looks like. */\n services?: ServiceAd[];\n /** A group link's `_tr` value, already encoded; absent publishes none. */\n transports?: string | null;\n}\n\nexport interface BuiltLinkRecords {\n records: GhostRecord[];\n /** How many of the newest messages made it into the packet. */\n keptMessages: number;\n /** False when the advertisement had to be left out to fit the packet. */\n servicesIncluded: boolean;\n}\n\nfunction record(label: string, value: string): GhostRecord {\n return { label, value, ttl: RECORD_TTL };\n}\n\n/**\n * The packet an inviter puts under the contact's key before they join, so that their first packet lands\n * as a newer one under a key the network already knows (seconds faster than a first packet under a\n * fresh key). It carries no message record, which every link session's own packet does, even empty:\n * that is how a reader tells it from a contact who is here (or was, and left without a word).\n */\nexport function emptyLinkRecords(): GhostRecord[] {\n return [record(LABEL.ts, \"0\")];\n}\n\n/** A packet nobody's link session published (see `emptyLinkRecords`): read as no packet at all. */\nexport function isEmptyLinkPacket(batch: ResolvedLink): boolean {\n return !batch.rawRecordNames.includes(LABEL.msgs);\n}\n\n/**\n * Builds the records for one publish. A Pkarr packet is at most 1000 bytes, so\n * space is handed out by priority: signaling first (it is what gets peers onto\n * WebRTC, where there is no such limit), then the service advertisement, then\n * as many of the newest messages as still fit.\n */\nexport function buildLinkRecords(\n pubKeyZ32: string,\n state: OutgoingLinkState,\n encKey: Uint8Array,\n): BuiltLinkRecords {\n const sorted = [...state.messages].sort((a, b) => a.t - b.t);\n const latestTs = sorted.length > 0 ? sorted[sorted.length - 1].t : 0;\n\n const fixed: GhostRecord[] = [record(LABEL.ts, String(latestTs))];\n if (state.ackTimestamp > 0) fixed.push(record(LABEL.ack, String(state.ackTimestamp)));\n if (state.nick) fixed.push(record(LABEL.nick, encrypt(state.nick, encKey)));\n if (state.callSignal) fixed.push(record(LABEL.call, encrypt(state.callSignal, encKey)));\n if (state.rtcSignal) fixed.push(record(LABEL.rtc, encrypt(state.rtcSignal, encKey)));\n const tr = state.transports ? record(LABEL.tr, encrypt(state.transports, encKey)) : null;\n\n const svc = state.services ? record(LABEL.svc, encrypt(encodeServices(state.services), encKey)) : null;\n\n const attempt = (withServices: boolean, withTransports = true): BuiltLinkRecords | null => {\n const head = withTransports && tr ? [...fixed, tr] : fixed;\n const base = withServices && svc ? [...head, svc] : head;\n const batch = [...sorted];\n for (;;) {\n let payload = encrypt(JSON.stringify(batch), encKey);\n if (batch.length === 1 && payload.length > MAX_MSGS_PAYLOAD_B64) {\n const truncated = { t: batch[0].t, m: [...batch[0].m].slice(0, MAX_SINGLE_MESSAGE_CHARS).join(\"\") };\n payload = encrypt(JSON.stringify([truncated]), encKey);\n }\n const records = [record(LABEL.msgs, payload), ...base];\n if (\n payload.length <= MAX_MSGS_PAYLOAD_B64 &&\n measureRecords(pubKeyZ32, records) <= MAX_DNS_PACKET_BYTES\n ) {\n return { records, keptMessages: batch.length, servicesIncluded: withServices && svc !== null };\n }\n if (batch.length === 0) return null;\n batch.shift();\n }\n };\n\n // The transports go before the advertisement, and are left out only when nothing else would fit.\n const built = attempt(true) ?? attempt(false) ?? attempt(false, false);\n if (!built) throw new Error(\"Link records do not fit in a Pkarr packet\");\n return built;\n}\n\nexport interface ResolvedMessage {\n text: string;\n timestamp: number;\n nick?: string;\n}\n\nexport interface ResolvedLink {\n messages: ResolvedMessage[];\n latestTimestamp: number;\n peerAck: number;\n nick?: string;\n callSignal: string | null;\n rtcSignal: string | null;\n /** `null` when the peer published no advertisement (legacy client or offline). */\n services: ServiceAd[] | null;\n /** A group link's `_tr` value as published (`parsePacketTransports`); `null` when there is none. */\n transports: string | null;\n rawRecordNames: string[];\n encryptedPayloadLength: number;\n /** Milliseconds since the UNIX epoch. */\n packetTimestamp: number;\n}\n\nexport function parseLinkRecords(packet: SignedPacket, encKey: Uint8Array): ResolvedLink {\n const resolved: ResolvedLink = {\n messages: [],\n latestTimestamp: 0,\n peerAck: 0,\n callSignal: null,\n rtcSignal: null,\n services: null,\n transports: null,\n rawRecordNames: [],\n encryptedPayloadLength: 0,\n packetTimestamp: Number(packet.timestampMicros / 1000n),\n };\n\n let msgsPayload = \"\";\n for (const { label, value } of packet.records) {\n resolved.rawRecordNames.push(label);\n switch (label) {\n case LABEL.msgs:\n resolved.encryptedPayloadLength = value.length;\n msgsPayload = value;\n break;\n case LABEL.ts:\n resolved.latestTimestamp = Number.parseInt(value, 10) || 0;\n break;\n case LABEL.ack:\n resolved.peerAck = Number.parseInt(value, 10) || 0;\n break;\n case LABEL.nick:\n // A nickname is shown beside every message and is copied onto each of\n // them, so it is cut and stripped before anything keeps it.\n resolved.nick = sanitizeNick(tryDecrypt(value, encKey));\n break;\n case LABEL.call:\n resolved.callSignal = tryDecrypt(value, encKey);\n break;\n case LABEL.rtc:\n resolved.rtcSignal = tryDecrypt(value, encKey);\n break;\n case LABEL.tr:\n resolved.transports = tryDecrypt(value, encKey);\n break;\n case LABEL.svc: {\n const json = tryDecrypt(value, encKey);\n if (json !== null) resolved.services = decodeServices(json);\n break;\n }\n default:\n break;\n }\n }\n\n if (msgsPayload) {\n const json = tryDecrypt(msgsPayload, encKey);\n if (json !== null) {\n try {\n const batch: unknown = JSON.parse(json);\n if (Array.isArray(batch)) {\n for (const entry of batch as Partial<CompactMessage>[]) {\n if (typeof entry?.t !== \"number\" || typeof entry?.m !== \"string\") continue;\n resolved.messages.push({ text: entry.m, timestamp: entry.t, nick: resolved.nick });\n }\n }\n } catch {\n // not a message batch\n }\n if (resolved.latestTimestamp === 0 && resolved.messages.length > 0) {\n resolved.latestTimestamp = Math.max(...resolved.messages.map((m) => m.timestamp));\n }\n }\n }\n\n return resolved;\n}\n","/**\n * Signaling for voice/video calls, carried in the `_call` record. A full SDP\n * does not fit in a Pkarr packet, so only the session-specific parameters\n * travel and each side rebuilds the SDP around them.\n */\nexport interface CallSignal {\n /**\n * offer, answer, hang-up, \"my picture changed\" while the call is up, or `r`: an offer that restarts ICE on the\n * call's own connection after its path was lost (WISP 601, \"Reconnecting\"). An app that does not know `r` drops it.\n */\n t: \"o\" | \"a\" | \"h\" | \"v\" | \"r\";\n /** The sender's clock when it made the signal: its order among that sender's signals. */\n ts: number;\n /** When this device heard a contact's signal, by its own clock. Never sent: the engine sets it (`heardCallSignal`). */\n at?: number;\n /**\n * On an answer: the `ts` of the offer it answers, so the caller takes it for that offer and no other, whatever the two\n * clocks say. Apps up to 1.0.2 send none and ignore it; they take an answer only when its `ts` is later than their\n * offer's, which a callee whose clock is behind by longer than it took to answer never met.\n */\n o?: number;\n u?: string;\n p?: string;\n f?: string;\n s?: string;\n m?: string[];\n c?: string[];\n ss?: number[];\n /** 1 while this peer is sending a picture, 0 while it is not. */\n v?: number;\n /** What the picture is: the camera or a screen. Only meaningful with `v: 1`. */\n k?: \"c\" | \"s\";\n /**\n * The payload types the sender's SDP gives Opus and VP8, when not 111 and 96 (the rebuilt SDP's own, and\n * Chromium's). WebKit offers H264 as 96 and VP8 as 106: rebuilt as `96 VP8`, the offerer read the answerer's\n * VP8 as H264 and never showed its picture.\n */\n ap?: number;\n vp?: number;\n /**\n * On a hang-up, why when it was not the person's choice: `u`, the call could not connect (no connection found a\n * candidate, or it did not connect in time). Older apps read it as a plain hang-up.\n */\n r?: \"u\";\n /**\n * On an offer or an answer: 1 when this side can restart ICE on the call's connection, so a call both sides said\n * it on reconnects after a network change instead of ending. Older apps leave it out, and ignore it.\n */\n x?: 1;\n /** On the answer to a restart offer (`r`): that offer's `ts`, so an answer to an older one is told apart. */\n re?: number;\n}\n\n/** What the rebuilt SDP gives Opus and VP8 unless the signal says otherwise. */\nconst DEFAULT_OPUS_PT = 111;\nconst DEFAULT_VP8_PT = 96;\n/** Dynamic RTP payload types: 96-127 (RFC 3551), and 35-63, which WebRTC engines use too. */\nconst dynamicPayloadType = (n: unknown): n is number => Number.isInteger(n) && (((n as number) >= 96 && (n as number) <= 127) || ((n as number) >= 35 && (n as number) <= 63));\n\nexport type CallState = \"idle\" | \"offering\" | \"incoming\" | \"answering\" | \"connecting\" | \"connected\" | \"ended\";\n\nexport type CallEventType =\n | \"call_started\"\n | \"call_received\"\n | \"call_connected\"\n | \"call_ended\"\n | \"call_missed\"\n /** Our call rang out (`RING_MS`) with nobody answering: the caller's line, as \"Missed call\" is the side it rang. */\n | \"call_unanswered\"\n /** We hung up our call while it still rang, before any answer. */\n | \"call_cancelled\"\n | \"call_rejected\"\n /** The call ended without ever connecting: no way to reach the contact was found, or none worked in time. */\n | \"call_failed\";\n\n/**\n * Where a call's media comes from: the browser's own WebRTC and capture, or a stand-in with the same shape.\n * Ghostly Desktop on Linux brings one: WebKitGTK has no WebRTC there, so GStreamer runs the call (WISP 601).\n */\nexport interface CallMedia {\n createPeerConnection(config: RTCConfiguration): RTCPeerConnection;\n getUserMedia(constraints: MediaStreamConstraints): Promise<MediaStream>;\n /** Left out where the screen cannot be shared. */\n getDisplayMedia?(options: DisplayMediaStreamOptions): Promise<MediaStream>;\n /**\n * Its connections restart ICE as a browser's do: `createOffer({ iceRestart: true })` on a connected one gives an\n * offer with new ICE credentials and candidates, and a remote offer with new credentials is answered on the same\n * connection (WISP 601, \"Reconnecting\"). The browser's own media does; a stand-in says so.\n */\n restartsIce?: boolean;\n /** Why the screen cannot be shared, where it cannot: the call window shows it on the share button, turned off. */\n screenUnavailable?: string;\n /**\n * `getUserMedia` captures from the `deviceId` asked for, and `replaceTrack` on the audio sender switches the\n * microphone: the profile's devices and the call's device menu work as with the browser's own media.\n */\n choosesDevices?: boolean;\n}\n\nexport function compressSdp(sdp: string): string {\n return btoa(sdp);\n}\n\nexport function decompressSdp(compressed: string): string {\n return atob(compressed);\n}\n\nexport const RTC_CONFIG: RTCConfiguration = {\n iceServers: [\n { urls: \"stun:stun.l.google.com:19302\" },\n { urls: \"stun:stun1.l.google.com:19302\" },\n { urls: \"stun:stun2.l.google.com:19302\" },\n { urls: \"stun:stun3.l.google.com:19302\" },\n { urls: \"stun:stun4.l.google.com:19302\" },\n ],\n iceCandidatePoolSize: 10,\n};\n\n/** An ICE server as a profile's settings keep it: one or more URLs in `urls` (space or comma separated). */\nexport interface CallIceServer { urls: string | string[]; username?: string; credential?: string }\n\n/**\n * A call's WebRTC configuration: the apps' STUN servers, then the profile's own (Settings → Network → ICE\n * servers: a TURN relay, typically). A relay is what connects a call when no direct path works: a VPN that takes\n * every packet through its tunnel, or a NAT that maps each destination apart.\n */\nexport function callRtcConfig(extra: readonly CallIceServer[] = []): RTCConfiguration {\n const servers: RTCIceServer[] = [];\n for (const server of extra) {\n const urls = (Array.isArray(server.urls) ? server.urls : server.urls.split(/[\\s,]+/)).filter(Boolean);\n if (!urls.length) continue;\n servers.push({ urls, ...(server.username ? { username: server.username } : {}), ...(server.credential ? { credential: server.credential } : {}) });\n }\n return { ...RTC_CONFIG, iceServers: [...(RTC_CONFIG.iceServers ?? []), ...servers] };\n}\n\n/**\n * How many candidates a signal on the chat session carries (`paired-call`, WISP 601): the receiver takes eight.\n * A compatibility chat's `_call` record keeps to one host and one server reflexive candidate (a DHT packet's worth).\n */\nexport const PAIRED_CALL_CANDIDATES = 8;\n\n/**\n * The candidates a signal carries, from the `a=candidate:` values of an SDP, when it may carry up to `max`.\n *\n * A computer often has several interfaces (Wi-Fi, Ethernet, VM bridges, Tailscale, a VPN) and the one listed first is\n * not always one the contact can reach: on a Mac with a VPN as its default route it is the tunnel's address, which\n * nothing on that machine can answer. So the signal carries every usable path, in this order:\n *\n * 1. host candidates on local networks, IPv4 or an mDNS name (at most four);\n * 2. the first server reflexive candidate, and relay candidates (a TURN server, when the profile has one);\n * 3. IPv6 host candidates, global ones first (at most two);\n * 4. host candidates a browser marks as costly (`network-cost` 50 or more: a VPN, or an interface it does not know);\n * 5. whatever else fits.\n *\n * UDP only, loopback, link-local and unspecified addresses left out (loopback kept with `loopback`), the related\n * address dropped (it is informational, and an IPv6 one broke apps before 0.5).\n */\nexport function pickCallCandidates(candidates: readonly string[], max = PAIRED_CALL_CANDIDATES, { loopback = false } = {}): string[] {\n const udp = candidates.map((c) => c.trim()).filter((c) => / udp /i.test(c));\n const address = (c: string) => c.split(\" \")[4] ?? \"\";\n const type = (c: string) => / typ (\\S+)/.exec(c)?.[1];\n const cost = (c: string) => Number(/ network-cost (\\d+)/.exec(c)?.[1] ?? 0);\n const ipv6 = (c: string) => address(c).includes(\":\");\n const usable = (c: string) => {\n const a = address(c).toLowerCase();\n if (a === \"::\" || a === \"0.0.0.0\" || a.startsWith(\"169.254.\") || a.startsWith(\"fe80:\")) return false;\n return loopback || !(a === \"::1\" || a.startsWith(\"127.\"));\n };\n const hosts = udp.filter((c) => type(c) === \"host\" && usable(c));\n const costly = (c: string) => cost(c) >= 50;\n const groups = [\n hosts.filter((c) => !ipv6(c) && !costly(c)).slice(0, 4),\n udp.filter((c) => type(c) === \"srflx\").slice(0, 1),\n udp.filter((c) => type(c) === \"relay\").slice(0, 2),\n [...hosts.filter((c) => ipv6(c) && !/^f[cd]/i.test(address(c)) && !costly(c)), ...hosts.filter((c) => ipv6(c) && (/^f[cd]/i.test(address(c)) || costly(c)))].slice(0, 2),\n hosts.filter((c) => !ipv6(c) && costly(c)),\n hosts,\n ];\n const picked: string[] = [];\n for (const group of groups) for (const c of group) if (picked.length < max && !picked.includes(c)) picked.push(c);\n return picked.map((c) => c.replace(/ raddr \\S+ rport \\d+/, \"\"));\n}\n\n/**\n * The parameters of a call signal, from an SDP. `maxCandidates` above two (a signal on the chat session) carries\n * the candidates `pickCallCandidates` chooses; the default keeps a compatibility chat's one host and one server\n * reflexive candidate.\n */\nexport function extractParamsFromSdp(sdp: string, { maxCandidates = 2, loopback = false }: { maxCandidates?: number; loopback?: boolean } = {}): Partial<CallSignal> {\n const lines = sdp.split(\"\\r\\n\");\n\n let ufrag = \"\";\n let pwd = \"\";\n let fingerprint = \"\";\n let setup = \"\";\n const media: string[] = [];\n const candidates: string[] = [];\n let audioSsrc: number | null = null;\n let videoSsrc: number | null = null;\n let opus: number | null = null;\n let vp8: number | null = null;\n let currentMedia = \"\";\n\n for (const line of lines) {\n if (line.startsWith(\"a=ice-ufrag:\") && !ufrag) {\n ufrag = line.substring(\"a=ice-ufrag:\".length);\n }\n if (line.startsWith(\"a=ice-pwd:\") && !pwd) {\n pwd = line.substring(\"a=ice-pwd:\".length);\n }\n if (line.startsWith(\"a=fingerprint:sha-256 \") && !fingerprint) {\n fingerprint = line\n .substring(\"a=fingerprint:sha-256 \".length)\n .replace(/:/g, \"\");\n }\n if (line.startsWith(\"a=setup:\") && !setup) {\n setup = line.substring(\"a=setup:\".length);\n }\n if (line.startsWith(\"m=audio\")) {\n media.push(\"a\");\n currentMedia = \"a\";\n }\n if (line.startsWith(\"m=video\")) {\n media.push(\"v\");\n currentMedia = \"v\";\n }\n const rtpmap = line.match(/^a=rtpmap:(\\d+) (opus\\/48000|VP8\\/90000)/i);\n if (rtpmap) {\n const pt = Number(rtpmap[1]);\n if (currentMedia === \"a\" && opus === null && /^opus/i.test(rtpmap[2])) opus = pt;\n if (currentMedia === \"v\" && vp8 === null && /^VP8/i.test(rtpmap[2])) vp8 = pt;\n }\n if (line.startsWith(\"a=candidate:\")) {\n candidates.push(line.substring(\"a=candidate:\".length));\n }\n if (line.startsWith(\"a=ssrc:\")) {\n const ssrcMatch = line.match(/^a=ssrc:(\\d+)/);\n if (ssrcMatch) {\n const ssrc = parseInt(ssrcMatch[1], 10);\n if (currentMedia === \"a\" && audioSsrc === null) {\n audioSsrc = ssrc;\n } else if (currentMedia === \"v\" && videoSsrc === null) {\n videoSsrc = ssrc;\n }\n }\n }\n }\n\n // `typ host` can end the line: browsers add `generation 0` after it, webrtc-rs (the Linux Desktop) does not.\n const srflxCandidates = candidates.filter(c => / typ srflx( |$)/.test(c));\n const hostCandidates = candidates.filter(c => / typ host( |$)/.test(c) && / udp /i.test(c));\n // Include 1 host candidate (for local connections) and 1 srflx (for remote)\n // Keep packet size under 1000 bytes DHT limit\n // An IPv6 related address (`raddr ::`, WebKit's IPv6 srflx) made apps before 0.5 refuse the whole signal:\n // the related address is informational, so it is left out.\n const selectedCandidates = maxCandidates > 2 ? pickCallCandidates(candidates, maxCandidates, { loopback }) : [\n ...hostCandidates.slice(0, 1),\n ...srflxCandidates.slice(0, 1),\n ].map(c => c.replace(/ raddr \\S*:\\S* rport \\d+/, \"\"));\n \n const ssrcs: number[] = [];\n if (audioSsrc !== null) ssrcs.push(audioSsrc);\n if (videoSsrc !== null) ssrcs.push(videoSsrc);\n \n return {\n u: ufrag,\n p: pwd,\n f: fingerprint,\n s: setup,\n m: media,\n c: selectedCandidates,\n ss: ssrcs,\n // Only when they differ from what every rebuilt SDP assumes: signals between Chromium apps stay as they were.\n ...(opus !== null && opus !== DEFAULT_OPUS_PT && dynamicPayloadType(opus) ? { ap: opus } : {}),\n ...(vp8 !== null && vp8 !== DEFAULT_VP8_PT && dynamicPayloadType(vp8) ? { vp: vp8 } : {}),\n };\n}\n\n/** Offers, answers and hang-ups older (or further in the future) than this are ignored. */\nexport const CALL_SIGNAL_MAX_AGE_MS = 120_000;\n/**\n * A signal dated further ahead of this clock than this is not taken, however it was heard. A receiver acts on a signal\n * only when its `ts` is later than the last one it acted on: taken, a signal dated a day ahead would be that last one,\n * and every offer, answer and hang-up its sender made afterwards would be dropped as older, until the app started again.\n */\nexport const CALL_SIGNAL_FUTURE_MS = 10 * 60_000;\n\n/** RFC 8839: ice-char is ALPHA / DIGIT / \"+\" / \"/\"; ufrag is 4-256 of them, pwd 22-256. */\nconst ICE_UFRAG = /^[A-Za-z0-9+/]{4,256}$/;\nconst ICE_PWD = /^[A-Za-z0-9+/]{22,256}$/;\nconst ICE_FOUNDATION = /^[A-Za-z0-9+/]{1,32}$/;\nconst CANDIDATE_ADDRESS = /^[A-Za-z0-9.:-]{1,64}$/;\nconst CANDIDATE_TYPES = new Set([\"host\", \"srflx\", \"prflx\", \"relay\"]);\nconst EXTENSION_TOKEN = /^[A-Za-z0-9+/._-]{1,64}$/;\nconst DIGITS = /^\\d{1,10}$/;\nconst MAX_CANDIDATES = 8;\n\nfunction uint(value: string, max: number): number | null {\n if (!DIGITS.test(value)) return null;\n const n = Number(value);\n return n <= max ? n : null;\n}\n\n/**\n * Parses the part of an `a=candidate:` line that the signal carries\n * (`foundation component transport priority address port typ type [raddr a rport p] [ext value]...`)\n * and serializes it again from the validated parts. Returns `undefined` for a\n * well-formed candidate that is not UDP (skipped) and `null` for anything malformed.\n */\nfunction normalizeCandidate(candidate: unknown): string | null | undefined {\n if (typeof candidate !== \"string\" || candidate.length > 512) return null;\n const parts = candidate.split(\" \");\n if (parts.length < 8 || parts[6] !== \"typ\") return null;\n const [foundation, componentStr, transport, priorityStr, address, portStr, , type] = parts;\n const component = uint(componentStr, 256);\n const priority = uint(priorityStr, 0xffffffff);\n const port = uint(portStr, 65535);\n if (!ICE_FOUNDATION.test(foundation) || component === null || component < 1) return null;\n if (!/^[A-Za-z]{1,8}$/.test(transport) || priority === null || port === null) return null;\n if (!CANDIDATE_ADDRESS.test(address) || !CANDIDATE_TYPES.has(type)) return null;\n\n let raddr: string | null = null;\n let rport: number | null = null;\n const rest = parts.slice(8);\n if (rest.length % 2 !== 0) return null;\n for (let i = 0; i < rest.length; i += 2) {\n const [name, value] = [rest[i], rest[i + 1]];\n if (!EXTENSION_TOKEN.test(name)) return null;\n // raddr is an address, IPv6 too (`raddr ::`, as WebKit gives an IPv6 srflx): the address pattern, not the token one.\n if (name === \"raddr\") {\n if (!CANDIDATE_ADDRESS.test(value)) return null;\n raddr = value;\n } else if (name === \"rport\") {\n rport = uint(value, 65535);\n if (rport === null) return null;\n } else if (!EXTENSION_TOKEN.test(value)) return null;\n // Other extension attributes (generation, network-id, ufrag...) are dropped.\n }\n\n if (transport.toLowerCase() !== \"udp\") return undefined;\n let line = `${foundation} ${component} udp ${priority} ${address} ${port} typ ${type}`;\n if (raddr !== null && rport !== null) line += ` raddr ${raddr} rport ${rport}`;\n return line;\n}\n\n/**\n * Validates an untrusted `_call` signal (from the Pkarr record or a `call`\n * frame). Every value that ends up in the rebuilt SDP is checked against an\n * anchored pattern and candidates are re-serialized from their parts, so a peer\n * cannot inject SDP lines. Signals outside {@link CALL_SIGNAL_MAX_AGE_MS} of\n * `now` are rejected so that a stale packet does not ring.\n */\nexport function parseCallSignal(json: string, now = Date.now()): CallSignal | null {\n let raw: Record<string, unknown>;\n try {\n raw = JSON.parse(json);\n } catch {\n return null;\n }\n if (!raw || typeof raw !== \"object\" || Array.isArray(raw)) return null;\n if (raw.t !== \"o\" && raw.t !== \"a\" && raw.t !== \"h\" && raw.t !== \"v\" && raw.t !== \"r\") return null;\n if (typeof raw.ts !== \"number\" || !Number.isFinite(raw.ts)) return null;\n if (raw.ts > now + CALL_SIGNAL_FUTURE_MS) return null;\n // How old it is, on this clock when the engine heard it come (`heardCallSignal`), by its own time otherwise.\n const heard = typeof raw.at === \"number\" && Number.isFinite(raw.at) ? { at: raw.at } : {};\n if (Math.abs(now - (heard.at ?? raw.ts)) > CALL_SIGNAL_MAX_AGE_MS) return null;\n if (raw.t === \"h\") return raw.r === \"u\" ? { t: \"h\", ts: raw.ts, r: \"u\", ...heard } : { t: \"h\", ts: raw.ts, ...heard };\n\n const picture = parsePicture(raw);\n if (picture === null) return null;\n // A media state carries no ICE: it only says whether a picture is on, and which.\n if (raw.t === \"v\") return { t: \"v\", ts: raw.ts, ...picture, ...heard };\n\n if (typeof raw.u !== \"string\" || !ICE_UFRAG.test(raw.u)) return null;\n if (typeof raw.p !== \"string\" || !ICE_PWD.test(raw.p)) return null;\n if (typeof raw.f !== \"string\" || !/^[0-9a-fA-F]{64}$/.test(raw.f)) return null;\n if (raw.s !== \"actpass\" && raw.s !== \"active\" && raw.s !== \"passive\") return null;\n\n let media: string[] | undefined;\n if (raw.m !== undefined) {\n if (!Array.isArray(raw.m) || raw.m.length < 1 || raw.m.length > 2) return null;\n if (!raw.m.every((m) => m === \"a\" || m === \"v\") || new Set(raw.m).size !== raw.m.length) return null;\n media = [...raw.m];\n }\n\n let ssrcs: number[] | undefined;\n if (raw.ss !== undefined) {\n if (!Array.isArray(raw.ss) || raw.ss.length > 2) return null;\n if (!raw.ss.every((n) => Number.isInteger(n) && n >= 0 && n <= 0xffffffff)) return null;\n ssrcs = [...raw.ss];\n }\n\n const candidates: string[] = [];\n if (raw.c !== undefined) {\n if (!Array.isArray(raw.c) || raw.c.length > MAX_CANDIDATES) return null;\n for (const c of raw.c) {\n const normalized = normalizeCandidate(c);\n if (normalized === null) return null;\n if (normalized !== undefined) candidates.push(normalized);\n }\n }\n\n const payloadTypes: Pick<CallSignal, \"ap\" | \"vp\"> = {};\n for (const key of [\"ap\", \"vp\"] as const) {\n const value = raw[key];\n if (value === undefined) continue;\n if (!dynamicPayloadType(value)) return null;\n payloadTypes[key] = value;\n }\n if (payloadTypes.ap !== undefined && payloadTypes.ap === payloadTypes.vp) return null;\n\n // Any other value of `x` says nothing. `re` only means something on an answer.\n const restart: Pick<CallSignal, \"x\" | \"re\"> = raw.x === 1 ? { x: 1 } : {};\n if (raw.re !== undefined) {\n if (raw.t !== \"a\" || typeof raw.re !== \"number\" || !Number.isFinite(raw.re)) return null;\n restart.re = raw.re;\n }\n\n // `o` names the offer an answer is for; on anything else it says nothing.\n if (raw.o !== undefined && (typeof raw.o !== \"number\" || !Number.isFinite(raw.o))) return null;\n const answers = raw.t === \"a\" && raw.o !== undefined ? { o: raw.o } : {};\n\n return { t: raw.t, ts: raw.ts, u: raw.u, p: raw.p, f: raw.f, s: raw.s, m: media, c: candidates, ss: ssrcs, ...picture, ...payloadTypes, ...restart, ...answers, ...heard };\n}\n\n/**\n * A contact's signal as the engine hands it on: `at` says when this device heard it, by its own clock (absent: nothing\n * says, as for one found in a record). Whatever `at` the signal came with is dropped: it is never the sender's to say.\n *\n * A signal's `ts` is its sender's clock. It orders that sender's signals, and settles two calls made at once the same\n * way on both sides. It cannot say how long ago the signal was made, nor whether it came before or after something this\n * device did: two devices' clocks are often minutes apart. Held against this clock, a contact two minutes off never\n * rang here, and an answer from a contact whose clock was behind by longer than it took to answer was dropped as\n * \"from before the offer\", so the call never connected.\n */\nexport function heardCallSignal(json: string, at?: number): string {\n let raw: unknown;\n try { raw = JSON.parse(json); } catch { return json; }\n if (!raw || typeof raw !== \"object\" || Array.isArray(raw)) return json;\n const { at: _theirs, ...signal } = raw as Record<string, unknown>;\n return JSON.stringify(at === undefined ? signal : { ...signal, at });\n}\n\n/**\n * When a contact's signal came, to hold against something this device did (its own offer): on this clock when the\n * engine said (`at`), by the signal's own time otherwise, as before.\n */\nexport const callSignalHeardAt = (signal: Pick<CallSignal, \"ts\" | \"at\">): number => signal.at ?? signal.ts;\n\n/**\n * Whether a contact's answer is for the offer this side made at `offerTs` (its own clock). An answer that names its\n * offer (`o`) says so itself. One that names none (an app up to 1.0.2) is taken when it came after this side offered.\n */\nexport function answersOffer(answer: Pick<CallSignal, \"ts\" | \"at\" | \"o\">, offerTs: number): boolean {\n return answer.o !== undefined ? answer.o === offerTs : callSignalHeardAt(answer) > offerTs;\n}\n\n/** The `v`/`k` pair of any signal. Returns null for a malformed one, `{}` when it says nothing. */\nfunction parsePicture(raw: Record<string, unknown>): Pick<CallSignal, \"v\" | \"k\"> | null {\n const picture: Pick<CallSignal, \"v\" | \"k\"> = {};\n if (raw.v !== undefined) {\n if (raw.v !== 0 && raw.v !== 1) return null;\n picture.v = raw.v;\n }\n if (raw.k !== undefined) {\n if (raw.k !== \"c\" && raw.k !== \"s\") return null;\n picture.k = raw.k;\n }\n return picture;\n}\n\n/**\n * Whether the peer that sent this offer or answer is sending a picture.\n *\n * Since v2 every call negotiates a video section, on or off, so the media list\n * no longer answers this: `v` does. A v1 peer sends no `v`, and there the video\n * section only carries an SSRC when it is really sending on it.\n */\nexport function signalHasVideo(signal: CallSignal | null): boolean {\n if (!signal) return false;\n if (signal.v !== undefined) return signal.v === 1;\n return (signal.m?.includes(\"v\") ?? false) && (signal.ss?.length ?? 0) > 1;\n}\n\n/**\n * Rebuilds an SDP around a signal. Only pass signals returned by {@link parseCallSignal}.\n *\n * Offer and answer are built the same way: which of the two this is comes from\n * the signal itself, as `a=setup:`, not from the caller.\n */\nexport function buildSdpFromSignal(signal: CallSignal): string {\n const hexPairs = signal.f!.match(/.{2}/g)!;\n const fingerprint = `sha-256 ${hexPairs.join(\":\")}`;\n \n const mediaOrder = signal.m ?? [\"a\"];\n \n // One session for every description of one connection (its fingerprint names it): a restart offer and its answer\n // describe the session the call already has, a version later.\n const sessionId = parseInt(signal.f!.slice(0, 12), 16);\n const sessionVersion = signal.t === \"r\" || signal.re !== undefined ? Math.max(3, Math.floor(signal.ts)) : 2;\n const audioSsrc = signal.ss?.[0] ?? Math.floor(Math.random() * 0xFFFFFFFF);\n const videoSsrc = signal.ss?.[1] ?? Math.floor(Math.random() * 0xFFFFFFFF);\n const opus = signal.ap ?? DEFAULT_OPUS_PT;\n const vp8 = signal.vp ?? DEFAULT_VP8_PT;\n\n const lines: string[] = [\n \"v=0\",\n `o=- ${sessionId} ${sessionVersion} IN IP4 127.0.0.1`,\n \"s=-\",\n \"t=0 0\",\n ];\n\n const mids = mediaOrder.map((_, i) => String(i));\n lines.push(`a=group:BUNDLE ${mids.join(\" \")}`);\n lines.push(\"a=msid-semantic: WMS stream\");\n\n mediaOrder.forEach((mediaType, idx) => {\n const mid = String(idx);\n \n if (mediaType === \"a\") {\n lines.push(\n `m=audio 9 UDP/TLS/RTP/SAVPF ${opus}`,\n \"c=IN IP4 0.0.0.0\",\n \"a=rtcp:9 IN IP4 0.0.0.0\",\n );\n for (const c of signal.c ?? []) {\n lines.push(`a=candidate:${c}`);\n }\n lines.push(\n \"a=end-of-candidates\",\n `a=ice-ufrag:${signal.u}`,\n `a=ice-pwd:${signal.p}`,\n `a=fingerprint:${fingerprint}`,\n `a=setup:${signal.s}`,\n `a=mid:${mid}`,\n \"a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\",\n \"a=sendrecv\",\n \"a=msid:stream audio0\",\n \"a=rtcp-mux\",\n `a=rtpmap:${opus} opus/48000/2`,\n `a=fmtp:${opus} minptime=10;useinbandfec=1`,\n `a=ssrc:${audioSsrc} cname:pkarr`,\n `a=ssrc:${audioSsrc} msid:stream audio0`,\n );\n } else if (mediaType === \"v\") {\n lines.push(\n `m=video 9 UDP/TLS/RTP/SAVPF ${vp8}`,\n \"c=IN IP4 0.0.0.0\",\n \"a=rtcp:9 IN IP4 0.0.0.0\",\n );\n for (const c of signal.c ?? []) {\n lines.push(`a=candidate:${c}`);\n }\n lines.push(\n \"a=end-of-candidates\",\n `a=ice-ufrag:${signal.u}`,\n `a=ice-pwd:${signal.p}`,\n `a=fingerprint:${fingerprint}`,\n `a=setup:${signal.s}`,\n `a=mid:${mid}`,\n // No header extensions here: their ids differ between engines, and an\n // answer that maps an id differently from the real offer is rejected\n // by Chromium (\"Failed to set recv parameters\").\n \"a=sendrecv\",\n `a=msid:stream video0`,\n \"a=rtcp-mux\",\n \"a=rtcp-rsize\",\n `a=rtpmap:${vp8} VP8/90000`,\n `a=rtcp-fb:${vp8} ccm fir`,\n `a=rtcp-fb:${vp8} nack`,\n `a=rtcp-fb:${vp8} nack pli`,\n `a=rtcp-fb:${vp8} goog-remb`,\n `a=ssrc:${videoSsrc} cname:pkarr`,\n `a=ssrc:${videoSsrc} msid:stream video0`,\n );\n }\n });\n\n return lines.join(\"\\r\\n\") + \"\\r\\n\";\n}\n\n/** Whether a description carries any candidate: one without cannot be reached, nor reach anyone. */\nexport function sdpHasCandidates(sdp: string | undefined | null): boolean {\n return /^a=candidate:/m.test(sdp ?? \"\");\n}\n\n/** How long to keep collecting after the candidate we were waiting for showed up. */\nconst ICE_SETTLE_MS = 400;\n\n/**\n * Resolves when the local description is good enough to publish. Waiting for\n * gathering to *complete* takes the full timeout whenever one STUN server or\n * address family does not answer, which made every call ring ~10 s late. The\n * signal only carries a host and a server reflexive candidate anyway (plus a\n * relay one when TURN is configured), so that is what is waited for.\n */\nexport function waitForIceGathering(\n pc: RTCPeerConnection,\n timeoutMs = 10000,\n /**\n * Give up after this long when not a single candidate showed up. Host candidates come in a few milliseconds; a\n * connection with none by then has stalled (Chromium, rarely, under load) and will not find any later.\n */\n {\n stallMs,\n /**\n * The description was just set on a connection that gathered before (an ICE restart): \"complete\" may still be\n * the last round's, so it only counts once the description has a candidate of this round.\n */\n fresh = false,\n }: { stallMs?: number; fresh?: boolean } = {},\n): Promise<void> {\n return new Promise((resolve) => {\n if (pc.iceGatheringState === \"complete\" && (!fresh || sdpHasCandidates(pc.localDescription?.sdp))) {\n resolve();\n return;\n }\n\n const usesTurn = (pc.getConfiguration().iceServers ?? []).some((server) =>\n [server.urls].flat().some((url) => url.startsWith(\"turn\")),\n );\n const wanted = usesTurn ? \" typ relay\" : \" typ srflx\";\n let settle: ReturnType<typeof setTimeout> | null = null;\n let found = false;\n\n const finish = () => {\n clearTimeout(timeout);\n if (stalled) clearTimeout(stalled);\n if (settle) clearTimeout(settle);\n pc.removeEventListener(\"icegatheringstatechange\", onState);\n pc.removeEventListener(\"icecandidate\", onCandidate);\n resolve();\n };\n const timeout = setTimeout(finish, timeoutMs);\n const stalled = stallMs === undefined ? null : setTimeout(() => {\n if (!found && !sdpHasCandidates(pc.localDescription?.sdp)) finish();\n }, stallMs);\n\n const onState = () => {\n if (pc.iceGatheringState === \"complete\" && (!fresh || sdpHasCandidates(pc.localDescription?.sdp))) finish();\n };\n const onCandidate = (event: RTCPeerConnectionIceEvent) => {\n if (event.candidate) found = true;\n if (!settle && event.candidate?.candidate.includes(wanted)) settle = setTimeout(finish, ICE_SETTLE_MS);\n };\n\n pc.addEventListener(\"icegatheringstatechange\", onState);\n pc.addEventListener(\"icecandidate\", onCandidate);\n });\n}\n","/**\n * Signaling for the WebRTC data link, carried in the `_rtc` record. It follows\n * the convention `_call` established: only what is unique to the session\n * travels through Pkarr and the SDP is rebuilt locally. The DTLS fingerprint\n * arrives inside a packet that is signed by the peer's key and encrypted with\n * the link key, which is what binds the WebRTC connection to the peer identity.\n */\nexport interface RtcSignal {\n t: \"o\" | \"a\";\n ts: number;\n /** For answers: `ts` of the offer being answered. */\n o?: number;\n /** ICE ufrag */\n u: string;\n /** ICE pwd */\n p: string;\n /** DTLS SHA-256 fingerprint, hex without separators */\n f: string;\n /** DTLS setup role */\n s: string;\n /** Candidates as `<h|s|r>,<address>,<port>` (host, server reflexive, relay) */\n c: string[];\n}\n\nexport const DATA_CHANNEL_LABEL = \"ghostly/1\";\nexport const DATA_CHANNEL_ID = 0;\n/** An offer older than this is ignored. */\nexport const RTC_SIGNAL_MAX_AGE_MS = 120_000;\n/** A signal dated further ahead of this clock than this is not taken: its time would outrank every later one of its sender. */\nexport const RTC_SIGNAL_FUTURE_MS = 10 * 60_000;\n/**\n * How this device saw a signal come. `since`: when its read of the maker's record that did not have the signal began,\n * by this clock; only a read the network answered counts, never a copy kept from before. `after`: the time of the\n * maker's own packet that read found, by the maker's clock; null when that read found none of the maker's.\n */\nexport interface SignalSight { since: number; after: number | null; live?: boolean }\n/**\n * Whether an offer its maker dated `ts` is recent enough to answer.\n *\n * With a `sight`, two things place it in time without holding the maker's clock against this one. It was not in the\n * maker's record at `since`, so it reached the record after that; and it is not dated before the maker's packet that\n * was there (`after`, the same clock as `ts`), so it was not made before that packet either. An offer made earlier and\n * put in the record again later fails the second. Its time is then held between `since` and now, and only this clock\n * measures its age.\n *\n * When that read found nothing of the maker's (`after` null: a first contact), there is no packet to hold the offer\n * against, and a record that looked empty a moment ago is weaker ground: the offer's own time then has to be within\n * `RTC_SIGNAL_FUTURE_MS` of this clock too, which is as far as two clocks are taken to differ.\n *\n * Without a sight (the first read of a run found the offer) its own time is all there is to go by, and it may differ\n * from this clock by the age allowed, either way.\n */\nexport function offerIsFresh(ts: number, sight?: SignalSight, now = Date.now()): boolean {\n if (!sight) return Math.abs(now - ts) <= RTC_SIGNAL_MAX_AGE_MS;\n if (!sight.live && sight.after !== null && ts < sight.after) return false;\n if (!sight.live && sight.after === null && Math.abs(now - ts) > RTC_SIGNAL_FUTURE_MS) return false;\n return now - Math.max(sight.since, Math.min(ts, now)) <= RTC_SIGNAL_MAX_AGE_MS;\n}\nconst MAX_MESSAGE_SIZE = 262_144;\n\nconst CANDIDATE_TYPES = { host: \"h\", srflx: \"s\", relay: \"r\" } as const;\nconst CANDIDATE_LIMITS: Record<string, number> = { h: 2, s: 2, r: 1 };\nconst CANDIDATE_PRIORITY: Record<string, number> = { h: 2122260223, s: 1686052607, r: 41885439 };\nconst CANDIDATE_TYPE_NAME: Record<string, string> = { h: \"host\", s: \"srflx\", r: \"relay\" };\n\nexport function extractRtcParams(sdp: string): Pick<RtcSignal, \"u\" | \"p\" | \"f\" | \"s\" | \"c\"> {\n let u = \"\";\n let p = \"\";\n let f = \"\";\n let s = \"\";\n const candidates: string[] = [];\n const counts: Record<string, number> = { h: 0, s: 0, r: 0 };\n\n for (const line of sdp.split(/\\r?\\n/)) {\n if (line.startsWith(\"a=ice-ufrag:\") && !u) u = line.slice(\"a=ice-ufrag:\".length);\n else if (line.startsWith(\"a=ice-pwd:\") && !p) p = line.slice(\"a=ice-pwd:\".length);\n else if (line.toLowerCase().startsWith(\"a=fingerprint:sha-256 \") && !f) {\n f = line.slice(\"a=fingerprint:sha-256 \".length).replace(/:/g, \"\").toLowerCase();\n } else if (line.startsWith(\"a=setup:\") && !s) s = line.slice(\"a=setup:\".length);\n else if (line.startsWith(\"a=candidate:\")) {\n // foundation component transport priority address port typ <type> ...\n const parts = line.slice(\"a=candidate:\".length).split(\" \");\n if (parts.length < 8 || parts[1] !== \"1\" || parts[2].toLowerCase() !== \"udp\") continue;\n const type = CANDIDATE_TYPES[parts[7] as keyof typeof CANDIDATE_TYPES];\n if (!type || counts[type] >= CANDIDATE_LIMITS[type]) continue;\n const compact = `${type},${parts[4]},${parts[5]}`;\n if (candidates.includes(compact)) continue;\n counts[type]++;\n candidates.push(compact);\n }\n }\n return { u, p, f, s, c: candidates };\n}\n\nconst SAFE_TOKEN = /^[A-Za-z0-9+/=_-]{1,256}$/;\nconst SAFE_ADDRESS = /^[A-Za-z0-9.:-]{1,64}$/;\n\n/** Validates an untrusted signal; anything that could inject SDP lines is rejected. */\nexport function parseRtcSignal(json: string): RtcSignal | null {\n let raw: Partial<RtcSignal>;\n try {\n raw = JSON.parse(json);\n } catch {\n return null;\n }\n if (!raw || typeof raw !== \"object\") return null;\n if (raw.t !== \"o\" && raw.t !== \"a\") return null;\n if (typeof raw.ts !== \"number\" || !Number.isFinite(raw.ts)) return null;\n if (raw.o !== undefined && typeof raw.o !== \"number\") return null;\n if (typeof raw.u !== \"string\" || !SAFE_TOKEN.test(raw.u)) return null;\n if (typeof raw.p !== \"string\" || !SAFE_TOKEN.test(raw.p)) return null;\n if (typeof raw.f !== \"string\" || !/^[0-9a-fA-F]{64}$/.test(raw.f)) return null;\n if (raw.s !== \"actpass\" && raw.s !== \"active\" && raw.s !== \"passive\") return null;\n if (!Array.isArray(raw.c) || raw.c.length > 8) return null;\n for (const c of raw.c) {\n if (typeof c !== \"string\") return null;\n const [type, address, port] = c.split(\",\");\n if (!CANDIDATE_TYPE_NAME[type] || !SAFE_ADDRESS.test(address ?? \"\") || !/^\\d{1,5}$/.test(port ?? \"\")) {\n return null;\n }\n }\n return { t: raw.t, ts: raw.ts, o: raw.o, u: raw.u, p: raw.p, f: raw.f, s: raw.s, c: raw.c };\n}\n\nexport function buildDataSdp(signal: RtcSignal): string {\n const fingerprint = signal.f.toUpperCase().match(/.{2}/g)!.join(\":\");\n const sessionId = Math.floor(Math.random() * 1e15);\n\n const lines = [\n \"v=0\",\n `o=- ${sessionId} 2 IN IP4 127.0.0.1`,\n \"s=-\",\n \"t=0 0\",\n \"a=group:BUNDLE 0\",\n \"a=msid-semantic: WMS\",\n \"m=application 9 UDP/DTLS/SCTP webrtc-datachannel\",\n \"c=IN IP4 0.0.0.0\",\n ];\n signal.c.forEach((c, index) => {\n const [type, address, port] = c.split(\",\");\n const priority = CANDIDATE_PRIORITY[type] - index;\n let line = `a=candidate:${index + 1} 1 udp ${priority} ${address} ${port} typ ${CANDIDATE_TYPE_NAME[type]}`;\n if (type !== \"h\") line += \" raddr 0.0.0.0 rport 0\";\n lines.push(line);\n });\n lines.push(\n \"a=end-of-candidates\",\n `a=ice-ufrag:${signal.u}`,\n `a=ice-pwd:${signal.p}`,\n `a=fingerprint:sha-256 ${fingerprint}`,\n `a=setup:${signal.s}`,\n \"a=mid:0\",\n \"a=sctp-port:5000\",\n `a=max-message-size:${MAX_MESSAGE_SIZE}`,\n );\n return lines.join(\"\\r\\n\") + \"\\r\\n\";\n}\n","import { utf8Encode } from \"./bytes\";\nimport { sanitizeNick } from \"./text\";\n\n/**\n * Framing on the `ghostly/1` DataChannel (ordered, reliable).\n *\n * - Text messages are control frames: compact JSON with a `t` discriminator,\n * the same shape Ghostly already uses for `_msgs` and `_call`.\n * - Binary messages are body chunks that belong to a stream opened by a\n * control frame: `<kind u8><stream id u32 BE><flags u8><payload>`.\n *\n * Everything here handles data from the remote peer and must never trust it.\n */\nexport const LIMITS = {\n /** Largest control frame accepted, headers included. */\n maxControlFrameBytes: 60 * 1024,\n /** Largest DataChannel message sent. 16 KiB is safe across WebRTC stacks. */\n maxChunkMessageBytes: 16 * 1024,\n maxRequestBodyBytes: 8 * 1024 * 1024,\n maxResponseBodyBytes: 64 * 1024 * 1024,\n maxHeaderCount: 128,\n maxHeaderValueBytes: 16 * 1024,\n maxPathBytes: 8 * 1024,\n /** Requests a host serves at once for one peer. */\n maxConcurrentRequestsPerPeer: 32,\n /** Requests a client keeps in flight; the rest wait in a queue. */\n maxClientInFlight: 16,\n maxChatMessageBytes: 16 * 1024,\n maxFileBytes: 100 * 1024 * 1024,\n /** Files a peer may be sending us at once. */\n maxIncomingFilesPerPeer: 3,\n /** Received bytes kept for one peer, in flight included. Past it new files are refused. */\n maxStoredIncomingBytesPerPeer: 500 * 1024 * 1024,\n requestTimeoutMs: 60_000,\n bodyIdleTimeoutMs: 30_000,\n /** Stop writing to the channel above this many buffered bytes. */\n sendHighWaterMark: 1024 * 1024,\n sendLowWaterMark: 256 * 1024,\n} as const;\n\nexport const PROTOCOL_VERSION = 1;\n\nexport const CHUNK_KIND = { requestBody: 1, responseBody: 2, fileBody: 3 } as const;\nexport type ChunkKind = (typeof CHUNK_KIND)[keyof typeof CHUNK_KIND];\n\nexport const CHUNK_FLAG_END = 1;\nconst CHUNK_HEADER_BYTES = 6;\nexport const MAX_CHUNK_PAYLOAD_BYTES = LIMITS.maxChunkMessageBytes - CHUNK_HEADER_BYTES;\n\nexport type HeaderList = [string, string][];\n\n/** First frame in each direction once the channel opens. */\nexport interface HelloFrame {\n t: \"hello\";\n v: number;\n /** Authoritative service list, same wire shape as the `_svc` record. */\n svc: unknown;\n nick?: string;\n}\n\n/** Chat message. Used instead of `_msgs` while the data link is up. */\nexport interface ChatFrame {\n t: \"m\";\n ts: number;\n m: string;\n}\n\n/** A `_call` signal, sent here as well so a connected peer rings without waiting for its next poll. */\nexport interface CallFrame {\n t: \"call\";\n s: string;\n}\n\n/** Updated service list while connected. */\nexport interface ServicesFrame {\n t: \"svc\";\n svc: unknown;\n}\n\nexport interface HttpRequestFrame {\n t: \"req\";\n id: number;\n /** Service id. The host maps it to a target; the peer never names a URL. */\n s: string;\n m: string;\n /** Origin-form path with query, e.g. `/api/items?page=2` */\n p: string;\n h: HeaderList;\n /** True when request body chunks follow. */\n b: boolean;\n}\n\nexport interface HttpResponseFrame {\n t: \"res\";\n id: number;\n st: number;\n h: HeaderList;\n /** True when response body chunks follow. */\n b: boolean;\n}\n\n/** Announces a file; its bytes follow as `fileBody` chunks on stream `id`. */\nexport interface FileFrame {\n t: \"file\";\n id: number;\n /** File id chosen by the sender, unique within the link. */\n f: string;\n ts: number;\n n: string;\n /** Size in bytes. The transfer fails unless exactly this many arrive. */\n s: number;\n m: string;\n}\n\n/**\n * A way to get paid, in Paykit's terms: a Payment Endpoint Identifier\n * (`btc-lightning-bolt11`, `cashu`, …) and its payload (an invoice, the mints\n * accepted, …). See `payments.ts`.\n */\nexport type WireEndpoint = [identifier: string, payload: string];\n\n/** Payment Request: the sender asks to be paid. */\nexport interface PayRequestFrame {\n t: \"pay-req\";\n /** Request id, chosen by the payee. */\n id: string;\n ts: number;\n /** Decimal amount as text, and its asset (`sat`). */\n v: string;\n u: string;\n memo?: string;\n e: WireEndpoint[];\n /** The `pay-ask` this answers, if any. */\n a?: string;\n /**\n * `mainnet` (real money) or `testnet` (test coins): what pays it. Absent from apps before wallets had their own\n * network; the endpoints tell then (a test mint, a test chain).\n */\n n?: \"mainnet\" | \"testnet\";\n}\n\n/** A payer asking to pay: the payee answers with a `pay-req` whose `a` is this id. */\nexport interface PayAskFrame {\n t: \"pay-ask\";\n id: string;\n ts: number;\n v: string;\n u: string;\n m: \"arkade\" | \"usdt\" | \"bark\" | \"bitcoin\" | \"fedimint\" | \"spark\";\n memo?: string;\n /** The network the payer will pay from: the payee answers from its wallet of that network. */\n n?: \"mainnet\" | \"testnet\";\n}\n\n/** A payment that travels in band, such as an ecash token. */\nexport interface PayFrame {\n t: \"pay\";\n id: string;\n ts: number;\n /** The request this settles, if any. */\n rid?: string;\n v: string;\n u: string;\n memo?: string;\n /** The endpoint used and the payment itself. */\n e: WireEndpoint;\n}\n\n/** What became of a payment or a request: the payee's receipt, or a refusal. */\nexport interface PayResultFrame {\n t: \"pay-res\";\n /** Id of the `pay` or `pay-req` frame this answers. */\n id: string;\n ok: boolean;\n /** Amount credited, when it differs from what was sent (fees). */\n v?: string;\n err?: string;\n /** On the payee's own `pay-req`: closed for good, never to be paid (its wallet was removed). */\n c?: true;\n}\n\n/** Aborts a stream in either direction. */\nexport interface ResetFrame {\n t: \"rst\";\n id: number;\n /** Which stream: `q` a request I sent, `s` a response I am sending, `f` a file (either side). */\n d: \"q\" | \"s\" | \"f\";\n e: string;\n}\n\nexport interface PingFrame {\n t: \"ping\" | \"pong\";\n ts: number;\n}\n\nexport type ControlFrame =\n | PayAskFrame\n | HelloFrame\n | ChatFrame\n | CallFrame\n | ServicesFrame\n | HttpRequestFrame\n | HttpResponseFrame\n | FileFrame\n | PayRequestFrame\n | PayFrame\n | PayResultFrame\n | ResetFrame\n | PingFrame;\n\nexport interface ChunkFrame {\n kind: ChunkKind;\n id: number;\n end: boolean;\n payload: Uint8Array;\n}\n\nexport function encodeControl(frame: ControlFrame): string {\n const json = JSON.stringify(frame);\n if (utf8Encode(json).length > LIMITS.maxControlFrameBytes) throw new Error(\"Control frame too large\");\n return json;\n}\n\nfunction isHeaderList(value: unknown): value is HeaderList {\n if (!Array.isArray(value) || value.length > LIMITS.maxHeaderCount) return false;\n return value.every(\n (h) =>\n Array.isArray(h) &&\n h.length === 2 &&\n typeof h[0] === \"string\" &&\n typeof h[1] === \"string\" &&\n h[0].length <= 256 &&\n h[1].length <= LIMITS.maxHeaderValueBytes,\n );\n}\n\nfunction isStreamId(value: unknown): value is number {\n return typeof value === \"number\" && Number.isInteger(value) && value >= 0 && value <= 0xffffffff;\n}\n\nfunction isPayId(value: unknown): value is string {\n return typeof value === \"string\" && /^[A-Za-z0-9_-]{8,64}$/.test(value);\n}\n\n/** Decimal text, as in Paykit's Payment Amount: no floats on the wire. */\nfunction isAmount(value: unknown): value is string {\n return typeof value === \"string\" && /^(0|[1-9]\\d{0,15})(\\.\\d{1,8})?$/.test(value);\n}\n\nfunction isUnit(value: unknown): value is string {\n return typeof value === \"string\" && /^[a-z0-9]{1,12}$/.test(value);\n}\n\nfunction isEndpoint(value: unknown): value is WireEndpoint {\n return (\n Array.isArray(value) &&\n value.length === 2 &&\n typeof value[0] === \"string\" &&\n value[0].length <= 64 &&\n /^[a-z0-9][a-z0-9-]*(?:\\/[1-9][0-9]*)?$/.test(value[0]) &&\n typeof value[1] === \"string\" &&\n value[1].length <= 32 * 1024\n );\n}\n\n/** Returns null for anything malformed; callers drop the frame. */\nexport function decodeControl(text: string): ControlFrame | null {\n // A character is at least one byte: the cheap check first, then the real one.\n if (text.length > LIMITS.maxControlFrameBytes || utf8Encode(text).length > LIMITS.maxControlFrameBytes) return null;\n let raw: unknown;\n try {\n raw = JSON.parse(text);\n } catch {\n return null;\n }\n if (!raw || typeof raw !== \"object\") return null;\n const f = raw as Record<string, unknown>;\n\n switch (f.t) {\n case \"hello\":\n if (typeof f.v !== \"number\") return null;\n return { t: \"hello\", v: f.v, svc: f.svc, nick: sanitizeNick(f.nick) };\n case \"m\":\n if (typeof f.ts !== \"number\" || typeof f.m !== \"string\") return null;\n if (utf8Encode(f.m).length > LIMITS.maxChatMessageBytes) return null;\n return { t: \"m\", ts: f.ts, m: f.m };\n case \"call\":\n if (typeof f.s !== \"string\" || utf8Encode(f.s).length > LIMITS.maxChatMessageBytes) return null;\n return { t: \"call\", s: f.s };\n case \"svc\":\n return { t: \"svc\", svc: f.svc };\n case \"req\":\n if (!isStreamId(f.id) || typeof f.s !== \"string\" || typeof f.m !== \"string\") return null;\n if (typeof f.p !== \"string\" || f.p.length > LIMITS.maxPathBytes || !isHeaderList(f.h)) return null;\n return { t: \"req\", id: f.id, s: f.s, m: f.m, p: f.p, h: f.h, b: f.b === true };\n case \"res\":\n if (!isStreamId(f.id) || typeof f.st !== \"number\" || !Number.isInteger(f.st)) return null;\n if (f.st < 100 || f.st > 599 || !isHeaderList(f.h)) return null;\n return { t: \"res\", id: f.id, st: f.st, h: f.h, b: f.b === true };\n case \"file\":\n if (!isStreamId(f.id) || typeof f.f !== \"string\" || typeof f.ts !== \"number\") return null;\n if (typeof f.n !== \"string\" || typeof f.m !== \"string\") return null;\n if (typeof f.s !== \"number\" || !Number.isSafeInteger(f.s) || f.s < 0) return null;\n return { t: \"file\", id: f.id, f: f.f, ts: f.ts, n: f.n, s: f.s, m: f.m };\n case \"pay-req\":\n case \"pay\": {\n if (!isPayId(f.id) || typeof f.ts !== \"number\" || !isAmount(f.v) || !isUnit(f.u)) return null;\n const memo = typeof f.memo === \"string\" ? f.memo.slice(0, 140) : undefined;\n if (f.t === \"pay-req\") {\n if (!Array.isArray(f.e) || f.e.length === 0 || f.e.length > 8 || !f.e.every(isEndpoint)) return null;\n return { t: \"pay-req\", id: f.id, ts: f.ts, v: f.v, u: f.u, memo, e: f.e, a: isPayId(f.a) ? f.a : undefined, n: f.n === \"mainnet\" || f.n === \"testnet\" ? f.n : undefined };\n }\n if (!isEndpoint(f.e) || (f.rid !== undefined && !isPayId(f.rid))) return null;\n return { t: \"pay\", id: f.id, ts: f.ts, rid: f.rid, v: f.v, u: f.u, memo, e: f.e };\n }\n case \"pay-ask\": {\n if (!isPayId(f.id) || typeof f.ts !== \"number\" || !isAmount(f.v) || !isUnit(f.u)) return null;\n if (f.m !== \"arkade\" && f.m !== \"usdt\" && f.m !== \"bark\" && f.m !== \"bitcoin\" && f.m !== \"fedimint\" && f.m !== \"spark\") return null;\n return { t: \"pay-ask\", id: f.id, ts: f.ts, v: f.v, u: f.u, m: f.m, memo: typeof f.memo === \"string\" ? f.memo.slice(0, 140) : undefined, n: f.n === \"mainnet\" || f.n === \"testnet\" ? f.n : undefined };\n }\n case \"pay-res\":\n if (!isPayId(f.id) || typeof f.ok !== \"boolean\") return null;\n if (f.v !== undefined && !isAmount(f.v)) return null;\n return { t: \"pay-res\", id: f.id, ok: f.ok, v: f.v, err: typeof f.err === \"string\" ? f.err.slice(0, 200) : undefined, ...(f.c === true && !f.ok ? { c: true as const } : {}) };\n case \"rst\":\n if (!isStreamId(f.id) || (f.d !== \"q\" && f.d !== \"s\" && f.d !== \"f\")) return null;\n return { t: \"rst\", id: f.id, d: f.d, e: typeof f.e === \"string\" ? f.e.slice(0, 256) : \"\" };\n case \"ping\":\n case \"pong\":\n if (typeof f.ts !== \"number\") return null;\n return { t: f.t, ts: f.ts };\n default:\n return null;\n }\n}\n\nexport function encodeChunk(chunk: ChunkFrame): Uint8Array {\n if (chunk.payload.length > MAX_CHUNK_PAYLOAD_BYTES) throw new Error(\"Chunk payload too large\");\n const out = new Uint8Array(CHUNK_HEADER_BYTES + chunk.payload.length);\n const view = new DataView(out.buffer);\n view.setUint8(0, chunk.kind);\n view.setUint32(1, chunk.id);\n view.setUint8(5, chunk.end ? CHUNK_FLAG_END : 0);\n out.set(chunk.payload, CHUNK_HEADER_BYTES);\n return out;\n}\n\nexport function decodeChunk(data: Uint8Array): ChunkFrame | null {\n if (data.length < CHUNK_HEADER_BYTES || data.length > LIMITS.maxChunkMessageBytes) return null;\n const view = new DataView(data.buffer, data.byteOffset, data.byteLength);\n const kind = view.getUint8(0);\n if (kind !== CHUNK_KIND.requestBody && kind !== CHUNK_KIND.responseBody && kind !== CHUNK_KIND.fileBody) return null;\n return {\n kind,\n id: view.getUint32(1),\n end: (view.getUint8(5) & CHUNK_FLAG_END) !== 0,\n payload: data.subarray(CHUNK_HEADER_BYTES),\n };\n}\n\n/**\n * The slice of RTCDataChannel the protocol needs. Tests use an in-memory pair,\n * platforms wrap a real channel with `wrapDataChannel`.\n */\nexport interface FrameChannel {\n send(data: string | Uint8Array): void;\n readonly bufferedAmount: number;\n /** Resolves once the send buffer has drained below the low water mark. */\n drained(): Promise<void>;\n /**\n * Bytes the channel queues before `send` throws (the native and iroh channels). Absent: it buffers freely, as a\n * WebRTC data channel does, and a sender only keeps under `LIMITS.sendHighWaterMark`.\n */\n readonly sendBudget?: number;\n close(): void;\n onMessage: ((data: string | Uint8Array) => void) | null;\n onClose: (() => void) | null;\n}\n\nexport function wrapDataChannel(dc: RTCDataChannel): FrameChannel {\n dc.binaryType = \"arraybuffer\";\n dc.bufferedAmountLowThreshold = LIMITS.sendLowWaterMark;\n\n const channel: FrameChannel = {\n send(data) {\n if (dc.readyState !== \"open\") throw new Error(\"Data link is not open\");\n if (typeof data === \"string\") dc.send(data);\n else dc.send(data as unknown as ArrayBuffer);\n },\n get bufferedAmount() {\n return dc.bufferedAmount;\n },\n drained() {\n if (dc.bufferedAmount <= LIMITS.sendLowWaterMark || dc.readyState !== \"open\") return Promise.resolve();\n return new Promise<void>((resolve) => {\n const done = () => {\n dc.removeEventListener(\"bufferedamountlow\", done);\n dc.removeEventListener(\"close\", done);\n resolve();\n };\n dc.addEventListener(\"bufferedamountlow\", done);\n dc.addEventListener(\"close\", done);\n });\n },\n close() {\n dc.close();\n },\n onMessage: null,\n onClose: null,\n };\n\n dc.addEventListener(\"message\", (event) => {\n const data = event.data;\n if (typeof data === \"string\") channel.onMessage?.(data);\n else if (data instanceof ArrayBuffer) channel.onMessage?.(new Uint8Array(data));\n });\n dc.addEventListener(\"close\", () => channel.onClose?.());\n return channel;\n}\n\n/** Writes a body as chunks, honouring backpressure. Always ends with an END chunk. */\nexport async function sendBody(\n channel: FrameChannel,\n kind: ChunkKind,\n id: number,\n body: AsyncIterable<Uint8Array> | Iterable<Uint8Array>,\n isCancelled: () => boolean = () => false,\n): Promise<void> {\n for await (const part of body) {\n for (let offset = 0; offset < part.length; offset += MAX_CHUNK_PAYLOAD_BYTES) {\n if (isCancelled()) return;\n if (channel.bufferedAmount > LIMITS.sendHighWaterMark) await channel.drained();\n const payload = part.subarray(offset, offset + MAX_CHUNK_PAYLOAD_BYTES);\n channel.send(encodeChunk({ kind, id, end: false, payload }));\n }\n }\n if (isCancelled()) return;\n channel.send(encodeChunk({ kind, id, end: true, payload: new Uint8Array(0) }));\n}\n","import { concatBytes, utf8Encode } from \"./bytes\";\nimport {\n CHUNK_KIND,\n LIMITS,\n encodeControl,\n sendBody,\n type ChunkFrame,\n type FrameChannel,\n type HeaderList,\n type HttpRequestFrame,\n type HttpResponseFrame,\n type ResetFrame,\n} from \"./frames\";\n\n/**\n * HTTP over the Ghostly data link (`ghostly-http/1`).\n *\n * The client names a service id and an origin-form path. The host owns the\n * mapping from service id to a loopback target the user configured; nothing a\n * remote peer sends can change where a request goes.\n */\n\n// ---------------------------------------------------------------------------\n// Local targets\n// ---------------------------------------------------------------------------\n\nexport interface LocalTarget {\n /** e.g. `http://localhost:3400` */\n origin: string;\n /** Path prefix without trailing slash, `\"\"` for the root. */\n basePath: string;\n}\n\nconst LOOPBACK_HOSTS = new Set([\"localhost\", \"127.0.0.1\", \"[::1]\"]);\n\n/** Accepts `localhost:3400`, `http://127.0.0.1:8080/app`, … and nothing that is not loopback. */\nexport function parseLocalTarget(input: string): LocalTarget {\n let text = input.trim();\n if (!/^[a-z][a-z0-9+.-]*:\\/\\//i.test(text)) text = `http://${text}`;\n let url: URL;\n try {\n url = new URL(text);\n } catch {\n throw new Error(\"Not a valid URL\");\n }\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") throw new Error(\"Only http and https targets are supported\");\n if (url.username || url.password) throw new Error(\"Credentials in the target URL are not supported\");\n if (!LOOPBACK_HOSTS.has(url.hostname)) {\n throw new Error(\"Only services on this machine can be shared (localhost, 127.0.0.1 or [::1])\");\n }\n if (url.search || url.hash) throw new Error(\"The target cannot contain a query or fragment\");\n return { origin: url.origin, basePath: url.pathname.replace(/\\/+$/, \"\") };\n}\n\nexport function formatLocalTarget(target: LocalTarget): string {\n return `${target.origin}${target.basePath}`;\n}\n\n/**\n * Resolves a peer-supplied path against the configured target. Returns null\n * unless the result stays on the target origin and under its base path.\n */\nexport function resolveTargetUrl(target: LocalTarget, path: string): string | null {\n if (!path.startsWith(\"/\") || path.startsWith(\"//\")) return null;\n for (const char of path) {\n const code = char.codePointAt(0) ?? 0;\n if (code <= 32 || code === 127 || char === \"\\\\\") return null;\n }\n // Encoded separators survive URL normalization here but not in every server:\n // one that decodes `%2f` before routing would take `/app/..%2fadmin` to `/admin`.\n const queryAt = path.indexOf(\"?\");\n if (/%(2f|5c|2e)/i.test(queryAt === -1 ? path : path.slice(0, queryAt))) return null;\n let url: URL;\n try {\n url = new URL(`${target.origin}${target.basePath}${path}`);\n } catch {\n return null;\n }\n if (url.origin !== target.origin || url.username || url.password) return null;\n if (target.basePath && url.pathname !== target.basePath && !url.pathname.startsWith(`${target.basePath}/`)) {\n return null;\n }\n return url.href;\n}\n\n// ---------------------------------------------------------------------------\n// Header hygiene\n// ---------------------------------------------------------------------------\n\nconst HOP_BY_HOP = new Set([\n \"connection\",\n \"keep-alive\",\n \"proxy-authenticate\",\n \"proxy-authorization\",\n \"proxy-connection\",\n \"te\",\n \"trailer\",\n \"transfer-encoding\",\n \"upgrade\",\n]);\n\n/** Set by the host's own HTTP stack, or meaningless once proxied. */\nconst DROPPED_REQUEST_HEADERS = new Set([\n \"host\",\n \"origin\",\n \"referer\",\n \"content-length\",\n \"accept-encoding\",\n \"forwarded\",\n \"via\",\n // Frameworks trust these from a proxy in front of them: they rewrite the\n // client address, the routed URL or the method the app sees.\n \"x-real-ip\",\n \"x-client-ip\",\n \"x-cluster-client-ip\",\n \"true-client-ip\",\n \"cf-connecting-ip\",\n \"fastly-client-ip\",\n \"x-original-url\",\n \"x-original-uri\",\n \"x-original-host\",\n \"x-original-method\",\n \"x-rewrite-url\",\n \"x-host\",\n \"x-http-method-override\",\n \"x-http-method\",\n \"x-method-override\",\n]);\n\n/** `X-Forwarded-For/Host/Proto/Port/Prefix/Server/…`: all of them describe a proxy we are not. */\nconst DROPPED_REQUEST_HEADER_PREFIXES = [\"sec-\", \"proxy-\", \"x-forwarded-\"];\n\n/** The local fetch decodes the body, so the encoding headers no longer apply. */\nconst DROPPED_RESPONSE_HEADERS = new Set([\n \"content-encoding\",\n \"content-length\",\n \"strict-transport-security\",\n \"alt-svc\",\n \"public-key-pins\",\n]);\n\nconst HEADER_NAME = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;\n\nfunction isCleanHeaderValue(value: string): boolean {\n for (const char of value) {\n const code = char.codePointAt(0) ?? 0;\n if (code === 0 || code === 10 || code === 13) return false;\n }\n return true;\n}\n\nexport function sanitizeRequestHeaders(headers: HeaderList): HeaderList {\n return headers.filter(([name, value]) => {\n const lower = name.toLowerCase();\n if (!HEADER_NAME.test(name) || !isCleanHeaderValue(value)) return false;\n if (HOP_BY_HOP.has(lower) || DROPPED_REQUEST_HEADERS.has(lower)) return false;\n return !DROPPED_REQUEST_HEADER_PREFIXES.some((prefix) => lower.startsWith(prefix));\n });\n}\n\nexport function sanitizeResponseHeaders(headers: HeaderList, target: LocalTarget): HeaderList {\n const out: HeaderList = [];\n for (const [name, value] of headers) {\n const lower = name.toLowerCase();\n if (!HEADER_NAME.test(name) || !isCleanHeaderValue(value)) continue;\n if (HOP_BY_HOP.has(lower) || DROPPED_RESPONSE_HEADERS.has(lower)) continue;\n if (lower === \"location\" || lower === \"content-location\") {\n // Off-target locations are dropped, never disclosed.\n const location = relativizeLocation(value, target);\n if (location !== null) out.push([name, location]);\n } else if (lower === \"set-cookie\") {\n // The cookie belongs to the virtual origin on the client, not to localhost.\n out.push([name, value.replace(/;\\s*domain\\s*=[^;]*/gi, \"\")]);\n } else {\n out.push([name, value]);\n }\n }\n return out;\n}\n\n/**\n * A contact's response headers as a viewer serves them to the page (the extension's viewer tab, Desktop's viewer\n * window). The contact's app decides them, and the wire checks only that they are strings, so a header whose name\n * is not a token or whose value carries CR, LF or NUL is dropped: it could otherwise add headers of its own. Each\n * `Set-Cookie` stays on the exact origin it came from (no `Domain=`), and one holding several cookies on separate\n * lines becomes one header per cookie.\n */\nexport function viewerResponseHeaders(headers: HeaderList): HeaderList {\n const out: HeaderList = [];\n for (const [name, value] of headers) {\n if (typeof name !== \"string\" || typeof value !== \"string\" || !HEADER_NAME.test(name)) continue;\n if (name.toLowerCase() === \"set-cookie\") {\n for (const cookie of value.split(/\\r?\\n/)) {\n const scoped = cookie.replace(/;\\s*domain\\s*=[^;]*/gi, \"\");\n if (scoped.trim() && isCleanHeaderValue(scoped)) out.push([name, scoped]);\n }\n } else if (isCleanHeaderValue(value)) {\n out.push([name, value]);\n }\n }\n return out;\n}\n\n/**\n * Turns `http://localhost:3400/base/x` into `/x` so the client never learns or\n * follows the local address. Null when the location is not on the target origin\n * and under its base path.\n */\nexport function relativizeLocation(location: string, target: LocalTarget): string | null {\n let url: URL;\n try {\n url = new URL(location, `${target.origin}${target.basePath}/`);\n } catch {\n return null;\n }\n if (url.origin !== target.origin || url.username || url.password) return null;\n let path = url.pathname;\n if (target.basePath) {\n if (path !== target.basePath && !path.startsWith(`${target.basePath}/`)) return null;\n path = path.slice(target.basePath.length) || \"/\";\n }\n // `//host/x` would read as another origin on the client.\n if (!path.startsWith(\"/\") || path.startsWith(\"//\")) return null;\n return `${path}${url.search}${url.hash}`;\n}\n\nfunction isRedirectBlocked(response: LocalResponse, target: LocalTarget): boolean {\n if (response.status < 300 || response.status > 399) return false;\n const location = response.headers.find(([name]) => name.toLowerCase() === \"location\");\n return location !== undefined && relativizeLocation(location[1], target) === null;\n}\n\n// ---------------------------------------------------------------------------\n// Host\n// ---------------------------------------------------------------------------\n\nexport interface HostedHttpService {\n id: string;\n target: LocalTarget;\n}\n\nexport interface LocalRequest {\n url: string;\n method: string;\n headers: HeaderList;\n body: Uint8Array | null;\n signal: AbortSignal;\n}\n\nexport interface LocalResponse {\n status: number;\n headers: HeaderList;\n body: AsyncIterable<Uint8Array> | Iterable<Uint8Array> | null;\n /**\n * Final URL when the local stack followed redirects itself (a browser's\n * fetch: its manual mode hides where a redirect goes). Desktop never follows\n * and returns the 3xx as it is.\n */\n finalUrl?: string;\n}\n\n/** Performs the request against the local service. Browser: `fetch`. Desktop: the Rust side. */\nexport type LocalFetch = (request: LocalRequest) => Promise<LocalResponse>;\n\nconst ALLOWED_METHODS = new Set([\"GET\", \"HEAD\", \"POST\", \"PUT\", \"PATCH\", \"DELETE\", \"OPTIONS\"]);\nconst BODYLESS_METHODS = new Set([\"GET\", \"HEAD\"]);\n\ninterface HostStream {\n frame: HttpRequestFrame;\n service: HostedHttpService;\n url: string;\n body: Uint8Array[];\n bodyBytes: number;\n abort: AbortController;\n started: boolean;\n idleTimer?: ReturnType<typeof setTimeout>;\n}\n\nexport class HttpHost {\n private readonly streams = new Map<number, HostStream>();\n /**\n * Streams in the map plus those reset while their local fetch still runs. A\n * local stack may not honour the abort signal (Desktop's Rust fetch doesn't),\n * so a stream counts toward the limit until its fetch has settled.\n */\n private readonly live = new Set<HostStream>();\n\n constructor(\n private readonly channel: FrameChannel,\n private readonly getService: (id: string) => HostedHttpService | undefined,\n private readonly localFetch: LocalFetch,\n ) {}\n\n get activeRequests(): number {\n return this.live.size;\n }\n\n handleRequest(frame: HttpRequestFrame): void {\n if (this.streams.has(frame.id)) return;\n\n const method = frame.m.toUpperCase();\n const service = this.getService(frame.s);\n if (!service) return this.fail(frame.id, 404, \"unknown-service\", \"This peer does not share that service.\");\n if (!ALLOWED_METHODS.has(method)) return this.fail(frame.id, 405, \"method-not-allowed\", \"Method not allowed.\");\n if (this.live.size >= LIMITS.maxConcurrentRequestsPerPeer) {\n return this.fail(frame.id, 503, \"busy\", \"Too many concurrent requests.\");\n }\n const url = resolveTargetUrl(service.target, frame.p);\n if (!url) return this.fail(frame.id, 400, \"bad-path\", \"Invalid request path.\");\n\n const stream: HostStream = {\n frame: { ...frame, m: method },\n service,\n url,\n body: [],\n bodyBytes: 0,\n abort: new AbortController(),\n started: false,\n };\n this.streams.set(frame.id, stream);\n this.live.add(stream);\n\n if (frame.b && !BODYLESS_METHODS.has(method)) this.armIdleTimer(stream);\n else void this.run(stream);\n }\n\n handleChunk(chunk: ChunkFrame): void {\n const stream = this.streams.get(chunk.id);\n if (!stream || stream.started) return;\n\n stream.bodyBytes += chunk.payload.length;\n if (stream.bodyBytes > LIMITS.maxRequestBodyBytes) {\n this.end(stream);\n return this.fail(chunk.id, 413, \"request-too-large\", \"Request body too large.\");\n }\n if (chunk.payload.length > 0) stream.body.push(chunk.payload.slice());\n if (chunk.end) void this.run(stream);\n else this.armIdleTimer(stream);\n }\n\n handleReset(frame: ResetFrame): void {\n const stream = this.streams.get(frame.id);\n if (stream) this.end(stream);\n }\n\n closeAll(): void {\n for (const stream of [...this.streams.values()]) this.end(stream);\n }\n\n private armIdleTimer(stream: HostStream): void {\n clearTimeout(stream.idleTimer);\n stream.idleTimer = setTimeout(() => {\n this.end(stream);\n this.fail(stream.frame.id, 408, \"request-timeout\", \"Request body timed out.\");\n }, LIMITS.bodyIdleTimeoutMs);\n }\n\n /** Ids are reused after a reset, so a stream is current only if it is the one in the map. */\n private isCurrent(stream: HostStream): boolean {\n return this.streams.get(stream.frame.id) === stream;\n }\n\n private end(stream: HostStream): void {\n clearTimeout(stream.idleTimer);\n stream.abort.abort();\n if (this.isCurrent(stream)) this.streams.delete(stream.frame.id);\n if (!stream.started) this.live.delete(stream);\n }\n\n private fail(id: number, status: number, code: string, message: string): void {\n const body = utf8Encode(`${message}\\n`);\n try {\n this.channel.send(\n encodeControl({\n t: \"res\",\n id,\n st: status,\n h: [\n [\"content-type\", \"text/plain; charset=utf-8\"],\n [\"x-ghostly-error\", code],\n ],\n b: true,\n }),\n );\n void sendBody(this.channel, CHUNK_KIND.responseBody, id, [body]).catch(() => {});\n } catch {\n // channel already closed\n }\n }\n\n private async run(stream: HostStream): Promise<void> {\n stream.started = true;\n clearTimeout(stream.idleTimer);\n const { frame, service } = stream;\n const timeout = setTimeout(() => stream.abort.abort(), LIMITS.requestTimeoutMs);\n let headersSent = false;\n\n try {\n const response = await this.localFetch({\n url: stream.url,\n method: frame.m,\n headers: sanitizeRequestHeaders(frame.h),\n body: stream.body.length > 0 ? concatBytes(...stream.body) : null,\n signal: stream.abort.signal,\n });\n stream.body = [];\n clearTimeout(timeout);\n if (!this.isCurrent(stream)) return;\n\n if (response.finalUrl && response.finalUrl !== stream.url) {\n // A browser followed a redirect. Hand it back to the client so its\n // address bar and relative URLs stay right, but never off the target.\n const location = relativizeLocation(response.finalUrl, service.target);\n this.end(stream);\n if (location === null) return this.fail(frame.id, 502, \"redirect-blocked\", \"The service redirected outside of the shared target.\");\n this.channel.send(\n encodeControl({ t: \"res\", id: frame.id, st: BODYLESS_METHODS.has(frame.m) ? 302 : 303, h: [[\"location\", location]], b: false }),\n );\n return;\n }\n\n // Redirects reach the client as they are, with a relative Location, and\n // only when they stay on the target. Anything else is neither followed\n // nor disclosed.\n if (isRedirectBlocked(response, service.target)) {\n this.end(stream);\n return this.fail(frame.id, 502, \"redirect-blocked\", \"The service redirected outside of the shared target.\");\n }\n\n const hasBody = response.body !== null && frame.m !== \"HEAD\";\n const res: HttpResponseFrame = {\n t: \"res\",\n id: frame.id,\n st: response.status,\n h: sanitizeResponseHeaders(response.headers, service.target),\n b: hasBody,\n };\n this.channel.send(encodeControl(res));\n headersSent = true;\n if (hasBody && response.body) {\n await sendBody(this.channel, CHUNK_KIND.responseBody, frame.id, response.body, () => !this.isCurrent(stream));\n }\n this.end(stream);\n } catch (error) {\n clearTimeout(timeout);\n if (!this.isCurrent(stream)) return;\n // Read before end(), which aborts the signal itself.\n const timedOut = stream.abort.signal.aborted;\n this.end(stream);\n if (headersSent) {\n try {\n this.channel.send(encodeControl({ t: \"rst\", id: frame.id, d: \"s\", e: \"The local service dropped the response.\" }));\n } catch {\n // channel already closed\n }\n } else if (timedOut && (error as Error)?.name === \"AbortError\") {\n this.fail(frame.id, 504, \"timeout\", \"The local service did not answer in time.\");\n } else {\n this.fail(frame.id, 502, \"unreachable\", \"The local service is not reachable.\");\n }\n } finally {\n this.live.delete(stream);\n }\n }\n}\n\n/** `LocalFetch` on top of the web `fetch` API, for any host that has one. */\nexport const webLocalFetch: LocalFetch = async (request) => {\n const headers = new Headers();\n for (const [name, value] of request.headers) {\n try {\n headers.append(name, value);\n } catch {\n // header rejected by the platform\n }\n }\n const response = await fetch(request.url, {\n method: request.method,\n headers,\n body: request.body as BodyInit | null,\n signal: request.signal,\n // Never the host user's own cookies or HTTP auth: a remote peer must not\n // inherit the session the host has with its local application.\n credentials: \"omit\",\n cache: \"no-store\",\n // Browsers hide where a manual redirect goes, so a same-app redirect\n // (`/docs` → `/docs/`, a login's 303) could only be refused. Followed\n // redirects are checked afterwards and never leave the target for the\n // client; the local request itself has happened by then (docs/SECURITY-REVIEW.md).\n redirect: \"follow\",\n referrerPolicy: \"no-referrer\",\n });\n\n const responseHeaders: HeaderList = [];\n response.headers.forEach((value, name) => responseHeaders.push([name, value]));\n\n return {\n status: response.status,\n headers: responseHeaders,\n body: response.body ? readStream(response.body) : null,\n finalUrl: response.redirected ? response.url : undefined,\n };\n};\n\nasync function* readStream(stream: ReadableStream<Uint8Array>): AsyncIterable<Uint8Array> {\n const reader = stream.getReader();\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) return;\n if (value && value.length > 0) yield value;\n }\n } finally {\n reader.cancel().catch(() => {});\n }\n}\n\n// ---------------------------------------------------------------------------\n// Client\n// ---------------------------------------------------------------------------\n\nexport interface ClientRequest {\n method: string;\n path: string;\n headers?: HeaderList;\n body?: Uint8Array | null;\n signal?: AbortSignal;\n maxResponseBytes?: number;\n}\n\nexport interface ClientResponse {\n status: number;\n headers: HeaderList;\n body: AsyncIterable<Uint8Array>;\n /** Collects the whole body, bounded by `maxResponseBytes`. */\n bytes(): Promise<Uint8Array>;\n}\n\nexport class GhostlyHttpError extends Error {\n constructor(\n public readonly code: string,\n message: string,\n ) {\n super(message);\n }\n}\n\ninterface ClientStream {\n resolve: (response: ClientResponse) => void;\n reject: (error: Error) => void;\n headersTimer?: ReturnType<typeof setTimeout>;\n idleTimer?: ReturnType<typeof setTimeout>;\n queue: BodyQueue | null;\n received: number;\n maxBytes: number;\n}\n\nclass BodyQueue implements AsyncIterable<Uint8Array> {\n private chunks: Uint8Array[] = [];\n private done = false;\n private error: Error | null = null;\n private wake: (() => void) | null = null;\n\n push(chunk: Uint8Array): void {\n this.chunks.push(chunk);\n this.wake?.();\n }\n\n finish(error?: Error): void {\n if (this.done) return;\n this.done = true;\n this.error = error ?? null;\n this.wake?.();\n }\n\n async *[Symbol.asyncIterator](): AsyncIterator<Uint8Array> {\n for (;;) {\n while (this.chunks.length > 0) yield this.chunks.shift()!;\n if (this.error) throw this.error;\n if (this.done) return;\n await new Promise<void>((resolve) => (this.wake = resolve));\n this.wake = null;\n }\n }\n}\n\nexport class HttpClient {\n private nextId = 1;\n private readonly streams = new Map<number, ClientStream>();\n private readonly waiting: (() => void)[] = [];\n private inFlight = 0;\n private closed = false;\n\n constructor(private readonly channel: FrameChannel) {}\n\n async request(serviceId: string, request: ClientRequest): Promise<ClientResponse> {\n if (this.closed) throw new GhostlyHttpError(\"closed\", \"Data link is closed\");\n if (this.inFlight >= LIMITS.maxClientInFlight) {\n await new Promise<void>((resolve) => this.waiting.push(resolve));\n if (this.closed) throw new GhostlyHttpError(\"closed\", \"Data link is closed\");\n }\n this.inFlight++;\n\n const id = this.nextId++;\n const body = request.body && request.body.length > 0 ? request.body : null;\n if (body && body.length > LIMITS.maxRequestBodyBytes) {\n this.release();\n throw new GhostlyHttpError(\"request-too-large\", \"Request body too large\");\n }\n\n return new Promise<ClientResponse>((resolve, reject) => {\n const stream: ClientStream = {\n resolve,\n reject,\n queue: null,\n received: 0,\n maxBytes: request.maxResponseBytes ?? LIMITS.maxResponseBodyBytes,\n };\n this.streams.set(id, stream);\n stream.headersTimer = setTimeout(\n () => this.abort(id, new GhostlyHttpError(\"timeout\", \"The peer did not answer in time\")),\n LIMITS.requestTimeoutMs + 5_000,\n );\n request.signal?.addEventListener(\"abort\", () => this.abort(id, new GhostlyHttpError(\"aborted\", \"Request aborted\")));\n // Aborted before this point (while it waited for a slot, or before the call): the listener never fires.\n if (request.signal?.aborted) {\n this.abort(id, new GhostlyHttpError(\"aborted\", \"Request aborted\"), false);\n return;\n }\n\n try {\n this.channel.send(\n encodeControl({\n t: \"req\",\n id,\n s: serviceId,\n m: request.method.toUpperCase(),\n p: request.path,\n h: request.headers ?? [],\n b: body !== null,\n }),\n );\n if (body) {\n sendBody(this.channel, CHUNK_KIND.requestBody, id, [body], () => !this.streams.has(id)).catch((error) =>\n this.abort(id, error as Error),\n );\n }\n } catch (error) {\n this.abort(id, error as Error, false);\n }\n });\n }\n\n handleResponse(frame: HttpResponseFrame): void {\n const stream = this.streams.get(frame.id);\n if (!stream || stream.queue) return;\n clearTimeout(stream.headersTimer);\n\n const queue = new BodyQueue();\n stream.queue = queue;\n if (frame.b) this.armIdleTimer(frame.id, stream);\n else this.finish(frame.id);\n\n stream.resolve({\n status: frame.st,\n headers: frame.h,\n body: queue,\n bytes: async () => {\n const parts: Uint8Array[] = [];\n for await (const part of queue) parts.push(part);\n return concatBytes(...parts);\n },\n });\n }\n\n handleChunk(chunk: ChunkFrame): void {\n const stream = this.streams.get(chunk.id);\n if (!stream?.queue) return;\n\n stream.received += chunk.payload.length;\n if (stream.received > stream.maxBytes) {\n return this.abort(chunk.id, new GhostlyHttpError(\"response-too-large\", \"Response body too large\"));\n }\n if (chunk.payload.length > 0) stream.queue.push(chunk.payload.slice());\n if (chunk.end) this.finish(chunk.id);\n else this.armIdleTimer(chunk.id, stream);\n }\n\n handleReset(frame: ResetFrame): void {\n this.abort(frame.id, new GhostlyHttpError(\"reset\", frame.e || \"Request reset by peer\"), false);\n }\n\n close(): void {\n this.closed = true;\n for (const id of [...this.streams.keys()]) {\n this.abort(id, new GhostlyHttpError(\"closed\", \"Data link closed\"), false);\n }\n for (const wake of this.waiting.splice(0)) wake();\n }\n\n private armIdleTimer(id: number, stream: ClientStream): void {\n clearTimeout(stream.idleTimer);\n stream.idleTimer = setTimeout(\n () => this.abort(id, new GhostlyHttpError(\"timeout\", \"Response body timed out\")),\n LIMITS.bodyIdleTimeoutMs,\n );\n }\n\n private finish(id: number): void {\n const stream = this.streams.get(id);\n if (!stream) return;\n clearTimeout(stream.headersTimer);\n clearTimeout(stream.idleTimer);\n this.streams.delete(id);\n stream.queue?.finish();\n this.release();\n }\n\n private abort(id: number, error: Error, notifyPeer = true): void {\n const stream = this.streams.get(id);\n if (!stream) return;\n clearTimeout(stream.headersTimer);\n clearTimeout(stream.idleTimer);\n this.streams.delete(id);\n if (stream.queue) stream.queue.finish(error);\n else stream.reject(error);\n if (notifyPeer) {\n try {\n this.channel.send(encodeControl({ t: \"rst\", id, d: \"q\", e: error.message }));\n } catch {\n // channel already closed\n }\n }\n this.release();\n }\n\n private release(): void {\n this.inFlight--;\n this.waiting.shift()?.();\n }\n}\n","import { fromBase64Url, utf8Encode } from \"./bytes\";\nimport { identityFromSeedB64, type Identity } from \"./identity\";\nimport type { LinkParams } from \"./invite\";\nimport {\n buildLinkRecords,\n isEmptyLinkPacket,\n parseLinkRecords,\n type CompactMessage,\n type ResolvedLink,\n type ResolvedMessage,\n} from \"./records\";\nimport type { ServiceAd } from \"./services\";\nimport type { SignalSight } from \"./signal\";\nimport { budgetRetryMs, isDiscoveryBudgetError, type PkarrTransport } from \"./transport\";\nimport { traceLink } from \"./linkTrace\";\n\n/**\n * The Pkarr side of a link: publish my records, poll the peer's. This is the\n * loop Ghostly Desktop runs in `useChat`, without React, so every client can\n * share it.\n */\nexport interface PollIntervals {\n /** The user is looking at the link. */\n active: number;\n /** …but nothing happened for a minute. */\n idle: number;\n /** Signaling in progress. */\n fast: number;\n /** Nobody is looking. */\n background: number;\n /** The data link is up and carries everything; Pkarr only has to notice re-offers. */\n connected: number;\n}\n\n/** What Ghostly Desktop uses against the DHT. */\nexport const DHT_POLL_INTERVALS: PollIntervals = {\n active: 2_000,\n idle: 8_000,\n fast: 700,\n background: 20_000,\n connected: 30_000,\n};\n\n/** Relays rate limit by IP, and several peers may sit behind one address. */\nexport const RELAY_POLL_INTERVALS: PollIntervals = {\n active: 4_000,\n idle: 10_000,\n fast: 2_000,\n background: 30_000,\n connected: 60_000,\n};\n\n/** Signaling that has not finished by then is not going to; stop polling fast. */\nconst FAST_POLL_MAX_MS = 45_000;\n/**\n * An offer to a saved contact (a chat, a group's edge) is looked at fast for an answer this long after it went out,\n * then less often (`OFFER_STEP_MAX`) for the rest of its window. A contact that is there answers within seconds;\n * one that still holds the session this app had before it restarted answers only once that session goes (about 20 s\n * with node-datachannel, 45 s where liveness finds out). An app back with several chats and edges, each looking every\n * 2 s meanwhile, spent the relays' minute in those seconds and could not read the answers when they came: a group's\n * edges were live again 75 and 110 s after a restart (bug hunt r5a, 2026-09-29).\n */\nexport const OFFER_FAST_MS = 10_000;\n/**\n * The longest wait between looks for an answer to an offer once it has been out `OFFER_FAST_MS`, in fast paces: 8 s on\n * the relays (fast 2 s), 2.8 s on the DHT (0.7 s), which costs no relay budget.\n */\nexport const OFFER_STEP_MAX = 4;\n/** An offer to a saved contact is looked at for an answer as long as its attempt lasts (`CONNECT_TIMEOUT_MS`), not 45 s. */\nexport const OFFER_LOOK_MS = 90_000;\n/**\n * How long a link looks fast when its peer, or the peer's offer, is due any moment. The peer that\n * dials does so as soon as it sees the other one here, and its offer lands in its packet a moment\n * after its presence did: without this the side that answers left the offer to a background poll\n * (30 s on the relays), once on a group's entry session and once more per member edge.\n */\nexport const EXPECT_PEER_MS = 30_000;\n/**\n * How long a link keeps looking at the active pace after its contact went away from a live session (it said goodbye,\n * or the session dropped): an app that restarts is back within this, and its dial or offer is seen in seconds.\n */\nexport const WATCH_PEER_MS = 2 * 60_000;\n/** A chat whose contact was never seen (an invite just sent) keeps looking at the active pace this long. */\nexport const AWAITING_PEER_MS = 10 * 60_000;\nconst PUBLISH_RETRY_MS = 4_000;\n/**\n * A packet the relays' request budget held back goes again when the budget frees a request, and at least this often\n * meanwhile: each refusal keeps it first in line for that request (`WRITE_FIRST_MS`), and costs no request itself.\n */\nconst BUDGET_RETRY_MIN_MS = 250;\n/** Two reads are never closer than this, however long the last one took. */\nconst MIN_POLL_GAP_MS = 250;\nexport const IDLE_THRESHOLD = 60_000;\nexport const MAX_DHT_TEXT_BYTES = 500;\n/** Presence is a fresh packet: advertising peers republish this often… */\nexport const PRESENCE_HEARTBEAT = 4 * 60_000;\n/** …and are considered gone once their packet is older than this. */\nexport const PRESENCE_WINDOW = 10 * 60_000;\n\n/** When this device first read the peer's latest packet, by its own clock (`PeerPresence.seenAt`). */\nexport const presenceSeenAt = (presence: Pick<PeerPresence, \"lastPacketAt\" | \"seenAt\">): number => presence.seenAt ?? presence.lastPacketAt;\n\nexport type LinkStatus = \"connecting\" | \"online\" | \"offline\" | \"error\";\n\nexport interface PeerPresence {\n /** Peer advertises services and its packet is fresh. */\n online: boolean;\n /** Timestamp of the peer's latest packet (ms), 0 if none was ever seen. The peer's clock: it names the packet, and is never compared with this one's. */\n lastPacketAt: number;\n /**\n * When this device first read that packet, by its own clock: what \"how long ago\" is measured from (`presenceSeenAt`).\n * A packet is dated by its own time, held between the read of this run before the one that found it and that one: a\n * few seconds apart while a link is being made, so the peer's clock no longer matters. The first read has no read\n * before it: a peer whose clock is behind looks that much older until its next packet, and no peer's clock makes a\n * packet look newer than the read that found it.\n */\n seenAt?: number;\n nick?: string;\n /** `null` for peers that do not advertise (legacy clients, or offline). */\n services: ServiceAd[] | null;\n}\n\nexport interface LinkSessionEvents {\n onDiscoveryError?(error: string | null): void;\n onMessages?(messages: ResolvedMessage[], batch: ResolvedLink): void;\n onPresence?(presence: PeerPresence): void;\n onPeerAck?(ackTimestamp: number): void;\n onCallSignal?(signal: string): void;\n /**\n * `sight`: a read of the peer's record that the network answered, earlier in this run, did not have the signal\n * (`SignalSight`): when that read began, and the time of the peer's packet it found, if any. Absent for a signal\n * the first such read found.\n */\n onRtcSignal?(signal: string, sight?: SignalSight): void;\n /**\n * The peer's packet, dated `packetAt` by the peer's clock, was not there at this run's read of `readBefore` and is\n * there at `readAt` (this clock): what its clock says against this one (`ClockWatch.peer`).\n */\n onPeerClock?(packetAt: number, readBefore: number, readAt: number): void;\n /** The peer's packet carries a new `_tr` value (a group link's transports, `parsePacketTransports`). */\n onPeerTransports?(value: string): void;\n onStatus?(status: LinkStatus): void;\n /** A poll started, or finished with the next one due in `nextInMs`. */\n onPoll?(poll: { polling: boolean; nextInMs: number }): void;\n /**\n * A publish finished: how long it took, and whether it carried an `_rtc` signal. `error` when it failed; `waiting`\n * when the relays' request budget held it back (nothing went out, and it goes again once the budget frees a request).\n * `signalOut` when it is the first to carry the current `_rtc` signal: an offer or an answer went out now.\n */\n onPublish?(result: { ms: number; rtc: boolean; error?: string; waiting?: boolean; signalOut?: boolean }): void;\n /** The first read of the peer's key is done (with `firstPublish: \"after-first-poll\"`, what to publish is decided now). */\n onFirstPoll?(): void;\n}\n\nexport interface LinkSessionOptions {\n params: LinkParams;\n transport: PkarrTransport;\n nick?: string;\n /** Highest peer message timestamp already stored locally. */\n lastSeenTimestamp?: number;\n /** Services to advertise right now. Return undefined to advertise nothing. */\n getServices?: () => ServiceAd[] | undefined;\n pollIntervals?: PollIntervals;\n events?: LinkSessionEvents;\n /**\n * When this side's first packet goes out. `at-start` (default): as the session starts. `after-first-poll`:\n * once the peer's key was read, unless whoever asked publishes something better by then (a first\n * pairing's joiner, who dials the moment it sees the inviter: its offer then travels in its first packet\n * instead of a second one right behind it, which relays hold back for seconds), and in any case within\n * `FIRST_PUBLISH_MAX_MS`.\n */\n firstPublish?: \"at-start\" | \"after-first-poll\";\n}\n\n/** With `firstPublish: \"after-first-poll\"`, the first packet goes out by then whatever happened. */\nexport const FIRST_PUBLISH_MAX_MS = 2_000;\n\nexport class LinkSession {\n readonly identity: Identity;\n readonly peerPubKeyZ32: string;\n private readonly encKey: Uint8Array;\n private readonly transport: PkarrTransport;\n private readonly events: LinkSessionEvents;\n private readonly getServices: () => ServiceAd[] | undefined;\n\n private nick?: string;\n private sentBuffer: CompactMessage[] = [];\n private myAck: number;\n private lastSeenTimestamp: number;\n private callSignal: string | null = null;\n private rtcSignal: string | null = null;\n private lastCallSignalIn: string | null = null;\n /** The `_rtc` signal the last packet that went out carried. */\n private rtcSignalOut: string | null = null;\n private lastRtcSignalIn: string | null = null;\n /** The `_rtc` signal the peer's packet carried as last read (null: none), whether or not it was new. */\n private peerRtcSignal: string | null = null;\n /** A group link's `_tr` value this side publishes (`setTransports`), and the last the peer's packet carried. */\n private transports: string | null = null;\n private lastTransportsIn: string | null = null;\n\n private running = false;\n /** Stopped without a last packet (`stop(false)`): nothing more goes out. */\n private silent = false;\n private polling = false;\n private publishing: Promise<void> | null = null;\n private publishAgain = false;\n private pollTimer: ReturnType<typeof setTimeout> | null = null;\n private heartbeatTimer: ReturnType<typeof setTimeout> | null = null;\n private lastActivity = Date.now();\n private readonly startedAt = Date.now();\n private readonly intervals: PollIntervals;\n private fastPollUntil = 0;\n /** An offer to a saved contact is out: from then on its fast window looks less often (`OFFER_FAST_MS`); 0 for none. */\n private fastStepsAfter = 0;\n /** When the last `expectPeer` window ends (or ended): the pace slows down from there step by step. */\n private expectUntil = 0;\n private watchUntil = 0;\n private publishRetryTimer: ReturnType<typeof setTimeout> | null = null;\n private active = false;\n private connected = false;\n private readonly firstPublish: \"at-start\" | \"after-first-poll\";\n private firstPublishTimer: ReturnType<typeof setTimeout> | null = null;\n private firstPollDone = false;\n /** When this side last published (0: never). */\n lastPublishedAt = 0;\n /** A publish is in flight, or finished this recently: another packet now would only queue behind it at the relays. */\n publishedRecently(withinMs: number): boolean {\n return this.publishing !== null || this.firstPublishTimer !== null || Date.now() - this.lastPublishedAt < withinMs;\n }\n private discoveryErrors: Partial<Record<\"publish\" | \"read\", string>> = {};\n private unsubscribe: (() => void) | null = null;\n private presence: PeerPresence = { online: false, lastPacketAt: 0, services: null };\n /**\n * When the last read of the peer's record that the network answered began (0: none yet in this run). A read that\n * handed back a copy kept from before (`PkarrTransport.readAnsweredAt`) is not one: it says nothing of what the\n * record holds now.\n */\n private lastReadAt = 0;\n /** The latest time of the peer's own packets those reads found, by the peer's clock (0: none; the inviter's empty packet is not the peer's). */\n private peerPacketAt = 0;\n\n constructor(options: LinkSessionOptions) {\n this.identity = identityFromSeedB64(options.params.seedB64);\n this.peerPubKeyZ32 = options.params.peerPubKeyZ32;\n this.encKey = fromBase64Url(options.params.encKeyB64);\n this.transport = options.transport;\n this.nick = options.nick;\n this.lastSeenTimestamp = options.lastSeenTimestamp ?? 0;\n this.myAck = this.lastSeenTimestamp;\n this.getServices = options.getServices ?? (() => undefined);\n this.intervals = options.pollIntervals ?? DHT_POLL_INTERVALS;\n this.events = options.events ?? {};\n this.firstPublish = options.firstPublish ?? \"at-start\";\n }\n\n get peerPresence(): PeerPresence {\n return this.presence;\n }\n\n /** The `_rtc` signal the peer's packet carries, as last read: null when it carries none. */\n get peerSignal(): string | null {\n return this.peerRtcSignal;\n }\n\n start(): void {\n if (this.running) return;\n this.running = true;\n this.silent = false;\n this.events.onStatus?.(\"connecting\");\n // Presence: a peer that advertises services says so as soon as it is up…\n if (this.getServices() !== undefined || this.myAck > 0) {\n if (this.firstPublish === \"at-start\") void this.publish().catch(() => {});\n // …or right after its first look, and by FIRST_PUBLISH_MAX_MS whatever came of it.\n else this.firstPublishTimer = setTimeout(() => { this.firstPublishTimer = null; this.ensureAdvertised(); }, FIRST_PUBLISH_MAX_MS);\n }\n this.scheduleHeartbeat();\n // A relay that answers again after failing: look and publish now, not at this link's pace (which may be minutes).\n this.unsubscribe = this.transport.subscribe?.(change => { if (change === \"recovered\") this.discoveryRecovered(); }) ?? null;\n void this.poll();\n }\n\n private discoveryRecovered(): void {\n if (!this.running) return;\n traceLink(this.identity.pubKeyZ32, \"discovery-recovered\", { publishWaiting: !!this.publishRetryTimer });\n if (this.publishRetryTimer || this.discoveryErrors.publish) void this.publish().catch(() => {});\n this.pollNow();\n }\n\n /** This side's packet goes out now, unless one already did. */\n ensureAdvertised(): void {\n if (!this.running || this.lastPublishedAt > 0 || this.publishing) return;\n void this.publish().catch(() => {});\n }\n\n /** Stops the loops. With `announce`, first tells the peer we are gone. */\n async stop(announce = true): Promise<void> {\n if (!this.running) return;\n this.running = false;\n this.silent = !announce;\n this.unsubscribe?.();\n this.unsubscribe = null;\n if (this.pollTimer) clearTimeout(this.pollTimer);\n if (this.heartbeatTimer) clearTimeout(this.heartbeatTimer);\n if (this.publishRetryTimer) clearTimeout(this.publishRetryTimer);\n if (this.firstPublishTimer) clearTimeout(this.firstPublishTimer);\n this.pollTimer = this.heartbeatTimer = this.publishRetryTimer = this.firstPublishTimer = null;\n this.rtcSignal = null;\n this.callSignal = null;\n this.events.onStatus?.(\"offline\");\n if (announce) {\n try {\n await this.publishOnce(false);\n } catch {\n // best effort, the packet goes stale on its own\n }\n }\n }\n\n setNick(nick: string | undefined): void {\n this.nick = nick || undefined;\n }\n\n /** The user is looking at this link: poll at chat speed. */\n setActive(active: boolean): void {\n this.active = active;\n if (active) this.pollNow();\n }\n\n /**\n * Signaling in progress: look fast. `offer`: this side's offer to a saved contact, whose answer may be a while\n * (`OFFER_FAST_MS`): fast at first, then less often.\n */\n setFastPoll(fast: boolean, offer = false): void {\n this.fastPollUntil = fast ? Date.now() + (offer ? OFFER_LOOK_MS : FAST_POLL_MAX_MS) : 0;\n this.fastStepsAfter = fast && offer ? Date.now() + OFFER_FAST_MS : 0;\n if (fast) this.pollNow();\n }\n\n /**\n * The peer, or its offer, is due any moment: poll fast for `EXPECT_PEER_MS` (never cutting a longer fast window short),\n * then slow down step by step (`nextInterval`) rather than at once.\n */\n expectPeer(): void {\n const until = Date.now() + EXPECT_PEER_MS;\n this.expectUntil = Math.max(this.expectUntil, until);\n // Due now: an offer of mine that was looking less often looks fast again, as long as this window.\n const stepping = this.fastStepsAfter > 0 && Date.now() >= this.fastStepsAfter;\n if (this.fastStepsAfter) this.fastStepsAfter = Math.max(this.fastStepsAfter, until);\n if (until <= this.fastPollUntil) { if (stepping) this.pollNow(); return; }\n this.fastPollUntil = until;\n this.pollNow();\n }\n\n /**\n * The contact just left a live session, and is likely coming back (an app restarting): fast for `EXPECT_PEER_MS`,\n * then the active pace until `WATCH_PEER_MS` passed, whatever the chat's pace would be.\n */\n watchPeer(): void {\n this.watchUntil = Math.max(this.watchUntil, Date.now() + WATCH_PEER_MS);\n this.expectPeer();\n }\n\n /** The data link carries chat and services while it is up. */\n setDataLinkOpen(open: boolean): void {\n this.connected = open;\n if (!open) this.pollNow();\n }\n\n async setCallSignal(signal: string | null): Promise<void> {\n this.callSignal = signal;\n this.lastActivity = Date.now();\n await this.publish().catch(() => {});\n this.pollNow();\n }\n\n /** Includes encryption, DNS encoding and the actual concurrent record budget. */\n fitsRtcSignal(signal: string): boolean {\n try {\n buildLinkRecords(this.identity.pubKeyZ32, { messages: this.sentBuffer, ackTimestamp: this.myAck,\n nick: this.nick, callSignal: this.callSignal, rtcSignal: signal, services: this.getServices(), transports: this.transports }, this.encKey);\n return true;\n } catch { return false; }\n }\n\n /**\n * A group link's transports and how to dial them (`_tr`, `encodePacketTransports`): published with the next packet,\n * now if it changed. `null` publishes none, which is what every link but a group's with an app lacking WebRTC does.\n */\n setTransports(value: string | null): void {\n if (this.transports === value) return;\n this.transports = value;\n if (!this.running) return;\n // A first packet still held back (`firstPublish`) carries it when it goes; one already out does not.\n if (this.firstPublishTimer && !this.lastPublishedAt && !this.publishing) return;\n void this.publish().catch(() => {});\n }\n\n async setRtcSignal(signal: string | null, reportFailure = false): Promise<void> {\n if (this.rtcSignal === signal) return;\n this.rtcSignal = signal;\n if (reportFailure) await this.publish();\n else await this.publish().catch(() => {});\n if (signal) this.pollNow();\n }\n\n /**\n * Hands over the messages the peer has not acknowledged, for delivery over\n * the data link. That channel is reliable, so they leave the Pkarr buffer.\n */\n takeUnacknowledged(): CompactMessage[] {\n const pending = this.sentBuffer;\n if (pending.length === 0) return [];\n this.sentBuffer = [];\n this.events.onPeerAck?.(Math.max(...pending.map((m) => m.t)));\n void this.publish().catch(() => {});\n return pending;\n }\n\n /** Call after the list of shared services changed. A first publish still being held back will say it all. */\n async refreshAdvertisement(): Promise<void> {\n if (this.firstPublishTimer) return;\n await this.publish().catch(() => {});\n }\n\n /** Sends a chat message through Pkarr. Returns an error string, or null on success. */\n async sendMessage(text: string, timestamp = Date.now()): Promise<string | null> {\n const byteLength = utf8Encode(text).length;\n if (byteLength > MAX_DHT_TEXT_BYTES) {\n return `Message too large for DHT (${byteLength} bytes, max ${MAX_DHT_TEXT_BYTES}). Try a shorter message or share a link instead.`;\n }\n this.lastActivity = Date.now();\n this.sentBuffer = [...this.sentBuffer, { t: timestamp, m: text }];\n try {\n const kept = await this.publishOnce(true);\n if (kept === 0) return \"Message could not be published — DHT payload limit exceeded.\";\n } catch (error) {\n // The message stays in the buffer; keep trying instead of losing it.\n if (this.publishRetryTimer) clearTimeout(this.publishRetryTimer);\n this.publishRetryTimer = setTimeout(() => void this.publish().catch(() => {}), this.retryDelay(error));\n // Not an error for the sender: it is queued, and the connection status shows the trouble (a wait for the\n // relays' budget is none).\n if (!isDiscoveryBudgetError(error)) this.events.onStatus?.(\"error\");\n return null;\n }\n this.pollNow();\n return null;\n }\n\n pollNow(): void {\n if (!this.running) return;\n if (this.pollTimer) clearTimeout(this.pollTimer);\n this.pollTimer = null;\n void this.poll();\n }\n\n /**\n * The next poll's wait. After a window that looked fast for the peer (`expectPeer`), the wait grows with the time\n * since the window ended (on the relays: 4, 4, 8, 16 s, then 30 s) up to the pace's own. A\n * peer's offer held back by its relays' budget lands whenever that frees a request: it used to land seconds after\n * the window, and wait out a whole background poll (30 s) there (2026-09-27). About three more reads a window that\n * ends with no offer.\n */\n private nextInterval(): number {\n const pace = this.pace();\n const interval = this.intervals[pace];\n const since = Date.now() - this.expectUntil;\n // An offer out a while: 4, 4, 8 s… between looks (on the relays), as long as its window lasts.\n const stepping = this.fastStepsAfter ? Date.now() - this.fastStepsAfter : -1;\n if (pace === \"fast\" && stepping >= 0) return Math.min(OFFER_STEP_MAX * interval, Math.max(2 * interval, stepping));\n if (pace === \"fast\" || pace === \"connected\" || since < 0) return interval;\n return Math.min(interval, Math.max(2 * this.intervals.fast, since));\n }\n\n /** How urgently this link looks right now. */\n private pace(): keyof PollIntervals {\n // Connected peers signal over the data link; no reason to hurry Pkarr.\n if (this.connected) return \"connected\";\n if (Date.now() < this.fastPollUntil) return \"fast\";\n if (Date.now() < this.watchUntil) return \"active\";\n // Someone who just sent an invite may look elsewhere while waiting: the join still comes in quickly.\n if (!this.active && this.presence.lastPacketAt === 0 && Date.now() - this.startedAt < AWAITING_PEER_MS) return \"active\";\n if (!this.active) return \"background\";\n return Date.now() - this.lastActivity > IDLE_THRESHOLD ? \"idle\" : \"active\";\n }\n\n private scheduleHeartbeat(): void {\n if (this.heartbeatTimer) clearTimeout(this.heartbeatTimer);\n this.heartbeatTimer = setTimeout(() => {\n if (!this.running) return;\n if (this.getServices() !== undefined) void this.publish().catch(() => {});\n this.scheduleHeartbeat();\n }, PRESENCE_HEARTBEAT);\n }\n\n /** Coalesces publishes: at most one in flight and one queued behind it. */\n private publish(): Promise<void> {\n if (this.silent) return Promise.resolve();\n if (this.publishing) {\n this.publishAgain = true;\n return this.publishing;\n }\n if (this.publishRetryTimer) clearTimeout(this.publishRetryTimer);\n this.publishRetryTimer = null;\n this.publishing = (async () => {\n try {\n do {\n this.publishAgain = false;\n await this.publishOnce(this.running);\n } while (this.publishAgain && this.running);\n } catch (error) {\n // A signal that is not published is a call that never rings: try again. Held back by the relays' request\n // budget, it goes as soon as the budget frees a request, and nothing is wrong meanwhile.\n if (this.running) {\n if (!isDiscoveryBudgetError(error)) this.discoveryResult(\"publish\", error);\n this.publishRetryTimer = setTimeout(() => void this.publish().catch(() => {}), this.retryDelay(error));\n }\n throw error;\n } finally {\n this.publishing = null;\n }\n })();\n return this.publishing;\n }\n\n private async publishOnce(advertise: boolean): Promise<number> {\n const rtcSignal = advertise ? this.rtcSignal : null;\n const built = buildLinkRecords(\n this.identity.pubKeyZ32,\n {\n messages: this.sentBuffer,\n ackTimestamp: this.myAck,\n nick: this.nick,\n callSignal: this.callSignal,\n rtcSignal,\n services: advertise ? this.getServices() : undefined,\n transports: advertise ? this.transports : null,\n },\n this.encKey,\n );\n const started = Date.now();\n try {\n // An offer or answer not out yet is what the contact waits for (`PkarrRequestOptions.signal`).\n await this.transport.publish(this.identity, built.records, rtcSignal && rtcSignal !== this.rtcSignalOut ? { signal: true } : undefined);\n } catch (error) {\n const ms = Date.now() - started, waiting = isDiscoveryBudgetError(error);\n traceLink(this.identity.pubKeyZ32, \"publish\", { ms, rtc: !!rtcSignal, error: String(error), ...(waiting && { waiting, retryInMs: error.retryInMs }) });\n this.events.onPublish?.({ ms, rtc: !!rtcSignal, error: error instanceof Error ? error.message : String(error), ...(waiting && { waiting }) });\n throw error;\n }\n const ms = Date.now() - started;\n this.lastPublishedAt = Date.now();\n // Signaling's fast window counts from when its signal went out: one the relays' budget held back for most of the\n // window (an offer held 50 s) would otherwise have its answer read at the background pace (2026-09-27).\n const signalOut = !!rtcSignal && rtcSignal !== this.rtcSignalOut;\n if (signalOut && this.fastPollUntil > 0) {\n const lapsed = this.fastPollUntil <= Date.now() || (this.fastStepsAfter > 0 && this.fastStepsAfter <= Date.now());\n this.fastPollUntil = Math.max(this.fastPollUntil, Date.now() + (this.fastStepsAfter ? OFFER_LOOK_MS : FAST_POLL_MAX_MS));\n // An offer's first seconds of fast looks count from then too.\n if (this.fastStepsAfter) this.fastStepsAfter = Date.now() + OFFER_FAST_MS;\n // Its next look was put off to a slower pace: it comes at the fast one now.\n if (lapsed) this.pollNow();\n }\n this.rtcSignalOut = rtcSignal;\n traceLink(this.identity.pubKeyZ32, \"publish\", { ms, rtc: !!rtcSignal, advertise });\n this.events.onPublish?.({ ms, rtc: !!rtcSignal, ...(signalOut && { signalOut }) });\n this.discoveryResult(\"publish\");\n return built.keptMessages;\n }\n\n /** When a publish that failed goes again: when the relays' budget frees a request, if that held it back. */\n private retryDelay(error: unknown): number {\n return isDiscoveryBudgetError(error) ? budgetRetryMs(error, BUDGET_RETRY_MIN_MS, PUBLISH_RETRY_MS) : PUBLISH_RETRY_MS;\n }\n\n private discoveryResult(operation: \"publish\" | \"read\", error?: unknown): void {\n if (error !== undefined) this.discoveryErrors[operation] = `Could not ${operation} discovery: ${error instanceof Error ? error.message : String(error)}`;\n else delete this.discoveryErrors[operation];\n this.events.onDiscoveryError?.(Object.values(this.discoveryErrors).join(\". \") || null);\n }\n\n private async poll(): Promise<void> {\n if (!this.running || this.polling) return;\n this.polling = true;\n this.events.onPoll?.({ polling: true, nextInMs: 0 });\n const started = Date.now();\n try {\n // A look that can wait (nobody watching, nothing expected) says so: a transport with a request\n // budget spends only part of it on those, and keeps the rest for links that are signaling.\n const pace = this.pace();\n // This side's offer is out, and this read looks for its answer: signaling (`PkarrRequestOptions.signal`).\n const signal = pace === \"fast\" && this.fastStepsAfter > 0 && this.rtcSignalOut !== null;\n const packet = await this.transport.resolve(this.peerPubKeyZ32, { background: pace === \"background\" || pace === \"connected\", urgent: pace === \"fast\", ...(signal && { signal }) });\n if (!this.running) return;\n this.discoveryResult(\"read\");\n const ms = Date.now() - started;\n const wasOnline = this.presence.online, wasSeen = this.presence.lastPacketAt;\n // What this read finds that the read before did not have came in between, by this device's clock.\n const readBefore = this.lastReadAt, peerPacketBefore = this.peerPacketAt;\n const answered = this.transport.readAnsweredAt?.(this.peerPubKeyZ32);\n const read = answered !== undefined && answered >= started;\n if (read) this.lastReadAt = started;\n\n let receivedNew = false;\n // The packet an inviter puts under the contact's key before they join (`emptyLinkRecords`), so\n // their first packet lands faster, says nobody is there yet.\n const batch = packet ? parseLinkRecords(packet, this.encKey) : null;\n if (batch && isEmptyLinkPacket(batch)) {\n if ((globalThis as { __ghostlyLinkTrace?: boolean }).__ghostlyLinkTrace) traceLink(this.identity.pubKeyZ32, \"poll\", { ms, pace, placeholder: true });\n } else if (batch) {\n if (batch.peerAck > 0) {\n this.sentBuffer = this.sentBuffer.filter((m) => m.t > batch.peerAck);\n this.events.onPeerAck?.(batch.peerAck);\n }\n\n if (batch.latestTimestamp > 0 && batch.latestTimestamp > this.lastSeenTimestamp) {\n const fresh = batch.messages.filter((m) => m.timestamp > this.lastSeenTimestamp);\n this.lastSeenTimestamp = batch.latestTimestamp;\n this.myAck = batch.latestTimestamp;\n this.lastActivity = Date.now();\n receivedNew = true;\n if (fresh.length > 0) this.events.onMessages?.(fresh, batch);\n }\n\n if (read && batch.packetTimestamp > this.peerPacketAt) this.peerPacketAt = batch.packetTimestamp;\n // How long ago the peer published is measured on this clock, from when its packet was first read here.\n const seenAt = batch.packetTimestamp === wasSeen ? presenceSeenAt(this.presence) : Math.max(readBefore, Math.min(batch.packetTimestamp, Date.now()));\n const online = batch.services !== null && Date.now() - seenAt < PRESENCE_WINDOW;\n this.presence = {\n online,\n lastPacketAt: batch.packetTimestamp,\n seenAt,\n nick: batch.nick,\n services: online ? batch.services : null,\n };\n // Before presence: a dial that presence starts ranks with them.\n if (batch.transports !== null && batch.transports !== this.lastTransportsIn) {\n this.lastTransportsIn = batch.transports;\n this.events.onPeerTransports?.(batch.transports);\n }\n this.events.onPresence?.(this.presence);\n if (read && readBefore && batch.packetTimestamp !== wasSeen) this.events.onPeerClock?.(batch.packetTimestamp, readBefore, Date.now());\n\n if (batch.callSignal !== null && batch.callSignal !== this.lastCallSignalIn) {\n this.lastCallSignalIn = batch.callSignal;\n this.events.onCallSignal?.(batch.callSignal);\n }\n this.peerRtcSignal = batch.rtcSignal;\n const newSignal = batch.rtcSignal !== null && batch.rtcSignal !== this.lastRtcSignalIn;\n // A slow read, the contact's first packet, or a signal: the steps of a pairing, timed. Every\n // read when a measurement asked for the whole trace.\n if (ms > 1_500 || (online && !wasOnline) || batch.packetTimestamp !== wasSeen || newSignal || (globalThis as { __ghostlyLinkTrace?: boolean }).__ghostlyLinkTrace)\n traceLink(this.identity.pubKeyZ32, \"poll\", { ms, pace, online, age: Date.now() - batch.packetTimestamp, seen: Date.now() - seenAt, rtc: newSignal, first: wasSeen === 0 });\n if (newSignal) {\n this.lastRtcSignalIn = batch.rtcSignal!;\n this.events.onRtcSignal?.(batch.rtcSignal!, readBefore ? { since: readBefore, after: peerPacketBefore || null } : undefined);\n }\n } else if (ms > 1_500 || (globalThis as { __ghostlyLinkTrace?: boolean }).__ghostlyLinkTrace) traceLink(this.identity.pubKeyZ32, \"poll\", { ms, pace, packet: false });\n\n if (receivedNew) await this.publish().catch(() => {});\n this.events.onStatus?.(\"online\");\n if (!this.firstPollDone) { this.firstPollDone = true; this.events.onFirstPoll?.(); }\n } catch (error) {\n if (this.running) {\n traceLink(this.identity.pubKeyZ32, \"poll\", { ms: Date.now() - started, error: String(error) });\n // A read the relays' request budget held back (nothing known yet to answer from) is a wait: the next poll reads.\n if (!isDiscoveryBudgetError(error)) {\n this.discoveryResult(\"read\", error);\n this.events.onStatus?.(\"error\");\n }\n if (!this.firstPollDone) { this.firstPollDone = true; this.events.onFirstPoll?.(); }\n }\n } finally {\n this.polling = false;\n if (this.running && !this.pollTimer) {\n const interval = this.nextInterval();\n // The interval is a period, counted from the start of this read: a read that took long (a key\n // nobody has yet, a relay asking its DHT) does not push the next one further out.\n const wait = Math.max(MIN_POLL_GAP_MS, interval - (Date.now() - started));\n this.events.onPoll?.({ polling: false, nextInMs: wait });\n this.pollTimer = setTimeout(() => {\n this.pollTimer = null;\n void this.poll();\n }, wait);\n }\n }\n }\n}\n","/**\n * Whether this device's own WebRTC attempts say that direct connections do not get through its network: a VPN that\n * takes every packet through its tunnel, a firewall that drops UDP, a carrier's NAT. The app cannot see a VPN; it can\n * see that its attempts keep failing the same way, and say so where people look at the connection (the chat's\n * connection panel, Settings → Network). Chats still go live through a relayed transport, or carry short texts on the\n * DHT.\n *\n * What an attempt says (`DataLink` tells, per attempt):\n * - `open`: a WebRTC connection opened (`closed`: that one ended). Direct connections work here, whatever failed before.\n * - `no-public`: the attempt did not connect, and this device had gathered no public candidate for it (none at all, or\n * host addresses only: no STUN server answered). Its own evidence: nothing of the contact's is in it.\n * - `symmetric`: it did not connect, and this device's public address was a different port for each STUN server it\n * asked (`symmetricNat`): its network maps every destination apart, so a contact cannot reach the port it was told.\n * Its own evidence too.\n * - `no-path`: this device's offer was answered, it had public candidates that read as reachable, and no pair\n * connected. The contact may be the one behind the network that blocks; several contacts are this device.\n *\n * The rule, on purpose slow to say yes: no WebRTC connection open now and none opened since, and either\n * `DIRECT_OWN_ATTEMPTS` attempts with this device's own evidence, or attempts that failed with `DIRECT_PEERS`\n * different contacts (a contact who is also a member of a group counts under each key, so two is not enough). One\n * failed dial never shows anything. It clears by itself: on the first connection that opens, when the network changes\n * (`reset`), and once the evidence is older than `DIRECT_EVIDENCE_MS` (a network that changed without the app hearing\n * of it).\n */\nexport type DirectEvidence = \"open\" | \"closed\" | \"no-public\" | \"symmetric\" | \"no-path\";\n\n/** Evidence older than this says nothing about the network now. */\nexport const DIRECT_EVIDENCE_MS = 30 * 60_000;\n/** Attempts with this device's own evidence (`no-public`, `symmetric`) that it takes. */\nexport const DIRECT_OWN_ATTEMPTS = 2;\n/** Different contacts the attempts must have failed with, whatever their kind. */\nexport const DIRECT_PEERS = 3;\n/** Failures kept: the rule needs a few, and a group's edges may bring many. */\nconst KEPT = 64;\n/** Server reflexive candidates on one address with this many different ports: a mapping per destination. */\nconst SYMMETRIC_PORTS = 3;\n\nexport interface DirectFailure { peer: string; kind: \"no-public\" | \"symmetric\" | \"no-path\"; at: number }\n\n/**\n * Whether a local description shows a NAT that maps each destination apart: the app asks several STUN servers from one\n * socket, and a NAT that keeps one mapping answers with one address and port (the browser lists it once). Three ports\n * on one address, not two: a device on two interfaces behind one router (Wi-Fi and a cable) has two.\n */\nexport function symmetricNat(sdp: string | undefined | null): boolean {\n const ports = new Map<string, Set<string>>();\n for (const [, address, port] of (sdp ?? \"\").matchAll(/^a=candidate:\\S+ \\d+ udp \\d+ (\\S+) (\\d+) typ srflx\\b/gim)) {\n const seen = ports.get(address) ?? new Set<string>();\n ports.set(address, seen.add(port));\n }\n return [...ports.values()].some(seen => seen.size >= SYMMETRIC_PORTS);\n}\n\n/** The rule alone: whether `failures` (every one after the last `open`) say direct connections are blocked at `now`. */\nexport function directBlocked(failures: readonly DirectFailure[], now: number): boolean {\n const fresh = failures.filter(f => now - f.at < DIRECT_EVIDENCE_MS);\n if (fresh.filter(f => f.kind !== \"no-path\").length >= DIRECT_OWN_ATTEMPTS) return true;\n return new Set(fresh.map(f => f.peer)).size >= DIRECT_PEERS;\n}\n\nexport class DirectPathWatch {\n private failures: DirectFailure[] = [];\n /** Contacts a WebRTC connection is open with now. */\n private readonly open = new Set<string>();\n private was = false;\n private timer: ReturnType<typeof setTimeout> | null = null;\n\n /** `onChange`: `blocked` changed (evidence came, a connection opened, the network changed, the evidence aged out). */\n constructor(private readonly onChange: (blocked: boolean) => void = () => {}, private readonly now: () => number = Date.now) {}\n\n /** One WebRTC attempt with `peer` ended this way (or its open connection closed). */\n note(peer: string, evidence: DirectEvidence): void {\n if (evidence === \"open\") { this.open.add(peer); this.failures = []; }\n else if (evidence === \"closed\") this.open.delete(peer);\n else this.failures = [...this.failures, { peer, kind: evidence, at: this.now() }].slice(-KEPT);\n this.settle();\n }\n\n /** The network changed: what failed on the old one says nothing about this one. */\n reset(): void {\n this.failures = [];\n this.settle();\n }\n\n get blocked(): boolean { return !this.open.size && directBlocked(this.failures, this.now()); }\n\n /** Stops the timer that lets the evidence age out. */\n close(): void {\n if (this.timer) clearTimeout(this.timer);\n this.timer = null;\n }\n\n private settle(): void {\n this.close();\n const blocked = this.blocked;\n if (blocked) {\n // Looked at again when the oldest evidence ages out: what is left may no longer be enough.\n const oldest = Math.min(...this.failures.map(f => f.at));\n this.timer = setTimeout(() => { this.failures = this.failures.filter(f => this.now() - f.at < DIRECT_EVIDENCE_MS); this.settle(); },\n Math.max(1_000, oldest + DIRECT_EVIDENCE_MS - this.now()));\n (this.timer as { unref?: () => void }).unref?.();\n }\n if (blocked === this.was) return;\n this.was = blocked;\n this.onChange(blocked);\n }\n}\n","import { sdpHasCandidates, waitForIceGathering } from \"./callSignal\";\nimport { symmetricNat, type DirectEvidence } from \"./directPath\";\nimport { wrapDataChannel, type FrameChannel } from \"./frames\";\nimport { traceLink } from \"./linkTrace\";\nimport {\n DATA_CHANNEL_ID,\n DATA_CHANNEL_LABEL,\n RTC_SIGNAL_FUTURE_MS,\n buildDataSdp,\n extractRtcParams,\n offerIsFresh,\n type SignalSight,\n parseRtcSignal,\n type RtcSignal,\n} from \"./signal\";\n\n/**\n * The WebRTC data link of one link: a single RTCPeerConnection carrying the\n * negotiated `ghostly/1` DataChannel. Offer and answer travel through the\n * `_rtc` record; once the channel is open Pkarr is out of the picture and all\n * application traffic flows peer to peer (or through TURN, if ICE needs it).\n */\nexport type DataLinkState = \"idle\" | \"offering\" | \"answering\" | \"connecting\" | \"open\";\n\nexport interface DataLinkOptions {\n myPubKeyZ32: string;\n peerPubKeyZ32: string;\n createPeerConnection: () => RTCPeerConnection;\n /** Publishes (or clears, with null) my `_rtc` record. */\n publishSignal: (signal: string | null) => void;\n /** Signaling in progress (or over): look fast for the peer's signal. `offer`: for the answer to this side's offer. */\n setFastPoll: (fast: boolean, offer?: boolean) => void;\n onOpen: (channel: FrameChannel) => void;\n onClose: () => void;\n onState?: (state: DataLinkState) => void;\n /**\n * How long an attempt (offer or answer out, channel not yet open) may take before it is given up, so the\n * next one can start, asked as each one starts. Default `CONNECT_TIMEOUT_MS`; a first pairing, whose contact\n * is right there reading its invite, uses a short one until it is over.\n */\n attemptTimeoutMs?: () => number | undefined;\n /**\n * The open connection went `disconnected` (true: ICE consent checks unanswered, the contact may be gone or its app\n * restarted; the connection is given `DISCONNECT_GRACE_MS`), or came back from it (false).\n */\n onDisconnected?: (disconnected: boolean) => void;\n /**\n * Whether the peer's packet, as last read, still carries its offer of that time (`RtcSignal.ts`). An answer to it\n * that did not connect is then made again (`REANSWERS`); without this, never.\n */\n offerStanding?: (offerTs: number) => boolean;\n /**\n * This side's attempt was given up because the answerer answered its offer again (its connection for the answer\n * applied here is gone): the answerer is there, so the caller may dial again now rather than at its next look.\n */\n onAnswerReplaced?: () => void;\n /**\n * What an attempt said about direct connections from this device (`directPath.ts`): it opened (and, later, closed);\n * it did not connect and this device had no public candidate for it, or a public port per STUN server; or this\n * side's offer was answered and no path connected.\n */\n onDirect?: (evidence: DirectEvidence) => void;\n}\n\n/**\n * An attempt ended from outside (another transport went live first) that had both descriptions for this long and no\n * connection says what one that failed does: a path that exists connects within a second or two.\n */\nexport const STALLED_EVIDENCE_MS = 5_000;\n/** A candidate another network can reach: server reflexive (STUN answered), relayed (TURN), or learnt from the peer. */\nconst PUBLIC_CANDIDATE = /^a=candidate:.* typ (srflx|relay|prflx)\\b/m;\n\nexport const CONNECT_TIMEOUT_MS = 90_000;\nconst ICE_GATHERING_TIMEOUT_MS = 5_000;\n/**\n * A connection with no candidate at all by then has stalled: Chromium, rarely and under load, gathers none, and an\n * offer or answer without one can never connect. It is made again in its place, up to `GATHER_ATTEMPTS` in all.\n */\nexport const GATHER_STALL_MS = 3_000;\nexport const GATHER_ATTEMPTS = 3;\n/** How long a connection may stay `disconnected` before it is given up. */\nconst DISCONNECT_GRACE_MS = 12_000;\n/**\n * An answer whose connection failed, while the peer still offers (`offerStanding`), is made again for the same offer,\n * up to this many times. The offerer's connection waits for an answer for its whole attempt (`CONNECT_TIMEOUT_MS`), and\n * its read of the answer may be held back by its relays' budget; the answerer's ICE gives up after about 30 s of no\n * reply. Going idle then left the link with an offer this side had answered once and would not answer again, until the\n * offerer's attempt ran out: a community's edge was live again 110 s after a member restarted (2026-09-30).\n */\nexport const REANSWERS = 2;\n/** An offer this close to the end of the offerer's attempt is not answered again: the new answer would come too late. */\nconst REANSWER_MARGIN_MS = 15_000;\n\nexport class DataLink {\n state: DataLinkState = \"idle\";\n private pc: RTCPeerConnection | null = null;\n private myOfferTs = 0;\n private lastSignalTs = 0;\n /** The peer's description as this connection was given it: Chrome shows it as `remoteDescription` only once applied. */\n private remoteSdp: string | null = null;\n /** When the connection had both descriptions (`connecting`). */\n private connectingSince = 0;\n private connectTimer: ReturnType<typeof setTimeout> | null = null;\n private disconnectTimer: ReturnType<typeof setTimeout> | null = null;\n /** The offer this side answered in the current attempt, when it took it (this clock), and how many times it answered it again. */\n private answered: { offer: RtcSignal; at: number; again: number } | null = null;\n\n constructor(private readonly options: DataLinkOptions) {}\n\n /** Offers a connection to the peer. Resolves once the offer is published. */\n async connect(): Promise<void> {\n if (this.state !== \"idle\") return;\n this.answered = null;\n this.setState(\"offering\");\n this.options.setFastPoll(true, true);\n try {\n const pc = await this.gathered(true, async (pc) => { await pc.setLocalDescription(await pc.createOffer()); });\n if (!pc) return;\n\n this.myOfferTs = Date.now();\n const signal: RtcSignal = { t: \"o\", ts: this.myOfferTs, ...extractRtcParams(pc.localDescription!.sdp) };\n this.options.publishSignal(JSON.stringify(signal));\n } catch {\n this.reset();\n }\n }\n\n /**\n * Feeds a decrypted `_rtc` value from the peer's packet. `sight`: how this device saw it come (`SignalSight`): the\n * peer's record did not carry it at a read the network answered, or it came on a live session just now.\n *\n * The peer's `ts` orders its signals (a later one replaces an earlier one, and an answer names the offer it is for).\n * It is not what says a signal is recent when this device saw it come: two clocks a few minutes apart are common, and\n * every signal of such a peer was dropped here, without a word. An answer is for the offer it names, which this side\n * made in this attempt, so it has no age to check. An offer is as old as the time since the read that did not have it\n * (`offerIsFresh`); only one the first read finds is judged by its own time, as nothing else dates it.\n *\n * A signal dated more than `RTC_SIGNAL_FUTURE_MS` ahead of this clock is not taken at all: taken, its time would\n * be the latest this link knows of the peer, and every signal the peer makes after it would read as older.\n */\n async handleSignal(json: string, sight?: SignalSight): Promise<void> {\n const signal = parseRtcSignal(json);\n if (!signal || signal.ts <= this.lastSignalTs) return;\n if (signal.ts > Date.now() + RTC_SIGNAL_FUTURE_MS) {\n traceLink(this.options.myPubKeyZ32, \"signal-ahead\", { aheadMs: signal.ts - Date.now() });\n return;\n }\n if (signal.t === \"o\" && !offerIsFresh(signal.ts, sight)) {\n traceLink(this.options.myPubKeyZ32, \"offer-stale\", { ageMs: Date.now() - signal.ts, ...(sight && { sinceMs: Date.now() - sight.since, ...(sight.after !== null && { beforePacketMs: sight.after - signal.ts }) }) });\n return;\n }\n\n if (signal.t === \"o\") {\n if (this.state === \"offering\") {\n // Both sides offered at once: the lower public key keeps its offer.\n if (this.options.myPubKeyZ32 < this.options.peerPubKeyZ32) return;\n } else if (this.state === \"answering\") {\n return;\n }\n // A fresh offer while connected means the peer lost the old connection.\n this.lastSignalTs = signal.ts;\n const wasOpen = this.state === \"open\";\n this.teardown();\n if (wasOpen) { this.options.onDirect?.(\"closed\"); this.options.onClose(); }\n this.answered = { offer: signal, at: Date.now(), again: 0 };\n await this.answer(signal);\n } else if (this.state === \"connecting\" && this.myOfferTs && signal.o === this.myOfferTs && this.pc) {\n // A newer answer to the offer this side already took an answer for: the answerer made it again (`REANSWERS`),\n // its connection for the one applied here is gone, and this one can never come up. It is given up now, rather\n // than at the end of its attempt (a hub that read a member's first answer late held a dead one 40 s, 2026-09-30).\n traceLink(this.options.myPubKeyZ32, \"answer-replaced\", { appliedMs: signal.ts - this.lastSignalTs });\n this.lastSignalTs = signal.ts;\n this.reset();\n this.options.onAnswerReplaced?.();\n } else if (this.state === \"offering\" && signal.o === this.myOfferTs && this.pc) {\n this.lastSignalTs = signal.ts;\n const pc = this.pc;\n try {\n this.remoteSdp = buildDataSdp(signal);\n await pc.setRemoteDescription({ type: \"answer\", sdp: this.remoteSdp });\n // On a quick path the channel is open before this resolves: never step back from open.\n if (this.pc === pc && this.state === \"offering\") this.setState(\"connecting\");\n } catch {\n if (this.pc === pc) this.reset();\n }\n }\n }\n\n /**\n * On a quick path the channel opens before setRemoteDescription resolves (see `handleSignal`), when the connection\n * does not show the answer yet: the one it was given counts, as DTLS already checked the peer against it.\n */\n get fingerprints(): [string, string] | null {\n const local = this.pc?.localDescription?.sdp;\n const remote = this.pc?.remoteDescription?.sdp ?? (this.pc ? this.remoteSdp : null);\n if (!local || !remote) return null;\n try { return [extractRtcParams(local).f.toLowerCase(), extractRtcParams(remote).f.toLowerCase()]; }\n catch { return null; }\n }\n\n close(): void {\n // Given up for another transport that went live while this one had long had both descriptions: evidence all the same.\n const evidence = Date.now() - this.connectingSince >= STALLED_EVIDENCE_MS ? this.directEvidence() : null;\n this.answered = null;\n this.reset();\n if (evidence) this.options.onDirect?.(evidence);\n }\n\n /**\n * What this attempt says about direct connections if it ends now without opening: nothing unless both descriptions\n * were exchanged (this side's offer answered, or its answer out). Then, what this device gathered: no public\n * candidate, a public port per STUN server, or (for the side that offered) candidates that read as reachable.\n */\n private directEvidence(): DirectEvidence | null {\n if (this.state !== \"connecting\" || !this.pc) return null;\n const local = this.pc.localDescription?.sdp ?? \"\";\n return !PUBLIC_CANDIDATE.test(local) ? \"no-public\" : symmetricNat(local) ? \"symmetric\" : this.myOfferTs ? \"no-path\" : null;\n }\n\n /**\n * The attempt on this connection failed (it timed out, ICE failed or closed, its channel closed). An answer the\n * offerer may not have read yet is made again for the same offer while it still stands (`REANSWERS`); anything\n * else goes back to idle.\n */\n private failed(): void {\n const answered = this.state === \"connecting\" ? this.answered : null;\n // How long the offerer's attempt still runs, counted on this clock from when its offer was taken (its `ts` is its clock's).\n const left = answered ? CONNECT_TIMEOUT_MS - (Date.now() - answered.at) : 0;\n const evidence = this.directEvidence();\n if (answered && answered.again < REANSWERS && left > REANSWER_MARGIN_MS && this.options.offerStanding?.(answered.offer.ts)) {\n answered.again++;\n traceLink(this.options.myPubKeyZ32, \"reanswer\", { again: answered.again, leftMs: left });\n this.teardown();\n void this.answer(answered.offer);\n return;\n }\n this.reset();\n if (evidence) this.options.onDirect?.(evidence);\n }\n\n private async answer(offer: RtcSignal): Promise<void> {\n this.setState(\"answering\");\n this.options.setFastPoll(true);\n try {\n const pc = await this.gathered(false, async (pc) => {\n await pc.setRemoteDescription({ type: \"offer\", sdp: buildDataSdp(offer) });\n await pc.setLocalDescription(await pc.createAnswer());\n });\n if (!pc) return;\n\n const signal: RtcSignal = {\n t: \"a\",\n ts: Date.now(),\n o: offer.ts,\n ...extractRtcParams(pc.localDescription!.sdp),\n };\n this.options.publishSignal(JSON.stringify(signal));\n this.setState(\"connecting\");\n } catch {\n this.reset();\n }\n }\n\n /**\n * Makes the connection (`describe` sets its description) and waits for its candidates. One that found none by\n * `GATHER_STALL_MS` is replaced by a new one, up to `GATHER_ATTEMPTS` in all; when none finds any, the link goes back\n * to idle at once rather than hold, for its whole timeout, an attempt that cannot connect. Null when the link moved\n * on meanwhile (closed, or another signal took over) or gave up.\n */\n private async gathered(offer: boolean, describe: (pc: RTCPeerConnection) => Promise<void>): Promise<RTCPeerConnection | null> {\n let stalled: RTCPeerConnection | null = null;\n for (let attempt = 1; ; attempt++) {\n // The new connection takes the old one's place first: closing that one then resets nothing.\n const pc = this.createConnection();\n stalled?.close();\n await describe(pc);\n if (this.pc !== pc) return null;\n const gathering = Date.now();\n await waitForIceGathering(pc, ICE_GATHERING_TIMEOUT_MS, { stallMs: GATHER_STALL_MS });\n if (this.pc !== pc) return null;\n const ms = Date.now() - gathering;\n if (sdpHasCandidates(pc.localDescription?.sdp)) {\n traceLink(this.options.myPubKeyZ32, \"ice-gathered\", { ms, offer, ...(attempt > 1 ? { attempt } : {}) });\n return pc;\n }\n traceLink(this.options.myPubKeyZ32, \"ice-stalled\", { ms, offer, attempt });\n if (attempt >= GATHER_ATTEMPTS) { this.reset(); this.options.onDirect?.(\"no-public\"); return null; }\n stalled = pc;\n }\n }\n\n private createConnection(): RTCPeerConnection {\n const pc = this.options.createPeerConnection();\n this.pc = pc;\n\n // Negotiated channel: both sides declare it, no in-band open handshake.\n const dc = pc.createDataChannel(DATA_CHANNEL_LABEL, { negotiated: true, id: DATA_CHANNEL_ID, ordered: true });\n dc.addEventListener(\"open\", () => {\n if (this.pc !== pc) return;\n this.clearTimers();\n this.answered = null;\n this.setState(\"open\");\n this.options.setFastPoll(false);\n this.options.publishSignal(null);\n this.options.onDirect?.(\"open\");\n this.options.onOpen(wrapDataChannel(dc));\n });\n dc.addEventListener(\"close\", () => {\n if (this.pc === pc) this.failed();\n });\n\n pc.addEventListener(\"connectionstatechange\", () => {\n if (this.pc !== pc) return;\n if (this.disconnectTimer) { clearTimeout(this.disconnectTimer); if (pc.connectionState === \"connected\") this.options.onDisconnected?.(false); }\n this.disconnectTimer = null;\n if (pc.connectionState === \"failed\" || pc.connectionState === \"closed\") this.failed();\n else if (pc.connectionState === \"disconnected\") {\n if (this.state === \"open\") this.options.onDisconnected?.(true);\n this.disconnectTimer = setTimeout(() => {\n if (this.pc === pc) this.failed();\n }, DISCONNECT_GRACE_MS);\n }\n });\n\n this.startAttemptTimer(pc);\n return pc;\n }\n\n /** The attempt on `pc` gives up this long from now (`CONNECT_TIMEOUT_MS`) unless it opens. */\n private startAttemptTimer(pc: RTCPeerConnection): void {\n if (this.connectTimer) clearTimeout(this.connectTimer);\n this.connectTimer = setTimeout(() => {\n if (this.pc === pc && this.state !== \"open\") {\n traceLink(this.options.myPubKeyZ32, \"attempt-timeout\", { state: this.state });\n this.failed();\n }\n }, this.options.attemptTimeoutMs?.() ?? CONNECT_TIMEOUT_MS);\n }\n\n /**\n * This side's offer or answer reached the relays only now (their budget, or an outage, held it back): the attempt\n * counts from here, so the other side has as long to read it as if it had gone out at once.\n */\n signalWentOut(): void {\n if (this.pc && this.connectTimer && this.state !== \"open\" && this.state !== \"idle\") this.startAttemptTimer(this.pc);\n }\n\n private clearTimers(): void {\n if (this.connectTimer) clearTimeout(this.connectTimer);\n if (this.disconnectTimer) clearTimeout(this.disconnectTimer);\n this.connectTimer = this.disconnectTimer = null;\n }\n\n private teardown(): boolean {\n this.clearTimers();\n const pc = this.pc;\n this.pc = null;\n this.myOfferTs = 0;\n this.remoteSdp = null;\n if (!pc) return false;\n pc.close();\n return true;\n }\n\n private reset(): void {\n const wasOpen = this.state === \"open\";\n const hadConnection = this.teardown();\n if (this.state === \"idle\" && !hadConnection) return;\n this.setState(\"idle\");\n this.options.setFastPoll(false);\n this.options.publishSignal(null);\n if (wasOpen) { this.options.onDirect?.(\"closed\"); this.options.onClose(); }\n }\n\n private setState(state: DataLinkState): void {\n if (this.state === state) return;\n this.state = state;\n if (state === \"connecting\") this.connectingSince = Date.now();\n this.options.onState?.(state);\n }\n}\n","import { ed25519, x25519 } from \"@noble/curves/ed25519.js\";\nimport { hkdf } from \"@noble/hashes/hkdf.js\";\nimport { hmac } from \"@noble/hashes/hmac.js\";\nimport { sha256 } from \"@noble/hashes/sha2.js\";\nimport { xchacha20poly1305 } from \"@noble/ciphers/chacha.js\";\nimport { bytesEqual, concatBytes, fromBase64Url, randomBytes, toBase64Url, utf8Decode, utf8Encode } from \"./bytes\";\nimport { identityFromSeed, publicKeyFromZ32 } from \"./identity\";\nimport type { LinkParams } from \"./invite\";\n\n/**\n * The cryptography of the `group-mesh/1` profile (WISP 900 / 9xx group mesh).\n *\n * One random 32-byte **epoch secret** per membership state. It never travels in\n * the clear: the committer seals it to each member of the new roster with an\n * ephemeral X25519 key against the member's Ed25519 member key. From it come\n * the message key of the epoch and the key of the confirmation tag that binds\n * the secret to its commit. Messages are XChaCha20-Poly1305 with the epoch\n * message key and the message header as associated data, and are signed by\n * the sender's member key, since the message key alone is shared by everyone.\n *\n * This is not MLS: there is no ratchet tree, member keys are static for the\n * life of the group, and the only source of post-compromise security is a\n * fresh random secret that a removed member is never sent.\n */\nexport const GROUP_PROFILE = \"group-mesh/1\" as const;\nconst NONCE_LENGTH = 24;\n\nconst info = (label: string) => utf8Encode(`ghostly-group/1 ${label}`);\n\n/** The keys of one epoch, from its secret. */\nexport function epochKeys(secret: Uint8Array, groupId: string, epoch: number): { message: Uint8Array; confirm: Uint8Array } {\n if (secret.length !== 32) throw new Error(\"Invalid epoch secret\");\n const salt = utf8Encode(`${groupId}/${epoch}`);\n return { message: hkdf(sha256, secret, salt, info(\"message\"), 32), confirm: hkdf(sha256, secret, salt, info(\"confirm\"), 32) };\n}\n\nexport function newEpochSecret(): Uint8Array { return randomBytes(32); }\n\nexport interface SealedSecret { e: string; n: string; c: string }\n\n/** The epoch secret for one member: an ephemeral X25519 exchange against the member's Ed25519 key. */\nexport function sealSecret(recipientZ32: string, secret: Uint8Array, aad: string): SealedSecret {\n const ephemeral = x25519.utils.randomSecretKey();\n const ephemeralPublic = x25519.getPublicKey(ephemeral);\n const recipient = ed25519.utils.toMontgomery(publicKeyFromZ32(recipientZ32));\n const key = sealKey(x25519.getSharedSecret(ephemeral, recipient), ephemeralPublic, recipientZ32);\n const nonce = randomBytes(NONCE_LENGTH);\n return { e: toBase64Url(ephemeralPublic), n: toBase64Url(nonce), c: toBase64Url(xchacha20poly1305(key, nonce, utf8Encode(aad)).encrypt(secret)) };\n}\n\n/** Null when the box was not sealed to this seed with this associated data. */\nexport function openSecret(mySeed: Uint8Array, myZ32: string, sealed: SealedSecret, aad: string): Uint8Array | null {\n try {\n const ephemeralPublic = fromBase64Url(sealed.e), nonce = fromBase64Url(sealed.n), box = fromBase64Url(sealed.c);\n if (ephemeralPublic.length !== 32 || nonce.length !== NONCE_LENGTH) return null;\n const key = sealKey(x25519.getSharedSecret(ed25519.utils.toMontgomerySecret(mySeed), ephemeralPublic), ephemeralPublic, myZ32);\n const secret = xchacha20poly1305(key, nonce, utf8Encode(aad)).decrypt(box);\n return secret.length === 32 ? secret : null;\n } catch { return null; }\n}\n\nfunction sealKey(shared: Uint8Array, ephemeralPublic: Uint8Array, recipientZ32: string): Uint8Array {\n return hkdf(sha256, shared, ephemeralPublic, concatBytes(info(\"seal\"), utf8Encode(recipientZ32)), 32);\n}\n\n/**\n * Something for one member only, from another, inside a frame the whole group carries (`group-community/1`\n * pair payloads: payments between two members relayed by hubs). The key mixes an ephemeral X25519 exchange\n * against the recipient's member key with the static exchange between sender and recipient: only the\n * recipient can open it, only the sender (or the recipient) can have made it, and a later leak of the\n * sender's seed alone opens nothing. The associated data binds it to the frame that carries it.\n */\nexport function sealPair(senderSeed: Uint8Array, senderZ32: string, recipientZ32: string, plaintext: Uint8Array, aad: string): SealedSecret {\n const ephemeral = x25519.utils.randomSecretKey();\n const ephemeralPublic = x25519.getPublicKey(ephemeral);\n const recipient = ed25519.utils.toMontgomery(publicKeyFromZ32(recipientZ32));\n const key = pairKey(x25519.getSharedSecret(ephemeral, recipient), x25519.getSharedSecret(ed25519.utils.toMontgomerySecret(senderSeed), recipient), ephemeralPublic, senderZ32, recipientZ32);\n const nonce = randomBytes(NONCE_LENGTH);\n return { e: toBase64Url(ephemeralPublic), n: toBase64Url(nonce), c: toBase64Url(xchacha20poly1305(key, nonce, utf8Encode(aad)).encrypt(plaintext)) };\n}\n\n/** Null unless `senderZ32` sealed it to this seed with this associated data. */\nexport function openPair(mySeed: Uint8Array, myZ32: string, senderZ32: string, sealed: SealedSecret, aad: string): Uint8Array | null {\n try {\n const ephemeralPublic = fromBase64Url(sealed.e), nonce = fromBase64Url(sealed.n);\n if (ephemeralPublic.length !== 32 || nonce.length !== NONCE_LENGTH) return null;\n const mine = ed25519.utils.toMontgomerySecret(mySeed);\n const key = pairKey(x25519.getSharedSecret(mine, ephemeralPublic), x25519.getSharedSecret(mine, ed25519.utils.toMontgomery(publicKeyFromZ32(senderZ32))), ephemeralPublic, senderZ32, myZ32);\n return xchacha20poly1305(key, nonce, utf8Encode(aad)).decrypt(fromBase64Url(sealed.c));\n } catch { return null; }\n}\n\nfunction pairKey(ephemeralShared: Uint8Array, staticShared: Uint8Array, ephemeralPublic: Uint8Array, senderZ32: string, recipientZ32: string): Uint8Array {\n return hkdf(sha256, concatBytes(ephemeralShared, staticShared), ephemeralPublic, concatBytes(utf8Encode(\"ghostly-group/2 pair\"), utf8Encode(senderZ32), utf8Encode(recipientZ32)), 32);\n}\n\n/** Text under the epoch message key, bound to its header. */\nexport function encryptText(messageKey: Uint8Array, aad: string, text: string): { n: string; c: string } {\n const nonce = randomBytes(NONCE_LENGTH);\n return { n: toBase64Url(nonce), c: toBase64Url(xchacha20poly1305(messageKey, nonce, utf8Encode(aad)).encrypt(utf8Encode(text))) };\n}\n\nexport function decryptText(messageKey: Uint8Array, aad: string, nonceB64: string, boxB64: string): string | null {\n try {\n const nonce = fromBase64Url(nonceB64);\n if (nonce.length !== NONCE_LENGTH) return null;\n return utf8Decode(xchacha20poly1305(messageKey, nonce, utf8Encode(aad)).decrypt(fromBase64Url(boxB64)));\n } catch { return null; }\n}\n\n/** Proves the committer holds the secret the commit distributes: HMAC of the commit's untagged hash. */\nexport function confirmationTag(confirmKey: Uint8Array, untaggedHashHex: string): string {\n return toBase64Url(hmac(sha256, confirmKey, utf8Encode(untaggedHashHex)));\n}\nexport function confirmationMatches(confirmKey: Uint8Array, untaggedHashHex: string, tag: string): boolean {\n try { return bytesEqual(fromBase64Url(tag), hmac(sha256, confirmKey, utf8Encode(untaggedHashHex))); } catch { return false; }\n}\n\nexport function sha256Hex(data: Uint8Array): string {\n return Array.from(sha256(data), b => b.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\n/**\n * The pairwise link two members of a group run between them, derived by both\n * from the X25519 secret of their member keys and nothing else: rendezvous\n * identities for each side and the symmetric key of their discovery records.\n * No third party, the admin included, can compute it; a member who left keeps\n * only the edges it was part of, and those are closed by the remaining side.\n */\nexport function edgeParams(groupId: string, mySeed: Uint8Array, myZ32: string, peerZ32: string): LinkParams {\n const shared = x25519.getSharedSecret(ed25519.utils.toMontgomerySecret(mySeed), ed25519.utils.toMontgomery(publicKeyFromZ32(peerZ32)));\n const salt = utf8Encode(groupId);\n const seed = (owner: string) => hkdf(sha256, shared, salt, concatBytes(info(\"edge seed\"), utf8Encode(owner)), 32);\n return {\n seedB64: toBase64Url(seed(myZ32)),\n peerPubKeyZ32: identityFromSeed(seed(peerZ32)).pubKeyZ32,\n encKeyB64: toBase64Url(hkdf(sha256, shared, salt, info(\"edge key\"), 32)),\n profile: \"paired-chat/1\",\n };\n}\n","import { fromBase64Url, toBase64Url, utf8Encode } from \"./bytes\";\nimport { publicKeyFromZ32, sign, verify } from \"./identity\";\nimport { sha256Hex } from \"./groupCrypto\";\n\n/**\n * Membership of a `group-mesh/1` group is a chain of signed commits. Each\n * commit carries the whole roster after it, so any member can name the roster\n * of any epoch from the chain alone; the chain, from its genesis, is what\n * authenticates a roster to a member who was not there for the changes.\n *\n * Exactly one admin per epoch (the coordinator of WISP 900): every commit is\n * signed by the admin of the previous epoch, and admission of a new member,\n * removal, transfer of the admin role and a bare rotation each advance the\n * epoch by one. Two different commits after the same epoch are a fork, which\n * halts the group rather than picking a winner.\n */\nexport type GroupRole = \"admin\" | \"member\";\nexport type CommitKind = \"create\" | \"add\" | \"remove\" | \"role\" | \"rotate\";\nexport type Roster = [key: string, role: GroupRole][];\n\nexport interface GroupCommit {\n v: 1;\n /** Group id: 22 base64url characters. */\n g: string;\n /** Epoch: the commit's index in the chain, from 0. */\n e: number;\n /** Hash of the previous commit; empty for the genesis. */\n p: string;\n k: CommitKind;\n /** The roster after this commit, sorted by key. */\n m: Roster;\n /** The committer. */\n by: string;\n /** The member added, removed or made admin. */\n s?: string;\n ts: number;\n /** Confirmation tag: HMAC of the untagged commit under the new epoch's confirm key. */\n c: string;\n sig: string;\n}\n\n/**\n * Members a roster may hold. Apps from before revision 0.9 of the profile refuse a commit whose roster is larger than\n * `LEGACY_GROUP_MEMBERS` (as malformed), so an admin grows a group past that only when every member's app said it can\n * (`paired-groups` version `GROUP_VERSION_LARGE`).\n */\nexport const MAX_GROUP_MEMBERS = 32;\nexport const LEGACY_GROUP_MEMBERS = 8;\n/**\n * The cap the chain rules and the engine apply: `MAX_GROUP_MEMBERS`, always, in the apps. Only the scale harness raises\n * it, to measure what hubs would cost past 32 (WISP 9xx · Group Mesh § Cost per member); an app never does.\n */\nexport const GROUP_MEMBER_CAP = { max: MAX_GROUP_MEMBERS as number };\n/** The `paired-groups` version an app announces when it takes mesh rosters of up to `MAX_GROUP_MEMBERS`. */\nexport const GROUP_VERSION_LARGE = 3;\nexport const MAX_GROUP_CHAIN = 1024;\nexport const GROUP_ID = /^[A-Za-z0-9_-]{22}$/;\nexport const MEMBER_KEY = /^[ybndrfg8ejkmcpqxot1uwisza345h769]{52}$/;\nconst TAG = /^[A-Za-z0-9_-]{43}$/;\nconst SIG = /^[A-Za-z0-9_-]{86}$/;\nconst HASH = /^[a-f0-9]{64}$/;\n\nconst tuple = (c: Omit<GroupCommit, \"sig\">, tag = c.c) => JSON.stringify([c.v, c.g, c.e, c.p, c.k, c.m, c.by, c.s ?? \"\", c.ts, tag]);\n\nexport function commitHash(commit: Omit<GroupCommit, \"sig\">): string { return sha256Hex(utf8Encode(tuple(commit))); }\n/** What the confirmation tag covers: the commit with an empty tag. */\nexport function commitUntaggedHash(commit: Omit<GroupCommit, \"sig\" | \"c\">): string { return sha256Hex(utf8Encode(tuple({ ...commit, c: \"\" }, \"\"))); }\n\nexport function signCommit(commit: Omit<GroupCommit, \"sig\">, seed: Uint8Array): GroupCommit {\n return { ...commit, sig: toBase64Url(sign(utf8Encode(tuple(commit)), seed)) };\n}\n\nexport function rosterAdmin(roster: Roster): string | undefined { return roster.find(([, role]) => role === \"admin\")?.[0]; }\nexport function rosterHas(roster: Roster, key: string): boolean { return roster.some(([k]) => k === key); }\nexport const sortRoster = (roster: Roster): Roster => [...roster].sort((a, b) => a[0] < b[0] ? -1 : 1);\n\nfunction parseCommit(raw: unknown): GroupCommit | null {\n if (!raw || typeof raw !== \"object\") return null;\n const c = raw as Record<string, unknown>;\n if (c.v !== 1 || typeof c.g !== \"string\" || !GROUP_ID.test(c.g) || !Number.isSafeInteger(c.e) || (c.e as number) < 0 ||\n typeof c.p !== \"string\" || (c.p !== \"\" && !HASH.test(c.p)) || ![\"create\", \"add\", \"remove\", \"role\", \"rotate\"].includes(c.k as string) ||\n !Array.isArray(c.m) || c.m.length === 0 || c.m.length > GROUP_MEMBER_CAP.max ||\n !c.m.every(entry => Array.isArray(entry) && entry.length === 2 && typeof entry[0] === \"string\" && MEMBER_KEY.test(entry[0]) && (entry[1] === \"admin\" || entry[1] === \"member\")) ||\n typeof c.by !== \"string\" || !MEMBER_KEY.test(c.by) || (c.s !== undefined && (typeof c.s !== \"string\" || !MEMBER_KEY.test(c.s))) ||\n !Number.isSafeInteger(c.ts) || (c.ts as number) <= 0 || typeof c.c !== \"string\" || !TAG.test(c.c) || typeof c.sig !== \"string\" || !SIG.test(c.sig)) return null;\n const m = c.m as Roster;\n if (new Set(m.map(([k]) => k)).size !== m.length || m.filter(([, r]) => r === \"admin\").length !== 1) return null;\n for (let i = 1; i < m.length; i++) if (!(m[i - 1][0] < m[i][0])) return null;\n return { v: 1, g: c.g, e: c.e as number, p: c.p, k: c.k as CommitKind, m, by: c.by, ...(c.s !== undefined ? { s: c.s as string } : {}), ts: c.ts as number, c: c.c, sig: c.sig };\n}\n\n/** The roster a commit must carry, given the previous one; null when the change is not allowed. */\nexport function expectedRoster(previous: Roster | null, kind: CommitKind, by: string, subject?: string): Roster | null {\n if (kind === \"create\") return previous === null && !subject ? [[by, \"admin\"]] : null;\n if (!previous || rosterAdmin(previous) !== by) return null;\n switch (kind) {\n case \"add\":\n if (!subject || subject === by || rosterHas(previous, subject) || previous.length >= GROUP_MEMBER_CAP.max) return null;\n return sortRoster([...previous, [subject, \"member\"]]);\n case \"remove\":\n if (!subject || subject === by || !rosterHas(previous, subject)) return null;\n return previous.filter(([k]) => k !== subject);\n case \"role\":\n if (!subject || subject === by || !rosterHas(previous, subject)) return null;\n return previous.map(([k]) => [k, k === subject ? \"admin\" : \"member\"]);\n case \"rotate\":\n return subject ? null : previous;\n }\n}\n\nconst sameRoster = (a: Roster, b: Roster) => a.length === b.length && a.every(([k, r], i) => b[i][0] === k && b[i][1] === r);\n\n/**\n * Checks one commit against the chain so far (`previous` is its last commit,\n * or null for a genesis): index, link, authority, roster arithmetic and\n * signature. The confirmation tag is checked separately, once the secret is known.\n */\nexport function verifyCommit(raw: unknown, previous: GroupCommit | null, groupId?: string): { commit: GroupCommit } | { error: string } {\n const commit = parseCommit(raw);\n if (!commit) return { error: \"Malformed membership commit\" };\n if (groupId && commit.g !== groupId) return { error: \"Commit for another group\" };\n if (previous) {\n if (commit.g !== previous.g) return { error: \"Commit for another group\" };\n if (commit.e !== previous.e + 1) return { error: \"Commit out of sequence\" };\n if (commit.p !== commitHash(previous)) return { error: \"Commit does not follow the last known membership\" };\n } else if (commit.e !== 0 || commit.p !== \"\" || commit.k !== \"create\") return { error: \"The chain does not start with the creation of the group\" };\n const expected = expectedRoster(previous?.m ?? null, commit.k, commit.by, commit.s);\n if (!expected || !sameRoster(expected, commit.m)) return { error: \"Unauthorized or inconsistent membership change\" };\n if (!verify(fromBase64Url(commit.sig), utf8Encode(tuple(commit)), publicKeyFromZ32(commit.by))) return { error: \"Membership commit signature is invalid\" };\n return { commit };\n}\n\n/** Only shape and signature: evidence that `by` signed this, whatever chain it belongs to. */\nexport function verifyCommitSignature(raw: unknown): GroupCommit | null {\n const commit = parseCommit(raw);\n return commit && verify(fromBase64Url(commit.sig), utf8Encode(tuple(commit)), publicKeyFromZ32(commit.by)) ? commit : null;\n}\n\n/** A whole chain, from genesis. */\nexport function verifyChain(raw: unknown, groupId?: string): { chain: GroupCommit[] } | { error: string } {\n if (!Array.isArray(raw) || raw.length === 0 || raw.length > MAX_GROUP_CHAIN) return { error: \"Malformed membership chain\" };\n const chain: GroupCommit[] = [];\n for (const entry of raw) {\n const result = verifyCommit(entry, chain[chain.length - 1] ?? null, groupId);\n if (\"error\" in result) return result;\n chain.push(result.commit);\n }\n return { chain };\n}\n","/**\n * How far ahead of this device's clock a number a peer made from its clock may be (a pin's number, a status card's\n * times): clocks drift, but a time past this is the peer's choice, not its clock.\n */\nexport const MESSAGE_CLOCK_SKEW_MS = 5 * 60_000;\n\n/**\n * The time a line of a group's history is kept under (a membership change, a rename): what its commit says, but at\n * most `MESSAGE_CLOCK_SKEW_MS` past this clock, and now when it is not a positive number. Messages do not use it:\n * they are placed where they arrive (below).\n */\nexport function receivedTimestamp(ts: number, now = Date.now()): number {\n if (!Number.isFinite(ts) || ts <= 0) return now;\n return Math.min(ts, now + MESSAGE_CLOCK_SKEW_MS);\n}\n\n/** The latest moment a date can hold. */\nconst MAX_DATE_MS = 8.64e15;\n\n/*\n * A received message has two times (WISP 400, requirement 10).\n *\n * Its place: when it was first stored on this device, kept as the row's `timestamp`. A history is sorted by it, so a\n * conversation reads in the order things happened here: what came is where it came, and what I send next goes\n * below it, whatever the sender's clock says. A peer picks the time it sends, so that time never orders anything.\n *\n * The sender's claim: kept beside it (`sentAt`) and shown on the bubble, never later than the place: a message does\n * not show a time that has not come yet.\n */\n\n/** The time a sender says, as it is kept: a positive time a date can hold, or nothing. */\nexport function claimedTime(ts: unknown): number | undefined {\n return typeof ts === \"number\" && Number.isFinite(ts) && ts > 0 && ts <= MAX_DATE_MS ? ts : undefined;\n}\n\n/**\n * The place of a row stored now: this clock, and past the last place given in its chat, so rows that come in the\n * same millisecond (a hold pickup, a group's catch-up) keep the order they came in.\n */\nexport function arrivalKey(now: number, last = 0): number {\n return Math.max(now, last + 1);\n}\n\n/** The time a message shows: what its sender said, never later than when it came. A row of mine shows its own time. */\nexport function shownTime(message: { timestamp: number; sentAt?: number }): number {\n return message.sentAt === undefined ? message.timestamp : Math.min(message.sentAt, message.timestamp);\n}\n\n/**\n * When something a peer dated happened (an edit), for this device: what the peer says, now at the latest, and now\n * when it says nothing a date can hold.\n */\nexport function heardTime(ts: unknown, now = Date.now()): number {\n return Math.min(claimedTime(ts) ?? now, now);\n}\n","import { utf8Encode } from \"./bytes\";\nimport { MESSAGE_CLOCK_SKEW_MS } from \"./messageTime\";\nimport { sanitizeDisplayText } from \"./text\";\n\n/*\n * Status cards (WISP 4xx · Status Cards): a bot's task or routine, shown as a small card instead of a text. A card rides\n * as `sc` beside the text of a message on every path that carries text with fields of its own (the live session's\n * `paired-message` and `paired-edit`, a private group's sealed box, a community's payload and its edit frame), and the\n * text is its fallback: what an app without cards, the DHT floor and a held item show. The bot keeps a card current by\n * editing its message; the highest edit number wins, as for any edit, and a card belongs to the version it came with.\n *\n * {\"kind\":\"task\",\"id\":\"relay-rotation\",\"title\":\"Fix relay rotation\",\"status\":\"running\",\"progress\":40,\n * \"step\":\"Running the e2e\",\"pr\":{\"url\":\"https://github.com/o/r/pull/612\",\"number\":612,\"additions\":123,\"deletions\":45}}\n *\n * Cards are display only: nothing on one runs anything, and its links are https, shown with their host. Every string\n * is one line of plain text without control, invisible or direction characters; every list and number is bounded.\n * `readStatusCard` is the reader's rule (what does not hold is left out, a card that cannot stand is dropped, never the\n * message); `checkStatusCard` is the sender's (anything out of bounds is refused, so nothing is lost silently).\n */\n\nexport const STATUS_CARD_LIMITS = {\n /** A card's JSON, as sent (UTF-8 bytes): with the fallback text it stays well inside every message bound. */\n bytes: 8 * 1024,\n /** The sender's id for the card: stable across its updates. */\n id: 64,\n title: 120,\n /** The current step, a list item, a run's summary. */\n line: 200,\n branch: 120,\n schedule: 80,\n cron: 64,\n linkLabel: 40,\n url: 512,\n items: 20,\n links: 4,\n runs: 10,\n /** Any count on a card (a PR's number, lines, files; steps). */\n count: 1_000_000_000,\n /** How far ahead a routine's next run may be. */\n nextRunMs: 366 * 24 * 60 * 60_000,\n /** Edits a message with a card takes (a text takes `MAX_EDITS_PER_MESSAGE`): a bot updates a long task often. */\n edits: 5_000,\n /** Labels on a task, and a label's length in characters. */\n tags: 3,\n tag: 24,\n /** Buttons on a message (WISP 4xx · Message Buttons). */\n buttons: 6,\n /** A button's id: what a press names. */\n buttonId: 32,\n /** A button's label, in characters. */\n buttonLabel: 40,\n} as const;\n\n/** How a button looks: `primary` is the one highlighted, `danger` warns; `neutral` when none is said. */\nexport const BUTTON_STYLES = [\"primary\", \"neutral\", \"danger\"] as const;\nexport type ButtonStyle = typeof BUTTON_STYLES[number];\n\nexport const TASK_STATUSES = [\"queued\", \"running\", \"blocked\", \"done\", \"failed\", \"cancelled\"] as const;\nexport type TaskStatus = typeof TASK_STATUSES[number];\n/** A task that is still going: shown first, and counted on the chat's Tasks button. */\nexport const ACTIVE_TASK_STATUSES: readonly TaskStatus[] = [\"queued\", \"running\", \"blocked\"];\nexport const ITEM_STATES = [\"pending\", \"running\", \"done\", \"failed\", \"skipped\"] as const;\nexport type ItemState = typeof ITEM_STATES[number];\nexport const ROUTINE_STATES = [\"active\", \"paused\"] as const;\nexport type RoutineState = typeof ROUTINE_STATES[number];\nexport const RUN_RESULTS = [\"ok\", \"failed\", \"skipped\"] as const;\nexport type RunResult = typeof RUN_RESULTS[number];\n\n/**\n * Where a task's pull request stands: `draft` (not ready for review), `open` (ready for review), `merged`, `closed`\n * (closed without a merge). A reader that does not know the field shows the pull request as before.\n */\nexport const PR_STATES = [\"draft\", \"open\", \"merged\", \"closed\"] as const;\nexport type PrState = typeof PR_STATES[number];\n/** How a pull request's checks stand. */\nexport const PR_CHECKS = [\"passing\", \"failing\", \"pending\"] as const;\nexport type PrChecks = typeof PR_CHECKS[number];\n\nexport interface CardLink { url: string; label?: string }\nexport interface TaskItem { text: string; state: ItemState }\nexport interface TaskPr { url: string; number?: number; additions?: number; deletions?: number; files?: number; state?: PrState; checks?: PrChecks }\n\nexport interface TaskCard {\n kind: \"task\";\n id: string;\n title: string;\n status: TaskStatus;\n /** 0 to 100. */\n progress?: number;\n /** Steps done of `total`. */\n done?: number;\n total?: number;\n /** What it is doing now. */\n step?: string;\n pr?: TaskPr;\n branch?: string;\n startedAt?: number;\n updatedAt?: number;\n items?: TaskItem[];\n links?: CardLink[];\n /** Up to `STATUS_CARD_LIMITS.tags` short labels (an area, a repository), shown as chips. */\n tags?: string[];\n /** The id of another task of the same sender in the same chat that this one is a part of. */\n parent?: string;\n}\n\nexport interface RoutineRun { at: number; result: RunResult; summary?: string }\n\nexport interface RoutineCard {\n kind: \"routine\";\n id: string;\n name: string;\n /** When it runs, as people say it (\"every day 01:00\"). */\n schedule: string;\n cron?: string;\n state: RoutineState;\n lastRun?: RoutineRun;\n nextRunAt?: number;\n /** Recent runs, newest first. */\n runs?: RoutineRun[];\n links?: CardLink[];\n}\n\n/**\n * A button under a bot's message (WISP 4xx · Message Buttons). `once`: a press on it answers the question for that\n * person, and their app offers none of the buttons again; a button without it may be pressed again.\n */\nexport interface CardButton { id: string; label: string; style?: ButtonStyle; once?: true }\n\n/**\n * Buttons under a bot's message (WISP 4xx · Message Buttons): unlike a task or a routine, the message's text shows\n * with them, and is also what an app without buttons shows (so it should say how to answer in words). `chosen` is the\n * answer the bot took, marked for everyone; `closed`: no button takes a press any more.\n */\nexport interface ButtonsCard {\n kind: \"buttons\";\n id: string;\n buttons: CardButton[];\n chosen?: string;\n closed?: true;\n}\n\nexport type StatusCard = TaskCard | RoutineCard | ButtonsCard;\nexport type StatusCardKind = StatusCard[\"kind\"];\n\nconst ID = /^[A-Za-z0-9_.:][A-Za-z0-9_.:-]{0,63}$/;\n/** A button's id: 1 to 32 of `A-Z a-z 0-9 _ . : -`, not starting with `-`. */\nexport const BUTTON_ID = /^[A-Za-z0-9_.:][A-Za-z0-9_.:-]{0,31}$/;\nconst CRON = /^[0-9A-Za-z*,/?#\\- ]{1,64}$/;\n\n/**\n * Whether a typed answer is a button's label or id (WISP 4xx · Message Buttons): ignoring case and spaces at the ends.\n * Not the device's locale's case: the presser's and the author's apps must agree (\"I\" is not \"ı\" anywhere).\n */\nexport const sameButtonText = (a: string, b: string): boolean => a.trim().toLowerCase() === b.trim().toLowerCase();\n\n/**\n * Two buttons a typed answer could not tell apart, or none: a label that reads as an earlier label (`label`), or a\n * label that reads as another button's id (`id`), both as `sameButtonText` compares them. The labels as a reader keeps\n * them (one line, cleaned). `at` is the button that clashes, `with` the one it clashes with.\n */\nexport function buttonLabelClash(buttons: readonly unknown[]): { at: number; with: number; kind: \"label\" | \"id\" } | undefined {\n const labels = buttons.map(button => (isObject(button) ? cardLine(button.label, Infinity) : undefined));\n const ids = buttons.map(button => (isObject(button) && typeof button.id === \"string\" ? button.id : undefined));\n for (const [at, label] of labels.entries()) {\n if (!label) continue;\n const twin = labels.findIndex((other, i) => i < at && !!other && sameButtonText(other, label));\n if (twin !== -1) return { at, with: twin, kind: \"label\" };\n const named = ids.findIndex((id, i) => i !== at && id !== undefined && sameButtonText(id, label));\n if (named !== -1) return { at, with: named, kind: \"id\" };\n }\n return undefined;\n}\n\nconst isObject = (value: unknown): value is Record<string, unknown> => typeof value === \"object\" && value !== null && !Array.isArray(value);\n\n/** One line of display text: runs of space (line breaks included) become one space, then the display rules for names. */\nexport function cardLine(value: unknown, max: number): string | undefined {\n if (typeof value !== \"string\") return undefined;\n return sanitizeDisplayText(value.replace(/\\s+/gu, \" \"), max);\n}\n\n/**\n * An https link a card may show: no credentials, a host, no spaces or control characters, at most\n * `STATUS_CARD_LIMITS.url` characters. Returned as the parser writes it (a non-ASCII host in punycode).\n */\nexport function cardUrl(value: unknown): string | undefined {\n if (typeof value !== \"string\" || !value || value.length > STATUS_CARD_LIMITS.url || /[\\s\\p{Cc}\\p{Cf}]/u.test(value)) return undefined;\n let url: URL;\n try { url = new URL(value); } catch { return undefined; }\n if (url.protocol !== \"https:\" || !url.hostname || url.username || url.password) return undefined;\n return url.href.length <= STATUS_CARD_LIMITS.url ? url.href : undefined;\n}\n\n/** The host a card shows beside a link, so the reader sees where it goes before following it. */\nexport function cardLinkHost(url: string): string {\n try { return new URL(url).host; } catch { return \"\"; }\n}\n\nconst count = (value: unknown, min = 0): number | undefined =>\n typeof value === \"number\" && Number.isSafeInteger(value) && value >= min && value <= STATUS_CARD_LIMITS.count ? value : undefined;\n\n/** A time on a card as a reader keeps it: a positive whole number, never later than `latest`. */\nconst time = (value: unknown, latest: number): number | undefined =>\n typeof value === \"number\" && Number.isSafeInteger(value) && value > 0 ? Math.min(value, latest) : undefined;\n\nconst oneOf = <T extends string>(values: readonly T[], value: unknown): T | undefined =>\n typeof value === \"string\" && (values as readonly string[]).includes(value) ? value as T : undefined;\n\nfunction readLinks(raw: unknown): CardLink[] {\n if (!Array.isArray(raw)) return [];\n const links: CardLink[] = [];\n for (const entry of raw.slice(0, STATUS_CARD_LIMITS.links)) {\n if (!isObject(entry)) continue;\n const url = cardUrl(entry.url);\n if (!url) continue;\n const label = cardLine(entry.label, STATUS_CARD_LIMITS.linkLabel);\n links.push({ url, ...(label && { label }) });\n }\n return links;\n}\n\n/** A task's labels as a reader keeps them: the first `STATUS_CARD_LIMITS.tags` that hold, each one clean line, none twice. */\nfunction readTags(raw: unknown): string[] {\n if (!Array.isArray(raw)) return [];\n const tags: string[] = [];\n for (const entry of raw) {\n const tag = cardLine(entry, STATUS_CARD_LIMITS.tag);\n if (tag && !tags.includes(tag)) tags.push(tag);\n if (tags.length === STATUS_CARD_LIMITS.tags) break;\n }\n return tags;\n}\n\nfunction readRun(raw: unknown, now: number): RoutineRun | undefined {\n if (!isObject(raw)) return undefined;\n const at = time(raw.at, now + MESSAGE_CLOCK_SKEW_MS), result = oneOf(RUN_RESULTS, raw.result);\n if (!at || !result) return undefined;\n const summary = cardLine(raw.summary, STATUS_CARD_LIMITS.line);\n return { at, result, ...(summary && { summary }) };\n}\n\n/** The bytes a card takes on the wire: what `STATUS_CARD_LIMITS.bytes` bounds. */\nexport function statusCardBytes(card: unknown): number {\n try { return utf8Encode(JSON.stringify(card) ?? \"\").length; } catch { return Infinity; }\n}\n\n/**\n * A card as a reader takes it from the wire (`sc`), or undefined when it cannot stand: not an object, past\n * `STATUS_CARD_LIMITS.bytes`, a kind this app does not know, no valid id, title or name, status or state. Fields that do\n * not hold are left out and lists are cut to their bound; times are kept no later than the reader's clock allows (a\n * routine's next run up to a year ahead). The message is always kept: without a card it shows its text.\n */\nexport function readStatusCard(raw: unknown, now = Date.now()): StatusCard | undefined {\n if (!isObject(raw) || statusCardBytes(raw) > STATUS_CARD_LIMITS.bytes) return undefined;\n const id = typeof raw.id === \"string\" && ID.test(raw.id) ? raw.id : undefined;\n if (!id) return undefined;\n const latest = now + MESSAGE_CLOCK_SKEW_MS;\n const links = readLinks(raw.links);\n if (raw.kind === \"task\") {\n const title = cardLine(raw.title, STATUS_CARD_LIMITS.title), status = oneOf(TASK_STATUSES, raw.status);\n if (!title || !status) return undefined;\n const progress = typeof raw.progress === \"number\" && Number.isFinite(raw.progress) ? Math.round(Math.min(100, Math.max(0, raw.progress))) : undefined;\n let done = count(raw.done), total = count(raw.total, 1);\n if (done === undefined || total === undefined || done > total) done = total = undefined;\n const step = cardLine(raw.step, STATUS_CARD_LIMITS.line), branch = cardLine(raw.branch, STATUS_CARD_LIMITS.branch);\n let pr: TaskPr | undefined;\n if (isObject(raw.pr)) {\n const url = cardUrl(raw.pr.url);\n if (url) {\n const number = count(raw.pr.number, 1), additions = count(raw.pr.additions), deletions = count(raw.pr.deletions), files = count(raw.pr.files);\n const state = oneOf(PR_STATES, raw.pr.state), checks = oneOf(PR_CHECKS, raw.pr.checks);\n pr = { url, ...(number !== undefined && { number }), ...(additions !== undefined && { additions }), ...(deletions !== undefined && { deletions }), ...(files !== undefined && { files }),\n ...(state && { state }), ...(checks && { checks }) };\n }\n }\n const items: TaskItem[] = [];\n if (Array.isArray(raw.items)) for (const entry of raw.items.slice(0, STATUS_CARD_LIMITS.items)) {\n if (!isObject(entry)) continue;\n const text = cardLine(entry.text, STATUS_CARD_LIMITS.line);\n if (text) items.push({ text, state: oneOf(ITEM_STATES, entry.state) ?? \"pending\" });\n }\n const startedAt = time(raw.startedAt, latest), updatedAt = time(raw.updatedAt, latest);\n const tags = readTags(raw.tags);\n // A task is never a part of itself.\n const parent = typeof raw.parent === \"string\" && ID.test(raw.parent) && raw.parent !== id ? raw.parent : undefined;\n return {\n kind: \"task\", id, title, status,\n ...(progress !== undefined && { progress }), ...(done !== undefined && total !== undefined && { done, total }),\n ...(step && { step }), ...(pr && { pr }), ...(branch && { branch }),\n ...(startedAt && { startedAt }), ...(updatedAt && { updatedAt }),\n ...(items.length && { items }), ...(links.length && { links }),\n ...(tags.length && { tags }), ...(parent && { parent }),\n };\n }\n if (raw.kind === \"routine\") {\n const name = cardLine(raw.name, STATUS_CARD_LIMITS.title), schedule = cardLine(raw.schedule, STATUS_CARD_LIMITS.schedule), state = oneOf(ROUTINE_STATES, raw.state);\n if (!name || !schedule || !state) return undefined;\n const cron = typeof raw.cron === \"string\" && CRON.test(raw.cron.trim()) ? raw.cron.trim() : undefined;\n const lastRun = readRun(raw.lastRun, now);\n const nextRunAt = typeof raw.nextRunAt === \"number\" && Number.isSafeInteger(raw.nextRunAt) && raw.nextRunAt > 0 && raw.nextRunAt <= now + STATUS_CARD_LIMITS.nextRunMs ? raw.nextRunAt : undefined;\n const runs: RoutineRun[] = [];\n if (Array.isArray(raw.runs)) for (const entry of raw.runs.slice(0, STATUS_CARD_LIMITS.runs)) {\n const run = readRun(entry, now);\n if (run) runs.push(run);\n }\n return {\n kind: \"routine\", id, name, schedule, state,\n ...(cron && { cron }), ...(lastRun && { lastRun }), ...(nextRunAt && { nextRunAt }),\n ...(runs.length && { runs }), ...(links.length && { links }),\n };\n }\n if (raw.kind === \"buttons\") {\n if (!Array.isArray(raw.buttons)) return undefined;\n const buttons: CardButton[] = [], seen = new Set<string>();\n for (const entry of raw.buttons.slice(0, STATUS_CARD_LIMITS.buttons)) {\n if (!isObject(entry) || typeof entry.id !== \"string\" || !BUTTON_ID.test(entry.id) || seen.has(entry.id)) continue;\n const label = cardLine(entry.label, STATUS_CARD_LIMITS.buttonLabel);\n if (!label) continue;\n seen.add(entry.id);\n const style = oneOf(BUTTON_STYLES, entry.style);\n buttons.push({ id: entry.id, label, ...(style && style !== \"neutral\" && { style }), ...(entry.once === true && { once: true as const }) });\n }\n if (!buttons.length) return undefined;\n const chosen = typeof raw.chosen === \"string\" && seen.has(raw.chosen) ? raw.chosen : undefined;\n return { kind: \"buttons\", id, buttons, ...(chosen && { chosen }), ...(raw.closed === true && { closed: true as const }) };\n }\n return undefined;\n}\n\n/**\n * A card as a sender may send it, or why not: the reader's rule, but anything it would leave out or cut is refused\n * here instead (an unknown status, a link that is not https, a list past its bound, a line too long), so a bot learns\n * at once and nothing it said is lost on the way. Line breaks and invisible characters are cleaned, as a reader would.\n */\nexport function checkStatusCard(raw: unknown, now = Date.now()): { card: StatusCard } | { error: string } {\n if (!isObject(raw)) return { error: \"A card is an object\" };\n const bytes = statusCardBytes(raw);\n if (bytes > STATUS_CARD_LIMITS.bytes) return { error: `The card takes ${bytes} bytes; at most ${STATUS_CARD_LIMITS.bytes}` };\n if (raw.kind !== \"task\" && raw.kind !== \"routine\" && raw.kind !== \"buttons\") return { error: \"kind is task, routine or buttons\" };\n if (typeof raw.id !== \"string\" || !ID.test(raw.id)) return { error: `id is 1 to ${STATUS_CARD_LIMITS.id} of A-Z a-z 0-9 _ . : - and does not start with -` };\n const line = (field: string, value: unknown, max: number, required = false): string | null => {\n if (value === undefined && !required) return null;\n if (typeof value !== \"string\" || !cardLine(value, Infinity)) return `${field} is text`;\n if ([...cardLine(value, Infinity)!].length > max) return `${field} is at most ${max} characters`;\n return null;\n };\n const url = (field: string, value: unknown) => (cardUrl(value) ? null : `${field} must be an https link of at most ${STATUS_CARD_LIMITS.url} characters`);\n const whole = (field: string, value: unknown, min = 0) => (value === undefined || count(value, min) !== undefined ? null : `${field} is a whole number from ${min}`);\n const errors: (string | null)[] = [];\n if (raw.links !== undefined) {\n if (!Array.isArray(raw.links) || raw.links.length > STATUS_CARD_LIMITS.links) errors.push(`links is a list of at most ${STATUS_CARD_LIMITS.links}`);\n else for (const [i, link] of raw.links.entries()) {\n if (!isObject(link)) { errors.push(`links[${i}] is an object`); continue; }\n errors.push(url(`links[${i}].url`, link.url), line(`links[${i}].label`, link.label, STATUS_CARD_LIMITS.linkLabel));\n }\n }\n if (raw.kind === \"buttons\") {\n if (raw.links !== undefined) errors.push(\"buttons take no links\");\n if (!Array.isArray(raw.buttons) || !raw.buttons.length || raw.buttons.length > STATUS_CARD_LIMITS.buttons) errors.push(`buttons is a list of 1 to ${STATUS_CARD_LIMITS.buttons}`);\n else {\n const ids = new Set<string>();\n for (const [i, button] of raw.buttons.entries()) {\n if (!isObject(button)) { errors.push(`buttons[${i}] is an object`); continue; }\n if (typeof button.id !== \"string\" || !BUTTON_ID.test(button.id)) errors.push(`buttons[${i}].id is 1 to ${STATUS_CARD_LIMITS.buttonId} of A-Z a-z 0-9 _ . : - and does not start with -`);\n else if (ids.has(button.id)) errors.push(`buttons[${i}].id \"${button.id}\" is used twice`);\n else ids.add(button.id);\n errors.push(line(`buttons[${i}].label`, button.label, STATUS_CARD_LIMITS.buttonLabel, true));\n if (button.style !== undefined && !oneOf(BUTTON_STYLES, button.style)) errors.push(`buttons[${i}].style is one of ${BUTTON_STYLES.join(\", \")}`);\n if (button.once !== undefined && button.once !== true) errors.push(`buttons[${i}].once is true or left out`);\n }\n // A typed answer is matched by label or id: two buttons it could not tell apart are refused (a reader takes the first).\n const clash = buttonLabelClash(raw.buttons);\n if (clash) errors.push(`buttons[${clash.at}].label ${JSON.stringify(cardLine((raw.buttons[clash.at] as Record<string, unknown>).label, Infinity))} ${clash.kind === \"label\" ? `repeats buttons[${clash.with}].label` : `is buttons[${clash.with}].id`} (ignoring case and spaces at the ends)`);\n if (raw.chosen !== undefined && !(typeof raw.chosen === \"string\" && ids.has(raw.chosen))) errors.push(\"chosen names one of the buttons\");\n }\n if (raw.closed !== undefined && raw.closed !== true) errors.push(\"closed is true or left out\");\n } else if (raw.kind === \"task\") {\n errors.push(line(\"title\", raw.title, STATUS_CARD_LIMITS.title, true));\n if (!oneOf(TASK_STATUSES, raw.status)) errors.push(`status is one of ${TASK_STATUSES.join(\", \")}`);\n if (raw.progress !== undefined && !(typeof raw.progress === \"number\" && Number.isFinite(raw.progress) && raw.progress >= 0 && raw.progress <= 100)) errors.push(\"progress is a number from 0 to 100\");\n if ((raw.done === undefined) !== (raw.total === undefined)) errors.push(\"done and total go together\");\n else if (raw.done !== undefined) {\n errors.push(whole(\"done\", raw.done), whole(\"total\", raw.total, 1));\n if (typeof raw.done === \"number\" && typeof raw.total === \"number\" && raw.done > raw.total) errors.push(\"done is at most total\");\n }\n errors.push(line(\"step\", raw.step, STATUS_CARD_LIMITS.line), line(\"branch\", raw.branch, STATUS_CARD_LIMITS.branch));\n if (raw.pr !== undefined) {\n if (!isObject(raw.pr)) errors.push(\"pr is an object\");\n else {\n errors.push(url(\"pr.url\", raw.pr.url), whole(\"pr.number\", raw.pr.number, 1), whole(\"pr.additions\", raw.pr.additions), whole(\"pr.deletions\", raw.pr.deletions), whole(\"pr.files\", raw.pr.files));\n if (raw.pr.state !== undefined && !oneOf(PR_STATES, raw.pr.state)) errors.push(`pr.state is one of ${PR_STATES.join(\", \")}`);\n if (raw.pr.checks !== undefined && !oneOf(PR_CHECKS, raw.pr.checks)) errors.push(`pr.checks is one of ${PR_CHECKS.join(\", \")}`);\n }\n }\n if (raw.tags !== undefined) {\n if (!Array.isArray(raw.tags) || raw.tags.length > STATUS_CARD_LIMITS.tags) errors.push(`tags is a list of at most ${STATUS_CARD_LIMITS.tags}`);\n else {\n const seen = new Set<string>();\n for (const [i, tag] of raw.tags.entries()) {\n const problem = line(`tags[${i}]`, tag, STATUS_CARD_LIMITS.tag, true);\n errors.push(problem);\n if (problem) continue;\n const clean = cardLine(tag, Infinity)!;\n if (seen.has(clean)) errors.push(`tags[${i}] ${JSON.stringify(clean)} is there twice`);\n seen.add(clean);\n }\n }\n }\n if (raw.parent !== undefined) {\n if (typeof raw.parent !== \"string\" || !ID.test(raw.parent)) errors.push(`parent is another task's id: 1 to ${STATUS_CARD_LIMITS.id} of A-Z a-z 0-9 _ . : - and does not start with -`);\n else if (raw.parent === raw.id) errors.push(\"parent is another task's id, not its own\");\n }\n for (const field of [\"startedAt\", \"updatedAt\"] as const) {\n const value = raw[field];\n if (value !== undefined && !(typeof value === \"number\" && Number.isSafeInteger(value) && value > 0 && value <= now + MESSAGE_CLOCK_SKEW_MS)) errors.push(`${field} is a time in milliseconds, not in the future`);\n }\n if (raw.items !== undefined) {\n if (!Array.isArray(raw.items) || raw.items.length > STATUS_CARD_LIMITS.items) errors.push(`items is a list of at most ${STATUS_CARD_LIMITS.items}`);\n else for (const [i, item] of raw.items.entries()) {\n if (!isObject(item)) { errors.push(`items[${i}] is an object`); continue; }\n errors.push(line(`items[${i}].text`, item.text, STATUS_CARD_LIMITS.line, true));\n if (item.state !== undefined && !oneOf(ITEM_STATES, item.state)) errors.push(`items[${i}].state is one of ${ITEM_STATES.join(\", \")}`);\n }\n }\n } else {\n errors.push(line(\"name\", raw.name, STATUS_CARD_LIMITS.title, true), line(\"schedule\", raw.schedule, STATUS_CARD_LIMITS.schedule, true));\n if (!oneOf(ROUTINE_STATES, raw.state)) errors.push(`state is one of ${ROUTINE_STATES.join(\", \")}`);\n if (raw.cron !== undefined && !(typeof raw.cron === \"string\" && CRON.test(raw.cron.trim()))) errors.push(`cron is a cron line of at most ${STATUS_CARD_LIMITS.cron} characters`);\n if (raw.nextRunAt !== undefined && !(typeof raw.nextRunAt === \"number\" && Number.isSafeInteger(raw.nextRunAt) && raw.nextRunAt > 0 && raw.nextRunAt <= now + STATUS_CARD_LIMITS.nextRunMs)) errors.push(\"nextRunAt is a time in milliseconds, within a year\");\n const run = (field: string, value: unknown) => {\n if (!isObject(value)) return errors.push(`${field} is an object`);\n if (!(typeof value.at === \"number\" && Number.isSafeInteger(value.at) && value.at > 0 && value.at <= now + MESSAGE_CLOCK_SKEW_MS)) errors.push(`${field}.at is a time in milliseconds, not in the future`);\n if (!oneOf(RUN_RESULTS, value.result)) errors.push(`${field}.result is one of ${RUN_RESULTS.join(\", \")}`);\n errors.push(line(`${field}.summary`, value.summary, STATUS_CARD_LIMITS.line));\n };\n if (raw.lastRun !== undefined) run(\"lastRun\", raw.lastRun);\n if (raw.runs !== undefined) {\n if (!Array.isArray(raw.runs) || raw.runs.length > STATUS_CARD_LIMITS.runs) errors.push(`runs is a list of at most ${STATUS_CARD_LIMITS.runs}`);\n else raw.runs.forEach((entry, i) => run(`runs[${i}]`, entry));\n }\n }\n const error = errors.find((e): e is string => typeof e === \"string\");\n if (error) return { error };\n const card = readStatusCard(raw, now);\n return card ? { card } : { error: \"The card does not hold\" };\n}\n\n/** The progress a task shows, 0 to 100: its own, else its steps done of total; undefined when it says neither. */\nexport function taskProgress(card: Pick<TaskCard, \"progress\" | \"done\" | \"total\">): number | undefined {\n if (card.progress !== undefined) return card.progress;\n return card.done !== undefined && card.total ? Math.round(card.done / card.total * 100) : undefined;\n}\n\nconst TASK_MARK: Record<TaskStatus, string> = { queued: \"⏳\", running: \"🔄\", blocked: \"⛔\", done: \"✅\", failed: \"❌\", cancelled: \"🚫\" };\nconst STATUS_WORD: Record<TaskStatus, string> = { queued: \"Queued\", running: \"Running\", blocked: \"Blocked\", done: \"Done\", failed: \"Failed\", cancelled: \"Cancelled\" };\nconst RESULT_WORD: Record<RunResult, string> = { ok: \"ok\", failed: \"failed\", skipped: \"skipped\" };\n\n/** A time as the fallback text says it: in UTC, to the minute (\"2026-09-30 01:00 UTC\"). */\nexport function cardTimeUtc(ms: number): string {\n return `${new Date(ms).toISOString().slice(0, 16).replace(\"T\", \" \")} UTC`;\n}\n\n/**\n * The text a card's message carries: what an app without cards shows, the DHT floor carries, the chat list previews\n * and search finds. Plain lines, the first naming the card, no bars or layout that would read badly elsewhere.\n */\nexport function statusCardText(card: StatusCard): string {\n // Buttons show with the bot's own text; this is only what goes when it gave none.\n if (card.kind === \"buttons\") return `Reply: ${card.buttons.map(b => b.label).join(\" / \")}`;\n if (card.kind === \"task\") {\n const progress = taskProgress(card);\n const state = [STATUS_WORD[card.status], ...(progress !== undefined ? [`${progress}%`] : []), ...(card.done !== undefined && card.total ? [`${card.done} of ${card.total} steps`] : [])].join(\" · \");\n // \"PR #612 +123 -45 (open, checks passing): https://…\": the state and the checks only when the card says them.\n const standing = card.pr && [...(card.pr.state ? [card.pr.state] : []), ...(card.pr.checks ? [`checks ${card.pr.checks}`] : [])].join(\", \");\n const pr = card.pr && [`PR${card.pr.number !== undefined ? ` #${card.pr.number}` : \"\"}`,\n ...(card.pr.additions !== undefined || card.pr.deletions !== undefined ? [`+${card.pr.additions ?? 0} -${card.pr.deletions ?? 0}`] : []),\n ...(standing ? [`(${standing})`] : [])].join(\" \") + `: ${card.pr.url}`;\n // \"Now:\" only while the task is going: a finished one's last step is not what it does now (the card hides it too).\n const now = card.step && ACTIVE_TASK_STATUSES.includes(card.status) ? [`Now: ${card.step}`] : [];\n return [`${TASK_MARK[card.status]} ${card.title}`, state, ...now, ...(pr ? [pr] : []),\n ...(card.tags?.length ? [`Tags: ${card.tags.join(\", \")}`] : []), ...(card.parent ? [`Part of: ${card.parent}`] : [])].join(\"\\n\");\n }\n const last = card.lastRun && `Last run: ${RESULT_WORD[card.lastRun.result]}, ${cardTimeUtc(card.lastRun.at)}${card.lastRun.summary ? ` · ${card.lastRun.summary}` : \"\"}`;\n return [`🔁 ${card.name}`, `${card.schedule} · ${card.state === \"active\" ? \"active\" : \"paused\"}`, ...(last ? [last] : []),\n ...(card.nextRunAt && card.state === \"active\" ? [`Next run: ${cardTimeUtc(card.nextRunAt)}`] : [])].join(\"\\n\");\n}\n\n/** Whether a message's edit number holds: a text's bound, or a card message's when the edit carries a card. */\nexport function cardEditNumber(e: unknown): e is number {\n return Number.isSafeInteger(e) && (e as number) >= 1 && (e as number) <= STATUS_CARD_LIMITS.edits;\n}\n","import { MEMBER_KEY } from \"./groupCommits\";\nimport { BUTTON_ID } from \"./statusCards\";\nimport { sanitizeDisplayText } from \"./text\";\n\n/*\n * A reply to a message (WISP 400 § Replies): which earlier message of the same chat a new one answers, with a short\n * line of it and who wrote it, so the reply still reads when the receiver no longer has the original (deleted here,\n * or never received). The receiver shows the line only as the replier's app saw it; the original it finds in its own\n * history, by id, is what it trusts. An app without replies ignores the field and shows the text alone.\n */\n\n/**\n * On the wire (`r`): `i` the original's id in this chat, `s` a line of it, `f` who wrote it. In a paired chat `f` is\n * `\"sender\"` (the reply's author wrote the original) or `\"recipient\"` (the reader did); in a group, a member key.\n * `b`: a button press (WISP 4xx · Message Buttons), the id of the original's button this reply presses; its text is\n * the button's label. An app from before buttons drops `b` and shows an ordinary reply.\n */\nexport interface WireReply { i: string; s: string; f: string; b?: string }\n\nexport const REPLY_LIMITS = {\n /** Code points of the line of the original, an ellipsis included when it was cut. */\n snippet: 120,\n /** Characters of the original's id. */\n id: 128,\n /** Characters of a line as it arrives, before it is cleaned: anything longer is not a line of a message. */\n raw: 2048,\n} as const;\n\n/** A paired chat's message id, a payment id or a file's wire id; a group message's `sender:epoch:seq`. */\nexport const REPLY_ID = /^[A-Za-z0-9_:.-]{1,128}$/;\n/** The two authors a paired chat has, from the reply author's side. */\nexport const PAIRED_REPLY_AUTHORS = [\"sender\", \"recipient\"] as const;\n\n/** A picture sent inline has no line worth quoting. */\nconst INLINE_PICTURE = /^data:image\\//i;\n\n/**\n * A line of a message to quote: plain text on one line (line breaks and runs of spaces become one space), without\n * control, invisible or direction characters (WISP 400, as for names), at most `REPLY_LIMITS.snippet` code points,\n * the last of them an ellipsis when it was cut.\n */\nexport function replySnippet(text: string): string {\n if (INLINE_PICTURE.test(text.trimStart())) return \"🖼️ Picture\";\n const flat = text.slice(0, REPLY_LIMITS.raw * 2).replace(/\\s+/gu, \" \");\n const clean = sanitizeDisplayText(flat, REPLY_LIMITS.snippet + 1) ?? \"\";\n const points = [...clean];\n return points.length > REPLY_LIMITS.snippet ? `${points.slice(0, REPLY_LIMITS.snippet - 1).join(\"\").trimEnd()}…` : clean;\n}\n\n/**\n * A reply as a receiver takes it: an object with an id of this chat's shape, a line (cleaned and cut again here,\n * whatever the sender did) and an author `author` accepts. Anything else is no reply: the message stays, without it.\n */\nexport function readReply(raw: unknown, author: (f: string) => boolean): WireReply | undefined {\n if (!raw || typeof raw !== \"object\" || Array.isArray(raw)) return undefined;\n const { i, s, f, b } = raw as Record<string, unknown>;\n if (typeof i !== \"string\" || !REPLY_ID.test(i)) return undefined;\n if (typeof s !== \"string\" || s.length > REPLY_LIMITS.raw) return undefined;\n if (typeof f !== \"string\" || !author(f)) return undefined;\n // A press that does not hold is only a reply.\n return { i, s: replySnippet(s), f, ...(typeof b === \"string\" && BUTTON_ID.test(b) && { b }) };\n}\n\n/** Who may have written the original in a paired chat. */\nexport const pairedReplyAuthor = (f: string): boolean => (PAIRED_REPLY_AUTHORS as readonly string[]).includes(f);\n/** Who may have written the original in a group: a member key (whether they are still a member is the reader's to see). */\nexport const groupReplyAuthor = (f: string): boolean => MEMBER_KEY.test(f);\n\n/** Only the wire fields, in a fixed order, the line cleaned: what the sender puts on a frame. */\nexport const wireReply = (reply: WireReply): WireReply => ({ i: reply.i, s: replySnippet(reply.s), f: reply.f, ...(reply.b && { b: reply.b }) });\n","import { utf8Encode } from \"./bytes\";\nimport { REPLY_ID } from \"./replies\";\n\n/**\n * Reactions to messages (WISP 400 § Reactions): one emoji per person per message, like WhatsApp. Whoever reacts says\n * which message (the id both sides know it by, as a reply names it), the emoji, and a number of its own that only\n * grows; an empty emoji takes the reaction back. For each person and message the highest number wins, whatever order\n * the frames arrive in, so a new reaction replaces the old one and a late frame changes nothing.\n *\n * A 1:1 chat says it on the live session once both sides list `react/1` in `paired-capabilities`, and the contact\n * confirms each number it took:\n *\n * {\"t\":\"paired-reaction\",\"id\":\"<message id>\",\"e\":\"❤️\",\"n\":1790000000000}\n * {\"t\":\"paired-reacted\",\"n\":1790000000000}\n *\n * Off the live session they ride on the DHT envelopes (WISP 403 § Reactions). Groups carry them in their own frames\n * (WISP 9xx § Reactions). Older apps drop all of these: a `t` they do not know, or a trailing element they skip.\n */\n\nexport const REACTION_FRAME = \"paired-reaction\";\nexport const REACTED_FRAME = \"paired-reacted\";\n/** A reaction in a private group, over the edge from the member who reacts (WISP 9xx group mesh § Reactions). */\nexport const GROUP_REACTION_FRAME = \"group-react\";\n/** A reaction in a community, as an application frame sealed to the group (WISP 9xx group community § Reactions). */\nexport const COMMUNITY_REACTION_FRAME = \"reaction\";\n\n/** The quick bar, in order: what most reactions are. Any other emoji comes from the picker. */\nexport const QUICK_REACTIONS = [\"👍\", \"❤️\", \"😂\", \"😮\", \"😢\", \"🙏\"] as const;\n\nexport const REACTION_LIMITS = {\n /** UTF-8 bytes of one emoji: a family or a subdivision flag fits, a run of emoji does not. */\n emojiBytes: 32,\n /** Reaction frames a receiver takes per window on one chat; the rest are dropped unconfirmed and come again. */\n receive: 30,\n /** Reactions a sender says per window on one chat; the newest of each message waits for room. */\n send: 20,\n windowMs: 10_000,\n /** A reaction to a message not here yet waits this long for it, at most `buffer` of them per chat. */\n bufferMs: 60_000,\n buffer: 64,\n /** Reactions of this side not confirmed yet, per chat: the oldest is dropped past it. */\n pending: 32,\n /** Reactions one DHT envelope carries at most, room allowing. */\n dht: 8,\n} as const;\n\n/** One reaction as it goes on the wire: the message's id, the emoji (\"\" takes it back) and the reactor's number. */\nexport interface WireReaction { id: string; e: string; n: number }\n\nconst PICTOGRAPH = /\\p{Extended_Pictographic}|\\p{Regional_Indicator}{2}|\\u20E3/u;\n/** Emoji_Component holds the joiner, the selector, the keycap, skin tones, tags and regional indicators. */\nconst EMOJI_POINT = /^[\\p{Emoji}\\p{Emoji_Component}]$/u;\nconst PRESENTATION = /^\\p{Emoji_Presentation}$/u;\n\ntype Segmenter = { segment(input: string): Iterable<{ segment: string }> };\nlet segmenter: Segmenter | null | undefined;\nfunction graphemes(text: string): string[] {\n if (segmenter === undefined) {\n const Ctor = (globalThis as { Intl?: { Segmenter?: new (locale?: string, options?: { granularity: string }) => Segmenter } }).Intl?.Segmenter;\n segmenter = Ctor ? new Ctor(undefined, { granularity: \"grapheme\" }) : null;\n }\n if (segmenter) return Array.from(segmenter.segment(text), s => s.segment);\n // Without a segmenter (none of the apps' runtimes): a sequence joined by ZWJ, modifiers or tags counts as one.\n return text ? [text] : [];\n}\n\n/**\n * The emoji a reaction may carry, in one form (so the same emoji from two apps is one chip), or null when it is not\n * one: exactly one grapheme, made of emoji code points only, with a pictograph in it, at most\n * `REACTION_LIMITS.emojiBytes`. A single pictograph drawn as text by default gets the emoji selector (❤ → ❤️); one\n * drawn as emoji by default loses a redundant one (👍️ → 👍). Sequences keep their selectors as sent.\n */\nexport function reactionEmoji(raw: unknown): string | null {\n if (typeof raw !== \"string\" || !raw || raw.length > REACTION_LIMITS.emojiBytes) return null;\n let emoji = raw.normalize(\"NFC\");\n const points = [...emoji.replace(/\\uFE0F/g, \"\")];\n if (points.length === 1) emoji = PRESENTATION.test(points[0]) ? points[0] : `${points[0]}\\uFE0F`;\n if (utf8Encode(emoji).length > REACTION_LIMITS.emojiBytes) return null;\n if (![...emoji].every(point => EMOJI_POINT.test(point)) || !PICTOGRAPH.test(emoji)) return null;\n return graphemes(emoji).length === 1 ? emoji : null;\n}\n\n/** A reactor's number: valid on the wire (a positive safe integer). */\nexport function validReactionNumber(n: unknown): n is number {\n return typeof n === \"number\" && Number.isSafeInteger(n) && n > 0;\n}\n\n/**\n * The number for this side's next reaction in a chat: the clock in milliseconds, or one past the last, whichever is\n * higher. It only grows, even when the clock goes back.\n */\nexport function nextReactionNumber(last = 0, now = Date.now()): number {\n return Math.max(Math.floor(now), last + 1);\n}\n\n/** Whether a reaction numbered `n` replaces what a person's reaction to that message is now. */\nexport function reactionIsNewer(current: { n: number } | undefined, n: number): boolean {\n return !current || n > current.n;\n}\n\n/**\n * A reaction as a receiver takes it, or null: a message id of a chat's shape (as a reply names it), an emoji that\n * holds or \"\" (taken back), and a valid number. Anything else says nothing and is not confirmed.\n */\nexport function readReaction(raw: unknown): WireReaction | null {\n if (!raw || typeof raw !== \"object\" || Array.isArray(raw)) return null;\n const { id, e, n } = raw as Record<string, unknown>;\n if (typeof id !== \"string\" || !REPLY_ID.test(id) || !validReactionNumber(n)) return null;\n if (e === \"\") return { id, e: \"\", n };\n const emoji = reactionEmoji(e);\n return emoji ? { id, e: emoji, n } : null;\n}\n\n/** Only the wire fields, in a fixed order. */\nexport const wireReaction = (r: WireReaction): WireReaction => ({ id: r.id, e: r.e, n: r.n });\n\nexport function reactionFrame(r: WireReaction): string {\n return JSON.stringify({ t: REACTION_FRAME, ...wireReaction(r) });\n}\n\nexport function reactedFrame(n: number): string {\n return JSON.stringify({ t: REACTED_FRAME, n });\n}\n\n/** The reaction a `paired-reaction` frame says, or null when it is malformed. */\nexport function parseReactionFrame(frame: Record<string, unknown>): WireReaction | null {\n return frame?.t === REACTION_FRAME ? readReaction(frame) : null;\n}\n\n/** The number a `paired-reacted` frame confirms, or null when it is malformed. */\nexport function parseReactedFrame(frame: Record<string, unknown>): number | null {\n return frame?.t === REACTED_FRAME && validReactionNumber(frame.n) ? frame.n : null;\n}\n\n/**\n * A DHT envelope's reactions (its thirteenth element): `[[id, emoji, n], …]`. What does not hold is skipped, never the\n * envelope; at most `REACTION_LIMITS.dht` are read.\n */\nexport function readDhtReactions(raw: unknown): WireReaction[] {\n if (!Array.isArray(raw)) return [];\n const out: WireReaction[] = [];\n for (const entry of raw.slice(0, REACTION_LIMITS.dht)) {\n if (!Array.isArray(entry) || entry.length !== 3) continue;\n const reaction = readReaction({ id: entry[0], e: entry[1], n: entry[2] });\n if (reaction) out.push(reaction);\n }\n return out;\n}\n\n/**\n * This side's reactions not confirmed yet, after one more: the newest per message (a new one replaces a pending one\n * for the same message), in the order of their numbers, the oldest dropped past `REACTION_LIMITS.pending`.\n */\nexport function queueReaction(pending: readonly WireReaction[], reaction: WireReaction): WireReaction[] {\n return [...pending.filter(p => p.id !== reaction.id), wireReaction(reaction)].sort((a, b) => a.n - b.n).slice(-REACTION_LIMITS.pending);\n}\n\n/** A sliding window of `limit` events per `windowMs`: a sender's pace and a receiver's limit. */\nexport class ReactionWindow {\n private recent: number[] = [];\n\n constructor(private readonly limit: number, private readonly windowMs: number = REACTION_LIMITS.windowMs, private readonly now: () => number = Date.now) {}\n\n /** Counts one now; false (and not counted) when the window is full. */\n take(): boolean {\n const now = this.now();\n this.recent = this.recent.filter(at => now - at < this.windowMs);\n if (this.recent.length >= this.limit) return false;\n this.recent.push(now);\n return true;\n }\n\n /** Milliseconds until the window has room again; 0 when it has room now. */\n wait(): number {\n const now = this.now();\n this.recent = this.recent.filter(at => now - at < this.windowMs);\n return this.recent.length < this.limit ? 0 : this.recent[0] + this.windowMs - now;\n }\n\n reset(): void { this.recent = []; }\n}\n","/*\n * A forwarded message (WISP 400 § Forwards): a text or a file someone sends on from another chat, as a new message of\n * theirs. Only the content goes, never who wrote it first or where it came from; what travels with it is a hop count,\n * how many times it has been forwarded, so the reader can say \"Forwarded\" and, past a few hops, \"Forwarded many times\".\n * An app without forwards ignores the field and shows a message like any other.\n */\n\nexport const FORWARD_LIMITS = {\n /** The count stops here: a message forwarded more often still says it was forwarded many times. */\n max: 255,\n /** From this many hops on, the reader says \"Forwarded many times\". */\n many: 5,\n} as const;\n\n/** A hop count as a receiver takes it: a whole number from 1 to `FORWARD_LIMITS.max`. Anything else is not forwarded. */\nexport function readForwarded(raw: unknown): number | undefined {\n return typeof raw === \"number\" && Number.isSafeInteger(raw) && raw >= 1 && raw <= FORWARD_LIMITS.max ? raw : undefined;\n}\n\n/** The count a message carries once forwarded again: one more hop than it had (none for a message never forwarded). */\nexport function forwardedAgain(hops?: number): number {\n return Math.min(FORWARD_LIMITS.max, (readForwarded(hops) ?? 0) + 1);\n}\n\n/** Whether a message has come far enough to be called \"Forwarded many times\". */\nexport function forwardedMany(hops?: number): boolean {\n return (readForwarded(hops) ?? 0) >= FORWARD_LIMITS.many;\n}\n","import { jpegSize } from \"./avatar\";\n\n/**\n * A link preview as it travels with a chat message (WISP 401 § Link previews): what the sender's app read from the\n * page when the message was written, carried in the message so the reader never contacts the site. Every field\n * but the link is optional. The receiver checks it here and drops it, never the message, when anything is off.\n */\nexport interface LinkPreview {\n /** The link the preview is of: a link of the message's text, without its tracking parameters (`canonicalUrl`). */\n u: string;\n /** The page's title. */\n t?: string;\n /** Its description. */\n d?: string;\n /** The site's name (\"YouTube\"). */\n s?: string;\n /** A thumbnail redrawn by the sender: a JPEG `data:` URL (`LINK_PREVIEW_LIMITS.imageBytes`, `imageSide`). */\n i?: string;\n}\n\nexport const LINK_PREVIEW_LIMITS = {\n urlChars: 2048,\n titleChars: 200,\n descriptionChars: 300,\n siteChars: 80,\n /** The thumbnail's JPEG bytes. Ghostly draws it at most 320 pixels wide and fits it under this. */\n imageBytes: 20_000,\n /** The largest side a thumbnail may declare in its header (a tiny file can claim a huge image). */\n imageSide: 640,\n /** The preview as JSON. With a text of 16 KiB the message frame stays under the 60 KiB a session takes. */\n jsonChars: 30 * 1024,\n} as const;\n\nconst JPEG_PREFIX = \"data:image/jpeg;base64,\";\n\n/**\n * Query parameters that only say where a click came from. They are dropped from a previewed link, and the card\n * shows and opens the link without them.\n */\nconst TRACKING_PARAMS = new Set([\n \"fbclid\", \"gclid\", \"dclid\", \"gbraid\", \"wbraid\", \"msclkid\", \"yclid\", \"twclid\", \"ttclid\", \"li_fat_id\", \"igshid\", \"igsh\",\n \"mc_cid\", \"mc_eid\", \"_hsenc\", \"_hsmi\", \"mkt_tok\", \"oly_anon_id\", \"oly_enc_id\", \"vero_id\", \"rb_clickid\", \"srsltid\",\n \"ref_src\", \"ref_url\", \"si\", \"__s\", \"s_cid\", \"spm\",\n]);\nconst isTracking = (name: string) => /^utm_/i.test(name) || TRACKING_PARAMS.has(name.toLowerCase());\n\nconst LINK_CLOSERS: Record<string, string> = { \")\": \"(\", \"]\": \"[\", \"}\": \"{\" };\n\n/**\n * A link ends where the sentence around it takes over: trailing punctuation (\"see https://x.example/a.\") and a\n * closing bracket or quote the link did not open (\"(https://x.example/a)\") are left as text. The same rule as the\n * message bubble's links.\n */\nexport function linkEnd(url: string): string {\n // Brackets counted once, then taken off as they go: linear in the link, however many \")\" a peer puts at its end.\n const count: Record<string, number> = { \"(\": 0, \")\": 0, \"[\": 0, \"]\": 0, \"{\": 0, \"}\": 0 };\n for (const c of url) if (c in count) count[c]++;\n let end = url.length;\n while (end > 0) {\n const last = url[end - 1];\n const opener = LINK_CLOSERS[last];\n if (!/[.,;:!?'\"*_>]/.test(last) && !(opener && count[last] > count[opener])) break;\n if (opener) count[last]--;\n end--;\n }\n return url.slice(0, end);\n}\n\n/** Characters of a link worth reading: a longer one is no address anyone shares, and is left as text. */\nexport const LINK_MAX_CHARS = 4 * 1024;\n\n/** The http(s) links of a text, in order, each ending as the bubble ends it. */\nexport function linksIn(text: string): string[] {\n return [...text.matchAll(/https?:\\/\\/\\S+/g)].filter(match => match[0].length <= LINK_MAX_CHARS).map(match => linkEnd(match[0])).filter(url => url.length > 8);\n}\n\n/**\n * The link as a preview names it: parsed, http(s) only, without tracking parameters (`utm_*`, `fbclid`, …). The\n * other parameters keep their order and spelling. Null for anything that is not an http(s) URL.\n */\nexport function canonicalUrl(link: string): string | null {\n if (link.length > LINK_PREVIEW_LIMITS.urlChars * 2) return null;\n let url: URL;\n try { url = new URL(link); } catch { return null; }\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") return null;\n if (url.username || url.password) return null;\n const kept = url.search.slice(1).split(\"&\").filter(pair => {\n if (!pair) return false;\n const name = pair.split(\"=\")[0];\n let decoded = name;\n try { decoded = decodeURIComponent(name.replace(/\\+/g, \" \")); } catch { /* the name as written */ }\n return !isTracking(decoded);\n });\n url.search = kept.length ? `?${kept.join(\"&\")}` : \"\";\n const out = url.href;\n return out.length <= LINK_PREVIEW_LIMITS.urlChars ? out : null;\n}\n\n/** Text as a preview carries it: one line, control characters out, cut at `max` characters. */\nfunction clip(value: unknown, max: number): string | undefined {\n if (typeof value !== \"string\") return undefined;\n // eslint-disable-next-line no-control-regex\n const clean = value.replace(/[\\u0000-\\u001f\\u007f-\\u009f‎‏‪-‮⁦-⁩]+/g, \" \").replace(/\\s+/g, \" \").trim();\n if (!clean) return undefined;\n return clean.length > max ? `${clean.slice(0, max - 1).trimEnd()}…` : clean;\n}\n\n/**\n * A thumbnail's size in pixels, from its JPEG header: a card keeps its box before the picture decodes. Undefined for\n * anything but a JPEG data URL within the limits.\n */\nexport function previewImageSize(value: unknown): { width: number; height: number } | undefined {\n if (typeof value !== \"string\" || !value.startsWith(JPEG_PREFIX)) return;\n const data = value.slice(JPEG_PREFIX.length);\n if (data.length > Math.ceil(LINK_PREVIEW_LIMITS.imageBytes / 3) * 4 || data.length % 4 !== 0 || !/^[A-Za-z0-9+/]+={0,2}$/.test(data)) return;\n let binary: string;\n try { binary = atob(data); } catch { return; }\n if (binary.length > LINK_PREVIEW_LIMITS.imageBytes) return;\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);\n return jpegSize(bytes);\n}\n\n/** The thumbnail if it is one Ghostly shows: a JPEG data URL within the limits whose header declares a sane size. */\nexport function sanitizePreviewImage(value: unknown): string | undefined {\n const size = previewImageSize(value);\n const side = LINK_PREVIEW_LIMITS.imageSide;\n if (!size || size.width < 1 || size.height < 1 || size.width > side || size.height > side) return;\n return value as string;\n}\n\n/**\n * A preview as a message may carry it, or undefined. `text` is the message's: the preview must be of one of its\n * links (compared without tracking parameters), so a card can never point somewhere the text does not. Titles and\n * descriptions are cut to their limits; a bad thumbnail is dropped and the rest kept.\n */\nexport function parseLinkPreview(raw: unknown, text: string): LinkPreview | undefined {\n if (!raw || typeof raw !== \"object\" || Array.isArray(raw)) return;\n const value = raw as Record<string, unknown>;\n if (typeof value.u !== \"string\") return;\n const u = canonicalUrl(value.u);\n if (!u || !linksIn(text).some(link => canonicalUrl(link) === u)) return;\n const preview: LinkPreview = { u };\n const t = clip(value.t, LINK_PREVIEW_LIMITS.titleChars), d = clip(value.d, LINK_PREVIEW_LIMITS.descriptionChars);\n const s = clip(value.s, LINK_PREVIEW_LIMITS.siteChars), i = sanitizePreviewImage(value.i);\n if (t) preview.t = t;\n if (d) preview.d = d;\n if (s) preview.s = s;\n if (i) preview.i = i;\n // A card with nothing but the link says no more than the link does.\n if (!t && !d && !i) return;\n if (JSON.stringify(preview).length > LINK_PREVIEW_LIMITS.jsonChars) return;\n return preview;\n}\n","import { sha256 } from \"@noble/hashes/sha2.js\";\nimport { toBase64Url, utf8Encode } from \"./bytes\";\nimport { LIMITS } from \"./frames\";\nimport { parseLinkPreview, type LinkPreview } from \"./linkPreview\";\nimport { cardEditNumber, readStatusCard, type StatusCard } from \"./statusCards\";\n\n/**\n * Editing a sent text in a 1:1 chat (WISP 401 § Edits). The sender says the whole new text of one of its own\n * messages, with a sequence number of its own per message, and the contact confirms the number it now shows:\n *\n * {\"t\":\"paired-edit\",\"id\":\"<the message's wire id>\",\"e\":3,\"ts\":1790000000000,\"m\":\"Done: 12 of 12\"}\n * {\"t\":\"paired-edited\",\"id\":\"<the message's wire id>\",\"e\":3}\n *\n * It needs `edit/1` in `paired-capabilities` on both sides. Only the contact's own messages can be edited from its\n * side: the frame comes on the session authenticated with the pinned contact, and the receiver looks the id up among\n * that contact's messages only. The highest `e` wins, whatever order frames arrive in; an edit that is not newer is\n * confirmed and changes nothing. A preview (`pv`, WISP 401 § Link previews) may come with the new text, as with a\n * message. A status card (`sc`, WISP 4xx · Status Cards) may come with it too; a card message takes up to\n * `STATUS_CARD_LIMITS.edits` edits, a number past `MAX_EDITS_PER_MESSAGE` holding only with a card. Older apps drop\n * both frames: they have an id, but a `t` they do not know.\n */\n\nexport const EDIT_FRAME = \"paired-edit\";\nexport const EDITED_FRAME = \"paired-edited\";\n\n/** Edits one message takes; the sender refuses the next one and the receiver ignores a higher number. */\nexport const MAX_EDITS_PER_MESSAGE = 100;\n\n/** A sender says at most this many edits per `EDIT_RATE_WINDOW_MS` in one chat; the newest text of each message waits. */\nexport const EDIT_SEND_LIMIT = 10;\n/** A receiver takes at most this many edit frames per window; the rest are dropped unconfirmed, so they come again. */\nexport const EDIT_RECEIVE_LIMIT = 30;\nexport const EDIT_RATE_WINDOW_MS = 10_000;\n\n/** An edit of a message not here yet waits this long for it, at most `EDIT_BUFFER_MAX` of them per chat. */\nexport const EDIT_BUFFER_MS = 60_000;\nexport const EDIT_BUFFER_MAX = 32;\n\nconst ID = /^[A-Za-z0-9_-]{22}$/;\n\n/** One edit as it goes on the wire. */\nexport interface WireEdit {\n /** The edited message's wire id. */\n id: string;\n /** Its edit number, 1 for the first edit. */\n e: number;\n /** When the sender made the edit (milliseconds). */\n ts: number;\n /** The whole new text. */\n m: string;\n pv?: LinkPreview;\n /** The status card of this version (WISP 4xx · Status Cards): a message with a card takes an edit with one. */\n sc?: StatusCard;\n}\n\n/** Largest edit frame sent with a preview: the preview is left out past it, as with a message. */\nconst MAX_EDIT_FRAME = 56 * 1024;\n\n/**\n * A `paired-edit` frame; the preview is left out when the frame would be too large with it. A card (`sc`) always\n * goes: it is bounded well under what the frame has room for, and it is what a card message is.\n */\nexport function editFrame(edit: WireEdit): string {\n const base = { t: EDIT_FRAME, id: edit.id, e: edit.e, ts: edit.ts, m: edit.m, ...(edit.sc && { sc: edit.sc }) };\n const plain = JSON.stringify(base);\n if (!edit.pv) return plain;\n const withPreview = JSON.stringify({ ...base, pv: edit.pv });\n return withPreview.length <= MAX_EDIT_FRAME ? withPreview : plain;\n}\n\n/** The frame that confirms edit `e` of message `id`. */\nexport function editedFrame(id: string, e: number): string {\n return JSON.stringify({ t: EDITED_FRAME, id, e });\n}\n\n/** An edit number a message can have. */\nexport function validEditNumber(e: unknown): e is number {\n return Number.isSafeInteger(e) && (e as number) >= 1 && (e as number) <= MAX_EDITS_PER_MESSAGE;\n}\n\n/** A message's new text: not empty, no space around it, within a chat message's bytes. The same on a session and on the DHT. */\nexport function validEditMessage(m: unknown): m is string {\n return typeof m === \"string\" && !!m.trim() && m === m.trim() && utf8Encode(m).length <= LIMITS.maxChatMessageBytes;\n}\n\n/**\n * The edit a `paired-edit` frame says, or null when it is malformed (then it says nothing and is not confirmed). A\n * preview or a card that does not hold is dropped, never the edit; an edit numbered past `MAX_EDITS_PER_MESSAGE`\n * holds only with a card.\n */\nexport function parseEditFrame(frame: Record<string, unknown>, now = Date.now()): WireEdit | null {\n if (frame?.t !== EDIT_FRAME) return null;\n const { id, e, ts, m } = frame;\n if (typeof id !== \"string\" || !ID.test(id) || !cardEditNumber(e)) return null;\n if (typeof ts !== \"number\" || !Number.isSafeInteger(ts) || ts <= 0) return null;\n if (!validEditMessage(m)) return null;\n const sc = frame.sc === undefined ? undefined : readStatusCard(frame.sc, now);\n if (!validEditNumber(e) && !sc) return null;\n const pv = frame.pv === undefined ? undefined : parseLinkPreview(frame.pv, m);\n return { id, e, ts, m, ...(pv && { pv }), ...(sc && { sc }) };\n}\n\n/** The confirmation a `paired-edited` frame says, or null when it is malformed. */\nexport function parseEditedFrame(frame: Record<string, unknown>): { id: string; e: number } | null {\n if (frame?.t !== EDITED_FRAME) return null;\n return typeof frame.id === \"string\" && ID.test(frame.id) && cardEditNumber(frame.e) ? { id: frame.id, e: frame.e } : null;\n}\n\n/**\n * The id an edit goes by on the DHT floor (WISP 403 § Edits): a text of its own there, with the edited message's id and\n * the edit's number beside it. The same for every send of the same edit, so its retries and its receipt match.\n */\nexport function dhtEditId(id: string, e: number): string {\n return toBase64Url(sha256(utf8Encode(`ghostly-edit/1:${id}:${e}`)).slice(0, 16));\n}\n\n/** A sliding window of `limit` events per `windowMs`: the sender's pace and the receiver's limit. */\nexport class RateWindow {\n private recent: number[] = [];\n\n constructor(private readonly limit: number, private readonly windowMs: number, private readonly now: () => number = Date.now) {}\n\n /** Counts one now; false (and not counted) when the window is full. */\n take(): boolean {\n const now = this.now();\n this.recent = this.recent.filter(at => now - at < this.windowMs);\n if (this.recent.length >= this.limit) return false;\n this.recent.push(now);\n return true;\n }\n\n /** Milliseconds until the window has room again; 0 when it has room now. */\n wait(): number {\n const now = this.now();\n this.recent = this.recent.filter(at => now - at < this.windowMs);\n return this.recent.length < this.limit ? 0 : this.recent[0] + this.windowMs - now;\n }\n\n reset(): void { this.recent = []; }\n}\n","import { ed25519, x25519 } from \"@noble/curves/ed25519.js\";\nimport { hkdf } from \"@noble/hashes/hkdf.js\";\nimport { sha256 } from \"@noble/hashes/sha2.js\";\nimport { fromBase64Url, toBase64Url, utf8Encode } from \"./bytes\";\nimport { encrypt, sealedLength, tryDecrypt } from \"./crypto\";\nimport { identityFromSeed, identityFromSeedB64, publicKeyFromZ32, sign, verify, type Identity } from \"./identity\";\nimport type { LinkParams } from \"./invite\";\nimport type { PairingCredentials } from \"./pairedSession\";\nimport { measureRecords, MAX_DNS_PACKET_BYTES, PKARR_FUTURE_SKEW_MS, type GhostRecord, type SignedPacket } from \"./pkarr\";\nimport { budgetRetryMs, isDiscoveryBudgetError, type PkarrRequestOptions, type PkarrTransport } from \"./transport\";\nimport { traceLink } from \"./linkTrace\";\nimport { REACTION_LIMITS, readDhtReactions, validReactionNumber, type WireReaction } from \"./reactions\";\nimport { readForwarded } from \"./forwards\";\nimport { validEditNumber } from \"./pairedEdits\";\n\nexport type DeliveryMode = \"stream\" | \"dht\";\nexport const DHT_TEXT_BYTES = 256;\nexport const DHT_MESSAGE_TTL = 5 * 60_000;\nconst CONTROL_TTL = 10 * 60_000;\n/**\n * How far apart two devices' clocks may be for an envelope's expiry: a reader takes one until this long after `expires`\n * by its own clock. A device's clock a few minutes off is common; an envelope's lifetime is five or ten minutes, so a\n * sender that far behind had every envelope expired on arrival, with nothing on screen on either side. Its `issued` is\n * refused for nothing: what stops an old envelope from being taken again is its `sequence`, which the reader keeps.\n */\nexport const DHT_EXPIRY_SKEW_MS = PKARR_FUTURE_SKEW_MS;\n/**\n * How far before its own clock a sender dates an envelope (`issued`), never before the envelope's lifetime began nor\n * 30 s before its text's time. Apps up to 1.0.2 refuse an envelope dated more than 30 s past their clock, so every\n * envelope of a clock two minutes ahead was dropped by them: no first contact, no text, no receipt. Dated back, a\n * control envelope from a clock up to two and a half minutes ahead is taken by them at once, and a text once it has\n * been going out for as long as the clock is ahead.\n */\nexport const DHT_ISSUED_BACK_MS = 2 * 60_000;\nconst MAX_ATTEMPTS = 8;\n/** The wait before publication attempt `attempts + 1` of a text, or of a receipt the contact still asks for. */\nconst backoff = (attempts: number) => Math.min(60_000, 4_000 * 2 ** attempts);\n/** The contact's mailbox is read this often while either side is DHT-only, and otherwise… */\nconst DHT_POLL_MS = 4_000;\nconst STREAM_POLL_MS = 30_000;\n/**\n * …except for this long after this side leaves DHT-only while the contact is still there: its own\n * switch then shows in seconds, not at the next 30 s read (both sides are blocked from a live link until\n * each has seen the other leave).\n */\nexport const LEAVING_DHT_FAST_MS = 2 * 60_000;\n/**\n * Reads of a DHT-only contact's mailbox that go as signaling (`expect(ms, true)`) once something shows it leaving,\n * for as long as it still says DHT only. What shows it (a fresh packet on its link key, its offer) can reach this side\n * before the envelope saying so reaches the relays, and the first read then finds DHT only still. With the relays'\n * minute spent, every read after it was held back for 42 s, and the chat stayed \"On DHT · retrying live\" while its\n * contact had left (mx-d707d8d5, 2026-09-30). A few, not the whole window: the allowance they spend is also what this\n * side's offer and its reads for the answer go over the minute on (`SIGNALING_ALLOWANCE_SHARE` in relay.ts).\n */\nexport const LEAVING_SIGNAL_READS = 3;\n/** Only this often while layer 1 carries the chat (WISP 403, Q7): the relays' per-IP budget is shared by every chat. */\nexport const LIVE_POLL_MS = 5 * 60_000;\n/**\n * On the DHT with the chat open, the mailbox is read this often (WISP 403 proposes 4 s). The relays' per-IP budget\n * (30 requests a minute per relay here, reads and publishes together) is shared with presence polling, which is at its\n * fastest right then, while layer 1 is redialled; 4 s starved the publications of held items in the store-and-forward\n * e2e. A text of ours awaiting its receipt still reads at 4 s, and a drop reads once, at once.\n */\nexport const ACTIVE_DHT_POLL_MS = 10_000;\nconst ID = /^[A-Za-z0-9_-]{22}$/;\n/** What a text may reply to over the DHT: a message, file or payment id of this chat (WISP 401 § Replies). */\nconst REPLY_TO = /^[A-Za-z0-9_-]{8,64}$/;\ntype Message = [id: string, timestamp: number, text: string];\n/**\n * The ninth element, the author's capability-record revision (WISP 03), is optional; readers ignore\n * trailing elements they do not know. The signature covers all of them. The tenth is the pinned mailbox\n * (WISP 403, revision 0.3): `1`, the author can use it; `2`, the author knows the reader can too, and reads\n * there first. A ninth element then goes as `null` when there is no revision to name. The eleventh, with a text only,\n * is the id of the message it replies to (WISP 403 § Replies): the id alone, which the reader looks up in its history.\n * The twelfth is kept for an edit (WISP 403 § Edits). The thirteenth, the author's reactions the reader has not confirmed\n * yet, `[[id, emoji, n], …]`, oldest first and as many as fit; the fourteenth, the highest number of the reader's\n * reactions the author took (WISP 403 § Reactions). The eleventh and twelfth then go as `null` when there is no reply\n * and no edit. The fifteenth, with a text only, is how many times it has been forwarded (WISP 403 § Forwards); the\n * four before it then go as `null`, `null`, `[]` and `null` when they have nothing to say.\n */\ntype DhtReaction = [id: string, emoji: string, n: number];\ntype Body = [version: 1, sequence: number, issued: number, expires: number, author: string, mode: DeliveryMode, message: Message | null, receipt: string | null, capsRev?: number | null, pinnedMailbox?: 1 | 2, replyTo?: string | null, edit?: DhtEdit | null, reactions?: DhtReaction[], reactionsTaken?: number | null, forwarded?: number];\n/** An envelope's plaintext past this is not read (`receive`): what rides along must stay under it. */\nconst MAX_ENVELOPE_PLAINTEXT = 900;\n/**\n * Every envelope's records carry this TTL, whatever it holds (WISP 403 § What a mailbox shows): a text's TTL used to\n * count down from its five minutes while a keep-alive's said ten, so a look at the mailbox told a text was pending.\n * Readers never read it; relays cache for at least this long anyway.\n */\nconst ENVELOPE_TTL = 300;\n/**\n * An envelope's packet, text or not, is padded to the largest plaintext that fits this. A few bytes under the DHT's\n * 1000: the Rust client splits TXT strings at 254 bytes, not 255, so the packet it builds can be a byte or two larger.\n */\nconst ENVELOPE_PACKET_BYTES = MAX_DNS_PACKET_BYTES - 8;\n/**\n * The twelfth element, with a text only: the text is an edit (WISP 403 § Edits), the new text of the message with this id,\n * edit number `e`. The eleventh then goes as `null` when the text replies to nothing. An app from before edits would show\n * the text as a message of its own: it is sent only to a contact whose capability record lists `edit/1`.\n */\ntype DhtEdit = [id: string, e: number];\n/**\n * Before the contact said it can use the pinned mailbox, the invite's mailbox is read. When that held a packet but\n * nothing from the contact (expired, or someone else's), the pinned one is looked in too, no more often than this:\n * the contact may have moved there while this side was away, and every read spends a relay request.\n */\nconst PINNED_PROBE_MS = 60_000;\nexport interface DhtDeliveryState {\n sequence: number;\n peerSequence: number;\n peerMode?: DeliveryMode;\n /**\n * How far the contact is in the pinned mailbox (WISP 403, revision 0.3), from its envelopes sealed to this side: it\n * `can` use it; it `reads` there first (it knows this side can); it was `seen` publishing there. This side reads\n * there first from `can` on and publishes there from `reads` on; from `seen` on it reads only there.\n */\n peerPinned?: \"can\" | \"reads\" | \"seen\";\n /** `reply`: the id of the message the text replies to, published with it (the eleventh element). */\n pending?: { message: Message; expires: number; attempts: number; next: number; reply?: string; edit?: DhtEdit; forwarded?: number };\n /**\n * The receipt this side owes for the contact's last text. `next`: when it forces a publication again (absent: at\n * once). `settled`: the contact's newer envelope no longer carries that text (it has a receipt, on either path, or\n * gave up on it), so the receipt only rides along on envelopes that go out anyway.\n */\n receipt?: { id: string; expires: number; attempts: number; next?: number; settled?: boolean };\n confirmed?: string;\n /** The highest number of the contact's reactions taken from its envelopes: said back in every envelope. */\n reactionsTaken?: number;\n /**\n * The contact's envelope, by its hint the pinned (or expected) key's, is sealed to another key: the contact paired with\n * someone else who used the same invite first. Kept until an envelope of the contact's newer than the last read opens.\n */\n inviteTaken?: true;\n}\nexport interface DhtDeliveryView {\n mode: DeliveryMode;\n peerMode?: DeliveryMode;\n authenticated: boolean;\n error?: string;\n pendingUntil?: number;\n maxTextBytes: number;\n /** The envelope last published from here, for a text's details; absent until one carried a text. */\n lastPublished?: DhtPacketFacts;\n /**\n * When an envelope or a connection signal signed by a participation key other than the pinned one last came in\n * over one of the chat's invite-derived keys. Anyone holding a copy of the invite can publish there, so it is\n * ignored, never a reason to stop the chat: a passive warning only.\n */\n foreignKeySeenAt?: number;\n /** The invite was used by someone else first (`DhtDeliveryState.inviteTaken`): this side's texts reach nobody. */\n inviteTaken?: boolean;\n}\n/**\n * What can be said about one DHT envelope without opening it: which text it carried, its sequence and times, the\n * signed packet's size, the sealed record's nonce (the first 24 bytes of the record, never a key) and the address\n * it was published under. All of it travels in the clear on the DHT already.\n */\nexport interface DhtPacketFacts {\n /** The text's stable id, when the envelope carried one. */\n id?: string;\n seq: number;\n issued: number;\n expires: number;\n packetBytes: number;\n nonce: string;\n recordKey: string;\n records: string[];\n}\nexport const emptyDhtDeliveryState = (): DhtDeliveryState => ({ sequence: 0, peerSequence: 0 });\n\n/** An invite is a secret capability, not an independently verified identity.\n * Participation signatures bind each encrypted envelope to both invite roles.\n * The same durable TOFU pin is required here and on all subsequent streams.\n * No forward-secrecy or guaranteed DHT retention claim is made. */\nexport class DhtDelivery {\n private state: DhtDeliveryState;\n private mode: DeliveryMode;\n private running = false;\n private ticking = false;\n private tickAgain = false;\n /** Until when this side, having left DHT-only, reads a contact still there at the fast pace. */\n private leavingUntil = 0;\n /** Until when the mailbox is read at the fast pace for any other reason (a drop, a first contact). */\n private fastUntil = 0;\n private live = false;\n private active = false;\n /** The next read was asked for (a refresh, a fresh packet of the contact): it is not a background one. */\n private urgent = false;\n /** Reads left that are signaling: the contact is leaving DHT only, and the live link waits on them (`expect`). */\n private signalReads = 0;\n private timer: ReturnType<typeof setTimeout> | null = null;\n /** The first control envelope of a start that waits (`firstControlAfterMs`). */\n private controlTimer: ReturnType<typeof setTimeout> | null = null;\n private chain = Promise.resolve();\n private lastPublish = 0;\n /** The sequence of the last envelope of the contact that was past its expiry (traced once). */\n private expiredSeen = 0;\n private controlDue = 0;\n /** Until when the relays' request budget holds publications back: what is due then goes at once. */\n private budgetUntil = 0;\n private errors: Partial<Record<\"publish\" | \"read\", string>> = {};\n private foreignKeySeenAt?: number;\n /** When the pinned mailbox was last looked in while the contact had not said it uses it. */\n private pinnedProbeAt = 0;\n private pinnedBoxes?: { peerKey: string; identity: Identity; peerAddress: string };\n private readonly identity;\n private readonly peerAddress: string;\n private readonly key: Uint8Array;\n private readonly from: string;\n private readonly to: string;\n private readonly participation;\n /** The capability-record revision the last envelope published here named. */\n private namedRev?: number;\n /** A reaction of this side waits to ride on an envelope, or the contact's reactions wait to be said taken. */\n private reactionsDue = false;\n constructor(private readonly options: {\n params: LinkParams; mode: DeliveryMode; state?: DhtDeliveryState;\n credentials: PairingCredentials; transport: PkarrTransport;\n save(state: DhtDeliveryState): Promise<void>;\n pin(key: string): Promise<void>;\n message(message: { id: string; text: string; timestamp: number; reply?: { i: string }; edit?: { i: string; e: number }; forwarded?: number }, packet: DhtPacketFacts): Promise<void>;\n receipt(id: string): Promise<void>;\n changed(view: DhtDeliveryView): void;\n /** This side's capability-record revision, told in every envelope (WISP 03). */\n capsRev?(): number | undefined;\n /** An envelope from the contact named this revision of its capability record. */\n peerCapsRev?(rev: number): void;\n /** Whether the contact's capability record accepts DHT text (`dht-text/1`); absent or unknown: it does. */\n peerAcceptsText?(): boolean;\n /** This side's reactions the contact has not confirmed, oldest first: they ride on the envelopes (WISP 403 § Reactions). */\n reactions?(): readonly WireReaction[];\n /** A reaction of the contact's, from an envelope, already checked. */\n reaction?(reaction: WireReaction): Promise<void>;\n /** The contact took this side's reactions up to number `n`. */\n reactionsTaken?(n: number): Promise<void>;\n pollMs?: number;\n /**\n * A chat already paired, started again: its first control envelope goes this long after the start, not in the\n * burst of an app coming back (bug hunt r7a). A text, a receipt, a new mode or a new capability revision still go at once.\n */\n firstControlAfterMs?: number;\n }) {\n // `peerRejected`, saved by apps before WISP 403 revision 0.3, stopped the chat for good on an envelope anyone\n // holding the invite could forge: it is dropped, not honoured.\n const { peerRejected: _peerRejected, ...state } = structuredClone(options.state ?? emptyDhtDeliveryState()) as DhtDeliveryState & { peerRejected?: boolean };\n this.state = state; this.mode = options.mode;\n this.from = identityFromSeedB64(options.params.seedB64).pubKeyZ32; this.to = options.params.peerPubKeyZ32;\n this.participation = identityFromSeedB64(options.credentials.seedB64);\n const secret = fromBase64Url(options.params.encKeyB64);\n const context = JSON.stringify([\"ghostly-dht-delivery/1\", [this.from, this.to].sort()]);\n const derive = (label: string) => hkdf(sha256, secret, utf8Encode(context), utf8Encode(label), 32);\n this.identity = identityFromSeed(derive(`mailbox:${this.from}`));\n this.peerAddress = identityFromSeed(derive(`mailbox:${this.to}`)).pubKeyZ32;\n this.key = derive(\"envelope\");\n }\n get view(): DhtDeliveryView {\n return { mode: this.mode, peerMode: this.state.peerMode, authenticated: !!this.options.credentials.peerKey,\n error: Object.values(this.errors).join(\". \") || undefined, pendingUntil: this.state.pending?.expires, maxTextBytes: DHT_TEXT_BYTES,\n ...(this.lastPublished && { lastPublished: this.lastPublished }), ...(this.foreignKeySeenAt && { foreignKeySeenAt: this.foreignKeySeenAt }),\n ...(this.state.inviteTaken && { inviteTaken: true }) };\n }\n /**\n * Something signed by a participation key other than the pinned one came in over the chat's invite-derived keys (this\n * mailbox, the link's signals, a connection to an endpoint whose address a copy of the invite could read): ignored,\n * and said in the view as a passive warning.\n */\n foreignKeySeen(path: \"dht\" | \"signal\" | \"stream\"): void {\n const first = !this.foreignKeySeenAt;\n this.foreignKeySeenAt = Date.now();\n traceLink(this.from, \"foreign-key-ignored\", { path });\n if (first) this.changed();\n }\n /**\n * The mailboxes once the contact is pinned (WISP 403, revision 0.3): derived from the secret only the two\n * participation keys share, so a copy of the invite can neither publish there nor overwrite the contact's texts.\n */\n private pinned(): { identity: Identity; peerAddress: string } | undefined {\n const peerKey = this.options.credentials.peerKey;\n if (!peerKey) return undefined;\n if (this.pinnedBoxes?.peerKey !== peerKey) {\n const shared = x25519.getSharedSecret(ed25519.utils.toMontgomerySecret(this.participation.seed), ed25519.utils.toMontgomery(publicKeyFromZ32(peerKey)));\n const derive = (label: string) => hkdf(sha256, shared, this.key, utf8Encode(`ghostly-dht-pinned-mailbox/1:${label}`), 32);\n this.pinnedBoxes = { peerKey, identity: identityFromSeed(derive(this.from)), peerAddress: identityFromSeed(derive(this.to)).pubKeyZ32 };\n }\n return this.pinnedBoxes;\n }\n private lastPublished?: DhtPacketFacts;\n /** The sealed record's nonce is its first 24 bytes: 32 characters of base64. */\n private static facts(body: Body, records: GhostRecord[], key: string): DhtPacketFacts {\n return { ...(body[6] && { id: body[6][0] }), seq: body[1], issued: body[2], expires: body[3], packetBytes: measureRecords(key, records),\n nonce: records[0].value.slice(0, 32), recordKey: key, records: records.map(r => r.label) };\n }\n get comparisonCode(): string | undefined {\n if (!this.options.credentials.peerKey) return undefined;\n const digest = sha256(utf8Encode(JSON.stringify([\"ghostly-dht-comparison/1\", [this.from, this.to].sort(), [this.participation.pubKeyZ32, this.options.credentials.peerKey].sort()])));\n return Array.from(digest, b => b.toString(16).padStart(2, \"0\")).join(\"\").slice(0, 24).match(/.{4}/g)!.join(\" \");\n }\n get peerMode(): DeliveryMode | undefined { return this.state.peerMode; }\n private changed(): void { this.options.changed(this.view); }\n private serialize<T>(run: () => Promise<T>): Promise<T> {\n const operation = this.chain.then(run); this.chain = operation.then(() => {}, () => {}); return operation;\n }\n private async persist(next: DhtDeliveryState): Promise<void> { await this.options.save(structuredClone(next)); this.state = next; }\n private sealedKey(peer: string): Uint8Array {\n const shared = x25519.getSharedSecret(ed25519.utils.toMontgomerySecret(this.participation.seed), ed25519.utils.toMontgomery(publicKeyFromZ32(peer)));\n return hkdf(sha256, shared, this.key, utf8Encode(\"ghostly-dht-participation-envelope/1\"), 32);\n }\n private signed(body: Body, recipient = \"invite\"): Uint8Array { return utf8Encode(JSON.stringify([\"ghostly-dht-envelope\", this.from, this.to, recipient, body])); }\n private records(body: Body, recipient = this.options.credentials.peerKey): GhostRecord[] {\n const signature = toBase64Url(sign(this.signed(body, recipient), this.participation.seed));\n const plaintext = JSON.stringify([body, signature]);\n const hint = recipient ? [{ label: \"_dmk\", value: encrypt(this.participation.pubKeyZ32, this.key), ttl: ENVELOPE_TTL }] : [];\n // The packet a plaintext of this many bytes makes: its size depends on the length alone.\n const fits = (bytes: number) => bytes <= MAX_ENVELOPE_PLAINTEXT &&\n measureRecords(this.identity.pubKeyZ32, [{ label: \"_dm\", value: \"A\".repeat(sealedLength(bytes)), ttl: ENVELOPE_TTL }, ...hint]) <= ENVELOPE_PACKET_BYTES;\n const bytes = utf8Encode(plaintext).length;\n if (!fits(bytes)) throw new Error(\"Text and authentication exceed the DHT packet budget. Shorten the message.\");\n // Padded with spaces up to the largest that fits, so a keep-alive is as long as a text. JSON allows spaces after the\n // value: readers from before read the envelope as ever.\n let padded = bytes;\n for (let step = 512; step >= 1; step >>= 1) if (fits(padded + step)) padded += step;\n return [{ label: \"_dm\", value: encrypt(plaintext + \" \".repeat(padded - bytes), recipient ? this.sealedKey(recipient) : this.key), ttl: ENVELOPE_TTL }, ...hint];\n }\n async start(): Promise<void> {\n if (this.running) return; this.running = true;\n const quiet = this.options.credentials.peerKey ? this.options.firstControlAfterMs ?? 0 : 0;\n if (quiet > 0) {\n this.controlDue = Math.max(this.controlDue, Date.now() + quiet);\n this.controlTimer = setTimeout(() => { this.controlTimer = null; void this.serialize(async () => { try { await this.publish(); } catch { /* the next tick */ } }); }, quiet);\n }\n if (this.state.confirmed) await this.options.receipt(this.state.confirmed);\n // A chat with no pinned contact is read at the signaling pace only once the contact shows up (`expect`, from its\n // fresh presence packet): an invite nobody opened yet, or one warmed ahead of time, spends no relay budget.\n this.changed(); void this.tick();\n }\n async stop(): Promise<void> {\n this.running = false;\n if (this.timer) clearTimeout(this.timer);\n if (this.controlTimer) clearTimeout(this.controlTimer);\n this.timer = this.controlTimer = null;\n await this.chain;\n }\n async setMode(mode: DeliveryMode): Promise<void> {\n await this.serialize(async () => {\n // Preserve accepted DHT intent, its stable ID and original expiry across mode changes.\n await this.persist({ ...this.state });\n // The contact learns the new method from the next envelope: it goes out now, not after the publish spacing.\n this.mode = mode; this.controlDue = 0; this.lastPublish = 0;\n this.leavingUntil = mode === \"stream\" ? Date.now() + LEAVING_DHT_FAST_MS : 0;\n // DHT only here: no live link waits on the contact leaving it.\n if (mode === \"dht\") this.signalReads = 0;\n this.changed();\n });\n void this.tick();\n }\n /** Reads the contact's mailbox now: something says it may have changed its delivery method. */\n refresh(): void { this.urgent = true; void this.tick(); }\n /**\n * Reads the contact's mailbox at the signaling pace for a while (a fresh packet of a contact not pinned yet).\n * `signal`: the contact is leaving DHT only (a fresh packet on its link key, or its offer), and the live link waits\n * on a read to say so: the next read goes now, and it and the next ones while the contact still says DHT only\n * (`LEAVING_SIGNAL_READS` in all) are signaling (`PkarrRequestOptions.signal`).\n */\n expect(ms = 2 * 60_000, signal = false): void {\n const until = Date.now() + ms;\n if (signal) this.signalReads = LEAVING_SIGNAL_READS;\n if (until <= this.fastUntil && !signal) return;\n this.fastUntil = Math.max(this.fastUntil, until); this.urgent = true; void this.tick();\n }\n /**\n * Layer 1 carries the chat, or no longer does. While it does, the mailbox is read every 5 minutes, after one\n * last look as it goes live: a text the contact put on the DHT in the seconds before (after this side's last\n * read) would otherwise wait for the next look. That look is a background request, which yields to links that\n * signal, and only for a contact that wrote in this mailbox already. The moment layer 1 is lost, at once\n * (WISP 403, poll pace), and then at the chat's pace.\n */\n setLive(live: boolean): void {\n if (live === this.live) return;\n this.live = live;\n if (!live) { this.urgent = true; void this.tick(); return; }\n this.fastUntil = 0; this.signalReads = 0;\n if (this.options.credentials.peerKey && this.state.peerMode) void this.tick(); else this.schedule();\n }\n get isLive(): boolean { return this.live; }\n /** The chat is open with the app in front: on the DHT, its mailbox is read at the signaling pace. */\n setActive(active: boolean): void {\n if (active === this.active) return;\n this.active = active;\n if (active && !this.live) void this.tick(); else this.schedule();\n }\n validate(text: string, timestamp: number, id: string, reply?: string, edit?: DhtEdit): string | null {\n // Every chat's first contact runs here too (WISP 403): before the pin the text is sealed with the invite key.\n if (this.options.peerAcceptsText?.() === false) return DHT_TEXT_REFUSED;\n if (!ID.test(id) || !Number.isSafeInteger(timestamp) || timestamp <= 0 || (reply !== undefined && !REPLY_TO.test(reply))) return \"Invalid message.\";\n if (edit !== undefined && (reply !== undefined || !ID.test(edit[0]) || !validEditNumber(edit[1]))) return \"Invalid message.\";\n if (utf8Encode(text).length > DHT_TEXT_BYTES) return `DHT text is limited to ${DHT_TEXT_BYTES} UTF-8 bytes. Shorten it or choose a live connection.`;\n if (/cashu[AB][A-Za-z0-9_-]+/i.test(text)) return \"Payment tokens cannot be sent through DHT delivery.\";\n const now = Date.now(), pending = this.state.pending;\n if (pending && pending.expires > now && pending.message[0] !== id) return \"One DHT text can await a receipt at a time. Wait for its receipt or expiry before sending another.\";\n if (pending?.message[0] === id && pending.expires > now && pending.attempts >= MAX_ATTEMPTS) return \"DHT retry budget exhausted. Wait for expiry before retrying this message.\";\n const next = pending?.message[0] === id && pending.expires > now ? pending : { message: [id, timestamp, text] as Message, expires: now + DHT_MESSAGE_TTL, attempts: 0, next: now, ...(reply && { reply }), ...(edit && { edit }) };\n // Validate the complete encrypted DNS packet before accepting local intent. A text near the bound goes without the\n // id of the message it replies to when both do not fit: the text is what matters. An edit never goes without its\n // element: it would read as a new message.\n const error = this.packetError(next.message, next.expires, next.reply, next.edit);\n return error && next.reply ? this.packetError(next.message, next.expires) : error;\n }\n private packetError(message: Message, expires: number, reply?: string, edit?: DhtEdit): string | null {\n try { this.records(this.body(this.state.sequence + 1, Date.now(), expires, message, this.state.receipt?.id ?? \"abcdefghijklmnopqrstuv\", reply, [], edit), this.options.credentials.peerKey ?? this.participation.pubKeyZ32); return null; }\n catch (error) { return error instanceof Error ? error.message : String(error); }\n }\n /** Whether a text with its hop count fits the packet and what a reader reads (`MAX_ENVELOPE_PLAINTEXT`). */\n private roomForHops(message: Message, expires: number, reply: string | undefined, forwarded: number): boolean {\n const body = this.body(this.state.sequence + 1, Date.now(), expires, message, this.state.receipt?.id ?? \"abcdefghijklmnopqrstuv\", reply, [], undefined, forwarded);\n try { this.records(body, this.options.credentials.peerKey ?? this.participation.pubKeyZ32); } catch { return false; }\n return utf8Encode(JSON.stringify([body, \"x\".repeat(86)])).length <= MAX_ENVELOPE_PLAINTEXT;\n }\n /**\n * `forwarded`: a forwarded text's hop count (WISP 403 § Forwards). Like a reply's id it goes only when the packet has\n * room for it: the text is what matters, and it then reads as written here.\n */\n async send(text: string, timestamp: number, id: string, reply?: string, edit?: DhtEdit, forwarded?: number): Promise<string | null> {\n return this.serialize(async () => {\n const error = this.validate(text, timestamp, id, reply, edit);\n if (error) return error;\n const now = Date.now(), pending = this.state.pending;\n const hops = edit ? undefined : readForwarded(forwarded);\n let next = pending?.message[0] === id && pending.expires > now ? pending : { message: [id, timestamp, text] as Message, expires: now + DHT_MESSAGE_TTL, attempts: 0, next: now, ...(reply && { reply }), ...(edit && { edit }), ...(hops && { forwarded: hops }) };\n if (next.forwarded && !this.roomForHops(next.message, next.expires, next.reply, next.forwarded)) { const { forwarded: _dropped, ...alone } = next; next = alone; }\n if (next.reply && this.packetError(next.message, next.expires, next.reply)) { const { reply: _dropped, ...alone } = next; next = alone; }\n await this.persist({ ...this.state, pending: next }); this.changed();\n // The next read comes at the pace for a text awaiting its receipt.\n try { await this.publish(true); this.schedule(); return null; }\n catch (error) {\n // Held back by the relays' request budget: the text is queued and goes as soon as the budget frees a request.\n if (isDiscoveryBudgetError(error)) { this.schedule(); return null; }\n this.errors.publish = `DHT publication failed: ${String(error instanceof Error ? error.message : error)}. Bounded retry continues until expiry.`; this.changed(); return this.errors.publish;\n }\n });\n }\n /**\n * This side's capability record has a new revision (WISP 03): an envelope names it now. Otherwise the contact\n * learns of it with the next envelope that goes out anyway, a control one minutes away when nothing is sent,\n * and dials no native transport the record newly offers until then.\n */\n async announce(): Promise<void> {\n await this.serialize(async () => {\n // Every publication spends a relay request: none when the last envelope named this revision already.\n const rev = this.options.capsRev?.();\n if (rev === undefined || rev === this.namedRev) return;\n try { await this.publish(true); } catch { /* The next envelope names it. */ }\n });\n }\n /**\n * This side has reactions the contact has not confirmed (`reactions`): the next envelope carries them, and goes\n * as soon as the publication spacing allows.\n */\n async announceReactions(): Promise<void> {\n if (!this.options.reactions?.().length) return;\n this.reactionsDue = true;\n await this.serialize(async () => { try { await this.publish(); } catch { /* the next envelope carries them */ } });\n if (this.reactionsDue) this.schedule();\n }\n /** A stream receipt for the same stable ID also cancels DHT retransmission. */\n async acknowledge(id: string): Promise<void> {\n await this.serialize(async () => {\n if (this.state.pending?.message[0] === id) await this.persist({ ...this.state, pending: undefined, confirmed: id });\n this.changed();\n });\n }\n private body(sequence: number, issued: number, expires: number, message: Message | null, receipt: string | null, reply?: string, reactions: readonly WireReaction[] = [], edit?: DhtEdit, forwarded?: number): Body {\n const rev = this.options.capsRev?.();\n const body: Body = [1, sequence, issued, expires, this.participation.pubKeyZ32, this.mode, message, receipt];\n // Whether it has a revision to name or not, the ninth element holds the place of the tenth: this side uses the pinned mailbox.\n body.push(rev !== undefined && Number.isSafeInteger(rev) && rev >= 0 ? rev : null, this.state.peerPinned ? 2 : 1);\n const taken = this.state.reactionsTaken;\n const hops = message && !edit ? readForwarded(forwarded) : undefined;\n if (reactions.length || taken || hops) {\n body.push(message && reply ? reply : null, message && edit ? edit : null, reactions.map(r => [r.id, r.e, r.n] as DhtReaction), taken ?? null);\n if (hops) body.push(hops);\n }\n else {\n if (message && (reply || edit)) body.push(reply ?? null);\n if (message && edit) body.push(edit);\n }\n return body;\n }\n /**\n * The envelope with as many of this side's pending reactions as fit, oldest first: the packet's budget and what a\n * reader reads (`MAX_ENVELOPE_PLAINTEXT`) both bound it. The contact confirms up to the newest it carried.\n */\n private fitted(sequence: number, issued: number, expires: number, message: Message | null, receipt: string | null, reply?: string, edit?: DhtEdit, forwarded?: number): { body: Body; records: GhostRecord[]; reactions: number } {\n const pending = (this.options.reactions?.() ?? []).slice(0, REACTION_LIMITS.dht);\n for (let count = pending.length; count > 0; count--) {\n const body = this.body(sequence, issued, expires, message, receipt, reply, pending.slice(0, count), edit, forwarded);\n try {\n const records = this.records(body);\n if (utf8Encode(JSON.stringify([body, \"x\".repeat(86)])).length <= MAX_ENVELOPE_PLAINTEXT) return { body, records, reactions: count };\n } catch { /* one fewer */ }\n }\n const body = this.body(sequence, issued, expires, message, receipt, reply, [], edit, forwarded);\n return { body, records: this.records(body), reactions: 0 };\n }\n private async publish(force = false): Promise<void> {\n const now = Date.now();\n if (!this.running || (!force && (now - this.lastPublish < 4_000 || now < this.budgetUntil))) return;\n const pending = this.state.pending && this.state.pending.expires > now && this.state.pending.attempts < MAX_ATTEMPTS ? this.state.pending : undefined;\n const receipt = this.state.receipt && this.state.receipt.expires > now && this.state.receipt.attempts < MAX_ATTEMPTS ? this.state.receipt : undefined;\n // A receipt forces an envelope of its own only while the contact still asks for it, and with a text's backoff: the\n // envelope stays in the mailbox until the next one, and every publication spends a request on each relay.\n const receiptDue = !!receipt && !receipt.settled && (receipt.next ?? 0) <= now;\n if (!force && (!pending || pending.next > now) && !receiptDue && !this.reactionsDue && this.controlDue > now) return;\n // Dated back (`DHT_ISSUED_BACK_MS`), and a control envelope's lifetime with it: a reader bounds `expires - issued`.\n const expires = pending?.expires ?? now - DHT_ISSUED_BACK_MS + CONTROL_TTL;\n const issued = Math.min(now, Math.max(now - DHT_ISSUED_BACK_MS, expires - (pending ? DHT_MESSAGE_TTL : CONTROL_TTL), (pending?.message[1] ?? 0) - 30_000));\n const { body, records, reactions } = this.fitted(this.state.sequence + 1, issued, expires, pending?.message ?? null, receipt?.id ?? null, pending?.reply, pending?.edit, pending?.forwarded);\n const reactionsWereDue = this.reactionsDue;\n // A text that left no room: the reactions go on the next envelope. Once some went, the rest wait for the contact\n // to say those were taken (the engine announces the rest then).\n this.reactionsDue = !reactions && !!this.options.reactions?.().length;\n // In the pinned mailbox once the contact said it reads there; until then where an older app looks.\n const identity = (this.state.peerPinned && this.state.peerPinned !== \"can\" && this.pinned()?.identity) || this.identity;\n // Persist sequence and attempt count first. A crash cannot reuse them or\n // reset the retransmission budget/absolute message deadline.\n await this.persist({ ...this.state, sequence: body[1], pending: pending ? { ...pending, attempts: pending.attempts + 1, next: now + backoff(pending.attempts) } : this.state.pending,\n receipt: receipt ? { ...receipt, attempts: receipt.attempts + 1, next: now + backoff(receipt.attempts) } : this.state.receipt });\n const before = { lastPublish: this.lastPublish, controlDue: this.controlDue };\n this.lastPublish = now; this.controlDue = now + 4 * 60_000;\n traceLink(this.from, \"dht-publish\", { mode: this.mode, seq: body[1], ...(identity !== this.identity && { pinned: true }) });\n if (pending) this.lastPublished = DhtDelivery.facts(body, records, identity.pubKeyZ32);\n try { await this.options.transport.publish(identity, records); }\n catch (error) {\n this.reactionsDue ||= reactionsWereDue;\n if (isDiscoveryBudgetError(error)) await this.heldBack(error, now, before, pending, receipt);\n throw error;\n }\n this.namedRev = body[8] ?? undefined;\n delete this.errors.publish; this.changed();\n }\n /**\n * The relays' request budget held the envelope back: nothing went out, so it was no attempt (a text keeps its eight),\n * and what it carried (a text, a receipt, a new mode) goes the moment the budget frees a request: the text and the\n * receipt as they were before, and no publication before then (`budgetUntil`) but a forced one.\n */\n private async heldBack(error: Parameters<typeof budgetRetryMs>[0], now: number, before: { lastPublish: number; controlDue: number },\n pending: DhtDeliveryState[\"pending\"], receipt: DhtDeliveryState[\"receipt\"]): Promise<void> {\n const at = now + budgetRetryMs(error, 1_000, 60_000);\n this.budgetUntil = at;\n this.lastPublish = before.lastPublish; this.controlDue = Math.min(before.controlDue, at);\n traceLink(this.from, \"dht-publish-waits\", { retryInMs: at - now });\n await this.persist({ ...this.state, pending: pending ?? this.state.pending, receipt: receipt ?? this.state.receipt });\n }\n /**\n * One packet from one of the contact's mailboxes, opened and checked the way `receive` takes it: the right address,\n * one sealed record, a well-formed body within its times, signed by the key it names. Null: not an envelope of the\n * contact's (the pin rules are the caller's).\n */\n private open(packet: SignedPacket, box: \"invite\" | \"pinned\") {\n const address = box === \"pinned\" ? this.pinned()?.peerAddress : this.peerAddress;\n if (!address || packet.pubKeyZ32 !== address || measureRecords(packet.pubKeyZ32, packet.records) > MAX_DNS_PACKET_BYTES) return null;\n const records = packet.records.filter(r => r.label === \"_dm\"); if (records.length !== 1) return null;\n const hints = packet.records.filter(r => r.label === \"_dmk\"); if (hints.length > 1) return null;\n const sender = hints.length ? tryDecrypt(hints[0].value, this.key) : null;\n if (hints.length && !sender) return null;\n let plaintext: string | null;\n try { plaintext = tryDecrypt(records[0].value, sender ? this.sealedKey(sender) : this.key); } catch { plaintext = null; }\n // Sealed, by its hint, from `sender` to a key other than this side's: nothing to read, but who it names says something.\n if (!plaintext && sender) return { sealedToAnother: sender };\n if (!plaintext || utf8Encode(plaintext).length > MAX_ENVELOPE_PLAINTEXT) return null;\n let envelope: unknown; try { envelope = JSON.parse(plaintext); } catch { return null; }\n if (!Array.isArray(envelope) || envelope.length !== 2 || !Array.isArray(envelope[0])) return null;\n const [body, signature] = envelope as [Body, string];\n const [version, sequence, issued, expires, author, mode, message, receipt, capsRev, pinnedMailbox, replyTo, editOf, reactions, reactionsTaken, forwarded] = body;\n const now = Date.now();\n if (body.length < 8 || body.length > 16 || version !== 1 || !Number.isSafeInteger(sequence) || sequence < 1 || !Number.isSafeInteger(issued) ||\n !Number.isSafeInteger(expires) || expires - issued > CONTROL_TTL || issued >= expires ||\n (mode !== \"stream\" && mode !== \"dht\") || typeof author !== \"string\" || typeof signature !== \"string\" || !/^[A-Za-z0-9_-]{86}$/.test(signature)) return null;\n // Past its expiry by more than two clocks differ (`DHT_EXPIRY_SKEW_MS`).\n const expired = expires + DHT_EXPIRY_SKEW_MS <= now;\n if (sender && sender !== author) return null;\n try {\n if (!verify(fromBase64Url(signature), utf8Encode(JSON.stringify([\"ghostly-dht-envelope\", this.to, this.from, sender ? this.participation.pubKeyZ32 : \"invite\", body])), publicKeyFromZ32(author))) return null;\n } catch { return null; }\n if (expired) {\n // Said once per envelope, and only of one its author signed: it stays in the mailbox.\n if (this.expiredSeen !== sequence) { this.expiredSeen = sequence; traceLink(this.from, \"dht-envelope-expired\", { seq: sequence, lateMs: now - expires }); }\n return null;\n }\n return { body, sender, author, sequence, issued, expires, mode, message, receipt, capsRev, pinnedMailbox, replyTo, editOf, reactions, reactionsTaken, forwarded };\n }\n /**\n * The contact's envelope is sealed to another key (`open`): it pinned someone else who used the same invite first, so\n * nothing of this side's will ever be read. Said once this side is not proven the one it pinned (never seen in the\n * pinned mailbox, no text confirmed, never live). The hint is sealed under the invite's key, which any copy of it\n * holds: a passive warning, taken back by the contact's next envelope that opens here.\n */\n private async sealedToAnother(sender: string): Promise<void> {\n const expected = this.options.credentials.peerKey ?? this.options.credentials.expectedPeerKey;\n if (this.state.inviteTaken || !expected || sender !== expected) return;\n if (this.state.peerPinned === \"seen\" || this.state.confirmed || this.live) return;\n traceLink(this.from, \"invite-taken\", {});\n await this.persist({ ...this.state, inviteTaken: true });\n this.changed();\n }\n /**\n * One packet from one of the contact's two mailboxes: `invite` (derived from the invite, which anyone holding a copy\n * can write to) or `pinned`. `none`: nothing from the contact in it; `old`: the contact's, already read; `new`: taken.\n */\n private async receive(packet: SignedPacket, box: \"invite\" | \"pinned\"): Promise<\"none\" | \"old\" | \"new\"> {\n const opened = this.open(packet, box);\n if (!opened) return \"none\";\n if (\"sealedToAnother\" in opened) { if (opened.sealedToAnother) await this.sealedToAnother(opened.sealedToAnother); return \"none\"; }\n const { body, sender, author, sequence, issued, expires, mode, message, receipt, capsRev, pinnedMailbox, replyTo, editOf, reactions, reactionsTaken, forwarded } = opened;\n // Before the pin, the key the invite named: another invite holder's envelope is refused, and not remembered,\n // since whoever holds a copy can write to this mailbox and the inviter's next envelope replaces it.\n if (!this.options.credentials.peerKey && this.options.credentials.expectedPeerKey && this.options.credentials.expectedPeerKey !== author) return \"none\";\n // After it, the same: this mailbox's key comes from the invite, so another key signing here proves only that someone\n // holds a copy of it. Ignored, never a reason to stop the chat; a key change is proven only on a stream (WISP 400).\n if (this.options.credentials.peerKey && this.options.credentials.peerKey !== author) { this.foreignKeySeen(\"dht\"); return \"none\"; }\n // How far the contact is in the pinned mailbox: only an envelope sealed to this side says it (the contact pinned this\n // side, so it can derive the mailbox), and it only goes forward.\n const peerPinned = box === \"pinned\" ? \"seen\" : !sender ? this.state.peerPinned\n : pinnedMailbox === 2 && this.state.peerPinned !== \"seen\" ? \"reads\" : this.state.peerPinned ?? (pinnedMailbox === 1 ? \"can\" : undefined);\n if (sequence <= this.state.peerSequence) {\n if (peerPinned !== this.state.peerPinned && this.options.credentials.peerKey) {\n await this.persist({ ...this.state, peerPinned }); traceLink(this.from, \"dht-peer-pinned\", { peerPinned }); this.changed();\n }\n return \"old\";\n }\n if (message !== null && (!Array.isArray(message) || message.length !== 3 || typeof message[0] !== \"string\" || !ID.test(message[0]) || !Number.isSafeInteger(message[1]) || message[1] <= 0 || message[1] > issued + 30_000 ||\n typeof message[2] !== \"string\" || utf8Encode(message[2]).length > DHT_TEXT_BYTES || expires - issued > DHT_MESSAGE_TTL)) return \"none\";\n if (receipt !== null && (typeof receipt !== \"string\" || !ID.test(receipt))) return \"none\";\n await this.options.pin(author); this.options.credentials.peerKey = author; this.options.credentials.requireSignedSignals = true;\n // Store content before advancing anti-replay state. Retrying after a crash\n // is safe because the durable message table deduplicates the stable ID.\n let nextReceipt = this.state.receipt;\n if (message) {\n // The text this side owes a receipt for was stored already (the receipt is saved only after it): a retransmission\n // that crossed the receipt, or the same envelope moved to the pinned mailbox, is not handed over twice.\n // A reply's id that is not one is dropped, never the text (readers from before ignore the element).\n const reply = typeof replyTo === \"string\" && REPLY_TO.test(replyTo) ? { reply: { i: replyTo } } : {};\n // An edit's element that is not one leaves a text of its own, as an app from before edits reads it.\n const edit = Array.isArray(editOf) && editOf.length === 2 && typeof editOf[0] === \"string\" && ID.test(editOf[0]) && validEditNumber(editOf[1]) ? { edit: { i: editOf[0], e: editOf[1] } } : {};\n // A hop count that is not one, or one on an edit, is dropped: the text reads as written here.\n // Owed for as long as the text had left when its envelope was made, counted on this clock: `expires` is the sender's.\n const hops = edit.edit ? undefined : readForwarded(forwarded);\n if (nextReceipt?.id !== message[0]) await this.options.message({ id: message[0], timestamp: message[1], text: message[2], ...reply, ...edit, ...(hops && { forwarded: hops }) }, DhtDelivery.facts(body, packet.records, packet.pubKeyZ32));\n if (nextReceipt?.id !== message[0]) nextReceipt = { id: message[0], expires: Date.now() + Math.min(DHT_MESSAGE_TTL, expires - issued), attempts: 0 };\n // Asked for again (the text sent anew after a lost session): its receipt goes at once again.\n else if (nextReceipt.settled) { const { settled: _settled, next: _next, ...asked } = nextReceipt; nextReceipt = asked; }\n } else if (nextReceipt && !nextReceipt.settled) nextReceipt = { ...nextReceipt, settled: true };\n // The contact's reactions, each checked on its own (what does not hold is skipped, never the envelope), and the\n // highest number taken, which every envelope of this side says back; one carrying only what was taken asks again.\n const theirs = this.options.reaction ? readDhtReactions(reactions) : [];\n for (const reaction of theirs) await this.options.reaction?.(reaction);\n const newest = theirs.reduce((max, r) => Math.max(max, r.n), 0);\n if (theirs.length) this.reactionsDue = true;\n const confirmed = receipt && this.state.pending?.message[0] === receipt ? receipt : this.state.confirmed;\n if (mode !== this.state.peerMode) traceLink(this.from, \"dht-peer-mode\", { peerMode: mode });\n if (peerPinned !== this.state.peerPinned) traceLink(this.from, \"dht-peer-pinned\", { peerPinned });\n // A newer envelope of the contact's that opens here: whatever said the invite was taken was not the contact.\n if (this.state.inviteTaken) traceLink(this.from, \"invite-untaken\", {});\n const { inviteTaken: _taken, ...kept } = this.state;\n await this.persist({ ...kept, peerSequence: sequence, peerMode: mode, receipt: nextReceipt, confirmed, ...(peerPinned && { peerPinned }),\n pending: confirmed && this.state.pending?.message[0] === confirmed ? undefined : this.state.pending,\n ...(newest > (this.state.reactionsTaken ?? 0) && { reactionsTaken: newest }) });\n if (confirmed) await this.options.receipt(confirmed);\n if (validReactionNumber(reactionsTaken)) await this.options.reactionsTaken?.(reactionsTaken);\n if (Number.isSafeInteger(capsRev) && (capsRev as number) >= 0) this.options.peerCapsRev?.(capsRev as number);\n this.changed();\n return \"new\";\n }\n /**\n * Reads the contact's mailbox: the pinned one first once the contact said it can use it, the invite's otherwise. The\n * other is read too when the first held nothing from the contact (the contact publishes in the other one still, or a\n * copy of the invite overwrote this one), or when the contact just said it can use the pinned one. Once the contact's\n * envelope was seen in the pinned mailbox, the invite's is not read any more.\n */\n private async read(background: boolean, signal = false): Promise<void> {\n const pinned = this.pinned(), options = signal ? { signal } : background ? { background } : undefined;\n const before = this.state.peerPinned;\n const boxes: (\"invite\" | \"pinned\")[] = !pinned ? [\"invite\"] : before === \"seen\" ? [\"pinned\"] : before ? [\"pinned\", \"invite\"] : [\"invite\", \"pinned\"];\n let found: SignedPacket | null = null;\n for (const [i, box] of boxes.entries()) {\n if (i > 0 && box === \"pinned\") {\n // The contact just said it can use it: looked in at once. Otherwise a probe, when the invite's mailbox held a\n // packet but nothing from the contact, now and then.\n if (!this.state.peerPinned && (!found || Date.now() - this.pinnedProbeAt < PINNED_PROBE_MS)) return;\n if (!this.state.peerPinned) this.pinnedProbeAt = Date.now();\n }\n const packet = found = await this.options.transport.resolve(box === \"pinned\" ? pinned!.peerAddress : this.peerAddress, options);\n if (!this.running) return;\n const got = packet ? await this.receive(packet, box) : \"none\";\n if (got !== \"none\" && !(box === \"invite\" && !before && this.state.peerPinned)) return;\n }\n }\n /**\n * A look at this chat from another profile of the device while this one is not running (WISP 04 § Checking other\n * profiles): the mailbox the chat reads first, opened and checked as `receive` does, with nothing saved, pinned or\n * published. It tells only whether the contact's envelope carries a text this side has not taken yet, and that text's\n * id; the words are dropped unread. `reads`: requests spent. Null `text`: nothing new, or nothing of the contact's.\n */\n async peek(options?: PkarrRequestOptions): Promise<{ reads: number; text: string | null }> {\n const expected = this.options.credentials.peerKey ?? this.options.credentials.expectedPeerKey;\n if (!expected) return { reads: 0, text: null };\n const pinned = this.pinned(), before = this.state.peerPinned;\n const boxes: (\"invite\" | \"pinned\")[] = !pinned || !before ? [\"invite\"] : before === \"seen\" ? [\"pinned\"] : [\"pinned\", \"invite\"];\n let reads = 0;\n for (const box of boxes) {\n const packet = await this.options.transport.resolve(box === \"pinned\" ? pinned!.peerAddress : this.peerAddress, options);\n reads++;\n const opened = packet && this.open(packet, box);\n if (!opened || \"sealedToAnother\" in opened || opened.author !== expected) continue;\n const { sequence, message } = opened;\n const fresh = Array.isArray(message) && typeof message[0] === \"string\" && ID.test(message[0]) && sequence > this.state.peerSequence && this.state.receipt?.id !== message[0];\n return { reads, text: fresh ? message[0] : null };\n }\n return { reads, text: null };\n }\n private async tick(): Promise<void> {\n if (!this.running) return;\n // A read asked for during one in flight happens right after it: its answer may predate the reason.\n if (this.ticking) { this.tickAgain = true; return; }\n this.ticking = true; this.tickAgain = false;\n if (this.timer) clearTimeout(this.timer); this.timer = null;\n try { await this.serialize(async () => {\n if (!this.running) return;\n // Only the slow, periodic looks (every 5 minutes while live, every 30 s for a chat in the background) are\n // background requests, served from the relays' background share; a chat on screen, a first contact, DHT\n // only, a drop or a text awaiting its receipt reads as signaling. The share also carries held items'\n // pointers, which a busy mailbox must not starve.\n const background = !this.urgent && this.pollMs >= STREAM_POLL_MS;\n const signal = this.signalReads > 0;\n this.urgent = false; if (signal) this.signalReads--;\n // A read or a publication the relays' request budget held back is a wait, not an error: it goes when the budget frees.\n try { await this.read(background, signal); if (!this.running) return; delete this.errors.read; }\n catch (error) { if (!isDiscoveryBudgetError(error)) this.errors.read = `Could not read DHT delivery: ${error instanceof Error ? error.message : String(error)}`; }\n // The contact says it left DHT only: nothing waits on its mailbox any more.\n if (this.state.peerMode !== \"dht\") this.signalReads = 0;\n try { await this.publish(); }\n catch (error) { if (!isDiscoveryBudgetError(error)) this.errors.publish = `Could not publish DHT delivery: ${error instanceof Error ? error.message : String(error)}`; }\n this.changed();\n });\n } finally { this.ticking = false; }\n if (!this.running) return;\n if (this.tickAgain) { void this.tick(); return; }\n this.schedule();\n }\n /** How long until the next read of the contact's mailbox (WISP 403, poll pace). */\n get pollMs(): number {\n if (this.options.pollMs) return this.options.pollMs;\n const now = Date.now();\n if (this.mode === \"dht\" || (this.state.peerMode === \"dht\" && now < this.leavingUntil) || now < this.fastUntil) return DHT_POLL_MS;\n if (this.live) return LIVE_POLL_MS;\n // Our text awaits its receipt: the receipt is what the person is looking at.\n if (this.state.pending && this.state.pending.expires > now) return DHT_POLL_MS;\n return this.active ? ACTIVE_DHT_POLL_MS : STREAM_POLL_MS;\n }\n private schedule(): void {\n if (!this.running || this.ticking) return;\n if (this.timer) clearTimeout(this.timer);\n // A publication the budget held back goes when the budget frees a request, if that comes before the next read.\n const held = this.budgetUntil - Date.now();\n // Reactions to carry, or to say taken, go once the publication spacing allows, not at the next read.\n const due = this.reactionsDue ? Math.max(0, this.lastPublish + 4_000 - Date.now()) : Infinity;\n this.timer = setTimeout(() => void this.tick(), Math.min(held > 0 ? Math.min(this.pollMs, held) : this.pollMs, due));\n }\n}\nexport const DHT_TEXT_REFUSED = \"Your contact's app does not accept text over the DHT. It is sent when you are live.\";\n\n/**\n * `DhtDelivery.peek` for a chat of a profile that is not running: built from what that profile stored, with every\n * callback a no-op that refuses to save, so the look can change nothing.\n */\nexport function peekDhtMailbox(options: { params: LinkParams; credentials: PairingCredentials; state?: DhtDeliveryState; transport: PkarrTransport; background?: boolean }): Promise<{ reads: number; text: string | null }> {\n const refuse = async () => { throw new Error(\"A peek saves nothing\"); };\n const delivery = new DhtDelivery({ params: options.params, mode: \"stream\", state: options.state, credentials: { ...options.credentials }, transport: options.transport,\n save: refuse, pin: refuse, message: refuse, receipt: refuse, changed: () => {} });\n return delivery.peek(options.background ? { background: true } : undefined);\n}\n","import { fromBase64Url } from \"./bytes\";\n\n/**\n * Videos. A video is an ordinary file (files/2, files/3 or a held item) whose announcement may also carry a\n * `video` description: how long it is, its size in pixels and a small JPEG of its first frame, so the\n * receiver shows a picture and a length before a byte of it has arrived. A peer that does not know `video`\n * ignores it and shows a file; a bad description only means no poster.\n */\nexport interface VideoMeta {\n /** Milliseconds. */\n duration: number;\n /** Pixels, as it plays (rotation applied). */\n width: number;\n height: number;\n /** A JPEG of an early frame, base64url, at most `VIDEO_LIMITS.maxPosterBytes`. */\n poster?: string;\n}\n\nexport const VIDEO_LIMITS = {\n /** The poster's bytes: with the rest of an offer, a frame stays far below the 60 KiB a session carries. */\n maxPosterBytes: 12 * 1024,\n /** The poster's longest side, in pixels, as the sender draws it. */\n posterEdge: 320,\n /** Ten hours: longer is a mistake, not a video. */\n maxDurationMs: 10 * 60 * 60_000,\n /** Pixels on either side (8K and a bit). */\n maxEdge: 16_384,\n} as const;\n\n/**\n * Video a player may be handed as it is. None of these runs anything in the page's origin, so received bytes\n * of these types are served with their type (see `safeBlobType`): WebKit will not play what is typed as bytes.\n */\nexport const PLAYABLE_VIDEO = /^video\\/(mp4|webm|quicktime|ogg|x-m4v)$/;\n\n/** Whether a file is a video, by its type: shown in a video bubble whether or not it came with a description. */\nexport function isVideoMime(mime: string): boolean {\n return mime.split(\";\")[0]!.trim().toLowerCase().startsWith(\"video/\");\n}\n\n/**\n * Whether a file is shown as a video (a poster, a player): a type a player may be handed. Other video types\n * (Matroska, AVI) are files, saved to be played elsewhere.\n */\nexport function isPlayableVideoType(mime: string): boolean {\n return PLAYABLE_VIDEO.test(mime.split(\";\")[0]!.trim().toLowerCase());\n}\n\nconst BASE64URL = /^[A-Za-z0-9_-]+$/;\nconst JPEG_START = [0xff, 0xd8, 0xff];\n\n/**\n * A peer's `video` description, or undefined when it is not one. It never refuses the file. A poster that is\n * too large or not a JPEG is dropped on its own; the length and size are kept.\n */\nexport function parseVideoMeta(value: unknown, mime?: string): VideoMeta | undefined {\n if (mime !== undefined && !isVideoMime(mime)) return undefined;\n if (!value || typeof value !== \"object\" || Array.isArray(value)) return undefined;\n const { duration, width, height, poster } = value as { duration?: unknown; width?: unknown; height?: unknown; poster?: unknown };\n if (typeof duration !== \"number\" || !Number.isSafeInteger(duration) || duration <= 0 || duration > VIDEO_LIMITS.maxDurationMs) return undefined;\n const edge = (n: unknown): n is number => typeof n === \"number\" && Number.isSafeInteger(n) && n > 0 && n <= VIDEO_LIMITS.maxEdge;\n if (!edge(width) || !edge(height)) return undefined;\n const meta: VideoMeta = { duration, width, height };\n if (typeof poster === \"string\" && poster.length <= Math.ceil((VIDEO_LIMITS.maxPosterBytes * 4) / 3) && BASE64URL.test(poster)) {\n try {\n const bytes = fromBase64Url(poster);\n if (bytes.length <= VIDEO_LIMITS.maxPosterBytes && JPEG_START.every((b, i) => bytes[i] === b)) meta.poster = poster;\n } catch { /* no poster */ }\n }\n return meta;\n}\n\n/** `0:07`, `12:45`, `1:02:03`: as a player shows a video's length. */\nexport function formatVideoDuration(ms: number): string {\n const total = Math.max(0, Math.floor((Number.isFinite(ms) ? ms : 0) / 1000));\n const hours = Math.floor(total / 3600), minutes = Math.floor((total % 3600) / 60), seconds = String(total % 60).padStart(2, \"0\");\n return hours ? `${hours}:${String(minutes).padStart(2, \"0\")}:${seconds}` : `${minutes}:${seconds}`;\n}\n","import { formatVideoDuration, type VideoMeta } from \"./video\";\n\n/**\n * Voice messages. A voice message is an ordinary file (files/2, or a held item) whose\n * announcement also carries a `voice` description: how long it is and the shape of its\n * sound. The shape is measured once, while recording, so no player ever decodes audio\n * to draw it. A peer that does not know `voice` ignores it and shows an audio file.\n */\nexport interface VoiceMeta {\n /** Milliseconds. */\n duration: number;\n /** Loudness per bar, 0-255, in playing order. */\n peaks: number[];\n}\n\nexport const VOICE_LIMITS = {\n /** Fifteen minutes: at the bitrate recorded (32 kbit/s) about 3.6 MB, well inside a held item's 8 MiB. */\n maxDurationMs: 15 * 60_000,\n /** Bars drawn for a recording. */\n bars: 64,\n /** Bars accepted from a peer. */\n maxBars: 128,\n /** Asked of the recorder; opus speech is clear well below it. */\n audioBitsPerSecond: 32_000,\n} as const;\n\n/**\n * Audio a player may be handed as it is. None of these runs anything in the page's origin,\n * so received bytes of these types are served with their type (see `safeBlobType`).\n */\nexport const PLAYABLE_AUDIO = /^audio\\/(webm|ogg|opus|mp4|mpeg|mp3|aac|x-m4a|wav|x-wav|wave|flac|x-flac)$/;\n\n/**\n * Whether a file sent as a file (not a voice message) is shown with a player: a type a player may be handed.\n * Other audio types (MIDI, AIFF, WMA) are files, saved to be played elsewhere.\n */\nexport function isPlayableAudioType(mime: string): boolean {\n return PLAYABLE_AUDIO.test(baseMime(mime));\n}\n\n/** The part of a type before its parameters, in lower case: `audio/webm;codecs=opus` → `audio/webm`. */\nexport function baseMime(mime: string): string {\n return mime.split(\";\")[0]!.trim().toLowerCase();\n}\n\n/**\n * A peer's `voice` description, or undefined when it is not one. It never refuses the file:\n * a bad description only means the file is shown as a file.\n */\nexport function parseVoiceMeta(value: unknown, mime?: string): VoiceMeta | undefined {\n if (mime !== undefined && !PLAYABLE_AUDIO.test(baseMime(mime))) return undefined;\n if (!value || typeof value !== \"object\" || Array.isArray(value)) return undefined;\n const { duration, peaks } = value as { duration?: unknown; peaks?: unknown };\n // A recorder stops a moment after the limit, never minutes after it.\n if (typeof duration !== \"number\" || !Number.isSafeInteger(duration) || duration <= 0 || duration > VOICE_LIMITS.maxDurationMs + 5_000) return undefined;\n if (!Array.isArray(peaks) || peaks.length === 0 || peaks.length > VOICE_LIMITS.maxBars) return undefined;\n if (!peaks.every((peak) => typeof peak === \"number\" && Number.isInteger(peak) && peak >= 0 && peak <= 255)) return undefined;\n return { duration, peaks: [...peaks] };\n}\n\n/**\n * How the level is metered while recording (apps/ui/src/lib/voiceRecorder.ts): every `intervalMs`, the RMS of the last\n * `window` samples an AnalyserNode holds (at a 48 kHz context, about 21 ms of sound).\n */\nexport const VOICE_METER = { intervalMs: 50, window: 1024, windowRate: 48_000 } as const;\n\n/** One reading of the meter, 0-1: the RMS of the samples, lifted because speech sits low on a linear scale. */\nexport function voiceLevel(samples: ArrayLike<number>): number {\n if (samples.length === 0) return 0;\n let sum = 0;\n for (let i = 0; i < samples.length; i++) sum += samples[i]! * samples[i]!;\n return Math.min(1, Math.sqrt(sum / samples.length) * 4);\n}\n\n/**\n * The readings the recorder would have taken of this sound (mono, -1 to 1), for a recording made elsewhere: one\n * every `intervalMs` of it, each over the window that ends there, as long in time as the analyser's.\n */\nexport function voiceLevelsOf(mono: Float32Array, sampleRate: number): number[] {\n if (!(sampleRate > 0) || mono.length === 0) return [];\n const window = Math.max(1, Math.round((VOICE_METER.window * sampleRate) / VOICE_METER.windowRate));\n const levels: number[] = [];\n for (let step = 1; ; step++) {\n const end = Math.round((step * VOICE_METER.intervalMs * sampleRate) / 1000);\n if (end > mono.length) break;\n levels.push(voiceLevel(mono.subarray(Math.max(0, end - window), end)));\n }\n return levels;\n}\n\n/** The waveform sent with a recording made elsewhere, drawn as the recorder draws its own. */\nexport function voicePeaksOf(mono: Float32Array, sampleRate: number, bars: number = VOICE_LIMITS.bars): number[] {\n return downsamplePeaks(voiceLevelsOf(mono, sampleRate), bars);\n}\n\n/** Below this a recording is silence, and is drawn flat rather than stretched to fill the bars. */\nconst QUIET = 0.02;\n\n/**\n * Bars from the loudness measured while recording (RMS, 0-1, oldest first): the loudest\n * sample of each stretch, scaled so the loudest bar is full height.\n */\nexport function downsamplePeaks(levels: ArrayLike<number>, bars: number = VOICE_LIMITS.bars): number[] {\n const count = Math.max(1, Math.min(VOICE_LIMITS.maxBars, Math.floor(bars)));\n const clean = Array.from(levels, (level) => (Number.isFinite(level) ? Math.min(1, Math.max(0, level)) : 0));\n if (clean.length === 0) return new Array<number>(count).fill(0);\n const loudest = Math.max(QUIET, ...clean);\n const out: number[] = [];\n for (let bar = 0; bar < count; bar++) {\n const from = Math.floor((bar * clean.length) / count);\n // Fewer samples than bars: each bar takes the sample under it.\n const to = Math.max(from + 1, Math.floor(((bar + 1) * clean.length) / count));\n let peak = 0;\n for (let i = from; i < to && i < clean.length; i++) peak = Math.max(peak, clean[i]!);\n out.push(Math.round((peak / loudest) * 255));\n }\n return out;\n}\n\n/**\n * What to record in, by preference: opus in WebM plays on Chromium, Firefox, Safari 15+ and\n * WebKitGTK; older WebKit (WKWebView on macOS before Safari 18.4) records only AAC in MP4,\n * which Chrome, Edge and Safari play. `recorder` is asked of MediaRecorder, `file` is sent.\n */\nexport const RECORDING_TYPES = [\"audio/webm;codecs=opus\", \"audio/ogg;codecs=opus\", \"audio/mp4;codecs=mp4a.40.2\", \"audio/mp4\", \"audio/webm\"] as const;\n\nexport function recordingMime(isTypeSupported: (mime: string) => boolean): { recorder: string; file: string } | null {\n for (const type of RECORDING_TYPES) {\n let supported = false;\n try { supported = isTypeSupported(type); } catch { /* an engine that throws for a type does not have it */ }\n if (supported) return { recorder: type, file: baseMime(type) };\n }\n return null;\n}\n\nconst EXTENSIONS: Record<string, string> = { \"audio/webm\": \"webm\", \"audio/ogg\": \"ogg\", \"audio/mp4\": \"m4a\", \"audio/x-m4a\": \"m4a\", \"audio/mpeg\": \"mp3\", \"audio/aac\": \"aac\", \"audio/wav\": \"wav\" };\n\nfunction voiceName(prefix: string, mime: string, timestamp: number): string {\n const date = new Date(timestamp);\n const pad = (n: number) => String(n).padStart(2, \"0\");\n const stamp = `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}.${pad(date.getMinutes())}.${pad(date.getSeconds())}`;\n return `${prefix} ${stamp}.${EXTENSIONS[baseMime(mime)] ?? \"audio\"}`;\n}\n\n/** A name for the file, for whoever saves it or runs a Ghostly that shows it as a file. */\nexport function voiceFileName(mime: string, timestamp: number): string {\n return voiceName(\"Voice message\", mime, timestamp);\n}\n\n/**\n * The name a downloaded voice message is saved under, made here from the message's time and its\n * type (the sender's own name for it is not used): \"Ghostly voice 2026-09-27 14.01.30.webm\".\n */\nexport function voiceDownloadName(mime: string, timestamp: number): string {\n return voiceName(\"Ghostly voice\", mime, timestamp);\n}\n\n/** `0:07`, `12:45`: minutes and seconds, as a player shows them. */\nexport function formatVoiceDuration(ms: number): string {\n const total = Math.max(0, Math.floor((Number.isFinite(ms) ? ms : 0) / 1000));\n const minutes = Math.floor(total / 60);\n return `${minutes}:${String(total % 60).padStart(2, \"0\")}`;\n}\n\n/** The line a file message shows in the chat list and in notifications. */\nexport function fileMessageText(file: { name: string; voice?: VoiceMeta; video?: VideoMeta }): string {\n if (file.voice) return `🎤 Voice message (${formatVoiceDuration(file.voice.duration)})`;\n if (file.video) return `🎬 Video (${formatVideoDuration(file.video.duration)})`;\n return `📎 ${file.name}`;\n}\n","import {\n CHUNK_KIND,\n LIMITS,\n encodeControl,\n sendBody,\n type ChunkFrame,\n type FileFrame,\n type FrameChannel,\n type ResetFrame,\n} from \"./frames\";\nimport type { WireReply } from \"./replies\";\nimport type { ImageMeta } from \"./image\";\nimport { PLAYABLE_VIDEO, type VideoMeta } from \"./video\";\nimport { PLAYABLE_AUDIO, type VoiceMeta } from \"./voice\";\n\n/**\n * Files over the data link. A `file` frame announces name, size and type, the\n * bytes follow as chunks, and the receiver only keeps the file if exactly the\n * announced number of bytes arrived. Nothing touches Pkarr: files go peer to\n * peer or not at all.\n */\nexport interface FileInfo {\n /** Chosen by the sender. */\n id: string;\n name: string;\n size: number;\n mime: string;\n timestamp: number;\n /** A voice message: its length and the shape of its sound (files/2 and held items only). */\n voice?: VoiceMeta;\n /** A video: its length, size and a small first frame (optional; old apps ignore it). */\n video?: VideoMeta;\n /** A picture: its size as it is shown, so its box is laid out before it arrives (optional; old apps ignore it). */\n image?: ImageMeta;\n /** The message this file answers (`r`, WISP 401 § Replies): files/2, files/3 and held items; older apps ignore it. */\n reply?: WireReply;\n /** How many times it has been forwarded (`fw`, WISP 401 § Forwards): files/2, files/3 and held items; older apps ignore it. */\n forwarded?: number;\n}\n\n/** Where a platform puts incoming bytes: memory, IndexedDB, disk. */\nexport interface FileSink {\n write(chunk: Uint8Array): void | Promise<void>;\n close(digest?: string): void | Promise<void>;\n abort(): void;\n}\n\nexport interface FileTransferEvents {\n /** Return a sink to accept the file, null to refuse it, or the reason for refusing it. */\n onIncoming(file: FileInfo): FileSink | string | null;\n onProgress?(fileId: string, transferred: number, direction: \"in\" | \"out\"): void;\n onComplete?(fileId: string, direction: \"in\" | \"out\"): void;\n onFailed?(fileId: string, reason: string, direction: \"in\" | \"out\"): void;\n}\n\nconst FILE_ID = /^[A-Za-z0-9_-]{8,64}$/;\nconst MIME = /^[a-z0-9][a-z0-9!#$&^_.+-]{0,63}\\/[a-z0-9][a-z0-9!#$&^_.+-]{0,63}$/i;\nconst MAX_NAME_LENGTH = 200;\n\n/**\n * Invisible and direction-changing characters: bidi overrides turn\n * \"invoice\\u202Efdp.exe\" into what reads as \"invoiceexe.pdf\", zero-width ones hide\n * text. Cf covers both; line and paragraph separators break the layout.\n */\nconst INVISIBLE = /[\\p{Cc}\\p{Cf}\\p{Zl}\\p{Zp}]/gu;\n\n/**\n * Names Windows keeps for devices, whatever the case, with any extension, and with spaces before the dot:\n * \"CON\", \"nul.txt\", \"COM1 .log\" open a device there, not a file.\n */\nconst WINDOWS_DEVICE = /^(CON|PRN|AUX|NUL|CONIN\\$|CONOUT\\$|COM[0-9¹²³]|LPT[0-9¹²³])\\s*(\\.|$)/i;\n\n/** A file name is display text and a download suggestion, never a path. */\nexport function sanitizeFileName(name: string): string {\n const visible = name.replace(INVISIBLE, \"\").replace(/[/\\\\:]/g, \"\");\n // Whitespace before the dots must not hide them: \" .bashrc\" is a dotfile too.\n const clean = [...visible.replace(/^[\\s.]+/, \"\")].slice(0, MAX_NAME_LENGTH).join(\"\").trim();\n if (WINDOWS_DEVICE.test(clean)) return [...\"_\" + clean].slice(0, MAX_NAME_LENGTH).join(\"\");\n return clean || \"file\";\n}\n\nexport function sanitizeMime(mime: string): string {\n return MIME.test(mime) ? mime.toLowerCase() : \"application/octet-stream\";\n}\n\n/** A size for people: B, KB, MB, GB, TB (powers of 1024), one decimal from KB up. */\nexport function formatFileSize(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`;\n const units = [\"KB\", \"MB\", \"GB\", \"TB\"];\n let value = bytes / 1024, unit = 0;\n while (value >= 1024 && unit < units.length - 1) { value /= 1024; unit++; }\n return `${value.toFixed(1)} ${units[unit]}`;\n}\n\n/** Raster images a UI may show inline. SVG is left out on purpose: it can carry scripts. */\nexport const PREVIEWABLE_IMAGE = /^image\\/(png|jpe?g|gif|webp)$/;\n\n/**\n * The type received bytes are served with. The peer picks the announced type,\n * and a blob: URL typed text/html or image/svg+xml would run in the app's\n * origin, so anything but a previewable image or playable audio or video is opaque bytes.\n * Audio and video keep their type because WebKit will not play what is typed as bytes.\n */\nexport function safeBlobType(mime: string): string {\n const clean = sanitizeMime(mime);\n return PREVIEWABLE_IMAGE.test(clean) || PLAYABLE_AUDIO.test(clean) || PLAYABLE_VIDEO.test(clean) ? clean : \"application/octet-stream\";\n}\n\ninterface Incoming {\n file: FileInfo;\n sink: FileSink;\n received: number;\n idleTimer?: ReturnType<typeof setTimeout>;\n /** Writes are chained so a slow sink cannot reorder chunks. */\n queue: Promise<void>;\n}\n\nexport class FileTransfers {\n private nextStreamId = 1;\n private readonly incoming = new Map<number, Incoming>();\n private readonly outgoing = new Map<number, { fileId: string; cancelled: boolean }>();\n\n constructor(\n private readonly channel: FrameChannel,\n private readonly events: FileTransferEvents,\n ) {}\n\n /** Streams a file to the peer. Resolves once every byte is handed to the channel. */\n async send(file: FileInfo, source: AsyncIterable<Uint8Array>): Promise<void> {\n if (file.size > LIMITS.maxFileBytes) throw new Error(\"File is too large\");\n const id = this.nextStreamId++;\n const state = { fileId: file.id, cancelled: false };\n this.outgoing.set(id, state);\n\n try {\n this.channel.send(\n encodeControl({\n t: \"file\",\n id,\n f: file.id,\n ts: file.timestamp,\n n: sanitizeFileName(file.name),\n s: file.size,\n m: sanitizeMime(file.mime),\n }),\n );\n let sent = 0;\n const counted = async function* (this: FileTransfers) {\n for await (const part of source) {\n sent += part.length;\n yield part;\n this.events.onProgress?.(file.id, sent, \"out\");\n }\n }.call(this);\n await sendBody(this.channel, CHUNK_KIND.fileBody, id, counted, () => state.cancelled);\n if (state.cancelled) throw new Error(\"The peer refused the file\");\n if (sent !== file.size) throw new Error(\"File changed while it was being sent\");\n this.events.onComplete?.(file.id, \"out\");\n } catch (error) {\n if (!state.cancelled) {\n try {\n this.channel.send(encodeControl({ t: \"rst\", id, d: \"f\", e: \"aborted\" }));\n } catch {\n // channel already closed\n }\n }\n this.events.onFailed?.(file.id, error instanceof Error ? error.message : String(error), \"out\");\n throw error;\n } finally {\n this.outgoing.delete(id);\n }\n }\n\n handleFile(frame: FileFrame): void {\n if (this.incoming.has(frame.id)) return;\n const refuse = (reason: string) => {\n try {\n this.channel.send(encodeControl({ t: \"rst\", id: frame.id, d: \"f\", e: reason }));\n } catch {\n // channel already closed\n }\n };\n if (!FILE_ID.test(frame.f)) return refuse(\"invalid file id\");\n if (frame.s > LIMITS.maxFileBytes) return refuse(\"file too large\");\n if (this.incoming.size >= LIMITS.maxIncomingFilesPerPeer) return refuse(\"too many transfers\");\n\n const file: FileInfo = {\n id: frame.f,\n name: sanitizeFileName(frame.n),\n size: frame.s,\n mime: sanitizeMime(frame.m),\n timestamp: frame.ts,\n };\n const sink = this.events.onIncoming(file);\n if (!sink || typeof sink === \"string\") return refuse(sink || \"refused\");\n\n const entry: Incoming = { file, sink, received: 0, queue: Promise.resolve() };\n this.incoming.set(frame.id, entry);\n this.armIdleTimer(frame.id, entry);\n }\n\n handleChunk(chunk: ChunkFrame): void {\n const entry = this.incoming.get(chunk.id);\n if (!entry) return;\n\n entry.received += chunk.payload.length;\n if (entry.received > entry.file.size) return this.failIncoming(chunk.id, \"more data than announced\", true);\n\n if (chunk.payload.length > 0) {\n const payload = chunk.payload.slice();\n entry.queue = entry.queue.then(() => entry.sink.write(payload));\n this.events.onProgress?.(entry.file.id, entry.received, \"in\");\n }\n if (!chunk.end) return this.armIdleTimer(chunk.id, entry);\n\n if (entry.received !== entry.file.size) return this.failIncoming(chunk.id, \"incomplete file\", false);\n clearTimeout(entry.idleTimer);\n this.incoming.delete(chunk.id);\n entry.queue\n .then(() => entry.sink.close())\n .then(\n () => this.events.onComplete?.(entry.file.id, \"in\"),\n () => {\n entry.sink.abort();\n this.events.onFailed?.(entry.file.id, \"could not store the file\", \"in\");\n },\n );\n }\n\n handleReset(frame: ResetFrame): void {\n if (this.incoming.has(frame.id)) this.failIncoming(frame.id, frame.e || \"cancelled by the sender\", false);\n const sending = this.outgoing.get(frame.id);\n if (sending) sending.cancelled = true;\n }\n\n /** The link went away: nothing in flight can finish. */\n closeAll(): void {\n for (const id of [...this.incoming.keys()]) this.failIncoming(id, \"connection lost\", false);\n for (const sending of this.outgoing.values()) sending.cancelled = true;\n }\n\n private armIdleTimer(id: number, entry: Incoming): void {\n clearTimeout(entry.idleTimer);\n entry.idleTimer = setTimeout(() => this.failIncoming(id, \"transfer stalled\", true), LIMITS.bodyIdleTimeoutMs);\n }\n\n private failIncoming(id: number, reason: string, notifyPeer: boolean): void {\n const entry = this.incoming.get(id);\n if (!entry) return;\n clearTimeout(entry.idleTimer);\n this.incoming.delete(id);\n entry.sink.abort();\n if (notifyPeer) {\n try {\n this.channel.send(encodeControl({ t: \"rst\", id, d: \"f\", e: reason }));\n } catch {\n // channel already closed\n }\n }\n this.events.onFailed?.(entry.file.id, reason, \"in\");\n }\n}\n","/**\n * Pictures. A picture is an ordinary file (files/2, files/3 or a held item) whose announcement may also carry an\n * `image` description: its size in pixels as it is shown, so the receiver lays out its box before a byte of it has\n * arrived and the chat does not move when it loads. A peer that does not know `image` ignores it and shows the\n * picture as before; a bad description only means no box.\n *\n * The sender reads the size from the file's first bytes (`imageSize`): no decoding, the same on every platform.\n */\nexport interface ImageMeta {\n /** Pixels, as it is shown (EXIF orientation applied). */\n width: number;\n height: number;\n}\n\nexport const IMAGE_LIMITS = {\n /** Pixels on either side: the most a JPEG can say, well past what anyone sends as a picture. */\n maxEdge: 65_535,\n /** How much of a file is read for its size: past a JPEG's EXIF, thumbnails and colour profile. */\n headBytes: 256 * 1024,\n} as const;\n\n/** Whether a file is a picture, by its type. */\nexport function isImageMime(mime: string): boolean {\n return mime.split(\";\")[0]!.trim().toLowerCase().startsWith(\"image/\");\n}\n\nconst edge = (n: unknown): n is number => typeof n === \"number\" && Number.isSafeInteger(n) && n > 0 && n <= IMAGE_LIMITS.maxEdge;\n\n/** A peer's `image` description, or undefined when it is not one. It never refuses the file. */\nexport function parseImageMeta(value: unknown, mime?: string): ImageMeta | undefined {\n if (mime !== undefined && !isImageMime(mime)) return undefined;\n if (!value || typeof value !== \"object\" || Array.isArray(value)) return undefined;\n const { width, height } = value as { width?: unknown; height?: unknown };\n return edge(width) && edge(height) ? { width, height } : undefined;\n}\n\nconst u16be = (b: Uint8Array, at: number) => (b[at]! << 8) | b[at + 1]!;\nconst u16le = (b: Uint8Array, at: number) => b[at]! | (b[at + 1]! << 8);\nconst u24le = (b: Uint8Array, at: number) => b[at]! | (b[at + 1]! << 8) | (b[at + 2]! << 16);\nconst u32be = (b: Uint8Array, at: number) => ((b[at]! << 24) >>> 0) + (b[at + 1]! << 16) + (b[at + 2]! << 8) + b[at + 3]!;\nconst ascii = (b: Uint8Array, at: number, text: string) => [...text].every((c, i) => b[at + i] === c.charCodeAt(0));\n\nconst sized = (width: number, height: number): ImageMeta | undefined => (edge(width) && edge(height) ? { width, height } : undefined);\n\n/** SOFn markers: every frame type but DHT (C4), JPG (C8) and DAC (CC). */\nconst JPEG_FRAME = new Set([0xc0, 0xc1, 0xc2, 0xc3, 0xc5, 0xc6, 0xc7, 0xc9, 0xca, 0xcb, 0xcd, 0xce, 0xcf]);\n\n/** The EXIF orientation in an APP1 segment's TIFF block (1 when it says none). */\nfunction exifOrientation(b: Uint8Array, tiff: number, end: number): number {\n if (tiff + 8 > end) return 1;\n const little = ascii(b, tiff, \"II\");\n if (!little && !ascii(b, tiff, \"MM\")) return 1;\n const u16 = (at: number) => (little ? u16le(b, at) : u16be(b, at));\n const u32 = (at: number) => (little ? (u16le(b, at) + u16le(b, at + 2) * 65536) : u32be(b, at));\n if (u16(tiff + 2) !== 42) return 1;\n const ifd = tiff + u32(tiff + 4);\n if (ifd + 2 > end) return 1;\n const count = u16(ifd);\n for (let i = 0; i < count; i++) {\n const entry = ifd + 2 + i * 12;\n if (entry + 12 > end) return 1;\n // Orientation, a SHORT: its value sits in the first two bytes of the value field.\n if (u16(entry) === 0x0112) { const value = u16(entry + 8); return value >= 1 && value <= 8 ? value : 1; }\n }\n return 1;\n}\n\nfunction jpegSize(b: Uint8Array): ImageMeta | undefined {\n let at = 2, orientation = 1;\n while (at + 4 <= b.length) {\n if (b[at] !== 0xff) return undefined;\n const marker = b[at + 1]!;\n // Fill bytes, then markers that stand alone.\n if (marker === 0xff) { at++; continue; }\n if (marker === 0x01 || (marker >= 0xd0 && marker <= 0xd7)) { at += 2; continue; }\n // The image data or its end, and still no frame header.\n if (marker === 0xd9 || marker === 0xda) return undefined;\n const length = u16be(b, at + 2);\n if (length < 2) return undefined;\n const end = Math.min(b.length, at + 2 + length);\n if (marker === 0xe1 && ascii(b, at + 4, \"Exif\\0\\0\")) orientation = exifOrientation(b, at + 10, end);\n if (JPEG_FRAME.has(marker)) {\n if (at + 9 > b.length) return undefined;\n const height = u16be(b, at + 5), width = u16be(b, at + 7);\n // 5 to 8 turn it a quarter: shown, it is as wide as it is stored tall.\n return orientation >= 5 ? sized(height, width) : sized(width, height);\n }\n at += 2 + length;\n }\n return undefined;\n}\n\nfunction webpSize(b: Uint8Array): ImageMeta | undefined {\n if (b.length < 30) return undefined;\n if (ascii(b, 12, \"VP8 \")) {\n // A key frame: its start code, then 14-bit sizes (the top two bits are scaling).\n if (b[23] !== 0x9d || b[24] !== 0x01 || b[25] !== 0x2a) return undefined;\n return sized(u16le(b, 26) & 0x3fff, u16le(b, 28) & 0x3fff);\n }\n if (ascii(b, 12, \"VP8L\")) {\n if (b[20] !== 0x2f) return undefined;\n const bits = b[21]! | (b[22]! << 8) | (b[23]! << 16) | (b[24]! << 24);\n return sized((bits & 0x3fff) + 1, ((bits >>> 14) & 0x3fff) + 1);\n }\n if (ascii(b, 12, \"VP8X\")) return sized(u24le(b, 24) + 1, u24le(b, 27) + 1);\n return undefined;\n}\n\n/**\n * A picture's size as it is shown, read from its first bytes (PNG, JPEG, GIF, WebP), or undefined when they do not\n * say. Only the header is read: a JPEG whose frame header sits past `IMAGE_LIMITS.headBytes` goes without.\n */\nexport function imageSize(head: Uint8Array): ImageMeta | undefined {\n const b = head;\n try {\n if (b.length >= 24 && b[0] === 0x89 && ascii(b, 1, \"PNG\\r\\n\\x1a\\n\") && ascii(b, 12, \"IHDR\")) return sized(u32be(b, 16), u32be(b, 20));\n if (b.length >= 10 && (ascii(b, 0, \"GIF87a\") || ascii(b, 0, \"GIF89a\"))) return sized(u16le(b, 6), u16le(b, 8));\n if (b.length >= 4 && b[0] === 0xff && b[1] === 0xd8 && b[2] === 0xff) return jpegSize(b);\n if (b.length >= 16 && ascii(b, 0, \"RIFF\") && ascii(b, 8, \"WEBP\")) return webpSize(b);\n } catch { /* not a picture this reads */ }\n return undefined;\n}\n\n/**\n * The description a picture is sent with, read from the start of the file, or undefined when it is not a picture\n * or its first bytes do not say (it then goes without one).\n */\nexport async function readImageMeta(source: Blob, mime: string): Promise<ImageMeta | undefined> {\n if (!isImageMime(mime)) return undefined;\n try {\n return imageSize(new Uint8Array(await source.slice(0, IMAGE_LIMITS.headBytes).arrayBuffer()));\n } catch {\n return undefined;\n }\n}\n","import { sha256 } from \"@noble/hashes/sha2.js\";\nimport { fromBase64Url, toBase64Url } from \"./bytes\";\nimport { LIMITS, type FrameChannel } from \"./frames\";\nimport { sanitizeFileName, sanitizeMime, type FileInfo, type FileSink, type FileTransferEvents } from \"./files\";\nimport { pairedReplyAuthor, readReply, wireReply } from \"./replies\";\nimport { readForwarded } from \"./forwards\";\nimport { parseVideoMeta } from \"./video\";\nimport { parseImageMeta } from \"./image\";\nimport { parseVoiceMeta } from \"./voice\";\n\nconst CHUNK = 16 * 1024;\nconst ID = /^[A-Za-z0-9_-]{8,64}$/;\ninterface Incoming {\n file: FileInfo;\n sink: FileSink | null;\n stored?: string;\n hash: ReturnType<typeof sha256.create>;\n offset: number;\n timer?: ReturnType<typeof setTimeout>;\n}\ninterface Pending { resolve(): void; reject(error: Error): void; frame: object; }\n/** Transfers finished lately, by id, with their final offset: a repeated end is acknowledged again. */\nconst DONE_KEPT = 32;\n\n/** files/2: authenticated text frames work on RTC, Iroh and HyperDHT alike.\n * Stop-and-wait credits bound each sender to one 16 KiB chunk in flight.\n * Final receipt follows digest verification and durable sink.close, never merely send().\n * A live transport switch moves transfers to the new channel (`rebind`): the one frame awaiting its\n * acknowledgement goes again there, and a receiver acknowledges a frame it already applied again. */\nexport class PairedFiles {\n private incoming = new Map<string, Incoming>();\n private outgoing = new Set<string>();\n private pending = new Map<string, Pending>();\n private done = new Map<string, number>();\n private closed = false;\n constructor(private channel: FrameChannel, private events: FileTransferEvents & {\n onStored?(file: FileInfo): Promise<string | undefined>;\n }) {}\n\n /** A file going either way on this session right now. */\n get busy(): boolean { return this.incoming.size > 0 || this.outgoing.size > 0; }\n\n private sendFrame(frame: object): void {\n if (this.closed) throw new Error(\"Connection lost\");\n this.channel.send(JSON.stringify(frame));\n }\n\n private exchange(id: string, offset: number, phase: string, frame: object): Promise<void> {\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => finish(new Error(\"File transfer timed out\")), LIMITS.bodyIdleTimeoutMs);\n const key = `${id}:${phase}:${offset}`;\n const finish = (error?: Error) => {\n clearTimeout(timer); this.pending.delete(key);\n if (error) reject(error); else resolve();\n };\n this.pending.set(key, { resolve: () => finish(), reject: finish, frame });\n try { this.sendFrame(frame); } catch (error) { finish(error instanceof Error ? error : new Error(String(error))); }\n });\n }\n\n async send(file: FileInfo, source: AsyncIterable<Uint8Array>): Promise<void> {\n if (this.closed || !ID.test(file.id) || !Number.isSafeInteger(file.size) || file.size < 0 || file.size > LIMITS.maxFileBytes)\n throw new Error(\"Invalid file or connection closed\");\n if (this.outgoing.has(file.id) || this.outgoing.size >= LIMITS.maxIncomingFilesPerPeer) throw new Error(\"File transfer already active\");\n this.outgoing.add(file.id);\n try {\n const { reply, forwarded, ...announced } = file;\n const hops = readForwarded(forwarded);\n await this.exchange(file.id, 0, \"start\", { t: \"pf-start\", ...announced, name: sanitizeFileName(file.name), mime: sanitizeMime(file.mime), voice: parseVoiceMeta(file.voice, sanitizeMime(file.mime)), video: parseVideoMeta(file.video, sanitizeMime(file.mime)), image: parseImageMeta(file.image, sanitizeMime(file.mime)), ...(reply && { r: wireReply(reply) }), ...(hops && { fw: hops }) });\n const hash = sha256.create();\n let offset = 0;\n for await (const part of source) {\n for (let pos = 0; pos < part.length; pos += CHUNK) {\n const chunk = part.subarray(pos, pos + CHUNK);\n if (offset + chunk.length > file.size) throw new Error(\"File changed during transfer\");\n hash.update(chunk);\n await this.exchange(file.id, offset + chunk.length, \"chunk\", { t: \"pf-chunk\", id: file.id, offset, data: toBase64Url(chunk) });\n offset += chunk.length;\n this.events.onProgress?.(file.id, offset, \"out\");\n }\n }\n if (offset !== file.size) throw new Error(\"Incomplete file\");\n await this.exchange(file.id, offset, \"end\", { t: \"pf-end\", id: file.id, offset, digest: toBase64Url(hash.digest()) });\n this.events.onComplete?.(file.id, \"out\");\n } catch (error) {\n try { this.sendFrame({ t: \"pf-cancel\", id: file.id, target: \"receiver\" }); } catch { /* disconnected */ }\n this.events.onFailed?.(file.id, error instanceof Error ? error.message : String(error), \"out\");\n throw error;\n } finally { this.outgoing.delete(file.id); }\n }\n\n /** The session moved to another transport: transfers carry on over `channel`, where what awaits an acknowledgement goes again. */\n rebind(channel: FrameChannel): void {\n if (this.closed) return;\n this.channel = channel;\n for (const pending of this.pending.values()) {\n try { this.sendFrame(pending.frame); } catch (error) { pending.reject(error instanceof Error ? error : new Error(String(error))); }\n }\n }\n\n async handle(frame: Record<string, unknown>): Promise<void> {\n if (this.closed || typeof frame.id !== \"string\" || !ID.test(frame.id)) return;\n const id = frame.id;\n if (frame.t === \"pf-ack\") {\n if (Number.isSafeInteger(frame.offset) && [\"start\", \"chunk\", \"end\"].includes(String(frame.phase)))\n this.pending.get(`${id}:${frame.phase}:${frame.offset}`)?.resolve();\n return;\n }\n if (frame.t === \"pf-cancel\") {\n if (frame.target === \"receiver\") this.fail(id, \"Cancelled by sender\", false);\n if (frame.target === \"sender\") for (const [key, pending] of this.pending)\n if (key.startsWith(`${id}:`)) pending.reject(new Error(\"Peer refused or could not store the file\"));\n return;\n }\n try {\n if (frame.t === \"pf-start\") {\n const active = this.incoming.get(id);\n // Sent again after a transport switch, before any chunk: the same announcement is acknowledged again.\n if (active && active.offset === 0 && frame.size === active.file.size && frame.name === active.file.name) {\n this.sendFrame({ t: \"pf-ack\", id, phase: \"start\", offset: 0 }); return;\n }\n if (active) throw new Error(\"Duplicate active file\");\n if (typeof frame.name !== \"string\" || frame.name.length > 1000 || typeof frame.mime !== \"string\" || frame.mime.length > 130 ||\n typeof frame.size !== \"number\" || !Number.isSafeInteger(frame.size) || frame.size < 0 || frame.size > LIMITS.maxFileBytes ||\n typeof frame.timestamp !== \"number\" || !Number.isSafeInteger(frame.timestamp) || frame.timestamp <= 0 ||\n this.incoming.size >= LIMITS.maxIncomingFilesPerPeer) throw new Error(\"Invalid file announcement\");\n const file: FileInfo = { id, name: sanitizeFileName(frame.name), mime: sanitizeMime(frame.mime), size: frame.size, timestamp: frame.timestamp };\n // A description that does not check out only costs the player: the file itself is fine.\n const voice = parseVoiceMeta(frame.voice, file.mime);\n if (voice) file.voice = voice;\n const video = parseVideoMeta(frame.video, file.mime);\n if (video) file.video = video;\n const image = parseImageMeta(frame.image, file.mime);\n if (image) file.image = image;\n const reply = readReply(frame.r, pairedReplyAuthor);\n if (reply) file.reply = reply;\n const forwarded = readForwarded(frame.fw);\n if (forwarded) file.forwarded = forwarded;\n const stored = await this.events.onStored?.(file);\n if (this.closed) return;\n const sink = stored ? null : this.events.onIncoming(file);\n if (!stored && (!sink || typeof sink === \"string\")) throw new Error(typeof sink === \"string\" ? sink : \"File refused\");\n const entry: Incoming = { file, sink: sink as FileSink | null, stored, hash: sha256.create(), offset: 0 };\n this.incoming.set(id, entry); this.arm(id, entry);\n this.sendFrame({ t: \"pf-ack\", id, phase: \"start\", offset: 0 });\n return;\n }\n const entry = this.incoming.get(id);\n if (!entry) {\n if (frame.t === \"pf-end\" && this.done.get(id) === frame.offset) this.sendFrame({ t: \"pf-ack\", id, phase: \"end\", offset: frame.offset });\n return;\n }\n // The last chunk again (its acknowledgement was lost in a transport switch): acknowledged again, applied once.\n if (frame.t === \"pf-chunk\" && typeof frame.offset === \"number\" && typeof frame.data === \"string\" && frame.offset < entry.offset &&\n frame.offset + fromBase64Url(frame.data).length === entry.offset) {\n this.sendFrame({ t: \"pf-ack\", id, phase: \"chunk\", offset: entry.offset }); return;\n }\n if (frame.offset !== entry.offset) throw new Error(\"Invalid file offset\");\n if (frame.t === \"pf-chunk\") {\n if (typeof frame.data !== \"string\" || frame.data.length > Math.ceil(CHUNK * 4 / 3) || !/^[A-Za-z0-9_-]+$/.test(frame.data)) throw new Error(\"Invalid chunk\");\n const bytes = fromBase64Url(frame.data);\n if (!bytes.length || bytes.length > CHUNK || entry.offset + bytes.length > entry.file.size) throw new Error(\"Invalid chunk size\");\n entry.hash.update(bytes);\n await entry.sink?.write(bytes);\n if (this.incoming.get(id) !== entry) return;\n entry.offset += bytes.length;\n this.arm(id, entry);\n this.events.onProgress?.(id, entry.offset, \"in\");\n this.sendFrame({ t: \"pf-ack\", id, phase: \"chunk\", offset: entry.offset });\n } else if (frame.t === \"pf-end\") {\n const digest = toBase64Url(entry.hash.digest());\n if (entry.offset !== entry.file.size || frame.digest !== digest || (entry.stored && entry.stored !== digest)) throw new Error(\"File integrity check failed\");\n await entry.sink?.close(digest);\n if (this.incoming.get(id) !== entry) return;\n clearTimeout(entry.timer); this.incoming.delete(id);\n this.done.set(id, entry.offset);\n if (this.done.size > DONE_KEPT) this.done.delete(this.done.keys().next().value!);\n if (!entry.stored) this.events.onComplete?.(id, \"in\");\n this.sendFrame({ t: \"pf-ack\", id, phase: \"end\", offset: entry.offset });\n }\n } catch (error) { this.fail(id, error instanceof Error ? error.message : String(error), true); }\n }\n\n private arm(id: string, entry: Incoming): void {\n clearTimeout(entry.timer);\n entry.timer = setTimeout(() => this.fail(id, \"File transfer stalled\", true), LIMITS.bodyIdleTimeoutMs);\n }\n private fail(id: string, reason: string, notify: boolean): void {\n const entry = this.incoming.get(id);\n if (entry) {\n clearTimeout(entry.timer); this.incoming.delete(id); entry.sink?.abort();\n if (!entry.stored) this.events.onFailed?.(id, reason, \"in\");\n }\n if (notify) try { this.sendFrame({ t: \"pf-cancel\", id, target: \"sender\" }); } catch { /* disconnected */ }\n }\n closeAll(): void {\n this.closed = true;\n for (const id of this.incoming.keys()) this.fail(id, \"Connection lost\", false);\n for (const pending of this.pending.values()) pending.reject(new Error(\"Connection lost\"));\n }\n}\n","import type { FrameChannel } from \"./frames\";\n\nexport const TRANSPORTS = [\"iroh/1\", \"hyperdht/1\", \"webrtc/1\"] as const;\nexport type PairedTransport = typeof TRANSPORTS[number];\nexport type NativeTransport = Exclude<PairedTransport, \"webrtc/1\">;\n\n/** Supplied only by the local adapter after its real cryptographic handshake.\n * Iroh uses a TLS exporter; HyperDHT uses its Noise handshake hash. */\nexport interface NativeBinding {\n transport: NativeTransport;\n context: string;\n identities: [string, string];\n}\nexport interface BoundChannel { channel: FrameChannel; binding: NativeBinding }\nexport interface NativeEndpoint {\n transport: NativeTransport;\n descriptor: unknown;\n connect(descriptor: unknown): Promise<BoundChannel>;\n close(): Promise<void>;\n onConnection: ((connection: BoundChannel) => void) | null;\n onDescriptor: (() => void) | null;\n onUnavailable?: (() => void) | null;\n}\nexport type TransportDescriptors = Partial<Record<NativeTransport, unknown>>;\n\n/**\n * One spelling per Iroh relay. Iroh compares relay URLs as text, and its own relays are named with the trailing dot\n * of a fully qualified name (`https://use1-1.relay.n0.iroh.link./`): the same server written without the dot is\n * another relay to it, and an endpoint dialling a contact there opens a second connection to the server it is already\n * homed on, which the server answers by setting the first one aside.\n *\n * So every relay URL is put in one spelling before it goes into an Iroh endpoint (the relays it homes on, a contact's\n * descriptor before a dial), and which one depends on the endpoint:\n * - `\"dotted\"`, the name with its trailing dot: what native Iroh homes on by default (the Desktop), and the form to\n * compare two relays in.\n * - `\"plain\"`, the name without it: the only one every browser opens. WebKit (Safari, the iPhone app) refuses a\n * host name that ends in a dot, for `fetch` and for a WebSocket alike, so nothing a browser's Iroh is given has one.\n *\n * An IP address and a single-label name (a search domain may complete it) stay as written; `localhost.` loses its dot\n * in both, the form the relay settings accept. What does not parse comes back untouched, for the caller's own check.\n */\nexport type IrohRelaySpelling = \"dotted\" | \"plain\";\nexport function irohRelayUrl(value: string, spelling: IrohRelaySpelling = \"dotted\"): string {\n let url: URL;\n try { url = new URL(value); } catch { return value; }\n const host = url.hostname, name = host.replace(/\\.$/, \"\");\n if (!name.includes(\".\") && name !== \"localhost\") return value;\n if (name.startsWith(\"[\") || /^\\d+(\\.\\d+){3}$/.test(name)) return value;\n const wanted = spelling === \"plain\" || name === \"localhost\" ? name : `${name}.`;\n if (wanted === host) return value;\n url.hostname = wanted;\n return url.href;\n}\n\n/** An Iroh descriptor with its relay in one spelling (`irohRelayUrl`); anything else as it is. */\nexport function irohDescriptor<T>(descriptor: T, spelling: IrohRelaySpelling = \"dotted\"): T {\n const relay = (descriptor as { relay?: unknown } | null | undefined)?.relay;\n if (typeof relay !== \"string\") return descriptor;\n const canonical = irohRelayUrl(relay, spelling);\n return canonical === relay ? descriptor : { ...descriptor, relay: canonical };\n}\n\n/**\n * Relay only (WISP 100, \"Relayed\"): the transport reaches this contact only through a relay server, never\n * directly. A descriptor says so with `relayed: true`, and an Iroh descriptor with no direct address is relayed\n * too (a browser's, WISP 102). Either side being relay only makes the whole path relayed. Both sides hold both\n * descriptors after the exchange, so they agree, and the rank stays symmetric.\n */\nexport function relayedTransports(local: TransportDescriptors, remote: TransportDescriptors): NativeTransport[] {\n const relayOnly = (transport: NativeTransport, descriptor: unknown): boolean => {\n if (!descriptor || typeof descriptor !== \"object\") return false;\n const d = descriptor as { relayed?: unknown; addresses?: unknown };\n return d.relayed === true || (transport === \"iroh/1\" && Array.isArray(d.addresses) && d.addresses.length === 0);\n };\n return ([\"iroh/1\", \"hyperdht/1\"] as const).filter(t => relayOnly(t, local[t]) || relayOnly(t, remote[t]));\n}\n\n/** Symmetric rank sum; the fixed order breaks ties deterministically. Neither\n * a remote list nor a preference can add an unavailable local adapter. A\n * relayed transport ranks after every direct one: it is where a failed direct\n * path goes, never ahead of one that works. */\nexport function rankTransports(local: readonly string[], remote: readonly string[], relayed: readonly string[] = []): PairedTransport[] {\n const penalty = (t: PairedTransport) => relayed.includes(t) ? 1 : 0;\n return TRANSPORTS.filter(t => local.includes(t) && remote.includes(t))\n .sort((a, b) => penalty(a) - penalty(b)\n || (local.indexOf(a) + remote.indexOf(a)) - (local.indexOf(b) + remote.indexOf(b))\n || TRANSPORTS.indexOf(a) - TRANSPORTS.indexOf(b));\n}\n\n/**\n * What a chat prefers when nobody chose (Automatic): WebRTC where the app has it, else the first transport it has\n * (a Linux Desktop: WebKitGTK has no WebRTC). With nothing at all, WebRTC, which the offer then leaves out.\n */\nexport function automaticTransport(available: readonly PairedTransport[]): PairedTransport {\n return available.includes(\"webrtc/1\") ? \"webrtc/1\" : TRANSPORTS.find(t => available.includes(t)) ?? \"webrtc/1\";\n}\n\nexport function transportOrder(available: readonly PairedTransport[], preferred: PairedTransport,\n fallback: boolean): PairedTransport[] {\n const rest = TRANSPORTS.filter(t => available.includes(t) && t !== preferred);\n return [...(available.includes(preferred) ? [preferred] : []), ...(fallback ? rest : [])];\n}\n","import { rankTransports, relayedTransports, transportOrder, TRANSPORTS, type PairedTransport, type TransportDescriptors } from \"./pairedTransports\";\n\nexport interface TransportPolicy {\n revision: number;\n intent: number;\n preferred: PairedTransport;\n fallback: boolean;\n available: PairedTransport[];\n descriptors: TransportDescriptors;\n}\nexport interface SwitchPlan {\n id: string;\n local: TransportPolicy;\n remote: TransportPolicy;\n choices: PairedTransport[];\n}\nexport const allowedTransports = (policy: TransportPolicy) => transportOrder(policy.available, policy.preferred, policy.fallback);\n\nfunction parsePolicy(raw: unknown): TransportPolicy | null {\n if (!raw || typeof raw !== \"object\") return null;\n const p = raw as TransportPolicy;\n if (!Number.isSafeInteger(p.revision) || p.revision < 0 || !Number.isSafeInteger(p.intent) || p.intent < 0 ||\n !TRANSPORTS.includes(p.preferred) || typeof p.fallback !== \"boolean\" || !Array.isArray(p.available) ||\n p.available.length > 3 || new Set(p.available).size !== p.available.length || p.available.some(t => !TRANSPORTS.includes(t)) ||\n !p.descriptors || typeof p.descriptors !== \"object\" || JSON.stringify(p.descriptors).length > 4096) return null;\n const descriptors: TransportDescriptors = {};\n for (const t of [\"iroh/1\", \"hyperdht/1\"] as const) if (p.descriptors[t]) descriptors[t] = p.descriptors[t];\n return { revision: p.revision, intent: p.intent, preferred: p.preferred, fallback: p.fallback, available: [...p.available], descriptors };\n}\n\n/** Runs only inside a participation-authenticated channel. Both roles may propose;\n * the lower rendezvous key coordinates a revision-checked plan, never both dials.\n * The existing channel stays alive while a replacement proves its own binding. */\nexport class TransportSwitch {\n private context = \"\";\n private revision = 0;\n private intent = 0;\n private remote: TransportPolicy | null = null;\n /** The contact's policy as it last said it, kept across sessions: where the next reconnect dials first. */\n private lastRemote: TransportPolicy | null = null;\n private actual?: PairedTransport;\n private plan: SwitchPlan | null = null;\n private timer: ReturnType<typeof setTimeout> | null = null;\n private settled = \"\";\n private failed = \"\";\n private preparing = false;\n /** This side chose while no session was open: the next one begins with it as a choice. */\n private apart = false;\n constructor(private readonly options: {\n key: string; peerKey: string;\n policy(): Omit<TransportPolicy, \"revision\" | \"intent\">;\n send(frame: object): void;\n peer(policy: TransportPolicy): void;\n state(error?: string, target?: PairedTransport): void;\n prepare(plan: SwitchPlan, dial: boolean): void;\n cancel(): void;\n /** A plan's target could not be reached and the session stayed on the current transport (fallback allowed it). */\n kept?(target: PairedTransport, reason?: string): void;\n /**\n * A plan's target could not be reached, and nothing kept the chat on another transport: the chat waits for it\n * (WISP 100, \"A chosen transport not reached yet\") rather than failing, and is told why here, not as an error.\n * `reason` is known on the side that dialled. Without this callback, the failure is an error state, as before.\n */\n unreached?(target: PairedTransport, reason?: string): void;\n timeoutMs?: number;\n }) {}\n\n get peerPolicy(): TransportPolicy | null { return this.remote; }\n /**\n * The transport a standing explicit choice names (the higher intent; the lower key on a tie), from this side's\n * policy and the contact's last one, when both can use it. None when neither side chose: the rank sum decides.\n * Both sides get the same answer, so whichever redials after a drop lands where the agreement would move it.\n */\n get chosenTarget(): PairedTransport | undefined {\n const local = this.local(), remote = this.lastRemote;\n if (!remote) return undefined;\n const winner = this.winner(local, remote);\n if (winner.intent === 0) return undefined;\n return rankTransports(allowedTransports(local), allowedTransports(remote)).includes(winner.preferred) ? winner.preferred : undefined;\n }\n private winner(local: TransportPolicy, remote: TransportPolicy): TransportPolicy {\n return local.intent > remote.intent || (local.intent === remote.intent && this.options.key < this.options.peerKey) ? local : remote;\n }\n get pending(): SwitchPlan | null { return this.plan; }\n /**\n * Where the policies put the chat while a session is open, and whose choice that is: the agreed target, or, when\n * the two sides' transports do not overlap, the winning side's preferred. `by` is absent when nobody chose (a\n * policy alone, such as Fallback off, names it). Both sides get the same answer.\n */\n wanted(): { transport: PairedTransport; by?: \"you\" | \"contact\" } | undefined {\n if (!this.context || !this.remote) return undefined;\n const local = this.local(), remote = this.remote, winner = this.winner(local, remote);\n const transport = this.choices(local, remote)[0] ?? winner.preferred;\n return { transport, ...(winner.intent > 0 ? { by: winner === local ? \"you\" as const : \"contact\" as const } : {}) };\n }\n private local(): TransportPolicy { return { ...this.options.policy(), revision: this.revision, intent: this.intent }; }\n private signature(local: TransportPolicy, remote: TransportPolicy): string {\n return this.options.key < this.options.peerKey ? `${local.revision}:${remote.revision}` : `${remote.revision}:${local.revision}`;\n }\n private send(frame: object): void { if (this.context) this.options.send({ ...frame, context: this.context }); }\n private announce(): void { this.send({ t: \"paired-policy\", policy: this.local() }); }\n\n /**\n * A session is ready. `migrated`: it is the one a plan dialled, so that plan is done, even on a fallback choice.\n * Otherwise it is a fresh session (a reconnect after a drop): a plan still pending from the old one did not happen,\n * and what the old one kept or gave up on does not bind this one. Both sides agree again from their policies, so\n * the chat ends on the same transport whichever side dialled.\n * A plan that landed on a fallback choice (its target did not connect, a later choice did) is a target not reached,\n * as when the current transport is kept: its owner waits for it and tries it again (WISP 100).\n */\n begin(context: string, actual: PairedTransport, migrated = false): void {\n const landed = migrated ? this.plan : null;\n this.settled = landed ? this.signature(landed.local, landed.remote) : \"\";\n if (!migrated) this.failed = \"\";\n // A choice made while apart is a choice now: above the contact's last intent, as one made on a session would be.\n if (this.apart) { this.intent = Math.max(this.intent, this.lastRemote?.intent ?? 0) + 1; this.apart = false; }\n this.clearPlan();\n this.context = context; this.actual = actual;\n this.options.state(); this.announce();\n if (landed && landed.choices[0] !== actual) this.options.kept?.(landed.choices[0]);\n }\n /** `automatic`: no choice of this side's any more; the contact's explicit one wins, and without one the\n * current transport is kept while it is allowed. */\n changed(userIntent: boolean | \"automatic\" = true): void {\n this.revision++;\n if (userIntent === \"automatic\") { this.intent = 0; this.apart = false; }\n else if (userIntent) this.intent = Math.max(this.intent, this.remote?.intent ?? 0) + 1;\n this.failed = \"\";\n this.announce(); this.reconcile();\n }\n /**\n * This side's user chose the transport its policy now names. `again`: the one it named already. Choosing again\n * what this side's standing choice names raises no intent: it would move nothing, and a raised intent could beat\n * a newer choice of the contact's still on its way (the tie goes to the lower key). It still counts as a retry.\n * Choosing again while the contact's choice stands is an override, and raises it.\n */\n chose(again = false): void { this.changed(!(again && this.standing)); }\n /**\n * This side's user chose with no session open (WISP 100, \"A choice made while not live\"). Nothing to announce it on:\n * the next session begins with it as a choice, so the chat moves there if that session opened elsewhere.\n */\n choseApart(): void { this.revision++; this.apart = true; this.failed = \"\"; }\n /** This side's explicit choice is the one that stands (its intent is the higher, or wins the tie). */\n get standing(): boolean {\n if (this.intent === 0) return false;\n const local = this.local();\n return !this.remote || this.winner(local, this.remote) === local;\n }\n keep(reason?: string): void {\n const target = this.plan?.choices[0];\n if (this.plan) this.settled = this.signature(this.plan.local, this.plan.remote);\n if (target && target !== this.actual) this.options.kept?.(target, reason);\n this.send({ t: \"paired-switch-keep\", id: this.plan?.id });\n this.clearPlan(); this.options.state(); this.reconcile();\n }\n retry(): void { this.failed = \"\"; this.announce(); this.reconcile(); }\n /**\n * Tries the target again after it did not connect (WISP 100: retried, never given up), also when a fallback kept\n * the chat on another transport. The coordinator plans again. The other side cannot: its policy goes out again under\n * a new revision, which the coordinator reconciles afresh, whatever it had given up on.\n */\n again(): void {\n if (!this.context || !this.remote || this.plan) return;\n this.failed = this.settled = \"\";\n if (this.options.key > this.options.peerKey) { this.revision++; this.announce(); }\n this.reconcile();\n }\n stop(): void { this.clearPlan(); this.context = \"\"; this.remote = null; }\n private clearPlan(): void {\n if (this.timer) clearTimeout(this.timer);\n this.timer = null; this.plan = null; this.preparing = false;\n }\n fail(error: string): void {\n const target = this.plan?.choices[0];\n if (this.plan) this.failed = this.signature(this.plan.local, this.plan.remote);\n this.send({ t: \"paired-switch-failed\", id: this.plan?.id });\n this.clearPlan(); this.options.cancel(); this.unreached(target, error, error);\n this.replanIfMoved();\n }\n /**\n * A choice made while a move was dialling could not cut in (WISP 100): once that move failed, it is planned at once.\n * Policies that are still the failed plan's are not planned again here; the owner's retry pace does that.\n */\n private replanIfMoved(): void {\n if (this.context && this.remote && this.failed && this.signature(this.local(), this.remote) !== this.failed) this.reconcile();\n }\n /** Waiting for `target`, not failed, where the owner says why; an error state for an owner that does not. */\n private unreached(target: PairedTransport | undefined, error: string, reason?: string): void {\n if (target && this.options.unreached) { this.options.unreached(target, reason); this.options.state(); }\n else this.options.state(error);\n }\n private choices(local: TransportPolicy, remote: TransportPolicy): PairedTransport[] {\n const relayed = relayedTransports(local.descriptors, remote.descriptors);\n const ranked = rankTransports(allowedTransports(local), allowedTransports(remote), relayed);\n if (!ranked.length) return [];\n const winner = this.winner(local, remote);\n // Nobody chose: the current transport stays, a relayed one too (no probing while live, WISP 100); the next\n // dial ranks direct paths first again.\n const target = winner.intent === 0 && this.actual && ranked.includes(this.actual) ? this.actual\n : ranked.includes(winner.preferred) ? winner.preferred : ranked[0];\n return [target, ...(local.fallback && remote.fallback ? ranked.filter(t => t !== target) : [])];\n }\n private install(plan: SwitchPlan): void {\n this.clearPlan(); this.plan = plan;\n this.options.state(undefined, plan.choices[0]);\n this.timer = setTimeout(() => this.fail(\"Transport change timed out. Your previous connection is kept when available; retry or choose another transport.\"), this.options.timeoutMs ?? 30_000);\n }\n private reconcile(): void {\n if (!this.context || !this.remote || this.plan) return;\n const local = this.local(), remote = this.remote;\n const choices = this.choices(local, remote);\n if (!choices.length) {\n this.clearPlan();\n // A transport one side does not run (yet) is waited for, and its owner says why; only choices that exclude\n // each other (both limited to different transports) are an error.\n const wanted = this.winner(local, remote).preferred;\n const missing = !local.available.includes(wanted) || !remote.available.includes(wanted);\n this.options.state(missing && this.options.unreached ? undefined\n : \"Transport preferences do not overlap. Enable fallback or choose the same supported transport on both sides.\");\n return;\n }\n const signature = this.signature(local, remote);\n if (choices[0] === this.actual || (signature === this.settled && this.actual && choices.includes(this.actual))) {\n this.settled = signature; this.clearPlan(); this.options.state(); return;\n }\n if (signature === this.failed) return;\n this.options.state(undefined, choices[0]);\n if (this.options.key > this.options.peerKey) {\n if (!this.timer) this.timer = setTimeout(() => {\n this.failed = signature; this.clearPlan();\n this.unreached(choices[0], \"Your contact did not acknowledge the transport change. Retry when both peers are connected.\", \"Your contact did not answer\");\n }, this.options.timeoutMs ?? 30_000);\n return;\n }\n const plan = { id: `${signature}:${choices[0]}`, local, remote, choices };\n this.install(plan);\n this.send({ t: \"paired-switch-plan\", id: plan.id, revisions: [local.revision, remote.revision], target: choices[0] });\n }\n handle(frame: Record<string, unknown>): boolean {\n if (![\"paired-policy\", \"paired-switch-plan\", \"paired-switch-ready\", \"paired-switch-go\", \"paired-switch-keep\", \"paired-switch-failed\"].includes(String(frame.t))) return false;\n if (!this.context || frame.context !== this.context) return true;\n if (frame.t === \"paired-policy\") {\n const policy = parsePolicy(frame.policy);\n if (!policy || (this.remote && (policy.revision < this.remote.revision ||\n (policy.revision === this.remote.revision && JSON.stringify(policy) !== JSON.stringify(this.remote))))) return true;\n const changed = !this.remote || policy.revision !== this.remote.revision;\n this.remote = this.lastRemote = policy; this.options.peer(policy);\n if (changed && this.plan && !this.preparing) this.clearPlan();\n this.reconcile(); return true;\n }\n if (frame.t === \"paired-switch-plan\") {\n if (this.options.key < this.options.peerKey || !this.remote) return true;\n const local = this.local(), remote = this.remote, choices = this.choices(local, remote);\n if (!Array.isArray(frame.revisions) || frame.revisions[0] !== remote.revision || frame.revisions[1] !== local.revision ||\n choices[0] !== frame.target || frame.id !== `${this.signature(local, remote)}:${choices[0]}`) { this.announce(); return true; }\n if (!this.plan || this.plan.id !== frame.id) this.install({ id: String(frame.id), local, remote, choices });\n // Permission is installed before the ACK; a native channel may arrive before\n // the subsequent GO frame on the old channel. Its fresh proof is still mandatory.\n this.options.prepare(this.plan!, false);\n this.send({ t: \"paired-switch-ready\", id: frame.id }); return true;\n }\n if (!this.plan || frame.id !== this.plan.id) return true;\n if (frame.t === \"paired-switch-failed\") {\n const target = this.plan.choices[0];\n this.failed = this.signature(this.plan.local, this.plan.remote);\n this.clearPlan(); this.options.cancel();\n this.unreached(target, \"The transport change failed. Retry or choose another transport.\");\n this.replanIfMoved(); return true;\n }\n if (frame.t === \"paired-switch-keep\") {\n if (this.actual && this.plan.choices.includes(this.actual)) {\n if (this.plan.choices[0] !== this.actual) this.options.kept?.(this.plan.choices[0]);\n this.settled = this.signature(this.plan.local, this.plan.remote);\n this.clearPlan(); this.options.state(); this.reconcile();\n }\n return true;\n }\n if (frame.t === \"paired-switch-ready\" && this.options.key < this.options.peerKey && !this.preparing) {\n if (!this.remote || this.signature(this.local(), this.remote) !== this.signature(this.plan.local, this.plan.remote)) {\n this.clearPlan(); this.announce(); this.reconcile(); return true;\n }\n this.preparing = true;\n this.send({ t: \"paired-switch-go\", id: frame.id });\n this.options.prepare(this.plan, true);\n } else if (frame.t === \"paired-switch-go\" && this.options.key > this.options.peerKey) this.preparing = true;\n return true;\n }\n}\n","/** The retired experimental Ring identity authorization (a modified Pubky Ring signed one peer challenge per\n * conversation). NOT a Pubky homeserver grant; the official Ring never supported it, and nothing makes it any more:\n * Pubky identities are the `pubky` identity provider now (WISP 302, pubkyProofs.ts). What is left verifies\n * evidence a contact already accepted, so old records are not orphaned.\n */\nimport { ed25519 } from '@noble/curves/ed25519.js';\nimport { sha256 } from '@noble/hashes/sha2.js';\nimport { base64urlnopad } from '@scure/base';\nimport { fromZ32, toZ32, utf8Encode, utf8Decode } from './bytes';\nexport const RING_LIFETIME = 600;\nconst enc = base64urlnopad.encode, dec = base64urlnopad.decode;\nconst hash = (s: string) => enc(sha256(utf8Encode(s)));\nconst header = enc(utf8Encode(JSON.stringify({alg:'EdDSA',typ:'ghostly-pubky-identity-delegation/1'})));\nexport interface RingEvidence { id: string; scheme: 'pubky-ring/1'; authorization: string; signature: string }\nexport interface RingClaims { iss: string; cnf: string; aud: 'ghostly-peer-proof/1'; challenge: string; iat: number; exp: number }\nexport function ringClaims(statement: string, issuer: string, delegate: string, issuedAt: number, expiresAt: number): RingClaims {\n return {iss:issuer,cnf:delegate,aud:'ghostly-peer-proof/1',challenge:hash(statement),iat:issuedAt,exp:expiresAt};\n}\nexport function ringAuthorization(claims: RingClaims, seed: Uint8Array): string {\n if(toZ32(ed25519.getPublicKey(seed))!==claims.iss) throw new Error('Ring identity mismatch');\n const input=header+'.'+enc(utf8Encode(JSON.stringify(claims)));\n return input+'.'+enc(ed25519.sign(utf8Encode(input),seed));\n}\nexport function verifyRingAuthorization(authorization: string, statement: string, issuer: string, issuedAt: number, expiresAt: number): RingClaims {\n if(typeof authorization!=='string'||authorization.length>2400)throw new Error('Invalid Ring authorization');\n const [h,p,s,...extra]=authorization.split('.');\n if(extra.length||h!==header||!p||!s)throw new Error('Unsupported Ring authorization');\n const claims=JSON.parse(utf8Decode(dec(p))) as RingClaims;\n if(typeof claims?.cnf!=='string'||!validRingKey(claims.cnf)||expiresAt!==issuedAt+RING_LIFETIME||\n p!==enc(utf8Encode(JSON.stringify(ringClaims(statement,issuer,claims.cnf,issuedAt,expiresAt))))||\n !validRingKey(issuer)||dec(s).length!==64||enc(dec(s))!==s||\n !ed25519.verify(dec(s),utf8Encode(h+'.'+p),fromZ32(issuer),{zip215:false}))throw new Error('Ring did not authorize this exact conversation');\n return claims;\n}\nexport function validRingKey(key: string): boolean {\n try{return /^[ybndrfg8ejkmcpqxot1uwisza345h769]{52}$/.test(key)&&toZ32(fromZ32(key))===key;}catch{return false;}\n}\nconst possession = (authorization: string, statement: string) => utf8Encode(JSON.stringify(['ghostly-pubky-possession',1,hash(authorization),statement]));\nfunction evidenceId(authorization: string, signature: string): string {\n return Array.from(sha256(utf8Encode(JSON.stringify(['pubky-ring/1',authorization,signature]))),b=>b.toString(16).padStart(2,'0')).join('');\n}\nexport function ringEvidence(authorization: string, statement: string, delegatedSeed: Uint8Array): RingEvidence {\n const signature=enc(ed25519.sign(possession(authorization,statement),delegatedSeed));\n return {id:evidenceId(authorization,signature),scheme:'pubky-ring/1',authorization,signature};\n}\nexport function verifyRingEvidence(e: RingEvidence, statement: string, issuer: string, issuedAt: number, expiresAt: number): void {\n if(!e||e.scheme!=='pubky-ring/1'||typeof e.signature!=='string'||e.signature.length!==86)throw new Error('Invalid Ring proof');\n const claims=verifyRingAuthorization(e.authorization,statement,issuer,issuedAt,expiresAt);\n const sig=dec(e.signature);\n if(enc(sig)!==e.signature||e.id!==evidenceId(e.authorization,e.signature)||!ed25519.verify(sig,possession(e.authorization,statement),fromZ32(claims.cnf),{zip215:false}))throw new Error('Invalid delegated proof of possession');\n}\n","// eslint-disable-next-line @typescript-eslint/triple-slash-reference -- Share ambient types with importing workspaces without a runtime import.\n/// <reference path=\"./keet-identity-key.d.ts\" />\nimport b4a from 'b4a';\nimport { RING_LIFETIME, verifyRingEvidence, type RingEvidence } from './pubkyRing';\nimport { verify } from './identity';\nimport { schnorr } from '@noble/curves/secp256k1.js';\nimport { fromBase64Url, fromZ32, toZ32, randomBytes, toBase64Url, utf8Encode } from './bytes';\n\nexport type ProofAdapter = 'nostr' | 'pubky-import' | 'keet-import' | 'pubky-ring' | 'pubky-storage';\nexport const PROOF_ADAPTERS: ProofAdapter[] = ['nostr', 'pubky-import', 'keet-import', 'pubky-storage'];\nexport const proofCapability = (adapter: ProofAdapter) => `proof-${adapter}/1`;\nexport const isProofAdapter = (value: unknown): value is ProofAdapter => PROOF_ADAPTERS.includes(value as ProofAdapter);\nexport interface ProofScope { subject: string; audience: string; context: string; session: string }\nexport interface ProofChallenge extends ProofScope {\n adapter: ProofAdapter; externalKey: string; nonce: string; issuedAt: number; expiresAt: number;\n}\nexport interface NostrProofEvent { id: string; pubkey: string; created_at: number; kind: number; tags: string[][]; content: string; sig: string }\nexport interface ImportedProof { id: string; scheme: 'pubky-import/1' | 'keet-import/1'; signature: string }\nexport interface StorageProof { scheme: 'pubky-storage/1'; id: string; folder: string }\nexport type StorageProofReader = (key: string, path: string) => Promise<string>;\nexport type ProofEvidence = NostrProofEvent | ImportedProof | RingEvidence | StorageProof;\nexport const STORAGE_LIFETIME = 600;\nexport const STORAGE_ROOT = '/pub/ghostly.app/proofs/';\nexport async function storageProof(c: ProofChallenge, folder: string): Promise<StorageProof> {\n if (c.adapter !== 'pubky-storage' || !/^[a-f0-9]{64}$/.test(folder)) throw new Error('Invalid storage proof');\n return { scheme: 'pubky-storage/1', folder, id: await proofHash(JSON.stringify(['pubky-storage/1', folder, proofStatement(c)])) };\n}\nexport function storageProofPath(e: StorageProof): `/pub/${string}` { return `${STORAGE_ROOT}${e.folder}/${e.id}.json`; }\nexport function storageProofBody(c: ProofChallenge, e: StorageProof): string {\n // No participants, conversation identifier, key, or chat content is published.\n return JSON.stringify({ version: 1, commitment: e.id, expiresAt: c.expiresAt });\n}\nexport interface ProofRecord { challenge: ProofChallenge; event: ProofEvidence; verifiedAt: number; status: 'pending' | 'accepted' | 'withdrawal-pending' | 'withdrawn' }\nexport interface ProofLedger { incoming: ProofChallenge[]; outgoing: ProofChallenge[]; local: ProofRecord[]; remote: ProofRecord[] }\nexport const emptyProofLedger = (): ProofLedger => ({ incoming: [], outgoing: [], local: [], remote: [] });\nexport interface ProofStorage {\n read(): ProofLedger;\n /** Atomic, durable read-transform-write, preserving other conversation fields. */\n update(change: (ledger: ProofLedger) => ProofLedger): Promise<void>;\n}\nexport const PROOF_CAPABILITY = 'proof-nostr/1';\nconst WINDOW = 300;\nconst LIFETIME = 86400;\nconst hex = /^[a-f0-9]{64}$/;\nconst unhex = (value: string) => Uint8Array.from(value.match(/../g)!, x => parseInt(x, 16));\nconst key = /^[ybndrfg8ejkmcpqxot1uwisza345h769]{52}$/;\nconst nonce = /^[A-Za-z0-9_-]{43}$/;\nconst nowSeconds = () => Math.floor(Date.now() / 1000);\nexport async function proofHash(text: string): Promise<string> {\n return Array.from(new Uint8Array(await crypto.subtle.digest('SHA-256', new Uint8Array(utf8Encode(text)))), b => b.toString(16).padStart(2, '0')).join('');\n}\nexport function proofStatement(c: ProofChallenge): string {\n return JSON.stringify(['ghostly-peer-proof', 1, c.adapter === 'pubky-storage' ? 'authorized-storage-control' : c.adapter === 'pubky-ring' ? 'delegated-conversation-proof' : 'control-of-external-key', c.adapter, c.externalKey,\n c.subject, c.audience, c.context, c.session, c.nonce, c.issuedAt, c.expiresAt]);\n}\nexport function nostrProofTemplate(c: ProofChallenge) {\n return { kind: 30078, created_at: c.issuedAt,\n tags: [['d', `ghostly-peer-proof/1:${c.context}`], ['expiration', String(c.expiresAt)]], content: proofStatement(c) };\n}\nfunction assertChallenge(c: ProofChallenge, scope: ProofScope, now: number) {\n if (!c || !isProofAdapter(c.adapter) || !validExternalKey(c.adapter, c.externalKey) || !key.test(c.subject) || !key.test(c.audience) ||\n !hex.test(c.context) || !hex.test(c.session) || !nonce.test(c.nonce) || c.subject === c.audience ||\n c.subject !== scope.subject || c.audience !== scope.audience || c.context !== scope.context || c.session !== scope.session ||\n !Number.isSafeInteger(c.issuedAt) || !Number.isSafeInteger(c.expiresAt) || c.expiresAt !== c.issuedAt + (c.adapter === 'pubky-storage' ? STORAGE_LIFETIME : c.adapter === 'pubky-ring' ? RING_LIFETIME : LIFETIME) ||\n c.issuedAt > now + 30 || c.issuedAt < now - WINDOW) throw new Error('Proof challenge expired or belongs to another connection');\n}\nexport async function verifyNostrProof(event: NostrProofEvent, c: ProofChallenge): Promise<void> {\n const expected = nostrProofTemplate(c);\n if (!event || event.pubkey !== c.externalKey || event.kind !== expected.kind || event.created_at !== expected.created_at ||\n event.content !== expected.content || JSON.stringify(event.tags) !== JSON.stringify(expected.tags) ||\n typeof event.id !== 'string' || !hex.test(event.id) || typeof event.sig !== 'string' || !/^[a-f0-9]{128}$/.test(event.sig))\n throw new Error('Signer did not sign the exact Ghostly proof');\n const id = await proofHash(JSON.stringify([0, event.pubkey, event.created_at, event.kind, event.tags, event.content]));\n if (event.id !== id || !schnorr.verify(unhex(event.sig), unhex(id), unhex(event.pubkey))) throw new Error('Invalid Nostr signature');\n}\nexport function validExternalKey(adapter: ProofAdapter, value: string): boolean {\n if (typeof value !== 'string') return false;\n if (adapter !== 'pubky-import' && adapter !== 'pubky-ring' && adapter !== 'pubky-storage') return hex.test(value);\n try { return key.test(value) && toZ32(fromZ32(value)) === value; } catch { return false; }\n}\nexport async function importedProof(c: ProofChallenge, signature: Uint8Array): Promise<ImportedProof> {\n if (c.adapter === 'nostr' || c.adapter === 'pubky-ring' || c.adapter === 'pubky-storage') throw new Error('Wrong proof adapter');\n const scheme = `${c.adapter}/1` as ImportedProof['scheme'];\n const encoded = toBase64Url(signature);\n return { scheme, signature: encoded, id: await proofHash(JSON.stringify([scheme, proofStatement(c), encoded])) };\n}\nexport async function verifyPeerProof(event: ProofEvidence, c: ProofChallenge, readStorage?: StorageProofReader): Promise<void> {\n if (c.adapter === 'nostr') return verifyNostrProof(event as NostrProofEvent, c);\n if (c.adapter === 'pubky-ring') return verifyRingEvidence(event as RingEvidence, proofStatement(c), c.externalKey, c.issuedAt, c.expiresAt);\n if (c.adapter === 'pubky-storage') {\n const e = event as StorageProof;\n if (!e || e.scheme !== 'pubky-storage/1' || !hex.test(e.id) || typeof e.folder !== 'string' ||\n !hex.test(e.folder) || Object.keys(e).sort().join(',') !== 'folder,id,scheme' ||\n (await storageProof(c, e.folder)).id !== e.id || !readStorage) throw new Error('Invalid storage proof');\n if (await readStorage(c.externalKey, storageProofPath(e)) !== storageProofBody(c, e)) throw new Error('Homeserver proof does not match this challenge');\n return;\n }\n const e = event as ImportedProof;\n if (!e || e.scheme !== `${c.adapter}/1` || typeof e.signature !== 'string' || !/^[A-Za-z0-9_-]{1,160}$/.test(e.signature) ||\n typeof e.id !== 'string' || !hex.test(e.id) || !validExternalKey(c.adapter,c.externalKey)) throw new Error('Invalid imported-key proof');\n const sig = fromBase64Url(e.signature);\n if (toBase64Url(sig) !== e.signature || (await importedProof(c,sig)).id !== e.id) throw new Error('Invalid proof encoding or digest');\n const data = utf8Encode(proofStatement(c));\n if (c.adapter === 'pubky-import') {\n if (sig.length !== 64 || !verify(sig,data,fromZ32(c.externalKey))) throw new Error('Invalid Pubky signature');\n } else if (c.adapter === 'keet-import') {\n // Pinned SDK v1 root-only DataAttestation: uint version, uint64 epoch,\n // fixed32 identity, empty chain, data flag, fixed64 signature. Enforce this\n // bounded canonical shape before the SDK's generic array decoder runs.\n if (sig.length !== 107 || sig[0] !== 1 || sig[41] !== 0 || sig[42] !== 1) throw new Error('Unsupported Keet attestation');\n const { default: IdentityKey } = await import('keet-identity-key');\n if (!IdentityKey.verify(b4a.from(sig), b4a.from(data), { expectedIdentity: b4a.from(unhex(c.externalKey)) }))\n throw new Error('Invalid Keet attestation');\n } else throw new Error('Unsupported proof adapter');\n}\nfunction replace(records: ProofRecord[], record: ProofRecord): ProofRecord[] {\n return [...records.filter(r => r.challenge.adapter !== record.challenge.adapter), record];\n}\n\n/** Optional application protocol. All calls require an authenticated, pinned paired channel.\n * A bad external proof never replaces participation authentication or disconnects ordinary chat. */\nexport class PeerProofs {\n private waiting = new Map<string, { adapter: ProofAdapter; externalKey: string; resolve: (c: ProofChallenge) => void; reject: (e: Error) => void; timer: ReturnType<typeof setTimeout> }>();\n constructor(private options: { scope(): ProofScope; send(frame: object): void; storage: ProofStorage; supports?(adapter: ProofAdapter): boolean; now?: () => number; readStorage?: StorageProofReader; onError?(error: string): void }) {}\n private now() { return (this.options.now ?? nowSeconds)(); }\n private scope(incoming = false): ProofScope {\n const s = this.options.scope();\n return incoming ? { ...s, subject: s.audience, audience: s.subject } : s;\n }\n async prepare(externalKey: string, adapter: ProofAdapter = 'nostr'): Promise<ProofChallenge> {\n this.scope();\n if (!isProofAdapter(adapter) || !validExternalKey(adapter, externalKey) || this.options.supports?.(adapter) === false) throw new Error('Proof adapter unavailable or invalid public key');\n if (this.waiting.size) throw new Error('A proof request is already pending');\n const id = toBase64Url(randomBytes(32));\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => { this.waiting.delete(id); reject(new Error('Peer did not provide a challenge. Try again.')); }, 15_000);\n this.waiting.set(id, { adapter, externalKey, resolve, reject, timer });\n try { this.options.send({ t: 'proof-request', id, adapter, externalKey }); }\n catch (e) { clearTimeout(timer); this.waiting.delete(id); reject(e); }\n });\n }\n async submit(c: ProofChallenge, event: ProofEvidence): Promise<void> {\n assertChallenge(c, this.scope(), this.now());\n if (this.options.supports?.(c.adapter) === false) throw new Error('Peer does not support this proof');\n await verifyPeerProof(event, c, this.options.readStorage);\n await this.options.storage.update(l => {\n assertChallenge(c, this.scope(), this.now());\n if (!l.outgoing.some(x => proofStatement(x) === proofStatement(c))) throw new Error('Unknown or used proof challenge');\n return { ...l, outgoing: l.outgoing.filter(x => x.nonce !== c.nonce),\n local: replace(l.local, { challenge: c, event, verifiedAt: this.now(), status: 'pending' }) };\n });\n this.options.send({ t: 'proof-present', challenge: c, event });\n }\n async withdraw(adapter: ProofAdapter = 'nostr'): Promise<void> {\n this.scope();\n const record = this.options.storage.read().local.find(r => r.challenge.adapter === adapter);\n if (!record) return;\n await this.options.storage.update(l => ({ ...l, local: l.local.map(r => r.event.id === record.event.id ? { ...r, status: 'withdrawal-pending' } : r) }));\n this.resendWithdrawals();\n }\n resendWithdrawals(): void {\n const scope = this.scope();\n for (const record of this.options.storage.read().local) {\n if (record.status === 'withdrawal-pending' && record.challenge.subject === scope.subject && record.challenge.audience === scope.audience && record.challenge.context === scope.context)\n this.options.send({ t: 'proof-withdraw', id: record.event.id });\n }\n }\n async receive(frame: Record<string, unknown>): Promise<void> {\n try {\n if (JSON.stringify(frame).length > 8192) throw new Error('Proof too large');\n const scope = this.scope();\n const now = this.now();\n if (frame.t === 'proof-request') {\n if (!isProofAdapter(frame.adapter) || this.options.supports?.(frame.adapter) === false || typeof frame.id !== 'string' || !nonce.test(frame.id) || typeof frame.externalKey !== 'string' || !validExternalKey(frame.adapter, frame.externalKey)) return;\n const c: ProofChallenge = { ...this.scope(true), adapter: frame.adapter, externalKey: frame.externalKey,\n nonce: toBase64Url(randomBytes(32)), issuedAt: now, expiresAt: now + (frame.adapter === 'pubky-storage' ? STORAGE_LIFETIME : frame.adapter === 'pubky-ring' ? RING_LIFETIME : LIFETIME) };\n await this.options.storage.update(l => {\n const incoming = l.incoming.filter(x => x.issuedAt >= now - WINDOW);\n if (incoming.length >= 8) throw new Error('Too many proof challenges');\n return { ...l, incoming: [...incoming, c] };\n });\n this.options.send({ t: 'proof-challenge', id: frame.id, challenge: c });\n } else if (frame.t === 'proof-challenge') {\n const pending = typeof frame.id === 'string' ? this.waiting.get(frame.id) : undefined;\n if (!pending) return;\n const c = frame.challenge as ProofChallenge;\n assertChallenge(c, scope, now);\n if (c.adapter !== pending.adapter || c.externalKey !== pending.externalKey) throw new Error(\"Peer changed the requested external key\");\n await this.options.storage.update(l => ({ ...l, outgoing: [c] }));\n clearTimeout(pending.timer); this.waiting.delete(frame.id as string); pending.resolve(c);\n } else if (frame.t === 'proof-present') {\n const c = frame.challenge as ProofChallenge, event = frame.event as ProofEvidence;\n assertChallenge(c, this.scope(true), now);\n if (this.options.supports?.(c.adapter) === false) throw new Error(\"Unsupported peer proof\");\n if (!this.options.storage.read().incoming.some(x => proofStatement(x) === proofStatement(c))) throw new Error('Unknown or reused proof challenge');\n await verifyPeerProof(event, c, this.options.readStorage);\n await this.options.storage.update(l => {\n assertChallenge(c, this.scope(true), this.now());\n // Consuming and recording are one transaction. An unknown nonce is always rejected,\n // including after restart, reconnect, or expiry of the bounded pending ledger.\n if (!l.incoming.some(x => proofStatement(x) === proofStatement(c))) throw new Error('Unknown or reused proof challenge');\n return { ...l, incoming: l.incoming.filter(x => x.nonce !== c.nonce),\n remote: replace(l.remote, { challenge: c, event, verifiedAt: now, status: 'accepted' }) };\n });\n this.options.send({ t: 'proof-accepted', id: event.id });\n } else if (frame.t === 'proof-accepted') {\n await this.options.storage.update(l => ({ ...l, local: l.local.map(r => r.event.id === frame.id &&\n r.challenge.subject === scope.subject && r.challenge.audience === scope.audience && r.challenge.context === scope.context && r.status === 'pending'\n ? { ...r, status: 'accepted' } : r) }));\n } else if (frame.t === 'proof-withdrawn') {\n await this.options.storage.update(l => ({ ...l, local: l.local.map(r => r.event.id === frame.id && r.status === 'withdrawal-pending' ? { ...r, status: 'withdrawn' } : r) }));\n } else if (frame.t === 'proof-withdraw') {\n if (typeof frame.id !== 'string' || !hex.test(frame.id)) return;\n await this.options.storage.update(l => ({ ...l, remote: l.remote.map(r => r.event.id === frame.id ? { ...r, status: 'withdrawn' } : r) }));\n this.options.send({ t: 'proof-withdrawn', id: frame.id });\n }\n } catch (e) { this.options.onError?.(e instanceof Error ? e.message : 'Invalid peer proof'); }\n }\n stop(): void {\n for (const p of this.waiting.values()) { clearTimeout(p.timer); p.reject(new Error('Connection closed')); }\n this.waiting.clear();\n }\n}\n","/**\n * What a chat's timeline shows of the identity-proof exchange (WISP 300, \"In the conversation\"): one entry when\n * an identity is shared, on both sides, turning verified or failed as the exchange settles, and one when it stops\n * (withdrawn by its owner, or found revoked). Local only: made from the exchange's own steps, stored with the\n * chat's identity ledger, never sent. Nothing new goes on the wire.\n */\n\n/** \"mine\": this side shared it. \"theirs\": the contact did. */\nexport type IdentityTimelineSide = \"mine\" | \"theirs\";\nexport type IdentityTimelineState = \"verifying\" | \"verified\" | \"failed\";\n\nexport interface IdentityTimelineEntry {\n /** Unique in the chat: side, proof and how many entries that proof already had on that side. */\n id: string;\n /** The proof's id (identityStatement().id). */\n proof: string;\n side: IdentityTimelineSide;\n kind: \"shared\" | \"stopped\";\n provider: string;\n /** The identity, once known: at once on the sharer's side, from the presentation on the contact's. */\n subject?: string;\n /** A share only: where the contact's check stands. */\n state?: IdentityTimelineState;\n /** A failed share: why, as the verifier said it. */\n error?: string;\n /** A stop only: its owner withdrew it here, or its proof key published a revocation. */\n reason?: \"withdrawn\" | \"revoked\";\n /** Milliseconds. When it happened; later steps of the same share leave it in place. */\n at: number;\n}\n\n/** One step of the exchange, as the timeline needs it. */\nexport type IdentityTimelineEvent =\n /** A share began: this side shared, or the contact asked for a challenge. `requested`: the id the request named. */\n | { type: \"shared\"; side: IdentityTimelineSide; proof: string; provider: string; subject?: string }\n /** The contact's check settled. `requested`: the id the request named, when the presented proof is another. */\n | { type: \"result\"; side: IdentityTimelineSide; proof: string; requested?: string; provider?: string; subject?: string; ok: boolean; error?: string }\n | { type: \"stopped\"; side: IdentityTimelineSide; proof: string; provider: string; subject?: string; reason: \"withdrawn\" | \"revoked\" }\n /** A share taken back before it ever left this side (queued, the contact never heard of it): its entry goes. */\n | { type: \"unsent\"; side: \"mine\"; proof: string };\n\n/** Entries a chat keeps, newest last. */\nexport const IDENTITY_TIMELINE_MAX = 100;\n\nconst lastOf = (timeline: readonly IdentityTimelineEntry[], side: IdentityTimelineSide, proofs: readonly (string | undefined)[]) => {\n for (let i = timeline.length - 1; i >= 0; i--) if (timeline[i].side === side && proofs.includes(timeline[i].proof)) return i;\n return -1;\n};\n\n/**\n * The timeline after one step. A share already on it is updated in place, never repeated: a request sent again at\n * a reconnect, a second share of a proof the contact already verified. A new attempt after a failure or a stop is\n * a new entry. A stop is written once.\n */\nexport function applyIdentityEvent(timeline: readonly IdentityTimelineEntry[], event: IdentityTimelineEvent, at: number): IdentityTimelineEntry[] {\n const out = [...timeline];\n const i = lastOf(out, event.side, event.type === \"result\" ? [event.proof, event.requested] : [event.proof]);\n const last = i >= 0 ? out[i] : undefined;\n const add = (entry: Omit<IdentityTimelineEntry, \"id\" | \"at\">) => {\n const n = out.filter(e => e.side === entry.side && e.proof === entry.proof).length;\n out.push({ ...entry, id: `${entry.side}:${entry.proof}:${n}`, at });\n };\n switch (event.type) {\n case \"shared\":\n if (last?.kind === \"shared\" && last.state !== \"failed\") {\n out[i] = { ...last, provider: event.provider, subject: event.subject ?? last.subject };\n break;\n }\n add({ proof: event.proof, side: event.side, kind: \"shared\", provider: event.provider, ...(event.subject ? { subject: event.subject } : {}), state: \"verifying\" });\n break;\n case \"result\": {\n const settled = { state: event.ok ? \"verified\" as const : \"failed\" as const, ...(event.ok || !event.error ? {} : { error: event.error }) };\n if (last?.kind === \"shared\") {\n const { error: _, ...rest } = last;\n out[i] = { ...rest, proof: event.proof, provider: event.provider ?? last.provider, subject: event.subject ?? last.subject, ...settled };\n break;\n }\n // Its start is no longer kept (the timeline is bounded): the outcome stands alone.\n if (event.provider) add({ proof: event.proof, side: event.side, kind: \"shared\", provider: event.provider, ...(event.subject ? { subject: event.subject } : {}), ...settled });\n break;\n }\n case \"stopped\":\n if (last?.kind === \"stopped\") break;\n add({ proof: event.proof, side: event.side, kind: \"stopped\", provider: event.provider, subject: event.subject ?? last?.subject, reason: event.reason });\n break;\n case \"unsent\":\n if (last?.kind === \"shared\" && last.state === \"verifying\") out.splice(i, 1);\n break;\n }\n return out.length > IDENTITY_TIMELINE_MAX ? out.slice(-IDENTITY_TIMELINE_MAX) : out;\n}\n\n/** When the contact last shared an identity here (milliseconds), for the chat list: 0 when never. */\nexport function lastSharedWithMe(timeline: readonly IdentityTimelineEntry[] | undefined): number {\n let at = 0;\n for (const e of timeline ?? []) if (e.side === \"theirs\" && e.kind === \"shared\" && e.at > at) at = e.at;\n return at;\n}\n","import { sha256 } from \"@noble/hashes/sha2.js\";\nimport { fromBase64Url, randomBytes, toBase64Url, utf8Encode } from \"./bytes\";\nimport { identityFromSeed, publicKeyFromZ32, sign, verify } from \"./identity\";\nimport { applyIdentityEvent, type IdentityTimelineEntry, type IdentityTimelineEvent } from \"./identityTimeline\";\n\n/**\n * Identity proofs (WISP 300), provider-agnostic half.\n *\n * A proof is made once per profile: the external identity (a Nostr key, a domain, an SSH key, an\n * OpenID Connect account…) signs or attests the canonical **statement** below, which authorizes a\n * fresh Ed25519 **proof key** that only this profile holds. Sharing it with a contact is then a\n * challenge-response on that contact's authenticated paired channel: the contact sends a fresh nonce,\n * the proof key signs a **presentation** binding the proof to both participation keys, the\n * conversation, the session and that nonce. A copy replayed to anyone else fails: wrong audience,\n * unknown nonce, and nobody else has the proof key.\n *\n * Providers (browser side, `packages/browser/src/proofs/`) only know how to produce and check the\n * evidence over the statement bytes; everything here is shared, so every provider signs the same bytes.\n */\n\nexport const IDENTITY_PROOF_CAPABILITY = \"identity-proof/1\";\n/** Domain separation: the first words of every statement. */\nexport const IDENTITY_STATEMENT_PREFIX = \"Ghostly identity proof v1\";\n/** No binding is valid for longer than this, whatever the provider allows. */\nexport const IDENTITY_MAX_VALIDITY = 400 * 86_400;\n/** A binding issued this far in the future is refused (seconds of clock disagreement tolerated). */\nexport const IDENTITY_CLOCK_SKEW = 300;\n/** A contact's challenge must be answered within this many seconds. */\nexport const IDENTITY_CHALLENGE_WINDOW = 300;\n/** Evidence, serialized as JSON, is at most this many characters. */\nexport const IDENTITY_MAX_EVIDENCE = 16 * 1024;\n/** A whole `idp-*` frame, serialized. */\nexport const IDENTITY_MAX_FRAME = 32 * 1024;\n/** Unanswered challenges a verifier keeps per conversation. */\nconst MAX_CHALLENGES = 8;\n\nexport const IDENTITY_PROVIDER_ID = /^[a-z][a-z0-9-]{0,31}$/;\n/** Printable ASCII without spaces, so the statement can be built but never needs parsing. */\nexport const IDENTITY_SUBJECT = /^[\\x21-\\x7e]{1,512}$/;\nconst KEY = /^[ybndrfg8ejkmcpqxot1uwisza345h769]{52}$/;\nconst BINDING_NONCE = /^[A-Za-z0-9_-]{22}$/;\nconst CHALLENGE_NONCE = /^[A-Za-z0-9_-]{43}$/;\nconst HEX64 = /^[a-f0-9]{64}$/;\nconst SIG = /^[A-Za-z0-9_-]{86}$/;\n\n/** What the external identity signs, as fields. `key` is the proof key (z-base-32 Ed25519). */\nexport interface IdentityBinding {\n v: 1;\n provider: string;\n subject: string;\n key: string;\n issuedAt: number;\n expiresAt: number;\n nonce: string;\n}\n\n/** A binding with its exact bytes. Providers sign `text` (UTF-8 `bytes`) and never rebuild it. */\nexport interface IdentityStatement {\n binding: IdentityBinding;\n text: string;\n bytes: Uint8Array;\n /** SHA-256 of `bytes`, lowercase hex: the proof's id everywhere (storage, frames, OIDC nonce input). */\n id: string;\n}\n\n/** What a provider's `verify` established. Stored by the verifier with the evidence. */\nexport interface VerifiedIdentity {\n /** The identity that was proven, canonical. Self-custodied providers: exactly `binding.subject`. */\n subject: string;\n /** How it was checked, for people: \"Nostr signature (BIP-340)\", \"ID token signed by accounts.google.com\". */\n source: string;\n /** Provider-attested only: who vouches for `subject` (an issuer host). */\n attester?: string;\n /** Earlier than the binding's own expiry when the evidence says so (a key or certificate expiring). */\n expiresAt?: number;\n /** Optional name/picture carried by the evidence itself, with where it came from. */\n display?: IdentityDisplay;\n}\n\nexport interface IdentityDisplay {\n name?: string;\n /** A sanitized `data:image/…` URL, never a remote one. */\n avatar?: string;\n /** Where the person can see the identity (a profile page), https only. */\n url?: string;\n /** Where name/picture came from: \"Nostr kind-0 metadata (self-described)\". */\n source: string;\n fetchedAt: number;\n}\n\nexport interface IdentityScope { subject: string; audience: string; context: string; session: string }\n\n/**\n * Early revocation: the proof key publishes, under its own Pkarr key, a TXT record naming the proof it\n * revokes. Anyone holding a copy can look it up by the key in the binding; nothing else is published,\n * and the key is random, so the record says nothing about the identity or its contacts.\n */\nexport const IDENTITY_REVOCATION_LABEL = \"_ghostly-revoked\";\nexport const identityRevocationValue = (id: string, at: number) => `v=1;id=${id};at=${at}`;\n/** True when these records (resolved and signature-checked under the proof key) revoke proof `id`. */\nexport function revokesIdentity(records: { label: string; value: string }[], id: string): boolean {\n return records.some(r => r.label === IDENTITY_REVOCATION_LABEL && new RegExp(`^v=1;id=${id};at=\\\\d{1,12}$`).test(r.value));\n}\n\nconst nowSeconds = () => Math.floor(Date.now() / 1000);\nconst hex = (bytes: Uint8Array) => Array.from(bytes, b => b.toString(16).padStart(2, \"0\")).join(\"\");\nconst iso = (seconds: number) => new Date(seconds * 1000).toISOString().replace(/\\.\\d{3}Z$/, \"Z\");\nconst isInt = (v: unknown): v is number => Number.isSafeInteger(v) && (v as number) > 0;\n\nexport function isIdentityBinding(value: unknown): value is IdentityBinding {\n const b = value as IdentityBinding;\n return !!b && typeof b === \"object\" && !Array.isArray(b) &&\n Object.keys(b).sort().join(\",\") === \"expiresAt,issuedAt,key,nonce,provider,subject,v\" && b.v === 1 &&\n typeof b.provider === \"string\" && IDENTITY_PROVIDER_ID.test(b.provider) &&\n typeof b.subject === \"string\" && IDENTITY_SUBJECT.test(b.subject) &&\n typeof b.key === \"string\" && KEY.test(b.key) && typeof b.nonce === \"string\" && BINDING_NONCE.test(b.nonce) &&\n isInt(b.issuedAt) && isInt(b.expiresAt) && b.expiresAt > b.issuedAt && b.expiresAt - b.issuedAt <= IDENTITY_MAX_VALIDITY &&\n // ISO dates in the text need years 1970–9999.\n b.expiresAt < 253_402_300_800;\n}\n\n/**\n * The canonical statement, one line, so it survives every tool and clipboard (no line-ending\n * ambiguity). Every field is space-free and the order is fixed, so the text is injective.\n */\nexport function identityStatementText(b: IdentityBinding): string {\n if (!isIdentityBinding(b)) throw new Error(\"Invalid identity binding\");\n return `${IDENTITY_STATEMENT_PREFIX}: I control ${b.provider}:${b.subject} and authorize the Ghostly key ${b.key} ` +\n `to present it to contacts I choose, from ${iso(b.issuedAt)} until ${iso(b.expiresAt)}. Nonce: ${b.nonce}`;\n}\n\nexport function identityStatement(binding: IdentityBinding): IdentityStatement {\n const text = identityStatementText(binding);\n const bytes = utf8Encode(text);\n return { binding: { ...binding }, text, bytes, id: hex(sha256(bytes)) };\n}\n\n/** A new binding and the seed of its proof key. The seed stays in this profile; never share it. */\nexport function newIdentityBinding(options: { provider: string; subject: string; validitySeconds: number; now?: number }): { binding: IdentityBinding; seed: Uint8Array } {\n const issuedAt = options.now ?? nowSeconds();\n const seed = randomBytes(32);\n const binding: IdentityBinding = { v: 1, provider: options.provider, subject: options.subject,\n key: identityFromSeed(seed).pubKeyZ32, issuedAt, expiresAt: issuedAt + options.validitySeconds, nonce: toBase64Url(randomBytes(16)) };\n if (!isIdentityBinding(binding)) { seed.fill(0); throw new Error(\"Invalid identity provider, subject or validity\"); }\n return { binding, seed };\n}\n\n/** Bytes the proof key signs for one contact. `scope` is the presenter's view (subject = presenter). */\nexport function identityPresentationBytes(proofId: string, scope: IdentityScope, nonce: string, issuedAt: number): Uint8Array {\n return utf8Encode(JSON.stringify([\"ghostly-identity-presentation\", 1, proofId, scope.subject, scope.audience, scope.context, scope.session, nonce, issuedAt]));\n}\n\nexport function signIdentityPresentation(seed: Uint8Array, proofId: string, scope: IdentityScope, nonce: string, issuedAt: number): string {\n return toBase64Url(sign(identityPresentationBytes(proofId, scope, nonce, issuedAt), seed));\n}\n\nexport function verifyIdentityPresentation(key: string, sig: string, proofId: string, scope: IdentityScope, nonce: string, issuedAt: number): boolean {\n if (typeof sig !== \"string\" || !SIG.test(sig) || !KEY.test(key)) return false;\n return verify(fromBase64Url(sig), identityPresentationBytes(proofId, scope, nonce, issuedAt), publicKeyFromZ32(key));\n}\n\nconst clean = (value: unknown, max: number): string | undefined => {\n if (typeof value !== \"string\") return undefined;\n // eslint-disable-next-line no-control-regex -- Strip control and bidi-override characters from peer-supplied text.\n const text = value.replace(/[\\u0000-\\u001f\\u007f-\\u009f‎‏‪-‮⁦-⁩]/g, \"\").trim();\n return text ? text.slice(0, max) : undefined;\n};\n\n/** Bounds a provider's result so a buggy provider cannot store or show arbitrary data. */\nexport function boundVerifiedIdentity(v: VerifiedIdentity): VerifiedIdentity {\n if (!v || typeof v.subject !== \"string\" || !IDENTITY_SUBJECT.test(v.subject)) throw new Error(\"Provider returned an invalid subject\");\n const source = clean(v.source, 120);\n if (!source) throw new Error(\"Provider did not say how it verified\");\n const out: VerifiedIdentity = { subject: v.subject, source };\n const attester = clean(v.attester, 253);\n if (attester) out.attester = attester;\n if (v.expiresAt !== undefined) { if (!isInt(v.expiresAt)) throw new Error(\"Provider returned an invalid expiry\"); out.expiresAt = v.expiresAt; }\n if (v.display) {\n const d = v.display;\n const display: IdentityDisplay = { source: clean(d.source, 120) ?? \"Self-described\", fetchedAt: isInt(d.fetchedAt) ? d.fetchedAt : nowSeconds() };\n const name = clean(d.name, 64);\n if (name) display.name = name;\n if (typeof d.avatar === \"string\" && d.avatar.length <= 48 * 1024 && /^data:image\\/(png|jpeg);base64,[A-Za-z0-9+/=]+$/.test(d.avatar)) display.avatar = d.avatar;\n if (typeof d.url === \"string\" && d.url.length <= 512) { try { const u = new URL(d.url); if (u.protocol === \"https:\" && !u.username && !u.password) display.url = u.href; } catch { /* dropped */ } }\n out.display = display;\n }\n return out;\n}\n\n// ---------------------------------------------------------------------------------------------\n// Per-conversation exchange\n\n/** Sharer side, one per proof shared in this conversation. */\nexport interface SharedIdentity {\n id: string;\n provider: string;\n subject: string;\n /** queued: the person chose to share, the contact is not connected yet. */\n status: \"queued\" | \"pending\" | \"accepted\" | \"rejected\" | \"withdrawal-pending\" | \"withdrawn\";\n /** Seconds. When the status last changed. */\n at: number;\n error?: string;\n}\n\n/** Verifier side: a contact's proof as this app checked it. */\nexport interface ReceivedIdentity {\n id: string;\n binding: IdentityBinding;\n evidence: unknown;\n verified: VerifiedIdentity;\n /** The participation keys it was presented between (presenter, then this side). */\n presenter: string;\n audience: string;\n context: string;\n verifiedAt: number;\n checkedAt: number;\n /**\n * unconfirmed: a re-check failed (the domain record is gone, the account cannot be confirmed).\n * revoked: its proof key published a revocation (the person removed it from their profile).\n */\n status: \"verified\" | \"withdrawn\" | \"unconfirmed\" | \"revoked\";\n error?: string;\n /** A name/picture looked up on the person's request (Nostr kind-0), with its source. */\n display?: IdentityDisplay;\n}\n\n/** `id`: the proof the request named, kept here only (never sent) so the timeline can settle its entry. */\nexport interface IdentityChallenge { nonce: string; issuedAt: number; provider: string; id?: string }\n\nexport interface IdentityLedger {\n challenges: IdentityChallenge[];\n shared: SharedIdentity[];\n received: ReceivedIdentity[];\n /** What the chat's timeline shows of this exchange (identityTimeline.ts). Local only; absent in older ledgers. */\n timeline?: IdentityTimelineEntry[];\n}\nexport const emptyIdentityLedger = (): IdentityLedger => ({ challenges: [], shared: [], received: [] });\n\nexport interface LocalIdentityProof { statement: IdentityStatement; evidence: unknown; seed: Uint8Array }\n\nexport interface IdentityExchangeOptions {\n /** This side's view of the authenticated channel; throws when there is none. */\n scope(): IdentityScope;\n send(frame: object): void;\n storage: { read(): IdentityLedger; update(change: (ledger: IdentityLedger) => IdentityLedger): Promise<void> };\n /** Providers this app can verify. */\n providers(): readonly string[];\n /** A proof of this profile, by id; undefined once removed. */\n localProof(id: string): LocalIdentityProof | undefined | Promise<LocalIdentityProof | undefined>;\n /** Parses and checks untrusted evidence for a statement (the registry's `verifyIdentity`). */\n verify(statement: IdentityStatement, evidence: unknown): Promise<VerifiedIdentity>;\n /** Looks up the proof key's revocation record. Resolves false when there is none or it cannot be read. */\n revoked?(statement: IdentityStatement): Promise<boolean>;\n now?(): number;\n onError?(error: string): void;\n}\n\nexport type IdentityStatus = \"verified\" | \"expired\" | \"withdrawn\" | \"revoked\" | \"unconfirmed\" | \"previous-key\";\n\n/** What the verifier should show for a received proof right now. */\nexport function receivedIdentityStatus(r: ReceivedIdentity, presenter: string | undefined, audience: string | undefined, now = nowSeconds()): IdentityStatus {\n if (r.presenter !== presenter || r.audience !== audience) return \"previous-key\";\n if (r.status === \"withdrawn\") return \"withdrawn\";\n if (r.status === \"revoked\") return \"revoked\";\n if (Math.min(r.binding.expiresAt, r.verified.expiresAt ?? Infinity) <= now) return \"expired\";\n return r.status === \"unconfirmed\" ? \"unconfirmed\" : \"verified\";\n}\n\nconst shortError = (e: unknown) => clean(e instanceof Error ? e.message : String(e), 160) ?? \"Invalid proof\";\n\n/**\n * A check this side could not finish because of what its own network did: a host it refused to contact, a server\n * that did not answer or answered an error. The message is for this side only. The contact is told the proof could\n * not be checked, never which host, address or status was seen, so a proof cannot make a contact's app report on\n * the network it sits in.\n */\nexport class IdentityCheckUnavailable extends Error {\n constructor(message: string) { super(message); this.name = \"IdentityCheckUnavailable\"; }\n}\n\n/** What the contact is told when the check failed with an IdentityCheckUnavailable. */\nexport const IDENTITY_CHECK_UNAVAILABLE = \"Your contact's app could not check it. Try again later.\";\n\nconst peerError = (e: unknown) => e instanceof IdentityCheckUnavailable ? IDENTITY_CHECK_UNAVAILABLE : shortError(e);\n\n/**\n * The `idp-*` exchange of one conversation. Frames (all JSON, additive, over the paired channel,\n * only when both offers carry `identity-proof/1`):\n *\n * idp-hello {providers} each side, when the channel is ready\n * idp-request {id, provider} sharer → verifier: \"give me a challenge for this proof\"\n * idp-challenge {id, nonce, issuedAt} verifier, after saving the nonce\n * idp-present {nonce, issuedAt, binding, evidence, sig}\n * idp-result {id, ok, error?} verifier, after saving the outcome\n * idp-withdraw {id} / idp-withdrawn {id}\n */\nexport class IdentityExchange {\n private peerProviders: readonly string[] | undefined;\n private requested = new Set<string>();\n constructor(private options: IdentityExchangeOptions) {}\n private now() { return (this.options.now ?? nowSeconds)(); }\n /** The ledger with one more step on the chat's timeline, in milliseconds (a test's clock, in seconds, when given). */\n private log(l: IdentityLedger, event: IdentityTimelineEvent): IdentityLedger {\n return { ...l, timeline: applyIdentityEvent(l.timeline ?? [], event, this.options.now ? this.now() * 1000 : Date.now()) };\n }\n private incomingScope(): IdentityScope { const s = this.options.scope(); return { ...s, subject: s.audience, audience: s.subject }; }\n private send(frame: object) {\n if (JSON.stringify(frame).length > IDENTITY_MAX_FRAME) throw new Error(\"Identity proof too large\");\n this.options.send(frame);\n }\n private shared(id: string) { return this.options.storage.read().shared.find(s => s.id === id); }\n private async setShared(entry: SharedIdentity, event?: IdentityTimelineEvent) {\n await this.options.storage.update(l => {\n const next = { ...l, shared: [...l.shared.filter(s => s.id !== entry.id), entry] };\n return event ? this.log(next, event) : next;\n });\n }\n\n /** The providers the contact said it verifies; undefined until its hello. */\n get contactProviders(): readonly string[] | undefined { return this.peerProviders; }\n\n /** The channel is ready: say what we verify, then send what the person queued while offline. */\n ready(): void {\n this.options.scope();\n this.send({ t: \"idp-hello\", providers: [...this.options.providers()] });\n for (const s of this.options.storage.read().shared) {\n if (s.status === \"queued\" || s.status === \"pending\") this.request(s);\n else if (s.status === \"withdrawal-pending\") this.send({ t: \"idp-withdraw\", id: s.id });\n }\n }\n stop(): void { this.requested.clear(); this.peerProviders = undefined; }\n\n /** Share a proof of this profile with this contact: now if connected, else when it next is. */\n async share(id: string, connected: boolean): Promise<void> {\n const local = await this.options.localProof(id);\n if (!local) throw new Error(\"That identity is no longer in your profile\");\n const { provider, subject } = local.statement.binding;\n const entry: SharedIdentity = { id, provider, subject, status: \"queued\", at: this.now() };\n await this.setShared(entry, { type: \"shared\", side: \"mine\", proof: id, provider, subject });\n if (connected) this.request(entry);\n }\n private request(s: SharedIdentity) {\n this.requested.add(s.id);\n this.send({ t: \"idp-request\", id: s.id, provider: s.provider });\n }\n\n /** Stop sharing here. The contact is told (now or on reconnect); a copy it kept cannot be erased. */\n async withdraw(id: string, connected: boolean): Promise<void> {\n const s = this.shared(id);\n if (!s || s.status === \"withdrawn\") return;\n this.requested.delete(id);\n if (s.status === \"queued\") { await this.options.storage.update(l => this.log({ ...l, shared: l.shared.filter(x => x.id !== id) }, { type: \"unsent\", side: \"mine\", proof: id })); return; }\n // One the contact refused, it never held: its app writes no stop, and neither does this side.\n await this.setShared({ ...s, status: \"withdrawal-pending\", at: this.now(), error: undefined },\n s.status === \"rejected\" ? undefined : { type: \"stopped\", side: \"mine\", proof: id, provider: s.provider, subject: s.subject, reason: \"withdrawn\" });\n if (connected) this.send({ t: \"idp-withdraw\", id });\n }\n\n /**\n * Run the checks again on a received proof: its revocation record, then (unless `revocationOnly`) the\n * provider's check (a domain record may be gone).\n */\n async recheck(id: string, { revocationOnly = false } = {}): Promise<ReceivedIdentity> {\n const r = this.options.storage.read().received.find(x => x.id === id);\n if (!r) throw new Error(\"Unknown identity\");\n if (r.status === \"revoked\") return r;\n let next: ReceivedIdentity;\n const settled = r.status === \"withdrawn\";\n if (await this.options.revoked?.(identityStatement(r.binding)).catch(() => false)) {\n next = { ...r, status: \"revoked\", checkedAt: this.now(), error: undefined };\n await this.options.storage.update(l => this.log({ ...l, received: l.received.map(x => x.id === id ? next : x) },\n { type: \"stopped\", side: \"theirs\", proof: id, provider: r.binding.provider, subject: r.verified.subject, reason: \"revoked\" }));\n return next;\n }\n if (revocationOnly) return r;\n try {\n const verified = boundVerifiedIdentity(await this.options.verify(identityStatement(r.binding), r.evidence));\n if (verified.subject !== r.verified.subject) throw new Error(\"The identity changed\");\n next = { ...r, verified: { ...verified, display: verified.display ?? r.verified.display }, checkedAt: this.now(), status: settled ? \"withdrawn\" : \"verified\", error: undefined };\n } catch (e) {\n next = { ...r, checkedAt: this.now(), status: settled ? \"withdrawn\" : \"unconfirmed\", error: shortError(e) };\n }\n await this.options.storage.update(l => ({ ...l, received: l.received.map(x => x.id === id ? next : x) }));\n return next;\n }\n\n async receive(frame: Record<string, unknown>): Promise<void> {\n try {\n if (JSON.stringify(frame).length > IDENTITY_MAX_FRAME) throw new Error(\"Identity proof too large\");\n const id = typeof frame.id === \"string\" && HEX64.test(frame.id) ? frame.id : undefined;\n switch (frame.t) {\n case \"idp-hello\":\n if (Array.isArray(frame.providers) && frame.providers.length <= 64)\n this.peerProviders = frame.providers.filter((p): p is string => typeof p === \"string\" && IDENTITY_PROVIDER_ID.test(p));\n return;\n case \"idp-request\": return await this.onRequest(id, frame.provider);\n case \"idp-challenge\": return await this.onChallenge(id, frame.nonce, frame.issuedAt);\n case \"idp-present\": return await this.onPresent(frame);\n case \"idp-result\": {\n const s = id ? this.shared(id) : undefined;\n if (!s || s.status !== \"pending\") return;\n const error = frame.ok === true ? undefined : clean(frame.error, 160) ?? \"Your contact could not verify it\";\n await this.setShared({ ...s, status: frame.ok === true ? \"accepted\" : \"rejected\", at: this.now(), error },\n { type: \"result\", side: \"mine\", proof: s.id, ok: frame.ok === true, error });\n return;\n }\n case \"idp-withdraw\":\n if (!id) return;\n await this.options.storage.update(l => {\n const r = l.received.find(x => x.id === id);\n const next = { ...l, received: l.received.map(x => x.id === id ? { ...x, status: \"withdrawn\" as const } : x) };\n return r && r.status !== \"withdrawn\"\n ? this.log(next, { type: \"stopped\", side: \"theirs\", proof: id, provider: r.binding.provider, subject: r.verified.subject, reason: \"withdrawn\" })\n : next;\n });\n this.send({ t: \"idp-withdrawn\", id });\n return;\n case \"idp-withdrawn\": {\n const s = id ? this.shared(id) : undefined;\n if (s?.status === \"withdrawal-pending\") await this.setShared({ ...s, status: \"withdrawn\", at: this.now() });\n return;\n }\n }\n } catch (e) { this.options.onError?.(shortError(e)); }\n }\n\n private async onRequest(id: string | undefined, provider: unknown) {\n if (!id) return;\n if (typeof provider !== \"string\" || !this.options.providers().includes(provider)) {\n const error = \"Your contact's app cannot verify this kind of identity yet\";\n // Shown here too, as a share that failed: a kind this app does not know, named as the contact's app named it.\n if (typeof provider === \"string\" && IDENTITY_PROVIDER_ID.test(provider)) {\n await this.options.storage.update(l => this.log(this.log(l, { type: \"shared\", side: \"theirs\", proof: id, provider }),\n { type: \"result\", side: \"theirs\", proof: id, provider, ok: false, error: \"This app cannot verify this kind of identity yet\" }));\n }\n this.send({ t: \"idp-result\", id, ok: false, error });\n return;\n }\n this.options.scope();\n const now = this.now();\n const challenge: IdentityChallenge = { nonce: toBase64Url(randomBytes(32)), issuedAt: now, provider, id };\n // Saved before it is sent: a presentation for a nonce this side never saved is always refused.\n await this.options.storage.update(l => {\n const live = l.challenges.filter(c => c.issuedAt > now - IDENTITY_CHALLENGE_WINDOW);\n if (live.length >= MAX_CHALLENGES) throw new Error(\"Too many identity challenges\");\n return this.log({ ...l, challenges: [...live, challenge] }, { type: \"shared\", side: \"theirs\", proof: id, provider });\n });\n this.send({ t: \"idp-challenge\", id, nonce: challenge.nonce, issuedAt: challenge.issuedAt });\n }\n\n private async onChallenge(id: string | undefined, nonce: unknown, issuedAt: unknown) {\n if (!id || !this.requested.has(id) || typeof nonce !== \"string\" || !CHALLENGE_NONCE.test(nonce) || !isInt(issuedAt)) return;\n const s = this.shared(id);\n if (!s || (s.status !== \"queued\" && s.status !== \"pending\")) return;\n this.requested.delete(id);\n const local = await this.options.localProof(id);\n if (!local) {\n const error = \"That identity is no longer in your profile\";\n await this.setShared({ ...s, status: \"rejected\", at: this.now(), error }, { type: \"result\", side: \"mine\", proof: id, ok: false, error });\n return;\n }\n const scope = this.options.scope();\n const sig = signIdentityPresentation(local.seed, id, scope, nonce, issuedAt);\n await this.setShared({ ...s, status: \"pending\", at: this.now(), error: undefined });\n this.send({ t: \"idp-present\", nonce, issuedAt, binding: local.statement.binding, evidence: local.evidence, sig });\n }\n\n private async onPresent(frame: Record<string, unknown>) {\n const { nonce, issuedAt, binding, evidence, sig } = frame;\n if (typeof nonce !== \"string\" || !CHALLENGE_NONCE.test(nonce) || !isInt(issuedAt) || !isIdentityBinding(binding)) return;\n const statement = identityStatement(binding);\n const id = statement.id;\n const scope = this.incomingScope();\n const now = this.now();\n const challengeOf = (l: IdentityLedger) => l.challenges.find(c => c.nonce === nonce && c.issuedAt === issuedAt && c.provider === binding.provider && c.issuedAt > now - IDENTITY_CHALLENGE_WINDOW);\n const known = (l: IdentityLedger) => !!challengeOf(l);\n if (!known(this.options.storage.read())) throw new Error(\"Unknown or reused identity challenge\");\n let verified: VerifiedIdentity | undefined;\n // Only what the contact may know: see IdentityCheckUnavailable.\n let error: string | undefined;\n /** The same, as this side's own timeline says it. */\n let reason: string | undefined;\n try {\n if (JSON.stringify(evidence ?? null).length > IDENTITY_MAX_EVIDENCE) throw new Error(\"Evidence too large\");\n if (!verifyIdentityPresentation(binding.key, sig as string, id, scope, nonce, issuedAt)) throw new Error(\"The proof key did not sign this conversation's challenge\");\n if (binding.issuedAt > now + IDENTITY_CLOCK_SKEW) throw new Error(\"The proof is dated in the future\");\n const result = boundVerifiedIdentity(await this.options.verify(statement, evidence));\n if (Math.min(binding.expiresAt, result.expiresAt ?? Infinity) <= now) throw new Error(\"The proof has expired\");\n // A presentation needs the proof key, so this only matters when its seed leaked: a lookup that\n // fails is not a refusal, a revocation found is.\n if (await this.options.revoked?.(statement).catch(() => false)) throw new Error(\"Its owner revoked this proof\");\n verified = result;\n } catch (e) { error = peerError(e); reason = shortError(e); }\n // Consuming the nonce and recording the outcome are one transaction.\n await this.options.storage.update(l => {\n const challenge = challengeOf(l);\n if (!challenge) throw new Error(\"Unknown or reused identity challenge\");\n const challenges = l.challenges.filter(c => c.nonce !== nonce);\n const logged = this.log(l, { type: \"result\", side: \"theirs\", proof: id, requested: challenge.id, provider: binding.provider,\n subject: verified?.subject ?? binding.subject, ok: !!verified, ...(reason ? { error: reason } : {}) });\n if (!verified) return { ...logged, challenges };\n const record: ReceivedIdentity = { id, binding, evidence, verified, presenter: scope.subject, audience: scope.audience, context: scope.context,\n verifiedAt: now, checkedAt: now, status: \"verified\" };\n // A newer proof of the same identity replaces the older one.\n const received = l.received.filter(r => r.id !== id && !(r.binding.provider === binding.provider && r.verified.subject === verified!.subject));\n return { ...logged, challenges, received: [...received, record] };\n });\n this.send(verified ? { t: \"idp-result\", id, ok: true } : { t: \"idp-result\", id, ok: false, error });\n }\n}\n","import { ed25519, x25519 } from \"@noble/curves/ed25519.js\";\nimport { hkdf } from \"@noble/hashes/hkdf.js\";\nimport { sha256 } from \"@noble/hashes/sha2.js\";\nimport { fromBase64Url, toBase64Url, utf8Encode } from \"./bytes\";\nimport { encrypt, tryDecrypt } from \"./crypto\";\nimport { identityFromSeed, identityFromSeedB64, publicKeyFromZ32, sign, verify, type Identity } from \"./identity\";\nimport type { LinkParams } from \"./invite\";\nimport type { PairingCredentials } from \"./pairedSession\";\nimport type { TransportDescriptors } from \"./pairedTransports\";\nimport { measureRecords, MAX_DNS_PACKET_BYTES, type GhostRecord, type SignedPacket } from \"./pkarr\";\nimport { budgetRetryMs, isDiscoveryBudgetError, type PkarrTransport } from \"./transport\";\n\n/**\n * The layer-0 capability record of a chat (WISP 03, revision 0.2): what each side accepts on the DHT\n * (`dht-text/1`, `hold/1`), what its layer 1 would carry, the minimum to dial its native transports\n * without a WebRTC session first, and the name it shares. One Pkarr key per direction, derived from the\n * invite secret like the DHT mailboxes, so its size never competes with a text.\n *\n * Sealed with the invitation-derived key until the contact is pinned, and with a key only the two\n * participation keys derive afterwards, so a copied invite stops reading it. Always signed by the\n * author's participation key over both rendezvous keys.\n */\nexport const DHT_TEXT_CAPABILITY = \"dht-text/1\" as const;\nexport const CAPS_LABEL = \"_caps\";\nexport const CAPS_LIMITS = {\n versions: 8,\n transports: 8,\n capabilities: 32,\n extensions: 32,\n /** One identifier, capability or extension. */\n itemChars: 48,\n nameBytes: 64,\n} as const;\n/**\n * How far before its own clock an author dates a record (`issued`). No reader orders records by that time, `rev` does,\n * and this one refuses none for it. Apps up to 1.0.2 refuse a record dated more than a minute past their clock: one\n * dated by a clock two minutes ahead was never read by them, with nothing on screen, and the chat could not dial its\n * contact's native transports before a session. Dated back, a record from a clock up to about eleven minutes ahead is\n * taken by them too.\n */\nexport const CAPS_ISSUED_BACK_MS = 10 * 60_000;\n/** Published again this often while the chat exists, so it does not age out of the DHT. */\nexport const CAPS_REFRESH_MS = 60 * 60_000;\n/** Reads of the contact's record closer together than this are merged into one. */\nconst CAPS_READ_SPACING_MS = 15_000;\n/**\n * An envelope named a revision of the contact's record and the read found an older one, or none: read again this long\n * after each read, while still behind. A DHT read directly (Desktop, CLI, relay reads off) hands back the copy it\n * already knows and looks the key up behind it, and a lookup that started before the publication landed finds the old\n * one; a relay may serve an old copy for a while too. The envelope names each revision once, so one read was the last\n * until the next envelope, minutes away, and the chat stayed on the DHT with no descriptors to dial.\n */\nconst CAPS_READ_RETRY_MS = [5_000, 15_000, 30_000, 60_000] as const;\nconst NATIVE = [\"iroh/1\", \"hyperdht/1\"] as const;\n/** The native transports a record says how to dial. */\nconst described = (content: CapsContent) => NATIVE.filter(t => !!content.descriptors[t]);\n/**\n * Changes closer together than this go out as one publication, the last: every publication spends one of the\n * relays' requests per relay, a budget the chat's signaling needs more.\n */\nexport const CAPS_PUBLISH_SPACING_MS = 30_000;\n\n/**\n * Per native transport, the minimum to dial it: no network address, ever. Keys travel as base64url (32\n * bytes, 43 characters) to leave the packet room for capabilities.\n */\nexport interface CapsDescriptors {\n \"iroh/1\"?: { id: string; relay?: string };\n /** `relay`: a browser's HyperDHT is reached through a relay server, which it names (a public service, not its address). */\n \"hyperdht/1\"?: { publicKey: string; relay?: string };\n}\nexport interface CapsContent {\n versions: number[];\n /** Layer-1 transports this runtime has, in local preference order. Never the DHT. */\n transports: string[];\n capabilities: string[];\n extensions: string[];\n descriptors: CapsDescriptors;\n /** The name this profile shares with contacts; empty when it shares none. */\n name: string;\n /**\n * The layer-1 transport chosen for this chat in its Connection menu, absent on Automatic (WISP 100, \"A choice made\n * while not live\"). It reaches a contact that has no session to hear a switch intent on. A trailing element: apps\n * from before it ignore it.\n */\n choice?: string;\n}\nexport interface CapsRecord extends CapsContent {\n rev: number;\n issued: number;\n /** The participation key that signed it. */\n author: string;\n}\ntype Body = [version: 1, rev: number, issued: number, author: string, versions: number[], transports: string[], capabilities: string[],\n extensions: string[], descriptors: CapsDescriptors, name: string, choice?: string];\n\nexport type CapsRefusal = \"address\" | \"size\" | \"sealed\" | \"format\" | \"signature\" | \"author\" | \"rev\";\n/** Why a record was not used. A refused record leaves the last good one in force. */\nexport class CapsRefusedError extends Error {\n constructor(readonly reason: CapsRefusal, message: string) { super(message); }\n}\n\nconst IDENT = /^[a-z0-9][a-z0-9._-]*\\/[0-9]+$/;\nconst HEX64 = /^[0-9a-f]{64}$/;\nconst SIG = /^[A-Za-z0-9_-]{86}$/;\nconst Z32 = /^[ybndrfg8ejkmcpqxot1uwisza345h769]{52}$/;\nconst KEY = /^[A-Za-z0-9_-]{43}$/;\n/**\n * A relay server's URL, short: https, or wss for a browser's relay; plain http or ws only on this machine's loopback,\n * as a test relay is (the same rule as the relay settings).\n */\nconst RELAY = /^((https|wss):\\/\\/[^\\s]{1,120}|(http|ws):\\/\\/(127\\.0\\.0\\.1|localhost|\\[::1\\])(:\\d{1,5})?(\\/[^\\s]{0,100})?)$/;\nconst hexToKey = (hex: string) => toBase64Url(Uint8Array.from(hex.match(/../g)!, b => parseInt(b, 16)));\nconst keyToHex = (key: string) => Array.from(fromBase64Url(key), b => b.toString(16).padStart(2, \"0\")).join(\"\");\n\n/**\n * The part of the native descriptors a capability record may carry: the Iroh endpoint id (and the relay\n * it is reachable through, a public service rather than an address of this device), the HyperDHT key.\n */\nexport function capsDescriptors(descriptors: TransportDescriptors | undefined): CapsDescriptors {\n const out: CapsDescriptors = {};\n const iroh = descriptors?.[\"iroh/1\"] as { id?: unknown; relay?: unknown } | undefined;\n if (iroh && typeof iroh.id === \"string\" && HEX64.test(iroh.id))\n out[\"iroh/1\"] = { id: hexToKey(iroh.id), ...(typeof iroh.relay === \"string\" && RELAY.test(iroh.relay) ? { relay: iroh.relay } : {}) };\n const hyper = descriptors?.[\"hyperdht/1\"] as { publicKey?: unknown; relay?: unknown } | undefined;\n if (hyper && typeof hyper.publicKey === \"string\" && HEX64.test(hyper.publicKey))\n out[\"hyperdht/1\"] = { publicKey: hexToKey(hyper.publicKey), ...(typeof hyper.relay === \"string\" && RELAY.test(hyper.relay) ? { relay: hyper.relay } : {}) };\n return out;\n}\n\n/**\n * A record's descriptors as the transport adapters dial them. With no address in the record, an Iroh path is\n * relayed; a HyperDHT that names a relay (a browser's) is too: both rank after direct paths and before the DHT\n * floor (WISP 100, \"Relayed\"), and a first contact can dial them before WebRTC has failed.\n */\nexport function dialDescriptors(descriptors: CapsDescriptors): TransportDescriptors {\n const out: TransportDescriptors = {};\n const iroh = descriptors[\"iroh/1\"], hyper = descriptors[\"hyperdht/1\"];\n if (iroh) out[\"iroh/1\"] = { id: keyToHex(iroh.id), relay: iroh.relay ?? null, addresses: [], relayed: true };\n if (hyper) out[\"hyperdht/1\"] = { publicKey: keyToHex(hyper.publicKey), ...(hyper.relay ? { relay: hyper.relay, relayed: true } : {}) };\n return out;\n}\n\nconst list = (value: unknown, max: number, item: (v: unknown) => boolean): boolean =>\n Array.isArray(value) && value.length <= max && value.every(item);\nconst ident = (v: unknown) => typeof v === \"string\" && v.length <= CAPS_LIMITS.itemChars && IDENT.test(v);\n\nfunction parseDescriptors(value: unknown): CapsDescriptors | null {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) return null;\n // Descriptors of transports this reader does not know are skipped, not a reason to refuse the record.\n const raw = value as Record<string, { id?: unknown; relay?: unknown; publicKey?: unknown } | undefined>;\n const out: CapsDescriptors = {};\n const iroh = raw[\"iroh/1\"], hyper = raw[\"hyperdht/1\"];\n if (iroh !== undefined) {\n if (!iroh || typeof iroh.id !== \"string\" || !KEY.test(iroh.id) || (iroh.relay !== undefined && (typeof iroh.relay !== \"string\" || !RELAY.test(iroh.relay)))) return null;\n out[\"iroh/1\"] = { id: iroh.id, ...(iroh.relay !== undefined ? { relay: iroh.relay as string } : {}) };\n }\n if (hyper !== undefined) {\n if (!hyper || typeof hyper.publicKey !== \"string\" || !KEY.test(hyper.publicKey) || (hyper.relay !== undefined && (typeof hyper.relay !== \"string\" || !RELAY.test(hyper.relay)))) return null;\n out[\"hyperdht/1\"] = { publicKey: hyper.publicKey, ...(hyper.relay !== undefined ? { relay: hyper.relay as string } : {}) };\n }\n return out;\n}\n\n/**\n * A group link's transports in its own packet (`_tr`, WISP 9xx § Transports): a group's edge or entry session has no\n * capability record, so an app with no WebRTC (the Linux Desktop) says there which layer-1 transports it runs and how to\n * dial its native ones, and a member's app that has WebRTC answers in kind. The same minimum a record carries: keys and\n * relays, never an address. Sealed with the link key like every other label of the packet.\n */\nexport interface PacketTransports {\n transports: PairedTransportName[];\n descriptors: CapsDescriptors;\n}\ntype PairedTransportName = \"iroh/1\" | \"hyperdht/1\" | \"webrtc/1\";\nconst PACKET_TRANSPORTS: readonly string[] = [\"iroh/1\", \"hyperdht/1\", \"webrtc/1\"];\n\n/** The `_tr` value: `{\"t\": transports, \"d\": descriptors}`, versionless and additive (an unknown field is ignored). */\nexport function encodePacketTransports(transports: readonly string[], descriptors: TransportDescriptors | undefined): string {\n return JSON.stringify({ t: transports.filter(t => PACKET_TRANSPORTS.includes(t)), d: capsDescriptors(descriptors) });\n}\n\n/** A `_tr` value read from a packet; null when it is not one (a reader drops it, as a record it cannot read). */\nexport function parsePacketTransports(json: string | null): PacketTransports | null {\n if (!json || json.length > 1_000) return null;\n try {\n const value = JSON.parse(json) as { t?: unknown; d?: unknown };\n if (!value || typeof value !== \"object\" || !Array.isArray(value.t) || value.t.length > CAPS_LIMITS.transports) return null;\n const transports = value.t.filter((t): t is PairedTransportName => typeof t === \"string\" && PACKET_TRANSPORTS.includes(t));\n const descriptors = parseDescriptors(value.d ?? {});\n if (!descriptors) return null;\n return { transports: [...new Set(transports)], descriptors };\n } catch { return null; }\n}\n\n/** Keys of the capability record of one link: my record's key and seals, the contact's address. */\nexport class CapsKeys {\n readonly from: string;\n readonly to: string;\n readonly me: string;\n /** Publishes my record. */\n readonly identity: Identity;\n /** Where the contact publishes its record. */\n readonly peerAddress: string;\n private readonly seed: Uint8Array;\n private readonly inviteKey: Uint8Array;\n\n constructor(params: LinkParams, participationSeedB64: string) {\n this.from = identityFromSeedB64(params.seedB64).pubKeyZ32;\n this.to = params.peerPubKeyZ32;\n const mine = identityFromSeedB64(participationSeedB64);\n this.seed = mine.seed;\n this.me = mine.pubKeyZ32;\n const secret = fromBase64Url(params.encKeyB64);\n const context = JSON.stringify([\"ghostly-caps/1\", [this.from, this.to].sort()]);\n const derive = (label: string) => hkdf(sha256, secret, utf8Encode(context), utf8Encode(label), 32);\n this.identity = identityFromSeed(derive(`caps:${this.from}`));\n this.peerAddress = identityFromSeed(derive(`caps:${this.to}`)).pubKeyZ32;\n this.inviteKey = derive(\"envelope\");\n }\n\n private pinnedKey(peer: string): Uint8Array {\n const shared = x25519.getSharedSecret(ed25519.utils.toMontgomerySecret(this.seed), ed25519.utils.toMontgomery(publicKeyFromZ32(peer)));\n return hkdf(sha256, shared, this.inviteKey, utf8Encode(\"ghostly-caps-participation/1\"), 32);\n }\n private signable(from: string, to: string, body: unknown[]): Uint8Array { return utf8Encode(JSON.stringify([\"ghostly-caps\", from, to, body])); }\n\n /**\n * My record as Pkarr records, sealed for the pinned contact (or with the invite key before the pin).\n * Past 1,000 bytes the name goes first, then the extensions; capabilities and transports are never cut.\n */\n seal(content: CapsContent, rev: number, peerKey?: string, now = Date.now()): { records: GhostRecord[]; dropped: (\"name\" | \"extensions\")[] } {\n const dropped: (\"name\" | \"extensions\")[] = [];\n let name = content.name, extensions = content.extensions;\n for (;;) {\n const body: Body = [1, rev, now - CAPS_ISSUED_BACK_MS, this.me, content.versions, content.transports, content.capabilities, extensions, content.descriptors, name,\n ...(content.choice ? [content.choice] : [])] as Body;\n const signature = toBase64Url(sign(this.signable(this.from, this.to, body), this.seed));\n const records = [{ label: CAPS_LABEL, value: encrypt(JSON.stringify([body, signature]), peerKey ? this.pinnedKey(peerKey) : this.inviteKey), ttl: 3600 }];\n if (measureRecords(this.identity.pubKeyZ32, records) <= MAX_DNS_PACKET_BYTES) return { records, dropped };\n if (name) { name = \"\"; dropped.push(\"name\"); continue; }\n if (extensions.length) { extensions = []; dropped.push(\"extensions\"); continue; }\n throw new CapsRefusedError(\"size\", \"The capability record does not fit a DHT packet even without its name and extensions.\");\n }\n }\n\n /**\n * The contact's record, checked: its address, its seal, a well-formed body, a signature by the pinned\n * participation key (before the pin, by `expected` when a first contact named one) and not older than `minRev`.\n *\n * Its `issued` is the author's word on its own clock and refuses nothing. A record is the author's alone (signed by\n * its participation key, bound to both rendezvous keys), and an older one cannot take a newer one's place because of\n * `rev`, which the reader keeps. Refusing a record dated over a minute ahead added nothing to that, and cost every\n * contact whose clock runs a couple of minutes fast its record.\n */\n open(packet: SignedPacket, options: { pinned?: string; expected?: string; minRev?: number } = {}): CapsRecord {\n if (packet.pubKeyZ32 !== this.peerAddress) throw new CapsRefusedError(\"address\", \"Not this contact's capability record.\");\n if (measureRecords(packet.pubKeyZ32, packet.records) > MAX_DNS_PACKET_BYTES) throw new CapsRefusedError(\"size\", \"Capability record over the DHT packet budget.\");\n const sealed = packet.records.filter(r => r.label === CAPS_LABEL);\n if (sealed.length !== 1) throw new CapsRefusedError(\"format\", \"No single capability record.\");\n const plaintext = (options.pinned ? tryDecrypt(sealed[0].value, this.pinnedKey(options.pinned)) : null) ?? tryDecrypt(sealed[0].value, this.inviteKey);\n if (!plaintext) throw new CapsRefusedError(\"sealed\", \"The capability record cannot be opened with this chat's keys.\");\n let envelope: unknown;\n try { envelope = JSON.parse(plaintext); } catch { throw new CapsRefusedError(\"format\", \"Malformed capability record.\"); }\n if (!Array.isArray(envelope) || envelope.length !== 2 || !Array.isArray(envelope[0]) || typeof envelope[1] !== \"string\" || !SIG.test(envelope[1]))\n throw new CapsRefusedError(\"format\", \"Malformed capability record.\");\n const body = envelope[0] as unknown[], signature = envelope[1];\n // Trailing elements a later revision may add are carried by the signature and otherwise ignored.\n if (body.length < 10 || body.length > 16) throw new CapsRefusedError(\"format\", \"Malformed capability record.\");\n const [version, rev, issued, author, versions, transports, capabilities, extensions, rawDescriptors, name, choice] = body;\n const descriptors = parseDescriptors(rawDescriptors);\n if (version !== 1 || !Number.isSafeInteger(rev) || (rev as number) < 0 || !Number.isSafeInteger(issued) || (issued as number) <= 0 ||\n typeof author !== \"string\" || !Z32.test(author) ||\n !list(versions, CAPS_LIMITS.versions, v => Number.isSafeInteger(v) && (v as number) > 0) ||\n !list(transports, CAPS_LIMITS.transports, ident) || (transports as string[]).includes(\"dht/1\") ||\n !list(capabilities, CAPS_LIMITS.capabilities, ident) || !list(extensions, CAPS_LIMITS.extensions, ident) ||\n !descriptors || typeof name !== \"string\" || utf8Encode(name).length > CAPS_LIMITS.nameBytes ||\n (choice !== undefined && (!ident(choice) || choice === \"dht/1\")))\n throw new CapsRefusedError(\"format\", \"Malformed capability record.\");\n const signer = options.pinned ?? options.expected;\n if (signer && author !== signer) throw new CapsRefusedError(\"author\", \"The capability record is signed by another participation key than this contact's.\");\n try {\n if (!verify(fromBase64Url(signature), this.signable(this.to, this.from, body), publicKeyFromZ32(author)))\n throw new CapsRefusedError(\"signature\", \"The capability record's signature does not verify.\");\n } catch (error) {\n if (error instanceof CapsRefusedError) throw error;\n throw new CapsRefusedError(\"signature\", \"The capability record's signature does not verify.\");\n }\n if (options.minRev !== undefined && (rev as number) < options.minRev) throw new CapsRefusedError(\"rev\", \"An older capability record than one already seen.\");\n return { rev: rev as number, issued: issued as number, author, versions: versions as number[], transports: transports as string[],\n capabilities: capabilities as string[], extensions: extensions as string[], descriptors, name, ...(choice !== undefined ? { choice: choice as string } : {}) };\n }\n}\n\nexport interface CapsState {\n /** My record's revision: increases with every change of its content. */\n rev: number;\n /**\n * A digest of the content of revision `rev`, and when it was last published, and whether sealed for the pinned\n * contact. No `publishedAt`: that revision is not known to be out (a publication failed, or was cut short), and goes\n * out at the next look.\n */\n digest?: string;\n publishedAt?: number;\n sealedFor?: string;\n /** The contact's last good record. */\n peer?: CapsRecord;\n}\nexport const emptyCapsState = (): CapsState => ({ rev: 0 });\n\nconst digestOf = (content: CapsContent) => toBase64Url(sha256(utf8Encode(JSON.stringify(content)))).slice(0, 22);\n\n/**\n * Publishes this side's record (at first start, at every change, hourly) and reads the contact's (at\n * pairing, when an envelope names a newer revision, when the chat drops to the DHT). Every read and\n * publish goes through one queue; reads close together are merged.\n */\nexport class CapsExchange {\n private state: CapsState;\n private readonly keys: CapsKeys;\n private chain: Promise<unknown> = Promise.resolve();\n private timer: ReturnType<typeof setTimeout> | null = null;\n private running = false;\n private lastRead = 0;\n private readTimer: ReturnType<typeof setTimeout> | null = null;\n /** The newest revision of the contact's record an envelope named, and the reads made again since for it. */\n private named = -1;\n private retries = 0;\n private retryTimer: ReturnType<typeof setTimeout> | null = null;\n /** What the last publication of this run carried. */\n private lastOut?: CapsContent;\n private dropped: (\"name\" | \"extensions\")[] = [];\n\n constructor(private readonly options: {\n params: LinkParams;\n credentials: PairingCredentials;\n transport: PkarrTransport;\n local(): CapsContent;\n state?: CapsState;\n save(state: CapsState): Promise<void>;\n /** The contact's record changed (a newer revision was read). */\n changed?(record: CapsRecord): void;\n /** A new revision of this side's record went out: the contact learns of it from an envelope that names it. */\n published?(rev: number): void;\n /** A record was refused; `author` is a security signal (another key signed under this chat's address). */\n refused?(error: CapsRefusedError): void;\n }) {\n this.state = structuredClone(options.state ?? emptyCapsState());\n this.keys = new CapsKeys(options.params, options.credentials.seedB64);\n }\n\n get rev(): number { return this.state.rev; }\n get peer(): CapsRecord | undefined { return this.state.peer; }\n /** What had to be left out to fit the packet at the last publication. */\n get droppedFields(): readonly (\"name\" | \"extensions\")[] { return this.dropped; }\n get address(): string { return this.keys.identity.pubKeyZ32; }\n\n private serialize<T>(run: () => Promise<T>): Promise<T> {\n const next = this.chain.then(run); this.chain = next.catch(() => {}); return next;\n }\n private async persist(next: CapsState): Promise<void> { await this.options.save(structuredClone(next)); this.state = next; }\n\n start(): void {\n if (this.running || this.ended) return;\n this.running = true;\n void this.update().catch(() => {});\n // Paired, and the contact's record never read: once. After that, a newer revision named in an envelope,\n // a drop or a new pin is what reads it again, not every start. One named before a late start is read now.\n if (this.options.credentials.peerKey && (!this.state.peer || this.named > this.state.peer.rev)) this.refresh(true);\n }\n /** Stopped for good: a start still waiting (the engine starts it once the chat's endpoints are up) does nothing. */\n private ended = false;\n async stop(): Promise<void> {\n this.ended = true;\n this.running = false;\n if (this.timer) clearTimeout(this.timer);\n if (this.readTimer) clearTimeout(this.readTimer);\n if (this.retryTimer) clearTimeout(this.retryTimer);\n this.timer = this.readTimer = this.retryTimer = null;\n await this.chain.catch(() => {});\n }\n\n /**\n * Publishes when the content changed, when the contact was pinned since the last publication (the\n * record is sealed anew for it), or when the last one is an hour old. Otherwise nothing goes out.\n */\n update(): Promise<void> {\n return this.serialize(async () => {\n if (!this.running) return;\n const content = this.options.local(), digest = digestOf(content), now = Date.now();\n const peerKey = this.options.credentials.peerKey;\n const changed = digest !== this.state.digest;\n // A revision saved but not known to be out (its publication failed): it goes now, as it is.\n const unpublished = this.state.digest !== undefined && !this.state.publishedAt;\n const due = changed || peerKey !== this.state.sealedFor || !this.state.publishedAt || now - this.state.publishedAt >= CAPS_REFRESH_MS;\n // A change right after a publication waits for the spacing; a new pin does not (the contact reads it next).\n // Nor does a native transport that can be dialled now and could not at the last one (its endpoint came up after the\n // record went out): the contact waits for it to go live. Once per transport and run, so the budget barely notices.\n const dialable = !!this.lastOut && described(content).some(t => !described(this.lastOut!).includes(t));\n const soon = changed && !dialable && peerKey === this.state.sealedFor && !!this.state.publishedAt && now - this.state.publishedAt < CAPS_PUBLISH_SPACING_MS;\n if (due && soon) { this.schedule(this.state.publishedAt! + CAPS_PUBLISH_SPACING_MS - now); return; }\n if (due) {\n const rev = changed ? this.state.rev + 1 : this.state.rev;\n const { records, dropped } = this.keys.seal(content, rev, peerKey, now);\n this.dropped = dropped;\n // The revision is saved before it is published, so a crash never reuses it for other content; until it is\n // out, it has no `publishedAt`, so a publication that fails (or a crash) does not lose it until the hourly one.\n await this.persist({ ...this.state, rev, digest, sealedFor: peerKey, publishedAt: undefined });\n try { await this.options.transport.publish(this.keys.identity, records, changed || unpublished ? undefined : { background: true }); }\n catch (error) {\n // Held back by the relays' request budget: again when the budget frees a request; any other failure, in a minute.\n this.schedule(isDiscoveryBudgetError(error) ? budgetRetryMs(error, 1_000, 60_000) : 60_000);\n throw error;\n }\n await this.persist({ ...this.state, publishedAt: now });\n this.lastOut = content;\n if (changed || unpublished) this.options.published?.(rev);\n // This side can dial it now: a contact's record that listed it with no descriptor is worth a few reads again.\n if (dialable) { this.retries = 0; this.readAgainIfBehind(); }\n }\n this.schedule();\n });\n }\n private schedule(inMs?: number): void {\n if (this.timer) clearTimeout(this.timer);\n if (!this.running) return;\n const age = Date.now() - (this.state.publishedAt ?? 0);\n // A failed publication already scheduled its retry.\n this.timer = setTimeout(() => void this.update().catch(() => {}), inMs ?? Math.max(60_000, CAPS_REFRESH_MS - age));\n }\n\n /** An envelope named this revision of the contact's record: read it when it is newer than the one known. */\n peerRev(rev: number): void {\n if (!Number.isSafeInteger(rev) || rev <= (this.state.peer?.rev ?? -1)) return;\n if (rev > this.named) { this.named = rev; this.retries = 0; }\n this.refresh(true);\n }\n\n /** Reads the contact's record now (or right after the last read, when that was a moment ago). */\n refresh(force = false): void {\n if (!this.running || this.readTimer) return;\n const wait = force ? 0 : Math.max(0, this.lastRead + CAPS_READ_SPACING_MS - Date.now());\n const run = () => { this.readTimer = null; void this.read().catch(() => {}); };\n if (!wait) { this.readTimer = setTimeout(run, 0); return; }\n this.readTimer = setTimeout(run, wait);\n }\n\n /** Reads and checks the contact's record; the last good one stays when this one is refused. */\n read(): Promise<CapsRecord | undefined> {\n const read = this.serialize(async () => {\n if (!this.running) return this.state.peer;\n this.lastRead = Date.now();\n const packet = await this.options.transport.resolve(this.keys.peerAddress);\n if (!packet || !this.running) return this.state.peer;\n let record: CapsRecord;\n try {\n record = this.keys.open(packet, { pinned: this.options.credentials.peerKey, minRev: this.state.peer?.rev });\n } catch (error) {\n if (error instanceof CapsRefusedError && error.reason !== \"rev\") this.options.refused?.(error);\n return this.state.peer;\n }\n if (this.state.peer && record.rev === this.state.peer.rev && record.author === this.state.peer.author) return this.state.peer;\n await this.persist({ ...this.state, peer: record });\n // A new revision: what it still lacks (a transport without its descriptor) is worth a few reads again.\n this.retries = 0;\n this.options.changed?.(record);\n return record;\n });\n return read.finally(() => this.readAgainIfBehind());\n }\n\n /**\n * Read again in a moment, a few times at most, while the record known is short of what the contact has: of the\n * revision an envelope named, or of a descriptor for a native transport it lists (its endpoint was still starting)\n * that this side runs too. Its next revision, with the descriptor, is named only once, by an envelope that may\n * come before the record is out; without it neither side could dial the other.\n */\n private readAgainIfBehind(): void {\n if (this.retryTimer) clearTimeout(this.retryTimer);\n this.retryTimer = null;\n if (!this.running || this.retries >= CAPS_READ_RETRY_MS.length) return;\n const peer = this.state.peer;\n const behind = this.named > (peer?.rev ?? -1);\n const starting = !!peer && described(this.options.local()).some(t => peer.transports.includes(t) && !peer.descriptors[t]);\n if (!behind && !starting) return;\n this.retryTimer = setTimeout(() => { this.retryTimer = null; void this.read().catch(() => {}); }, CAPS_READ_RETRY_MS[this.retries++]);\n }\n}\n","import { fromBase64Url, toBase64Url, utf8Encode } from \"./bytes\";\nimport { identityFromSeedB64, publicKeyFromZ32, sign, verify } from \"./identity\";\nimport { parseRtcSignal } from \"./signal\";\n\nconst transcript = (signal: string, from: string, to: string) =>\n utf8Encode(JSON.stringify([\"ghostly-paired-signal\", 1, from, to, JSON.parse(signal)]));\n\n/** Sign the existing compact signal, including its timestamp and DTLS binding.\n * Extra fields remain readable by first-increment clients during migration. */\nexport function signPairedSignal(signal: string, seed: string, from: string, to: string): string {\n const parsed = parseRtcSignal(signal);\n if (!parsed) throw new Error(\"Invalid outgoing signal\");\n const canonical = JSON.stringify(parsed);\n const identity = identityFromSeedB64(seed);\n return JSON.stringify({ ...parsed, auth: { key: identity.pubKeyZ32,\n sig: toBase64Url(sign(transcript(canonical, from, to), identity.seed)) } });\n}\n\n/** Invalid input never reaches DataLink's timestamp/glare/SDP state machine. */\nexport function verifyPairedSignal(signal: string, from: string, to: string,\n peerKey?: string, required = false): string | null {\n try {\n if (signal.length > 4096) return null;\n const raw = JSON.parse(signal);\n const parsed = parseRtcSignal(signal);\n if (!parsed) return null;\n if (!raw.auth) return required ? null : JSON.stringify(parsed);\n const { key, sig } = raw.auth;\n if (typeof key !== \"string\" || typeof sig !== \"string\" || !/^[A-Za-z0-9_-]{86}$/.test(sig) ||\n (peerKey && key !== peerKey)) return null;\n const canonical = JSON.stringify(parsed);\n return verify(fromBase64Url(sig), transcript(canonical, from, to), publicKeyFromZ32(key)) ? canonical : null;\n } catch { return null; }\n}\n\n\n/** Keep at least one candidate. Drop duplicate candidate types first; if still\n * necessary prefer relay/server-reflexive reachability over another host route.\n * Every resulting set is signed afresh, never truncated after authentication. */\nexport function fitSignedPairedSignal(signal: string, seed: string, from: string, to: string,\n fits: (signed: string) => boolean): string {\n const parsed = parseRtcSignal(signal);\n if (!parsed) throw new Error(\"Invalid outgoing signal\");\n for (;;) {\n const signed = signPairedSignal(JSON.stringify(parsed), seed, from, to);\n if (fits(signed)) return signed;\n if (parsed.c.length <= 1) throw new Error(\"Signed connection details exceed the discovery packet budget\");\n let remove = -1;\n for (const type of [\"h\", \"s\", \"r\"]) {\n const indexes = parsed.c.map((c, i) => c.startsWith(`${type},`) ? i : -1).filter(i => i >= 0);\n if (indexes.length > 1) { remove = indexes[indexes.length - 1]; break; }\n }\n if (remove < 0) remove = parsed.c.findIndex(c => c.startsWith(\"h,\"));\n if (remove < 0) remove = parsed.c.findIndex(c => c.startsWith(\"s,\"));\n parsed.c.splice(remove < 0 ? parsed.c.length - 1 : remove, 1);\n }\n}\n","import { ed25519, x25519 } from \"@noble/curves/ed25519.js\";\nimport { xsalsa20poly1305 } from \"@noble/ciphers/salsa.js\";\nimport { hkdf } from \"@noble/hashes/hkdf.js\";\nimport { sha256 } from \"@noble/hashes/sha2.js\";\nimport { concatBytes, fromBase64Url, randomBytes, toBase64Url, utf8Decode, utf8Encode } from \"./bytes\";\nimport { encrypt, tryDecrypt } from \"./crypto\";\nimport { identityFromSeed, identityFromSeedB64, publicKeyFromZ32, sign, verify, type Identity } from \"./identity\";\nimport type { LinkParams } from \"./invite\";\nimport { measureRecords, MAX_DNS_PACKET_BYTES, type GhostRecord, type SignedPacket } from \"./pkarr\";\n\n/**\n * Store-and-forward for an away contact (WISP 4xx draft, `hold/1`).\n *\n * What a person sends while the contact is away is sealed into a *bundle* (signed by the sender's\n * participation key, then encrypted to a key only the two of them can derive), put in the sender's\n * own storage (WISP 1000), and named in a *manifest* the storage also holds. A small signed, encrypted\n * *pointer* record on a per-link Pkarr key tells the contact where the manifest is (a URL that expires)\n * and which of the contact's own bundles have been received. Storage and relays see ciphertext, sizes\n * and timing only. Nothing here promises forward secrecy or retention beyond the bounds below.\n */\nexport const HOLD_CAPABILITY = \"hold/1\" as const;\nexport const HOLD_LIMITS = {\n /** One bundle, ciphertext and header included: a picture, not a video. */\n maxBundleBytes: 8 * 1024 * 1024,\n /** All bundles held for one contact at once. */\n maxMailboxBytes: 64 * 1024 * 1024,\n maxMailboxBundles: 64,\n /** A bundle not picked up by then is dropped by its sender. The longest a presigned URL lives, too. */\n ttlMs: 7 * 24 * 60 * 60_000,\n maxTextBytes: 16 * 1024,\n maxPaymentRequestBytes: 64 * 1024,\n maxManifestBytes: 64 * 1024,\n /**\n * How far past one lifetime from now an expiry may reach by the reader's clock: the sender's clock may be ahead, and\n * a day covers any clock that is merely wrong. Nothing else about a pointer or a bundle is held against this clock.\n */\n clockSkewMs: 24 * 60 * 60_000,\n} as const;\n/**\n * How far before its own clock a sender dates its pointer and its manifest, and ends the lifetimes it gives. A reader\n * refuses nothing for those dates: `rev` orders pointers and `seq` orders items, both kept by the reader. Apps up to\n * 1.0.2 refuse a pointer or a manifest dated more than a minute past their clock, and an expiry more than a minute\n * past one lifetime from now, each without a word: the items of a sender whose clock ran two minutes fast were never\n * fetched. Dated back, a sender up to about eleven minutes ahead is read by them.\n */\nexport const HOLD_DATED_BACK_MS = 10 * 60_000;\n\nexport type HoldKind = \"text\" | \"file\" | \"pay-req\" | \"manifest\";\nconst KINDS: readonly string[] = [\"text\", \"file\", \"pay-req\", \"manifest\"];\nconst WIRE_ID = /^[A-Za-z0-9_-]{8,64}$/;\nconst MAILBOX = /^[A-Za-z0-9_-]{22}$/;\nconst SIG = /^[A-Za-z0-9_-]{86}$/;\nconst MAGIC = utf8Encode(\"GHLD\");\nconst VERSION = 1;\nconst NONCE = 24;\n\nexport interface HoldFileMeta { name: string; size: number; mime: string }\n/** One held bundle as the manifest lists it: where to get it, and what to expect. */\nexport type HoldManifestEntry = [seq: number, id: string, kind: Exclude<HoldKind, \"manifest\">, bytes: number, url: string, expires: number];\nexport interface HoldHeader {\n v: 1;\n /** Rendezvous keys, as the sender sees the link. */\n from: string;\n to: string;\n /** Participation keys: who signed, who may open. */\n author: string;\n recipient: string;\n mailbox: string;\n seq: number;\n id: string;\n ts: number;\n kind: HoldKind;\n meta: unknown;\n /** base64url SHA-256 of the body. */\n digest: string;\n expires: number;\n}\nexport interface HoldPointer {\n rev: number;\n issued: number;\n /** When the manifest URL stops working. */\n expires: number;\n manifestUrl: string | null;\n /** Highest sequence the publisher has held for the reader. */\n top: number;\n /** Highest sequence the publisher received from the reader. */\n ack: number;\n count: number;\n bytes: number;\n /** Sequences of the reader's items this publisher refused (changed, oversized, not for it): at most the last 32. */\n refused: number[];\n}\n\n/** Why a bundle, manifest or pointer was refused. Never a reason to trust it a little. */\nexport class HoldRefusedError extends Error {\n constructor(readonly reason: \"size\" | \"format\" | \"tampered\" | \"not-for-me\" | \"author\" | \"signature\" | \"digest\" | \"limits\" | \"expired\", message: string) {\n super(message);\n }\n}\n\nconst bodyDigest = (body: Uint8Array) => toBase64Url(sha256(body));\nconst newMailbox = () => toBase64Url(randomBytes(16)).slice(0, 22);\nexport { newMailbox as newHoldMailbox };\n\n/**\n * Everything derived once per link for this feature: my pointer key, the contact's pointer address and\n * the key that seals bundles and pointers between the two participation keys. Needs a pinned contact.\n */\nexport class HoldKeys {\n readonly from: string;\n readonly to: string;\n readonly me: string;\n readonly peer: string;\n /** Publishes my pointer. */\n readonly identity: Identity;\n /** Where the contact publishes its pointer. */\n readonly peerAddress: string;\n private readonly seed: Uint8Array;\n private readonly sealKey: Uint8Array;\n\n constructor(params: LinkParams, participationSeedB64: string, peerKey: string) {\n this.from = identityFromSeedB64(params.seedB64).pubKeyZ32;\n this.to = params.peerPubKeyZ32;\n const mine = identityFromSeedB64(participationSeedB64);\n this.seed = mine.seed;\n this.me = mine.pubKeyZ32;\n this.peer = peerKey;\n const secret = fromBase64Url(params.encKeyB64);\n const context = JSON.stringify([\"ghostly-hold/1\", [this.from, this.to].sort()]);\n const derive = (label: string) => hkdf(sha256, secret, utf8Encode(context), utf8Encode(label), 32);\n this.identity = identityFromSeed(derive(`pointer:${this.from}`));\n this.peerAddress = identityFromSeed(derive(`pointer:${this.to}`)).pubKeyZ32;\n const shared = x25519.getSharedSecret(ed25519.utils.toMontgomerySecret(this.seed), ed25519.utils.toMontgomery(publicKeyFromZ32(peerKey)));\n this.sealKey = hkdf(sha256, shared, derive(\"envelope\"), utf8Encode(\"ghostly-hold-envelope/1\"), 32);\n }\n\n /** Seals one item for the contact: signed with my participation key, encrypted to our shared key. */\n seal(item: { mailbox: string; seq: number; id: string; ts: number; kind: HoldKind; meta?: unknown; expires: number }, body: Uint8Array): Uint8Array {\n const fields = [VERSION, this.from, this.to, this.me, this.peer, item.mailbox, item.seq, item.id, item.ts, item.kind, item.meta ?? null, bodyDigest(body), item.expires];\n const signature = toBase64Url(sign(utf8Encode(JSON.stringify([\"ghostly-hold-bundle\", fields])), this.seed));\n const header = utf8Encode(JSON.stringify([fields, signature]));\n const length = new Uint8Array(4);\n new DataView(length.buffer).setUint32(0, header.length);\n const nonce = randomBytes(NONCE);\n const box = xsalsa20poly1305(this.sealKey, nonce).encrypt(concatBytes(length, header, body));\n return concatBytes(MAGIC, new Uint8Array([VERSION]), nonce, box);\n }\n\n /**\n * Opens a bundle the contact sealed for me. Everything is checked before anything is returned: the\n * authenticated encryption, the header's shape, that it names this link, this contact and me, the\n * signature, the body digest and the limits of its kind.\n */\n open(bytes: Uint8Array, expect: { maxBytes?: number; mailbox?: string; now?: number } = {}): { header: HoldHeader; body: Uint8Array } {\n const now = expect.now ?? Date.now();\n if (bytes.length > (expect.maxBytes ?? HOLD_LIMITS.maxBundleBytes)) throw new HoldRefusedError(\"size\", \"The held item is larger than allowed\");\n if (bytes.length < MAGIC.length + 1 + NONCE + 16 + 4 || !MAGIC.every((b, i) => bytes[i] === b)) throw new HoldRefusedError(\"format\", \"Not a held item\");\n if (bytes[MAGIC.length] !== VERSION) throw new HoldRefusedError(\"format\", \"Held item from a newer Ghostly\");\n let plain: Uint8Array;\n try { plain = xsalsa20poly1305(this.sealKey, bytes.subarray(MAGIC.length + 1, MAGIC.length + 1 + NONCE)).decrypt(bytes.subarray(MAGIC.length + 1 + NONCE)); }\n catch { throw new HoldRefusedError(\"tampered\", \"The held item was changed or is not for this chat\"); }\n if (plain.length < 4) throw new HoldRefusedError(\"format\", \"Not a held item\");\n const headerLength = new DataView(plain.buffer, plain.byteOffset, 4).getUint32(0);\n if (headerLength > 128 * 1024 || 4 + headerLength > plain.length) throw new HoldRefusedError(\"format\", \"Not a held item\");\n let parsed: unknown;\n try { parsed = JSON.parse(utf8Decode(plain.subarray(4, 4 + headerLength))); } catch { throw new HoldRefusedError(\"format\", \"Not a held item\"); }\n if (!Array.isArray(parsed) || parsed.length !== 2 || !Array.isArray(parsed[0]) || parsed[0].length !== 13 || typeof parsed[1] !== \"string\" || !SIG.test(parsed[1])) throw new HoldRefusedError(\"format\", \"Not a held item\");\n const [fields, signature] = parsed as [unknown[], string];\n const [v, from, to, author, recipient, mailbox, seq, id, ts, kind, meta, digest, expires] = fields;\n if (v !== VERSION || typeof from !== \"string\" || typeof to !== \"string\" || typeof author !== \"string\" || typeof recipient !== \"string\" || typeof mailbox !== \"string\" || !MAILBOX.test(mailbox) ||\n !Number.isSafeInteger(seq) || (seq as number) < 0 || typeof id !== \"string\" || !WIRE_ID.test(id) || !Number.isSafeInteger(ts) || (ts as number) <= 0 ||\n typeof kind !== \"string\" || !KINDS.includes(kind) || typeof digest !== \"string\" || !Number.isSafeInteger(expires)) throw new HoldRefusedError(\"format\", \"Not a held item\");\n // Both directions of this link are sealed with the same key: the header says which one this is.\n if (from !== this.to || to !== this.from || recipient !== this.me) throw new HoldRefusedError(\"not-for-me\", \"The held item is for another chat\");\n if (author !== this.peer) throw new HoldRefusedError(\"author\", \"The held item was not signed by this contact\");\n if (expect.mailbox && mailbox !== expect.mailbox) throw new HoldRefusedError(\"not-for-me\", \"The held item is from another mailbox\");\n let valid: boolean;\n try { valid = verify(fromBase64Url(signature), utf8Encode(JSON.stringify([\"ghostly-hold-bundle\", fields])), publicKeyFromZ32(author)); } catch { valid = false; }\n if (!valid) throw new HoldRefusedError(\"signature\", \"The held item's signature does not check out\");\n const body = plain.subarray(4 + headerLength);\n if (bodyDigest(body) !== digest) throw new HoldRefusedError(\"digest\", \"The held item's content does not match its header\");\n // Its `ts` is its sender's clock, often minutes from this one: nothing is refused for it. It was, past a minute\n // ahead, and for good: the item's sequence was passed, and its sender told \"could not be verified as yours, or was\n // too large\". What stops an item from being taken twice is its sequence (the manifest's, the pointer's `rev`).\n // The item may be older than its upload (a retry, a message written offline); its expiry may not reach past one\n // lifetime from now, give or take what the sender's clock is ahead.\n if ((expires as number) <= (ts as number) || (expires as number) > now + HOLD_LIMITS.ttlMs + HOLD_LIMITS.clockSkewMs) throw new HoldRefusedError(\"limits\", \"The held item's lifetime is out of bounds\");\n const header: HoldHeader = { v: 1, from, to, author, recipient, mailbox, seq: seq as number, id, ts: ts as number, kind: kind as HoldKind, meta, digest, expires: expires as number };\n checkKind(header, body);\n return { header, body };\n }\n\n /** My pointer, as records to publish under `identity`. Throws when it cannot fit a packet. */\n pointerRecords(pointer: HoldPointer, now = Date.now()): GhostRecord[] {\n const body = [VERSION, pointer.rev, pointer.issued, pointer.expires, pointer.manifestUrl, pointer.top, pointer.ack, pointer.count, pointer.bytes, pointer.refused.slice(-32)];\n const signature = toBase64Url(sign(utf8Encode(JSON.stringify([\"ghostly-hold-pointer\", this.from, this.to, body])), this.seed));\n const ttl = Math.min(24 * 3600, Math.max(60, Math.ceil((pointer.expires - now) / 1000)));\n const records = [{ label: \"_hold\", value: encrypt(JSON.stringify([body, signature]), this.sealKey), ttl }];\n if (measureRecords(this.identity.pubKeyZ32, records) > MAX_DNS_PACKET_BYTES) throw new Error(\"The storage address is too long for a DHT packet. Use a shorter endpoint or prefix.\");\n return records;\n }\n\n /**\n * The contact's pointer out of its packet, or null when the packet is not one of its pointers. Its `issued` is the\n * contact's clock and refuses nothing: an older pointer cannot take a newer one's place because of `rev`.\n */\n readPointer(packet: SignedPacket): HoldPointer | null {\n if (packet.pubKeyZ32 !== this.peerAddress || measureRecords(packet.pubKeyZ32, packet.records) > MAX_DNS_PACKET_BYTES) return null;\n const records = packet.records.filter(r => r.label === \"_hold\");\n if (records.length !== 1) return null;\n const plain = tryDecrypt(records[0].value, this.sealKey);\n if (!plain) return null;\n let parsed: unknown;\n try { parsed = JSON.parse(plain); } catch { return null; }\n if (!Array.isArray(parsed) || parsed.length !== 2 || !Array.isArray(parsed[0]) || (parsed[0].length !== 9 && parsed[0].length !== 10) || typeof parsed[1] !== \"string\" || !SIG.test(parsed[1])) return null;\n const [body, signature] = parsed as [unknown[], string];\n const [v, rev, issued, expires, manifestUrl, top, ack, count, bytes, refused = []] = body;\n if (v !== VERSION || ![rev, issued, expires, top, ack, count, bytes].every(n => Number.isSafeInteger(n) && (n as number) >= 0) ||\n (manifestUrl !== null && (typeof manifestUrl !== \"string\" || manifestUrl.length > 2048)) ||\n !Array.isArray(refused) || refused.length > 32 || !refused.every(n => Number.isSafeInteger(n) && (n as number) > 0)) return null;\n try {\n if (!verify(fromBase64Url(signature), utf8Encode(JSON.stringify([\"ghostly-hold-pointer\", this.to, this.from, body])), publicKeyFromZ32(this.peer))) return null;\n } catch { return null; }\n return { rev: rev as number, issued: issued as number, expires: expires as number, manifestUrl: manifestUrl as string | null, top: top as number, ack: ack as number, count: count as number, bytes: bytes as number, refused: refused as number[] };\n }\n}\n\n/** A URL a bundle or manifest may be fetched from: HTTPS, or HTTP to this machine while testing. */\nexport function isHoldUrl(value: unknown): value is string {\n if (typeof value !== \"string\" || value.length > 2048) return false;\n try {\n const url = new URL(value);\n return url.protocol === \"https:\" || (url.protocol === \"http:\" && [\"localhost\", \"127.0.0.1\", \"[::1]\"].includes(url.hostname));\n } catch { return false; }\n}\n\n/** The limits of each kind, on the way in. */\nfunction checkKind(header: HoldHeader, body: Uint8Array): void {\n const bad = (message: string) => new HoldRefusedError(\"limits\", message);\n switch (header.kind) {\n case \"text\":\n if (body.length > HOLD_LIMITS.maxTextBytes) throw bad(\"The held text is too long\");\n try { utf8Decode(body); } catch { throw bad(\"The held text is not text\"); }\n return;\n case \"pay-req\":\n if (body.length > HOLD_LIMITS.maxPaymentRequestBytes) throw bad(\"The held payment request is too large\");\n return;\n case \"file\": {\n const meta = header.meta as Partial<HoldFileMeta> | null;\n if (!meta || typeof meta !== \"object\" || typeof meta.name !== \"string\" || meta.name.length > 255 || typeof meta.mime !== \"string\" || meta.mime.length > 255 || meta.size !== body.length) throw bad(\"The held file does not match its description\");\n return;\n }\n case \"manifest\": {\n if (body.length !== 0) throw bad(\"A manifest carries no body\");\n readManifest(header.meta);\n return;\n }\n }\n}\n\n/** The entries of a manifest, checked: strictly increasing sequences, bounded sizes, fetchable URLs. */\nexport function readManifest(meta: unknown): HoldManifestEntry[] {\n const bad = (message: string) => new HoldRefusedError(\"limits\", message);\n const entries = (meta as { entries?: unknown } | null)?.entries;\n if (!Array.isArray(entries) || entries.length > HOLD_LIMITS.maxMailboxBundles) throw bad(\"The manifest lists too many items\");\n let last = 0, total = 0;\n const out: HoldManifestEntry[] = [];\n for (const entry of entries) {\n if (!Array.isArray(entry) || entry.length !== 6) throw bad(\"The manifest is malformed\");\n const [seq, id, kind, bytes, url, expires] = entry;\n if (!Number.isSafeInteger(seq) || seq <= last || typeof id !== \"string\" || !WIRE_ID.test(id) || typeof kind !== \"string\" || !KINDS.includes(kind) || kind === \"manifest\" ||\n !Number.isSafeInteger(bytes) || bytes < 0 || bytes > HOLD_LIMITS.maxBundleBytes || !isHoldUrl(url) || !Number.isSafeInteger(expires)) throw bad(\"The manifest is malformed\");\n last = seq; total += bytes;\n if (total > HOLD_LIMITS.maxMailboxBytes) throw bad(\"The manifest lists more than a mailbox may hold\");\n out.push([seq, id, kind as HoldManifestEntry[2], bytes, url, expires]);\n }\n return out;\n}\n","import { PROOF_ADAPTERS, proofCapability, type ProofAdapter } from \"./peerProofs\";\nimport { IDENTITY_PROOF_CAPABILITY } from \"./identityProofs\";\nimport { HOLD_CAPABILITY } from \"./storeForward\";\nimport { fromBase64Url, randomBytes, toBase64Url, utf8Encode } from \"./bytes\";\nimport { identityFromSeedB64, publicKeyFromZ32, sign, verify } from \"./identity\";\nimport type { FrameChannel } from \"./frames\";\nimport { rankTransports, type PairedTransport, type NativeBinding } from \"./pairedTransports\";\n\n/** Experimental, opt-in profile. Ed25519 authenticates a transcript bound to WebRTC DTLS.\n * This is not a new cipher/key exchange, nor an audited WISP Final profile. */\nexport const PAIRED_PROFILE = \"paired-chat/1\" as const;\nexport interface PairingState {\n transport?: PairedTransport;\n preferred?: PairedTransport;\n status: \"connecting\" | \"negotiating\" | \"confirm\" | \"waiting\" | \"ready\" | \"error\";\n code?: string;\n error?: string;\n peerKey?: string;\n verified?: boolean;\n peerNeedsConfirmation?: boolean;\n keyMismatch?: boolean;\n transitionTarget?: PairedTransport;\n transitionError?: string;\n}\nexport interface PairingCredentials {\n seedB64: string;\n peerKey?: string;\n requireSignedSignals?: boolean;\n verifiedPeerKey?: string;\n /**\n * The key the invite named (a `ghostly1` code carries the inviter's): before\n * anything is pinned, an answer signed by any other key is a security rejection.\n */\n expectedPeerKey?: string;\n}\ninterface Offer {\n t: \"pair-offer\";\n versions: number[];\n transports: string[];\n capabilities: string[];\n /** Absent in apps that do not send it; see `OFFER_EXTENSIONS`. */\n extensions?: string[];\n key: string;\n nonce: string;\n}\nexport interface PairedSessionOptions {\n credentials: PairingCredentials;\n rendezvousKeys: [string, string];\n /** SHA-256 certificate fingerprints extracted from this exact RTCPeerConnection. */\n fingerprints?: [string, string];\n binding?: NativeBinding;\n transports?: PairedTransport[];\n allowFallback?: boolean;\n proofSupport?: boolean;\n /** Identity proofs (WISP 300): `identity-proof/1`. */\n identitySupport?: boolean;\n filesSupport?: boolean;\n paymentsSupport?: boolean;\n /** Ecash straight in the chat. Absent means allowed, as before ways of paying could be chosen per chat. */\n cashuPaymentsSupport?: boolean;\n /** Lightning invoices in the chat. Absent means allowed. */\n lightningPaymentsSupport?: boolean;\n arkPaymentsSupport?: boolean;\n usdtPaymentsSupport?: boolean;\n barkPaymentsSupport?: boolean;\n transportSwitchSupport?: boolean;\n /** Store-and-forward for an away contact (`hold/1`): this device accepts held items and may hold some. */\n holdSupport?: boolean;\n /** TOFU admission is distinct from an optional human comparison. */\n trustOnFirstUse?: boolean;\n /**\n * How long the peer has to authenticate before the session fails: three minutes by default, room for a person to\n * compare codes. A connection that holds a place no person waits on (one dialled in on a pinned chat) gets less.\n */\n authTimeoutMs?: number;\n verifyPeer?: (key: string) => Promise<void>;\n /** Atomic durable compare-and-set. Resolving means the key is pinned on disk. */\n pinPeer: (key: string, signedSignals?: boolean) => Promise<void>;\n onState: (state: PairingState) => void;\n onReady: () => void;\n onApplication: (data: string | Uint8Array) => void | Promise<void>;\n onFailure: () => void;\n}\n\n/** The ways of paying a chat can allow one by one. */\n/**\n * `bitcoin` (on-chain), `fedimint` and `spark` are never offered in the handshake: a full offer is already at the 16\n * capabilities apps before 0.5 accept. Both sides allow them only through the `paired-payments` list of the open\n * session.\n */\nexport type PaymentMethodName = \"cashu\" | \"lightning\" | \"arkade\" | \"usdt\" | \"bark\" | \"bitcoin\" | \"fedimint\" | \"spark\";\n\n/**\n * What this app does that grants nothing, said outside `capabilities`: apps before 0.5 accept at most 16\n * of those, and a full offer is already there. The field is not in the transcript (an app that does not\n * know it could not sign it), so nothing that needs authenticating may go here; the DTLS binding of the\n * transcript still ties it to this connection. `ping/1`: this app answers `paired-ping`, from the open.\n */\nconst OFFER_EXTENSIONS = [\"ping/1\"];\n\nconst MAX_HANDSHAKE_BYTES = 4096;\nconst KEY = /^[ybndrfg8ejkmcpqxot1uwisza345h769]{52}$/;\nconst NONCE = /^[A-Za-z0-9_-]{43}$/;\nconst SIG = /^[A-Za-z0-9_-]{86}$/;\nconst FINGERPRINT = /^[a-f0-9]{64}$/;\nconst isList = (v: unknown, maximum = 8): v is string[] => Array.isArray(v) && v.length > 0 && v.length <= maximum &&\n v.every(x => typeof x === \"string\" && /^[a-z0-9/-]{1,40}$/.test(x)) && new Set(v).size === v.length;\n\nfunction parseOffer(v: Record<string, unknown>): Offer | null {\n if (!Array.isArray(v.versions) || v.versions.length === 0 || v.versions.length > 8 ||\n !v.versions.every(n => Number.isSafeInteger(n) && n > 0) || new Set(v.versions).size !== v.versions.length ||\n !isList(v.transports) || !isList(v.capabilities, 32) || typeof v.key !== \"string\" || !KEY.test(v.key) ||\n typeof v.nonce !== \"string\" || !NONCE.test(v.nonce)) return null;\n // Optional and new: one this app cannot read counts as none said, rather than failing the offer.\n const extensions = isList(v.extensions, 32) ? v.extensions : undefined;\n return { t: \"pair-offer\", versions: v.versions, transports: v.transports, capabilities: v.capabilities, ...(extensions && { extensions }), key: v.key, nonce: v.nonce };\n}\nconst offerTuple = (o: Offer) => [o.key, o.nonce, o.versions, o.transports, o.capabilities];\n\nexport class PairedSession {\n state: PairingState = { status: \"negotiating\" };\n /** The peer said nothing more within `authTimeoutMs`: the connection carried nothing, which proves nothing about the peer. */\n authTimedOut = false;\n private readonly identity;\n private readonly offer: Offer;\n private readonly transport: PairedTransport;\n private preferred?: PairedTransport;\n private peer: Offer | null = null;\n private transcript: Uint8Array | null = null;\n private digest = \"\";\n private proofReceived = false;\n private localReady = false;\n private peerReady = false;\n private stopped = false;\n private confirming = false;\n private admission: Promise<void> | null = null;\n private pending = 0;\n private queue = Promise.resolve();\n private timer: ReturnType<typeof setTimeout> | null = null;\n\n constructor(private channel: FrameChannel, private options: PairedSessionOptions) {\n this.identity = identityFromSeedB64(options.credentials.seedB64);\n this.transport = options.binding?.transport ?? \"webrtc/1\";\n this.offer = { t: \"pair-offer\", versions: [1], transports: options.transports ?? [this.transport], capabilities: [\"chat/1\", \"signed-signal/1\", ...(options.trustOnFirstUse ? [\"tofu/1\"] : []), ...(options.filesSupport ? [\"files/2\"] : []), ...(options.paymentsSupport && (options.cashuPaymentsSupport !== false || options.lightningPaymentsSupport !== false) ? [\"payments/1\"] : []), ...(options.paymentsSupport && options.cashuPaymentsSupport !== false ? [\"payments-cashu/1\"] : []), ...(options.paymentsSupport && options.lightningPaymentsSupport !== false ? [\"payments-lightning/1\"] : []), ...(options.arkPaymentsSupport && options.paymentsSupport ? [\"payments-arkade/1\"] : []), ...(options.usdtPaymentsSupport && options.paymentsSupport ? [\"payments-usdt/1\"] : []), ...(options.barkPaymentsSupport && options.paymentsSupport ? [\"payments-bark/1\"] : []), ...(options.transportSwitchSupport ? [\"transport-switch/1\"] : []), ...(options.holdSupport ? [HOLD_CAPABILITY] : []), ...(options.proofSupport ? PROOF_ADAPTERS.map(proofCapability) : []), ...(options.identitySupport ? [IDENTITY_PROOF_CAPABILITY] : []), ...(options.allowFallback ? [\"transport-fallback/1\"] : [])],\n extensions: OFFER_EXTENSIONS, key: this.identity.pubKeyZ32, nonce: toBase64Url(randomBytes(32)) };\n }\n\n supports(capability: \"files/2\" | \"payments/1\" | \"payments-arkade/1\" | \"payments-usdt/1\" | \"payments-bark/1\" | typeof HOLD_CAPABILITY): boolean { return this.state.status === \"ready\" && this.offer.capabilities.includes(capability) && !!this.peer?.capabilities.includes(capability); }\n /**\n * Both sides allow this way of paying. A peer that offers payments/1 without naming Cashu or\n * Lightning predates choosing them per chat, and allows both.\n */\n allowsPayment(method: PaymentMethodName): boolean {\n return this.state.status === \"ready\" && this.offer.capabilities.includes(`payments-${method}/1`) && this.peerAllowsPayment(method);\n }\n /** The peer's offer alone allows this way of paying. */\n peerAllowsPayment(method: PaymentMethodName): boolean {\n if (this.state.status !== \"ready\" || !this.peer) return false;\n const theirs = this.peer.capabilities, capability = `payments-${method}/1`;\n if (method === \"arkade\" || method === \"usdt\" || method === \"bark\" || method === \"bitcoin\" || method === \"fedimint\" || method === \"spark\") return theirs.includes(capability);\n const other = method === \"cashu\" ? \"payments-lightning/1\" : \"payments-cashu/1\";\n return theirs.includes(\"payments/1\") && (theirs.includes(capability) || !theirs.includes(other));\n }\n get peerTransportSwitchSupport(): boolean { return !!this.peer?.capabilities.includes(\"transport-switch/1\"); }\n /** The peer's offer alone carries `hold/1`: it accepts held items from us, whatever we offered. */\n get peerHoldSupport(): boolean { return !!this.peer?.capabilities.includes(HOLD_CAPABILITY); }\n\n get proofSession(): string { return this.digest; }\n get peerProofAdapters(): ProofAdapter[] { return PROOF_ADAPTERS.filter(a => this.offer.capabilities.includes(proofCapability(a)) && this.peer?.capabilities.includes(proofCapability(a))); }\n get peerProofSupport(): boolean { return this.peerProofAdapters.length > 0; }\n /** Both offers carry `identity-proof/1`. */\n get identitySupport(): boolean { return this.state.status === \"ready\" && this.offer.capabilities.includes(IDENTITY_PROOF_CAPABILITY) && !!this.peer?.capabilities.includes(IDENTITY_PROOF_CAPABILITY); }\n get peerTransports(): readonly string[] { return this.peer?.transports ?? []; }\n /** The peer said in its offer that it answers pings, so its silence from the open means it stopped. */\n get peerAnswersPings(): boolean { return !!this.peer?.extensions?.includes(\"ping/1\"); }\n get peerAllowsFallback(): boolean { return this.peer?.capabilities.includes(\"transport-fallback/1\") ?? false; }\n\n start(): void {\n const binding = this.options.binding;\n const validBinding = binding\n ? (binding.transport === \"hyperdht/1\" ? /^[a-f0-9]{128}$/.test(binding.context) : FINGERPRINT.test(binding.context)) && binding.identities.length === 2 && binding.identities.every(k => FINGERPRINT.test(k)) && binding.identities[0] !== binding.identities[1]\n : this.options.fingerprints?.length === 2 && this.options.fingerprints.every(f => FINGERPRINT.test(f));\n if (!validBinding || !this.offer.transports.includes(this.transport) ||\n this.options.rendezvousKeys.some(k => !KEY.test(k))) return this.fail(\"Invalid connection binding or unavailable transport\");\n this.channel.onMessage = data => {\n if (this.stopped) return;\n if ((typeof data === \"string\" ? data.length : data.length) > 60 * 1024 || ++this.pending > 64)\n return this.fail(\"Session receive limit exceeded\");\n this.queue = this.queue.then(async () => {\n if (this.stopped) return;\n if (typeof data === \"string\" && data.startsWith('{\"t\":\"pair-')) {\n if (data.length > MAX_HANDSHAKE_BYTES) return this.fail(\"Negotiation message too large\");\n await this.receive(data);\n } else if (this.state.status === \"ready\") await this.options.onApplication(data);\n }).catch(() => this.fail(\"Invalid session negotiation\"))\n .finally(() => { this.pending--; });\n };\n this.timer = setTimeout(() => { this.authTimedOut = true; this.fail(\"The peer did not finish authentication. Reconnect to try again.\"); }, this.options.authTimeoutMs ?? 180_000);\n this.update({ status: \"negotiating\" });\n this.send(this.offer);\n }\n\n private send(frame: object): void {\n if (!this.stopped) this.channel.send(JSON.stringify(frame));\n }\n\n private async receive(text: string): Promise<void> {\n if (this.stopped) return;\n const raw = JSON.parse(text) as Record<string, unknown>;\n if (raw.t === \"pair-offer\") {\n const peer = parseOffer(raw);\n if (!peer || peer.key === this.offer.key) return this.fail(\"Invalid participation key or offer\");\n if (this.peer) {\n if (JSON.stringify(peer) !== JSON.stringify(this.peer)) this.fail(\"The peer changed its negotiation offer\");\n return;\n }\n if (!peer.versions.includes(1) || !peer.transports.includes(this.transport) || !peer.capabilities.includes(\"chat/1\"))\n return this.fail(\"No compatible paired chat profile\");\n const pinned = this.options.credentials.peerKey ?? this.options.credentials.expectedPeerKey;\n if (pinned && pinned !== peer.key)\n return this.fail(\"Saved contact key mismatch. This invite is already paired with another participation key. Check with your contact and use a new invitation if the change was intended.\", true);\n const ranked = rankTransports(this.offer.transports, peer.transports);\n if (!ranked.length) return this.fail(\"No common available transport\");\n this.preferred = ranked[0];\n if (this.transport !== this.preferred && !(this.options.allowFallback && peer.capabilities.includes(\"transport-fallback/1\")))\n return this.fail(\"This transport would violate the negotiated fallback policy\");\n this.peer = peer;\n // Fixed arrays define the canonical transcript; unknown offer fields grant nothing.\n const offers = [this.offer, peer].sort((a,b) => a.key < b.key ? -1 : 1).map(offerTuple);\n this.transcript = utf8Encode(JSON.stringify([\"ghostly-paired-chat\", 1,\n [...this.options.rendezvousKeys].sort(), this.options.binding\n ? [this.transport, [...this.options.binding.identities].sort(), this.options.binding.context]\n : [...this.options.fingerprints!].sort(), offers,\n [1, this.transport, \"chat/1\", \"no-dht-payload\"]]));\n const hash = await crypto.subtle.digest(\"SHA-256\", new Uint8Array(this.transcript));\n if (this.stopped) return;\n this.digest = Array.from(new Uint8Array(hash), b => b.toString(16).padStart(2, \"0\")).join(\"\");\n this.send({ t: \"pair-proof\", sig: toBase64Url(sign(this.transcript, this.identity.seed)) });\n } else if (raw.t === \"pair-proof\") {\n if (!this.peer || !this.transcript || typeof raw.sig !== \"string\" || !SIG.test(raw.sig) ||\n !verify(fromBase64Url(raw.sig), this.transcript, publicKeyFromZ32(this.peer.key)))\n return this.fail(\"The peer could not authenticate this connection\");\n if (this.proofReceived) return;\n this.proofReceived = true;\n if (this.options.credentials.peerKey || this.options.trustOnFirstUse) await this.admit();\n else this.update({ status: \"confirm\", code: this.code(), peerKey: this.peer.key });\n } else if (raw.t === \"pair-ready\") {\n if (!this.proofReceived || raw.context !== this.digest) return this.fail(\"Invalid session confirmation\");\n this.peerReady = true;\n this.maybeReady();\n } else this.fail(\"Unsupported session message\");\n }\n\n private code(): string { return this.digest.slice(0, 24).match(/.{4}/g)!.join(\" \"); }\n\n /** A user's explicit comparison. Never called by automatic admission/reconnection. */\n async confirm(expectedCode: string): Promise<void> {\n if (this.stopped || !this.proofReceived || !this.peer) throw new Error(\"No authenticated peer to verify\");\n if (expectedCode !== this.code()) throw new Error(\"The connection changed. Compare the current code again.\");\n if (this.confirming) return;\n this.confirming = true;\n const peerKey = this.peer.key;\n try {\n await this.admit();\n // Admission reports its own failure and stops the session; a stop is also\n // what a deliberate disconnect looks like. Neither is a verification, and\n // neither is news worth raising at whoever asked for one.\n if (this.stopped) return;\n if (!this.localReady) throw new Error(\"The connection closed. Compare again after reconnecting.\");\n await this.options.verifyPeer?.(peerKey);\n this.options.credentials.verifiedPeerKey = peerKey;\n if (!this.stopped) this.update({ ...this.state, verified: true });\n } finally { this.confirming = false; }\n }\n\n /** Save the authenticated key before enabling any application capability. */\n private admit(): Promise<void> {\n if (this.admission) return this.admission;\n this.admission = this.admitOnce();\n return this.admission;\n }\n\n private async admitOnce(): Promise<void> {\n if (this.stopped || !this.proofReceived || !this.peer || this.localReady) return;\n const peerKey = this.peer.key;\n try {\n const signedSignals = this.peer.capabilities.includes(\"signed-signal/1\");\n if (this.options.credentials.requireSignedSignals && !signedSignals) throw new Error(\"This peer needs a version supporting signed signaling\");\n await this.options.pinPeer(peerKey, signedSignals);\n this.options.credentials.requireSignedSignals ||= signedSignals;\n if (this.stopped) return;\n this.options.credentials.peerKey = peerKey;\n this.localReady = true;\n this.update({ status: \"waiting\", code: this.code(), peerKey,\n peerNeedsConfirmation: !this.peer.capabilities.includes(\"tofu/1\") });\n // This means authenticated, durably pinned and ready, not independently verified.\n this.send({ t: \"pair-ready\", context: this.digest });\n this.maybeReady();\n } catch {\n this.fail(\"Could not durably save this peer. No messages were enabled.\");\n }\n }\n\n private maybeReady(): void {\n if (this.stopped || !this.localReady || !this.peerReady || this.state.status === \"ready\") return;\n if (this.timer) clearTimeout(this.timer);\n this.timer = null;\n this.update({ status: \"ready\", code: this.code(), peerKey: this.peer!.key });\n this.options.onReady();\n }\n\n private update(state: PairingState): void { this.state = { ...state, verified: !!state.peerKey && this.options.credentials.verifiedPeerKey === state.peerKey, transport: this.transport, preferred: this.preferred }; this.options.onState(this.state); }\n private fail(error: string, keyMismatch = false): void {\n if (this.stopped) return;\n this.update({ status: \"error\", error, keyMismatch });\n this.stop();\n this.options.onFailure();\n }\n stop(): void {\n this.stopped = true;\n if (this.timer) clearTimeout(this.timer);\n this.timer = null;\n }\n}\n","import { fromBase64Url, toBase64Url } from \"./bytes\";\nimport { CHUNK_KIND, decodeChunk, decodeControl, type FrameChannel } from \"./frames\";\nimport { HttpClient, HttpHost, type HostedHttpService, type LocalFetch } from \"./http\";\n\n/**\n * Shared services over a paired session: the same HTTP frames as the older data link, carried as\n * `ph` application frames on the authenticated channel. Binary chunks travel base64url-encoded,\n * since paired application data is text. The frames carry no `id`, so an older app drops them.\n */\nexport class PairedHttp {\n readonly client: HttpClient;\n readonly host: HttpHost;\n\n constructor(channel: FrameChannel, getService: (id: string) => HostedHttpService | undefined, localFetch: LocalFetch) {\n const tunnel: FrameChannel = {\n send: (data) => channel.send(JSON.stringify(typeof data === \"string\" ? { t: \"ph\", c: data } : { t: \"ph\", b: toBase64Url(data) })),\n get bufferedAmount() { return channel.bufferedAmount; },\n drained: () => channel.drained(),\n // The paired channel belongs to the session; closing is the session's business.\n close: () => {},\n onMessage: null,\n onClose: null,\n };\n this.client = new HttpClient(tunnel);\n this.host = new HttpHost(tunnel, getService, localFetch);\n }\n\n /** One `ph` frame from the peer. Anything that is not HTTP traffic is ignored. */\n handle(frame: Record<string, unknown>): void {\n if (typeof frame.b === \"string\") {\n let bytes: Uint8Array;\n try { bytes = fromBase64Url(frame.b); } catch { return; }\n const chunk = decodeChunk(bytes);\n if (chunk?.kind === CHUNK_KIND.requestBody) this.host.handleChunk(chunk);\n else if (chunk?.kind === CHUNK_KIND.responseBody) this.client.handleChunk(chunk);\n return;\n }\n if (typeof frame.c !== \"string\") return;\n const control = decodeControl(frame.c);\n if (control?.t === \"req\") this.host.handleRequest(control);\n else if (control?.t === \"res\") this.client.handleResponse(control);\n else if (control?.t === \"rst\" && control.d === \"q\") this.host.handleReset(control);\n else if (control?.t === \"rst\" && control.d === \"s\") this.client.handleReset(control);\n }\n\n close(): void {\n this.host.closeAll();\n this.client.close();\n }\n}\n","/**\n * Capabilities a paired session announces once it is ready (WISP 03, 401): what this app does on this\n * session beyond what the handshake offer carries. Apps before 0.5 accept at most 16 entries in the offer's\n * `capabilities`, and a full offer is already there, so these travel in one frame on the authenticated\n * session instead, the way `paired-payments` and `paired-groups` do:\n *\n * {\"t\":\"paired-capabilities\",\"c\":[\"calls/1\",\"services/1\",\"files/3\",\"typing/1\",\"react/1\",\"edit/1\"]}\n *\n * Each side sends it on every ready session, and again whenever what it offers changes. Something is on\n * only while both sides list it; a peer that never sends the frame (an older app) offers nothing here.\n * The frame carries no message id, so older apps drop it.\n */\n\n/** Voice and video calls: `paired-call` signals on this session, media on a WebRTC connection of its own (WISP 601). */\nexport const CALLS_CAPABILITY = \"calls/1\";\n/** Shared local web apps: `paired-services` and `ph` HTTP frames on this session (WISP 701). */\nexport const SERVICES_CAPABILITY = \"services/1\";\n\n/**\n * Files of any size (WISP 501 rev 0.3): offered and accepted, many chunks in flight, resumed from the last\n * confirmed byte, checked by digest (`pf-offer` … frames, `chatFiles.ts`). Without it, files/2 and 100 MiB.\n */\nexport const FILES_CAPABILITY = \"files/3\";\n\n/** The typing indicator: `paired-typing` frames on this session, 1:1 chats only (WISP 401 § Typing, `pairedTyping.ts`). */\nexport const TYPING_CAPABILITY = \"typing/1\";\n\n/** Reactions to messages: `paired-reaction` frames on this session, 1:1 chats only (WISP 401 § Reactions, `reactions.ts`). */\nexport const REACTIONS_CAPABILITY = \"react/1\";\n\n/** Editing sent texts: `paired-edit` frames on this session, 1:1 chats only (WISP 401 § Edits, `pairedEdits.ts`). */\nexport const EDIT_CAPABILITY = \"edit/1\";\n\n/** Wake-up push: `paired-wake` frames on this session, 1:1 chats only (WISP 401 § Wake-up push, `pairedWake.ts`). */\nexport const WAKE_SESSION_CAPABILITY = \"wake/1\";\n\n/** A pinned message: `paired-pin` frames on this session, 1:1 chats only (WISP 401 § Pinned message, `pins.ts`). */\nexport const PIN_CAPABILITY = \"pin/1\";\n\n/**\n * Status cards: this app shows a message's card (`sc`) and takes a card message's edits past the text's 100, which an\n * older app would drop (WISP 4xx · Status Cards, `statusCards.ts`). A card itself needs nothing: older apps show its text.\n */\nexport const STATUS_CARD_CAPABILITY = \"status-card/1\";\n\n/**\n * Message buttons: this app shows a message's buttons (a card of kind `buttons`) and presses them with a reply naming\n * the button (WISP 4xx · Message Buttons). Nothing is gated on it: an older app shows the text and sends replies, and\n * a press reads as an ordinary reply there. It tells a bot whether its contact can press at all.\n */\nexport const BUTTONS_CAPABILITY = \"buttons/1\";\n\nexport type SessionCapability = typeof CALLS_CAPABILITY | typeof SERVICES_CAPABILITY | typeof FILES_CAPABILITY | typeof TYPING_CAPABILITY | typeof REACTIONS_CAPABILITY | typeof EDIT_CAPABILITY | typeof WAKE_SESSION_CAPABILITY | typeof PIN_CAPABILITY | typeof STATUS_CARD_CAPABILITY | typeof BUTTONS_CAPABILITY;\n\n/** Every capability this app knows on a session: what `receive` reports changes of. */\nexport const KNOWN_SESSION_CAPABILITIES: readonly SessionCapability[] = [CALLS_CAPABILITY, SERVICES_CAPABILITY, FILES_CAPABILITY, TYPING_CAPABILITY, REACTIONS_CAPABILITY, EDIT_CAPABILITY, WAKE_SESSION_CAPABILITY, PIN_CAPABILITY, STATUS_CARD_CAPABILITY, BUTTONS_CAPABILITY];\n\nexport const SESSION_CAPABILITIES_FRAME = \"paired-capabilities\";\n\nconst MAX_ENTRIES = 32;\nconst IDENTIFIER = /^[a-z0-9/-]{1,40}$/;\n\n/** The frame that says what this side offers. */\nexport function sessionCapabilitiesFrame(offered: readonly SessionCapability[]): { t: typeof SESSION_CAPABILITIES_FRAME; c: string[] } {\n return { t: SESSION_CAPABILITIES_FRAME, c: [...new Set(offered)] };\n}\n\n/**\n * The capabilities a `paired-capabilities` frame lists, or null when it is malformed (then it says nothing,\n * and what the peer said before stands). Identifiers this app does not know are kept: they grant nothing,\n * since only what both sides list is on.\n */\nexport function parseSessionCapabilities(frame: Record<string, unknown>): Set<string> | null {\n const list = frame.c;\n if (!Array.isArray(list) || list.length > MAX_ENTRIES) return null;\n if (!list.every((entry): entry is string => typeof entry === \"string\" && IDENTIFIER.test(entry))) return null;\n return new Set(list);\n}\n\n/** What both sides of one session offer. Reset when the session goes. */\nexport class SessionCapabilities {\n private peer: Set<string> | null = null;\n\n constructor(private readonly offered: () => readonly SessionCapability[]) {}\n\n /** The frame to send now: on every ready session, and after what this side offers changed. */\n announcement() { return sessionCapabilitiesFrame(this.offered()); }\n\n /** This side offers it (whatever the peer says). */\n offers(capability: SessionCapability): boolean { return this.offered().includes(capability); }\n\n /** The peer listed it on this session. False until the peer says anything. */\n peerOffers(capability: SessionCapability): boolean { return !!this.peer?.has(capability); }\n\n /** The peer has said what it offers on this session. */\n get peerAnnounced(): boolean { return this.peer !== null; }\n\n /** Both sides offer it on this session. */\n agreed(capability: SessionCapability): boolean { return this.offers(capability) && this.peerOffers(capability); }\n\n /**\n * A `paired-capabilities` frame from the peer. Returns the capabilities whose agreement changed, or null\n * when the frame was malformed and ignored.\n */\n receive(frame: Record<string, unknown>, known: readonly SessionCapability[] = KNOWN_SESSION_CAPABILITIES): SessionCapability[] | null {\n const parsed = parseSessionCapabilities(frame);\n if (!parsed) return null;\n const before = known.map(capability => this.agreed(capability));\n this.peer = parsed;\n return known.filter((capability, index) => this.agreed(capability) !== before[index]);\n }\n\n /** The session ended: nothing is agreed until the next one says so. */\n reset(): void { this.peer = null; }\n}\n","import { p256 } from \"@noble/curves/nist.js\";\nimport { gcm } from \"@noble/ciphers/aes.js\";\nimport { hkdf } from \"@noble/hashes/hkdf.js\";\nimport { sha256 } from \"@noble/hashes/sha2.js\";\nimport { concatBytes, fromBase64Url, randomBytes, toBase64Url, utf8Encode } from \"./bytes\";\n\n/*\n * Web Push, the sending side, with no server: what a Ghostly app needs to wake a contact's closed web app\n * through the contact's own push subscription (WISP 401 § Wake-up push). Two standards:\n * - VAPID (RFC 8292): the request carries a short JWT signed with the key pair the subscription was made\n * with, so the push service accepts it. The contact shares that key pair's private half with its paired\n * contacts, so each of them can sign; nothing else can be done with it.\n * - Message encryption (RFC 8291, `aes128gcm`, RFC 8188): the body is encrypted to the subscription's\n * P-256 key and auth secret. The push service (Google, Apple, Mozilla) sees only ciphertext, and only\n * the browser that subscribed can read it.\n */\n\n/** A VAPID key pair: the public key as an uncompressed P-256 point, the private one as its 32-byte scalar. */\nexport interface VapidKeys {\n publicKey: string;\n privateKey: string;\n}\n\n/** What a browser's `PushSubscription` gives: where to post, and the keys to encrypt to. */\nexport interface PushTarget {\n endpoint: string;\n /** The subscription's P-256 public key, uncompressed, base64url (`getKey(\"p256dh\")`). */\n p256dh: string;\n /** Its 16-byte auth secret, base64url (`getKey(\"auth\")`). */\n auth: string;\n}\n\n/** An HTTP request, ready for `fetch` or a relay. */\nexport interface PushRequest {\n url: string;\n headers: Record<string, string>;\n body: Uint8Array;\n}\n\nexport class WebPushError extends Error {}\n\nconst RECORD_SIZE = 4096;\n/** Push services refuse a token valid for more than 24 hours; 12 leaves room for skewed clocks. */\nconst TOKEN_SECONDS = 12 * 60 * 60;\n\nexport function generateVapidKeys(): VapidKeys {\n const secret = p256.utils.randomSecretKey();\n return { publicKey: toBase64Url(p256.getPublicKey(secret, false)), privateKey: toBase64Url(secret) };\n}\n\nfunction point(value: string, what: string): Uint8Array {\n let bytes: Uint8Array;\n try { bytes = fromBase64Url(value); } catch { throw new WebPushError(`${what} is not base64url`); }\n if (bytes.length !== 65 || bytes[0] !== 4) throw new WebPushError(`${what} is not an uncompressed P-256 key`);\n try { p256.Point.fromBytes(bytes); } catch { throw new WebPushError(`${what} is not a point on P-256`); }\n return bytes;\n}\n\n/** Whether a VAPID key pair is well formed and its halves belong together. */\nexport function vapidKeysMatch(keys: VapidKeys): boolean {\n try {\n const secret = fromBase64Url(keys.privateKey);\n return secret.length === 32 && toBase64Url(p256.getPublicKey(secret, false)) === toBase64Url(point(keys.publicKey, \"The VAPID key\"));\n } catch {\n return false;\n }\n}\n\n/**\n * The push services a browser subscribes with: Google (Chrome, Edge on Android, Brave, Opera), Apple (Safari), Mozilla\n * (Firefox), Microsoft (Edge on Windows). The same list as the push relay's (infra/services/push-relay/relay.mjs).\n */\nexport const PUSH_SERVICE_HOSTS: readonly RegExp[] = [/^fcm\\.googleapis\\.com$/, /(^|\\.)push\\.apple\\.com$/, /(^|\\.)push\\.services\\.mozilla\\.com$/, /(^|\\.)notify\\.windows\\.com$/];\n\n/**\n * Only https, on one of the push services' host names and their default port, without credentials. The endpoint came\n * from a contact: it must not turn this app into a way to post to the contact's choice of address, local or not.\n */\nexport function checkPushEndpoint(endpoint: string): URL {\n let url: URL;\n try { url = new URL(endpoint); } catch { throw new WebPushError(\"The push endpoint is not a URL\"); }\n if (url.protocol !== \"https:\") throw new WebPushError(\"The push endpoint is not https\");\n if (url.username || url.password) throw new WebPushError(\"The push endpoint carries credentials\");\n if (url.port) throw new WebPushError(\"The push endpoint names a port\");\n const host = url.hostname.toLowerCase();\n if (!PUSH_SERVICE_HOSTS.some(pattern => pattern.test(host))) throw new WebPushError(\"The push endpoint is not a push service\");\n if (endpoint.length > 2048) throw new WebPushError(\"The push endpoint is too long\");\n return url;\n}\n\n/** `Authorization: vapid t=<JWT>, k=<public key>` for a request to `endpoint` (RFC 8292). */\nexport function vapidAuthorization(endpoint: string, keys: VapidKeys, subject: string, now = Date.now()): string {\n const audience = checkPushEndpoint(endpoint).origin;\n const encode = (value: object) => toBase64Url(utf8Encode(JSON.stringify(value)));\n const unsigned = `${encode({ typ: \"JWT\", alg: \"ES256\" })}.${encode({ aud: audience, exp: Math.floor(now / 1000) + TOKEN_SECONDS, sub: subject })}`;\n const signature = p256.sign(utf8Encode(unsigned), fromBase64Url(keys.privateKey));\n return `vapid t=${unsigned}.${toBase64Url(signature)}, k=${keys.publicKey}`;\n}\n\n/**\n * `plaintext` encrypted for a subscription (RFC 8291 with RFC 8188 `aes128gcm`, one record). `sender` fixes\n * the sender's ephemeral key and salt, for the RFC's test vector only.\n */\nexport function encryptPushPayload(plaintext: Uint8Array, target: Pick<PushTarget, \"p256dh\" | \"auth\">, sender?: { secret: Uint8Array; salt: Uint8Array }): Uint8Array {\n const receiver = point(target.p256dh, \"The subscription key\");\n let auth: Uint8Array;\n try { auth = fromBase64Url(target.auth); } catch { throw new WebPushError(\"The auth secret is not base64url\"); }\n if (auth.length !== 16) throw new WebPushError(\"The auth secret is not 16 bytes\");\n if (plaintext.length > RECORD_SIZE - 17 - 86) throw new WebPushError(\"The payload is too long for one record\");\n\n const secret = sender?.secret ?? p256.utils.randomSecretKey();\n const salt = sender?.salt ?? randomBytes(16);\n const senderPublic = p256.getPublicKey(secret, false);\n const shared = p256.getSharedSecret(secret, receiver).subarray(1);\n\n const keyInfo = concatBytes(utf8Encode(\"WebPush: info\\0\"), receiver, senderPublic);\n const ikm = hkdf(sha256, shared, auth, keyInfo, 32);\n const cek = hkdf(sha256, ikm, salt, utf8Encode(\"Content-Encoding: aes128gcm\\0\"), 16);\n const nonce = hkdf(sha256, ikm, salt, utf8Encode(\"Content-Encoding: nonce\\0\"), 12);\n // The last (and only) record ends with the delimiter 0x02, no padding.\n const ciphertext = gcm(cek, nonce).encrypt(concatBytes(plaintext, Uint8Array.of(2)));\n\n const header = new Uint8Array(16 + 4 + 1 + senderPublic.length);\n header.set(salt, 0);\n new DataView(header.buffer).setUint32(16, RECORD_SIZE);\n header[20] = senderPublic.length;\n header.set(senderPublic, 21);\n return concatBytes(header, ciphertext);\n}\n\n/**\n * The receiving side of `encryptPushPayload`, which a browser does for its service worker. Here for tests and\n * for a push service stand-in; the app itself never decrypts a push.\n */\nexport function decryptPushPayload(body: Uint8Array, receiverSecret: Uint8Array, authSecret: Uint8Array): Uint8Array {\n if (body.length < 21) throw new WebPushError(\"Too short\");\n const salt = body.subarray(0, 16);\n const idLength = body[20];\n const senderPublic = body.subarray(21, 21 + idLength);\n const receiverPublic = p256.getPublicKey(receiverSecret, false);\n const shared = p256.getSharedSecret(receiverSecret, senderPublic).subarray(1);\n const keyInfo = concatBytes(utf8Encode(\"WebPush: info\\0\"), receiverPublic, senderPublic);\n const ikm = hkdf(sha256, shared, authSecret, keyInfo, 32);\n const cek = hkdf(sha256, ikm, salt, utf8Encode(\"Content-Encoding: aes128gcm\\0\"), 16);\n const nonce = hkdf(sha256, ikm, salt, utf8Encode(\"Content-Encoding: nonce\\0\"), 12);\n const padded = gcm(cek, nonce).decrypt(body.subarray(21 + idLength));\n let end = padded.length - 1;\n while (end >= 0 && padded[end] === 0) end--;\n if (end < 0 || padded[end] !== 2) throw new WebPushError(\"No last-record delimiter\");\n return padded.subarray(0, end);\n}\n\n/**\n * The whole request that wakes a subscription: POST, encrypted body, VAPID authorization, a short time to live\n * (a wake-up an hour late is no use) and high urgency (a phone on battery saver still gets it).\n */\nexport function pushRequest(target: PushTarget, keys: VapidKeys, payload: Uint8Array, options: { subject: string; ttlSeconds?: number; now?: number }): PushRequest {\n checkPushEndpoint(target.endpoint);\n const body = encryptPushPayload(payload, target);\n return {\n url: target.endpoint,\n headers: {\n Authorization: vapidAuthorization(target.endpoint, keys, options.subject, options.now),\n \"Content-Encoding\": \"aes128gcm\",\n \"Content-Type\": \"application/octet-stream\",\n TTL: String(options.ttlSeconds ?? 60 * 60),\n Urgency: \"high\",\n },\n body,\n };\n}\n","import { toBase64Url, utf8Encode, randomBytes } from \"./bytes\";\nimport { checkPushEndpoint, pushRequest, vapidKeysMatch, type PushRequest, type PushTarget, type VapidKeys } from \"./webPush\";\n\n/**\n * Wake-up push (WISP 401 § Wake-up push): a contact whose web app is closed can be woken by this app, with no\n * server of Ghostly's. The contact's app subscribes to its browser's push service and shares, with each paired\n * contact and only on the live session, where to post and how:\n *\n * {\"t\":\"paired-wake\",\"w\":{\"e\":\"https://fcm.googleapis.com/fcm/send/…\",\"p\":\"<p256dh>\",\"a\":\"<auth>\",\n * \"vp\":\"<VAPID public>\",\"vk\":\"<VAPID private>\",\"k\":\"<token>\"}}\n * {\"t\":\"paired-wake\",\"w\":null} (stopped: forget what I shared)\n *\n * When a message to that contact cannot go live (the contact's app is closed), this app posts an encrypted push\n * of a few bytes to the endpoint: `{\"wake\":1,\"k\":\"<token>\"}`. It says nothing about the message: the contact's\n * app shows \"New message\", opens on a tap, connects and fetches the message itself. The token is the contact's\n * own name for this chat, different for every contact, so its app knows which chat (and whether it is muted)\n * without the push service learning anything: it sees only that a push happened, and when.\n *\n * It needs `wake/1` in `paired-capabilities` on both sides. The VAPID key pair belongs to that one subscription,\n * so sharing its private half lets a contact post to that subscription and nothing else. Rotating it (a new\n * subscription, new tokens) is how the contact's app revokes everyone it no longer shares with.\n */\n\nexport { WAKE_SESSION_CAPABILITY as WAKE_CAPABILITY } from \"./pairedCapabilities\";\nexport const WAKE_FRAME = \"paired-wake\";\n\n/** At most one wake-up per contact in this long. A burst of messages is one push. */\nexport const WAKE_INTERVAL_MS = 5 * 60_000;\n/** A wake-up that has not reached the phone within this is no use: the push service may drop it. */\nexport const WAKE_TTL_SECONDS = 60 * 60;\n/** The VAPID subject push services ask for: who to contact about abuse. The project, never a person. */\nexport const WAKE_SUBJECT = \"https://ghostly.tools\";\n\n/** What a contact shares: its push subscription, the key pair it was made with, and its token for this chat. */\nexport interface WakeTarget extends PushTarget {\n vapid: VapidKeys;\n token: string;\n}\n\nexport type WakeFrame = {\n t: typeof WAKE_FRAME;\n w: { e: string; p: string; a: string; vp: string; vk: string; k: string } | null;\n};\n\n/** A fresh per-contact token: 16 random bytes, base64url. */\nexport function newWakeToken(): string {\n return toBase64Url(randomBytes(16));\n}\n\nexport function wakeFrame(target: WakeTarget | null): WakeFrame {\n return {\n t: WAKE_FRAME,\n w: target && { e: target.endpoint, p: target.p256dh, a: target.auth, vp: target.vapid.publicKey, vk: target.vapid.privateKey, k: target.token },\n };\n}\n\nconst TOKEN = /^[A-Za-z0-9_-]{16,64}$/;\nconst B64URL = /^[A-Za-z0-9_-]{1,200}$/;\n\n/**\n * What a `paired-wake` frame says: the contact's target, null when it stopped sharing, or undefined when the frame\n * is malformed (then it says nothing, and what the contact said before stands). The endpoint must be an https\n * push service on a public host name, and the key pair must be a pair.\n */\nexport function parseWakeFrame(frame: Record<string, unknown>): WakeTarget | null | undefined {\n if (frame.w === null) return null;\n const w = frame.w as Record<string, unknown> | undefined;\n if (!w || typeof w !== \"object\") return undefined;\n const { e, p, a, vp, vk, k } = w;\n if (typeof e !== \"string\" || ![p, a, vp, vk].every((v) => typeof v === \"string\" && B64URL.test(v)) || typeof k !== \"string\" || !TOKEN.test(k)) return undefined;\n try { checkPushEndpoint(e); } catch { return undefined; }\n const target: WakeTarget = { endpoint: e, p256dh: p as string, auth: a as string, vapid: { publicKey: vp as string, privateKey: vk as string }, token: k };\n if (!vapidKeysMatch(target.vapid)) return undefined;\n return target;\n}\n\n/**\n * What a wake-up is for: a message waits, or a call does (the caller rings until the contact's app is live, then\n * calls). The receiving app picks its own words for each; the push says nothing more.\n */\nexport type WakeKind = \"message\" | \"call\";\n\n/** At most one call wake-up per contact in this long: a caller who tries again at once is not a new push. */\nexport const WAKE_CALL_INTERVAL_MS = 30_000;\n/** How long a caller waits for the woken contact's app to come live before giving up. */\nexport const WAKE_CALL_WAIT_MS = 60_000;\n/** A call wake-up is useless once the caller stopped waiting: the push service may drop it after this. */\nexport const WAKE_CALL_TTL_SECONDS = 60;\n\n/** The wake-up's plaintext: the contact's token for this chat, and whether it is for a call. Nothing else. */\nexport function wakePayload(token: string, kind: WakeKind = \"message\"): Uint8Array {\n return utf8Encode(JSON.stringify(kind === \"call\" ? { wake: 1, k: token, c: 1 } : { wake: 1, k: token }));\n}\n\n/** A wake-up as the receiving service worker reads it: the token and its kind; null for anything else. */\nexport function readWake(text: string | null | undefined): { token: string; kind: WakeKind } | null {\n if (!text || text.length > 200) return null;\n try {\n const value = JSON.parse(text) as { wake?: unknown; k?: unknown; c?: unknown };\n if (value?.wake !== 1 || typeof value.k !== \"string\" || !TOKEN.test(value.k)) return null;\n return { token: value.k, kind: value.c === 1 ? \"call\" : \"message\" };\n } catch {\n return null;\n }\n}\n\n/** The token a wake-up carries; null for anything else. */\nexport function readWakePayload(text: string | null | undefined): string | null {\n return readWake(text)?.token ?? null;\n}\n\n/** The request that wakes the contact. Throws `WebPushError` when the target cannot be used. */\nexport function wakeRequest(target: WakeTarget, now = Date.now(), kind: WakeKind = \"message\"): PushRequest {\n return pushRequest(target, target.vapid, wakePayload(target.token, kind), {\n subject: WAKE_SUBJECT, ttlSeconds: kind === \"call\" ? WAKE_CALL_TTL_SECONDS : WAKE_TTL_SECONDS, now,\n });\n}\n\n/** One wake-up per contact per `WAKE_INTERVAL_MS`. Kept in memory: a restart may send one more, never a flood. */\nexport class WakeLimiter {\n private readonly last = new Map<string, number>();\n constructor(private readonly interval = WAKE_INTERVAL_MS) {}\n /** Whether a wake-up to `contact` may go now; if so, it counts as sent. */\n take(contact: string, now = Date.now()): boolean {\n const before = this.last.get(contact);\n if (before !== undefined && now - before < this.interval) return false;\n this.last.set(contact, now);\n return true;\n }\n /** The contact answered (it is live again): the next time it is away, it may be woken at once. */\n reset(contact: string): void {\n this.last.delete(contact);\n }\n}\n\n/**\n * What a push relay is asked (Settings → Network → Push relay): a browser page may not post to a push service\n * itself (the services answer without CORS), so it can hand the finished request to a relay the person chose,\n * which posts it as it is. The relay learns what the push service learns, plus this device's address.\n *\n * POST <relay> {\"endpoint\":\"https://…\",\"headers\":{\"Authorization\":\"vapid …\",…},\"body\":\"<base64url>\"}\n */\nexport function relayRequest(request: PushRequest): { endpoint: string; headers: Record<string, string>; body: string } {\n return { endpoint: request.url, headers: request.headers, body: toBase64Url(request.body) };\n}\n\n","import { REPLY_ID } from \"./replies\";\nimport { nextReactionNumber, validReactionNumber } from \"./reactions\";\n\n/**\n * A pinned message (WISP 400 § Pinned message): one per chat, shown in a bar at the top. Whoever pins says which\n * message (the id both sides know it by, as a reply names it) and a number: the clock in milliseconds, past the pin\n * it replaces. An empty id unpins. The highest number wins, whatever order the frames arrive in, so the last pin wins\n * on every side and a late frame changes nothing. A number further ahead of the receiver's clock than\n * `PIN_CLOCK_SKEW_MS` is refused: clocks drift, but a number past that is the sender's choice, and one near the\n * largest safe integer would leave nobody a higher number to pin or unpin with.\n *\n * A 1:1 chat says it on the live session once both sides list `pin/1` in `paired-capabilities`, and the contact\n * confirms the number it took:\n *\n * {\"t\":\"paired-pin\",\"id\":\"<message id>\",\"n\":1790000000000}\n * {\"t\":\"paired-pinned\",\"n\":1790000000000}\n *\n * A private group signs it and passes it over the edges (`group-pin`); a community seals it to the group as an\n * application frame (`pin`). Older apps drop all of these: a `t` they do not know.\n */\n\nexport const PIN_FRAME = \"paired-pin\";\nexport const PINNED_FRAME = \"paired-pinned\";\n/** A pin in a private group, signed by the member who pinned, passed on by every member that takes it. */\nexport const GROUP_PIN_FRAME = \"group-pin\";\n/** A pin in a community, as an application frame sealed to the group. */\nexport const COMMUNITY_PIN_FRAME = \"pin\";\n\n/**\n * How far ahead of this device's clock a pin's number may be. What a session and a call allow a contact's clock\n * (`RTC_SIGNAL_FUTURE_MS`, `CALL_SIGNAL_FUTURE_MS`): a contact whose clock runs seven minutes ahead pairs, chats and\n * calls, and its pins were dropped, each said again every 30 seconds and never taken.\n */\nexport const PIN_CLOCK_SKEW_MS = 10 * 60_000;\n\nexport const PIN_LIMITS = {\n /** Pin frames a receiver takes per window on one chat; the rest are dropped unconfirmed and come again. */\n receive: 10,\n windowMs: 10_000,\n /** A pin said on the live session and not confirmed is said again after this long. */\n resendMs: 30_000,\n} as const;\n\n/** A pin as it goes on the wire: the message's id (\"\" unpins) and the pinner's number. */\nexport interface WirePin { id: string; n: number }\n\n/**\n * Who may pin in a group. The one place the rule lives (WISP 400 § Pinned message): in a private group any member,\n * in a community only its admin.\n */\nexport function mayPin(kind: \"mesh\" | \"community\", member: string, admin: string | undefined): boolean {\n return kind === \"mesh\" || member === admin;\n}\n\n/**\n * Whether a pin's number holds at `now`: a positive safe integer no further ahead of the clock than\n * `PIN_CLOCK_SKEW_MS`. A kept pin whose number does not hold (taken before receivers checked it) counts as none.\n */\nexport function pinNumberHolds(n: unknown, now = Date.now()): n is number {\n return validReactionNumber(n) && n <= now + PIN_CLOCK_SKEW_MS;\n}\n\n/**\n * The number for a new pin: the clock, or one past the pin shown now, whichever is higher. Past a shown pin whose\n * number does not hold, the clock: a receiver would refuse one past it.\n */\nexport function nextPinNumber(current = 0, now = Date.now()): number {\n if (current && !pinNumberHolds(current, now)) return Math.max(1, Math.floor(now));\n return Math.min(nextReactionNumber(current, now), Number.MAX_SAFE_INTEGER);\n}\n\n/**\n * Whether `pin` replaces the chat's pin now: a higher number wins; two pins with the same number (two sides at the\n * same millisecond) are settled by the id, so every side ends on the same one. A shown pin whose number does not hold\n * is replaced by any pin.\n */\nexport function pinIsNewer(current: WirePin | undefined, pin: WirePin, now = Date.now()): boolean {\n return !current || !pinNumberHolds(current.n, now) || pin.n > current.n || (pin.n === current.n && pin.id > current.id);\n}\n\n/**\n * A pin as a receiver takes it at `now`, or null: an id of a chat's shape or \"\" (unpinned), and a number that holds\n * (`pinNumberHolds`). One refused is not confirmed: its sender says it again, and it is taken once the clocks agree.\n */\nexport function readPin(raw: unknown, now = Date.now()): WirePin | null {\n if (!raw || typeof raw !== \"object\" || Array.isArray(raw)) return null;\n const { id, n } = raw as Record<string, unknown>;\n if (typeof id !== \"string\" || (id !== \"\" && !REPLY_ID.test(id)) || !pinNumberHolds(n, now)) return null;\n return { id, n };\n}\n\nexport function pinFrame(pin: WirePin): string {\n return JSON.stringify({ t: PIN_FRAME, id: pin.id, n: pin.n });\n}\n\nexport function pinnedFrame(n: number): string {\n return JSON.stringify({ t: PINNED_FRAME, n });\n}\n\n/** The pin a `paired-pin` frame says, or null when it is malformed. */\nexport function parsePinFrame(frame: Record<string, unknown>): WirePin | null {\n return frame?.t === PIN_FRAME ? readPin(frame) : null;\n}\n\n/** The number a `paired-pinned` frame confirms, or null when it is malformed. */\nexport function parsePinnedFrame(frame: Record<string, unknown>): number | null {\n return frame?.t === PINNED_FRAME && validReactionNumber(frame.n) ? frame.n : null;\n}\n","import { sanitizeDisplayText } from \"./text\";\n\n/**\n * The typing indicator of a 1:1 chat (WISP 401 § Typing): \"your contact is writing\", said on the live session only.\n *\n * {\"t\":\"paired-typing\",\"s\":\"start\"} {\"t\":\"paired-typing\",\"s\":\"stop\"}\n * {\"t\":\"paired-typing\",\"s\":\"start\",\"kind\":\"recording\"}\n * {\"t\":\"paired-typing\",\"s\":\"start\",\"kind\":\"thinking\",\"status\":\"Transcribing your audio…\"}\n *\n * It needs `typing/1` in `paired-capabilities` on both sides. It is presence, not a message: never sent on the\n * DHT, never stored, never counted as unread, no message id (older apps drop it). While the person keeps typing,\n * `start` is said again at most every `TYPING_REFRESH_MS`; the receiver shows it until `stop`, a message from that\n * contact, the end of the session, or `TYPING_TIMEOUT_MS` without a new `start` (so a dropped link never leaves it on).\n *\n * A `start` may say what the contact is doing (`kind`: typing, recording a voice note, thinking) and, for bots, a\n * short `status` line. Both are optional: an app that knows neither shows \"typing…\", and so does a kind it does not\n * know. A `start` with a new kind or status goes at once, not at the next refresh.\n */\n\nexport const TYPING_FRAME = \"paired-typing\";\n\n/** How often a `start` is said again while the person keeps typing. */\nexport const TYPING_REFRESH_MS = 3_000;\n/** How long a `start` holds on the receiving side without another. Twice the refresh, so one lost frame is no gap. */\nexport const TYPING_TIMEOUT_MS = 6_000;\n/** At most this many typing frames count per `TYPING_RATE_WINDOW_MS`; the rest are dropped unread. */\nexport const TYPING_RATE_LIMIT = 8;\nexport const TYPING_RATE_WINDOW_MS = 10_000;\n/**\n * The sender keeps under the reader's limit: a `start` goes only while fewer than this many frames went in the\n * window, so the `stop` that follows it always has room (and a clock skew between the two sides has one frame more).\n */\nexport const TYPING_SEND_LIMIT = TYPING_RATE_LIMIT - 2;\n\n/** What the contact is doing: writing, recording a voice note, or working on an answer (a bot). */\nexport const TYPING_KINDS = [\"typing\", \"recording\", \"thinking\"] as const;\nexport type TypingKind = (typeof TYPING_KINDS)[number];\n/** Longest status line kept, in characters (code points). */\nexport const TYPING_STATUS_MAX = 40;\n\n/** A `start`'s word: the kind, and a bot's status line when it gave one. */\nexport interface TypingActivity { kind: TypingKind; status?: string }\n\nexport type TypingState = \"start\" | \"stop\";\n\n// A type, not an interface: it is read back as a plain frame record.\nexport type TypingFrame = { t: typeof TYPING_FRAME; s: TypingState; kind?: Exclude<TypingKind, \"typing\">; status?: string };\n\n/** A scheme (`https://`, `nostr:npub…`, `javascript:`), `www.`, or a tag: a status that holds one is not shown. */\nconst LINK_OR_MARKUP = /[a-z][a-z0-9+.-]*:\\/\\/|\\bwww\\.|\\b(?:mailto|javascript|data|nostr|lightning|bitcoin|ghostly|magnet|tel|sms|file):|<\\/?[a-z!]/i;\n\n/**\n * A status line as it may be shown: one line (line breaks and runs of spaces become one space), no invisible or\n * direction-changing characters, at most `TYPING_STATUS_MAX` characters. Undefined when nothing is left, or when it\n * holds a link or markup (it is plain text, never a way to put a link on the contact's screen).\n */\nexport function sanitizeTypingStatus(value: unknown): string | undefined {\n if (typeof value !== \"string\") return undefined;\n const oneLine = value.slice(0, 4 * TYPING_STATUS_MAX).replace(/\\s+/g, \" \");\n const clean = sanitizeDisplayText(oneLine, TYPING_STATUS_MAX);\n if (!clean || LINK_OR_MARKUP.test(clean)) return undefined;\n return clean;\n}\n\n/** A kind as it arrived: anything this app does not know reads as plain typing. */\nexport function typingKindOf(value: unknown): TypingKind {\n return typeof value === \"string\" && (TYPING_KINDS as readonly string[]).includes(value) ? value as TypingKind : \"typing\";\n}\n\n/** The activity as it goes on the wire and is shown: kind checked, status sanitized. */\nexport function typingActivity(kind?: unknown, status?: unknown): TypingActivity {\n const clean = sanitizeTypingStatus(status);\n return { kind: typingKindOf(kind), ...(clean ? { status: clean } : {}) };\n}\n\nexport function typingFrame(state: TypingState, activity?: TypingActivity): TypingFrame {\n const frame: TypingFrame = { t: TYPING_FRAME, s: state };\n if (state !== \"start\" || !activity) return frame;\n if (activity.kind !== \"typing\") frame.kind = activity.kind;\n const status = sanitizeTypingStatus(activity.status);\n if (status) frame.status = status;\n return frame;\n}\n\n/** The state a `paired-typing` frame says, or null when it is malformed (then it says nothing). */\nexport function parseTypingFrame(frame: Record<string, unknown>): TypingState | null {\n if (frame?.t !== TYPING_FRAME) return null;\n return frame.s === \"start\" || frame.s === \"stop\" ? frame.s : null;\n}\n\n/** What a `start` says the contact is doing: `typing` when it says nothing (an older app) or a kind unknown here. */\nexport function parseTypingActivity(frame: Record<string, unknown>): TypingActivity {\n return typingActivity(frame?.kind, frame?.status);\n}\n\nconst sameActivity = (a: TypingActivity | null, b: TypingActivity | null) => a?.kind === b?.kind && a?.status === b?.status;\n\n/** This side's word: when to say `start` again, and whether a `stop` is owed. One per session. */\nexport class TypingSender {\n private startedAt = 0;\n private standing: TypingActivity | null = null;\n private recent: number[] = [];\n\n constructor(private readonly now: () => number = Date.now) {}\n\n /**\n * The person typed (or records, or thinks): the frame to send now, or null when a `start` with the same word went\n * out less than a refresh ago, or the window is full (the next call tries again).\n */\n typing(activity: TypingActivity = { kind: \"typing\" }): TypingFrame | null {\n const now = this.now();\n const word = typingActivity(activity.kind, activity.status);\n if (this.startedAt && now - this.startedAt < TYPING_REFRESH_MS && sameActivity(this.standing, word)) return null;\n this.recent = this.recent.filter(at => now - at < TYPING_RATE_WINDOW_MS);\n if (this.recent.length >= TYPING_SEND_LIMIT) return null;\n this.recent.push(now);\n this.startedAt = now;\n this.standing = word;\n return typingFrame(\"start\", word);\n }\n\n /** The person stopped (cleared the text, sent it, left the chat): a `stop`, only when a `start` is standing. */\n stopped(): TypingFrame | null {\n if (!this.startedAt) return null;\n this.recent.push(this.now());\n this.startedAt = 0;\n this.standing = null;\n return typingFrame(\"stop\");\n }\n\n /** A `start` is standing on this session. */\n get active(): boolean { return this.startedAt !== 0; }\n\n /** The session ended: nothing is standing on the next one. */\n reset(): void { this.startedAt = 0; this.standing = null; }\n}\n\n/**\n * The contact's word, with its timeout and a rate limit. `onChange` hears every change, and only changes: typing on\n * or off, and a new kind or status while it is on.\n */\nexport class TypingReceiver {\n private activity: TypingActivity | null = null;\n private timer: ReturnType<typeof setTimeout> | null = null;\n private recent: number[] = [];\n\n constructor(private readonly onChange: (typing: boolean, activity: TypingActivity | null) => void, private readonly now: () => number = Date.now) {}\n\n /** The contact is typing now. */\n get peerTyping(): boolean { return this.activity !== null; }\n /** What the contact is doing now, or null. */\n get peerActivity(): TypingActivity | null { return this.activity; }\n\n /**\n * A `paired-typing` frame from the contact. False when it was dropped: malformed, or over the rate limit (a\n * contact flooding frames gets its word held where it was until the window lets it speak again).\n */\n receive(frame: Record<string, unknown>): boolean {\n const now = this.now();\n this.recent = this.recent.filter(at => now - at < TYPING_RATE_WINDOW_MS);\n if (this.recent.length >= TYPING_RATE_LIMIT) return false;\n this.recent.push(now);\n const state = parseTypingFrame(frame);\n if (!state) return false;\n if (state === \"stop\") { this.clear(); return true; }\n if (this.timer) clearTimeout(this.timer);\n this.timer = setTimeout(() => { this.timer = null; this.set(null); }, TYPING_TIMEOUT_MS);\n this.set(parseTypingActivity(frame));\n return true;\n }\n\n /** A message from the contact arrived, or the session ended: it is not typing any more. */\n clear(): void {\n if (this.timer) { clearTimeout(this.timer); this.timer = null; }\n this.set(null);\n }\n\n private set(activity: TypingActivity | null): void {\n if (sameActivity(activity, this.activity)) return;\n this.activity = activity;\n this.onChange(activity !== null, activity);\n }\n}\n","import { fromBase64Url, toBase64Url } from \"./bytes\";\nimport { formatFileSize, sanitizeFileName, sanitizeMime, type FileInfo } from \"./files\";\nimport { pairedReplyAuthor, readReply, wireReply } from \"./replies\";\nimport { readForwarded } from \"./forwards\";\nimport { parseVideoMeta } from \"./video\";\nimport { parseImageMeta } from \"./image\";\nimport { parseVoiceMeta } from \"./voice\";\n\n/**\n * files/3 (WISP 501, revision 0.3): files of any size in a chat, offered before they are sent, sent with\n * many chunks in flight, resumed from the last confirmed byte after a drop, a transport switch or a\n * restart, and checked against the sender's SHA-256 once stored.\n *\n * One `ChatFiles` per chat lives as long as the app does, across sessions: `attach` when a session that\n * agreed `files/3` is open, `detach` when it goes. Records (`FileTransferRecord`) are the state that\n * survives a restart; the host keeps them and hands them back with `restore`.\n *\n * Frames (authenticated JSON on the paired session):\n *\n * sender → receiver pf-offer {id,name,mime,size,ts,voice?,video?,image?,r?,paused?} pf-data {id,offset,data}\n * pf-sum {id,size,digest} pf-abort {id}\n * receiver → sender pf-accept {id,offset} pf-wait {id,why} pf-got {id,offset} pf-done {id}\n * pf-refuse {id,why,room?}\n * both pf-room {max}\n *\n * The offer is repeated on every session until the transfer ends, so every answer is idempotent: the\n * receiver says where it stands (accept from an offset, wait, done, refuse) and the sender goes on from\n * there. Nothing but the offset a receiver confirms moves a sender forward. A receiver may say `pf-accept`\n * again at any time (nothing arrived for a while, or its person asked again): the sender goes on from that offset.\n */\n\nexport const FILE_LIMITS = {\n /** Bytes in one `pf-data` frame: a frame stays far below the 60 KiB a session carries. */\n chunkBytes: 16 * 1024,\n /** Bytes a sender has sent and not seen confirmed: throughput is this per round trip. */\n windowBytes: 1024 * 1024,\n /** A receiver asks its person before taking a file larger than this. */\n askAboveBytes: 25 * 1024 * 1024,\n /** Files taken without asking, per contact, while stored. Past it, every file is asked about. */\n autoAcceptBytesPerPeer: 500 * 1024 * 1024,\n /** Offers from one contact waiting for an answer here. */\n maxWaitingOffers: 16,\n /** Files one contact sends at once; accepted ones beyond wait their turn. */\n maxActiveIncoming: 3,\n /** An offer nobody answered ends after this long. */\n offerTtlMs: 7 * 24 * 60 * 60 * 1000,\n /**\n * No answer while something is outstanding: the sender offers again, which puts both sides back in step. Nothing\n * arriving for a file under way: the receiver says again where it stands.\n */\n idleMs: 30_000,\n /**\n * A transfer that has not moved for this long (or not since the app started) offers its person \"Send again\" or\n * \"Ask again\". Longer than `idleMs`, so what happens on its own has its turn first.\n */\n stallMs: 60_000,\n /** A receiver makes what it stored durable, and records the point to resume from, this often. */\n checkpointBytes: 8 * 1024 * 1024,\n /** A receiver confirms at least this often while writing. */\n ackEveryBytes: 128 * 1024,\n /** A sender reads its file this much at a time. */\n readBytes: 1024 * 1024,\n /** The session refused a data frame (its send budget was full): the sender looks again after this long. */\n sendRetryMs: 250,\n} as const;\n\nconst ID = /^[A-Za-z0-9_-]{8,64}$/;\nconst DIGEST = /^[A-Za-z0-9_-]{43}$/;\nconst DATA = /^[A-Za-z0-9_-]+$/;\n\nexport const FILE_FRAMES = new Set([\"pf-offer\", \"pf-accept\", \"pf-wait\", \"pf-data\", \"pf-got\", \"pf-sum\", \"pf-done\", \"pf-refuse\", \"pf-abort\", \"pf-room\"]);\n\nexport type TransferState =\n /** Out: waiting for a session to offer it on. In: accepted, waiting for its turn or for the sender. */\n | \"queued\"\n /** Out: offered on this session, no answer yet. */\n | \"offered\"\n /** Out: the receiver's person has not decided (or it waits its turn there, `waitingFor: \"busy\"`). In: this person has not decided. */\n | \"asking\"\n | \"active\"\n | \"paused\"\n /** Every byte confirmed; the receiver is checking the digest. */\n | \"verifying\"\n | \"done\"\n | \"failed\"\n | \"declined\"\n | \"cancelled\";\n\nexport interface FileTransferRecord {\n /** The sender's id, on the wire. */\n id: string;\n direction: \"in\" | \"out\";\n file: FileInfo;\n state: TransferState;\n /** Out: bytes the receiver confirmed. In: bytes stored durably, where a restart resumes. */\n confirmed: number;\n /** SHA-256 of the file, base64url: the sender's own, or the one it announced. */\n digest?: string;\n pausedBy?: \"me\" | \"peer\";\n /** Out, `asking`: why the receiver waits. */\n waitingFor?: \"consent\" | \"busy\";\n error?: string;\n /** When the transfer began (the offer), for its time to live. */\n since: number;\n /** In: the person accepted it (not taken on its own). */\n consented?: boolean;\n /**\n * In, `cancelled`: the sender stopped it (`pf-abort`), not this person. Offered again (the sender could not read its\n * file and sends it again), it is taken again from the start.\n */\n abortedBySender?: boolean;\n /**\n * In: taken, by the person or within the app's limits. An offer that expired unanswered never was, so offered again\n * it is refused, never taken without asking.\n */\n agreed?: boolean;\n}\n\nconst FINAL: ReadonlySet<TransferState> = new Set([\"done\", \"failed\", \"declined\", \"cancelled\"]);\nexport const transferEnded = (record: FileTransferRecord): boolean => FINAL.has(record.state);\n\n/**\n * Whether an unfinished transfer is stuck, so its person is offered to push it: \"Send again\" (sending) or \"Ask again\"\n * (receiving). `movedAt` is when its bytes last moved, `restored` that it has not moved since the app started. What\n * waits for a person (an answer, a pause) is not stuck, and a failed one has its own \"Retry\".\n */\nexport function transferStalled(record: FileTransferRecord, movedAt: number, restored: boolean, now: number): boolean {\n if (transferEnded(record) || record.state === \"asking\" || record.state === \"paused\") return false;\n if (record.direction === \"in\" && record.state === \"verifying\") return false;\n return restored || now - movedAt >= FILE_LIMITS.stallMs;\n}\n\n/** A file being sent, read in ranges. */\nexport interface OutgoingSource {\n read(offset: number, length: number): Promise<Uint8Array>;\n /** SHA-256 of the whole file, base64url. */\n digest(): Promise<string>;\n}\n\n/** Where a file being received is written, in order, from `offset`. */\nexport interface IncomingTarget {\n /** Bytes already stored when it was opened: where this transfer resumes. */\n readonly offset: number;\n append(bytes: Uint8Array): Promise<void>;\n /** What was appended is durable. */\n flush(): Promise<void>;\n /** Checks what was stored against the sender's digest, and keeps the file when it matches. */\n verify(digest: string): Promise<boolean>;\n /** Removes what was stored. */\n discard(): Promise<void>;\n}\n\nexport type OfferDecision = \"accept\" | \"ask\" | { refuse: RefuseReason; room?: number };\nexport type RefuseReason = \"declined\" | \"no-room\" | \"too-many\" | \"damaged\" | \"cancelled\" | \"expired\" | \"invalid\";\n\nexport interface ChatFilesHost {\n /** Sends a frame on the open session; false when there is none. */\n send(frame: Record<string, unknown>): boolean;\n /**\n * A new offer from the contact: take it, ask the person, or refuse it. `again`: a file taken here without asking\n * that ended (failed, or stopped by the sender) and is offered again; it counts against the same limits as a new one.\n */\n decide(file: FileInfo, again?: boolean): Promise<OfferDecision>;\n openTarget(record: FileTransferRecord): Promise<IncomingTarget>;\n openSource(record: FileTransferRecord): Promise<OutgoingSource>;\n /**\n * Something changed: keep it and show it. `transferred` is what moved so far (confirmed by the receiver when\n * sending, stored when receiving); `progress` says only that moved, so keeping it can wait.\n */\n changed(record: FileTransferRecord, transferred: number, progress: boolean): void;\n /** Bytes a file may still take here, or null when the platform does not say. */\n room?(): Promise<number | null>;\n /**\n * Resolves once the session can take another data frame without crowding out everything else on it (nothing when it\n * can now: a large file does not make a promise per frame). Channels with a\n * small send budget (native, iroh) refuse frames past it; sending into a full one again at once spun a loop of\n * promises that never let the budget drain, and froze the app.\n */\n writable?(): Promise<void> | undefined;\n now?(): number;\n}\n\ninterface Outgoing {\n source?: Promise<OutgoingSource>;\n next: number;\n /** Bumped whenever sending stops or restarts: a read that finishes late sends nothing. */\n generation: number;\n /**\n * The generation a pump runs for. A pump still waiting on a session that went (a send that never drains) belongs to\n * an older generation, and does not keep the next session's pump from starting.\n */\n pumping?: number;\n block?: { start: number; bytes: Uint8Array };\n timer?: ReturnType<typeof setTimeout>;\n /** A pump that paused on a refused frame starts again. */\n wake?: ReturnType<typeof setTimeout>;\n}\n\ninterface Incoming {\n target?: Promise<IncomingTarget>;\n /** Nothing arrived for a while: the receiver says again where it stands. */\n timer?: ReturnType<typeof setTimeout>;\n /** When data last came (the timer is not moved for every chunk). */\n heardAt?: number;\n /** Bytes taken in order (some may still be on their way to storage). */\n written: number;\n stored: number;\n acked: number;\n checkpointed: number;\n gapAt?: number;\n writing: Promise<void>;\n verifying?: boolean;\n}\n\ninterface Entry {\n record: FileTransferRecord;\n out?: Outgoing;\n in?: Incoming;\n}\n\nconst key = (direction: \"in\" | \"out\", id: string) => `${direction}:${id}`;\nconst isInt = (value: unknown, max = Number.MAX_SAFE_INTEGER): value is number => typeof value === \"number\" && Number.isSafeInteger(value) && value >= 0 && value <= max;\n\n/** What a refusal means to the sender's person. */\nexport function refusalText(why: string, room?: number): { state: TransferState; error: string } {\n switch (why) {\n case \"declined\": return { state: \"declined\", error: \"Declined by your contact\" };\n case \"cancelled\": return { state: \"cancelled\", error: \"Cancelled by your contact\" };\n case \"no-room\": return { state: \"failed\", error: room !== undefined ? `Not enough space on your contact's device (${formatFileSize(room)} free)` : \"Not enough space on your contact's device\" };\n case \"too-many\": return { state: \"failed\", error: \"Your contact has too many files waiting. Try again later.\" };\n case \"damaged\": return { state: \"failed\", error: \"The file arrived damaged and was deleted. Send it again.\" };\n case \"expired\": return { state: \"failed\", error: \"Not accepted in time\" };\n default: return { state: \"failed\", error: \"Your contact could not take this file\" };\n }\n}\n\nexport class ChatFiles {\n private readonly entries = new Map<string, Entry>();\n private attached = false;\n private peerRoomValue: number | null = null;\n\n constructor(private readonly host: ChatFilesHost) {}\n\n private now(): number { return this.host.now?.() ?? Date.now(); }\n\n /** Records kept from before a restart. Nothing is sent until `attach`. */\n restore(records: FileTransferRecord[]): void {\n for (const record of records) {\n const entry: Entry = { record: { ...record } };\n if (record.direction === \"out\") entry.out = { next: record.confirmed, generation: 0 };\n else entry.in = { written: record.confirmed, stored: record.confirmed, acked: record.confirmed, checkpointed: record.confirmed, writing: Promise.resolve() };\n this.entries.set(key(record.direction, record.id), entry);\n }\n }\n\n get live(): boolean { return this.attached; }\n /** What the contact said it can take, on this session. */\n get peerRoom(): number | null { return this.attached ? this.peerRoomValue : null; }\n\n records(): FileTransferRecord[] { return [...this.entries.values()].map((e) => ({ ...e.record })); }\n get(direction: \"in\" | \"out\", id: string): FileTransferRecord | undefined {\n const record = this.entries.get(key(direction, id))?.record;\n return record && { ...record };\n }\n /** Bytes moved so far: confirmed by the receiver (sending), stored (receiving). */\n transferred(direction: \"in\" | \"out\", id: string): number {\n const entry = this.entries.get(key(direction, id));\n return entry ? this.moved(entry) : 0;\n }\n private moved(entry: Entry): number {\n return entry.in && !transferEnded(entry.record) ? Math.max(entry.in.stored, entry.record.confirmed) : entry.record.confirmed;\n }\n\n private changed(entry: Entry, progress = false): void {\n this.host.changed({ ...entry.record }, this.moved(entry), progress);\n }\n\n private send(frame: Record<string, unknown>): boolean {\n return this.attached && this.host.send(frame);\n }\n\n /** A session that agreed files/3 is open: say what fits here, then offer again what is not finished. */\n attach(): void {\n this.attached = true;\n this.peerRoomValue = null;\n void this.sayRoom();\n this.sweep();\n for (const entry of this.entries.values()) {\n if (transferEnded(entry.record)) continue;\n if (entry.record.direction === \"out\") this.announce(entry);\n else if (entry.record.state === \"queued\") this.startIncoming(entry);\n // The sender offers again on its own; one that does not (an older app, one stuck) is asked.\n else if (entry.record.state === \"active\") this.armNudge(entry);\n }\n }\n\n /** The session went: nothing moves until the next one. What was received is made durable. */\n detach(): void {\n if (!this.attached) return;\n this.attached = false;\n this.peerRoomValue = null;\n for (const entry of this.entries.values()) {\n if (entry.out) { entry.out.generation++; clearTimeout(entry.out.timer); clearTimeout(entry.out.wake); entry.out.next = entry.record.confirmed; }\n if (entry.in) clearTimeout(entry.in.timer);\n if (entry.in && !transferEnded(entry.record)) void this.checkpoint(entry);\n }\n }\n\n /** Offers ended by their time to live. The host calls it now and then; `attach` does too. */\n sweep(): void {\n const now = this.now();\n for (const entry of this.entries.values()) {\n const { record } = entry;\n if (![\"queued\", \"offered\", \"asking\"].includes(record.state) || record.since + FILE_LIMITS.offerTtlMs > now) continue;\n if (record.direction === \"in\" && record.state === \"queued\") continue;\n this.end(entry, \"failed\", record.direction === \"in\" ? \"The offer expired\" : record.state === \"queued\" ? \"Not delivered in time\" : \"Not accepted in time\");\n if (record.direction === \"in\") this.send({ t: \"pf-refuse\", id: record.id, why: \"expired\" });\n }\n }\n\n private async sayRoom(): Promise<void> {\n const room = await this.host.room?.().catch(() => null);\n if (typeof room === \"number\" && Number.isSafeInteger(room)) this.send({ t: \"pf-room\", max: room });\n }\n\n // ─── Sending ────────────────────────────────────────────────────────────\n\n /** A new file to send: offered now if a session is open, or on the next one. */\n offer(file: FileInfo, digest?: string): FileTransferRecord {\n if (!ID.test(file.id) || !isInt(file.size)) throw new Error(\"Invalid file\");\n if (this.entries.has(key(\"out\", file.id))) throw new Error(\"File transfer already active\");\n const entry: Entry = {\n record: { id: file.id, direction: \"out\", file, state: \"queued\", confirmed: 0, since: this.now(), ...(digest && { digest }) },\n out: { next: 0, generation: 0 },\n };\n this.entries.set(key(\"out\", file.id), entry);\n this.changed(entry);\n if (this.attached) this.announce(entry);\n return { ...entry.record };\n }\n\n private offerFrame(record: FileTransferRecord, paused: boolean): Record<string, unknown> {\n const { file } = record;\n return { t: \"pf-offer\", id: file.id, name: file.name, mime: file.mime, size: file.size, ts: file.timestamp, ...(file.voice && { voice: file.voice }), ...(file.video && { video: file.video }), ...(file.image && { image: file.image }), ...(file.reply && { r: wireReply(file.reply) }), ...(readForwarded(file.forwarded) && { fw: file.forwarded }), ...(paused && { paused: true }) };\n }\n\n private announce(entry: Entry): void {\n const { record } = entry, out = entry.out!;\n out.generation++;\n out.next = record.confirmed;\n const paused = record.state === \"paused\" && record.pausedBy === \"me\";\n if (!this.send(this.offerFrame(record, paused))) return;\n if (!paused && record.state !== \"offered\") { record.state = \"offered\"; record.pausedBy = undefined; this.changed(entry); }\n this.armStall(entry);\n }\n\n private armStall(entry: Entry): void {\n const out = entry.out!;\n clearTimeout(out.timer);\n if (!this.attached || transferEnded(entry.record) || entry.record.state === \"paused\" || entry.record.state === \"asking\") return;\n out.timer = setTimeout(() => {\n // Nothing came back: what is on its way was lost (a switch, a drop not seen yet). Offering again resyncs.\n if (this.attached && !transferEnded(entry.record) && entry.record.state !== \"paused\" && entry.record.state !== \"asking\") this.announce(entry);\n }, FILE_LIMITS.idleMs);\n }\n\n private source(entry: Entry): Promise<OutgoingSource> {\n const out = entry.out!;\n out.source ??= this.host.openSource(entry.record);\n out.source.catch(() => { out.source = undefined; });\n return out.source;\n }\n\n private async chunkAt(entry: Entry, offset: number): Promise<Uint8Array> {\n const out = entry.out!, size = entry.record.file.size;\n const length = Math.min(FILE_LIMITS.chunkBytes, size - offset);\n const block = out.block;\n if (!block || offset < block.start || offset + length > block.start + block.bytes.length) {\n const bytes = await (await this.source(entry)).read(offset, Math.min(FILE_LIMITS.readBytes, size - offset));\n if (!bytes.length) throw new Error(\"The file is shorter than it says\");\n out.block = { start: offset, bytes };\n }\n const at = offset - out.block!.start;\n return out.block!.bytes.subarray(at, at + length);\n }\n\n private shouldPump(entry: Entry): boolean {\n const { record } = entry, out = entry.out!;\n return this.attached && record.state === \"active\" && out.next < record.file.size && out.next - record.confirmed < FILE_LIMITS.windowBytes;\n }\n\n private async pump(entry: Entry): Promise<void> {\n const out = entry.out!;\n if (out.pumping === out.generation) return;\n const generation = out.pumping = out.generation;\n clearTimeout(out.wake);\n let refused = false;\n try {\n while (this.shouldPump(entry) && generation === out.generation) {\n const offset = out.next;\n const chunk = await this.chunkAt(entry, offset);\n const room = this.host.writable?.();\n if (room) await room;\n if (generation !== out.generation || !this.shouldPump(entry) || out.next !== offset) break;\n if (!this.send({ t: \"pf-data\", id: entry.record.id, offset, data: toBase64Url(chunk) })) { refused = true; break; }\n out.next = offset + chunk.length;\n }\n } catch (error) {\n if (generation === out.generation) {\n this.send({ t: \"pf-abort\", id: entry.record.id });\n this.end(entry, \"failed\", `Could not read the file: ${error instanceof Error ? error.message : String(error)}`);\n }\n } finally {\n if (out.pumping === generation) out.pumping = undefined;\n if (generation === out.generation && this.shouldPump(entry)) {\n // A refused frame is tried again later, never at once: at once, nothing else would run until it went.\n if (refused) out.wake = setTimeout(() => void this.pump(entry), FILE_LIMITS.sendRetryMs);\n else void this.pump(entry);\n }\n }\n }\n\n private async sendSum(entry: Entry): Promise<void> {\n const { record } = entry;\n if (record.state !== \"verifying\") { record.state = \"verifying\"; this.changed(entry); }\n try {\n record.digest ??= await (await this.source(entry)).digest();\n } catch (error) {\n this.send({ t: \"pf-abort\", id: record.id });\n this.end(entry, \"failed\", `Could not read the file: ${error instanceof Error ? error.message : String(error)}`);\n return;\n }\n if (record.state !== \"verifying\") return;\n this.send({ t: \"pf-sum\", id: record.id, size: record.file.size, digest: record.digest });\n this.armStall(entry);\n }\n\n // ─── Receiving ──────────────────────────────────────────────────────────\n\n private waitingOffers(): number {\n return [...this.entries.values()].filter((e) => e.record.direction === \"in\" && e.record.state === \"asking\").length;\n }\n\n private activeIncoming(except: Entry): number {\n return [...this.entries.values()].filter((e) => e !== except && e.record.direction === \"in\" && (e.record.state === \"active\" || e.record.state === \"verifying\")).length;\n }\n\n /**\n * Removes what this side stored of an incoming file that ends here. Through its target, opened now if need be: after\n * a restart a transfer has none until bytes arrive again, and one cancelled before that kept its part on the disk.\n */\n private discardStored(entry: Entry): Promise<void> {\n return entry.in ? this.target(entry).then((t) => t.discard()).catch(() => {}) : Promise.resolve();\n }\n\n private target(entry: Entry): Promise<IncomingTarget> {\n const incoming = entry.in!;\n if (!incoming.target) {\n incoming.target = this.host.openTarget(entry.record).then((target) => {\n incoming.written = incoming.stored = incoming.acked = incoming.checkpointed = target.offset;\n return target;\n });\n incoming.target.catch(() => { incoming.target = undefined; });\n }\n return incoming.target;\n }\n\n /** An accepted file goes when a session is open and fewer than three others arrive at once. */\n private startIncoming(entry: Entry): void {\n if (!this.attached || transferEnded(entry.record) || entry.record.state === \"asking\" || (entry.record.state === \"paused\" && entry.record.pausedBy === \"me\")) return;\n if (this.activeIncoming(entry) >= FILE_LIMITS.maxActiveIncoming) {\n if (entry.record.state !== \"queued\") { entry.record.state = \"queued\"; this.changed(entry); }\n this.send({ t: \"pf-wait\", id: entry.record.id, why: \"busy\" });\n return;\n }\n void this.target(entry).then(() => {\n if (transferEnded(entry.record) || !this.attached) return;\n if (entry.record.state === \"paused\" && entry.record.pausedBy === \"me\") return;\n entry.record.state = \"active\";\n entry.record.pausedBy = undefined;\n entry.in!.gapAt = undefined;\n this.changed(entry);\n this.send({ t: \"pf-accept\", id: entry.record.id, offset: entry.in!.written });\n entry.in!.heardAt = this.now();\n this.armNudge(entry);\n }, (error) => {\n this.end(entry, \"failed\", `Could not store the file: ${error instanceof Error ? error.message : String(error)}`);\n this.send({ t: \"pf-refuse\", id: entry.record.id, why: \"no-room\" });\n });\n }\n\n /** The next file waiting its turn, once one ends. */\n private startNext(): void {\n for (const entry of this.entries.values()) if (entry.record.direction === \"in\" && entry.record.state === \"queued\") this.startIncoming(entry);\n }\n\n private async onOffer(frame: Record<string, unknown>): Promise<void> {\n const id = frame.id as string;\n if (typeof frame.name !== \"string\" || frame.name.length > 1000 || typeof frame.mime !== \"string\" || frame.mime.length > 130 ||\n !isInt(frame.size) || !isInt(frame.ts) || frame.ts === 0) { this.send({ t: \"pf-refuse\", id, why: \"invalid\" }); return; }\n const file: FileInfo = { id, name: sanitizeFileName(frame.name), mime: sanitizeMime(frame.mime), size: frame.size, timestamp: frame.ts };\n const voice = parseVoiceMeta(frame.voice, file.mime);\n if (voice) file.voice = voice;\n const video = parseVideoMeta(frame.video, file.mime);\n if (video) file.video = video;\n const image = parseImageMeta(frame.image, file.mime);\n if (image) file.image = image;\n const reply = readReply(frame.r, pairedReplyAuthor);\n if (reply) file.reply = reply;\n const forwarded = readForwarded(frame.fw);\n if (forwarded) file.forwarded = forwarded;\n const paused = frame.paused === true;\n const existing = this.entries.get(key(\"in\", id));\n if (existing) {\n const { record } = existing;\n if (record.file.size !== file.size || record.file.name !== file.name) { this.send({ t: \"pf-refuse\", id, why: \"invalid\" }); return; }\n switch (record.state) {\n case \"done\": this.send({ t: \"pf-done\", id }); return;\n case \"declined\": this.send({ t: \"pf-refuse\", id, why: \"declined\" }); return;\n case \"cancelled\":\n // Cancelled here stays cancelled; stopped by the sender (it could not read its file), it is taken again.\n if (!record.abortedBySender || (!record.agreed && !record.consented)) { this.send({ t: \"pf-refuse\", id, why: \"cancelled\" }); return; }\n if (!(await this.retakeAgreed(existing))) return;\n break;\n case \"failed\":\n // Expired here before anyone answered: nothing was agreed, and offered again it would skip the question.\n if (!record.agreed && !record.consented) { this.send({ t: \"pf-refuse\", id, why: \"expired\" }); return; }\n // Sent again after it failed here (damaged, not stored): taken again from the start, as it was agreed.\n if (!(await this.retakeAgreed(existing))) return;\n break;\n case \"asking\": this.send({ t: \"pf-wait\", id, why: \"consent\" }); return;\n }\n if (record.state === \"paused\" && record.pausedBy === \"me\") { this.send({ t: \"pf-wait\", id, why: \"paused\" }); return; }\n if (paused) {\n if (record.state !== \"paused\") { record.state = \"paused\"; record.pausedBy = \"peer\"; this.changed(existing); void this.checkpoint(existing); }\n return;\n }\n if (record.state === \"paused\") { record.state = \"queued\"; record.pausedBy = undefined; this.changed(existing); }\n this.startIncoming(existing);\n return;\n }\n if (this.waitingOffers() >= FILE_LIMITS.maxWaitingOffers) { this.send({ t: \"pf-refuse\", id, why: \"too-many\" }); return; }\n let decision: OfferDecision;\n try { decision = await this.host.decide(file); } catch { decision = { refuse: \"invalid\" }; }\n if (this.entries.has(key(\"in\", id))) return;\n if (typeof decision === \"object\") { this.send({ t: \"pf-refuse\", id, why: decision.refuse, ...(decision.room !== undefined && { room: decision.room }) }); return; }\n const entry: Entry = {\n record: { id, direction: \"in\", file, state: decision === \"ask\" ? \"asking\" : paused ? \"paused\" : \"queued\", confirmed: 0, since: this.now(),\n ...(decision !== \"ask\" && { agreed: true }), ...(paused && decision !== \"ask\" && { pausedBy: \"peer\" as const }) },\n in: { written: 0, stored: 0, acked: 0, checkpointed: 0, writing: Promise.resolve() },\n };\n this.entries.set(key(\"in\", id), entry);\n this.changed(entry);\n if (decision === \"ask\") this.send({ t: \"pf-wait\", id, why: \"consent\" });\n else if (!paused) this.startIncoming(entry);\n }\n\n /**\n * An agreed file that ended here, offered again. One the person consented to is taken again from the start; one taken\n * without asking is decided again, as a new offer would be (the contact's limit for files taken without asking, and\n * the room here): taken again, asked about, or refused. True when it is taken again.\n */\n private async retakeAgreed(entry: Entry): Promise<boolean> {\n const { record } = entry;\n if (!record.consented) {\n let decision: OfferDecision;\n try { decision = await this.host.decide(record.file, true); } catch { decision = { refuse: \"invalid\" }; }\n if (typeof decision === \"object\") { this.send({ t: \"pf-refuse\", id: record.id, why: decision.refuse, ...(decision.room !== undefined && { room: decision.room }) }); return false; }\n if (decision === \"ask\") {\n if (this.waitingOffers() >= FILE_LIMITS.maxWaitingOffers) { this.send({ t: \"pf-refuse\", id: record.id, why: \"too-many\" }); return false; }\n await this.retake(entry);\n record.state = \"asking\";\n record.agreed = undefined;\n this.changed(entry);\n this.send({ t: \"pf-wait\", id: record.id, why: \"consent\" });\n return false;\n }\n }\n await this.retake(entry);\n return true;\n }\n\n /** An agreed file that ended here, offered again: taken again from the start. */\n private async retake(entry: Entry): Promise<void> {\n await entry.in?.target?.then((t) => t.discard()).catch(() => {});\n entry.in = { written: 0, stored: 0, acked: 0, checkpointed: 0, writing: Promise.resolve() };\n Object.assign(entry.record, { state: \"queued\", confirmed: 0, error: undefined, digest: undefined, abortedBySender: undefined, since: this.now() });\n this.changed(entry);\n }\n\n private onData(entry: Entry, frame: Record<string, unknown>): void {\n const { record } = entry, incoming = entry.in!;\n if (record.state !== \"active\" || !incoming.target) return;\n incoming.heardAt = this.now();\n const { offset, data } = frame;\n if (!isInt(offset) || typeof data !== \"string\" || !data.length || data.length > Math.ceil(FILE_LIMITS.chunkBytes * 4 / 3) || !DATA.test(data)) return;\n if (offset < incoming.written) {\n // Sent again (a resync): already taken. Said again only once everything is stored.\n if (incoming.stored === incoming.written) this.send({ t: \"pf-got\", id: record.id, offset: incoming.stored });\n return;\n }\n if (offset > incoming.written) {\n // Something before this was lost: once per gap, the sender is told where to go on from.\n if (incoming.gapAt !== incoming.written) { incoming.gapAt = incoming.written; this.send({ t: \"pf-accept\", id: record.id, offset: incoming.written }); }\n return;\n }\n const bytes = fromBase64Url(data);\n if (!bytes.length || bytes.length > FILE_LIMITS.chunkBytes || offset + bytes.length > record.file.size) return;\n incoming.written += bytes.length;\n const target = incoming.target;\n incoming.writing = incoming.writing.then(async () => {\n if (transferEnded(record)) return;\n await (await target).append(bytes);\n incoming.stored = offset + bytes.length;\n const drained = incoming.stored === incoming.written;\n if (drained || incoming.stored - incoming.acked >= FILE_LIMITS.ackEveryBytes) {\n incoming.acked = incoming.stored;\n this.send({ t: \"pf-got\", id: record.id, offset: incoming.stored });\n this.changed(entry, true);\n }\n // Inside the chain of writes: waiting for the chain here would wait for itself.\n if (incoming.stored - incoming.checkpointed >= FILE_LIMITS.checkpointBytes) await this.checkpointNow(entry);\n }).catch((error) => {\n if (transferEnded(record)) return;\n this.end(entry, \"failed\", `Could not store the file: ${error instanceof Error ? error.message : String(error)}`);\n void target.then((t) => t.discard()).catch(() => {});\n this.send({ t: \"pf-refuse\", id: record.id, why: \"no-room\" });\n this.startNext();\n });\n }\n\n /** What was stored is durable, and a restart resumes from there. */\n private async checkpoint(entry: Entry): Promise<void> {\n if (!entry.in?.target) return;\n await entry.in.writing;\n await this.checkpointNow(entry);\n }\n\n private async checkpointNow(entry: Entry): Promise<void> {\n const incoming = entry.in!;\n if (!incoming.target) return;\n const stored = incoming.stored;\n if (stored === incoming.checkpointed && entry.record.confirmed === stored) return;\n try { await (await incoming.target).flush(); } catch { return; }\n incoming.checkpointed = Math.max(incoming.checkpointed, stored);\n // Kept at once (not as progress): a restart resumes from here.\n if (!transferEnded(entry.record)) { entry.record.confirmed = incoming.checkpointed; this.changed(entry); }\n }\n\n /** Nothing arrives for a file under way: after a quiet spell the receiver says again where it stands. */\n private armNudge(entry: Entry, delay: number = FILE_LIMITS.idleMs): void {\n const incoming = entry.in!;\n clearTimeout(incoming.timer);\n if (!this.attached || entry.record.state !== \"active\") return;\n incoming.heardAt ??= this.now();\n incoming.timer = setTimeout(() => {\n const quiet = this.now() - (incoming.heardAt ?? 0);\n if (quiet < FILE_LIMITS.idleMs) this.armNudge(entry, FILE_LIMITS.idleMs - quiet);\n else void this.askAgain(entry);\n }, delay);\n }\n\n /** `pf-accept` from what is taken here: the sender goes on from there (it ignores it for a transfer that ended). */\n private async askAgain(entry: Entry): Promise<void> {\n const { record } = entry, incoming = entry.in!;\n // Kept from before a restart, not opened yet: opening it says where it stands.\n if (!incoming.target) { this.startIncoming(entry); return; }\n await incoming.writing;\n if (!this.attached || record.state !== \"active\" || incoming.verifying) return;\n incoming.gapAt = undefined;\n this.send({ t: \"pf-accept\", id: record.id, offset: incoming.written });\n incoming.heardAt = this.now();\n this.armNudge(entry);\n }\n\n private async onSum(entry: Entry, frame: Record<string, unknown>): Promise<void> {\n const { record } = entry, incoming = entry.in!;\n if (record.state === \"done\") { this.send({ t: \"pf-done\", id: record.id }); return; }\n if (record.state !== \"active\" || incoming.verifying || !incoming.target) return;\n if (frame.size !== record.file.size || typeof frame.digest !== \"string\" || !DIGEST.test(frame.digest)) return;\n const digest = frame.digest;\n await incoming.writing;\n if (transferEnded(record) || record.state !== \"active\") return;\n if (incoming.stored < record.file.size) { this.send({ t: \"pf-accept\", id: record.id, offset: incoming.stored }); return; }\n incoming.verifying = true;\n record.state = \"verifying\";\n this.changed(entry);\n let ok: boolean;\n try { const target = await incoming.target; await target.flush(); ok = await target.verify(digest); } catch { ok = false; }\n incoming.verifying = false;\n if (transferEnded(record)) return;\n if (ok) {\n record.digest = digest;\n record.confirmed = record.file.size;\n this.end(entry, \"done\");\n this.send({ t: \"pf-done\", id: record.id });\n } else {\n await incoming.target.then((t) => t.discard()).catch(() => {});\n this.end(entry, \"failed\", \"The file arrived damaged and was deleted. Ask for it again.\");\n this.send({ t: \"pf-refuse\", id: record.id, why: \"damaged\" });\n }\n this.startNext();\n }\n\n // ─── Frames ─────────────────────────────────────────────────────────────\n\n /** A frame from the contact (one of `FILE_FRAMES`). Returns quickly: writing goes on in the background. */\n async handle(frame: Record<string, unknown>): Promise<void> {\n if (!this.attached) return;\n if (frame.t === \"pf-room\") { if (isInt(frame.max)) this.peerRoomValue = frame.max; return; }\n if (typeof frame.id !== \"string\" || !ID.test(frame.id)) return;\n const id = frame.id;\n if (frame.t === \"pf-offer\") return this.onOffer(frame);\n const incoming = this.entries.get(key(\"in\", id)), outgoing = this.entries.get(key(\"out\", id));\n if (frame.t === \"pf-data\") { if (incoming) this.onData(incoming, frame); return; }\n if (frame.t === \"pf-sum\") { if (incoming) await this.onSum(incoming, frame); return; }\n if (frame.t === \"pf-abort\") {\n if (incoming && !transferEnded(incoming.record)) {\n await this.discardStored(incoming);\n incoming.record.abortedBySender = true;\n this.end(incoming, \"cancelled\", \"Cancelled by the sender\");\n this.startNext();\n }\n return;\n }\n if (!outgoing || transferEnded(outgoing.record)) {\n if (outgoing?.record.state === \"cancelled\" && frame.t !== \"pf-refuse\") this.send({ t: \"pf-abort\", id });\n return;\n }\n const { record } = outgoing, out = outgoing.out!;\n switch (frame.t) {\n case \"pf-accept\": {\n if (!isInt(frame.offset, record.file.size)) return;\n if (record.state === \"paused\" && record.pausedBy === \"me\") return;\n out.generation++;\n record.confirmed = frame.offset;\n out.next = frame.offset;\n record.state = \"active\";\n record.pausedBy = record.waitingFor = undefined;\n this.changed(outgoing);\n if (record.confirmed === record.file.size) void this.sendSum(outgoing);\n else { this.armStall(outgoing); void this.pump(outgoing); }\n return;\n }\n case \"pf-wait\": {\n out.generation++;\n out.next = record.confirmed;\n clearTimeout(out.timer);\n if (frame.why === \"paused\") { record.state = \"paused\"; record.pausedBy = \"peer\"; }\n else { record.state = \"asking\"; record.waitingFor = frame.why === \"busy\" ? \"busy\" : \"consent\"; }\n this.changed(outgoing);\n return;\n }\n case \"pf-got\": {\n if (!isInt(frame.offset) || frame.offset <= record.confirmed || frame.offset > out.next) return;\n record.confirmed = frame.offset;\n this.changed(outgoing, true);\n if (record.confirmed === record.file.size) void this.sendSum(outgoing);\n else { this.armStall(outgoing); void this.pump(outgoing); }\n return;\n }\n case \"pf-done\": {\n if (record.state !== \"verifying\") return;\n record.confirmed = record.file.size;\n this.end(outgoing, \"done\");\n return;\n }\n case \"pf-refuse\": {\n const { state, error } = refusalText(String(frame.why), isInt(frame.room) ? frame.room : undefined);\n this.end(outgoing, state, error);\n return;\n }\n }\n }\n\n private end(entry: Entry, state: TransferState, error?: string): void {\n const { record } = entry;\n record.state = state;\n record.error = error;\n record.pausedBy = record.waitingFor = undefined;\n if (entry.out) { entry.out.generation++; clearTimeout(entry.out.timer); clearTimeout(entry.out.wake); entry.out.block = undefined; entry.out.source = undefined; }\n if (entry.in) clearTimeout(entry.in.timer);\n this.changed(entry);\n }\n\n // ─── What the person does ───────────────────────────────────────────────\n\n private entry(direction: \"in\" | \"out\", id: string): Entry {\n const entry = this.entries.get(key(direction, id));\n if (!entry) throw new Error(\"No such file transfer\");\n return entry;\n }\n\n /** Takes an offered file. */\n accept(id: string): void {\n const entry = this.entry(\"in\", id);\n if (entry.record.state !== \"asking\") return;\n entry.record.state = \"queued\";\n entry.record.consented = entry.record.agreed = true;\n this.changed(entry);\n this.startIncoming(entry);\n }\n\n decline(id: string): void {\n const entry = this.entry(\"in\", id);\n if (transferEnded(entry.record)) return;\n void this.discardStored(entry);\n this.end(entry, \"declined\", \"You declined it\");\n this.send({ t: \"pf-refuse\", id, why: \"declined\" });\n this.startNext();\n }\n\n pause(direction: \"in\" | \"out\", id: string): void {\n const entry = this.entry(direction, id), { record } = entry;\n if (transferEnded(record) || record.state === \"verifying\" || (record.state === \"paused\" && record.pausedBy === \"me\")) return;\n if (direction === \"in\" && record.state === \"asking\") return;\n record.state = \"paused\";\n record.pausedBy = \"me\";\n if (entry.out) { entry.out.generation++; entry.out.next = record.confirmed; clearTimeout(entry.out.timer); this.send(this.offerFrame(record, true)); }\n else { this.send({ t: \"pf-wait\", id, why: \"paused\" }); void this.checkpoint(entry); this.startNext(); }\n this.changed(entry);\n }\n\n resume(direction: \"in\" | \"out\", id: string): void {\n const entry = this.entry(direction, id), { record } = entry;\n if (record.state !== \"paused\" || record.pausedBy !== \"me\") return;\n record.pausedBy = undefined;\n record.state = \"queued\";\n this.changed(entry);\n if (direction === \"out\") { if (this.attached) this.announce(entry); }\n else this.startIncoming(entry);\n }\n\n cancel(direction: \"in\" | \"out\", id: string): void {\n const entry = this.entry(direction, id);\n if (transferEnded(entry.record)) return;\n if (direction === \"out\") {\n this.send({ t: \"pf-abort\", id });\n this.end(entry, \"cancelled\", \"You cancelled it\");\n } else {\n void this.discardStored(entry);\n this.end(entry, \"cancelled\", \"You cancelled it\");\n this.send({ t: \"pf-refuse\", id, why: \"cancelled\" });\n this.startNext();\n }\n }\n\n /** A sent file that failed, offered again from the start under its id. */\n retry(id: string): void {\n const entry = this.entry(\"out\", id), { record } = entry;\n if (record.state !== \"failed\") return;\n // The digest is taken again from the source: the one sent last time may be what was wrong.\n Object.assign(record, { state: \"queued\", confirmed: 0, error: undefined, digest: undefined, since: this.now() });\n clearTimeout(entry.out!.timer); clearTimeout(entry.out!.wake);\n entry.out = { next: 0, generation: entry.out!.generation + 1 };\n this.changed(entry);\n if (this.attached) this.announce(entry);\n }\n\n /**\n * \"Send again\": a sent file that failed, or stopped moving, is offered again now (or on the next session). The\n * receiver answers from what it holds, so it goes on from there, never twice in the chat: same id, same message.\n * True when it went now.\n */\n resend(id: string): boolean {\n const entry = this.entry(\"out\", id), { record } = entry;\n if (record.state === \"failed\") { this.retry(id); return this.attached; }\n if (transferEnded(record)) return false;\n if (record.state === \"paused\" && record.pausedBy === \"me\") { this.resume(\"out\", id); return this.attached; }\n if (!this.attached) return false;\n this.announce(entry);\n return true;\n }\n\n /**\n * \"Ask again\": a file that stopped arriving is asked for again from where it stands here. No new frame: a receiver's\n * `pf-accept` already moves any files/3 sender to its offset.\n * True when it went now.\n */\n request(id: string): boolean {\n const entry = this.entry(\"in\", id), { record } = entry;\n if (transferEnded(record) || record.state === \"asking\" || record.state === \"verifying\") return false;\n if (record.state === \"paused\") {\n if (record.pausedBy !== \"me\") return false;\n this.resume(\"in\", id);\n return this.attached;\n }\n if (!this.attached) return false;\n if (record.state === \"queued\") this.startIncoming(entry);\n else void this.askAgain(entry);\n return true;\n }\n\n /** Forgets a transfer (its message was deleted): an unfinished one is cancelled on both sides first. */\n forget(direction: \"in\" | \"out\", id: string): void {\n const entry = this.entries.get(key(direction, id));\n if (!entry) return;\n if (!transferEnded(entry.record)) this.cancel(direction, id);\n this.entries.delete(key(direction, id));\n }\n}\n","import { CALL_SIGNAL_MAX_AGE_MS, heardCallSignal, parseCallSignal } from \"./callSignal\";\n\n/**\n * Call signaling on a paired session (`calls/1`, WISP 601): the same compact signals a compatibility chat\n * keeps in its `_call` record, carried as one frame each on the authenticated session instead:\n *\n * {\"t\":\"paired-call\",\"s\":\"<signal>\"}\n *\n * The media is not on this session: it runs on a WebRTC connection of its own, whose DTLS fingerprint the\n * offer and answer carry, so whatever carries the chat (WebRTC, Iroh, HyperDHT) the media is bound to the\n * contact this session authenticated. Nothing about a call is ever published on the DHT.\n */\nexport const PAIRED_CALL_FRAME = \"paired-call\";\n\n/** A compact offer with the eight candidates `parseCallSignal` accepts stays well under this. */\nexport const MAX_PAIRED_CALL_SIGNAL = 8192;\n\nexport interface PairedCallFrame { t: typeof PAIRED_CALL_FRAME; s: string }\n\n/**\n * The signal a `paired-call` frame carries, or null when it is not a well-formed one. It came on a live session, now:\n * that is when it was heard (`heardCallSignal`), whatever time its sender's clock gave it.\n */\nexport function parsePairedCallFrame(frame: Record<string, unknown>, now = Date.now()): string | null {\n const signal = frame.s;\n if (typeof signal !== \"string\" || signal.length > MAX_PAIRED_CALL_SIGNAL) return null;\n const heard = heardCallSignal(signal, now);\n return parseCallSignal(heard, now) ? heard : null;\n}\n\nfunction signalKind(signal: string): unknown {\n try { return (JSON.parse(signal) as { t?: unknown }).t; } catch { return undefined; }\n}\n\n/**\n * This side's latest call signal. What could not go (the session dropped between two frames) goes on the\n * next session while it is still fresh, as the `_call` record of a compatibility chat is read on the next\n * poll; clearing it (after a hang-up, or a call that never started) means there is nothing left to say.\n */\nexport class PairedCalls {\n private latest: { signal: string; at: number } | null = null;\n /** When this side last said anything about a call, a clear included. */\n private saidAt = 0;\n /** The contact's latest signal on a session: its kind, when it came, and the time its sender gave it. */\n private peer: { kind: unknown; at: number; ts: number } | null = null;\n\n constructor(private readonly now: () => number = Date.now) {}\n\n /** A new signal to send (null clears it). Returns the frame to send now, if any. */\n set(signal: string | null): PairedCallFrame | null {\n this.saidAt = this.now();\n if (signal === null) { this.latest = null; return null; }\n if (signal.length > MAX_PAIRED_CALL_SIGNAL) throw new Error(\"Call signal too large\");\n this.latest = { signal, at: this.saidAt };\n return { t: PAIRED_CALL_FRAME, s: signal };\n }\n\n /**\n * A signal the contact sent (one `parsePairedCallFrame` took). False when it is not newer than the last one heard\n * from the contact on this chat: the contact's app sends its latest signal again on each new session while it is\n * fresh (`pending`), and one already heard is not news. Handed on again, an answer to an earlier call that names no\n * offer (an app up to 1.0.2) was taken for the call placed since, whose connection it does not fit.\n */\n heard(signal: string): boolean {\n let ts = NaN;\n try { ts = Number((JSON.parse(signal) as { ts?: unknown }).ts); } catch { /* not a signal the parser took */ }\n if (!Number.isFinite(ts) || (this.peer && ts <= this.peer.ts)) return false;\n this.peer = { kind: signalKind(signal), at: this.now(), ts };\n return true;\n }\n\n /**\n * Whether a call is on, as the latest signal of either side says: an answer or a picture change, or an offer still\n * fresh enough to ring. A hang-up, a clear, or an offer nobody answered in time ends it. Read by kind, as `hangUp` is:\n * the media runs on a connection of its own, so an hour-long call shows nothing else on the session.\n */\n get on(): boolean {\n const own = { kind: this.latest ? signalKind(this.latest.signal) : \"h\", at: this.saidAt };\n const last = this.peer && this.peer.at > own.at ? this.peer : own;\n // A restart offer (`r`) is only sent on a call that is on.\n if (last.kind === \"a\" || last.kind === \"v\" || last.kind === \"r\") return true;\n return last.kind === \"o\" && this.now() - last.at <= CALL_SIGNAL_MAX_AGE_MS;\n }\n\n /**\n * This app is going away: a call it placed or answered is hung up, as its person would (its latest signal an\n * offer, an answer, or a picture change, which only a call on sends). Returns the hang-up to send before the\n * goodbye, or null when no call of ours is on. Read by its kind, not its age: a call outlasts its offer's freshness.\n */\n hangUp(): PairedCallFrame | null {\n if (!this.latest) return null;\n let said: { t?: unknown; ts?: unknown };\n try { said = JSON.parse(this.latest.signal); } catch { return null; }\n if (said.t !== \"o\" && said.t !== \"a\" && said.t !== \"v\" && said.t !== \"r\") return null;\n const ts = Math.max(this.now(), typeof said.ts === \"number\" ? said.ts + 1 : 0);\n return this.set(JSON.stringify({ t: \"h\", ts }));\n }\n\n /** On a new session that carries calls: the latest signal again, if it is still fresh enough to act on. */\n pending(): PairedCallFrame | null {\n if (!this.latest) return null;\n if (this.now() - this.latest.at > CALL_SIGNAL_MAX_AGE_MS) { this.latest = null; return null; }\n return { t: PAIRED_CALL_FRAME, s: this.latest.signal };\n }\n}\n","import { xchacha20poly1305 } from \"@noble/ciphers/chacha.js\";\nimport { hkdf } from \"@noble/hashes/hkdf.js\";\nimport { sha256 } from \"@noble/hashes/sha2.js\";\nimport { concatBytes, fromBase64Url, randomBytes, toBase64Url, toZ32, utf8Encode } from \"./bytes\";\nimport { identityFromSeed, publicKeyFromZ32, type Identity } from \"./identity\";\nimport type { GhostRecord } from \"./pkarr\";\n\n/**\n * Where the members of a `group-community/1` group find each other (WISP 9xx · Group Community §\n * Topology): a **beacon** listing the current hubs, and a **lobby** per hub where a member asks\n * that hub for an edge. Both are Pkarr records under identities every member derives from the\n * group's rendezvous secret, sealed with a key from the same secret: relays see a key and an\n * opaque value. Several members write the same record, so a writer reads, merges and publishes.\n */\nexport const COMMUNITY_TOPOLOGY = {\n /** Hubs listed in the beacon, at most. */\n maxHubs: 8,\n /** Hubs wanted before a member settles for being a member. */\n minHubs: 2,\n /** Members one hub keeps edges with. */\n hubCapacity: 48,\n /** Hubs a member connects to: one; if it goes, the member asks another and a sync fills the gap. */\n hubsPerMember: 1,\n /** A hub republishes its beacon entry this often… */\n beaconEveryMs: 30_000,\n /** …and an entry older than this is stale. */\n beaconFreshMs: 90_000,\n /** Lobby entries: at most this many, fresh this long. */\n lobbyEntries: 6,\n lobbyFreshMs: 2 * 60_000,\n} as const;\n\nconst NONCE_LENGTH = 24;\nconst BEACON_LABEL = \"_hubs\";\nconst HEAD_LABEL = \"_head\";\nconst LOBBY_LABEL = \"_lobby\";\nconst info = (label: string) => utf8Encode(`ghostly-group-community/1 ${label}`);\n\n/** A hub as the beacon lists it: when it last said so (`ts`), since when it is a hub, and how many members it holds. */\nexport interface Hub { key: string; ts: number; load: number; since?: number }\nexport interface LobbyEntry { key: string; ts: number }\n\nfunction material(rv: Uint8Array, salt: Uint8Array, label: string): Uint8Array {\n return hkdf(sha256, rv, salt, info(label), 32);\n}\n\n/** The beacon's Pkarr identity and sealing key, for a group. */\nexport function beaconKeys(rvB64: string, groupId: string): { identity: Identity; key: Uint8Array; aad: Uint8Array } {\n const rv = fromBase64Url(rvB64), salt = utf8Encode(groupId);\n return { identity: identityFromSeed(material(rv, salt, \"beacon seed\")), key: material(rv, salt, \"beacon key\"), aad: utf8Encode(`beacon/${groupId}`) };\n}\n\n/** A hub's lobby: its Pkarr identity and sealing key. */\nexport function lobbyKeys(rvB64: string, groupId: string, hubKey: string): { identity: Identity; key: Uint8Array; aad: Uint8Array } {\n const rv = fromBase64Url(rvB64), salt = concatBytes(utf8Encode(groupId), publicKeyFromZ32(hubKey));\n return { identity: identityFromSeed(material(rv, salt, \"lobby seed\")), key: material(rv, salt, \"lobby key\"), aad: utf8Encode(`lobby/${groupId}/${hubKey}`) };\n}\n\nfunction seal(label: string, keys: { key: Uint8Array; aad: Uint8Array }, body: Uint8Array): GhostRecord[] {\n const nonce = randomBytes(NONCE_LENGTH);\n return [{ label, value: toBase64Url(concatBytes(nonce, xchacha20poly1305(keys.key, nonce, keys.aad).encrypt(body))) }];\n}\nfunction open(label: string, keys: { key: Uint8Array; aad: Uint8Array }, records: GhostRecord[]): Uint8Array | null {\n const value = records.find(r => r.label === label)?.value;\n if (!value || value.length > 900) return null;\n try {\n const bytes = fromBase64Url(value);\n return xchacha20poly1305(keys.key, bytes.slice(0, NONCE_LENGTH), keys.aad).decrypt(bytes.slice(NONCE_LENGTH));\n } catch { return null; }\n}\n\n/** 32-byte key, 4-byte time in seconds, then (beacon) one byte of load and 4 bytes of since (seconds). */\nfunction pack(entries: { key: string; ts: number; load?: number; since?: number }[], withLoad: boolean): Uint8Array {\n const size = 36 + (withLoad ? 5 : 0), out = new Uint8Array(entries.length * size), view = new DataView(out.buffer);\n entries.forEach((entry, i) => {\n out.set(publicKeyFromZ32(entry.key), i * size);\n view.setUint32(i * size + 32, Math.floor(entry.ts / 1000));\n if (withLoad) {\n out[i * size + 36] = Math.max(0, Math.min(255, entry.load ?? 0));\n view.setUint32(i * size + 37, Math.floor((entry.since ?? entry.ts) / 1000));\n }\n });\n return out;\n}\nfunction unpack(bytes: Uint8Array, withLoad: boolean, max: number): Hub[] {\n const size = 36 + (withLoad ? 5 : 0);\n if (bytes.length % size !== 0) return [];\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength), out: Hub[] = [];\n for (let i = 0; i < bytes.length / size && out.length < max; i++) {\n try {\n const key = toZ32(bytes.slice(i * size, i * size + 32));\n publicKeyFromZ32(key);\n const ts = view.getUint32(i * size + 32) * 1000;\n out.push(withLoad ? { key, ts, load: bytes[i * size + 36], since: view.getUint32(i * size + 37) * 1000 } : { key, ts, load: 0 });\n } catch { /* not a key: skipped */ }\n }\n return out;\n}\n\n/**\n * The hubs that take turns at the door (WISP 9xx § Admission): those that said so lately and have\n * been hubs for a minute, so that every hub, having read the beacon at least once since, agrees on\n * the same set. Only when there is none (a group's first minute), the one that has been a hub longest. And when none said so lately, a hub asking (`me`)\n * that sees no other fresh hub in the beacon is the door alone, listed or not: its own entry may not have reached the\n * relays yet (a new group's first hub, whose write the relays' budget holds back), and there is nobody to agree with.\n */\nexport function doorHubs(hubs: Hub[], now = Date.now(), me?: string): string[] {\n const lately = hubs.filter(h => now - h.ts < COMMUNITY_TOPOLOGY.beaconEveryMs * 1.5);\n const settled = lately.filter(h => now - (h.since ?? h.ts) >= 60_000);\n // None settled (a group's first minute): the hub that has been one longest, alone, and the lowest key between two of\n // the same second. A hub alone in the beacon reads it only when it republishes, so for half a minute it does not know\n // of a member that just became a hub; that member, counting both of them at the door with the lower key, answered\n // the same knock as the first hub did: two hubs on one entry session, where at best one of them looks 90 s for nobody.\n const since = (h: Hub) => Math.floor((h.since ?? h.ts) / 1000);\n const first = [...lately].sort((a, b) => since(a) - since(b) || (a.key < b.key ? -1 : 1)).slice(0, 1);\n const door = (settled.length ? settled : first).map(h => h.key);\n if (door.length || me === undefined || freshHubs(hubs, now).some(h => h.key !== me)) return door;\n return [me];\n}\n\n/**\n * The newest message frame a hub holds (WISP 9xx · Group Community § Head): who sent it, in which epoch (number and\n * first 16 hex of its commit hash), its sequence and time. Hubs publish it beside the hub list, so a member's other\n * profile on the same device can tell something new was said without being online (WISP 04 § Checking other profiles).\n */\nexport interface CommunityHead { s: string; e: number; h: string; n: number; ts: number }\nconst HEAD_BYTES = 32 + 4 + 8 + 4 + 4;\n/** The head is sealed with the beacon's key under its own associated data: it cannot be passed off as a hub list. */\nconst headKeys = (keys: { key: Uint8Array; aad: Uint8Array }) => ({ key: keys.key, aad: concatBytes(keys.aad, utf8Encode(\"/head\")) });\nfunction packHead(head: CommunityHead): Uint8Array {\n const out = new Uint8Array(HEAD_BYTES), view = new DataView(out.buffer);\n out.set(publicKeyFromZ32(head.s), 0);\n view.setUint32(32, head.e);\n for (let i = 0; i < 8; i++) out[36 + i] = parseInt(head.h.slice(i * 2, i * 2 + 2), 16);\n view.setUint32(44, head.n);\n view.setUint32(48, Math.floor(head.ts / 1000));\n return out;\n}\nfunction unpackHead(bytes: Uint8Array): CommunityHead | null {\n if (bytes.length !== HEAD_BYTES) return null;\n try {\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength), s = toZ32(bytes.slice(0, 32));\n publicKeyFromZ32(s);\n const h = Array.from(bytes.slice(36, 44), (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n return { s, e: view.getUint32(32), h, n: view.getUint32(44), ts: view.getUint32(48) * 1000 };\n } catch { return null; }\n}\n/** A frame can be a head: a member key, and numbers that fit. */\nexport function validHead(head: CommunityHead | null | undefined): head is CommunityHead {\n if (!head || !/^[0-9a-f]{16}$/.test(head.h) || ![head.e, head.n].every(n => Number.isSafeInteger(n) && n >= 0 && n <= 0xffffffff) || !Number.isSafeInteger(head.ts) || head.ts < 0 || head.ts / 1000 > 0xffffffff) return false;\n try { publicKeyFromZ32(head.s); return true; } catch { return false; }\n}\n/** The newer of two heads, by time; a head from more than a minute ahead of now is not taken. */\nexport function newerHead(a: CommunityHead | null, b: CommunityHead | null, now = Date.now()): CommunityHead | null {\n const ok = (h: CommunityHead | null) => (h && h.ts <= now + 60_000 ? h : null);\n const x = ok(a), y = ok(b);\n return !x ? y : !y ? x : y.ts > x.ts ? y : x;\n}\n\nexport function beaconRecords(keys: { key: Uint8Array; aad: Uint8Array }, hubs: Hub[], head?: CommunityHead | null): GhostRecord[] {\n return [...seal(BEACON_LABEL, keys, pack(hubs.slice(0, COMMUNITY_TOPOLOGY.maxHubs), true)),\n ...(validHead(head) ? seal(HEAD_LABEL, headKeys(keys), packHead(head)) : [])];\n}\n/** The head a beacon packet carries, if a hub of this revision wrote it last (an older hub's publication drops it). */\nexport function readBeaconHead(keys: { key: Uint8Array; aad: Uint8Array }, records: GhostRecord[]): CommunityHead | null {\n const body = open(HEAD_LABEL, headKeys(keys), records);\n return body ? unpackHead(body) : null;\n}\nexport function readBeacon(keys: { key: Uint8Array; aad: Uint8Array }, records: GhostRecord[]): Hub[] {\n const body = open(BEACON_LABEL, keys, records);\n return body ? unpack(body, true, COMMUNITY_TOPOLOGY.maxHubs) : [];\n}\nexport function lobbyRecords(keys: { key: Uint8Array; aad: Uint8Array }, entries: LobbyEntry[]): GhostRecord[] {\n return seal(LOBBY_LABEL, keys, pack(entries.slice(-COMMUNITY_TOPOLOGY.lobbyEntries), false));\n}\nexport function readLobby(keys: { key: Uint8Array; aad: Uint8Array }, records: GhostRecord[]): LobbyEntry[] {\n const body = open(LOBBY_LABEL, keys, records);\n return body ? unpack(body, false, COMMUNITY_TOPOLOGY.lobbyEntries).map(({ key, ts }) => ({ key, ts })) : [];\n}\n\n/**\n * The beacon to publish: my entry (or none, `mine` null, when I step down) with the other fresh\n * hubs, newest first, at most `maxHubs`. A hub not in the roster any more is dropped.\n */\nexport function mergeBeacon(existing: Hub[], me: string, mine: Hub | null, now = Date.now(), isMember: (key: string) => boolean = () => true): Hub[] {\n const fresh = existing.filter(h => h.key !== me && now - h.ts < COMMUNITY_TOPOLOGY.beaconFreshMs && h.ts <= now + 60_000 && isMember(h.key));\n const all = mine ? [mine, ...fresh] : fresh;\n return all.sort((a, b) => b.ts - a.ts).slice(0, COMMUNITY_TOPOLOGY.maxHubs);\n}\n\n/** The lobby to publish: my request refreshed, others' still fresh, newest last. */\nexport function mergeLobby(existing: LobbyEntry[], mine: LobbyEntry, now = Date.now()): LobbyEntry[] {\n const fresh = existing.filter(e => e.key !== mine.key && now - e.ts < COMMUNITY_TOPOLOGY.lobbyFreshMs && e.ts <= now + 60_000);\n return [...fresh.sort((a, b) => a.ts - b.ts).slice(-(COMMUNITY_TOPOLOGY.lobbyEntries - 1)), mine];\n}\n\n/** Fresh hubs of a beacon, as a member sees them. */\nexport function freshHubs(hubs: Hub[], now = Date.now()): Hub[] {\n return hubs.filter(h => now - h.ts < COMMUNITY_TOPOLOGY.beaconFreshMs && h.ts <= now + 60_000);\n}\n\n/** Rendezvous hashing: how strongly a hub is responsible for someone (a knock, a member). Highest first. */\nexport function hubRank(subject: string, hubKey: string): string {\n return toBase64Url(sha256(concatBytes(utf8Encode(\"ghostly-group-community/1 rank\"), publicKeyFromZ32(subject), publicKeyFromZ32(hubKey))));\n}\nexport function rankHubs(subject: string, hubs: string[]): string[] {\n return [...hubs].sort((a, b) => { const ra = hubRank(subject, a), rb = hubRank(subject, b); return ra < rb ? 1 : ra > rb ? -1 : 0; });\n}\n\n/**\n * The hubs a member asks for edges: the least loaded fresh ones with room, ties broken by rank\n * for this member (so members spread out), at most `hubsPerMember`.\n */\nexport function pickHubs(me: string, hubs: Hub[], now = Date.now(), avoid: ReadonlySet<string> = new Set()): string[] {\n // A hub that republished lately first (one whose app closed stays listed a while), then the least loaded.\n const lately = (h: Hub) => now - h.ts < COMMUNITY_TOPOLOGY.beaconEveryMs * 1.5 ? 0 : 1;\n const open = freshHubs(hubs, now).filter(h => h.key !== me && !avoid.has(h.key) && h.load < COMMUNITY_TOPOLOGY.hubCapacity)\n .sort((a, b) => lately(a) - lately(b) || a.load - b.load || (hubRank(me, a.key) < hubRank(me, b.key) ? 1 : -1));\n return open.slice(0, COMMUNITY_TOPOLOGY.hubsPerMember).map(h => h.key);\n}\n\n/** Should this member be a hub? Too few fresh hubs, or all full, and room in the beacon. */\nexport function shouldBeHub(me: string, hubs: Hub[], now = Date.now()): boolean {\n const fresh = freshHubs(hubs, now).filter(h => h.key !== me);\n if (fresh.length >= COMMUNITY_TOPOLOGY.maxHubs) return false;\n return fresh.length < COMMUNITY_TOPOLOGY.minHubs || fresh.every(h => h.load >= COMMUNITY_TOPOLOGY.hubCapacity);\n}\n","import { hkdf } from \"@noble/hashes/hkdf.js\";\nimport { sha256 } from \"@noble/hashes/sha2.js\";\nimport { toBase64Url, utf8Encode } from \"./bytes\";\nimport { COMMUNITY_TOPOLOGY, freshHubs, rankHubs, type Hub } from \"./communityRendezvous\";\nimport { MEMBER_KEY } from \"./groupCommits\";\n\n/**\n * Hubs in a private (mesh) group (WISP 9xx · Group Mesh § Hubs). Up to `threshold` members a group is a full mesh, as\n * before. Past it, members that stay online (the Desktop app and the CLI, or members the admin pins) become **hubs**:\n * they keep edges with every other hub and with the members that ask them, and pass on every frame they take. The\n * other members keep edges with `hubsPerMember` hubs only. Frames are author-signed and sealed under the epoch key, and\n * hubs are members, so what a hub passes on is what any member hands on in a catch-up (§ Catch-up): it can withhold a\n * frame, never read more than it could already, alter or forge one.\n *\n * Where hubs are found is the community profile's machinery (`communityRendezvous.ts`): a sealed Pkarr **beacon**\n * listing them, and a **lobby** per hub where a member asks for an edge. The community derives both from the secret its\n * link carries; a private group has no such secret, so they derive from a rendezvous secret of the **current epoch**\n * (`meshRendezvous`). The beacon moves with every commit, and someone removed cannot read or write the next one.\n */\nexport const MESH_HUBS = {\n /** Members up to which a group stays a full mesh, whatever hubs there are. */\n threshold: 16,\n /** Hubs a member keeps edges with, when there are that many: one to spare, so losing a hub loses nothing. */\n hubsPerMember: 2,\n /** Hubs listed at most (the beacon's own bound). */\n maxHubs: COMMUNITY_TOPOLOGY.maxHubs,\n /** Members one hub holds before members prefer another: hubs with room first, a full one rather than none. */\n hubCapacity: COMMUNITY_TOPOLOGY.hubCapacity,\n /** Members the admin pins, and excludes, at most. */\n pinned: 8,\n excluded: 32,\n} as const;\n\n/** The `paired-groups` version an app announces when it takes part in a mesh group's hubs (it may pass frames on). */\nexport const GROUP_VERSION_HUBS = 4;\n\n/**\n * The epoch's rendezvous secret, which `beaconKeys` and `lobbyKeys` take as the community takes its link's: HKDF-SHA-256\n * of the epoch secret, salted by the group id and the epoch, under a label of its own (it can never be the message key).\n */\nexport function meshRendezvous(epochSecret: Uint8Array, groupId: string, epoch: number): string {\n if (epochSecret.length !== 32) throw new Error(\"Invalid epoch secret\");\n return toBase64Url(hkdf(sha256, epochSecret, utf8Encode(`${groupId}/${epoch}`), utf8Encode(\"ghostly-group-mesh/1 rendezvous\"), 32));\n}\n\n/** What the admin says about hubs, in the group's metadata (`hubs` in the body): members pinned as hubs, and excluded. */\nexport interface MeshHubPolicy { pin: string[]; no: string[] }\nexport const NO_HUB_POLICY: MeshHubPolicy = { pin: [], no: [] };\n\n/** A policy as the body carries it: distinct member keys, within the bounds, none both pinned and excluded. Null when it is not one. */\nexport function parseHubPolicy(raw: unknown): MeshHubPolicy | null {\n if (raw === undefined) return NO_HUB_POLICY;\n if (!raw || typeof raw !== \"object\" || Array.isArray(raw)) return null;\n const { pin, no } = raw as Record<string, unknown>;\n const keys = (v: unknown, max: number): string[] | null => {\n if (v === undefined) return [];\n if (!Array.isArray(v) || v.length > max || !v.every(k => typeof k === \"string\" && MEMBER_KEY.test(k)) || new Set(v).size !== v.length) return null;\n return [...v as string[]].sort();\n };\n const p = keys(pin, MESH_HUBS.pinned), n = keys(no, MESH_HUBS.excluded);\n if (!p || !n || p.some(k => n.includes(k))) return null;\n return { pin: p, no: n };\n}\n\n/** The policy as the body writes it: nothing when it says nothing. */\nexport function wireHubPolicy(policy: MeshHubPolicy): MeshHubPolicy | undefined {\n return policy.pin.length || policy.no.length ? { pin: [...policy.pin].sort(), no: [...policy.no].sort() } : undefined;\n}\n\n/**\n * Whether a member may be a hub: in the roster, not excluded, and either pinned by the admin or on an app that stays\n * online (`staysOnline`, which only the member's own app knows; others go by what the beacon says).\n */\nexport function mayBeHub(key: string, policy: MeshHubPolicy, inRoster: (key: string) => boolean, staysOnline: boolean): boolean {\n return inRoster(key) && !policy.no.includes(key) && (staysOnline || policy.pin.includes(key));\n}\n\n/**\n * The hubs of the beacon a member counts on: fresh, in the roster, not excluded, not itself. Everyone filters the same\n * way, so a member the admin excluded, or someone removed, who lists itself anyway is listed for nobody.\n */\nexport function meshHubs(hubs: Hub[], me: string, policy: MeshHubPolicy, inRoster: (key: string) => boolean, now = Date.now()): Hub[] {\n return freshHubs(hubs, now).filter(h => h.key !== me && inRoster(h.key) && !policy.no.includes(h.key));\n}\n\n/**\n * The hubs a member keeps edges with: `hubsPerMember` of them, by rendezvous rank for this member, so the members\n * spread over the hubs and a hub that goes moves only its own members. First hubs with room that republished lately,\n * then full ones (a hub keeps an edge with every member, so a full one is only a busier one), then hubs that may be\n * gone (their app closed; they stay listed a while), and hubs to `avoid` (they did not take me) last.\n */\nexport function pickMeshHubs(me: string, hubs: Hub[], now = Date.now(), avoid: ReadonlySet<string> = new Set()): string[] {\n const lately = (h: Hub) => now - h.ts < COMMUNITY_TOPOLOGY.beaconEveryMs * 1.5;\n const room = (h: Hub) => h.load < MESH_HUBS.hubCapacity;\n const ranked = (list: Hub[]) => rankHubs(me, list.map(h => h.key));\n const tiers = [(h: Hub) => lately(h) && room(h), (h: Hub) => lately(h), () => true];\n const order: string[] = [];\n for (const tier of tiers) for (const key of ranked(hubs.filter(h => h.key !== me && !avoid.has(h.key) && tier(h)))) if (!order.includes(key)) order.push(key);\n for (const key of ranked(hubs.filter(h => h.key !== me && avoid.has(h.key)))) if (!order.includes(key)) order.push(key);\n return order.slice(0, MESH_HUBS.hubsPerMember);\n}\n","import type { PairedTransport } from \"./pairedTransports\";\n\n/**\n * How far a first pairing got, as one side can know it. Shared by the engine (which measures it) and\n * the UI (which shows it), the way a group join's stages are (`GroupJoinStage`).\n */\n\n/** Who this side is: the one who made the invite, or the one who joined with it. */\nexport type PairingRole = \"inviter\" | \"joiner\";\n\n/**\n * The stages, in the order they come. `publishing`: the inviter is putting its invite on the network.\n * `waiting`: the inviter waits for someone to read it. `resolving`: the joiner is looking the invite up\n * (relay, then DHT). `knocking`: this side sent its offer. `answering`: this side is answering the other's\n * offer. `connecting`: offer and answer met; the transport is coming up. `live`: the chat is open.\n * `on-dht`: the contact is pinned over the DHT and no transport is up (yet); the chat carries text on the DHT\n * while layer 1 is retried in the background (WISP 400). `failed`: only what the DHT cannot get past (`reason`:\n * `key-mismatch`, `rejected`, `publish`, `offline`); the engine tries again by itself when `retryable`. On a first pairing the joiner knocks and the inviter answers; an inviter that sees a joiner\n * from before that rule knocks itself after a grace, so either role can be at either.\n */\nexport type PairingStage = \"publishing\" | \"waiting\" | \"resolving\" | \"knocking\" | \"answering\" | \"connecting\" | \"live\" | \"on-dht\" | \"failed\";\n\n/**\n * Why a pairing is `on-dht` (WISP 400): `no-common-transport` (the two apps share none), `transport` (every\n * attempt failed), `chosen` (either side chose DHT only), `waiting` (pinned over the DHT first; still trying).\n */\nexport type PairingOnDhtReason = \"no-common-transport\" | \"transport\" | \"chosen\" | \"waiting\";\n\n/**\n * Why an attempt failed, in a word the UI has words for. `publish`: this side could not publish\n * (discovery unreachable). `timeout`: the offer was not answered in time. `transport`: offer and answer\n * met but no connection came up. `rejected`: the authenticated session failed, or a signal was forged or\n * unauthenticated (never retried). `key-mismatch`: the contact's key is not the one saved (never retried).\n * `offline`: this side went offline. `taken`: a joiner whose inviter answers someone else, who used the same invite\n * first (the inviter's envelopes are sealed to that one); it holds until an envelope of the inviter's reads again.\n */\nexport type PairingFailureReason = \"publish\" | \"timeout\" | \"transport\" | \"rejected\" | \"key-mismatch\" | \"offline\" | \"taken\";\n\nexport interface PairingProgress {\n role: PairingRole;\n stage: PairingStage;\n /** When this stage began (ms since the epoch). */\n since: number;\n /** When the pairing began: the invite was made, or joined (ms since the epoch). */\n startedAt: number;\n /** Connection attempts so far, from 1. A failed attempt is followed by the next one. */\n attempt: number;\n /** A short technical note (an error message), when there is one. */\n detail?: string;\n /** When `stage` is `failed` or `on-dht`: why, and whether the engine tries again on its own. */\n reason?: PairingFailureReason | PairingOnDhtReason;\n retryable?: boolean;\n /** The contact's packet was seen (they are on the network); their offer or answer is what is awaited. */\n peerSeen?: boolean;\n /** `connecting` and `live`: the transport coming up, or in use. */\n transport?: PairedTransport;\n}\n\nconst FIRST_STAGE: Record<PairingRole, PairingStage> = { inviter: \"publishing\", joiner: \"resolving\" };\n\n/**\n * Keeps one side's `PairingProgress` from what the link reports, and says when it changed. Stops at\n * `live`: what happens to a paired chat afterwards is its connection state, not its pairing.\n */\nexport class PairingTracker {\n progress: PairingProgress;\n private peerSeen = false;\n /** An attempt is open from its offer until it ends (live, failed, or torn down). */\n private attemptOpen = false;\n private attempts = 0;\n /** The contact was pinned over the DHT: stream attempts that fail leave the pairing `on-dht`, not `failed`. */\n private dhtPinned = false;\n\n constructor(role: PairingRole, startedAt: number, private readonly changed: (progress: PairingProgress) => void, private readonly now: () => number = Date.now) {\n this.progress = { role, stage: FIRST_STAGE[role], since: startedAt, startedAt, attempt: 1 };\n }\n\n /**\n * Live, or stopped by a security rejection (a key mismatch, a forged or refused session), or by an invite someone else\n * took: nothing moves it after (the last one only `untaken`).\n */\n get done(): boolean {\n return this.progress.stage === \"live\" || (this.progress.stage === \"failed\" && (this.progress.reason === \"key-mismatch\" || this.progress.reason === \"rejected\" || this.progress.reason === \"taken\"));\n }\n\n /** This side's packet is on the network (its first, or the one after a publish that failed). */\n published(): void {\n const { stage, reason } = this.progress;\n if (stage === \"publishing\" || (stage === \"failed\" && reason === \"publish\")) this.set(this.idle());\n }\n\n /** The contact's packet was seen: they are on the network. */\n sawPeer(): void {\n if (this.peerSeen) return;\n this.peerSeen = true;\n this.set(this.progress.stage);\n }\n\n offerSent(): void { this.attempt(\"knocking\"); }\n offerReceived(): void { this.attempt(\"answering\"); }\n answerSent(): void { if (this.progress.stage === \"answering\") this.set(\"connecting\"); }\n answerReceived(): void { if (this.progress.stage === \"knocking\") this.set(\"connecting\"); }\n\n live(transport?: PairedTransport): void {\n this.attemptOpen = false;\n this.set(\"live\", transport ? { transport } : undefined);\n }\n\n failed(reason: PairingFailureReason, retryable: boolean, detail?: string): void {\n if (this.done) return;\n this.attemptOpen = false;\n // A stream that did not come up is no failure of the pairing (WISP 400): pinned over the DHT, the chat is\n // on the DHT; not pinned yet, the first contact goes on (on the DHT too) and so do the attempts.\n if (reason === \"timeout\" || reason === \"transport\") {\n if (this.dhtPinned) this.set(\"on-dht\", { reason: \"transport\", retryable: true, ...(detail ? { detail } : {}) });\n else this.set(this.idle(), { detail: detail ?? (reason === \"timeout\" ? \"The offer was not answered in time; trying again.\" : \"No connection came up; trying again.\") });\n return;\n }\n this.set(\"failed\", { reason, retryable, ...(detail ? { detail } : {}) });\n }\n\n /**\n * The contact was pinned over the DHT: nothing to show yet (a stream attempt is usually a moment away or under\n * way), but from now on a stream that fails ends the pairing `on-dht`, not back between attempts.\n */\n pinnedOverDht(): void { if (!this.done) this.dhtPinned = true; }\n\n /**\n * The contact is pinned over the DHT (its first-contact envelope verified first), or the chat is kept there:\n * the pairing ends `on-dht`, and later attempts only count until one goes `live`.\n */\n onDht(reason: PairingOnDhtReason): void {\n if (this.done) return;\n this.dhtPinned = true;\n // A stream attempt under way goes on to its end (live, or on-dht if it fails): a pin over the DHT a moment\n // before the stream opens is not a stage of its own. DHT only chosen ends the attempts.\n if (this.attemptOpen && reason !== \"chosen\") return;\n this.attemptOpen = false;\n this.set(\"on-dht\", { reason, retryable: reason !== \"chosen\" });\n }\n\n /** The inviter's envelope reads again after `taken` (that one was forged by someone holding the invite): still trying. */\n untaken(): void {\n if (this.progress.stage !== \"failed\" || this.progress.reason !== \"taken\") return;\n const { role, startedAt } = this.progress;\n this.progress = { role, stage: this.dhtPinned ? \"on-dht\" : this.idle(), since: this.now(), startedAt, attempt: Math.max(1, this.attempts),\n ...(this.peerSeen ? { peerSeen: true } : {}), ...(this.dhtPinned ? { reason: \"waiting\" as const, retryable: true } : {}) };\n this.changed(this.progress);\n }\n\n /** The attempt ended without a word (the connection went away before the session was ready). */\n reset(): void {\n if (this.done || !this.attemptOpen) return;\n this.attemptOpen = false;\n if (this.progress.stage === \"on-dht\") return;\n this.set(this.idle());\n }\n\n private attempt(stage: \"knocking\" | \"answering\"): void {\n if (this.done) return;\n if (!this.attemptOpen) this.attempts++;\n this.attemptOpen = true;\n // On the DHT the chat is already usable: background attempts do not take the pairing back to its steps.\n if (this.progress.stage === \"on-dht\") return;\n this.set(stage);\n }\n\n /** The stage this side is at between attempts. */\n private idle(): PairingStage { return this.progress.role === \"inviter\" ? \"waiting\" : \"resolving\"; }\n\n private set(stage: PairingStage, extra?: Pick<PairingProgress, \"detail\" | \"reason\" | \"retryable\" | \"transport\">): void {\n if (this.done) return;\n const { role, startedAt, since } = this.progress;\n // A stage that only gained a detail (the contact was seen) keeps the moment it began.\n this.progress = { role, stage, since: stage === this.progress.stage ? since : this.now(), startedAt, attempt: Math.max(1, this.attempts),\n ...(this.peerSeen ? { peerSeen: true } : {}), ...extra };\n this.changed(this.progress);\n }\n}\n","import { DhtDelivery, type DeliveryMode, type DhtDeliveryState, type DhtDeliveryView , type DhtPacketFacts } from \"./dhtDelivery\";\nimport { PairedFiles } from \"./pairedFiles\";\nimport { TransportSwitch, allowedTransports, type SwitchPlan } from \"./transportSwitch\";\nimport { proofHash, type ProofAdapter, type ProofScope } from \"./peerProofs\";\nimport { IDENTITY_MAX_FRAME, type IdentityScope } from \"./identityProofs\";\nimport { sha256 } from \"@noble/hashes/sha2.js\";\nimport { identityFromSeedB64 } from \"./identity\";\nimport { TRANSPORTS, irohRelayUrl, rankTransports, relayedTransports, transportOrder, type NativeEndpoint, type NativeBinding, type NativeTransport, type PairedTransport, type TransportDescriptors } from \"./pairedTransports\";\nimport { dialDescriptors, encodePacketTransports, parsePacketTransports } from \"./capsRecord\";\nimport { sanitizeNick } from \"./text\";\nimport { sanitizeAvatar } from \"./avatar\";\nimport { parseLinkPreview, type LinkPreview } from \"./linkPreview\";\nimport { pairedReplyAuthor, readReply, wireReply, type WireReply } from \"./replies\";\nimport { readForwarded } from \"./forwards\";\nimport { readStatusCard, type StatusCard } from \"./statusCards\";\nimport { randomBytes, toBase64Url, utf8Encode } from \"./bytes\";\nimport { fitSignedPairedSignal, verifyPairedSignal } from \"./pairedSignal\";\nimport { parseRtcSignal, type SignalSight } from \"./signal\";\nimport { PairedSession, type PairingState, type PairingCredentials, type PaymentMethodName } from \"./pairedSession\";\nimport { DataLink, type DataLinkState } from \"./datalink\";\nimport type { DirectEvidence } from \"./directPath\";\nimport {\n CHUNK_KIND,\n LIMITS,\n PROTOCOL_VERSION,\n decodeChunk,\n decodeControl,\n encodeControl,\n type FrameChannel,\n} from \"./frames\";\nimport { FileTransfers, type FileInfo, type FileSink } from \"./files\";\nimport {\n HttpClient,\n HttpHost,\n GhostlyHttpError,\n type ClientRequest,\n type ClientResponse,\n type HostedHttpService,\n type LocalFetch,\n} from \"./http\";\nimport type { LinkParams } from \"./invite\";\nimport type { Payment, PaymentAsk, PaymentRequest, PaymentResult } from \"./payments\";\nimport { EXPECT_PEER_MS, WATCH_PEER_MS, LinkSession, presenceSeenAt, type LinkStatus, type PeerPresence, type PollIntervals } from \"./link\";\nimport type { ResolvedLink } from \"./records\";\nimport { servicesFromWire, servicesToWire, type ServiceAd } from \"./services\";\nimport { PairedHttp } from \"./pairedHttp\";\nimport { CALLS_CAPABILITY, FILES_CAPABILITY, KNOWN_SESSION_CAPABILITIES, SERVICES_CAPABILITY, SESSION_CAPABILITIES_FRAME, SessionCapabilities, TYPING_CAPABILITY, REACTIONS_CAPABILITY, EDIT_CAPABILITY, WAKE_SESSION_CAPABILITY, PIN_CAPABILITY, STATUS_CARD_CAPABILITY, BUTTONS_CAPABILITY, type SessionCapability } from \"./pairedCapabilities\";\nimport { WAKE_FRAME, parseWakeFrame, wakeFrame, type WakeTarget } from \"./pairedWake\";\nimport { REACTED_FRAME, REACTION_FRAME, REACTION_LIMITS, ReactionWindow, parseReactedFrame, parseReactionFrame, reactedFrame, reactionFrame, type WireReaction } from \"./reactions\";\nimport { PINNED_FRAME, PIN_FRAME, PIN_LIMITS, parsePinFrame, parsePinnedFrame, pinFrame, pinnedFrame, type WirePin } from \"./pins\";\nimport { TYPING_FRAME, TypingReceiver, TypingSender, type TypingActivity } from \"./pairedTyping\";\nimport { EDIT_FRAME, EDIT_RATE_WINDOW_MS, EDIT_RECEIVE_LIMIT, EDITED_FRAME, RateWindow, dhtEditId, editFrame, editedFrame, parseEditFrame, parseEditedFrame, validEditMessage, validEditNumber, type WireEdit } from \"./pairedEdits\";\nimport { FILE_FRAMES } from \"./chatFiles\";\nimport { PAIRED_CALL_FRAME, PairedCalls, parsePairedCallFrame } from \"./pairedCalls\";\nimport { heardCallSignal } from \"./callSignal\";\nimport { traceLink } from \"./linkTrace\";\nimport { isDiscoveryBudgetError, type PkarrTransport } from \"./transport\";\nimport { GROUP_VERSION_LARGE } from \"./groupCommits\";\nimport { GROUP_VERSION_HUBS } from \"./groupHubs\";\nimport { PairingTracker, type PairingProgress, type PairingRole } from \"./pairingProgress\";\n\n/**\n * One link to one peer, complete: Pkarr presence and signaling, the WebRTC\n * data link, and the services multiplexed on top of it. Platform specifics\n * (how to reach Pkarr, how to create a peer connection, how to reach a local\n * HTTP server) are injected, so Desktop and Browser run the same code.\n */\n/** Unanswered offers are repeated less and less often: 1.5, 3, 6, then every 12 minutes. */\n/** After a failed attempt: 20 s, then doubling up to 3 min. Someone opening the chat starts it over. */\nconst AUTO_CONNECT_RETRY_MS = 20_000;\n/** A group link with nothing to dial looks fast for the member's `_tr` on this many attempts, then at its own pace. */\nconst PACKET_TRANSPORTS_FAST_ATTEMPTS = 3;\n/** A native transport that fails this many attempts in a row is skipped for `DEMOTE_MS`, while another remains (WISP 100). */\n/** Pinned over the DHT with no stream up this long after, a first pairing shows as on the DHT (WISP 400). */\nexport const DHT_PIN_GRACE_MS = 10_000;\n/** Back after a restart (`resume`), dials wait this long at most for the native endpoint that carried the chat to start. */\nexport const RESUME_WAIT_MS = 10_000;\n/** Two dials crossing: how long the lower key's side waits for its own to connect before it takes the contact's. */\nexport const CROSSED_WAIT_MS = 3_000;\n/**\n * A session this side dialled this recently is one a crossing dial of the contact's may meet (both connected): the\n * lower key keeps its own. Older, the session is one the contact has since lost (its app restarted), and its dial\n * takes over.\n */\nexport const CROSSED_FRESH_MS = 10_000;\n/** What a joiner is told when someone else used the invite first (`DhtDeliveryView.inviteTaken`). */\nexport const INVITE_TAKEN = \"Someone else joined with this invite first. Ask your contact for a new one.\";\n/**\n * A dial the contact made while its mailbox, as last read here, said DHT-only waits this long for the read that says\n * it left (the mailbox is read at the DHT pace meanwhile), then closes.\n */\nexport const BLOCKED_DIAL_WAIT_MS = 10_000;\n/**\n * A connection dialled in on a pinned chat must authenticate within this long, or it closes. Anyone who read the\n * endpoint from the record can dial it: until it proves the pinned key it holds no place a person waits on. A native\n * connection this side dialled on a pinned chat has as long too: its path is up when it opens, and the handshake takes\n * a few round trips, so one still unfinished by then carries nothing (a relay path that died with the handshake), and\n * the chat dials again rather than hold it until QUIC's idle timeout (Omarchy, 2026-10-01: the web app had refused the\n * Desktop's knock 15 s in, and the Desktop dialled again only 30 s in).\n */\nexport const UNPROVEN_AUTH_MS = 15_000;\n/**\n * A native connection this side dialled that has not authenticated this long after it opened, while the contact's\n * record names a newer way to dial it: the dial went to an old address (the web app's Iroh homed on another relay after\n * a restart), and the chat dials the new one at once. Sooner, it is a handshake still under way.\n */\nexport const REDESCRIBED_GRACE_MS = 3_000;\n/**\n * A WebRTC offer this side made that has no answer after this long: a direct native transport ranked after WebRTC is\n * dialled meanwhile, the offer still standing, and whichever goes live first carries the chat. The contact may not read\n * the offer for a while (a live chat reads every minute), or the relays' request budget may hold its answer back until\n * its answering attempt is over, and the offer would wait out its attempt timeout (`CONNECT_TIMEOUT_MS`, 90 s). A native\n * dial takes a second and no request to the relays.\n */\nexport const RACE_DIRECT_MS = 8_000;\n/**\n * A relayed one waits until a contact reading in the background (every 30 s) has had its look: a chat that went live\n * over a relay stays there (no probing while live, WISP 100), where the answer would have made it direct.\n */\nexport const RACE_RELAYED_MS = 40_000;\n/**\n * A WebRTC offer this side made that was answered, and has not connected this long after the answer was applied: where\n * a path exists ICE finds it in a second or two, so there is none (a VPN's NAT, a firewall that drops UDP, and no TURN\n * server), and every transport ranked after WebRTC is dialled now, the relayed ones too, the attempt still standing.\n * Without it the chat waited for ICE to give up (about 15 s in Chromium, the attempt's 90 s where the connection never\n * says `failed`), then for the next attempt.\n */\nexport const RACE_ANSWERED_MS = 6_000;\n/**\n * WebRTC that could not start an attempt at all (no candidate gathered: UDP blocked, a VPN's browser extension; or no\n * connection could be made) is tried last for this long: each try costs its stall time (`GATHER_STALL_MS` three times\n * over) before the transport that works is dialled.\n */\nexport const RTC_UNSTARTED_MS = 10 * 60_000;\n/** A transport due in the race that cannot be dialled yet (its endpoint still starting, no address of the contact's) is looked at again this often. */\nconst RACE_RETRY_MS = 2_000;\nexport const DEMOTE_AFTER_FAILURES = 3;\nexport const DEMOTE_MS = 60 * 60_000;\nconst AUTO_CONNECT_MAX_RETRY_MS = 3 * 60_000;\n/**\n * A first pairing is different: the contact is right there, having just read the invite, so an attempt\n * that did not make it in `PAIRING_ATTEMPT_MS` is given up and the next one starts after `PAIRING_RETRY_MS`\n * (doubling up to `PAIRING_MAX_RETRY_MS`), instead of the 90 s and 20 s a saved contact's reconnect waits.\n */\nexport const PAIRING_ATTEMPT_MS = 15_000;\nexport const PAIRING_RETRY_MS = 3_000;\nconst PAIRING_MAX_RETRY_MS = 30_000;\n/** A packet published this recently is fresh enough for whoever comes back to the chat. */\nconst RECENTLY_PUBLISHED_MS = 5_000;\n/** An inviter that sees the joiner without its offer waits this long for one before offering itself. */\nexport const INVITER_DIAL_GRACE_MS = 2_500;\n/**\n * Liveness of a paired session: a ping this often, and the session is taken for dead after this many\n * pings in a row with nothing at all back. Counted in pings, not seconds, so a throttled background\n * tab (timers once a minute) is not mistaken for a dead peer. Only once the peer has answered a ping:\n * older apps drop the frame and must not be cut off for it.\n */\nexport const LIVENESS_PING_MS = 15_000;\n/**\n * After a live switch the old channel stays open this long, unread, before it is closed: the contact swaps on its own\n * side a round trip later, and seeing its current channel close first would make it drop the whole session.\n */\nexport const SWITCH_RETIRE_MS = 3_000;\nexport const LIVENESS_MISSED_PINGS = 3;\n/**\n * Frames that keep a session going rather than say something of the two people's: what the contact sent last outside\n * these is when the chat was last in use (`canYieldEndpoint`). Every other frame (a text, a receipt, typing, a\n * reaction, an edit, a file, a call, a payment) is use; what this side sends is answered with one of them.\n */\n/** How long a session giving its listener up waits after its goodbye before it closes, so the goodbye gets there. */\nconst YIELD_BYE_MS = 300;\nconst QUIET_FRAMES = new Set([\"paired-ping\", \"paired-pong\", \"paired-bye\", \"paired-policy\", \"paired-switch-plan\", \"paired-switch-ready\",\n \"paired-switch-failed\", \"paired-switch-keep\", \"paired-rtc\", \"paired-adapters\", \"paired-nick\", \"paired-avatar\", \"paired-capabilities\",\n \"paired-wake\", \"paired-hold\", \"paired-payments\", \"paired-groups\", \"paired-services\", \"paired-reconnect\", \"paired-reconnect-ack\"]);\n/**\n * A ping with nothing at all back this long after it went: the contact may have crashed and started again, with its new\n * offer waiting on the relays, while this side still holds the old session and reads them at a live chat's pace (30 s).\n * They are read now. Nothing more: the session is let go only by `LIVENESS_MISSED_PINGS`, or by the contact's offer\n * (`DataLink.handleSignal`). A connection whose close this side does not hear (node-datachannel never says\n * `disconnected`) otherwise went on until its consent checks gave up, 27 s after the crash (CI run 36741701666).\n */\nexport const PONG_WAIT_MS = 4_000;\n\nexport interface IncomingMessage {\n id?: string;\n text: string;\n timestamp: number;\n nick?: string;\n via: \"pkarr\" | \"datalink\";\n batch?: ResolvedLink;\n /** A paired chat's text from the DHT floor: the envelope it came in (`DhtPacketFacts`). */\n packet?: DhtPacketFacts;\n /** The link preview the sender's app attached (`pv`, WISP 401 § Link previews), already checked against the text. */\n preview?: LinkPreview;\n /**\n * The message this one answers (`r`, WISP 401 § Replies), its line cleaned. Over the DHT only the id comes\n * (`s` empty, `f` absent): the receiver finds the rest in its own history.\n */\n reply?: WireReply | { i: string };\n /** How many times it has been forwarded (`fw`, WISP 401 § Forwards); absent for a message written in this chat. */\n forwarded?: number;\n /** A status card (`sc`, WISP 4xx · Status Cards), already checked; the text is its fallback. Live session only. */\n card?: StatusCard;\n}\n\n/** Largest `paired-message` frame sent with a preview: a session fails on a frame over 60 KiB (`PairedSession`). */\nexport const MAX_PAIRED_MESSAGE_FRAME = 56 * 1024;\n\n/**\n * A `paired-message` frame. The preview (`pv`) is left out when the frame would pass `MAX_PAIRED_MESSAGE_FRAME`\n * with it: the text matters, the card does not. A reply (`r`, WISP 401 § Replies) is a few hundred bytes at most\n * and always goes, and so does a forwarded message's hop count (`fw`, WISP 401 § Forwards) and a status card (`sc`,\n * WISP 4xx · Status Cards), whose 8 KiB with the text's 16 stay well inside the frame.\n */\nexport function pairedMessageFrame(id: string, ts: number, m: string, preview?: LinkPreview, reply?: WireReply, forwarded?: number, card?: StatusCard): string {\n const r = { ...(reply && { r: wireReply(reply) }), ...(readForwarded(forwarded) && { fw: forwarded }), ...(card && { sc: card }) };\n const plain = JSON.stringify({ t: \"paired-message\", id, ts, m, ...r });\n if (!preview) return plain;\n const withPreview = JSON.stringify({ t: \"paired-message\", id, ts, m, pv: preview, ...r });\n return withPreview.length <= MAX_PAIRED_MESSAGE_FRAME ? withPreview : plain;\n}\n\n/**\n * A native descriptor from the contact's capability record against the one known (a session's, or an older record's).\n * The record carries only how to dial, never an address: the Iroh endpoint id and the relay it is homed on, the\n * HyperDHT key and the relay a browser's goes through. For the same endpoint, a known Iroh descriptor keeps its\n * addresses and takes the record's relay. `newer`: the record was just read, so what it says replaces what is known;\n * otherwise it only fills a relay the known one lacks. The known one itself when nothing changes. `listed`: the record\n * names the transport. One just read that names it with no descriptor says its endpoint is not up (WISP 03: still\n * starting, or no free listener there): the one known is not dialled meanwhile (none, `undefined`).\n */\nfunction recordDescriptor(transport: NativeTransport, known: unknown, record: unknown, newer: boolean, listed: boolean): unknown {\n if (!record) return newer && listed ? undefined : known;\n if (!known) return record;\n const k = known as { id?: unknown; publicKey?: unknown; relay?: unknown }, r = record as typeof k;\n if (transport === \"iroh/1\" ? k.id !== r.id : k.publicKey !== r.publicKey) return newer ? record : known;\n // The same Iroh relay spelled another way (with or without the trailing dot) is no change.\n const same = r.relay === k.relay || (transport === \"iroh/1\" && typeof r.relay === \"string\" && typeof k.relay === \"string\" && irohRelayUrl(r.relay) === irohRelayUrl(k.relay));\n if (!r.relay || same || (k.relay && !newer)) return known;\n return transport === \"iroh/1\" ? { ...k, relay: r.relay } : record;\n}\n\n/**\n * The transport a chat is set to reach and is not on yet (WISP 100, \"A chosen transport not reached yet\"): waited for,\n * never failed. `by`: whose choice names it (absent: a policy alone, such as Fallback off, limits the chat to it).\n */\nexport interface TransportWait {\n transport: PairedTransport;\n by?: \"you\" | \"contact\";\n /**\n * - `unknown`: the contact's app has not said whether it has it (no capability record read, no session lists it);\n * - `starting`: the contact's record lists it, but there is no way to dial it yet (its endpoint is starting);\n * - `connecting`: a switch to it is under way;\n * - `unreachable`: both have it, and the last attempt did not connect (`error`, when this side knows);\n * - `waiting`: both have it and nothing failed yet: the next attempt goes when the contact is there;\n * - `contact-lacks`: the contact's latest record does not list it: its app does not have it;\n * - `app-lacks`: this app does not run it (the contact chose it, or this side's adapter is gone).\n */\n reason: \"unknown\" | \"starting\" | \"connecting\" | \"unreachable\" | \"waiting\" | \"contact-lacks\" | \"app-lacks\";\n /** The chat is live on another transport meanwhile (both allow a fallback); otherwise it is on the DHT. */\n live?: PairedTransport;\n /** Attempts that did not connect in a row, and why the last one did not, when this side dialled it. */\n failures: number;\n error?: string;\n /** When the next attempt goes, when one is scheduled on this side. */\n retryAt?: number;\n}\n\n/**\n * The last attempt to go live that did not, as one side saw it (WISP 100, \"Why a chat is not live\"): shown so that a\n * chat on the DHT is never a silent retry loop.\n */\nexport interface LiveAttempt {\n /** When it ended (ms since the epoch). */\n at: number;\n /** This side dialled it (the side with the lower rendezvous key, or a joiner), or answered the contact's offer. */\n side: \"dialled\" | \"answered\";\n /** Each transport tried and why it did not connect, in the order tried. An answering side knows only its own part. */\n failed: { transport: PairedTransport; error: string }[];\n /** Why nothing could be tried, when nothing was (no transport both apps have and can dial yet). */\n reason?: string;\n /** When this side dials again (it dials): about then, once the contact is seen. */\n retryAt?: number;\n}\n\nexport interface GhostLinkEvents {\n onDhtDelivery?(view: DhtDeliveryView): void;\n onDiscoveryError?(error: string | null): void;\n onPeerProof?(frame: Record<string, unknown>): Promise<void>;\n /** An `idp-*` identity-proof frame from the paired contact (only when both sides offer `identity-proof/1`). */\n onIdentityProof?(frame: Record<string, unknown>): Promise<void>;\n onTransportsChanged?(): void;\n /**\n * The contact chose a transport for this chat just now (its switch intent went up on the open session), or went\n * back to automatic (its intent fell to 0). `apart`: heard with no session to raise an intent on, from its capability\n * record, or from the first policy of a session it chose before (WISP 100, \"A choice made while not live\"); the\n * owner may have told it already.\n */\n onPeerTransportChoice?(transport: PairedTransport | \"automatic\", apart?: boolean): void;\n /**\n * A switch to `target` could not connect and the session stayed where it was (both allow fallback). `reason` is\n * known on the side that dialled; the other side learns only that it did not happen.\n */\n onTransportSwitchFailed?(target: PairedTransport, reason?: string): void;\n /** The transport the chat waits for, or why, changed (`transportWait`); undefined once nothing is waited for. */\n onTransportWait?(wait: TransportWait | undefined): void;\n /** An attempt to go live ended without it (`liveAttempt`), or the chat went live (undefined). */\n onLiveAttempt?(attempt: LiveAttempt | undefined): void;\n /** A live transport switch completed: the session now runs over `to`, and nothing reconnected. */\n onTransportSwitched?(from: PairedTransport | undefined, to: PairedTransport): void;\n /** Round trip of a liveness ping on the open session, in milliseconds. */\n onRtt?(ms: number): void;\n onTransportDiscovery?(descriptors: TransportDescriptors, transports: PairedTransport[], fallback: boolean): Promise<void>;\n /**\n * With `packetTransports`: the member's packet said which transports its app runs on this link and how to dial them.\n * Without `webrtc/1` among them, it has none, and a session takes native endpoints on this side too.\n */\n onPacketTransports?(transports: PairedTransport[], descriptors: TransportDescriptors): void;\n onPairingState?(state: PairingState): void;\n /** How far a first pairing got (`pairingProgress` option): every change, up to `live`. */\n onPairingProgress?(progress: PairingProgress): void;\n onMessage?(message: IncomingMessage): void | Promise<void>;\n onPresence?(presence: PeerPresence): void;\n /** A paired contact's profile picture, already checked; `null` when they removed it. */\n onPeerAvatar?(avatar: string | null): void;\n /**\n * The name a paired contact asked to be shown by, already checked; `null` when they have none (never set,\n * removed, or not shared). Said on every session, so what arrives here is the contact's current choice.\n */\n onPeerNick?(nick: string | null): void;\n onMessageReceipt?(id: string): void | Promise<void>;\n /**\n * What the contact's app says about held items (`hold/1`): whether it accepts them (from the handshake, or\n * a `paired-hold` frame on the session) and the highest sequence it has held for this side, if it said.\n */\n onHold?(state: { peerAllows: boolean; peerTop?: number }): void | Promise<void>;\n onPeerAck?(ackTimestamp: number): void;\n onCallSignal?(signal: string): void;\n /**\n * The contact started or stopped typing (`typing/1`, WISP 401 § Typing), or said something new while it goes on\n * (recording, thinking, a bot's status); only changes, never stored.\n */\n onPeerTyping?(typing: boolean, activity: TypingActivity | null): void;\n /**\n * The contact reacted to a message (`react/1`, WISP 401 § Reactions), already checked. True: confirm it (it was\n * taken: shown, kept for its message, or older than what is shown); false: not now, it comes again.\n */\n onReaction?(reaction: WireReaction): boolean | Promise<boolean>;\n /** The contact confirmed this side's reaction numbered `n`. */\n onReactionReceipt?(n: number): void | Promise<void>;\n /** The contact's DHT envelope said it took this side's reactions up to number `n`. */\n onReactionsTaken?(n: number): void | Promise<void>;\n /** Both sides offer `react/1` on the open session (true), or no longer (false). */\n onReactionsSupport?(supported: boolean): void;\n /**\n * The contact edited one of its messages (`edit/1`, WISP 401 § Edits), already checked. True: confirm it now (it\n * was shown, or was not newer, or its message is gone); false: not yet (its message is not here), and\n * `confirmEdit` says so once it is.\n */\n onMessageEdit?(edit: WireEdit): boolean | Promise<boolean>;\n /** The contact confirmed edit `e` of one of this side's messages. */\n onEditReceipt?(id: string, e: number): void | Promise<void>;\n /** Both sides offer `edit/1` on the open session (true), or no longer (false). */\n onEditSupport?(supported: boolean): void;\n /**\n * The contact pinned a message, or unpinned (`pin/1`, WISP 401 § Pinned message), already checked. True: confirm it\n * (taken, or older than the pin shown); false: not now, it comes again.\n */\n onPin?(pin: WirePin): boolean | Promise<boolean>;\n /** The contact confirmed this side's pin numbered `n`. */\n onPinReceipt?(n: number): void | Promise<void>;\n /** Both sides offer `pin/1` on the open session (true), or no longer (false). */\n onPinSupport?(supported: boolean): void;\n /**\n * The contact shared how to wake its closed web app (`wake/1`, WISP 401 § Wake-up push), already checked; null:\n * it stopped sharing (forget it). Kept by the caller: it is what makes a wake-up possible while the contact is away.\n */\n onPeerWake?(target: WakeTarget | null): void;\n /** Both sides offer `wake/1` on the open session (true), or no longer (false): the moment to share this side's. */\n onWakeSupport?(supported: boolean): void;\n onStatus?(status: LinkStatus): void;\n onDataLinkState?(state: DataLinkState): void;\n /** What a WebRTC attempt of this link said about direct connections from this device (`directPath.ts`). */\n onDirectEvidence?(evidence: DirectEvidence): void;\n /** The contact's packet came between two reads of its record: its clock against this one (`LinkSessionEvents.onPeerClock`). */\n onPeerClock?(packetAt: number, readBefore: number, readAt: number): void;\n onPoll?(poll: { polling: boolean; nextInMs: number }): void;\n /** The peer is sending a file. Return where to put it, or null (or a reason) to refuse. */\n onFileStored?(file: FileInfo): Promise<string | undefined>;\n onFileIncoming?(file: FileInfo): FileSink | string | null;\n onFileProgress?(fileId: string, transferred: number, direction: \"in\" | \"out\"): void;\n onFileComplete?(fileId: string, direction: \"in\" | \"out\"): void;\n onFileFailed?(fileId: string, reason: string, direction: \"in\" | \"out\"): void;\n /** A files/3 frame (`FILE_FRAMES`), only while both sides agreed `files/3` on the open session. */\n onFilesFrame?(frame: Record<string, unknown>): void | Promise<void>;\n /**\n * files/3 can flow (a session agreed it and is open), or no longer can. Said again, open, after a live\n * transport switch: frames on the old channel may be lost, and offering again puts both sides in step.\n */\n onFilesSession?(open: boolean): void;\n onPaymentRequest?(request: PaymentRequest): void | Promise<void>;\n /** A contact asking to pay this side (paired chats): answer with a request carrying the ask's id. */\n onPaymentAsk?(ask: PaymentAsk): void | Promise<void>;\n onPayment?(payment: Payment): void | Promise<void>;\n onPaymentResult?(result: PaymentResult): void | Promise<void>;\n /** A `group-*` frame (WISP 900) from the peer, only while both sides announced `paired-groups`. */\n onGroupFrame?(frame: Record<string, unknown>): void | Promise<void>;\n /** Group frames can flow (both announced), or no longer can. */\n onGroupsSupport?(supported: boolean): void;\n}\n\nconst PAYMENT_METHODS: PaymentMethodName[] = [\"cashu\", \"lightning\", \"arkade\", \"usdt\", \"bark\", \"bitcoin\", \"fedimint\", \"spark\"];\n\n/** For each way of paying, the networks (real money, test coins) this side has a wallet on. */\nexport type PaymentNetworks = Partial<Record<PaymentMethodName, (\"mainnet\" | \"testnet\")[]>>;\n\n/** A `paired-payments` networks map from the contact, or null when it is not a valid one (it is then ignored). */\nexport function parsePaymentNetworks(value: unknown): PaymentNetworks | null {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) return null;\n const entries = Object.entries(value as Record<string, unknown>);\n if (entries.length > 16) return null;\n const networks: PaymentNetworks = {};\n for (const [method, list] of entries) {\n if (!Array.isArray(list) || list.length > 2 || !list.every((n) => n === \"mainnet\" || n === \"testnet\")) return null;\n // A method this app does not know yet is left out, like in the methods list.\n if (PAYMENT_METHODS.includes(method as PaymentMethodName)) networks[method as PaymentMethodName] = [...new Set(list as (\"mainnet\" | \"testnet\")[])];\n }\n return networks;\n}\n\nexport interface GhostLinkOptions {\n /** Ways of paying this chat allows. One that is off is not offered in the handshake, sent or accepted. Absent: allowed. */\n paymentMethods?: Partial<Record<PaymentMethodName, boolean>>;\n /**\n * The networks of this side's wallets, per way of paying: said with the methods on the open session, so the\n * contact offers only cards whose networks meet. Older apps ignore it.\n */\n paymentNetworks?: PaymentNetworks;\n arkPaymentsSupport?: boolean;\n usdtPaymentsSupport?: boolean;\n barkPaymentsSupport?: boolean;\n /** Announce private groups (WISP 900) on the open session. Announced after the handshake, like `paired-payments`, so a full offer stays within what older apps accept. */\n groupsSupport?: boolean;\n /** Offer `hold/1`: accept items held for this side, and hold items for the contact while it is away. */\n holdSupport?: boolean;\n /** Offer `calls/1` on paired sessions: this app can place and take calls (WebRTC media and capture). */\n callsSupport?: boolean;\n /** Why this app cannot call, when `callsSupport` is off and the platform can say more (what to install). */\n callsMissing?: string;\n /** Offer `services/1` on paired sessions: this app can serve granted local web apps and open the contact's. */\n servicesSupport?: boolean;\n /** Offer `files/3` on paired sessions: files of any size, offered, resumed and checked (`chatFiles.ts`). */\n largeFilesSupport?: boolean;\n /** Offer `typing/1` on paired sessions: say and show when either side is writing (1:1 chats, not group edges). */\n typingSupport?: boolean;\n /** Offer `react/1` on paired sessions: reactions to messages (1:1 chats, not group edges). */\n reactionsSupport?: boolean;\n /** Offer `edit/1` on paired sessions: sent texts can be edited (1:1 chats, not group edges). */\n editSupport?: boolean;\n /** Offer `pin/1` on paired sessions: a pinned message (1:1 chats, not group edges). */\n pinSupport?: boolean;\n /** Offer `status-card/1` on the paired session: this app shows status cards (WISP 4xx · Status Cards). */\n statusCardSupport?: boolean;\n /** Offer `buttons/1` on the paired session: this app shows and presses message buttons (WISP 4xx · Message Buttons). */\n buttonsSupport?: boolean;\n /** Offer `wake/1` on paired sessions: this app wakes a contact's closed web app with a push (1:1 chats, not group edges). */\n wakeSupport?: boolean;\n dht?: {\n state?: DhtDeliveryState; save(state: DhtDeliveryState): Promise<void>; pollMs?: number;\n /** A chat already paired, started again: its first control envelope waits this long (`DhtDelivery`). */\n firstControlAfterMs?: number;\n /** This side's capability-record revision, told in every envelope (WISP 03). */\n capsRev?(): number | undefined;\n /** An envelope from the contact named this revision of its capability record. */\n peerCapsRev?(rev: number): void;\n /** Whether the contact's capability record accepts DHT text; absent or unknown: it does. */\n peerAcceptsText?(): boolean;\n /** This side's reactions the contact has not confirmed, oldest first: they ride on the envelopes. */\n reactions?(): readonly WireReaction[];\n };\n rtcAvailable?: boolean;\n /** `automatic`: the chat follows the app's rule (no transport chosen for it); `preferred` is then the rule's. Absent: automatic unless `preferred` is given. */\n native?: { peerDescriptors?: TransportDescriptors; peerTransports?: PairedTransport[]; peerFallback?: boolean; preferred?: PairedTransport; fallback?: boolean; automatic?: boolean };\n params: LinkParams;\n pairing?: { credentials: PairingCredentials; pinPeer: (key: string, signedSignals?: boolean) => Promise<void>; verifyPeer?: (key: string) => Promise<void>; trustOnFirstUse?: boolean };\n /**\n * A chat that was never paired: follow its first pairing (`onPairingProgress`), and pace its attempts\n * for a contact who is right there. `startedAt`: when the invite was made or joined.\n */\n pairingProgress?: { role: PairingRole; startedAt: number };\n transport: PkarrTransport;\n nick?: string;\n /** This side's profile picture (a small JPEG data URL), told to a paired peer directly. */\n avatar?: string;\n lastSeenTimestamp?: number;\n pollIntervals?: PollIntervals;\n /**\n * The transport this chat was live on when the app last ran (it quit, or crashed, with the session up). The contact is\n * probably still there, holding a session that is dead now: that transport is dialled first, and a native one is\n * dialled at once by either side, not only by the side whose turn it is (WISP 100, \"Back after a restart\").\n */\n resume?: PairedTransport;\n /**\n * When the session that was live when this app last ran began (ms). A contact's offer made before it is the one that\n * set that session up, or an older one, read from a relay that never got the packet clearing it: it is not answered,\n * and a resumed link dials instead. An offer made after it (the contact noticed the session go, during a long\n * downtime) is answered as any. Unknown (never live, a log lost): every offer is taken as before (bug hunt r7a).\n */\n resumeFloor?: number;\n /**\n * Only this link's two ends can write its records: a group's edge or entry session, whose keys come from the two\n * members' own keys. A chat's records are under keys from its invite, which whoever holds a copy of it can write.\n */\n ownRecords?: boolean;\n /**\n * Open the data link on its own whenever the peer is online, instead of on\n * first use. Chat and call signaling then travel peer to peer and Pkarr is\n * only polled once a minute, which is what keeps relays happy.\n */\n autoConnect?: boolean;\n /** When its first packet goes out (`LinkSessionOptions.firstPublish`): after its first look, the side that dials puts its offer in it. */\n firstPublish?: \"at-start\" | \"after-first-poll\";\n /**\n * A link for one exchange, closed right after it (a group's entry session): once connected, it leaves\n * its offer or answer in its packet rather than publishing again to clear it.\n */\n oneShot?: boolean;\n /**\n * A group's link (an edge, an entry session), which has no capability record: once this side runs a native endpoint on\n * it, its transports and how to dial them ride its own packet (`_tr`), and the member's are read from its packet\n * (WISP 9xx § Transports). The owner starts endpoints only where one side has no WebRTC (`onPacketTransports`).\n */\n packetTransports?: boolean;\n createPeerConnection: () => RTCPeerConnection;\n localFetch: LocalFetch;\n /** Everything this peer currently offers on this link. */\n getServices: () => ServiceAd[] | undefined;\n /** Web apps a paired contact may reach: told to that contact alone, on the open session, never published. */\n getPairedServices?: () => ServiceAd[];\n /** Resolves an advertised `http` service id to its local target. */\n getHostedHttpService: (id: string) => HostedHttpService | undefined;\n events?: GhostLinkEvents;\n}\n\n/**\n * What a link says when its connection details could not go out: the transport's reason (the Desktop's Rust client\n * rejects with a string, not an Error), and when it is tried again, the soonest a relay is asked (\"asked again in N s\").\n */\nexport function publishFailure(error: unknown): string {\n const reason = error instanceof Error ? error.message : typeof error === \"string\" && error ? error : \"discovery unavailable\";\n const waits = [...reason.matchAll(/asked again in (\\d+) s/g)].map((m) => Number(m[1]));\n const text = reason.replace(/; asked again in \\d+ s/g, \"\").replace(/\\.$/, \"\");\n return `Could not publish connection details: ${text}. ${waits.length ? `Retrying in ${Math.min(...waits)} s.` : \"Retrying.\"}`;\n}\n\nexport class GhostLink {\n readonly session: LinkSession;\n private readonly dht: DhtDelivery | null;\n private deliveryMode: DeliveryMode;\n private securityRejected = false;\n readonly dataLink: DataLink;\n private readonly options: GhostLinkOptions;\n private channel: FrameChannel | null = null;\n private endpoints = new Map<PairedTransport, NativeEndpoint>();\n private activeBinding?: NativeBinding;\n private dialing = false;\n private dhtPinGrace: ReturnType<typeof setTimeout> | null = null;\n /** Native attempts that failed in a row, and transports demoted until when (WISP 100, demotion). */\n private nativeFailures = new Map<PairedTransport, number>();\n private demotedUntil = new Map<PairedTransport, number>();\n private connectionEpoch = 0;\n private switchAllowedUntil = 0;\n private switchAck: (() => void) | null = null;\n private readonly switcher: TransportSwitch;\n /** `plan`: a switch both sides agreed; without one, a replacement the contact dialled (`attachReplacement`). */\n private candidate: { channel: FrameChannel; session: PairedSession; binding?: NativeBinding; plan?: SwitchPlan; reject(error: Error): void } | null = null;\n private candidateEpoch = 0;\n /** Channels a switch replaced, closed after `SWITCH_RETIRE_MS` (or at once on disconnect). */\n private retiring = new Map<FrameChannel, { timer: ReturnType<typeof setTimeout>; close(): void }>();\n private rtcCandidateWaiter: { resolve(): void; reject(error: Error): void } | null = null;\n private transitionTarget?: PairedTransport;\n private transitionError?: string;\n private applicationOpen = false;\n private stopped = false;\n /** Connection details this side could not publish were reported as a pairing error, which the next packet out ends. */\n private signalPublishFailed = false;\n private peerDescriptors: TransportDescriptors;\n private peerTransports?: PairedTransport[];\n private peerFallback: boolean;\n private preferred: PairedTransport;\n private fallback: boolean;\n /** No transport chosen for this chat: `preferred` is the app's rule. */\n private automatic: boolean;\n /** What the contact's latest capability record says its app runs (WISP 03); unknown until one is read. */\n private peerRecordTransports?: PairedTransport[];\n /**\n * The contact's explicit choice for this chat as last heard, from its capability record or a session's switch\n * intent; none on Automatic, or before either said (WISP 100, \"A choice made while not live\").\n */\n private peerChoice?: PairedTransport;\n /** The attempt to go live under way on this side, and what failed in it so far. */\n private attempt: { side: LiveAttempt[\"side\"]; failed: LiveAttempt[\"failed\"] } | null = null;\n private lastAttempt?: LiveAttempt;\n /** The transport the chat waits for (WISP 100): attempts that failed in a row, the last error, the next retry. */\n private waiting: { transport: PairedTransport; failures: number; told?: boolean; error?: string; retryAt?: number; timer?: ReturnType<typeof setTimeout> } | null = null;\n /** Why the target of the switch this side dialled did not connect, when a later choice may still carry the chat. */\n private switchMissed?: { transport: PairedTransport; reason: string };\n private paired: PairedSession | null = null;\n private pairedPending = new Map<string, number>();\n private httpHost: HttpHost | null = null;\n /** HTTP to and from shared web apps over a paired session. */\n private pairedHttp: PairedHttp | null = null;\n private httpClient: HttpClient | null = null;\n private files: FileTransfers | null = null;\n private pairedFiles: PairedFiles | null = null;\n private peerServicesOverride: ServiceAd[] | null = null;\n /** A paired peer's name arrives over the channel; nothing about it is published. */\n /** Ways of paying the contact allows, as it last said on this session; null until it does. */\n private peerPaymentMethods: Set<PaymentMethodName> | null = null;\n /** The contact's networks per way of paying, as it last said; null while it said none (an older app). */\n private peerNetworks: PaymentNetworks | null = null;\n /** The contact's word on held items on this session; null until it says. */\n private peerHoldOverride: boolean | null = null;\n private peerNickOverride: string | null = null;\n /** Group protocol versions the peer announced on this session; null until it does. */\n private peerGroupVersions: number[] | null = null;\n /** What each side announced after the handshake (`paired-capabilities`): calls, services. */\n private readonly sessionCapabilities = new SessionCapabilities(() => this.offeredCapabilities());\n private readonly pairedCalls = new PairedCalls();\n /** Typing on this session (`typing/1`): when to say `start` again, and the contact's word with its timeout. */\n private readonly typingSender = new TypingSender();\n private readonly typingReceiver = new TypingReceiver((typing, activity) => this.options.events?.onPeerTyping?.(typing, activity));\n /** Reaction frames the contact may send per window; the rest go unconfirmed and come again. */\n private readonly reactionsReceived = new ReactionWindow(REACTION_LIMITS.receive);\n /** Pin frames the contact may send per window; the rest go unconfirmed and come again. */\n private readonly pinsReceived = new ReactionWindow(PIN_LIMITS.receive, PIN_LIMITS.windowMs);\n /** Edit frames the contact may send per window; the rest go unconfirmed and come again. */\n private readonly editsReceived = new RateWindow(EDIT_RECEIVE_LIMIT, EDIT_RATE_WINDOW_MS);\n private readonly wakeReceived = new RateWindow(6, 60_000);\n private livenessTimer: ReturnType<typeof setInterval> | null = null;\n private peerAnswersPings = false;\n /** When the ping awaiting its pong went, and the round trip last measured on this session. */\n private pingSentAt = 0;\n private rtt?: number;\n /** The contact's transport policy as last seen on this session, to tell its explicit choices apart. */\n private peerPolicySeen: { intent: number } | null = null;\n private unansweredPings = 0;\n /** The last ping's `PONG_WAIT_MS`: running until something comes back from the contact. */\n private pongWait: ReturnType<typeof setTimeout> | null = null;\n private openWaiters: { resolve: () => void; reject: (e: Error) => void }[] = [];\n /** Set while a WebRTC attempt this side dialled is pending: the ranked transports to try if it fails. */\n private afterRtc?: { epoch: number; rest: PairedTransport[] };\n /** When the transports after WebRTC are dialled while its offer is still out (`race`). */\n private raceTimer: ReturnType<typeof setTimeout> | null = null;\n /** Set while `race` dials: the offer ending meanwhile is followed up by the race. */\n private racing = false;\n /** The WebRTC offer this side's dial made last, with fallback allowed: what a transport that comes up meanwhile joins. */\n private offered?: { epoch: number; at: number };\n /** When that offer's answer was applied (`RACE_ANSWERED_MS`); 0 while the data link is not connecting on it. */\n private answeredAt = 0;\n /** This side's offer is out unanswered, or answered and not connected yet: what is ranked after WebRTC may race it. */\n private get offerOut(): boolean {\n return this.dataLink.state === \"offering\" || (this.dataLink.state === \"connecting\" && this.answeredAt > 0);\n }\n /** The attempt (`connectionEpoch`) in which the data link began answering the contact's offer. */\n private answeringEpoch = -1;\n private lastAutoConnectAt = 0;\n private autoConnectFailures = 0;\n /** Native transports a record just read listed with no way to dial them (`record-undescribed`), until one describes them again. */\n private readonly undescribed = new Set<NativeTransport>();\n /** The peer's packet I last looked fast for its offer after (its timestamp): once per packet. */\n private offerAwaitedFor = 0;\n /** Whether the stream was blocked (DHT-only on either side) when last looked at. */\n private streamWasBlocked: boolean;\n /** The contact's latest `_rtc` signal while the stream was blocked. */\n private heldSignal: { signal: string; sight?: SignalSight } | null = null;\n /** The link packet of a DHT-only contact its mailbox was last read early for (its timestamp): once per packet. */\n private leftDhtSeenFor = 0;\n /** The first pairing of this chat, while it is one (`pairingProgress` option, no peer key yet). */\n private readonly tracker: PairingTracker | null;\n private lastDataLinkState: DataLinkState = \"idle\";\n /** When an inviter first saw the joiner on the network (0: not yet). */\n private peerSeenAt = 0;\n /** When the channel carrying the chat now was attached. */\n private channelSince = 0;\n /** When the contact last sent something other than the session's upkeep (`QUIET_FRAMES`) on a session. */\n private activityAt = 0;\n /** Set while `yieldEndpoint` ends the session to let its listener go: nothing dials meanwhile. */\n private yielding = false;\n /**\n * The contact said goodbye on the live session (`paired-bye`): its app is going away, likely to restart. Its packet\n * then was `packet`; it is dialled again once a newer one shows it back, or after `until`.\n */\n private departed: { packet: number; until: number } | null = null;\n /** The contact's link packet last seen (its timestamp), to tell a new one from the same one read again. */\n private peerPacketSeen = 0;\n /** A live session ended without this side ending it (`peerLost`): until when the contact's next new packet is news. */\n private lostUntil = 0;\n /** This app is going away (`depart`): no more dials. */\n private leaving = false;\n /** `resume` still to act on: the first dial after the start goes to it, and a native one is knocked on. */\n private resuming: PairedTransport | undefined;\n /** Until then, a dial waits for the native endpoint `resuming` names to start (it is knocked on as it does). */\n private resumeWaitUntil = 0;\n\n constructor(options: GhostLinkOptions) {\n this.options = options;\n this.deliveryMode = options.params.deliveryMode ?? \"stream\";\n // A DHT-only invite pairs through its mailbox, not through these stages.\n this.tracker = options.pairingProgress && options.pairing && !options.pairing.credentials.peerKey && this.deliveryMode !== \"dht\"\n ? new PairingTracker(options.pairingProgress.role, options.pairingProgress.startedAt, progress => {\n traceLink(this.myPubKeyZ32, \"progress\", { stage: progress.stage, attempt: progress.attempt, peerSeen: progress.peerSeen, reason: progress.reason, retryable: progress.retryable, transport: progress.transport });\n options.events?.onPairingProgress?.(progress);\n }) : null;\n this.dht = options.params.profile && options.pairing && options.dht ? new DhtDelivery({\n params: options.params, mode: this.deliveryMode, state: options.dht.state, credentials: options.pairing.credentials, transport: options.transport,\n save: options.dht.save, pollMs: options.dht.pollMs, firstControlAfterMs: options.dht.firstControlAfterMs,\n capsRev: options.dht.capsRev, peerCapsRev: options.dht.peerCapsRev, peerAcceptsText: options.dht.peerAcceptsText,\n // Reactions ride on the envelopes off the live session (WISP 403 § Reactions).\n ...(options.reactionsSupport && {\n reactions: options.dht.reactions,\n reaction: async reaction => { await options.events?.onReaction?.(reaction); },\n reactionsTaken: async n => { await options.events?.onReactionsTaken?.(n); },\n }),\n // A first contact verified on the DHT pins the contact: the pairing is on the DHT until a stream is up.\n pin: async key => {\n await options.pairing!.pinPeer(key, true);\n if (this.isDataLinkOpen || !this.tracker || this.tracker.done) return;\n if (this.deliveryMode === \"dht\") { this.tracker.onDht(\"chosen\"); return; }\n // A stream is usually a moment away: the pairing shows its steps, and ends on the DHT only if no stream\n // makes it (an attempt fails), or none is under way a little later.\n this.tracker.pinnedOverDht();\n if (!this.dhtPinGrace) this.dhtPinGrace = setTimeout(() => {\n this.dhtPinGrace = null;\n if (!this.isDataLinkOpen && this.tracker?.progress.stage !== \"on-dht\") this.tracker?.onDht(\"waiting\");\n }, DHT_PIN_GRACE_MS);\n },\n message: async (message, packet) => {\n // An edit (WISP 403 § Edits) is not a message of its own: it changes one, on an app that takes edits.\n const { edit, ...text } = message;\n // Its text holds as a live edit's must (never blank): one that does not is dropped, never shown as a text.\n if (edit && options.editSupport) { if (validEditMessage(message.text)) await options.events?.onMessageEdit?.({ id: edit.i, e: edit.e, ts: message.timestamp, m: message.text }); return; }\n await options.events?.onMessage?.({ ...text, via: \"pkarr\", packet });\n },\n receipt: async id => { await options.events?.onMessageReceipt?.(id); },\n changed: view => {\n options.events?.onDhtDelivery?.(view);\n // Someone else used the invite first (the contact's envelopes are sealed to them): the pairing says so, and\n // stops saying it if an envelope of the contact's reads here again.\n if (view.inviteTaken) this.tracker?.failed(\"taken\", false, INVITE_TAKEN);\n else this.tracker?.untaken();\n this.streamBlockChanged();\n // A contact who chose DHT only (a ghostly1 code carries no mode) paired through the mailbox: the first\n // pairing ends on the DHT, chosen (WISP 400), and the stream attempt it will never answer is not a failure.\n if (this.dht?.peerMode === \"dht\" && options.pairing?.credentials.peerKey && this.tracker && !this.tracker.done && this.tracker.progress.reason !== \"chosen\") this.tracker.onDht(\"chosen\");\n if (this.streamBlocked) {\n if (this.channel || this.dialing || this.dataLink.state !== \"idle\") this.disconnect();\n this.emitDeliveryState();\n } else if (!this.paired) this.maybeAutoConnect(this.session.peerPresence);\n },\n }) : null;\n this.streamWasBlocked = this.streamBlocked;\n this.peerDescriptors = options.native?.peerDescriptors ?? {};\n this.peerTransports = options.native?.peerTransports;\n this.peerFallback = options.native?.peerFallback ?? false;\n this.preferred = options.native?.preferred ?? \"webrtc/1\";\n this.fallback = options.native?.fallback ?? true;\n this.automatic = options.native?.automatic ?? !options.native?.preferred;\n this.resuming = options.pairing?.credentials.peerKey && options.params.profile ? options.resume : undefined;\n const events = options.events ?? {};\n\n this.session = new LinkSession({\n params: options.params,\n transport: options.transport,\n nick: options.nick,\n lastSeenTimestamp: options.lastSeenTimestamp,\n pollIntervals: options.pollIntervals,\n getServices: options.getServices,\n // A joiner dials the moment it sees the inviter, and an app back after a restart (`resume`) the moment it reads its\n // contact: its offer goes in its first packet, not in a second one right behind its presence.\n firstPublish: options.firstPublish ?? ((this.tracker && options.pairingProgress?.role === \"joiner\") || this.resuming ? \"after-first-poll\" : \"at-start\"),\n events: {\n // The first look decided nothing to dial: say we are here now (a dial says it with its offer).\n onFirstPoll: () => { if (!this.dialing) this.session.ensureAdvertised(); },\n onMessages: (messages, batch) => {\n if (options.params.profile) return;\n for (const m of messages) events.onMessage?.({ ...m, via: \"pkarr\", batch });\n },\n onPresence: (presence) => {\n if (presence.online) this.tracker?.sawPeer();\n this.peerPacketArrived(presence);\n // A first contact under way: a fresh packet of the contact says its envelope is a read away.\n if (presence.online && !options.pairing?.credentials.peerKey && Date.now() - presenceSeenAt(presence) < EXPECT_PEER_MS) this.dht?.expect();\n events.onPresence?.(this.mergePresence(presence));\n this.peerMayHaveLeftDht(presence);\n this.maybeAutoConnect(presence);\n },\n onPublish: result => {\n // Held back by the relays' request budget: nothing failed, and the session sends it when the budget frees a request.\n if (result.waiting) return;\n if (result.error) { this.tracker?.failed(\"publish\", true, result.error); return; }\n // An offer or answer the relays held back (their budget, an outage) has its whole attempt from when it went out.\n if (result.signalOut) this.dataLink.signalWentOut();\n this.tracker?.published();\n this.publishRecovered();\n },\n onPeerAck: (ack) => events.onPeerAck?.(ack),\n onCallSignal: (signal) => { if (!options.params.profile) events.onCallSignal?.(heardCallSignal(signal)); },\n onRtcSignal: (signal, sight) => {\n traceLink(this.myPubKeyZ32, \"rtc-signal-in\", { held: this.streamBlocked });\n // Going away: an answer now would pair the contact with a connection about to die, and it would wait on it.\n if (this.leaving) return;\n // Seen once only: kept, and answered as soon as nothing blocks the stream any more.\n if (this.streamBlocked) { this.heldSignal = { signal, sight }; this.peerDialsFromDht(signal); return; }\n this.handleRtcSignal(signal, sight);\n },\n onPeerClock: (packetAt, readBefore, readAt) => events.onPeerClock?.(packetAt, readBefore, readAt),\n onPeerTransports: value => this.peerPacketTransports(value),\n onDiscoveryError: error => events.onDiscoveryError?.(error),\n onStatus: (status) => events.onStatus?.(status),\n onPoll: (poll) => events.onPoll?.(poll),\n },\n });\n\n this.dataLink = new DataLink({\n myPubKeyZ32: this.session.identity.pubKeyZ32,\n peerPubKeyZ32: options.params.peerPubKeyZ32,\n createPeerConnection: options.createPeerConnection,\n publishSignal: signal => {\n // Stopped: its data link closing clears a signal nobody will read.\n if (this.stopped) return;\n // A one-exchange link connected: it closes in a moment, and a packet clearing its offer would only spend the relays' budget.\n if (!signal && options.oneShot && this.dataLink.state === \"open\") return;\n const report = (error: unknown) => {\n // The relays' request budget held it back: the session publishes it once the budget frees a request, and\n // the pairing goes on meanwhile. Waiting for the budget is no connection issue.\n if (isDiscoveryBudgetError(error)) return;\n if (!signal || !options.params.profile) return;\n this.signalPublishFailed = true;\n events.onPairingState?.({ status: \"error\",\n error: publishFailure(error) });\n };\n try {\n if (signal && this.tracker) {\n const kind = (JSON.parse(signal) as { t?: string }).t;\n if (kind === \"o\") this.tracker.offerSent(); else if (kind === \"a\") this.tracker.answerSent();\n }\n const signed = signal && options.params.profile && options.pairing\n ? fitSignedPairedSignal(signal, options.pairing.credentials.seedB64, this.myPubKeyZ32,\n options.params.peerPubKeyZ32, candidate => this.session.fitsRtcSignal(candidate)) : signal;\n // During migration the authenticated channel already reaches the peer.\n // Carry signed ICE signaling there instead of waiting for DHT polling.\n if (this.switcher?.pending?.choices.includes(\"webrtc/1\") && this.channel && this.paired?.state.status === \"ready\") {\n if (signed) this.channel.send(JSON.stringify({ t: \"paired-rtc\", signal: signed }));\n return;\n }\n void this.session.setRtcSignal(signed, !!options.params.profile).catch(report);\n } catch (error) { report(error); throw error; }\n },\n // An offer to a saved contact: its answer may be a while (the contact may still hold this app's old session),\n // so the look for it slows after its first seconds (`OFFER_FAST_MS`). A first pairing looks fast throughout.\n setFastPoll: (fast, offer) => this.session.setFastPoll(fast, !!offer && !!options.pairing?.credentials.peerKey),\n // An answer that did not connect is made again only while the contact's packet still carries that offer.\n offerStanding: ts => !this.leaving && !this.stopped && !this.streamBlocked && this.peerOfferTs() === ts,\n onAnswerReplaced: () => this.redial(),\n onDirect: evidence => events.onDirectEvidence?.(evidence),\n onOpen: channel => {\n if (this.streamBlocked || this.keyStopped) { channel.close(); return; }\n const plan = this.switcher.pending;\n if (plan?.choices.includes(\"webrtc/1\") && this.paired?.state.status === \"ready\") {\n void this.attachCandidate(channel, undefined, plan).catch(() => {});\n } else if (this.activeBinding) channel.close(); else this.attach(channel);\n },\n onClose: () => {\n if (this.activeBinding) return;\n // A live WebRTC session the contact closed: dialled again at once, and watched for it to come back.\n const wasLive = !!this.channel && this.paired?.state.status === \"ready\";\n this.detach();\n if (wasLive) this.peerLost(\"closed\");\n },\n onState: (state) => {\n const was = this.lastDataLinkState;\n traceLink(this.myPubKeyZ32, \"datalink\", { state });\n this.trackDataLink(state);\n if (this.activeBinding) return;\n events.onDataLinkState?.(state);\n if (state === \"open\") this.afterRtc = undefined;\n if (state === \"answering\") this.answeringEpoch = this.connectionEpoch;\n this.answeredAt = 0;\n // This side's offer was answered: what is ranked after WebRTC is dialled soon after, if nothing connects.\n if (state === \"connecting\" && was === \"offering\" && this.offered?.epoch === this.connectionEpoch) {\n this.answeredAt = Date.now();\n this.scheduleRace(this.offered.epoch, this.offered.at);\n }\n if (state !== \"idle\") return;\n // An answer that could not even be made (no candidate at all), in an attempt nothing else ended: this side has\n // no WebRTC to give for now, and dials what else it runs, whatever its key. The contact, its offer standing,\n // would dial a relayed transport only after `RACE_RELAYED_MS`.\n const unanswerable = was === \"answering\" && this.answeringEpoch === this.connectionEpoch && !this.channel && !this.stopped && !this.leaving;\n if (unanswerable) this.rtcUnstarted();\n this.clearRace();\n // Transports ranked after WebRTC are being dialled: what comes next is theirs to say.\n if (this.racing) return;\n const next = this.afterRtc;\n if (next && next.epoch === this.connectionEpoch && !this.stopped && !this.channel && !this.streamBlocked) {\n // Still inside dial() (WebRTC failed while starting): the dial loop goes on by itself.\n if (this.dialing) return;\n this.afterRtc = undefined;\n void this.dialAfterRtc(next); return;\n }\n this.afterRtc = undefined;\n if (!this.dialing) this.attemptEnded();\n this.rejectWaiters(new GhostlyHttpError(\"unreachable\", \"Could not connect to the peer\"));\n if (unanswerable && this.dialsPastRtc()) this.redial();\n },\n // Asked as each attempt starts: once the first pairing is over, the contact reads at its chat's pace (30 s in the\n // background), and a short offer would be withdrawn before it looked.\n attemptTimeoutMs: () => this.tracker && !this.tracker.done ? PAIRING_ATTEMPT_MS : undefined,\n // The contact may have restarted: its offer (a restarted app offers whatever its key, `resume`) is read now, not\n // at the minute pace of a live chat, and answered at once, which ends the dead session (`DataLink.handleSignal`).\n onDisconnected: disconnected => {\n if (!disconnected || this.activeBinding || !this.channel) return;\n traceLink(this.myPubKeyZ32, \"rtc-disconnected\", {});\n this.session.pollNow();\n },\n });\n this.switcher = new TransportSwitch({\n key: this.myPubKeyZ32, peerKey: options.params.peerPubKeyZ32,\n policy: () => ({ preferred: this.preferred, fallback: this.fallback, available: this.availableTransports,\n descriptors: Object.fromEntries([...this.endpoints].map(([transport, endpoint]) => [transport, endpoint.descriptor])) }),\n send: frame => {\n try { if (this.channel && this.paired?.state.status === \"ready\") this.channel.send(JSON.stringify(frame)); }\n catch { /* Channel closure is handled by its owner; retry uses a fresh session. */ }\n },\n peer: policy => {\n const before = this.peerPolicySeen;\n this.peerPolicySeen = { intent: policy.intent };\n if (before && policy.intent > before.intent) { this.peerChoice = policy.preferred; options.events?.onPeerTransportChoice?.(policy.preferred); }\n // Intent 0 is only ever \"automatic\": the contact dropped its standing choice.\n else if (before && before.intent > 0 && policy.intent === 0) { this.peerChoice = undefined; options.events?.onPeerTransportChoice?.(\"automatic\"); }\n // A session's first policy that stands for a choice not heard of yet: made while no session was open, and its\n // record not read (yet). Said once, and the owner knows what it already told.\n else if (!before && policy.intent > 0 && policy.preferred !== this.peerChoice) { this.peerChoice = policy.preferred; options.events?.onPeerTransportChoice?.(policy.preferred, true); }\n this.peerDescriptors = policy.descriptors;\n this.peerTransports = transportOrder(policy.available, policy.preferred, true);\n this.peerFallback = policy.fallback;\n void options.events?.onTransportDiscovery?.(policy.descriptors, this.peerTransports, policy.fallback).catch(() => {});\n this.emitPairingState();\n },\n state: (error, target) => {\n this.transitionError = error;\n // Trying again for a transport the chat already waits for is not a switch starting: the wait says it.\n this.transitionTarget = target && this.waiting?.transport === target && this.waiting.failures > 0 ? undefined : target;\n this.emitPairingState();\n },\n prepare: (plan, dial) => { if (dial) void this.prepareSwitch(plan); },\n cancel: () => this.cancelCandidate(),\n kept: (target, reason) => {\n // The chat stayed live on a fallback, or moved to one: said once per choice (a row), then tried again quietly.\n // Moved: why the target did not connect is known on the side that dialled it (`prepareSwitch`).\n reason ??= this.switchMissed?.transport === target ? this.switchMissed.reason : undefined;\n if (this.unreached(target, reason)) options.events?.onTransportSwitchFailed?.(target, reason);\n },\n unreached: (target, reason) => { this.unreached(target, reason); },\n });\n // A transport chosen before this link started (the owner says `automatic: false`: kept from a run before a\n // restart) is still a choice (WISP 100, \"A choice made while not live\"): the first session begins with it as a\n // switch intent. Without it both sides' intents start at none, and the chat stays wherever the first dial landed,\n // waiting for nothing. A `preferred` alone is only where to dial first.\n if (options.native?.automatic === false) this.switcher.choseApart();\n }\n\n get myPubKeyZ32(): string {\n return this.session.identity.pubKeyZ32;\n }\n\n /** The first pairing's stages, read off the data link's states. */\n private trackDataLink(state: DataLinkState): void {\n const was = this.lastDataLinkState;\n this.lastDataLinkState = state;\n // WebRTC as a way to go live, not as the candidate of a switch on a session already open.\n if (!this.activeBinding) {\n if (state === \"answering\") this.attempt ??= { side: \"answered\", failed: [] };\n else if (state === \"idle\" && (was === \"offering\" || was === \"answering\" || was === \"connecting\"))\n this.attemptFailed(\"webrtc/1\", was === \"offering\" ? \"The offer was not answered\" : \"No connection came up\");\n }\n const tracker = this.tracker;\n if (!tracker || tracker.done) return;\n if (state === \"answering\") tracker.offerReceived();\n else if (state === \"connecting\" && was === \"offering\") tracker.answerReceived();\n else if (state === \"idle\") {\n if (was === \"offering\") tracker.failed(\"timeout\", true);\n else if (was === \"answering\" || was === \"connecting\") tracker.failed(\"transport\", true);\n else if (was === \"open\") tracker.reset();\n }\n }\n\n /** How far the first pairing got; absent for a chat that was paired before, or has no `pairingProgress`. */\n get pairingProgress(): PairingProgress | undefined { return this.tracker?.progress; }\n\n /**\n * A contact's offer from before the session that was live when this app last ran began (`resumeFloor`): answering\n * it held the resumed link on a connection the contact no longer offers, until ICE gave up (about 30 s), and its\n * resume dial never went (bug hunt r7a). Dropped; the resumed link dials when it sees the contact.\n *\n * The floor is this device's clock and the offer's time is the contact's: the two are compared only for an offer\n * the first read of this run found, which nothing else dates. One this run saw arrive (`sight`: the contact's record\n * did not have it at an earlier read, and it is not dated before the contact's packet that read found) was made\n * since this app is back. Held against the floor, the offers of a contact whose clock is behind were all dropped\n * until that clock passed the moment the last session began: minutes without a live chat after a restart.\n */\n private predatesLastSession(verified: string, sight?: SignalSight): boolean {\n const floor = this.options.resumeFloor;\n if (floor === undefined) return false;\n const offer = parseRtcSignal(verified);\n if (offer?.t !== \"o\" || offer.ts >= floor) return false;\n if (sight && (sight.after === null || offer.ts >= sight.after)) return false;\n traceLink(this.myPubKeyZ32, \"stale-offer\", { beforeMs: floor - offer.ts });\n this.maybeAutoConnect(this.presence);\n return true;\n }\n\n /** The time of the offer the contact's packet carries, as last read (verified as `handleRtcSignal` does); null for none. */\n private peerOfferTs(): number | null {\n const signal = this.session.peerSignal, credentials = this.options.pairing?.credentials;\n if (!signal) return null;\n const verified = this.options.params.profile ? verifyPairedSignal(signal, this.options.params.peerPubKeyZ32,\n this.myPubKeyZ32, credentials?.peerKey, credentials?.requireSignedSignals) : signal;\n const parsed = verified ? parseRtcSignal(verified) : null;\n return parsed?.t === \"o\" ? parsed.ts : null;\n }\n\n private handleRtcSignal(signal: string, seen?: SignalSight): void {\n // A read that found nothing of the contact's leaves nothing to hold the offer's own time against. A first contact\n // goes by it all the same, its first packet being the one with its offer: a first pairing, before the pin, and a\n // group's link, whose records only its two members write. A pinned contact's offer in a chat is then judged by\n // its own time, as one the first read finds.\n const firstContact = !!this.options.ownRecords || (!!this.options.pairing && !this.options.pairing.credentials.peerKey);\n const sight = seen && seen.after === null && !firstContact ? undefined : seen;\n const options = this.options, credentials = options.pairing?.credentials;\n const verified = options.params.profile ? verifyPairedSignal(signal, options.params.peerPubKeyZ32,\n this.myPubKeyZ32, credentials?.peerKey, credentials?.requireSignedSignals) : signal;\n if (verified && this.predatesLastSession(verified, sight)) return;\n if (verified) void this.dataLink.handleSignal(verified, sight);\n else if (credentials?.peerKey) {\n // The link's records are published under keys derived from the invite: anyone holding a copy of it can put a\n // signal there, signed by a key of their own or by none. After the pin such a signal is dropped, never a reason\n // to stop the chat; one validly signed by another key is a passive warning (WISP 400).\n if (verifyPairedSignal(signal, options.params.peerPubKeyZ32, this.myPubKeyZ32, undefined, true)) this.dht?.foreignKeySeen(\"signal\");\n else traceLink(this.myPubKeyZ32, \"signal-dropped\", {});\n } else if (credentials?.requireSignedSignals && !this.isDataLinkOpen) {\n this.securityRejected = true;\n this.tracker?.failed(\"rejected\", false);\n options.events?.onPairingState?.({ status: \"error\", keyMismatch: false,\n error: \"Ignored an unauthenticated discovery signal. Keep both peers on the updated version; the saved key has not been replaced.\" });\n }\n }\n\n /**\n * Leaving DHT-only takes both sides: each is blocked from a live link until it has seen the other one\n * leave too. The moment it is not, the link is dialled or answered at once: the wait between failed\n * attempts starts over, the contact's offer (or its presence) is looked for fast, and an offer that\n * came in while blocked is answered now instead of timing out on the contact's side (90 s, then its\n * backoff) before it offers again.\n */\n private streamBlockChanged(): void {\n const blocked = this.streamBlocked, was = this.streamWasBlocked;\n this.streamWasBlocked = blocked;\n if (blocked || !was) return;\n traceLink(this.myPubKeyZ32, \"unblocked\", { held: !!this.heldSignal });\n this.autoConnectFailures = 0;\n this.lastAutoConnectAt = 0;\n this.session.expectPeer();\n const held = this.heldSignal;\n this.heldSignal = null;\n if (held) this.handleRtcSignal(held.signal, held.sight);\n const parked = this.blockedDial;\n if (parked) { this.blockedDial = null; clearTimeout(parked.timer); parked.channel.onClose = null; this.takeDialIn(parked.channel, parked.binding); }\n }\n\n /**\n * A DHT-only contact runs no link session, so it does not advertise itself on the link's key: a fresh\n * packet there that does is it leaving DHT-only. Its mailbox, which says so, is read now rather than at\n * the next poll, and at the DHT pace for a while after: the link packet can reach a relay before the\n * envelope saying so does (back from `chat disconnect --hold`), and a read that still finds DHT-only\n * would otherwise keep the chat off a live link until the next background read, 30 s on.\n */\n private peerMayHaveLeftDht(presence: PeerPresence): void {\n if (!this.dht || this.dht.peerMode !== \"dht\" || this.deliveryMode === \"dht\" || !presence.online) return;\n if (Date.now() - presenceSeenAt(presence) >= EXPECT_PEER_MS || presence.lastPacketAt === this.leftDhtSeenFor) return;\n this.leftDhtSeenFor = presence.lastPacketAt;\n traceLink(this.myPubKeyZ32, \"peer-link-packet\", { age: Date.now() - presenceSeenAt(presence) });\n this.dht.expect(EXPECT_PEER_MS, true);\n }\n\n /**\n * The contact dials (a WebRTC offer signed by its pinned key, or a connection on a native endpoint) while its\n * mailbox last said DHT-only: it has left, and the envelope saying so is on its way. The offer's packet often has no\n * room for the presence `peerMayHaveLeftDht` looks at, so the mailbox is read now and at the DHT pace for a while\n * here too. The offer is kept and answered, and the dial taken, once that read unblocks the stream. False when\n * something else blocks it (this side is DHT-only, the contact is not pinned, or the offer is not the contact's).\n */\n private peerDialsFromDht(signal?: string): boolean {\n if (!this.dht || this.dht.peerMode !== \"dht\" || this.deliveryMode === \"dht\") return false;\n const credentials = this.options.pairing?.credentials;\n if (!credentials?.peerKey) return false;\n if (signal !== undefined && !verifyPairedSignal(signal, this.options.params.peerPubKeyZ32, this.myPubKeyZ32, credentials.peerKey, true)) return false;\n traceLink(this.myPubKeyZ32, \"peer-dials\", { signal: signal !== undefined });\n this.dht.expect(EXPECT_PEER_MS, true);\n return true;\n }\n\n get isDataLinkOpen(): boolean {\n return !this.streamBlocked && this.channel !== null && (!this.options.params.profile || (this.paired?.state.status === \"ready\" && this.currentTransportAllowed()));\n }\n\n private currentTransportAllowed(): boolean {\n const actual = this.paired?.state.transport;\n const remote = this.switcher?.peerPolicy;\n return !!actual && this.transportOffer().includes(actual) && (!remote || allowedTransports(remote).includes(actual));\n }\n /**\n * A packet went out after connection details could not be: the session kept trying (every few seconds), so the\n * error that was reported is over and the pairing carries on from where it is.\n */\n private publishRecovered(): void {\n if (!this.signalPublishFailed) return;\n this.signalPublishFailed = false;\n if (this.stopped || this.keyStopped || this.securityRejected) return;\n if (this.streamBlocked) this.emitDeliveryState();\n else if (this.paired?.state) this.emitPairingState();\n else this.options.events?.onPairingState?.({ status: \"connecting\" });\n }\n\n private emitPairingState(): void {\n this.syncWait();\n const state = this.paired?.state;\n if (!state) { this.notifyWait(); return; }\n const blocked = state.status === \"ready\" && !this.currentTransportAllowed();\n const open = this.isDataLinkOpen;\n if (open !== this.applicationOpen) {\n this.applicationOpen = open;\n // While layer 1 carries the chat its mailbox is read every 5 minutes; lost, at once (WISP 403).\n this.dht?.setLive(open);\n if (!open) this.pairedFiles?.closeAll();\n this.options.events?.onDataLinkState?.(open ? \"open\" : \"idle\");\n }\n this.options.events?.onPairingState?.({ ...state,\n ...(blocked ? { status: this.transitionError ? \"error\" as const : \"negotiating\" as const, error: this.transitionError } : {}),\n transitionTarget: this.transitionTarget, transitionError: this.transitionError,\n });\n this.emitFilesSession();\n this.notifyWait();\n }\n\n get presence(): PeerPresence {\n return this.mergePresence(this.session.peerPresence);\n }\n\n private get streamBlocked(): boolean { return this.deliveryMode === \"dht\" || this.dht?.peerMode === \"dht\"; }\n /**\n * A stream authenticated a participation key other than the pinned one: a security rejection, which stops the chat\n * on both layers until the person acts (WISP 400), never a reason to fall back to the other path. Only a stream can\n * prove it: the DHT mailbox and the link's signals are written under keys any copy of the invite derives, so another\n * key there is ignored instead.\n */\n private keyStopped = false;\n /** A connection whose failure to authenticate says nothing about the contact: dialled in on a pinned chat. */\n private unproven(channel: FrameChannel): boolean { return this.dialedIn.has(channel) && !!this.options.pairing?.credentials.peerKey; }\n get textDelivery(): \"stream\" | \"dht\" | \"unavailable\" {\n if (this.isDataLinkOpen) return \"stream\";\n if (!this.dht || this.securityRejected || this.keyStopped) return \"unavailable\";\n if (this.deliveryMode === \"dht\") return \"dht\";\n // A session that the policies keep from carrying the chat (a transport waited for with Fallback off) is not live:\n // text goes over the DHT floor meanwhile, as with no session (WISP 100).\n return (!this.channel || this.sessionBlocked) && !!this.options.pairing?.credentials.peerKey && !!this.dht.peerMode ? \"dht\" : \"unavailable\";\n }\n /** A session is ready, but on a transport the policies do not allow: it coordinates a switch and carries nothing else. */\n private get sessionBlocked(): boolean { return this.paired?.state.status === \"ready\" && !this.currentTransportAllowed(); }\n validateText(text: string, timestamp: number, id: string, reply?: WireReply): string | null {\n // Nobody reads this side's texts: the contact seals everything to whoever used the invite first.\n if (!this.isDataLinkOpen && this.dht?.view.inviteTaken) return INVITE_TAKEN;\n if (this.textDelivery === \"dht\") return this.dht!.validate(text, timestamp, id, reply?.i);\n return this.canSendText ? null : \"No authenticated text delivery method is available.\";\n }\n get canSendText(): boolean { return this.textDelivery !== \"unavailable\"; }\n get dhtDelivery(): DhtDeliveryView | undefined { return this.dht?.view; }\n /** The chat is on screen: on the DHT its mailbox is read at the signaling pace (WISP 403, poll pace). */\n setChatActive(active: boolean): void {\n this.session.setActive(active);\n this.dht?.setActive(active);\n }\n private emitDeliveryState(): void {\n if (!this.streamBlocked) return;\n const credentials = this.options.pairing?.credentials;\n const error = this.dht?.view.error;\n this.options.events?.onPairingState?.({ status: error ? \"error\" : credentials?.peerKey ? \"ready\" : \"connecting\",\n peerKey: credentials?.peerKey, code: this.dht?.comparisonCode, verified: !!credentials?.peerKey && credentials.verifiedPeerKey === credentials.peerKey, error });\n }\n async setDeliveryMode(mode: DeliveryMode): Promise<void> {\n if (!this.dht) throw new Error(\"DHT delivery is unavailable for this conversation.\");\n traceLink(this.myPubKeyZ32, \"set-mode\", { mode, peerMode: this.dht.peerMode, dials: this.myPubKeyZ32 < this.options.params.peerPubKeyZ32 });\n this.deliveryMode = mode;\n // Someone chose this just now: attempts that failed before are no reason to wait.\n this.autoConnectFailures = 0;\n this.lastAutoConnectAt = 0;\n if (mode === \"dht\") { this.disconnect(); this.heldSignal = null; }\n // The link session runs before the contact can learn the new method (from the envelope setMode publishes).\n else this.session.start();\n await this.dht.setMode(mode);\n if (mode === \"dht\") {\n await this.session.stop(false);\n await Promise.allSettled([...this.endpoints.keys()].map(t => this.releaseEndpoint(t)));\n }\n this.streamBlockChanged();\n this.emitDeliveryState();\n }\n start(): void {\n if (this.deliveryMode !== \"dht\") this.session.start();\n // Back after a restart: the contact, if it still holds the old session, dials or offers as soon as it notices;\n // its offer is looked for fast rather than at the background pace.\n if (this.resuming && this.deliveryMode !== \"dht\") {\n this.session.expectPeer();\n this.resumeWaitUntil = Date.now() + RESUME_WAIT_MS;\n }\n // A joiner has just opened the invite: the inviter is there, and its first-contact envelope is worth reading\n // at the signaling pace now (an inviter speeds up when the joiner's fresh packet shows).\n if (this.tracker && !this.tracker.done && this.options.pairingProgress?.role === \"joiner\") this.dht?.expect();\n void this.dht?.start();\n }\n\n async stop(announce = true): Promise<void> {\n this.stopped = true;\n this.clearRace();\n if (this.dhtPinGrace) clearTimeout(this.dhtPinGrace);\n if (this.waiting?.timer) clearTimeout(this.waiting.timer);\n await this.dht?.stop();\n this.disconnect();\n await Promise.allSettled([...this.endpoints.values()].map(endpoint => endpoint.close()));\n this.endpoints.clear();\n await this.session.stop(announce);\n }\n\n /** Opens the data link if needed and resolves once it is usable. */\n connect(timeoutMs = 90_000): Promise<void> {\n if (this.streamBlocked) return Promise.reject(new Error(\"DHT-only delivery does not open a live connection. Both peers must choose a live method first.\"));\n if (this.paired?.state.status === \"ready\") { this.switcher.retry(); return Promise.resolve(); }\n if (this.isDataLinkOpen) return Promise.resolve();\n const epoch = this.connectionEpoch;\n return new Promise<void>((resolve, reject) => {\n const waiter = {\n resolve: () => { clearTimeout(timer); resolve(); },\n reject: (error: Error) => { clearTimeout(timer); reject(error); },\n };\n this.openWaiters.push(waiter);\n const timer = setTimeout(() => {\n const index = this.openWaiters.indexOf(waiter);\n if (index < 0) return;\n this.openWaiters.splice(index, 1);\n reject(new GhostlyHttpError(\"timeout\", \"Timed out connecting to the peer\"));\n }, timeoutMs);\n void this.dial().catch(error => {\n if (epoch !== this.connectionEpoch) return;\n const message = error instanceof Error ? error.message : String(error);\n this.tracker?.failed(\"transport\", true, message);\n // Waiting for a chosen transport is not a connection error (WISP 100): the wait says why.\n if (!this.dialFailed(message)) this.options.events?.onPairingState?.({ status: \"error\", error: message });\n this.rejectWaiters(error instanceof Error ? error : new Error(message));\n });\n });\n }\n\n /** Closes what a switch replaced once the contact has had time to move too. */\n private retire(old: FrameChannel, also: () => void): void {\n if (this.retiring.has(old)) return;\n const close = () => { this.retiring.delete(old); old.close(); also(); };\n this.retiring.set(old, { close, timer: setTimeout(close, SWITCH_RETIRE_MS) });\n }\n private closeRetired(): void {\n for (const { timer, close } of [...this.retiring.values()]) { clearTimeout(timer); close(); }\n }\n\n disconnect(): void {\n this.closeRetired();\n this.dropCrossed();\n this.dropBlockedDial();\n this.switcher.stop(); this.cancelCandidate();\n this.transitionTarget = this.transitionError = undefined;\n this.connectionEpoch++;\n this.dialing = false;\n this.clearRace();\n const channel = this.channel;\n this.detach();\n channel?.close();\n this.dataLink.close();\n this.rejectWaiters(new GhostlyHttpError(\"unreachable\", \"Connection attempt cancelled\"));\n }\n\n /** What the contact's app can use on this link, as it last said (on the open session, or remembered). */\n get peerAvailableTransports(): PairedTransport[] | undefined {\n const now = this.switcher.peerPolicy?.available;\n if (!now) return this.peerTransports;\n // A session can open before every endpoint of the contact's started: its record says what its app runs.\n return [...now, ...(this.peerRecordTransports ?? []).filter(t => !now.includes(t))];\n }\n /** The open session goes through relays (WISP 100, \"Relayed\"): their hosts, this side's first. */\n get relayedPath(): { relays: string[] } | undefined {\n const transport = this.activeBinding?.transport;\n if (!transport || !this.isDataLinkOpen) return undefined;\n const local = this.localDescriptors(), remote = this.switcher.peerPolicy?.descriptors ?? this.peerDescriptors;\n if (!relayedTransports(local, remote).includes(transport)) return undefined;\n const relays: string[] = [];\n for (const descriptor of [local[transport], remote[transport]]) {\n const relay = (descriptor as { relay?: unknown } | undefined)?.relay;\n if (typeof relay !== \"string\") continue;\n try { const host = new URL(relay).host.replace(/\\.$/, \"\"); if (!relays.includes(host)) relays.push(host); } catch { /* Not a URL: not shown. */ }\n }\n return { relays };\n }\n /** The round trip last measured on the open session; unknown until a ping was answered. */\n get rttMs(): number | undefined { return this.isDataLinkOpen ? this.rtt : undefined; }\n\n /**\n * The transports a session with this contact would cross a relay on: those either side reaches only\n * through one (a browser's HyperDHT or Iroh), per the latest descriptors both sides gave.\n */\n get relayedTransports(): PairedTransport[] {\n return relayedTransports(this.localDescriptors(), this.switcher.peerPolicy?.descriptors ?? this.peerDescriptors);\n }\n\n /** This side's native descriptors, for its capability record (WISP 03): what a contact dials without WebRTC first. */\n get nativeDescriptors(): TransportDescriptors {\n return Object.fromEntries([...this.endpoints].map(([transport, endpoint]) => [transport, endpoint.descriptor])) as TransportDescriptors;\n }\n\n /**\n * The contact's capability record named its native transports and how to dial them: a chat whose WebRTC never\n * connected can still try Iroh or HyperDHT. `fresh`: a record just read, the contact's latest word, so a way to dial\n * it gives (a relay its Iroh endpoint homed on after it started, a new endpoint) replaces the one known. Otherwise (the\n * record kept from before) it only fills what is missing: what a session said is fresher, and transcript-bound.\n */\n learnPeerTransports(transports: PairedTransport[], descriptors: TransportDescriptors, fresh = false): void {\n const wanted = this.wanted()?.transport;\n const listed = !!wanted && !!this.peerRecordTransports?.includes(wanted), dialable = !!wanted && !!this.peerDescriptors[wanted as NativeTransport];\n // The record lists every transport the contact's app runs, started or not (WISP 03): what it lacks, it lacks.\n this.peerRecordTransports = [...transports];\n let changed = false, redescribed = false, again = false, news = false;\n const renamed = new Set<NativeTransport>();\n for (const t of [\"iroh/1\", \"hyperdht/1\"] as const) {\n const known = this.peerDescriptors[t], next = recordDescriptor(t, known, descriptors[t], fresh, transports.includes(t));\n if (next === known) continue;\n if (next === undefined) {\n // Its endpoint is down for now: dials to the old one only timed out (each 20 s, the chat \"retrying live\" for\n // minutes after both apps restarted). The record that describes it again is news, and dialled at once.\n const { [t]: _gone, ...rest } = this.peerDescriptors;\n this.peerDescriptors = rest;\n traceLink(this.myPubKeyZ32, \"record-undescribed\", { transport: t });\n this.undescribed.add(t);\n continue;\n }\n this.peerDescriptors = { ...this.peerDescriptors, [t]: next };\n // Described again after a record said its endpoint was down: dialled at once, but the attempts that failed keep\n // counting. A contact whose record keeps going down and up again must not undo the wait between attempts.\n if (this.undescribed.delete(t)) again = true; else news = true;\n changed = true; redescribed ||= t === wanted; renamed.add(t);\n // A new way to dial it: the attempts that failed on the old one say nothing about this one.\n this.nativeFailures.delete(t); this.demotedUntil.delete(t);\n }\n if (!this.peerTransports && transports.length) { this.peerTransports = transports; this.peerFallback = true; changed = news = true; }\n else {\n // Its app runs more than a session said (an endpoint that started later): those go after the session's order.\n const added = transports.filter(t => !this.peerTransports?.includes(t));\n if (added.length) { this.peerTransports = [...this.peerTransports!, ...added]; changed = news = true; }\n }\n // A transport the chat waits for, newly listed or dialable: tried again now, not at the next retry (WISP 100).\n if (wanted && ((!listed && transports.includes(wanted)) || (!dialable && !!this.peerDescriptors[wanted as NativeTransport]) || redescribed)) this.waitNews();\n this.notifyWait();\n if (!changed) return;\n traceLink(this.myPubKeyZ32, \"record-transports\", { transports });\n // A connection of this side's that has not authenticated, dialled where the contact's record no longer says it is:\n // it went to the old address, and the new one is dialled now rather than when that connection gives up.\n const held = this.channel, heldOn = this.activeBinding?.transport as NativeTransport | undefined;\n if (held && heldOn && renamed.has(heldOn) && !this.dialedIn.has(held) && this.paired?.state.status !== \"ready\"\n && Date.now() - this.channelSince >= REDESCRIBED_GRACE_MS) {\n traceLink(this.myPubKeyZ32, \"redescribed-redial\", { transport: heldOn });\n this.disconnect();\n this.redial();\n return;\n }\n // Something new to dial: the next attempt is now, not after the wait that attempts with nothing to try built up.\n if (this.channel || this.dialing) return;\n if (news || !again) { this.autoConnectFailures = 0; this.lastAutoConnectAt = 0; }\n else this.dialOnNews();\n this.maybeAutoConnect(this.presence);\n }\n\n /** When `dialOnNews` last let a dial go before its wait was over. */\n private lastNewsDialAt = -Infinity;\n /**\n * A chat's listener came up, or the contact's record describes again an endpoint it said was down: the next dial goes\n * now rather than after the wait between attempts, once per that wait. An endpoint registered again and again (a\n * listener lost and started anew) or a record going down and up again would otherwise dial every time, each a native\n * dial, Rust work on a Desktop (a Linux Desktop's idle CPU, 2026-10-01). The failures keep counting.\n */\n private dialOnNews(): void {\n if (Date.now() - this.lastNewsDialAt < this.dialWait()) return;\n this.lastNewsDialAt = Date.now();\n this.lastAutoConnectAt = 0;\n }\n\n /**\n * This side's capability record has a new revision: a DHT envelope names it now, so the contact reads it (WISP 03).\n * Only on the DHT with the contact pinned. A live session carries the same news itself, and before the pin the\n * contact reads the record when it pins; either way an envelope would only spend a relay request, the budget the\n * chat's signalling and held items need.\n */\n announceCapsRevision(): void {\n if (this.isDataLinkOpen || !this.options.pairing?.credentials.peerKey) return;\n void this.dht?.announce().catch(() => {});\n }\n\n /** The transport chosen for this chat on this side, for its capability record; none on Automatic. */\n get choice(): PairedTransport | undefined { return this.automatic ? undefined : this.preferred; }\n\n /**\n * The contact's explicit choice as its capability record names it (WISP 100, \"A choice made while not live\"): how a\n * contact with no session hears of it. `fresh`: a record just read. One that names another choice than the last heard\n * is told (`onPeerTransportChoice`, apart) and dialled first from now on. With a session open, its intents are the\n * contact's word, not a record that may be older; the record kept from before only sets where to start.\n */\n learnPeerChoice(choice: string | undefined, fresh = false): void {\n const next = (TRANSPORTS as readonly string[]).includes(choice ?? \"\") ? choice as PairedTransport : undefined;\n if (!fresh) { this.peerChoice = next; return; }\n if (this.isDataLinkOpen || next === this.peerChoice) return;\n this.peerChoice = next;\n this.options.events?.onPeerTransportChoice?.(next ?? \"automatic\", true);\n this.syncWait(); this.notifyWait();\n this.dialNow();\n }\n\n /**\n * The explicit choice that stands with no session to agree in (WISP 100, \"A choice made while not live\"): this\n * side's, or the contact's last heard. When both chose and they differ, the lower rendezvous key's, as a tie is\n * settled on a session. None when neither chose.\n */\n private get choiceApart(): { transport: PairedTransport; by: \"you\" | \"contact\" } | undefined {\n const mine = this.choice, theirs = this.peerChoice;\n if (mine && (!theirs || theirs === mine || this.myPubKeyZ32 < this.options.params.peerPubKeyZ32)) return { transport: mine, by: \"you\" };\n return theirs ? { transport: theirs, by: \"contact\" } : undefined;\n }\n\n /** Something decides where the chat dials first now: the next attempt goes at once, not after the wait failures built up. */\n private dialNow(): void {\n if (this.channel || this.dialing) return;\n this.autoConnectFailures = 0;\n this.lastAutoConnectAt = 0;\n this.maybeAutoConnect(this.presence);\n }\n\n /**\n * Where the chat is set to go and is not yet (WISP 100, \"A chosen transport not reached yet\"). With a session, the\n * agreement of both policies; without one, a limit of this side's (Fallback off), or else an explicit choice (this\n * side's, or the contact's from its record): otherwise any transport will do, and the chat is simply retrying live.\n */\n private wanted(): { transport: PairedTransport; by?: \"you\" | \"contact\" } | undefined {\n if (!this.options.params.profile || this.streamBlocked || this.keyStopped || this.stopped || !this.options.pairing?.credentials.peerKey) return undefined;\n const state = this.paired?.state;\n if (state?.status === \"ready\") {\n if (!this.paired!.peerTransportSwitchSupport) return undefined;\n const wanted = this.switcher.wanted();\n return wanted && wanted.transport !== state.transport ? wanted : undefined;\n }\n if (!this.fallback) return { transport: this.preferred, ...(this.automatic ? {} : { by: \"you\" as const }) };\n // Any transport may carry the chat, but someone chose one (WISP 100, \"A choice made while not live\"): a side that\n // lacks it says so, as with Fallback off.\n return this.choiceApart;\n }\n\n /** The transport this chat waits for, and why; absent when it is on it, or nothing limits where it goes. */\n get transportWait(): TransportWait | undefined {\n const wanted = this.wanted();\n if (!wanted) return undefined;\n const t = wanted.transport, policy = this.switcher.peerPolicy, waiting = this.waiting?.transport === t ? this.waiting : null;\n const sessionHas = !!policy?.available.includes(t), recordHas = !!this.peerRecordTransports?.includes(t);\n const dialable = t === \"webrtc/1\" ? !!this.peerTransports?.includes(t) : !!this.peerDescriptors[t];\n const failures = waiting?.failures ?? 0;\n const reason: TransportWait[\"reason\"] = !this.availableTransports.includes(t) ? \"app-lacks\"\n : this.switcher.pending?.choices[0] === t ? \"connecting\"\n : sessionHas ? (failures ? \"unreachable\" : \"waiting\")\n : this.peerRecordTransports && !recordHas ? \"contact-lacks\"\n : policy || !dialable ? (recordHas ? \"starting\" : \"unknown\")\n : failures ? \"unreachable\" : \"waiting\";\n // Without a session, a wait is about the transport being there on both sides. A contact that has it and is away,\n // or not reached yet, is the chat retrying live, as any chat on the DHT is (WISP 400, `on-dht`).\n if (this.paired?.state.status !== \"ready\" && reason !== \"contact-lacks\" && reason !== \"starting\" && reason !== \"app-lacks\") return undefined;\n const live = this.isDataLinkOpen ? this.paired?.state.transport : undefined;\n return { transport: t, ...(wanted.by ? { by: wanted.by } : {}), reason, ...(live ? { live } : {}), failures,\n ...(waiting?.error ? { error: waiting.error } : {}), ...(waiting?.retryAt ? { retryAt: waiting.retryAt } : {}) };\n }\n\n /**\n * An attempt to reach `target` did not connect. The chat waits for it, and the side that plans switches tries again\n * on the background pace (`schedule`: with a session; without one the dial's own pace does). True the first time\n * for this wait: a fallback that kept the chat live is said once, later attempts are quiet.\n */\n private unreached(target: PairedTransport, error?: string, schedule = true): boolean {\n const same = this.waiting?.transport === target ? this.waiting : null;\n if (same?.timer) clearTimeout(same.timer);\n const failures = (same?.failures ?? 0) + 1, told = !!same?.told;\n const delay = Math.min(AUTO_CONNECT_RETRY_MS * 2 ** (failures - 1), AUTO_CONNECT_MAX_RETRY_MS);\n const coordinates = this.myPubKeyZ32 < this.options.params.peerPubKeyZ32 && this.paired?.state.status === \"ready\";\n this.waiting = { transport: target, failures, told: true, ...(error ?? same?.error ? { error: error ?? same?.error } : {}) };\n if (schedule && coordinates && !this.stopped) {\n this.waiting.retryAt = Date.now() + delay;\n this.waiting.timer = setTimeout(() => this.retryWait(), delay);\n }\n traceLink(this.myPubKeyZ32, \"wait\", { transport: target, failures, retryIn: this.waiting.retryAt ? delay : undefined, error });\n this.notifyWait();\n return !told;\n }\n\n /** A dial with no session failed while the chat waits for a transport: counted there. True when it waits. */\n private dialFailed(message: string): boolean {\n const wait = this.transportWait;\n if (wait) this.unreached(wait.transport, message, false);\n return !!wait;\n }\n\n /** The next attempt for the transport the chat waits for, on the background pace. */\n private retryWait(): void {\n const waiting = this.waiting;\n if (!waiting) return;\n waiting.timer = waiting.retryAt = undefined;\n if (this.stopped || this.paired?.state.status !== \"ready\" || !this.paired.peerTransportSwitchSupport) return;\n traceLink(this.myPubKeyZ32, \"wait-retry\", { transport: waiting.transport, failures: waiting.failures });\n this.switcher.again();\n this.notifyWait();\n }\n\n /** Something new about the transport waited for (the contact's record lists it, or says how to dial it): try now. */\n private waitNews(): void {\n if (this.waiting) {\n if (this.waiting.timer) clearTimeout(this.waiting.timer);\n this.waiting.timer = this.waiting.retryAt = undefined; this.waiting.failures = 0;\n }\n if (this.paired?.state.status === \"ready\" && this.paired.peerTransportSwitchSupport) this.switcher.again();\n }\n\n private clearWait(): void {\n if (this.waiting?.timer) clearTimeout(this.waiting.timer);\n this.waiting = null;\n }\n /** The chat reached what it waited for, or waits for something else now (a new choice, Automatic, a policy change). */\n private syncWait(): void {\n if (this.waiting && this.wanted()?.transport !== this.waiting.transport) this.clearWait();\n }\n private waitSaid = \"\";\n /** Tells the owner when the wait's view changed (its reason, failures, next retry). */\n private notifyWait(): void {\n const view = this.transportWait, said = view ? JSON.stringify(view) : \"\";\n if (said === this.waitSaid) return;\n this.waitSaid = said;\n this.options.events?.onTransportWait?.(view);\n }\n\n get availableTransports(): PairedTransport[] {\n return [...(this.options.rtcAvailable !== false ? [\"webrtc/1\" as const] : []), ...this.endpoints.keys()];\n }\n private transportOffer(): PairedTransport[] { return transportOrder(this.availableTransports, this.preferred, this.fallback); }\n\n registerEndpoint(endpoint: NativeEndpoint): void {\n if (this.stopped || this.deliveryMode === \"dht\" || !this.options.params.profile) { void endpoint.close(); return; }\n this.endpoints.set(endpoint.transport, endpoint);\n endpoint.onConnection = ({ channel, binding }) => {\n traceLink(this.myPubKeyZ32, \"dialed-in\", { transport: binding.transport, held: !!this.channel, dialing: this.dialing });\n this.takeDialIn(channel, binding);\n };\n endpoint.onUnavailable = () => {\n if (this.endpoints.get(endpoint.transport) !== endpoint) return;\n this.endpoints.delete(endpoint.transport);\n this.advertiseTransports(); this.options.events?.onTransportsChanged?.();\n };\n // A way to dial it changed (the Desktop's Iroh homed on a relay a few seconds after it bound): the open session\n // hears it, and the owner republishes the capability record, which is all a contact with no session has.\n endpoint.onDescriptor = () => { this.advertiseTransports(); this.options.events?.onTransportsChanged?.(); };\n // A changed available offer needs a new authenticated session before native\n // transports are usable. Until then descriptors can be saved but not selected.\n this.advertiseTransports();\n if (this.resuming === endpoint.transport) void this.knock(endpoint.transport);\n else this.joinRace(endpoint.transport);\n // Something new to dial, now: a group link whose member's packet already said how to dial this transport, or a\n // chat that had no listener for it (every native slot was taken when the app started) and its contact is there.\n // A chat's attempts that failed keep counting: an endpoint registered again and again (a listener lost and started\n // anew, a slot taken back and given again) must not undo the wait between attempts, each of them a native dial.\n if (!this.channel && !this.dialing && this.dataLink.state === \"idle\" && this.canDial(endpoint.transport)\n && this.peerTransports?.includes(endpoint.transport)) {\n if (this.options.packetTransports) { this.autoConnectFailures = 0; this.lastAutoConnectAt = 0; }\n else this.dialOnNews();\n this.maybeAutoConnect(this.presence);\n }\n }\n\n /** A group link's `_tr` in its packet: what this side runs and how to dial it, once it runs a native endpoint there. */\n private publishPacketTransports(): void {\n if (!this.options.packetTransports) return;\n this.session.setTransports(this.endpoints.size ? encodePacketTransports(this.availableTransports, this.localDescriptors()) : null);\n }\n\n /** The member's packet said which transports its app runs on this group link, and how to dial them. */\n private peerPacketTransports(value: string): void {\n if (!this.options.packetTransports || this.stopped) return;\n const said = parsePacketTransports(value);\n if (!said) return;\n const transports = said.transports as PairedTransport[], descriptors = dialDescriptors(said.descriptors);\n traceLink(this.myPubKeyZ32, \"packet-transports\", { transports });\n // Its app has no WebRTC: an offer out to it is never answered, and would hold the data link for its whole attempt.\n if (!transports.includes(\"webrtc/1\") && !this.channel && this.dataLink.state === \"offering\") this.disconnect();\n this.options.events?.onPacketTransports?.(transports, descriptors);\n this.learnPeerTransports(transports, descriptors, true);\n }\n\n /** A connection the contact dialled on one of this side's native endpoints. */\n private takeDialIn(channel: FrameChannel, binding: NativeBinding): void {\n if (this.streamBlocked) {\n // Only the contact's mailbox blocks it, and a dial says the contact left DHT-only: the connection waits for the\n // read that says so. Closed, it would end the contact's whole attempt, and its next only after a backoff.\n if (this.peerDialsFromDht()) this.parkBlockedDial(channel, binding);\n else channel.close();\n return;\n }\n this.dialedIn.add(channel);\n const plan = this.switcher.pending;\n if (this.channel && plan?.choices.includes(binding.transport)) {\n void this.attachCandidate(channel, binding, plan).catch(() => {}); return;\n }\n if (this.channel && Date.now() < this.switchAllowedUntil) { this.switchAllowedUntil = 0; this.disconnect(); }\n if (this.stopped || this.leaving || !this.transportOffer().includes(binding.transport)) { channel.close(); return; }\n if (this.channel) { this.attachReplacement(channel, binding); return; }\n if (this.dialing && this.myPubKeyZ32 < this.options.params.peerPubKeyZ32 && this.options.pairing?.credentials.peerKey) { this.parkCrossed(channel, binding); return; }\n this.attach(channel, binding);\n }\n\n /** A dial that came in while the contact's mailbox still said DHT-only, kept for the read that clears it (`BLOCKED_DIAL_WAIT_MS`). */\n private blockedDial: { channel: FrameChannel; binding: NativeBinding; timer: ReturnType<typeof setTimeout> } | null = null;\n private parkBlockedDial(channel: FrameChannel, binding: NativeBinding): void {\n this.dropBlockedDial();\n traceLink(this.myPubKeyZ32, \"dial-waits-mailbox\", { transport: binding.transport });\n const timer = setTimeout(() => { if (this.blockedDial?.channel === channel) this.dropBlockedDial(); }, BLOCKED_DIAL_WAIT_MS);\n channel.onClose = () => { if (this.blockedDial?.channel === channel) { clearTimeout(timer); this.blockedDial = null; } };\n this.blockedDial = { channel, binding, timer };\n }\n private dropBlockedDial(): void {\n const parked = this.blockedDial;\n if (!parked) return;\n this.blockedDial = null; clearTimeout(parked.timer); parked.channel.close();\n }\n\n /**\n * Back after a restart on a native transport: dialled at once, whichever side's turn it is. The contact likely still\n * holds the old session and would notice only when its liveness gives up (a minute); a dial reaches it now and takes\n * over (`attachReplacement`). Once only, and a knock that does not connect counts as no failure: the contact may\n * simply be away. Two dials crossing are settled by key order (see `registerEndpoint`).\n */\n private async knock(transport: NativeTransport): Promise<void> {\n if (this.resuming === transport) this.resuming = undefined;\n const endpoint = this.endpoints.get(transport), descriptor = this.peerDescriptors[transport];\n if (!endpoint || !descriptor || this.channel || this.dialing || this.stopped || this.streamBlocked || this.keyStopped) return;\n if (!this.transportOffer().includes(transport)) return;\n const epoch = this.connectionEpoch;\n this.dialing = true;\n traceLink(this.myPubKeyZ32, \"knock\", { transport });\n try {\n const { channel, binding } = await endpoint.connect(descriptor);\n if (this.stopped || epoch !== this.connectionEpoch || this.channel) { channel.close(); return; }\n this.attach(channel, binding);\n } catch (error) {\n traceLink(this.myPubKeyZ32, \"knock-failed\", { transport, error: error instanceof Error ? error.message : String(error) });\n } finally {\n if (epoch === this.connectionEpoch) { this.dialing = false; this.settleCrossed(); }\n }\n if (!this.channel) this.maybeAutoConnect(this.presence);\n }\n\n canReleaseEndpoint(transport: PairedTransport): boolean {\n return this.activeBinding?.transport !== transport && this.candidate?.binding?.transport !== transport && !this.dialing && !this.switcher.pending && !this.transitionTarget;\n }\n async releaseEndpoint(transport: PairedTransport): Promise<void> {\n if (!this.canReleaseEndpoint(transport)) return;\n const endpoint = this.endpoints.get(transport);\n if (!endpoint) return;\n this.endpoints.delete(transport); await endpoint.close(); this.advertiseTransports();\n this.options.events?.onTransportsChanged?.();\n }\n\n /** When the chat was last in use on its session: the contact's last frame of use, or the session's start. */\n get lastActivityAt(): number { return Math.max(this.activityAt, this.channelSince); }\n\n /**\n * Whether the live session on this transport may end so another chat takes its listener (`yieldEndpoint`): open, and\n * unused, for `idleMs` at least (a session that just started has been in use for none of it); no call on, no file\n * moving, nothing being dialled or switched. `canReleaseEndpoint` never gives up a live session; this does, once idle.\n */\n canYieldEndpoint(transport: PairedTransport, idleMs: number): boolean {\n if (this.activeBinding?.transport !== transport || !this.isDataLinkOpen || this.paired?.state.status !== \"ready\") return false;\n if (this.candidate || this.dialing || this.switcher.pending || this.transitionTarget) return false;\n if (this.pairedCalls.on || this.pairedFiles?.busy) return false;\n return Date.now() - this.lastActivityAt >= idleMs;\n }\n\n /**\n * Ends the idle live session on this transport and lets its listener go, for a chat in use that found every one\n * taken. The contact hears a goodbye, as when an app quits, rather than finding out when its pings go unanswered; the\n * record then lists the transport undescribed, so it stops dialling here. The chat goes on over the DHT (or WebRTC),\n * and takes a listener again the same way once it is in use. False when the session may not end (see above).\n */\n async yieldEndpoint(transport: PairedTransport, idleMs: number): Promise<boolean> {\n if (!this.canYieldEndpoint(transport, idleMs)) return false;\n traceLink(this.myPubKeyZ32, \"native-yield\", { transport, idle: Date.now() - this.lastActivityAt });\n // Nothing dials meanwhile (the contact closing first would make this side look for it again), so the endpoint\n // is free to go once the goodbye is out.\n this.yielding = true;\n try {\n try { this.channel?.send(JSON.stringify({ t: \"paired-bye\" })); } catch { /* closing already: its close says as much */ }\n // Closed at once, a HyperDHT stream dropped the goodbye and the contact heard only the close (`peer-lost`).\n await new Promise(resolve => setTimeout(resolve, YIELD_BYE_MS));\n this.disconnect();\n await this.releaseEndpoint(transport);\n } finally { this.yielding = false; }\n return !this.endpoints.has(transport);\n }\n\n private advertiseTransports(): void {\n this.publishPacketTransports();\n if (this.paired?.state.status !== \"ready\" || !this.options.params.profile) return;\n if (this.paired.peerTransportSwitchSupport) { this.switcher.changed(false); return; }\n const descriptors: TransportDescriptors = {};\n for (const endpoint of this.endpoints.values()) descriptors[endpoint.transport] = endpoint.descriptor;\n try { this.channel?.send(JSON.stringify({ t: \"paired-adapters\", descriptors })); } catch { /* Closing channel; exchange again on reconnect. */ }\n }\n\n /**\n * `automatic`: this side stops choosing. Its policy still says `preferred` (the app's default) for the order\n * it dials in, but its switch intent drops to none, so the contact's explicit choice wins on the open session\n * and, without one, the current transport is kept. Never adds an unavailable adapter either way.\n */\n /**\n * `automatic`: back to the app's rule (no choice of this side's). `choice` false: the same transport with another\n * fallback (the Fallback switch), a policy change that raises no switch intent. A choice of the transport this side\n * already chose raises none either while that choice stands (see `TransportSwitch.chose`).\n */\n async setTransportPreference(preferred: PairedTransport, fallback: boolean, automatic = false, choice = true): Promise<void> {\n if (!this.options.params.profile || !this.availableTransports.includes(preferred)) throw new Error(\"Transport unavailable in this runtime\");\n const again = this.preferred === preferred;\n this.preferred = preferred; this.fallback = fallback; this.automatic = automatic;\n // Someone chose: a wait from before starts over (its failures, its retry, the row a failure gets).\n if (automatic || choice) this.clearWait();\n if (this.paired?.state.status === \"ready\") {\n if (!this.paired.peerTransportSwitchSupport) {\n if (automatic) return;\n this.transitionError = \"Your contact needs an updated app to negotiate a transport change.\";\n this.emitPairingState(); return;\n }\n if (automatic) this.switcher.changed(\"automatic\");\n else if (choice) this.switcher.chose(again);\n else this.switcher.changed(false);\n this.emitPairingState(); return;\n }\n this.notifyWait();\n // A choice made with no session open (WISP 100, \"A choice made while not live\") reaches the contact in the\n // capability record, goes first in the dial, and is the choice the next session begins with (a switch intent), so\n // a session that opens elsewhere moves there. Going automatic clears it, and an older one.\n if (automatic) this.switcher.changed(\"automatic\");\n else if (choice) this.switcher.choseApart();\n // Discovery/incoming connections use this offer when a contact arrives. This also applies to a saved contact that\n // is currently offline.\n if (!this.isDataLinkOpen) {\n // Invalidate an older, incomplete attempt. A late native dial must not\n // install a session using preferences that have since been changed.\n if (this.channel || this.dialing || this.dataLink.state !== \"idle\") this.disconnect();\n // Someone chose: the dialling side tries at once, the choice first.\n if (automatic || choice) this.dialNow();\n return;\n }\n }\n\n private async dial(): Promise<void> {\n if (this.streamBlocked || this.dialing || this.channel) return;\n if (!this.options.params.profile) { await this.dataLink.connect(); return; }\n this.dialing = true;\n const epoch = this.connectionEpoch;\n this.attempt = { side: \"dialled\", failed: [] };\n this.options.events?.onPairingState?.({ status: \"connecting\" });\n try {\n const local = this.transportOffer();\n const relayed = relayedTransports(this.localDescriptors(), this.peerDescriptors);\n // Cached availability is a routing hint, not permission: the fresh signed\n // PairedSession offer enforces the peer's current (possibly offline-edited) policy.\n const remembered = this.peerTransports && rankTransports(local, this.peerFallback ? this.peerTransports : this.peerTransports.slice(0, 1), relayed);\n const ranked = remembered?.length ? remembered : this.peerTransports ? rankTransports(local, this.peerTransports, relayed) : local.filter(t => t === \"webrtc/1\");\n // A standing explicit choice goes first, relayed or not: the session starts where the agreement would move it\n // anyway. With no session behind it, the choice this side or the contact made meanwhile (WISP 100).\n const chosen = this.switcher.chosenTarget ?? this.choiceApart?.transport ?? this.resuming;\n if (chosen && chosen === this.resuming && ranked.includes(chosen)) this.resuming = undefined;\n const choices = chosen && ranked.includes(chosen) ? [chosen, ...ranked.filter(t => t !== chosen)] : ranked;\n if (!choices.length) {\n // A group link: the member's app may be starting a native endpoint for this one right now, and says how to dial\n // it in its packet (`_tr`), which is looked for fast meanwhile rather than at the background pace. On the first\n // few attempts only: a member whose app never says one (an older app, no transport in common) is not read fast forever.\n if (this.options.packetTransports && this.autoConnectFailures <= PACKET_TRANSPORTS_FAST_ATTEMPTS) this.session.expectPeer();\n throw new Error(\"No transport both apps allow is available yet\");\n }\n const fallback = this.fallback && this.peerFallback;\n let lastError: unknown;\n // A transport that keeps failing is tried last for an hour, not first on every attempt.\n const now = Date.now(), demoted = (t: PairedTransport) => (this.demotedUntil.get(t) ?? 0) > now;\n const ordered = choices.length > 1 ? [...choices.filter(t => !demoted(t)), ...choices.filter(demoted)] : choices;\n // Native transports dialled before WebRTC in this attempt that did not connect (a chosen one goes first).\n const failedFirst: PairedTransport[] = [];\n for (const [index, transport] of ordered.entries()) {\n if (index > 0 && !fallback) break;\n if (transport === \"webrtc/1\") {\n // WebRTC settles later (ICE can fail minutes from now): what ranks after it is where a failed attempt\n // goes, typically a relayed Iroh behind a symmetric NAT (WISP 100, \"Relayed\"), before the DHT floor.\n // A native tried first that did not connect races the offer too, once more: after a restart of both apps the\n // contact's endpoint may still be starting (HyperDHT answers PEER_NOT_FOUND at once), and the chosen\n // transport would otherwise wait for the offer's 90 s timeout.\n const rest = [...failedFirst, ...ordered.slice(index + 1)];\n this.afterRtc = fallback && rest.length ? { epoch, rest } : undefined;\n const offeredAt = Date.now();\n this.offered = fallback ? { epoch, at: offeredAt } : undefined;\n await this.dataLink.connect();\n if (this.dataLink.state !== \"idle\" || epoch !== this.connectionEpoch) { this.scheduleRace(epoch, offeredAt); return; }\n // WebRTC gave up while starting (no candidate at all, no connection to be made): tried last for a while.\n this.rtcUnstarted();\n const next = this.afterRtc;\n if (!next) return;\n this.afterRtc = undefined;\n // A transport whose endpoint came up meanwhile joined what follows the offer (`joinRace`): it is dialled\n // too, rather than left to the next attempt, 20 s away or more.\n for (const joined of next.rest) if (!ordered.includes(joined)) ordered.push(joined);\n continue;\n }\n const result = await this.dialNative(transport, epoch);\n if (result === true) return;\n lastError = result;\n failedFirst.push(transport);\n }\n throw lastError ?? new Error(\"No permitted transport could connect\");\n } catch (error) {\n if (epoch === this.connectionEpoch) this.attemptEnded(error instanceof Error ? error.message : String(error));\n throw error;\n } finally {\n if (epoch === this.connectionEpoch) {\n this.dialing = false;\n // A WebRTC attempt that already ended, with nothing ranked after it to go on to.\n if (!this.channel && !this.afterRtc && this.dataLink.state === \"idle\") this.attemptEnded();\n this.settleCrossed();\n }\n }\n }\n\n private localDescriptors(): TransportDescriptors {\n return Object.fromEntries([...this.endpoints].map(([transport, endpoint]) => [transport, endpoint.descriptor]));\n }\n\n /** True once attached (or when a newer attempt took over); the error otherwise. */\n private async dialNative(transport: PairedTransport, epoch: number): Promise<true | Error> {\n const endpoint = this.endpoints.get(transport as NativeEndpoint[\"transport\"]);\n const descriptor = this.peerDescriptors[transport as NativeEndpoint[\"transport\"]];\n if (!endpoint || !descriptor) return new Error(\"Peer native address unavailable; reconnect WebRTC once to exchange endpoints\");\n const started = Date.now();\n try {\n const { channel, binding } = await endpoint.connect(descriptor);\n this.nativeFailures.delete(transport); this.demotedUntil.delete(transport);\n if (this.stopped || epoch !== this.connectionEpoch || this.channel) { channel.close(); return true; }\n this.attach(channel, binding); return true;\n } catch (error) {\n traceLink(this.myPubKeyZ32, \"dial-failed\", { transport, error: error instanceof Error ? error.message : String(error), ms: Date.now() - started });\n if (epoch !== this.connectionEpoch) return true;\n this.attemptFailed(transport, error instanceof Error ? error.message : String(error));\n // Three failures in a row demote it for an hour (WISP 100).\n const failures = (this.nativeFailures.get(transport) ?? 0) + 1;\n if (failures >= DEMOTE_AFTER_FAILURES) { this.nativeFailures.delete(transport); this.demotedUntil.set(transport, Date.now() + DEMOTE_MS); traceLink(this.myPubKeyZ32, \"demote\", { transport }); }\n else this.nativeFailures.set(transport, failures);\n return error instanceof Error ? error : new Error(String(error));\n }\n }\n\n /** A WebRTC attempt this side dialled ended without opening: the next ranked transports, in order. */\n private async dialAfterRtc(next: { epoch: number; rest: PairedTransport[] }): Promise<void> {\n this.dialing = true;\n try {\n let lastError: Error | undefined;\n for (const transport of next.rest) {\n if (transport === \"webrtc/1\") continue;\n const result = await this.dialNative(transport, next.epoch);\n if (result === true) return;\n lastError = result;\n }\n if (next.epoch === this.connectionEpoch) {\n this.attemptEnded();\n this.rejectWaiters(new GhostlyHttpError(\"unreachable\", lastError ? `Could not connect to the peer: ${lastError.message}` : \"Could not connect to the peer\"));\n }\n } finally { if (next.epoch === this.connectionEpoch) { this.dialing = false; this.settleCrossed(); } }\n }\n\n /**\n * WebRTC went first and its offer is out: if no answer came within `RACE_DIRECT_MS` (a direct transport ranked after\n * it) or `RACE_RELAYED_MS` (a relayed one), those are dialled too, the offer standing meanwhile.\n */\n private scheduleRace(epoch: number, offeredAt: number): void {\n this.clearRace();\n const next = this.afterRtc;\n if (!next || next.epoch !== epoch || epoch !== this.connectionEpoch || !this.offerOut) return;\n const relayed = this.relayedTransports, now = Date.now();\n // Answered and still not connected: everything left is due `RACE_ANSWERED_MS` after the answer, if not sooner.\n const answered = this.answeredAt ? this.answeredAt + RACE_ANSWERED_MS : Infinity;\n const at = next.rest.filter(t => t !== \"webrtc/1\").map(t => Math.max(Math.min(offeredAt + (relayed.includes(t) ? RACE_RELAYED_MS : RACE_DIRECT_MS), answered),\n this.canDial(t) ? 0 : now + RACE_RETRY_MS));\n if (!at.length) return;\n this.raceTimer = setTimeout(() => { this.raceTimer = null; void this.race(epoch, offeredAt); }, Math.max(0, Math.min(...at) - now));\n }\n /**\n * A native endpoint that started while this side's offer is out unanswered: its transport was not up when the dial\n * ranked what to try, and joins the race (and what the offer ending goes on to) if both sides allow it.\n */\n private joinRace(transport: NativeTransport): void {\n const offered = this.offered;\n if (!offered || offered.epoch !== this.connectionEpoch || !this.offerOut || this.channel) return;\n if (!this.peerFallback || !this.peerTransports?.includes(transport) || !this.transportOffer().includes(transport)) return;\n const rest = this.afterRtc?.epoch === offered.epoch ? this.afterRtc.rest : [];\n if (rest.includes(transport)) return;\n this.afterRtc = { epoch: offered.epoch, rest: [...rest, transport] };\n traceLink(this.myPubKeyZ32, \"race-join\", { transport });\n if (!this.racing) this.scheduleRace(offered.epoch, offered.at);\n }\n /** WebRTC could not start an attempt here: it goes last in the dial order for `RTC_UNSTARTED_MS`, while another transport remains. */\n private rtcUnstarted(): void {\n traceLink(this.myPubKeyZ32, \"rtc-unstarted\", {});\n this.demotedUntil.set(\"webrtc/1\", Date.now() + RTC_UNSTARTED_MS);\n }\n /**\n * Whether this side dials although its key says it waits for the contact's offer: WebRTC cannot start here for now\n * (`rtcUnstarted`), so an offer of the contact's finds no answer, and this side has a native transport to dial, with\n * fallback allowed on both sides.\n */\n private dialsPastRtc(): boolean {\n return (this.demotedUntil.get(\"webrtc/1\") ?? 0) > Date.now() && this.fallback && this.peerFallback\n && TRANSPORTS.some(t => this.canDial(t) && !!this.peerTransports?.includes(t) && this.transportOffer().includes(t));\n }\n /** Whether a native dial of `transport` can go now: this side's endpoint is up and the contact's descriptor known. */\n private canDial(transport: PairedTransport): boolean {\n return transport !== \"webrtc/1\" && this.endpoints.has(transport) && !!this.peerDescriptors[transport];\n }\n private clearRace(): void {\n if (this.raceTimer) clearTimeout(this.raceTimer);\n this.raceTimer = null;\n }\n\n /**\n * The offer is still unanswered: the transports ranked after WebRTC that are due are dialled now, each once in this\n * attempt (the offer timing out goes on to those left, not to these again). A native session that goes live ends the\n * offer (`attach`); the offer answered first, the native dial is closed.\n */\n private async race(epoch: number, offeredAt: number): Promise<void> {\n const next = this.afterRtc;\n if (!next || next.epoch !== epoch || epoch !== this.connectionEpoch || this.channel || this.dialing || this.stopped || this.leaving\n || this.streamBlocked || this.keyStopped || !this.offerOut) return;\n const relayed = this.relayedTransports;\n const late = Date.now() - offeredAt >= RACE_RELAYED_MS || (this.answeredAt > 0 && Date.now() - this.answeredAt >= RACE_ANSWERED_MS);\n // One that cannot be dialled yet is not given up on: it stays for later in the race, or for when the offer ends.\n const due = next.rest.filter(t => this.canDial(t) && (late || !relayed.includes(t)));\n const left = next.rest.filter(t => t !== \"webrtc/1\" && !due.includes(t));\n if (!due.length) { this.scheduleRace(epoch, offeredAt); return; }\n this.afterRtc = left.length ? { epoch, rest: left } : undefined;\n traceLink(this.myPubKeyZ32, \"race\", { transports: due, afterMs: Date.now() - offeredAt });\n this.dialing = this.racing = true;\n let live = false;\n try {\n for (const transport of due) if ((live = await this.dialNative(transport, epoch) === true)) break;\n } finally {\n this.racing = false;\n if (epoch === this.connectionEpoch) { this.dialing = false; this.settleCrossed(); }\n }\n if (live || epoch !== this.connectionEpoch || this.channel || this.stopped) return;\n // (Read again: the dials took time.)\n if ((this.dataLink.state as DataLinkState) !== \"idle\") { this.scheduleRace(epoch, offeredAt); return; }\n // The offer ended while these were dialled: on to what is left, or the attempt is over.\n const rest = this.afterRtc;\n this.afterRtc = undefined;\n if (rest && !this.streamBlocked) { void this.dialAfterRtc(rest); return; }\n this.attemptEnded();\n this.rejectWaiters(new GhostlyHttpError(\"unreachable\", \"Could not connect to the peer\"));\n }\n\n /** A transport of the attempt under way did not connect. */\n private attemptFailed(transport: PairedTransport, error: string): void {\n if (this.isDataLinkOpen) return;\n const attempt = this.attempt ??= { side: \"dialled\", failed: [] };\n attempt.failed = [...attempt.failed.filter(f => f.transport !== transport), { transport, error }];\n }\n\n /** The attempt under way ended without going live: kept as the last one, and told. `reason`: why, when nothing was tried. */\n private attemptEnded(reason?: string): void {\n const attempt = this.attempt;\n this.attempt = null;\n if (!attempt || this.isDataLinkOpen || this.stopped || (!attempt.failed.length && !reason)) return;\n this.lastAttempt = { at: Date.now(), side: attempt.side, failed: attempt.failed, ...(!attempt.failed.length && reason ? { reason } : {}),\n ...(attempt.side === \"dialled\" ? { retryAt: Math.max(Date.now(), this.lastAutoConnectAt + this.dialWait()) } : {}) };\n traceLink(this.myPubKeyZ32, \"attempt-ended\", { side: attempt.side, failed: attempt.failed.map(f => f.transport), reason: this.lastAttempt.reason });\n this.options.events?.onLiveAttempt?.(this.lastAttempt);\n }\n\n /** The last attempt to go live that did not (WISP 100, \"Why a chat is not live\"); none while live. */\n get liveAttempt(): LiveAttempt | undefined { return this.isDataLinkOpen ? undefined : this.lastAttempt; }\n /**\n * Which side dials this chat to go live: the lower rendezvous key, or the joiner of a first pairing (WISP 100). The\n * other side answers, so what it can tell of a failed attempt is only what reached it.\n */\n get dialer(): \"you\" | \"contact\" {\n const pairing = !!this.tracker && !this.tracker.done && this.tracker.progress.stage !== \"on-dht\";\n const role = pairing ? this.options.pairingProgress?.role : undefined;\n return role === \"joiner\" || (role !== \"inviter\" && this.myPubKeyZ32 < this.options.params.peerPubKeyZ32) ? \"you\" : \"contact\";\n }\n\n /**\n * Chat goes over the data link when it is up, through Pkarr otherwise. A `preview` (WISP 401 § Link previews) goes\n * only with a paired message on the live session, and only while the frame stays within what a session takes;\n * the DHT has no room for one, and the text goes without it. A `reply` (WISP 401 § Replies) goes whole on the\n * session; on the DHT only its id does, which the contact looks up in its own history. `forwarded`: the hop count\n * of a forwarded message (WISP 401 § Forwards), on both paths (the DHT drops it when the packet has no room). `card`:\n * a status card (WISP 4xx · Status Cards), on the live session only; on the DHT the text, its fallback, goes alone.\n */\n async sendMessage(text: string, timestamp = Date.now(), stableId?: string, preview?: LinkPreview, reply?: WireReply, forwarded?: number, card?: StatusCard): Promise<string | null> {\n const trimmed = text.trim();\n if (!trimmed) return null;\n if (this.textDelivery === \"dht\" && this.dht) return forwarded ? this.dht.send(trimmed, timestamp, stableId ?? toBase64Url(randomBytes(16)), reply?.i, undefined, forwarded)\n : this.dht.send(trimmed, timestamp, stableId ?? toBase64Url(randomBytes(16)), reply?.i);\n if (this.options.params.profile && !this.isDataLinkOpen) return \"Confirm the peer and connect before sending. This chat never falls back to DHT messages.\";\n if (this.options.params.profile && this.channel) {\n if (utf8Encode(trimmed).length > LIMITS.maxChatMessageBytes) return \"Message exceeds 16 KiB\";\n if (this.pairedPending.size >= 128) this.pairedPending.delete(this.pairedPending.keys().next().value!);\n const id = stableId ?? toBase64Url(randomBytes(16));\n if (!/^[A-Za-z0-9_-]{22}$/.test(id)) return \"Invalid message ID\";\n this.pairedPending.set(id, timestamp);\n try { this.channel.send(pairedMessageFrame(id, timestamp, trimmed, preview, reply, forwarded, card)); return null; }\n catch { this.pairedPending.delete(id); return \"The connection closed before sending. Reconnect and retry.\"; }\n }\n if (this.channel && trimmed.length <= LIMITS.maxChatMessageBytes / 4) {\n try {\n this.channel.send(encodeControl({ t: \"m\", ts: timestamp, m: trimmed }));\n return null;\n } catch {\n // fall through to Pkarr\n }\n }\n if (this.options.params.profile) return \"Message could not be sent over the negotiated data link.\";\n return this.session.sendMessage(trimmed, timestamp);\n }\n\n /** Publishes a `_call` signal; a connected peer also gets it right away over the data link. */\n async setCallSignal(signal: string | null): Promise<void> {\n if (this.options.params.profile) {\n // Kept until cleared, so what the session could not carry goes on the next one while still fresh.\n const frame = this.pairedCalls.set(signal);\n if (!frame) return;\n if (!this.supportsCalls || !this.channel) throw new Error(this.callsUnavailable ?? \"Calls need a live connection\");\n this.channel.send(JSON.stringify(frame));\n return;\n }\n if (signal && this.channel) {\n try {\n this.channel.send(encodeControl({ t: \"call\", s: signal }));\n } catch {\n // Pkarr still carries it\n }\n }\n await this.session.setCallSignal(signal);\n }\n\n /** Issues an HTTP request to a service the peer advertises. */\n async request(serviceId: string, request: ClientRequest): Promise<ClientResponse> {\n if (this.options.params.profile) {\n await this.connect();\n if (!this.supportsServices) throw new GhostlyHttpError(\"unavailable\", this.isDataLinkOpen\n ? \"Your contact's app cannot share web apps in this chat, or needs an updated Ghostly\" : \"Shared apps need a live connection\");\n if (!this.pairedHttp) throw new GhostlyHttpError(\"closed\", \"Data link is closed\");\n return this.pairedHttp.client.request(serviceId, request);\n }\n this.requireLegacyCapabilities();\n await this.connect();\n if (!this.httpClient) throw new GhostlyHttpError(\"closed\", \"Data link is closed\");\n return this.httpClient.request(serviceId, request);\n }\n\n /** Payments only travel over the data link: tokens and invoices do not fit in Pkarr, and should not sit there. */\n async sendPaymentRequest(request: PaymentRequest): Promise<void> {\n await this.requirePaymentSupport();\n if (!this.channel) throw new Error(\"Data link is closed\");\n this.channel.send(\n encodeControl({\n t: \"pay-req\",\n id: request.id,\n ts: request.timestamp,\n v: request.amount.value,\n u: request.amount.asset,\n memo: request.memo,\n e: request.endpoints,\n a: request.ask,\n n: request.network,\n }),\n );\n }\n\n /** Asks the contact for a way to pay it (an address, an invoice): its app answers with a request. */\n async sendPaymentAsk(ask: PaymentAsk): Promise<void> {\n await this.requirePaymentSupport();\n if (!this.options.params.profile) throw new Error(\"Paying without a request needs a paired chat\");\n if (!this.channel) throw new Error(\"Data link is closed\");\n this.channel.send(encodeControl({ t: \"pay-ask\", id: ask.id, ts: ask.timestamp, v: ask.amount.value, u: ask.amount.asset, m: ask.method, memo: ask.memo, n: ask.network }));\n }\n\n async sendPayment(payment: Payment): Promise<void> {\n await this.requirePaymentSupport();\n if (!this.channel) throw new GhostlyHttpError(\"closed\", \"Data link is closed\");\n this.channel.send(\n encodeControl({\n t: \"pay\",\n id: payment.id,\n ts: payment.timestamp,\n rid: payment.requestId,\n v: payment.amount.value,\n u: payment.amount.asset,\n memo: payment.memo,\n e: payment.endpoint,\n }),\n );\n }\n\n sendPaymentResult(result: PaymentResult): void {\n if (this.options.params.profile && !this.supportsPayments) return;\n try {\n this.channel?.send(encodeControl({ t: \"pay-res\", id: result.id, ok: result.ok, v: result.credited, err: result.error, ...(result.closed ? { c: true as const } : {}) }));\n } catch {\n // the peer learns on reconnect that nothing came back\n }\n }\n\n /** Sends a file over the data link, opening it first if needed. Files never travel through Pkarr. */\n async sendFile(file: FileInfo, source: AsyncIterable<Uint8Array>): Promise<void> {\n await this.connect();\n if (this.options.params.profile) {\n if (!this.supportsFiles || !this.pairedFiles) throw new Error(\"This contact does not support files. Both peers need an updated Ghostly.\");\n return this.pairedFiles.send(file, source);\n }\n if (!this.files) throw new GhostlyHttpError(\"closed\", \"Data link is closed\");\n await this.files.send(file, source);\n }\n\n /** Call after the set of shared services changed. */\n async refreshServices(): Promise<void> {\n if (this.options.params.profile) { this.sendPairedServices(); return; }\n if (this.channel) {\n try {\n this.channel.send(encodeControl({ t: \"svc\", svc: servicesToWire(this.options.getServices() ?? []) }));\n } catch {\n // channel closing\n }\n }\n await this.session.refreshAdvertisement();\n }\n\n /**\n * Only the lower key offers, so two peers coming online together do not collide. A first pairing is\n * the exception: the joiner offers, whatever its key, in the very packet that says it is here (one hop\n * less than presence, then the inviter's offer), and the inviter waits for that offer. An inviter that\n * sees the joiner but no offer for `INVITER_DIAL_GRACE_MS` (an app from before this rule) offers itself;\n * the lower-key rule settles any collision, on both sides.\n */\n private maybeAutoConnect(presence: PeerPresence): void {\n // Stopping (its loops end after an await): a poll finishing meanwhile must not dial again.\n if (this.stopped || this.leaving || this.yielding || this.streamBlocked || this.keyStopped || !this.options.autoConnect || !presence.online || this.channel || this.dialing || this.dataLink.state !== \"idle\") return;\n // On the DHT the chat is usable: layer 1 is retried at the background pace (WISP 100), not the pairing's.\n // A pin over the DHT alone changes nothing here: the joiner still knocks and the inviter still answers.\n // Back after a restart on a native transport whose endpoint is still starting: it is knocked on once it has, and a\n // WebRTC offer meanwhile would hold the data link for as long as the contact takes to read it.\n if (this.resuming && this.resuming !== \"webrtc/1\" && !this.endpoints.has(this.resuming) && Date.now() < this.resumeWaitUntil) return;\n // The contact said it was going: its old packet says nothing about whether it is back. Its next one does.\n if (this.departed) {\n if (presence.lastPacketAt === this.departed.packet && Date.now() < this.departed.until) return;\n this.departed = null;\n }\n const pairing = !!this.tracker && !this.tracker.done && this.tracker.progress.stage !== \"on-dht\";\n const role = pairing ? this.options.pairingProgress?.role : undefined;\n if (role === \"inviter\") {\n this.peerSeenAt ||= Date.now();\n if (Date.now() - this.peerSeenAt < INVITER_DIAL_GRACE_MS) { this.session.expectPeer(); return; }\n } else if (role !== \"joiner\" && this.myPubKeyZ32 > this.options.params.peerPubKeyZ32 && !this.resuming && !this.dialsPastRtc()) {\n // Back after a restart (`resume`), this side dials once whatever its key: the contact may still hold the old\n // session, and would not dial. A WebRTC offer from it reaches that session as \"the contact lost the connection\"\n // (`DataLink.handleSignal`), and two offers crossing are settled by key there.\n // The other side dials as soon as it sees me. A packet of its that is new to me and fresh says it just\n // (re)appeared, so its offer is a poll away; an old one (a contact online for a while, as when this app\n // starts) says nothing is coming now, and looking fast for it would only spend the relays' budget.\n const fresh = Date.now() - presenceSeenAt(presence) < EXPECT_PEER_MS;\n if (fresh && presence.lastPacketAt !== this.offerAwaitedFor) { this.offerAwaitedFor = presence.lastPacketAt; this.session.expectPeer(); }\n return;\n }\n const wait = this.dialWait();\n if (Date.now() - this.lastAutoConnectAt < wait) { traceLink(this.myPubKeyZ32, \"dial-backoff\", { failures: this.autoConnectFailures, left: wait - (Date.now() - this.lastAutoConnectAt) }); return; }\n traceLink(this.myPubKeyZ32, \"dial\", { failures: this.autoConnectFailures });\n this.lastAutoConnectAt = Date.now();\n this.autoConnectFailures++;\n void this.dial().catch(error => {\n this.tracker?.failed(\"transport\", true);\n this.dialFailed(error instanceof Error ? error.message : String(error));\n });\n }\n\n /**\n * The contact answered this side's offer again, and the data link gave the attempt up (`DataLink.onAnswerReplaced`):\n * the contact is there, so this side dials again now. Its packet with that answer often has no room for its presence,\n * and waiting for a packet that says it is online left the edge to the next look at the background pace (60 s, CLI\n * bug hunt 2026-09-30).\n */\n private redial(): void {\n if (this.stopped || this.leaving || this.streamBlocked || this.keyStopped || !this.options.autoConnect || this.channel || this.dialing || this.dataLink.state !== \"idle\") return;\n traceLink(this.myPubKeyZ32, \"dial\", { failures: this.autoConnectFailures, again: true });\n this.lastAutoConnectAt = Date.now();\n this.autoConnectFailures++;\n void this.dial().catch(error => {\n this.tracker?.failed(\"transport\", true);\n this.dialFailed(error instanceof Error ? error.message : String(error));\n });\n }\n\n /** How long after the last automatic dial the next one may go. A first pairing tries again sooner: the contact just read the invite and is waiting. */\n private dialWait(): number {\n const pairing = !!this.tracker && !this.tracker.done && this.tracker.progress.stage !== \"on-dht\";\n return pairing ? Math.min(PAIRING_RETRY_MS * 2 ** this.autoConnectFailures, PAIRING_MAX_RETRY_MS)\n : Math.min(AUTO_CONNECT_RETRY_MS * 2 ** this.autoConnectFailures, AUTO_CONNECT_MAX_RETRY_MS);\n }\n\n /** The name this side shows, told to a paired peer directly. */\n setNick(nick: string | undefined): void {\n this.options.nick = nick || undefined;\n this.session.setNick(nick);\n this.sendPairedNick();\n }\n\n /** The picture this side shows, told to a paired peer directly. Legacy chats have no room for it. */\n setAvatar(avatar: string | undefined): void {\n this.options.avatar = avatar || undefined;\n this.sendPairedAvatar();\n }\n\n /** Older apps drop this frame (it carries no id). An empty `a` says the picture was removed. */\n private sendPairedAvatar(): void {\n if (!this.options.params.profile || !this.channel || !this.isDataLinkOpen) return;\n try { this.channel.send(JSON.stringify({ t: \"paired-avatar\", a: this.options.avatar ?? \"\" })); } catch { /* sent again on the next session */ }\n }\n\n /** An empty `n` says there is no name to show (none set, removed, or not shared). */\n private sendPairedNick(): void {\n if (!this.options.params.profile || !this.channel || !this.isDataLinkOpen) return;\n try { this.channel.send(JSON.stringify({ t: \"paired-nick\", n: this.options.nick ?? \"\" })); } catch { /* sent again on the next session */ }\n }\n\n private mergePresence(presence: PeerPresence): PeerPresence {\n if (!this.channel) return presence;\n return { ...presence, online: true, nick: this.peerNickOverride ?? presence.nick,\n services: this.peerServicesOverride ?? presence.services };\n }\n\n get peerProofAdapters(): ProofAdapter[] { return this.options.events?.onPeerProof && this.isDataLinkOpen ? this.paired?.peerProofAdapters ?? [] : []; }\n get peerProofSupport(): boolean { return !!this.options.events?.onPeerProof && !!this.paired?.peerProofSupport && this.isDataLinkOpen; }\n\n async peerProofScope(): Promise<ProofScope> {\n const credentials = this.options.pairing?.credentials;\n if (!credentials?.peerKey || !this.paired || !this.peerProofSupport) throw new Error(\"Connect to a confirmed peer supporting optional proofs first\");\n const paired = this.paired;\n const context = await proofHash(JSON.stringify([\"ghostly-conversation\", 1, [this.myPubKeyZ32, this.options.params.peerPubKeyZ32].sort()]));\n if (this.paired !== paired || !this.peerProofSupport) throw new Error(\"Connection changed\");\n return { subject: identityFromSeedB64(credentials.seedB64).pubKeyZ32, audience: credentials.peerKey, context, session: paired.proofSession };\n }\n\n get proofSession(): string | undefined { return this.peerProofSupport ? this.paired?.proofSession : undefined; }\n\n /** Identity proofs can be exchanged now: both offers carry `identity-proof/1` and the channel is open. */\n get identitySupport(): boolean { return !!this.options.events?.onIdentityProof && !!this.paired?.identitySupport && this.isDataLinkOpen; }\n\n /** This side's view of the authenticated channel, for identity presentations. */\n identityScope(): IdentityScope {\n const credentials = this.options.pairing?.credentials;\n if (!credentials?.peerKey || !this.paired || !this.identitySupport) throw new Error(\"Connect to this contact first\");\n const conversation = [this.myPubKeyZ32, this.options.params.peerPubKeyZ32].sort();\n const context = Array.from(sha256(utf8Encode(JSON.stringify([\"ghostly-conversation\", 1, conversation]))), b => b.toString(16).padStart(2, \"0\")).join(\"\");\n return { subject: identityFromSeedB64(credentials.seedB64).pubKeyZ32, audience: credentials.peerKey, context, session: this.paired.proofSession };\n }\n\n sendIdentityProof(frame: object): void {\n if (!this.identitySupport || !this.channel) throw new Error(\"Identity proofs are unavailable on this connection\");\n const data = JSON.stringify(frame);\n if (data.length > IDENTITY_MAX_FRAME) throw new Error(\"Identity proof too large\");\n this.channel.send(data);\n }\n\n sendPeerProof(frame: object): void {\n if (!this.peerProofSupport || !this.channel) throw new Error(\"Peer proof channel unavailable\");\n const data = JSON.stringify(frame);\n if (data.length > 8192) throw new Error(\"Proof too large\");\n this.channel.send(data);\n }\n\n async confirmPair(code: string): Promise<void> {\n if (this.streamBlocked && this.dht?.comparisonCode) {\n const credentials = this.options.pairing!.credentials;\n if (code !== this.dht.comparisonCode) throw new Error(\"Compare the current code again.\");\n await this.options.pairing!.verifyPeer?.(credentials.peerKey!);\n credentials.verifiedPeerKey = credentials.peerKey; this.emitDeliveryState(); return;\n }\n if (!this.paired) throw new Error(\"No peer is waiting for confirmation\");\n await this.paired.confirm(code);\n }\n\n get supportsFiles(): boolean { return this.options.params.profile ? this.isDataLinkOpen && !!this.paired?.supports(\"files/2\") : true; }\n /** This device allows the way of paying in this chat. */\n paymentEnabled(method: PaymentMethodName): boolean { return this.options.paymentMethods?.[method] !== false; }\n /** Both sides allow it. A chat from before pairing negotiates nothing: Cashu and Lightning as this device allows. */\n allowsPayment(method: PaymentMethodName): boolean {\n if (!this.paymentEnabled(method)) return false;\n if (!this.options.params.profile) return method === \"cashu\" || method === \"lightning\";\n if (!this.isDataLinkOpen || this.paired?.state.status !== \"ready\") return false;\n // The contact's latest word in this session wins over its handshake offer.\n return this.peerPaymentMethods ? this.peerPaymentMethods.has(method) : this.paired.peerAllowsPayment(method);\n }\n get supportsUsdtPayments(): boolean { return this.allowsPayment(\"usdt\"); }\n get supportsArkPayments(): boolean { return this.allowsPayment(\"arkade\"); }\n get supportsBarkPayments(): boolean { return this.allowsPayment(\"bark\"); }\n /** On-chain: only ever through the contact's `paired-payments` list (see PaymentMethodName). */\n get supportsBitcoinPayments(): boolean { return this.allowsPayment(\"bitcoin\"); }\n /** Fedimint: only ever through the contact's `paired-payments` list, like on-chain. */\n get supportsFedimintPayments(): boolean { return this.allowsPayment(\"fedimint\"); }\n /** Spark: like on-chain, only ever through the contact's `paired-payments` list. */\n get supportsSparkPayments(): boolean { return this.allowsPayment(\"spark\"); }\n /** Payment messages flow at all: some way of paying is allowed by both sides. */\n get supportsPayments(): boolean { return PAYMENT_METHODS.some(m => this.allowsPayment(m)); }\n /** The contact accepts held items: its latest word on this session, else its handshake offer. */\n get peerAllowsHold(): boolean {\n if (!this.options.params.profile || !this.paired || this.paired.state.status !== \"ready\") return false;\n return this.peerHoldOverride ?? this.paired.peerHoldSupport;\n }\n /** Both sides offer `hold/1` on this session. */\n get supportsHold(): boolean { return !!this.options.holdSupport && this.isDataLinkOpen && this.peerAllowsHold; }\n /** The contact's own choice about one way of paying, as it last said on this session or offered in the handshake. */\n peerAllowsPayment(method: PaymentMethodName): boolean {\n if (!this.options.params.profile || !this.paired || this.paired.state.status !== \"ready\") return false;\n return this.peerPaymentMethods ? this.peerPaymentMethods.has(method) : this.paired.peerAllowsPayment(method);\n }\n /**\n * The contact's choice about a way of paying in this chat, whether or not it has a wallet for it. Its session list\n * leaves out a way it has no wallet of (its networks name none): that one is as its handshake offer said. What is\n * remembered for a later request, which goes when the chat is live: a wallet made meanwhile then counts.\n */\n peerChoosesPayment(method: PaymentMethodName): boolean {\n if (!this.options.params.profile || !this.paired || this.paired.state.status !== \"ready\") return false;\n if (!this.peerPaymentMethods || this.peerPaymentMethods.has(method)) return this.peerAllowsPayment(method);\n return !!this.peerNetworks && this.peerNetworks[method] === undefined && this.paired.peerAllowsPayment(method);\n }\n /** Takes effect at once; a connected contact is told on the open session, and the next handshake offers it. */\n setHoldSupport(on: boolean, top?: number): void { this.options.holdSupport = on; this.sendHoldState(top); }\n /** Older apps drop this frame (it carries no id) and keep using the handshake offer. */\n private sendHoldState(top?: number): void {\n if (!this.options.params.profile || !this.channel || !this.isDataLinkOpen || this.paired?.state.status !== \"ready\") return;\n try { this.channel.send(JSON.stringify({ t: \"paired-hold\", on: !!this.options.holdSupport, ...(top !== undefined ? { top } : {}) })); } catch { /* the next session offers it */ }\n }\n /** Takes effect at once, and a connected contact is told on the open session; the next handshake offers it too. */\n setPaymentMethods(methods: Partial<Record<PaymentMethodName, boolean>>): void { this.options.paymentMethods = { ...methods }; this.sendPaymentMethods(); }\n /** This side's wallets changed (one was created): a connected contact is told at once. */\n setPaymentNetworks(networks: PaymentNetworks): void {\n if (JSON.stringify(networks) === JSON.stringify(this.options.paymentNetworks ?? null)) return;\n this.options.paymentNetworks = networks; this.sendPaymentMethods();\n }\n /**\n * The networks the contact has a wallet on for this way of paying, as it said on this session. Undefined: it did\n * not say (an older app, or before its first word): any network may meet.\n */\n peerPaymentNetworks(method: PaymentMethodName): (\"mainnet\" | \"testnet\")[] | undefined { return this.peerNetworks ? this.peerNetworks[method] ?? [] : undefined; }\n /** The whole map as the contact said it: a way of paying it has no wallet of is not in it. Undefined: it did not say. */\n peerWalletNetworks(): PaymentNetworks | undefined { return this.peerNetworks ? { ...this.peerNetworks } : undefined; }\n /** The apps this contact may reach, said on the open session. Older apps drop the frame (no id). */\n private sendPairedServices(): void {\n if (!this.options.params.profile || !this.channel || !this.supportsServices || this.paired?.state.status !== \"ready\") return;\n try { this.channel.send(JSON.stringify({ t: \"paired-services\", s: servicesToWire(this.options.getPairedServices?.() ?? []) })); } catch { /* sent again on the next session */ }\n }\n private offeredCapabilities(): SessionCapability[] {\n const offered: SessionCapability[] = [];\n if (this.options.callsSupport) offered.push(CALLS_CAPABILITY);\n if (this.options.servicesSupport) offered.push(SERVICES_CAPABILITY);\n if (this.options.largeFilesSupport) offered.push(FILES_CAPABILITY);\n if (this.options.typingSupport) offered.push(TYPING_CAPABILITY);\n if (this.options.reactionsSupport) offered.push(REACTIONS_CAPABILITY);\n if (this.options.editSupport) offered.push(EDIT_CAPABILITY);\n if (this.options.wakeSupport) offered.push(WAKE_SESSION_CAPABILITY);\n if (this.options.pinSupport) offered.push(PIN_CAPABILITY);\n if (this.options.statusCardSupport) offered.push(STATUS_CARD_CAPABILITY);\n if (this.options.buttonsSupport) offered.push(BUTTONS_CAPABILITY);\n return offered;\n }\n /** Both sides offer `calls/1` on the open session: call signals can flow. Calls need a live session. */\n get supportsCalls(): boolean { return this.isDataLinkOpen && this.sessionCapabilities.agreed(CALLS_CAPABILITY); }\n /** Both sides offer `services/1` on the open session: shared web apps can be listed and reached. */\n get supportsServices(): boolean { return this.isDataLinkOpen && this.sessionCapabilities.agreed(SERVICES_CAPABILITY); }\n /** Both sides offer `files/3` on the open session: files of any size, offered and resumed. */\n get supportsLargeFiles(): boolean { return this.isDataLinkOpen && this.sessionCapabilities.agreed(FILES_CAPABILITY); }\n /** Both sides offer `typing/1` on the open session: typing can be said and shown. */\n get supportsTyping(): boolean { return this.isDataLinkOpen && this.sessionCapabilities.agreed(TYPING_CAPABILITY); }\n /** Both sides offer `react/1` on the open session: reactions can be said and confirmed here. */\n get supportsReactions(): boolean { return this.isDataLinkOpen && this.sessionCapabilities.agreed(REACTIONS_CAPABILITY); }\n /**\n * Says a reaction of this side (WISP 401 § Reactions) on the live session, once both sides offer `react/1`. An\n * error when it could not go now: the caller keeps it, and it goes again later (or on the DHT).\n */\n /**\n * This side has reactions the contact has not confirmed (`reactions` in the DHT options): off the live session they\n * ride on the DHT envelopes, and one goes now (WISP 403 § Reactions).\n */\n reactionsPending(): void { if (this.options.params.profile) void this.dht?.announceReactions(); }\n sendReaction(reaction: WireReaction): string | null {\n if (!this.options.params.profile) return \"Reactions need a current chat\";\n if (!this.channel || !this.isDataLinkOpen) return \"Reactions go when you are live\";\n if (!this.supportsReactions) return \"Your contact's app does not show reactions yet\";\n try { this.channel.send(reactionFrame(reaction)); return null; } catch { return \"The connection closed before sending\"; }\n }\n /** The contact is typing now, as it said on this session. */\n get peerTyping(): boolean { return this.typingReceiver.peerTyping; }\n /** What the contact is doing now (typing, recording, thinking, and a bot's status), or null. */\n get peerTypingActivity(): TypingActivity | null { return this.typingReceiver.peerActivity; }\n /** Both sides offer `wake/1` on the open session: push subscriptions can be shared (WISP 401 § Wake-up push). */\n get supportsWake(): boolean { return !!this.options.params.profile && this.isDataLinkOpen && this.sessionCapabilities.agreed(WAKE_SESSION_CAPABILITY); }\n /**\n * Shares how to wake this side's closed web app (null: stop, forget it) on the live session, once both sides offer\n * `wake/1`. False when it could not go now: the caller shares it again on the next session.\n */\n sendWake(target: WakeTarget | null): boolean {\n if (!this.channel || !this.supportsWake) return false;\n try { this.channel.send(JSON.stringify(wakeFrame(target))); return true; } catch { return false; }\n }\n /** Both sides offer `pin/1` on the open session: a pin can be said and confirmed. */\n get supportsPins(): boolean { return this.isDataLinkOpen && this.sessionCapabilities.agreed(PIN_CAPABILITY); }\n /**\n * Says this side's pin (WISP 401 § Pinned message) on the live session, once both sides offer `pin/1`. An error when\n * it could not go now: the caller keeps it, and it goes on the next session.\n */\n sendPin(pin: WirePin): string | null {\n if (!this.options.params.profile) return \"Pins need a current chat\";\n if (!this.channel || !this.isDataLinkOpen) return \"A pin goes when you are live\";\n if (!this.supportsPins) return \"Your contact's app does not show pins yet\";\n try { this.channel.send(pinFrame(pin)); return null; } catch { return \"The connection closed before sending\"; }\n }\n /** Both sides offer `edit/1` on the open session: edits can be said and confirmed. */\n get supportsEdits(): boolean { return this.isDataLinkOpen && this.sessionCapabilities.agreed(EDIT_CAPABILITY); }\n /** Both sides offer `status-card/1` on the open session: the contact shows cards and takes a card's edits past 100. */\n get supportsStatusCards(): boolean { return this.isDataLinkOpen && this.sessionCapabilities.agreed(STATUS_CARD_CAPABILITY); }\n /**\n * Says an edit of one of this side's messages (WISP 401 § Edits): on the live session once both sides offer `edit/1`;\n * while not live, on the DHT floor when `dht` says the contact's app takes edits there (WISP 403 § Edits). An error\n * when it could not go now; the caller keeps it and says it again later.\n */\n async sendEdit(edit: WireEdit, { dht = false }: { dht?: boolean } = {}): Promise<string | null> {\n if (!this.options.params.profile) return \"Edits need a current chat\";\n if (this.channel && this.isDataLinkOpen) {\n if (!this.supportsEdits) return \"Your contact's app does not show edits yet\";\n // Past a text's edits only an app that shows cards takes one (WISP 4xx · Status Cards); an older one would drop it.\n if (!validEditNumber(edit.e) && !(edit.sc && this.supportsStatusCards)) return \"Your contact's app takes no more edits of this message\";\n try { this.channel.send(editFrame(edit)); return null; } catch { return \"The connection closed before sending\"; }\n }\n // A card's edit waits for the live session: on the floor the card would read as its text until the next one.\n if (edit.sc) return \"A card's update goes when you are live\";\n if (dht && this.dht && this.textDelivery === \"dht\") return this.dht.send(edit.m, edit.ts, dhtEditId(edit.id, edit.e), undefined, [edit.id, edit.e]);\n return \"Edits go when you are live\";\n }\n /** Confirms an edit of the contact's that waited for its message (`onMessageEdit` returned false). */\n confirmEdit(id: string, e: number): void {\n if (!this.channel || !this.supportsEdits) return;\n try { this.channel.send(editedFrame(id, e)); } catch { /* the contact says it again */ }\n }\n /**\n * This side is typing (true) or stopped (false). Said only on the live session and only when both sides offer\n * `typing/1`; never on the DHT. A `start` goes at most every few seconds (at once when `activity` changes), a\n * `stop` only after a `start`.\n */\n setTyping(typing: boolean, activity?: TypingActivity): void {\n if (!this.options.params.profile) return;\n const frame = typing ? (this.supportsTyping ? this.typingSender.typing(activity) : null) : this.typingSender.stopped();\n if (!frame || !this.channel || !this.supportsTyping) return;\n try { this.channel.send(JSON.stringify(frame)); } catch { /* the session is going; the contact's timeout ends it */ }\n }\n /** Whether files/3 was agreed on the session open now; kept to say when that changes. */\n private filesOpen = false;\n private emitFilesSession(again = false): void {\n const open = this.supportsLargeFiles;\n if (open === this.filesOpen && !(open && again)) return;\n this.filesOpen = open;\n this.options.events?.onFilesSession?.(open);\n }\n /** A files/3 frame to the contact, on the open session. False when files/3 cannot flow now. */\n sendFilesFrame(frame: Record<string, unknown>): boolean {\n if (!this.supportsLargeFiles || !this.channel) return false;\n try { this.channel.send(JSON.stringify(frame)); return true; } catch { return false; }\n }\n /**\n * Resolves once the open session can take another files/3 data frame; undefined when it can now. A channel with a send budget (native and\n * iroh ones throw past 120 KiB) gives files a quarter of it, so a ping, a message or any other frame still fits\n * while a file goes; others keep under the high water mark, as files/2 does.\n */\n filesWritable(): Promise<void> | undefined {\n const channel = this.channel;\n if (!channel) return;\n const cap = channel.sendBudget ? channel.sendBudget / 4 : LIMITS.sendHighWaterMark;\n // Room now: nothing to wait for, and nothing made for each frame of a large file.\n if (channel.bufferedAmount < cap) return;\n return (async () => {\n while (this.channel === channel && channel.bufferedAmount >= cap) {\n await channel.drained().catch(() => {});\n // A drain that resolved above the cap (a low water mark higher than it) is not waited on in a loop of promises.\n if (this.channel === channel && channel.bufferedAmount >= cap) await new Promise((resolve) => setTimeout(resolve, 20));\n }\n })();\n }\n /** What each side offers on the open session, for showing why something is unavailable. `peer` is null until it says. */\n get sessionOffers(): { mine: SessionCapability[]; peer: string[] | null } {\n const known = KNOWN_SESSION_CAPABILITIES;\n return { mine: this.offeredCapabilities(),\n peer: this.isDataLinkOpen && this.sessionCapabilities.peerAnnounced ? known.filter(c => this.sessionCapabilities.peerOffers(c)) : null };\n }\n /** Why a call cannot be placed in this paired chat right now, or null when it can. */\n get callsUnavailable(): string | null {\n if (!this.options.params.profile || this.supportsCalls) return null;\n if (!this.options.callsSupport) return this.options.callsMissing ?? \"Calls are not available in this app\";\n if (!this.isDataLinkOpen) return \"Calls need a live connection\";\n return this.sessionCapabilities.peerAnnounced ? \"Your contact's app cannot take calls\" : \"Your contact needs an updated Ghostly for calls\";\n }\n /** Says what this side offers on the open session. Older apps drop the frame (no id). */\n private sendSessionCapabilities(): void {\n if (!this.options.params.profile || !this.channel || !this.isDataLinkOpen) return;\n try { this.channel.send(JSON.stringify(this.sessionCapabilities.announcement())); } catch { /* said again on the next session */ }\n }\n /** The contact said what it offers: start what both sides now agree on. */\n private sessionCapabilitiesChanged(changed: SessionCapability[]): void {\n if (changed.includes(SERVICES_CAPABILITY)) {\n if (this.supportsServices) this.sendPairedServices();\n else if (this.peerServicesOverride) { this.peerServicesOverride = null; this.options.events?.onPresence?.(this.presence); }\n }\n if (changed.includes(CALLS_CAPABILITY) && this.supportsCalls && this.channel) {\n const pending = this.pairedCalls.pending();\n if (pending) try { this.channel.send(JSON.stringify(pending)); } catch { /* the next session */ }\n }\n if (changed.includes(TYPING_CAPABILITY) && !this.supportsTyping) { this.typingSender.reset(); this.typingReceiver.clear(); }\n if (changed.includes(REACTIONS_CAPABILITY)) this.options.events?.onReactionsSupport?.(this.supportsReactions);\n if (changed.includes(EDIT_CAPABILITY)) this.options.events?.onEditSupport?.(this.supportsEdits);\n if (changed.includes(PIN_CAPABILITY)) this.options.events?.onPinSupport?.(this.supportsPins);\n if (changed.includes(WAKE_SESSION_CAPABILITY)) this.options.events?.onWakeSupport?.(this.supportsWake);\n this.emitPairingState();\n }\n /** Both sides announced groups on this session and it is open. */\n get groupsSupport(): boolean { return !!this.options.groupsSupport && this.isDataLinkOpen && !!this.peerGroupVersions?.includes(1); }\n /** Both sides announced this version of groups (1: `group-mesh/1`, 2: `group-community/1` too). */\n supportsGroupVersion(version: number): boolean { return this.groupsSupport && !!this.peerGroupVersions?.includes(version); }\n /** A `group-*` frame to the peer. Only while both sides support groups; never queued. */\n sendGroupFrame(frame: object): void {\n if (!this.groupsSupport || !this.channel) throw new Error(\"This contact is not connected, or needs an updated Ghostly for groups\");\n const data = JSON.stringify(frame);\n if (data.length > LIMITS.maxControlFrameBytes) throw new Error(\"Group frame too large\");\n this.channel.send(data);\n }\n /** Older apps drop this frame (it carries no id): to them this contact has no groups. */\n private sendGroupsSupport(): void {\n if (!this.options.groupsSupport || !this.options.params.profile || !this.channel || !this.isDataLinkOpen) return;\n try { this.channel.send(JSON.stringify({ t: \"paired-groups\", v: [1, 2, GROUP_VERSION_LARGE, GROUP_VERSION_HUBS] })); } catch { /* the next session announces it */ }\n }\n /** Older apps drop this frame (it carries no id) and keep using the handshake offer. */\n private sendPaymentMethods(): void {\n if (!this.options.params.profile || !this.channel || !this.isDataLinkOpen || this.paired?.state.status !== \"ready\") return;\n // Offered: the ways this chat has on and (once this app says its wallets' networks) has a wallet of; a way with\n // no wallet is nothing the contact can pay to. A wallet whose networks are all off here still names the way.\n const networks = this.options.paymentNetworks;\n const methods = PAYMENT_METHODS.filter(m => this.paymentEnabled(m) && (!networks || networks[m] !== undefined));\n // The networks go for every wallet, on or off here: a way turned off is told apart from one with no wallet.\n try { this.channel.send(JSON.stringify({ t: \"paired-payments\", m: methods, ...(networks ? { n: networks } : {}) })); } catch { /* the next session offers it */ }\n }\n async requirePaymentSupport(): Promise<void> {\n if (!PAYMENT_METHODS.some(m => this.paymentEnabled(m))) throw new Error(\"Payments are turned off in this chat.\");\n await this.connect();\n if (!this.supportsPayments) throw new Error(\"This contact does not support payments. Both peers need an updated Ghostly.\");\n }\n\n private requireLegacyCapabilities(): void {\n if (this.options.params.profile) throw new Error(\"This experimental paired profile supports chat only.\");\n }\n\n private cancelCandidate(): void {\n this.candidateEpoch++;\n const candidate = this.candidate;\n this.candidate = null;\n candidate?.session.stop();\n candidate?.reject(new Error(\"Transport change cancelled\"));\n candidate?.channel.close();\n this.rtcCandidateWaiter?.reject(new Error(\"Transport change cancelled\"));\n this.rtcCandidateWaiter = null;\n if (this.activeBinding) this.dataLink.close();\n }\n\n private async prepareSwitch(plan: SwitchPlan): Promise<void> {\n const epoch = ++this.candidateEpoch;\n let lastError: unknown;\n this.switchMissed = undefined;\n for (const transport of plan.choices) {\n if (epoch !== this.candidateEpoch || this.switcher.pending !== plan || this.stopped) return;\n if (this.paired?.state.transport === transport) {\n this.switcher.keep(lastError instanceof Error ? lastError.message : undefined); return;\n }\n try {\n await new Promise<void>((resolve, reject) => {\n let active = true;\n const timer = setTimeout(() => { active = false; reject(new Error(`${transport} did not connect in time`)); }, 8_000);\n const done = { resolve: () => { active = false; clearTimeout(timer); resolve(); }, reject: (error: Error) => { active = false; clearTimeout(timer); reject(error); } };\n if (transport === \"webrtc/1\") {\n this.rtcCandidateWaiter = done;\n void this.dataLink.connect().catch(done.reject);\n } else {\n const endpoint = this.endpoints.get(transport), descriptor = plan.remote.descriptors[transport];\n if (!endpoint || !descriptor) { done.reject(new Error(\"Peer native address unavailable\")); return; }\n void endpoint.connect(descriptor).then(({ channel, binding }) => {\n if (!active || epoch !== this.candidateEpoch || this.switcher.pending !== plan) { channel.close(); return; }\n return this.attachCandidate(channel, binding, plan).then(done.resolve, done.reject);\n }).catch(done.reject);\n }\n });\n return;\n } catch (error) {\n lastError = error;\n if (transport === plan.choices[0]) this.switchMissed = { transport, reason: error instanceof Error ? error.message : String(error) };\n if (epoch !== this.candidateEpoch || this.switcher.pending !== plan) return;\n const candidate = this.candidate; this.candidate = null;\n candidate?.session.stop(); candidate?.reject(new Error(\"Candidate failed\")); candidate?.channel.close();\n this.rtcCandidateWaiter = null;\n if (transport === \"webrtc/1\") this.dataLink.close();\n }\n }\n if (epoch === this.candidateEpoch && this.switcher.pending === plan)\n this.switcher.fail(`Transport change failed: ${lastError instanceof Error ? lastError.message : \"no permitted transport connected\"}. Retry or choose another transport.`);\n }\n\n private attachCandidate(channel: FrameChannel, binding: NativeBinding | undefined, plan: SwitchPlan): Promise<void> {\n if (this.candidate || this.switcher.pending !== plan || !plan.choices.includes(binding?.transport ?? \"webrtc/1\")) {\n channel.close(); return Promise.reject(new Error(\"Obsolete or duplicate transport candidate\"));\n }\n return new Promise<void>((resolve, reject) => this.attach(channel, binding, { plan, resolve, reject }));\n }\n\n /**\n * Both sides dialled at once (two apps back after a restart, each knocking): the lower key's dial carries the chat, on\n * both sides; the higher key's side takes it over as a replacement (`attachReplacement`). Here, on the lower key's\n * side, the contact's connection waits: dropped once this side's own dial connects, taken if that dial fails, or has\n * not connected within `CROSSED_WAIT_MS` (the contact may be dialling an app that is not up yet, for as long as its\n * dial takes to give up).\n */\n private crossed: { channel: FrameChannel; binding: NativeBinding; timer: ReturnType<typeof setTimeout> } | null = null;\n private parkCrossed(channel: FrameChannel, binding: NativeBinding): void {\n this.dropCrossed();\n traceLink(this.myPubKeyZ32, \"dial-crossed\", { transport: binding.transport });\n const timer = setTimeout(() => {\n if (this.crossed?.channel !== channel) return;\n this.crossed = null;\n if (this.channel || this.stopped || this.leaving) { channel.close(); return; }\n // This side's dial did not connect in time: the contact's carries the chat, and this one is closed if it does yet.\n this.connectionEpoch++; this.dialing = false; this.attempt = null;\n this.attach(channel, binding);\n }, CROSSED_WAIT_MS);\n channel.onClose = () => { if (this.crossed?.channel === channel) { clearTimeout(timer); this.crossed = null; } };\n this.crossed = { channel, binding, timer };\n }\n private dropCrossed(): void {\n const crossed = this.crossed;\n if (!crossed) return;\n this.crossed = null; clearTimeout(crossed.timer); crossed.channel.close();\n }\n /** This side's dial ended: a connection of the contact's that waited is dropped, or, if nothing connected, taken now. */\n private settleCrossed(): void {\n const crossed = this.crossed;\n if (!crossed) return;\n if (this.channel || this.stopped || this.leaving) { this.dropCrossed(); return; }\n this.crossed = null; clearTimeout(crossed.timer);\n this.attach(crossed.channel, crossed.binding);\n }\n\n /**\n * The pinned contact dialled in while this side still holds a session with it. A contact dials only with no session\n * of its own (a switch both agreed goes through `attachCandidate`): its app restarted, or lost the session, and the\n * one held here is dead without this side having heard (a crash, a quit that said nothing, a network change). The new\n * connection authenticates first, then takes over; a dial that does not authenticate closes and changes nothing.\n * Before the pin a held session keeps the chat, as it always did.\n */\n private attachReplacement(channel: FrameChannel, binding: NativeBinding): void {\n if (!this.options.pairing?.credentials.peerKey || !this.options.params.profile) { channel.close(); return; }\n // A replacement dialled in that has not authenticated yet keeps no place against a newer one: whoever read the\n // endpoint could otherwise hold it while the contact, back after a restart, is turned away.\n const waiting = this.candidate;\n if (waiting && !waiting.plan && this.unproven(waiting.channel) && waiting.session.state.status !== \"ready\") {\n traceLink(this.myPubKeyZ32, \"dialed-in-dropped\", { transport: waiting.binding?.transport });\n this.candidate = null; waiting.session.stop(); waiting.channel.close(); waiting.reject(new Error(\"A newer connection took its place\"));\n }\n if (this.candidate) { channel.close(); return; }\n // Two dials crossed and both connected: the lower key's carries the chat. Here, the lower key's side, holding the\n // one it just dialled, refuses the contact's; the contact takes this one over as a replacement.\n const held = this.channel;\n if (held && this.activeBinding && !this.dialedIn.has(held) && Date.now() - this.channelSince < CROSSED_FRESH_MS\n && this.myPubKeyZ32 < this.options.params.peerPubKeyZ32) {\n traceLink(this.myPubKeyZ32, \"dial-crossed\", { transport: binding.transport, held: true }); channel.close(); return;\n }\n traceLink(this.myPubKeyZ32, \"replacing\", { transport: binding.transport, held: this.paired?.state.transport });\n this.attach(channel, binding, { resolve: () => {}, reject: () => {} });\n }\n\n /**\n * Connections that came in on this side's native endpoints. Their address was in the capability record, which before\n * the pin anyone holding a copy of the invite could read: a key other than the pinned one on such a connection proves\n * no more than on the DHT, so it closes that connection and nothing else.\n */\n private readonly dialedIn = new WeakSet<FrameChannel>();\n\n /**\n * `migration`: the channel is a candidate that takes over from the session open now once it authenticated. With a\n * `plan`, a live switch both sides agreed; without one, a replacement (`attachReplacement`): the contact dialled\n * afresh, having no session with this side any more.\n */\n private attach(channel: FrameChannel, binding?: NativeBinding, migration?: { plan?: SwitchPlan; resolve(): void; reject(error: Error): void }): void {\n if (this.options.params.profile) {\n const fingerprints = this.dataLink.fingerprints;\n if ((!binding && !fingerprints) || !this.options.pairing) { channel.close(); migration?.reject(new Error(\"Connection binding unavailable\")); return; }\n channel.onClose = () => {\n if (this.candidate?.channel === channel) { this.candidate.session.stop(); this.candidate = null; migration?.reject(new Error(\"Candidate connection closed\")); }\n if (this.channel === channel) {\n const wasLive = this.paired?.state.status === \"ready\";\n this.rejectWaiters(new Error(\"The peer closed this connection. Check that both transport preferences allow a common transport, then reconnect.\"));\n this.detach();\n if (wasLive) this.peerLost(\"closed\");\n // A native connection that never authenticated (the contact refused it, or its path died): the side whose turn\n // it is dials again now, not when a later look at the contact's packet happens to ask.\n else if (binding) this.maybeAutoConnect(this.presence);\n }\n };\n const unproven = this.unproven(channel);\n if (!migration) {\n this.activeBinding = binding; this.channel = channel; this.channelSince = Date.now();\n // One dialled in is nobody until it authenticated: the DHT keeps its pace meanwhile (set once it is ready).\n if (!unproven) this.session.setDataLinkOpen(true);\n }\n const paired = new PairedSession(channel, {\n ...this.options.pairing,\n trustOnFirstUse: this.options.pairing.trustOnFirstUse ?? true,\n rendezvousKeys: [this.myPubKeyZ32, this.options.params.peerPubKeyZ32],\n fingerprints: fingerprints ?? undefined,\n binding, transports: migration?.plan?.choices ?? this.transportOffer(), allowFallback: migration?.plan?.local.fallback ?? this.fallback,\n transportSwitchSupport: true,\n // Dialled in, or dialled out on a native transport (its path was up when it opened): bounded either way on a pinned chat.\n ...(unproven || (binding && this.options.pairing.credentials.peerKey) ? { authTimeoutMs: UNPROVEN_AUTH_MS } : {}),\n holdSupport: !!this.options.holdSupport,\n proofSupport: !!this.options.events?.onPeerProof,\n identitySupport: !!this.options.events?.onIdentityProof,\n filesSupport: !!this.options.events?.onFileIncoming,\n arkPaymentsSupport: this.options.arkPaymentsSupport && this.paymentEnabled(\"arkade\"),\n usdtPaymentsSupport: this.options.usdtPaymentsSupport && this.paymentEnabled(\"usdt\"),\n barkPaymentsSupport: this.options.barkPaymentsSupport && this.paymentEnabled(\"bark\"),\n cashuPaymentsSupport: this.paymentEnabled(\"cashu\"),\n lightningPaymentsSupport: this.paymentEnabled(\"lightning\"),\n paymentsSupport: PAYMENT_METHODS.some(m => this.paymentEnabled(m)) && !!this.options.events?.onPayment && !!this.options.events?.onPaymentRequest && !!this.options.events?.onPaymentResult,\n // A connection dialled in on a pinned chat says nothing until it authenticated: one refused leaves no trace in the state.\n onState: () => { if (this.channel === channel && (!this.unproven(channel) || paired.state.status === \"ready\")) this.emitPairingState(); },\n onFailure: () => {\n // Dialled in and unproven, or a connection that carried nothing in time: neither says anything about the contact.\n if (this.unproven(channel) || paired.authTimedOut) {\n if (paired.state.keyMismatch) this.dht?.foreignKeySeen(\"stream\");\n if (this.unproven(channel)) traceLink(this.myPubKeyZ32, \"dialed-in-refused\", { keyMismatch: !!paired.state.keyMismatch });\n else traceLink(this.myPubKeyZ32, \"auth-timeout\", { transport: binding?.transport ?? \"webrtc/1\" });\n migration?.reject(new Error(paired.state.error ?? \"Candidate authentication failed\"));\n channel.close(); if (this.channel === channel) this.detach();\n if (!this.channel && binding) this.maybeAutoConnect(this.presence);\n return;\n }\n this.securityRejected = true;\n // Another key on the stream than the one pinned: the chat stops on both layers, the DHT's too.\n if (paired.state.keyMismatch) { this.keyStopped = true; traceLink(this.myPubKeyZ32, \"key-stop\", { path: \"stream\" }); }\n this.tracker?.failed(paired.state.keyMismatch ? \"key-mismatch\" : \"rejected\", !paired.state.keyMismatch, paired.state.error);\n migration?.reject(new Error(paired.state.error ?? \"Candidate authentication failed\"));\n channel.close(); if (this.channel === channel) this.detach();\n },\n onReady: () => {\n let carried: PairedFiles | null = null;\n let switched: { from?: PairedTransport; to: PairedTransport } | null = null, replaced = false;\n if (migration) {\n if (this.candidate?.channel !== channel) { paired.stop(); channel.close(); return; }\n const oldChannel = this.channel, oldPaired = this.paired, oldBinding = this.activeBinding;\n const from = oldPaired?.state.transport;\n this.candidate = null;\n // Transfers in flight carry on over the new channel; they end only if it cannot take files. A replacement is a\n // new session on the contact's side: nothing of the old one carries over.\n const files = this.pairedFiles; this.pairedFiles = null;\n if (files && migration.plan && paired.supports(\"files/2\")) { files.rebind(channel); carried = files; } else files?.closeAll();\n this.pairedHttp?.close(); this.pairedHttp = null;\n this.channel = channel; this.paired = paired; this.activeBinding = binding; this.channelSince = Date.now();\n oldPaired?.stop();\n // Off WebRTC: its peer connection goes with the channel, unless WebRTC carries the chat again by then.\n if (oldChannel) this.retire(oldChannel, () => { if (!oldBinding && this.activeBinding) this.dataLink.close(); });\n this.session.setDataLinkOpen(true);\n if (migration.plan) switched = { from, to: paired.state.transport! };\n else { replaced = true; traceLink(this.myPubKeyZ32, \"replaced\", { from, to: paired.state.transport }); }\n }\n if (this.channel !== channel) return;\n this.session.setDataLinkOpen(true);\n this.securityRejected = false;\n const events = this.options.events ?? {};\n if (carried) this.pairedFiles = carried;\n else if (paired.supports(\"files/2\")) this.pairedFiles = new PairedFiles(channel, {\n onIncoming: file => events.onFileIncoming?.(file) ?? null,\n onStored: events.onFileStored,\n onProgress: events.onFileProgress,\n onComplete: events.onFileComplete,\n onFailed: events.onFileFailed,\n });\n this.peerPolicySeen = null;\n if (paired.peerTransportSwitchSupport) this.switcher.begin(paired.proofSession, paired.state.transport!, !!migration?.plan);\n else this.advertiseTransports();\n this.peerPaymentMethods = null; this.peerNetworks = null;\n this.peerHoldOverride = null;\n this.sessionCapabilities.reset();\n this.typingSender.reset(); this.typingReceiver.clear();\n this.pairedHttp?.close();\n this.pairedHttp = new PairedHttp(channel, this.options.getHostedHttpService, this.options.localFetch);\n this.emitPairingState();\n this.sendPairedNick();\n this.sendPairedAvatar();\n this.sendPaymentMethods();\n this.sendSessionCapabilities();\n this.sendGroupsSupport();\n this.sendHoldState();\n void this.options.events?.onHold?.({ peerAllows: paired.peerHoldSupport });\n migration?.resolve();\n this.rtcCandidateWaiter?.resolve(); this.rtcCandidateWaiter = null;\n // The wait between attempts is for attempts that failed: a link that worked and then dropped\n // (the contact reloaded, or came back) is dialled again as soon as the contact is seen.\n this.autoConnectFailures = 0;\n this.lastAutoConnectAt = 0;\n traceLink(this.myPubKeyZ32, \"paired-ready\");\n this.attempt = null;\n if (this.lastAttempt) { this.lastAttempt = undefined; this.options.events?.onLiveAttempt?.(undefined); }\n this.tracker?.live(paired.state.transport);\n // A native session carries the chat: a WebRTC attempt still under way (an offer out, an answer held back by the\n // relays' budget) ends, and with it its fast reads of the relays. Its record's signal is cleared once.\n if (binding && !migration?.plan && (this.dataLink.state === \"offering\" || this.dataLink.state === \"answering\" || this.dataLink.state === \"connecting\")) {\n this.afterRtc = undefined; this.dataLink.close();\n }\n this.startLiveness(channel, paired.peerAnswersPings);\n for (const waiter of this.openWaiters.splice(0)) waiter.resolve();\n this.options.events?.onPresence?.(this.presence);\n if (switched) this.options.events?.onTransportSwitched?.(switched.from, switched.to);\n if (switched) this.emitFilesSession(true);\n // A new session in place of a dead one, live all along as the owner saw it: what the old one may have lost\n // (the outbox's unconfirmed text, a file offer) goes again now, as after any reconnect.\n if (replaced) { this.options.events?.onDataLinkState?.(\"open\"); this.emitFilesSession(true); }\n },\n onApplication: async data => {\n if (this.channel !== channel) return;\n // Anything from the peer shows the session is alive.\n this.unansweredPings = 0; this.heardFromPeer();\n if (typeof data !== \"string\" || data.length > 60 * 1024) return;\n let frame: Record<string, unknown>;\n try { frame = JSON.parse(data); } catch { return; }\n // What the two people do (texts, receipts, typing, files, calls, payments...), not the session's upkeep.\n if (frame && typeof frame === \"object\" && !QUIET_FRAMES.has(String(frame.t))) this.activityAt = Date.now();\n if (frame?.t === \"paired-ping\") { try { channel.send(JSON.stringify({ t: \"paired-pong\" })); } catch { /* closing */ } return; }\n if (frame?.t === \"paired-pong\") {\n this.peerAnswersPings = true;\n if (this.pingSentAt) { this.rtt = Date.now() - this.pingSentAt; this.pingSentAt = 0; this.options.events?.onRtt?.(this.rtt); }\n return;\n }\n if (frame?.t === \"paired-bye\") { if (paired.state.status === \"ready\") this.peerDeparted(channel); return; }\n if (!frame || typeof frame !== \"object\") return;\n if (paired.peerTransportSwitchSupport && this.switcher.handle(frame)) return;\n if (frame.t === \"paired-rtc\") {\n if (this.switcher.pending?.choices.includes(\"webrtc/1\") && typeof frame.signal === \"string\") {\n const credentials = this.options.pairing!.credentials;\n const signal = verifyPairedSignal(frame.signal, this.options.params.peerPubKeyZ32,\n this.myPubKeyZ32, credentials.peerKey, true);\n if (signal) void this.dataLink.handleSignal(signal, { since: Date.now(), after: null, live: true });\n }\n return;\n }\n if (!this.isDataLinkOpen) return;\n if (typeof frame?.t === \"string\" && FILE_FRAMES.has(frame.t)) {\n if (this.supportsLargeFiles) await this.options.events?.onFilesFrame?.(frame);\n return;\n }\n if (typeof frame?.t === \"string\" && frame.t.startsWith(\"pf-\")) {\n if (this.supportsFiles) await this.pairedFiles?.handle(frame);\n return;\n }\n if ([\"pay\", \"pay-req\", \"pay-res\", \"pay-ask\"].includes(String(frame?.t))) {\n if (this.supportsPayments) {\n const payment = decodeControl(data);\n if (payment?.t === \"pay\") await this.options.events?.onPayment?.({ id: payment.id, timestamp: payment.ts, requestId: payment.rid, amount: { value: payment.v, asset: payment.u }, memo: payment.memo, endpoint: payment.e });\n else if (payment?.t === \"pay-req\") await this.options.events?.onPaymentRequest?.({ id: payment.id, timestamp: payment.ts, amount: { value: payment.v, asset: payment.u }, memo: payment.memo, endpoints: payment.e, ask: payment.a, network: payment.n });\n else if (payment?.t === \"pay-ask\") await this.options.events?.onPaymentAsk?.({ id: payment.id, timestamp: payment.ts, amount: { value: payment.v, asset: payment.u }, method: payment.m, memo: payment.memo, network: payment.n });\n else if (payment?.t === \"pay-res\") await this.options.events?.onPaymentResult?.({ id: payment.id, ok: payment.ok, credited: payment.v, error: payment.err, ...(payment.c ? { closed: true } : {}) });\n }\n return;\n }\n if (typeof frame?.t === \"string\" && frame.t.startsWith(\"idp-\") && this.identitySupport) {\n await this.options.events?.onIdentityProof?.(frame); return;\n }\n if (typeof frame?.t === \"string\" && frame.t.startsWith(\"proof-\") && this.peerProofSupport) {\n await this.options.events?.onPeerProof?.(frame); return;\n }\n if (frame?.t === \"paired-avatar\") {\n const avatar = sanitizeAvatar(frame.a);\n if (avatar !== undefined) this.options.events?.onPeerAvatar?.(avatar);\n return;\n }\n if (frame?.t === \"paired-nick\") {\n const nick = sanitizeNick(frame.n);\n if (typeof frame.n === \"string\") this.options.events?.onPeerNick?.(nick ?? null);\n if (nick !== this.peerNickOverride) {\n this.peerNickOverride = nick ?? null;\n this.options.events?.onPresence?.(this.presence);\n }\n return;\n }\n if (frame?.t === SESSION_CAPABILITIES_FRAME) {\n const changed = this.sessionCapabilities.receive(frame);\n if (changed) this.sessionCapabilitiesChanged(changed);\n return;\n }\n if (frame?.t === WAKE_FRAME) {\n // Only once both said wake/1; a handful per minute at most (it changes when the contact rotates it).\n if (!this.supportsWake || !this.wakeReceived.take()) return;\n const target = parseWakeFrame(frame);\n if (target !== undefined) this.options.events?.onPeerWake?.(target);\n return;\n }\n if (frame?.t === TYPING_FRAME) {\n // Only on the authenticated session with the pinned contact, and only once both said typing/1.\n if (this.supportsTyping) this.typingReceiver.receive(frame);\n return;\n }\n if (frame?.t === REACTION_FRAME) {\n // Counted before it is read: a flood of malformed frames costs as much as one of reactions.\n if (!this.supportsReactions || !this.reactionsReceived.take()) return;\n const reaction = parseReactionFrame(frame);\n if (!reaction) return;\n if (await this.options.events?.onReaction?.(reaction) && this.channel === channel && this.isDataLinkOpen)\n try { channel.send(reactedFrame(reaction.n)); } catch { /* it comes again */ }\n return;\n }\n if (frame?.t === REACTED_FRAME) {\n const n = parseReactedFrame(frame);\n if (n !== null) await this.options.events?.onReactionReceipt?.(n);\n return;\n }\n if (frame?.t === PIN_FRAME) {\n // Counted before it is read, as reactions are.\n if (!this.supportsPins || !this.pinsReceived.take()) return;\n const pin = parsePinFrame(frame);\n if (!pin) return;\n if (await this.options.events?.onPin?.(pin) && this.channel === channel && this.isDataLinkOpen)\n try { channel.send(pinnedFrame(pin.n)); } catch { /* it comes again */ }\n return;\n }\n if (frame?.t === PINNED_FRAME) {\n const n = parsePinnedFrame(frame);\n if (n !== null) await this.options.events?.onPinReceipt?.(n);\n return;\n }\n if (frame?.t === EDIT_FRAME) {\n // Counted before it is read: a flood of malformed frames costs as much as one of edits.\n if (!this.supportsEdits || !this.editsReceived.take()) return;\n const edit = parseEditFrame(frame);\n if (!edit) return;\n if (await this.options.events?.onMessageEdit?.(edit) && this.channel === channel && this.isDataLinkOpen)\n channel.send(editedFrame(edit.id, edit.e));\n return;\n }\n if (frame?.t === EDITED_FRAME) {\n const receipt = parseEditedFrame(frame);\n if (!receipt) return;\n await this.options.events?.onEditReceipt?.(receipt.id, receipt.e);\n // The same edit waiting on the DHT floor is not published again.\n await this.dht?.acknowledge(dhtEditId(receipt.id, receipt.e));\n return;\n }\n if (frame?.t === PAIRED_CALL_FRAME) {\n const signal = this.supportsCalls ? parsePairedCallFrame(frame) : null;\n // One already heard on an earlier session is not handed on again (`PairedCalls.heard`).\n if (signal && this.pairedCalls.heard(signal)) this.options.events?.onCallSignal?.(signal);\n return;\n }\n if (frame?.t === \"ph\") { if (this.supportsServices) this.pairedHttp?.handle(frame); return; }\n if (frame?.t === \"paired-services\") {\n if (!this.supportsServices) return;\n const services = servicesFromWire(frame.s);\n if (services) { this.peerServicesOverride = services; this.options.events?.onPresence?.(this.presence); }\n return;\n }\n if (frame?.t === \"paired-payments\") {\n // A method this app does not know yet (a newer contact) is left out, not a reason to drop the list.\n if (!Array.isArray(frame.m) || frame.m.length > 16 || !frame.m.every(m => typeof m === \"string\")) return;\n this.peerPaymentMethods = new Set((frame.m as string[]).filter((m): m is PaymentMethodName => PAYMENT_METHODS.includes(m as PaymentMethodName)));\n this.peerNetworks = frame.n === undefined ? null : parsePaymentNetworks(frame.n) ?? this.peerNetworks;\n this.emitPairingState();\n return;\n }\n if (frame?.t === \"paired-groups\") {\n if (!Array.isArray(frame.v) || frame.v.length > 8 || !frame.v.every(v => Number.isSafeInteger(v) && v > 0)) return;\n const before = this.groupsSupport;\n this.peerGroupVersions = frame.v as number[];\n if (this.groupsSupport !== before) this.options.events?.onGroupsSupport?.(this.groupsSupport);\n return;\n }\n if (typeof frame?.t === \"string\" && frame.t.startsWith(\"group-\")) {\n if (this.groupsSupport) await this.options.events?.onGroupFrame?.(frame);\n return;\n }\n if (frame?.t === \"paired-hold\") {\n // The contact's latest word on held items; an older app never sends it and keeps its handshake offer.\n if (typeof frame.on !== \"boolean\" || (frame.top !== undefined && !(Number.isSafeInteger(frame.top) && (frame.top as number) >= 0))) return;\n this.peerHoldOverride = frame.on;\n // Not awaited: the owner may be picking up held items (from the contact's storage, seconds or more), and\n // every frame after this one would wait for it, the edits, texts and pings that keep this session alive too.\n void Promise.resolve(this.options.events?.onHold?.({ peerAllows: frame.on, peerTop: frame.top as number | undefined })).catch(() => {});\n this.emitPairingState();\n return;\n }\n if (frame?.t === \"paired-reconnect\") {\n this.switchAllowedUntil = Date.now() + 30_000;\n channel.send(JSON.stringify({ t: \"paired-reconnect-ack\" })); return;\n }\n if (frame?.t === \"paired-reconnect-ack\") { this.switchAck?.(); return; }\n if (frame?.t === \"paired-adapters\") {\n if (!frame.descriptors || typeof frame.descriptors !== \"object\" || JSON.stringify(frame.descriptors).length > 4096) return;\n const descriptors: TransportDescriptors = {};\n for (const transport of [\"iroh/1\", \"hyperdht/1\"] as const) {\n const value = (frame.descriptors as TransportDescriptors)[transport];\n if (value) descriptors[transport] = value;\n }\n const transports = [...(this.paired?.peerTransports ?? [])] as PairedTransport[];\n const fallback = this.paired?.peerAllowsFallback ?? false;\n await this.options.events?.onTransportDiscovery?.(descriptors, transports, fallback);\n this.peerDescriptors = descriptors; this.peerTransports = transports; this.peerFallback = fallback;\n return;\n }\n if (!frame || typeof frame.id !== \"string\" || !/^[A-Za-z0-9_-]{22}$/.test(frame.id)) return;\n if (frame.t === \"paired-message\" && typeof frame.m === \"string\" &&\n utf8Encode(frame.m).length <= LIMITS.maxChatMessageBytes &&\n typeof frame.ts === \"number\" && Number.isSafeInteger(frame.ts) && frame.ts > 0) {\n // A bad preview is dropped, never the message (older apps ignore `pv` altogether).\n const preview = frame.pv === undefined ? undefined : parseLinkPreview(frame.pv, frame.m);\n // What the contact was typing arrived: it is not typing any more.\n this.typingReceiver.clear();\n // The same for a reply: one that does not hold is left out (older apps ignore `r`).\n const reply = frame.r === undefined ? undefined : readReply(frame.r, pairedReplyAuthor);\n // A hop count that is not one is left out: the message arrives as written here (older apps ignore `fw`).\n const forwarded = readForwarded(frame.fw);\n // A card that does not hold is left out: the message shows its text, as an older app shows it.\n const card = frame.sc === undefined ? undefined : readStatusCard(frame.sc);\n await this.options.events?.onMessage?.({ id: frame.id, text: frame.m, timestamp: frame.ts, via: \"datalink\", ...(preview && { preview }), ...(reply && { reply }), ...(forwarded && { forwarded }), ...(card && { card }) });\n if (this.channel === channel && this.isDataLinkOpen)\n channel.send(JSON.stringify({ t: \"paired-received\", id: frame.id }));\n } else if (frame.t === \"paired-received\") {\n const timestamp = this.pairedPending.get(frame.id);\n if (timestamp !== undefined) {\n await this.options.events?.onMessageReceipt?.(frame.id);\n await this.dht?.acknowledge(frame.id);\n this.pairedPending.delete(frame.id);\n }\n }\n },\n });\n if (migration) this.candidate = { channel, session: paired, binding, ...(migration.plan && { plan: migration.plan }), reject: migration.reject };\n else this.paired = paired;\n paired.start();\n return;\n }\n this.channel = channel;\n this.autoConnectFailures = 0;\n this.lastAutoConnectAt = 0;\n this.httpClient = new HttpClient(channel);\n this.httpHost = new HttpHost(channel, this.options.getHostedHttpService, this.options.localFetch);\n const events = this.options.events ?? {};\n this.files = new FileTransfers(channel, {\n onIncoming: (file) => events.onFileIncoming?.(file) ?? null,\n onProgress: (id, transferred, direction) => events.onFileProgress?.(id, transferred, direction),\n onComplete: (id, direction) => events.onFileComplete?.(id, direction),\n onFailed: (id, reason, direction) => events.onFileFailed?.(id, reason, direction),\n });\n this.session.setDataLinkOpen(true);\n\n channel.onMessage = (data) => this.handleFrame(data);\n channel.send(\n encodeControl({\n t: \"hello\",\n v: PROTOCOL_VERSION,\n svc: servicesToWire(this.options.getServices() ?? []),\n nick: this.options.nick,\n }),\n );\n // Whatever was still waiting in Pkarr for the peer's next poll goes now.\n for (const message of this.session.takeUnacknowledged()) {\n channel.send(encodeControl({ t: \"m\", ts: message.t, m: message.m }));\n }\n this.options.events?.onPresence?.(this.presence);\n for (const waiter of this.openWaiters.splice(0)) waiter.resolve();\n }\n\n /**\n * Pings the paired peer; a session that stopped answering is closed, so it can be dialled again. A peer\n * that said in its offer that it answers pings counts from the open; any other counts once it answered\n * one (an app that never does is never cut off for it).\n */\n private startLiveness(channel: FrameChannel, peerAnswersPings = false): void {\n this.stopLiveness();\n this.peerAnswersPings = peerAnswersPings;\n // One ping at the open, not counted as missed: the round trip is known at once, not 15 s later.\n const ping = () => {\n this.pingSentAt = Date.now(); channel.send(JSON.stringify({ t: \"paired-ping\" }));\n // An app that never answers pings (an older one) would cost a read of the relays at each.\n if (!this.peerAnswersPings || this.pongWait) return;\n this.pongWait = setTimeout(() => {\n this.pongWait = null;\n if (this.channel !== channel || this.stopped) return;\n traceLink(this.myPubKeyZ32, \"pong-late\", { unanswered: this.unansweredPings });\n this.session.pollNow();\n }, PONG_WAIT_MS);\n };\n if (peerAnswersPings) try { ping(); } catch { /* closing: the timer below finds out */ }\n this.livenessTimer = setInterval(() => {\n if (this.channel !== channel) { this.stopLiveness(); return; }\n if (this.peerAnswersPings && this.unansweredPings >= LIVENESS_MISSED_PINGS) { this.dropDeadSession(channel); return; }\n this.unansweredPings++;\n try { ping(); } catch { this.dropDeadSession(channel); }\n }, LIVENESS_PING_MS);\n }\n private stopLiveness(): void {\n if (this.livenessTimer) clearInterval(this.livenessTimer);\n this.livenessTimer = null;\n this.heardFromPeer();\n this.unansweredPings = 0;\n this.peerAnswersPings = false;\n this.pingSentAt = 0;\n this.rtt = undefined;\n }\n /** Something came back on the session: the ping's wait is over. */\n private heardFromPeer(): void {\n if (this.pongWait) clearTimeout(this.pongWait);\n this.pongWait = null;\n }\n private dropDeadSession(channel: FrameChannel): void {\n this.stopLiveness();\n if (this.channel !== channel) return;\n // Everything of the dead session goes (channel, peer connection), as a hang-up would.\n this.disconnect();\n this.peerLost(\"liveness\");\n }\n\n /**\n * A live session ended without this side ending it (its channel closed, or its liveness gave up): tried again at\n * once, and the contact is watched closely for a while, since an app that went away is usually back in seconds.\n */\n private peerLost(why: string): void {\n if (this.stopped || this.leaving || this.yielding) return;\n traceLink(this.myPubKeyZ32, \"peer-lost\", { why });\n this.lostUntil = Date.now() + WATCH_PEER_MS;\n this.autoConnectFailures = 0;\n this.lastAutoConnectAt = 0;\n this.session.watchPeer();\n this.maybeAutoConnect(this.presence);\n }\n\n /**\n * The contact said goodbye on the live session: its app is closing (and, often, starting again). The session ends now\n * rather than when liveness gives up, and the contact is watched closely: the side that dials does so once a newer\n * packet shows it back, the other looks fast for its dial or offer.\n */\n private peerDeparted(channel: FrameChannel): void {\n if (this.channel !== channel || this.stopped) return;\n traceLink(this.myPubKeyZ32, \"peer-departed\", {});\n this.departed = { packet: this.session.peerPresence.lastPacketAt, until: Date.now() + WATCH_PEER_MS };\n this.disconnect();\n this.peerLost(\"bye\");\n }\n\n /**\n * A packet of the contact's not seen before, fresh, while nothing is live, soon after a live session ended: it is back\n * (its app restarted), so the wait that the attempts since built up is over, and the next dial goes now. Once per\n * loss: a contact that is there but unreachable publishes often, and the wait between attempts is what spares the\n * relays then.\n */\n private peerPacketArrived(presence: PeerPresence): void {\n const before = this.peerPacketSeen;\n this.peerPacketSeen = presence.lastPacketAt;\n if (!before || presence.lastPacketAt === before || !presence.online || this.dialing || this.channel) return;\n if (!this.options.pairing?.credentials.peerKey || Date.now() - presenceSeenAt(presence) >= EXPECT_PEER_MS) return;\n if (Date.now() > this.lostUntil) return;\n this.lostUntil = 0;\n traceLink(this.myPubKeyZ32, \"peer-back\", {});\n this.autoConnectFailures = 0;\n this.lastAutoConnectAt = 0;\n }\n\n /**\n * This app is going away (it quits, the page closes): the contact hears it on the live session now, rather than when\n * its liveness gives up about a minute later, and watches for this app to come back. Older apps drop the frame (it\n * carries no id). Synchronous, so it goes before anything else an app does on its way out.\n */\n depart(): void {\n // Going: nothing is dialled or answered from now on, whatever the contact does in the moments left.\n this.leaving = true;\n if (!this.channel || this.paired?.state.status !== \"ready\") return;\n traceLink(this.myPubKeyZ32, \"depart\", {});\n // A call still on is hung up first: the goodbye ends the contact's session, so a hang-up after it never arrives,\n // and the contact's call went on until its media gave up (half a minute).\n const hangUp = this.supportsCalls ? this.pairedCalls.hangUp() : null;\n if (hangUp) try { this.channel.send(JSON.stringify(hangUp)); } catch { /* closing already */ }\n try { this.channel.send(JSON.stringify({ t: \"paired-bye\" })); } catch { /* closing already: the contact's liveness finds out */ }\n }\n\n /** A link made just now for a peer that is about to show up (a group's entry session): look fast for a while. */\n expectPeer(): void {\n this.session.expectPeer();\n }\n\n /**\n * Someone is here again (the chat was opened, the window came back): look now, and try at once rather\n * than after the wait between failed attempts.\n */\n wake(): void {\n this.autoConnectFailures = 0;\n this.lastAutoConnectAt = 0;\n this.session.pollNow();\n if (!this.channel) {\n // The side that does not dial makes sure the other one sees it here, fresh — unless it just did:\n // a second packet right behind the first is one relays hold back for seconds.\n if (!this.session.publishedRecently(RECENTLY_PUBLISHED_MS)) void this.session.refreshAdvertisement();\n this.maybeAutoConnect(this.presence);\n }\n }\n\n private detach(): void {\n this.stopLiveness();\n const wasNative = !!this.activeBinding;\n this.applicationOpen = false;\n this.dht?.setLive(false);\n // A replacement still authenticating may yet carry the chat. Without one, the session a plan meant to move is\n // gone, and so is the plan: its dial must not go on and open a session of its own, or settle the next one.\n if (!this.candidate) { this.cancelCandidate(); this.switcher.stop(); }\n this.activeBinding = undefined;\n this.paired?.stop();\n this.paired = null;\n this.pairedPending.clear();\n this.httpHost?.closeAll();\n this.httpClient?.close();\n this.files?.closeAll();\n this.pairedFiles?.closeAll();\n this.pairedFiles = null;\n this.pairedHttp?.close();\n this.pairedHttp = null;\n this.channel = this.httpHost = this.httpClient = this.files = null;\n this.peerServicesOverride = null;\n this.peerNickOverride = null;\n this.sessionCapabilities.reset();\n this.typingSender.reset(); this.typingReceiver.clear();\n this.emitFilesSession();\n if (this.peerGroupVersions) { this.peerGroupVersions = null; this.options.events?.onGroupsSupport?.(false); }\n this.session.setDataLinkOpen(false);\n if (wasNative) this.options.events?.onDataLinkState?.(\"idle\");\n this.options.events?.onPresence?.(this.presence);\n }\n\n private rejectWaiters(error: Error): void {\n for (const waiter of this.openWaiters.splice(0)) waiter.reject(error);\n }\n\n private handleFrame(data: string | Uint8Array): void {\n if (typeof data !== \"string\") {\n const chunk = decodeChunk(data);\n if (!chunk) return;\n if (chunk.kind === CHUNK_KIND.requestBody) this.httpHost?.handleChunk(chunk);\n else if (chunk.kind === CHUNK_KIND.responseBody) this.httpClient?.handleChunk(chunk);\n else this.files?.handleChunk(chunk);\n return;\n }\n\n const frame = decodeControl(data);\n if (!frame) return;\n switch (frame.t) {\n case \"hello\":\n case \"svc\": {\n const services = servicesFromWire(frame.svc);\n if (services) {\n this.peerServicesOverride = services;\n this.options.events?.onPresence?.(this.presence);\n }\n break;\n }\n case \"m\":\n this.options.events?.onMessage?.({ text: frame.m, timestamp: frame.ts, via: \"datalink\" });\n break;\n case \"call\":\n this.options.events?.onCallSignal?.(heardCallSignal(frame.s, Date.now()));\n break;\n case \"req\":\n this.httpHost?.handleRequest(frame);\n break;\n case \"res\":\n this.httpClient?.handleResponse(frame);\n break;\n case \"file\":\n this.files?.handleFile(frame);\n break;\n case \"pay-req\":\n this.options.events?.onPaymentRequest?.({\n id: frame.id,\n timestamp: frame.ts,\n amount: { value: frame.v, asset: frame.u },\n memo: frame.memo,\n endpoints: frame.e,\n });\n break;\n case \"pay\":\n this.options.events?.onPayment?.({\n id: frame.id,\n timestamp: frame.ts,\n requestId: frame.rid,\n amount: { value: frame.v, asset: frame.u },\n memo: frame.memo,\n endpoint: frame.e,\n });\n break;\n case \"pay-res\":\n this.options.events?.onPaymentResult?.({ id: frame.id, ok: frame.ok, credited: frame.v, error: frame.err, ...(frame.c ? { closed: true } : {}) });\n break;\n case \"rst\":\n if (frame.d === \"q\") this.httpHost?.handleReset(frame);\n else if (frame.d === \"s\") this.httpClient?.handleReset(frame);\n else this.files?.handleReset(frame);\n break;\n case \"ping\":\n try {\n this.channel?.send(encodeControl({ t: \"pong\", ts: frame.ts }));\n } catch {\n // channel closing\n }\n break;\n case \"pong\":\n break;\n }\n }\n}\n","import type { WireEndpoint } from \"./frames\";\n\n/**\n * Payments between two linked peers. Ghostly does not move money itself: it\n * carries payment requests, in-band payments such as ecash tokens, and\n * receipts over the data link, and a platform wallet executes them.\n *\n * Vocabulary and identifiers follow Pubky's Paykit so that publishing the same\n * endpoints through Paykit later is a change of transport, not of model:\n * an endpoint is an identifier plus a payload, an amount is decimal text plus\n * an asset.\n */\nexport const ENDPOINT = {\n /** Payload: a BOLT11 invoice. */\n bolt11: \"btc-lightning-bolt11\",\n /**\n * Payload in a request: JSON `{ \"mints\": [\"https://…\"] }`, the mints the\n * payee accepts ecash from. Payload in a payment: a `cashuB…` token.\n */\n cashu: \"cashu\",\n arkade: \"btc-arkade/1\",\n /** Second's Ark (Bark). Payload in a request: a payment target; in a payment: a receipt hint. */\n bark: \"btc-bark/1\",\n usdt: \"usdt-erc20/1\",\n /** On-chain Bitcoin. Payload in a request: a payment target (a fresh address); in a payment: a receipt hint `{ txid }`. */\n bitcoin: \"btc-onchain/1\",\n /**\n * Fedimint ecash. Payload in a request: JSON `{ \"federations\": [\"<federation id>\", …] }`, the federations the\n * payee has joined and takes ecash from. Payload in a payment: the notes themselves (out-of-band notes, a\n * bearer string only the federation that issued them redeems).\n */\n fedimint: \"fedimint-ecash/1\",\n /**\n * Spark, wallet to wallet. Payload in a request: a payment target whose address is a Spark invoice made for\n * that request; in a payment: a receipt hint `{ id }` (the payer's transfer id).\n */\n spark: \"btc-spark/1\",\n} as const;\n\nexport interface PaymentAmount {\n /** Decimal text; whole numbers for `sat`. */\n value: string;\n asset: string;\n}\n\nexport interface PaymentRequest {\n id: string;\n timestamp: number;\n amount: PaymentAmount;\n memo?: string;\n endpoints: WireEndpoint[];\n /** The ask this request answers, when the contact asked to pay (see {@link PaymentAsk}). */\n ask?: string;\n /** Real money or test coins: only a wallet of this network pays it. Absent from older apps. */\n network?: \"mainnet\" | \"testnet\";\n}\n\n/**\n * \"I want to pay you this much this way\": sent by a payer who has no address to pay to (Ark, USDT).\n * The payee's app answers with an ordinary payment request carrying `ask`, which the payer\n * then reviews and approves like any other.\n */\nexport interface PaymentAsk {\n id: string;\n timestamp: number;\n amount: PaymentAmount;\n method: \"arkade\" | \"usdt\" | \"bark\" | \"bitcoin\" | \"fedimint\" | \"spark\";\n memo?: string;\n /** The network the payer pays from. Absent from older apps. */\n network?: \"mainnet\" | \"testnet\";\n}\n\nexport interface Payment {\n id: string;\n timestamp: number;\n requestId?: string;\n amount: PaymentAmount;\n memo?: string;\n endpoint: WireEndpoint;\n}\n\nexport interface PaymentResult {\n /** Id of the payment or request this is about. */\n id: string;\n ok: boolean;\n /** Amount credited when it differs from the amount sent. */\n credited?: string;\n error?: string;\n /**\n * On a request (`ok` false): its payee closed it for good, and a payment made to it now is lost. Only the payee of a\n * request says it; the payer stops offering to pay it.\n */\n closed?: boolean;\n}\n\nexport function cashuRequestPayload(mints: string[]): string {\n return JSON.stringify({ mints });\n}\n\n/** Mints named by a `cashu` endpoint of a request. Anything malformed yields none. */\nexport function parseCashuRequestPayload(payload: string): string[] {\n try {\n const { mints } = JSON.parse(payload) as { mints?: unknown };\n if (!Array.isArray(mints)) return [];\n return mints.filter((m): m is string => typeof m === \"string\" && /^https?:\\/\\//.test(m)).slice(0, 8);\n } catch {\n return [];\n }\n}\n\n/** A Fedimint federation id: 32 bytes, lowercase hex. */\nexport const isFederationId = (value: unknown): value is string => typeof value === \"string\" && /^[0-9a-f]{64}$/.test(value);\n\nexport function fedimintRequestPayload(federations: string[]): string {\n return JSON.stringify({ federations });\n}\n\n/** Federations named by a `fedimint-ecash/1` endpoint of a request. Anything malformed yields none. */\nexport function parseFedimintRequestPayload(payload: string): string[] {\n try {\n const { federations } = JSON.parse(payload) as { federations?: unknown };\n if (!Array.isArray(federations)) return [];\n return [...new Set(federations.filter(isFederationId))].slice(0, 8);\n } catch {\n return [];\n }\n}\n\nexport function findEndpoint(endpoints: WireEndpoint[], identifier: string): string | undefined {\n return endpoints.find(([id]) => id === identifier)?.[1];\n}\n","/**\n * Reads what a Lightning invoice (BOLT11) asks for, so a pasted invoice can be\n * shown as an amount and a description instead of a wall of characters. It\n * checks the bech32 checksum but not the signature: this is for display, the\n * wallet that pays is the one that must verify.\n */\nexport interface Bolt11Invoice {\n /** The invoice as it should be handed to a wallet: lower case, no `lightning:` prefix. */\n invoice: string;\n network: \"bitcoin\" | \"testnet\" | \"signet\" | \"regtest\";\n /** Null for an invoice that lets the payer choose the amount. */\n amountMsat: bigint | null;\n /** Rounded up to whole sats; null when the payer chooses. */\n amountSat: number | null;\n /** Seconds since the UNIX epoch. */\n createdAt: number;\n expiresAt: number;\n description?: string;\n /** The `h` tag: sha256 of a description the invoice does not carry (LNURL-pay invoices commit to their metadata this way). */\n descriptionHash?: string;\n paymentHash?: string;\n}\n\nconst CHARSET = \"qpzry9x8gf2tvdw0s3jn54khce6mua7l\";\nconst GENERATOR = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3];\nconst NETWORKS = { bc: \"bitcoin\", tb: \"testnet\", tbs: \"signet\", bcrt: \"regtest\" } as const;\nconst MSAT_PER_UNIT: Record<string, bigint> = { \"\": 100_000_000_000n, m: 100_000_000n, u: 100_000n, n: 100n };\nconst SIGNATURE_WORDS = 104;\nconst CHECKSUM_WORDS = 6;\nconst TIMESTAMP_WORDS = 7;\nconst DEFAULT_EXPIRY = 3600;\nconst MAX_LENGTH = 7089;\n\n/** Finds an invoice inside free text. The match still has to pass `decodeBolt11`. */\nexport const BOLT11_PATTERN = /(?:lightning:)?(ln(?:bcrt|tbs|bc|tb)[0-9]*[munp]?1[02-9ac-hj-np-z]{100,})/i;\n\nfunction polymod(values: number[]): number {\n let chk = 1;\n for (const value of values) {\n const top = chk >>> 25;\n chk = ((chk & 0x1ffffff) << 5) ^ value;\n for (let i = 0; i < 5; i++) if ((top >>> i) & 1) chk ^= GENERATOR[i];\n }\n return chk >>> 0;\n}\n\nfunction expandHrp(hrp: string): number[] {\n const high: number[] = [];\n const low: number[] = [];\n for (const char of hrp) {\n high.push(char.charCodeAt(0) >>> 5);\n low.push(char.charCodeAt(0) & 31);\n }\n return [...high, 0, ...low];\n}\n\nfunction wordsToInt(words: number[]): number {\n return words.reduce((value, word) => value * 32 + word, 0);\n}\n\n/** Regroups 5-bit words into bytes, dropping the padding bits at the end. */\nfunction wordsToBytes(words: number[]): Uint8Array {\n const bytes: number[] = [];\n let buffer = 0;\n let bits = 0;\n for (const word of words) {\n buffer = (buffer << 5) | word;\n bits += 5;\n if (bits >= 8) {\n bits -= 8;\n bytes.push((buffer >>> bits) & 0xff);\n }\n }\n return new Uint8Array(bytes);\n}\n\nexport function decodeBolt11(text: string): Bolt11Invoice | null {\n const invoice = text.trim().toLowerCase().replace(/^lightning:/, \"\");\n if (invoice.length > MAX_LENGTH) return null;\n const separator = invoice.lastIndexOf(\"1\");\n if (separator < 4) return null;\n\n const hrp = invoice.slice(0, separator);\n const prefix = /^ln(bcrt|tbs|bc|tb)(?:([0-9]+)([munp]?))?$/.exec(hrp);\n if (!prefix) return null;\n\n const words: number[] = [];\n for (const char of invoice.slice(separator + 1)) {\n const word = CHARSET.indexOf(char);\n if (word < 0) return null;\n words.push(word);\n }\n if (words.length < TIMESTAMP_WORDS + SIGNATURE_WORDS + CHECKSUM_WORDS) return null;\n if (polymod([...expandHrp(hrp), ...words]) !== 1) return null;\n\n let amountMsat: bigint | null = null;\n if (prefix[2] !== undefined) {\n const digits = BigInt(prefix[2]);\n if (prefix[3] === \"p\") {\n // A pico-bitcoin is a tenth of a millisatoshi, so only multiples of ten exist.\n if (digits % 10n !== 0n) return null;\n amountMsat = digits / 10n;\n } else {\n amountMsat = digits * MSAT_PER_UNIT[prefix[3]];\n }\n }\n\n const data = words.slice(0, -(SIGNATURE_WORDS + CHECKSUM_WORDS));\n const createdAt = wordsToInt(data.slice(0, TIMESTAMP_WORDS));\n let expiry = DEFAULT_EXPIRY;\n let description: string | undefined;\n let paymentHash: string | undefined;\n let descriptionHash: string | undefined;\n\n for (let at = TIMESTAMP_WORDS; at + 3 <= data.length; ) {\n const tag = CHARSET[data[at]];\n const length = data[at + 1] * 32 + data[at + 2];\n const field = data.slice(at + 3, at + 3 + length);\n if (field.length < length) return null;\n at += 3 + length;\n\n if (tag === \"d\") {\n description = new TextDecoder().decode(wordsToBytes(field)).trim() || undefined;\n } else if (tag === \"x\") {\n expiry = wordsToInt(field);\n } else if (tag === \"p\" && length === 52) {\n paymentHash = [...wordsToBytes(field)].map((byte) => byte.toString(16).padStart(2, \"0\")).join(\"\");\n } else if (tag === \"h\" && length === 52) {\n descriptionHash = [...wordsToBytes(field)].map((byte) => byte.toString(16).padStart(2, \"0\")).join(\"\");\n }\n }\n\n return {\n invoice,\n network: NETWORKS[prefix[1] as keyof typeof NETWORKS],\n amountMsat,\n amountSat: amountMsat === null ? null : Number((amountMsat + 999n) / 1000n),\n createdAt,\n expiresAt: createdAt + expiry,\n description,\n descriptionHash,\n paymentHash,\n };\n}\n\n/** The first valid invoice in a chat message, with whatever else the message says. */\nexport function findBolt11(text: string): { invoice: Bolt11Invoice; rest: string } | null {\n const match = BOLT11_PATTERN.exec(text);\n if (!match) return null;\n const invoice = decodeBolt11(match[1]);\n if (!invoice) return null;\n return { invoice, rest: (text.slice(0, match.index) + text.slice(match.index + match[0].length)).trim() };\n}\n","/**\n * The engine's known errors: a stable code each, and its English text with the values in it. The engine throws\n * `engineError(code, values)`; the text is the one it always wrote, so the CLI and every log keep printing the same.\n * The app's pages get only the text (it crosses the engine's RPC and is kept on payments and operations as a string),\n * and `parseEngineError` finds the code and the values back in it, to say it in the app's language. A text that is\n * none of these (a mint's own words, a provider's) stays as it is.\n *\n * `{name}` in a template is a value: a host or domain (no spaces), a network (\"Mainnet\" or \"Testnet\"), a chain's name,\n * or an amount (digits, with the separators of whatever locale wrote it).\n */\nexport const ENGINE_ERRORS = {\n // Reaching a mint, a server, the network.\n mintUnreachable: \"Could not reach {host}. Check the address: it should be a Cashu mint.\",\n lnurlUnreachable: \"Could not reach {host}. It may be down, or not allow web apps to read it (CORS).\",\n hostUnreachable: \"Could not reach {host}\",\n networkUnreachable: \"Could not reach the network\",\n hostTimedOut: \"{host} did not answer in time\",\n networkTimedOut: \"The network did not answer in time\",\n // Cashu mints.\n testMintOnMainnet: \"This is a test mint, and its sats are worth nothing: add it to a Testnet Cashu wallet\",\n realMintOnTestnet: \"This mint holds real sats: add it to a Mainnet Cashu wallet\",\n mintHoldsSats: \"Move your sats out of this mint before removing it\",\n lastMint: \"This is the last mint of your {network} Cashu wallet: remove the wallet to remove it\",\n invalidMintUrl: \"That is not a valid mint URL\",\n mintNotHttps: \"Mints must use https\",\n notEnoughSats: \"Not enough sats in your wallet\",\n noSharedMint: \"You share no mint with this contact\",\n ecashAlreadySpent: \"This ecash was already spent somewhere else\",\n reviewedEcashSpent: \"The ecash for this payment was already spent somewhere else. Nothing was sent, and your balance now shows what the mint still holds.\",\n reviewedSatsGone: \"The sats this payment was reviewed with went to another payment. Nothing was sent: review it again.\",\n // Lightning.\n lightningFeeTooHigh: \"The Lightning fee ({fee} sats) is too high\",\n invoiceExpired: \"That invoice has expired\",\n lnurlExactly: \"{domain} asks for exactly {amount} sats\",\n lnurlRange: \"{domain} takes between {min} and {max} sats\",\n // A reviewed payment, from its review to its outcome (paymentAdapters/coordinator.ts, persistence.ts, node.ts).\n feeAboveLimit: \"The fee exceeds your limit\",\n cashuFeeAboveLimit: \"Cashu fee exceeds your limit\",\n invalidMaxFee: \"Invalid maximum fee\",\n methodUnavailable: \"This payment method is unavailable\",\n unknownPaymentIntent: \"Unknown payment intent\",\n paymentExpired: \"Payment request expired or has an invalid expiry\",\n requestHasPayment: \"This request already has a payment. Reconcile it before trying again.\",\n requestPaymentInFlight: \"This request already has a payment; reconcile it instead\",\n requestNotAwaiting: \"This request is no longer awaiting payment. Check its status before spending.\",\n reconnectBeforeApprove: \"Reconnect the data link before approving. Your review was saved.\",\n cannotSubmitAgain: \"This payment cannot be submitted again. Reconcile its existing transaction.\",\n alreadySubmitted: \"This payment was already submitted or could not be saved\",\n sentAfterReview: \"A payment from this wallet was sent after this review was made. Create a new review\",\n cannotCancelSubmitted: \"A submitted payment cannot be cancelled; reconcile it instead\",\n outcomeUnknown: \"Outcome unknown. Check the existing payment; do not send another.\",\n notYetConfirmed: \"Not yet confirmed. No second payment was sent.\",\n couldNotVerify: \"Could not verify this payment yet. No second payment was sent.\",\n paymentTakenBack: \"This payment was taken back. The sats are in your wallet.\",\n // Real money and test coins.\n realMoneyUnconfirmed: \"This pays with real money: confirm it with Send real money first. Nothing was sent.\",\n mainnetPaymentOnTestnet: \"This is a Mainnet payment (real money): a Testnet wallet never pays it. Use a Mainnet wallet.\",\n testnetPaymentOnMainnet: \"This is a Testnet payment (test coins): a Mainnet wallet never pays it. Use a Testnet wallet.\",\n // USDT.\n usdtRpcUnavailable: \"USDT RPC unavailable\",\n usdtNotEnoughTokens: \"Insufficient token balance\",\n usdtNotEnoughGas: \"Insufficient ETH for gas\",\n usdtGasAboveLimit: \"Estimated maximum gas exceeds your limit\",\n usdtNeedsSepoliaGas: \"This needs a little Sepolia ETH for gas first\",\n // Fedimint.\n fedimintNotInvite: \"That is not a Fedimint invite code: it starts with fed1\",\n fedimintAlreadyJoined: \"You already joined this federation\",\n fedimintConnecting: \"Wait for the federation to connect\",\n fedimintNotEnough: \"Not enough in this federation\",\n fedimintRealOnTestnet: \"This federation holds real bitcoin: it belongs in a Mainnet Fedimint wallet\",\n fedimintTestOnMainnet: \"This federation is on {chain}, a test network: it belongs in a Testnet Fedimint wallet\",\n} as const;\n\nexport type EngineErrorCode = keyof typeof ENGINE_ERRORS;\nexport type EngineErrorValues = Record<string, string | number>;\n\n/**\n * An error the engine knows: `engineCode` travels with it inside the engine, the text everywhere. Not `code`: the\n * CLI reads a `code` as its own exit code, and Node's errors carry one of theirs.\n */\nexport class EngineError extends Error {\n constructor(readonly engineCode: EngineErrorCode, readonly values: EngineErrorValues = {}) {\n super(engineText(engineCode, values));\n }\n}\n\n/** The English text of `code` with its values in it, exactly as the engine writes it. */\nexport function engineText(code: EngineErrorCode, values: EngineErrorValues = {}): string {\n return ENGINE_ERRORS[code].replace(/\\{(\\w+)\\}/g, (_, name: string) => (name in values ? String(values[name]) : `{${name}}`));\n}\n\nexport const engineError = (code: EngineErrorCode, values?: EngineErrorValues): EngineError => new EngineError(code, values);\n\n/** What each kind of value looks like in a text: never a space in a host, a network by its name, an amount as digits. */\nconst VALUE: Record<string, string> = {\n host: \"(\\\\S+)\", domain: \"(\\\\S+)\", chain: \"(\\\\S+)\", network: \"(Mainnet|Testnet)\",\n amount: \"(\\\\d[\\\\d.,\\\\u00a0\\\\u202f' ]*?)\", fee: \"(\\\\d[\\\\d.,\\\\u00a0\\\\u202f' ]*?)\", min: \"(\\\\d[\\\\d.,\\\\u00a0\\\\u202f' ]*?)\", max: \"(\\\\d[\\\\d.,\\\\u00a0\\\\u202f' ]*?)\",\n};\nconst escape = (text: string) => text.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\nconst PATTERNS = (Object.keys(ENGINE_ERRORS) as EngineErrorCode[]).map((code) => {\n const names: string[] = [];\n const source = ENGINE_ERRORS[code].split(/(\\{\\w+\\})/).map((part) => {\n const value = /^\\{(\\w+)\\}$/.exec(part);\n if (!value) return escape(part);\n names.push(value[1]);\n return VALUE[value[1]] ?? \"(.+?)\";\n }).join(\"\");\n return { code, names, pattern: new RegExp(`^${source}$`) };\n});\n\n/** The code and values of a text the engine wrote, or null for any other text (it is then shown as it is). */\nexport function parseEngineError(text: string): { code: EngineErrorCode; values: Record<string, string> } | null {\n const trimmed = text.trim();\n for (const { code, names, pattern } of PATTERNS) {\n const match = pattern.exec(trimmed);\n if (match) return { code, values: Object.fromEntries(names.map((name, i) => [name, match[i + 1]])) };\n }\n return null;\n}\n","import { engineError } from \"./engineErrors\";\nimport { bech32 } from \"@scure/base\";\nimport { sha256 } from \"@noble/hashes/sha2.js\";\nimport { decodeBolt11, type Bolt11Invoice } from \"./bolt11\";\n\n/**\n * Paying a Lightning address or an LNURL: the payer's side of\n * [LUD-16](https://github.com/lnurl/luds/blob/luds/16.md) (`name@domain`),\n * [LUD-06](https://github.com/lnurl/luds/blob/luds/06.md) (`payRequest`),\n * [LUD-01](https://github.com/lnurl/luds/blob/luds/01.md) (`lnurl1…` bech32),\n * [LUD-17](https://github.com/lnurl/luds/blob/luds/17.md) (`lnurlp://`),\n * [LUD-12](https://github.com/lnurl/luds/blob/luds/12.md) (a comment) and\n * [LUD-09](https://github.com/lnurl/luds/blob/luds/09.md) (a success message).\n *\n * Resolving reveals the request to the service's domain (it is an HTTPS fetch), so the domain is part of\n * what a person is shown before anything is fetched. Everything the service answers is checked: the\n * amounts it allows, the invoice it returns (its amount, and that it commits to the metadata the person\n * saw), and how much of it is read at all. Paying then goes through the active Lightning source like any\n * other invoice. Receiving on a Lightning address needs a server the person runs; it is not here.\n */\n\nexport interface LightningDestination {\n kind: \"address\" | \"lnurl\";\n /** What was pasted, normalised: a lower-cased `name@domain`, or the `lnurl…` / `lnurlp://…` text. */\n text: string;\n /** Where the pay request is fetched from. */\n url: string;\n /** The host that learns about the request. */\n domain: string;\n /** LUD-16: the part before the `@`. */\n name?: string;\n}\n\nexport interface LnurlSuccessAction {\n tag: \"message\" | \"url\";\n message?: string;\n description?: string;\n url?: string;\n}\n\n/** A service's `payRequest`, checked. Amounts in `minSendable`/`maxSendable` are millisats, `minSat`/`maxSat` whole sats. */\nexport interface LnurlPayParams {\n destination: LightningDestination;\n callback: string;\n /** The host the invoice is asked from, when it is not the one that answered. */\n callbackDomain: string;\n minSendable: number;\n maxSendable: number;\n minSat: number;\n maxSat: number;\n /** The metadata string as the service sent it: what the invoice must commit to. */\n metadata: string;\n /** sha256 of `metadata`, hex: the invoice's `h` tag when it has one. */\n descriptionHash: string;\n /** The `text/plain` entry: what the payment is for, as the service says. */\n description: string;\n /** The `text/identifier` or `text/email` entry, when the service names an address. */\n identifier?: string;\n /** LUD-12: how long a comment may be; 0 when none is taken. */\n commentAllowed: number;\n}\n\nexport interface LnurlFetchOptions {\n fetch?: typeof fetch;\n signal?: AbortSignal;\n timeoutMs?: number;\n maxBytes?: number;\n}\n\nexport const LNURL_LIMITS = { timeoutMs: 15_000, maxBytes: 64 * 1024, maxUrlLength: 2048, maxMetadataLength: 8 * 1024, maxComment: 2000 } as const;\n\nconst ADDRESS = /^([a-z0-9._+-]{1,64})@([a-z0-9-]+(?:\\.[a-z0-9-]+)*(?::\\d{1,5})?)$/i;\nconst LNURL_BECH32 = /^lnurl1[02-9ac-hj-np-z]{6,}$/i;\n/** A Lightning address or LNURL inside free text: `lightning:` and `⚡` prefixes are tolerated. */\nexport const LIGHTNING_DESTINATION_PATTERN = /(?:lightning:|lnurlp:\\/\\/|lnurl1|[a-z0-9._+-]{1,64}@[a-z0-9-]+(?:\\.[a-z0-9-]+)+)/i;\n\nconst isLoopback = (hostname: string) => [\"localhost\", \"127.0.0.1\", \"[::1]\"].includes(hostname);\n\n/** HTTPS, or plain HTTP only for a hidden service or a test server on this machine; never credentials. */\nfunction acceptUrl(text: string): URL {\n if (text.length > LNURL_LIMITS.maxUrlLength) throw new Error(\"That LNURL is too long\");\n let url: URL;\n try { url = new URL(text); } catch { throw new Error(\"That LNURL does not point to a valid address\"); }\n const plainAllowed = url.hostname.endsWith(\".onion\") || isLoopback(url.hostname);\n if (url.protocol !== \"https:\" && !(url.protocol === \"http:\" && plainAllowed)) throw new Error(\"Lightning addresses and LNURLs must use HTTPS\");\n if (url.username || url.password) throw new Error(\"That LNURL carries credentials, which is not allowed\");\n return url;\n}\n\n/** Reads what was pasted as a Lightning address or an LNURL; null when it is neither. Throws when it is one, but a bad one. */\nexport function parseLightningDestination(input: string): LightningDestination | null {\n const text = input.trim().replace(/^(lightning:|⚡\\s*)/i, \"\").trim();\n if (!text) return null;\n if (LNURL_BECH32.test(text)) {\n let decoded: { prefix: string; words: number[] };\n try { decoded = bech32.decode(text.toLowerCase() as `${string}1${string}`, LNURL_LIMITS.maxUrlLength); }\n catch { throw new Error(\"That LNURL is not valid\"); }\n const url = acceptUrl(new TextDecoder().decode(bech32.fromWords(decoded.words)));\n return { kind: \"lnurl\", text: text.toLowerCase(), url: url.href, domain: url.host };\n }\n if (/^lnurlp:\\/\\//i.test(text)) {\n const bare = text.slice(\"lnurlp://\".length);\n const host = bare.split(/[/?#]/)[0].toLowerCase();\n const scheme = host.endsWith(\".onion\") || isLoopback(host.replace(/:\\d+$/, \"\")) ? \"http\" : \"https\";\n const url = acceptUrl(`${scheme}://${bare}`);\n return { kind: \"lnurl\", text: `lnurlp://${bare}`, url: url.href, domain: url.host };\n }\n const address = ADDRESS.exec(text);\n if (!address) return null;\n const name = address[1].toLowerCase(), domain = address[2].toLowerCase();\n const hostname = domain.replace(/:\\d+$/, \"\");\n if (!hostname.includes(\".\") && !isLoopback(hostname)) return null;\n const scheme = hostname.endsWith(\".onion\") || isLoopback(hostname) ? \"http\" : \"https\";\n const url = acceptUrl(`${scheme}://${domain}/.well-known/lnurlp/${name}`);\n return { kind: \"address\", text: `${name}@${domain}`, url: url.href, domain: url.host, name };\n}\n\n/** The first Lightning address or LNURL in a message, with whatever else the message says. */\nexport function findLightningDestination(text: string): { destination: LightningDestination; rest: string } | null {\n const match = LIGHTNING_DESTINATION_PATTERN.exec(text);\n if (!match) return null;\n // Take the whole token the match starts in: an lnurl runs to the next space, an address stops at its domain.\n const start = match.index, end = text.slice(start).search(/\\s|$/);\n // The sentence's closing punctuation stays out, taken off one by one: `/[…]+$/` retries from every character of a\n // long run that does not reach the end, which is quadratic.\n let stop = start + end;\n while (stop > start && \"),.;:!?\".includes(text[stop - 1])) stop--;\n const token = text.slice(start, stop);\n let destination: LightningDestination | null;\n try { destination = parseLightningDestination(token); } catch { return null; }\n if (!destination) return null;\n return { destination, rest: (text.slice(0, start) + text.slice(start + token.length)).trim() };\n}\n\nconst hex = (bytes: Uint8Array) => Array.from(bytes, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\nconst reason = (value: unknown) => (typeof value === \"string\" ? value.replace(/\\s+/g, \" \").slice(0, 200) : \"\");\nconst isRecord = (value: unknown): value is Record<string, unknown> => !!value && typeof value === \"object\" && !Array.isArray(value);\n\nasync function readBounded(response: Response, maxBytes: number): Promise<string> {\n const reader = response.body?.getReader?.();\n if (!reader) {\n const text = await response.text();\n if (text.length > maxBytes) throw new Error(\"The server's answer is too large\");\n return text;\n }\n const chunks: Uint8Array[] = [];\n let size = 0;\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n size += value.byteLength;\n if (size > maxBytes) { await reader.cancel().catch(() => {}); throw new Error(\"The server's answer is too large\"); }\n chunks.push(value);\n }\n const all = new Uint8Array(size);\n let at = 0;\n for (const chunk of chunks) { all.set(chunk, at); at += chunk.byteLength; }\n return new TextDecoder().decode(all);\n}\n\n/** One GET to an LNURL service: bounded in time and size, no credentials, no referrer, and the answer must be JSON. */\nexport async function fetchLnurlJson(url: string, options: LnurlFetchOptions = {}): Promise<unknown> {\n const target = acceptUrl(url);\n const doFetch = options.fetch ?? globalThis.fetch;\n if (!doFetch) throw new Error(\"No way to reach the network here\");\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(new Error(\"The server took too long to answer\")), options.timeoutMs ?? LNURL_LIMITS.timeoutMs);\n const onAbort = () => controller.abort(options.signal?.reason);\n options.signal?.addEventListener(\"abort\", onAbort, { once: true });\n try {\n let response: Response;\n try {\n response = await doFetch(target.href, { signal: controller.signal, headers: { accept: \"application/json\" }, credentials: \"omit\", referrerPolicy: \"no-referrer\", redirect: \"follow\" });\n } catch {\n if (controller.signal.aborted) throw controller.signal.reason instanceof Error ? controller.signal.reason : new Error(\"The request was cancelled\");\n throw engineError(\"lnurlUnreachable\", { host: target.host });\n }\n // A redirect may not lead somewhere plain HTTP or with credentials.\n if (response.url) acceptUrl(response.url);\n const text = await readBounded(response, options.maxBytes ?? LNURL_LIMITS.maxBytes);\n let body: unknown;\n try { body = JSON.parse(text); } catch { throw new Error(`${target.host} did not answer with JSON${response.ok ? \"\" : ` (HTTP ${response.status})`}`); }\n if (isRecord(body) && body.status === \"ERROR\") throw new Error(`${target.host} refused: ${reason(body.reason) || \"no reason given\"}`);\n if (!response.ok) throw new Error(`${target.host} answered HTTP ${response.status}`);\n return body;\n } finally {\n clearTimeout(timer);\n options.signal?.removeEventListener(\"abort\", onAbort);\n }\n}\n\n/** Checks a service's `payRequest` answer (LUD-06), and reads what a person needs to see of it. */\nexport function parsePayParams(body: unknown, destination: LightningDestination): LnurlPayParams {\n if (!isRecord(body)) throw new Error(`${destination.domain} did not answer with a pay request`);\n if (body.tag !== \"payRequest\") throw new Error(`${destination.domain} answered with something other than a pay request${typeof body.tag === \"string\" ? ` (${reason(body.tag)})` : \"\"}`);\n if (typeof body.callback !== \"string\") throw new Error(\"The pay request has no callback\");\n const callback = acceptUrl(body.callback);\n const { minSendable, maxSendable, metadata, commentAllowed } = body;\n if (!Number.isSafeInteger(minSendable) || !Number.isSafeInteger(maxSendable) || (minSendable as number) <= 0 || (maxSendable as number) < (minSendable as number)) throw new Error(\"The pay request has invalid amount limits\");\n const minSat = Math.ceil((minSendable as number) / 1000), maxSat = Math.floor((maxSendable as number) / 1000);\n if (minSat > maxSat) throw new Error(\"The pay request only takes fractions of a sat, which cannot be paid here\");\n if (typeof metadata !== \"string\" || metadata.length > LNURL_LIMITS.maxMetadataLength) throw new Error(\"The pay request has no usable metadata\");\n let entries: unknown;\n try { entries = JSON.parse(metadata); } catch { throw new Error(\"The pay request's metadata is not JSON\"); }\n if (!Array.isArray(entries) || !entries.every((e) => Array.isArray(e) && typeof e[0] === \"string\")) throw new Error(\"The pay request's metadata is malformed\");\n const entry = (type: string) => (entries as unknown[][]).find((e) => e[0] === type)?.[1];\n const description = entry(\"text/plain\");\n if (typeof description !== \"string\") throw new Error(\"The pay request says nothing about what it is for (no text/plain metadata)\");\n const identifier = [entry(\"text/identifier\"), entry(\"text/email\")].find((v): v is string => typeof v === \"string\")?.toLowerCase();\n if (destination.kind === \"address\" && identifier && identifier !== destination.text) throw new Error(`${destination.domain} answered for ${identifier}, not ${destination.text}`);\n const comment = Number.isSafeInteger(commentAllowed) && (commentAllowed as number) > 0 ? Math.min(commentAllowed as number, LNURL_LIMITS.maxComment) : 0;\n return {\n destination, callback: callback.href, callbackDomain: callback.host,\n minSendable: minSendable as number, maxSendable: maxSendable as number, minSat, maxSat,\n metadata, descriptionHash: hex(sha256(new TextEncoder().encode(metadata))), description: description.slice(0, 280), identifier, commentAllowed: comment,\n };\n}\n\n/** Where to ask for the invoice of `amountSat` (and a comment, LUD-12). Throws when the amount or comment is not allowed. */\nexport function invoiceCallbackUrl(params: LnurlPayParams, amountSat: number, comment?: string): string {\n if (!Number.isSafeInteger(amountSat) || amountSat <= 0) throw new Error(\"Enter a whole number of sats\");\n if (amountSat < params.minSat || amountSat > params.maxSat) throw params.minSat === params.maxSat ? engineError(\"lnurlExactly\", { domain: params.destination.domain, amount: params.minSat.toLocaleString() }) : engineError(\"lnurlRange\", { domain: params.destination.domain, min: params.minSat.toLocaleString(), max: params.maxSat.toLocaleString() });\n const url = new URL(params.callback);\n url.searchParams.set(\"amount\", String(amountSat * 1000));\n const text = comment?.trim();\n if (text) {\n if (!params.commentAllowed) throw new Error(`${params.destination.domain} does not take a comment`);\n if (text.length > params.commentAllowed) throw new Error(`The comment can be at most ${params.commentAllowed} characters`);\n url.searchParams.set(\"comment\", text);\n }\n return url.href;\n}\n\n/**\n * Checks the invoice a service answered with (LUD-06): the amount asked, a payment hash, not expired, and\n * a commitment to the metadata the person was shown: the `h` tag is its sha256, or the invoice carries the\n * metadata itself as its description (the same commitment, unhashed).\n */\nexport function parseInvoiceCallback(body: unknown, params: LnurlPayParams, amountSat: number): { invoice: Bolt11Invoice; successAction?: LnurlSuccessAction } {\n if (!isRecord(body) || typeof body.pr !== \"string\") throw new Error(`${params.callbackDomain} did not answer with an invoice`);\n const invoice = decodeBolt11(body.pr);\n if (!invoice) throw new Error(`${params.callbackDomain} answered with an invoice that does not decode`);\n if (invoice.amountMsat !== BigInt(amountSat) * 1000n) throw new Error(`The invoice asks for ${invoice.amountSat === null ? \"any amount\" : `${invoice.amountSat.toLocaleString()} sats`}, not the ${amountSat.toLocaleString()} sats you chose`);\n if (!invoice.paymentHash) throw new Error(\"The invoice has no payment hash\");\n if (invoice.descriptionHash !== params.descriptionHash && invoice.description !== params.metadata) throw new Error(\"The invoice does not commit to what the service said the payment is for\");\n if (invoice.expiresAt * 1000 < Date.now()) throw new Error(\"The invoice the service answered with has already expired\");\n let successAction: LnurlSuccessAction | undefined;\n const action = body.successAction;\n if (isRecord(action)) {\n if (action.tag === \"message\" && typeof action.message === \"string\") successAction = { tag: \"message\", message: reason(action.message) };\n else if (action.tag === \"url\" && typeof action.url === \"string\" && /^https:\\/\\//i.test(action.url) && action.url.length <= LNURL_LIMITS.maxUrlLength) successAction = { tag: \"url\", description: reason(action.description), url: action.url };\n }\n return { invoice, successAction };\n}\n\n/** Fetches and checks what a Lightning address or LNURL asks for. The service's domain learns of it. */\nexport async function resolveLightningDestination(text: string, options: LnurlFetchOptions = {}): Promise<LnurlPayParams> {\n const destination = parseLightningDestination(text);\n if (!destination) throw new Error(\"That is not a Lightning address or an LNURL\");\n return parsePayParams(await fetchLnurlJson(destination.url, options), destination);\n}\n\n/** Asks the service for an invoice of `amountSat`, and checks it before it is handed to any wallet. */\nexport async function requestLnurlInvoice(params: LnurlPayParams, amountSat: number, comment: string | undefined, options: LnurlFetchOptions = {}): Promise<{ invoice: Bolt11Invoice; successAction?: LnurlSuccessAction }> {\n return parseInvoiceCallback(await fetchLnurlJson(invoiceCallbackUrl(params, amountSat, comment), options), params, amountSat);\n}\n","import { concatBytes, fromZ32, toZ32, utf8Decode, utf8Encode } from './bytes';\n\n/**\n * Domain identity proofs (draft WISP 3xx, docs/wisps/3xx-domain.md), the pure half.\n *\n * Under WISP 300 a proof authorizes a fresh Ed25519 *proof key* that only this\n * profile holds, through a statement the external identity vouches for. A\n * domain vouches by publishing: a DNS TXT record at `_ghostly.<domain>` or a\n * `/.well-known/ghostly.json` file names the proof key **and the statement id**\n * (SHA-256 of the statement), so the record commits to the exact binding —\n * subject, key, dates and nonce — not just to a key. Sharing with a contact is\n * then the contract's per-contact presentation, signed by the proof key.\n *\n * People who already run NIP-05 can use `/.well-known/nostr.json` instead: its\n * root name `_` must be the very Nostr key that signed the statement.\n *\n * The record names no participation key and no contact. Fetching, DNS over\n * HTTPS and caching live in `@ghostly/browser/proofs/domain`.\n */\n\nexport type DomainMethod = 'dns' | 'https' | 'nip05';\n/** What a record says: this Ghostly proof key, for this statement. */\nexport interface DomainRecord { key: string; proof: string }\n\nexport const DOMAIN_METHODS: readonly DomainMethod[] = ['dns', 'https', 'nip05'];\nexport const DNS_LABEL = '_ghostly';\nexport const WELL_KNOWN_PATH = '/.well-known/ghostly.json';\nexport const NIP05_PATH = '/.well-known/nostr.json?name=_';\n/** Everything read from a domain is bounded; these match the fetch limits. */\nexport const DOMAIN_FILE_MAX_BYTES = 16 * 1024;\nexport const DOMAIN_TXT_MAX_BYTES = 1024;\nexport const DOMAIN_MAX_RECORDS = 8;\n\nconst z32 = /^[ybndrfg8ejkmcpqxot1uwisza345h769]{52}$/;\nconst hex64 = /^[a-f0-9]{64}$/;\n\n// ---------------------------------------------------------------------------\n// Domain names\n\n/** Names that never resolve publicly (RFC 6761, 6762, 7686, 8375, 9476) or that\n * only ever mean a private network. A proof for them could only point inward. */\nconst SPECIAL_USE = ['localhost', 'local', 'internal', 'lan', 'home', 'corp', 'intranet', 'private',\n 'test', 'example', 'invalid', 'onion', 'alt', 'arpa'];\n\n/**\n * Normalizes what a person typed (\"Example.COM.\", \"https://example.com/\") into\n * a lowercase ASCII host name, or throws with a message fit for the UI.\n * Internationalized names become punycode (xn--), the form DNS carries.\n */\nexport function normalizeDomain(input: string): string {\n if (typeof input !== 'string') throw new Error('Enter a domain name, like example.com');\n let value = input.trim();\n if (!value || value.length > 300) throw new Error('Enter a domain name, like example.com');\n if (/^[a-z][a-z0-9+.-]*:\\/\\//i.test(value)) {\n let url: URL;\n try { url = new URL(value); } catch { throw new Error('Enter a domain name, like example.com'); }\n if (url.protocol !== 'https:' || url.username || url.password || url.port || (url.pathname !== '/' && url.pathname !== '') || url.search || url.hash)\n throw new Error('Enter only the domain name, without a path or port');\n value = url.hostname;\n } else {\n if (/[/:@?#\\s\\\\]/.test(value)) throw new Error('Enter only the domain name, without a path or port');\n try { value = new URL(`https://${value}`).hostname; } catch { throw new Error('That is not a valid domain name'); }\n }\n value = value.toLowerCase().replace(/\\.$/, '');\n if (value.length > 253 - DNS_LABEL.length - 1) throw new Error('That domain name is too long');\n const labels = value.split('.');\n if (labels.length < 2) throw new Error('Use a full domain name, like example.com');\n for (const label of labels) {\n if (!/^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/.test(label)) throw new Error('That is not a valid domain name');\n }\n const tld = labels[labels.length - 1];\n if (/^[0-9]+$/.test(tld) || /^\\[/.test(value)) throw new Error('Use a domain name, not an IP address');\n if (SPECIAL_USE.includes(tld) || (labels.length >= 2 && labels.slice(-2).join('.') === 'home.arpa'))\n throw new Error('That name only works on private networks');\n return value;\n}\n\nexport const dnsProofName = (domain: string) => `${DNS_LABEL}.${normalizeDomain(domain)}`;\nexport const wellKnownUrl = (domain: string, method: 'https' | 'nip05' = 'https') =>\n `https://${normalizeDomain(domain)}${method === 'nip05' ? NIP05_PATH : WELL_KNOWN_PATH}`;\n\n// ---------------------------------------------------------------------------\n// Records\n\nexport function validDomainRecord(r: DomainRecord): boolean {\n if (!r || typeof r.key !== 'string' || typeof r.proof !== 'string' || !z32.test(r.key) || !hex64.test(r.proof)) return false;\n try { return toZ32(fromZ32(r.key)) === r.key; } catch { return false; }\n}\n\n/** The TXT record value, in the `tag=value; …` style people know from DKIM. */\nexport function domainTxtRecord(r: DomainRecord): string {\n if (!validDomainRecord(r)) throw new Error('Invalid domain record');\n return `v=ghostly1; key=${r.key}; proof=${r.proof}`;\n}\n\n/** Parses one TXT string. Undefined for anything that is not ours or not exactly\n * well-formed; unknown extra tags are ignored for forward compatibility. */\nexport function parseDomainTxt(value: string): DomainRecord | undefined {\n if (typeof value !== 'string' || utf8Encode(value).length > DOMAIN_TXT_MAX_BYTES) return;\n const tags = new Map<string, string>();\n const parts = value.split(';').map(p => p.trim()).filter(Boolean);\n for (const part of parts) {\n const eq = part.indexOf('=');\n if (eq < 1) return;\n const name = part.slice(0, eq).trim().toLowerCase();\n if (tags.has(name)) return;\n tags.set(name, part.slice(eq + 1).trim());\n }\n if (!parts[0]?.toLowerCase().startsWith('v=') || tags.get('v') !== 'ghostly1') return;\n const r = { key: tags.get('key'), proof: tags.get('proof') } as DomainRecord;\n return validDomainRecord(r) ? r : undefined;\n}\n\nconst addRecord = (list: DomainRecord[], r: DomainRecord) => {\n if (!list.some(x => x.key === r.key && x.proof === r.proof)) list.push(r);\n};\n\n/** All records among the TXT strings of `_ghostly.<domain>`; other strings are skipped. */\nexport function recordsFromTxt(values: string[]): DomainRecord[] {\n const out: DomainRecord[] = [];\n for (const value of values.slice(0, 32)) { const r = parseDomainTxt(value); if (r) addRecord(out, r); }\n return out.slice(0, DOMAIN_MAX_RECORDS);\n}\n\n/** The body of `/.well-known/ghostly.json`. */\nexport function domainWellKnownFile(records: DomainRecord[]): string {\n if (!records.length || records.length > DOMAIN_MAX_RECORDS || !records.every(validDomainRecord)) throw new Error('Invalid domain records');\n return JSON.stringify({ ghostly: 1, proofs: records.map(r => ({ key: r.key, proof: r.proof })) }, null, 2) + '\\n';\n}\n\nfunction parseJson(text: string): unknown {\n if (typeof text !== 'string' || utf8Encode(text).length > DOMAIN_FILE_MAX_BYTES) throw new Error('File too large');\n return JSON.parse(text);\n}\n\n/** Records from `ghostly.json`. Throws on a malformed file; [] when it names none. */\nexport function recordsFromWellKnown(text: string): DomainRecord[] {\n const body = parseJson(text) as { ghostly?: unknown; proofs?: unknown };\n if (!body || typeof body !== 'object' || Array.isArray(body) || body.ghostly !== 1 || !Array.isArray(body.proofs))\n throw new Error('Not a Ghostly domain file');\n const out: DomainRecord[] = [];\n for (const entry of body.proofs.slice(0, 32) as { key?: unknown; proof?: unknown }[]) {\n if (!entry || typeof entry !== 'object') continue;\n const r = { key: entry.key, proof: entry.proof } as DomainRecord;\n if (validDomainRecord(r)) addRecord(out, r);\n }\n return out.slice(0, DOMAIN_MAX_RECORDS);\n}\n\n/** NIP-05: only the root name `_` speaks for the domain itself. Undefined when absent. */\nexport function nip05RootKey(text: string): string | undefined {\n const body = parseJson(text) as { names?: unknown };\n if (!body || typeof body !== 'object' || !body.names || typeof body.names !== 'object' || Array.isArray(body.names))\n throw new Error('Not a NIP-05 file');\n const root = (body.names as Record<string, unknown>)['_'];\n return typeof root === 'string' && hex64.test(root) ? root : undefined;\n}\n\nexport const recordsName = (records: DomainRecord[], r: DomainRecord) => records.some(x => x.key === r.key && x.proof === r.proof);\n\n// ---------------------------------------------------------------------------\n// DNS over HTTPS (RFC 8484) wire messages\n\nexport const DNS_TYPE = { A: 1, CNAME: 5, TXT: 16, AAAA: 28 } as const;\nexport type DnsType = keyof typeof DNS_TYPE;\nexport interface DnsAnswer { name: string; type: number; ttl: number; data: Uint8Array }\nexport interface DnsResponse {\n /** 0 = NOERROR, 3 = NXDOMAIN; anything else is a failed lookup. */\n rcode: number;\n /** The resolver reports it validated the answer with DNSSEC. */\n authenticated: boolean;\n answers: DnsAnswer[];\n}\n\n/** A recursive query for one name and type. ID 0, as RFC 8484 recommends for caching;\n * the AD bit asks the resolver to report DNSSEC validation (RFC 6840 §5.7). */\nexport function encodeDnsQuery(name: string, type: DnsType): Uint8Array {\n const labels = name.replace(/\\.$/, '').split('.');\n const parts: Uint8Array[] = [Uint8Array.of(0, 0, 0x01, 0x20, 0, 1, 0, 0, 0, 0, 0, 0)];\n for (const label of labels) {\n const bytes = utf8Encode(label);\n if (!bytes.length || bytes.length > 63) throw new Error('Invalid DNS name');\n parts.push(Uint8Array.of(bytes.length), bytes);\n }\n const code = DNS_TYPE[type];\n parts.push(Uint8Array.of(0, code >> 8, code & 0xff, 0, 1));\n return concatBytes(...parts);\n}\n\nfunction readName(data: Uint8Array, start: number): { name: string; next: number } {\n const labels: string[] = [];\n let pos = start, next = -1, jumps = 0, length = 0;\n for (;;) {\n if (pos >= data.length) throw new Error('DNS name out of bounds');\n const len = data[pos];\n if ((len & 0xc0) === 0xc0) {\n if (pos + 1 >= data.length) throw new Error('DNS pointer out of bounds');\n if (next < 0) next = pos + 2;\n pos = ((len & 0x3f) << 8) | data[pos + 1];\n if (++jumps > 32) throw new Error('DNS name loops');\n continue;\n }\n if (len & 0xc0) throw new Error('Unsupported DNS label');\n pos += 1;\n if (len === 0) break;\n if (pos + len > data.length) throw new Error('DNS label out of bounds');\n length += len + 1;\n if (length > 255) throw new Error('DNS name too long');\n labels.push(utf8Decode(data.subarray(pos, pos + len)).toLowerCase());\n pos += len;\n }\n return { name: labels.join('.'), next: next < 0 ? pos : next };\n}\n\n/**\n * Decodes a resolver's answer to `encodeDnsQuery(name, type)`. Refuses a reply\n * to another question or a truncated one, and keeps only records of the asked\n * type owned by the name or by the CNAME chain the answer itself follows.\n */\nexport function decodeDnsResponse(data: Uint8Array, name: string, type: DnsType): DnsResponse {\n if (data.length < 12) throw new Error('DNS response too short');\n const view = new DataView(data.buffer, data.byteOffset, data.byteLength);\n const flags = view.getUint16(2);\n if (!(flags & 0x8000) || (flags & 0x7800) || (flags & 0x0200)) throw new Error('Unusable DNS response');\n const questions = view.getUint16(4), count = view.getUint16(6);\n const want = name.replace(/\\.$/, '').toLowerCase(), code = DNS_TYPE[type];\n if (questions !== 1) throw new Error('DNS response is for another question');\n const q = readName(data, 12);\n if (q.name !== want || q.next + 4 > data.length || view.getUint16(q.next) !== code) throw new Error('DNS response is for another question');\n let pos = q.next + 4;\n const all: DnsAnswer[] = [];\n for (let i = 0; i < count && i < 64; i++) {\n const owner = readName(data, pos);\n pos = owner.next;\n if (pos + 10 > data.length) throw new Error('DNS record out of bounds');\n const rtype = view.getUint16(pos), ttl = view.getUint32(pos + 4), len = view.getUint16(pos + 8);\n pos += 10;\n if (pos + len > data.length) throw new Error('DNS record out of bounds');\n const rdata = rtype === DNS_TYPE.CNAME ? utf8Encode(readName(data, pos).name) : data.slice(pos, pos + len);\n all.push({ name: owner.name, type: rtype, ttl, data: rdata });\n pos += len;\n }\n const owners = new Set([want]);\n for (let hop = 0; hop < 8; hop++) {\n const cname = all.find(a => a.type === DNS_TYPE.CNAME && owners.has(a.name) && !owners.has(utf8Decode(a.data)));\n if (!cname) break;\n owners.add(utf8Decode(cname.data));\n }\n return { rcode: flags & 0x000f, authenticated: !!(flags & 0x0020), answers: all.filter(a => a.type === code && owners.has(a.name)) };\n}\n\n/** The TXT character-strings of one record, joined as RFC 7208 §3.3 does. */\nexport function txtValue(rdata: Uint8Array): string {\n const parts: Uint8Array[] = [];\n for (let p = 0; p < rdata.length;) {\n const len = rdata[p];\n if (p + 1 + len > rdata.length) throw new Error('DNS text out of bounds');\n parts.push(rdata.subarray(p + 1, p + 1 + len));\n p += 1 + len;\n }\n return new TextDecoder('utf-8', { fatal: true }).decode(concatBytes(...parts));\n}\n\n// ---------------------------------------------------------------------------\n// Addresses: a proof may only make a contact's app fetch from the public Internet\n\n/** False for loopback, private, link-local, shared (CGNAT), multicast, reserved\n * and unspecified addresses, including IPv4 embedded in IPv6. */\nexport function isPublicAddress(address: Uint8Array): boolean {\n if (address.length === 4) {\n const [a, b, c] = address;\n return !(a === 0 || a === 10 || a === 127 || a >= 224 ||\n (a === 100 && b >= 64 && b <= 127) || (a === 169 && b === 254) || (a === 172 && b >= 16 && b <= 31) ||\n (a === 192 && b === 168) || (a === 192 && b === 0 && c === 0) || (a === 198 && (b === 18 || b === 19)));\n }\n if (address.length !== 16) return false;\n const zeroTo = (n: number) => address.subarray(0, n).every(x => x === 0);\n if (zeroTo(15) && (address[15] === 0 || address[15] === 1)) return false; // :: and ::1\n if (zeroTo(10) && address[10] === 0xff && address[11] === 0xff) return isPublicAddress(address.subarray(12)); // ::ffff:a.b.c.d\n if (address[0] === 0x00 && address[1] === 0x64 && address[2] === 0xff && address[3] === 0x9b && address.subarray(4, 12).every(x => x === 0))\n return isPublicAddress(address.subarray(12)); // 64:ff9b::/96 NAT64\n if (zeroTo(12)) return false; // deprecated IPv4-compatible\n if ((address[0] & 0xfe) === 0xfc) return false; // fc00::/7 unique local\n if (address[0] === 0xfe && (address[1] & 0xc0) === 0x80) return false; // fe80::/10 link-local\n if (address[0] === 0xfe && (address[1] & 0xc0) === 0xc0) return false; // fec0::/10 site-local\n if (address[0] === 0xff) return false; // multicast\n if (address[0] === 0x01 && address.subarray(1, 8).every(x => x === 0)) return false; // 100::/64 discard\n return (address[0] & 0xe0) === 0x20; // only 2000::/3 is global unicast\n}\n","import { decodeDnsAnswers, readDnsName } from \"./dns\";\nimport { normalizeDomain } from \"./domainProofs\";\n\n/**\n * Pubky's PKDNS records, read (https://pubky.org). A Pubky key's signed Pkarr packet names its homeserver in an\n * HTTPS (or SVCB) record at `_pubky` whose target is the homeserver's own key; the homeserver's packet names, in\n * HTTPS records at its root, where it answers: its Pubky TLS endpoint (target \".\", for native clients) and an ICANN\n * host name for browsers. The Pubky SDK resolves exactly this. Packets reach these functions already checked\n * against their key (`openRelayPayload`); nothing here trusts a host or URL from anywhere else.\n */\n\nconst SVCB = 64;\nconst HTTPS = 65;\n/** SvcParamKey \"port\" (RFC 9460). */\nconst PORT = 3;\nconst Z32 = /^[ybndrfg8ejkmcpqxot1uwisza345h769]{52}$/;\n\nexport interface SvcbRecord {\n /** The owner name, lowercase, without a trailing dot (a Pkarr packet's names end in its key). */\n name: string;\n priority: number;\n /** The target name, lowercase; \"\" is \".\" (the owner itself). */\n target: string;\n /** SvcParams by key, raw. */\n params: Map<number, Uint8Array>;\n}\n\nconst u16 = (data: Uint8Array, at: number) => (data[at] << 8) | data[at + 1];\n\n/** Every SVCB and HTTPS record of a DNS packet, in packet order. Throws on a malformed one. */\nexport function decodeSvcbRecords(dnsPacket: Uint8Array): SvcbRecord[] {\n const records: SvcbRecord[] = [];\n for (const answer of decodeDnsAnswers(dnsPacket)) {\n if (answer.type !== SVCB && answer.type !== HTTPS) continue;\n const end = answer.offset + answer.length;\n if (answer.length < 3) throw new Error(\"SVCB record too short\");\n const priority = u16(dnsPacket, answer.offset);\n const { name: target, next } = readDnsName(dnsPacket, answer.offset + 2);\n if (next > end) throw new Error(\"SVCB target out of bounds\");\n const params = new Map<number, Uint8Array>();\n for (let at = next; at < end;) {\n if (at + 4 > end) throw new Error(\"SVCB parameter out of bounds\");\n const key = u16(dnsPacket, at), length = u16(dnsPacket, at + 2);\n at += 4;\n if (at + length > end) throw new Error(\"SVCB parameter out of bounds\");\n if (!params.has(key)) params.set(key, dnsPacket.slice(at, at + length));\n at += length;\n }\n records.push({ name: answer.name.toLowerCase(), priority, target: target.toLowerCase(), params });\n }\n return records;\n}\n\n/** The homeserver a Pubky key's packet names: the z-base-32 key its `_pubky` record points at, or undefined. */\nexport function pubkyHomeserverOf(dnsPacket: Uint8Array, userZ32: string): string | undefined {\n // Like the SDK: the first `_pubky` record. Pkarr stores names with the key appended; accept the relative form too.\n const record = decodeSvcbRecords(dnsPacket).find(r => r.name === `_pubky.${userZ32}` || r.name === \"_pubky\");\n return record && Z32.test(record.target) ? record.target : undefined;\n}\n\n/** Where a homeserver answers browsers: a public host name and, when its record says so, a port. */\nexport interface HomeserverEndpoint { host: string; port?: number }\n\n/**\n * The homeserver's web endpoint from its own packet: of the HTTPS/SVCB records at its root, the lowest priority one\n * whose target is a public ICANN host name (not \".\", not another Pkarr key, not a name that only works on private\n * networks, which would point a contact's app inward), with its port parameter. Undefined when there is none.\n */\nexport function homeserverWebEndpoint(dnsPacket: Uint8Array, homeserverZ32: string): HomeserverEndpoint | undefined {\n const records = decodeSvcbRecords(dnsPacket)\n .filter(r => (r.name === homeserverZ32 || r.name === \"\") && r.priority > 0)\n .sort((a, b) => a.priority - b.priority);\n for (const r of records) {\n if (!r.target || Z32.test(r.target.split(\".\")[0])) continue;\n let host: string;\n try { host = normalizeDomain(r.target); } catch { continue; }\n const port = r.params.get(PORT);\n if (port && port.length !== 2) continue;\n const number = port ? u16(port, 0) : undefined;\n if (number === 0) continue;\n return number === undefined || number === 443 ? { host } : { host, port: number };\n }\n return undefined;\n}\n\n/**\n * A DNS packet of SVCB/HTTPS records, the way Pkarr stores them (names with the key appended, targets\n * uncompressed). For tests and test services: Ghostly itself never publishes these.\n */\nexport function encodeSvcbPacket(records: { name: string; priority: number; target: string; port?: number; type?: 64 | 65 }[]): Uint8Array {\n const name = (value: string) => {\n const labels = value.split(\".\").filter(Boolean).map(l => new TextEncoder().encode(l));\n return Uint8Array.from([...labels.flatMap(l => [l.length, ...l]), 0]);\n };\n const be16 = (n: number) => [(n >> 8) & 0xff, n & 0xff];\n const bytes: number[] = [0, 0, 0x80, 0, 0, 0, ...be16(records.length), 0, 0, 0, 0];\n for (const r of records) {\n const rdata = [...be16(r.priority), ...name(r.target), ...(r.port === undefined ? [] : [...be16(PORT), 0, 2, ...be16(r.port)])];\n bytes.push(...name(r.name), ...be16(r.type ?? HTTPS), 0, 1, 0, 0, 0x0e, 0x10, ...be16(rdata.length), ...rdata);\n }\n return Uint8Array.from(bytes);\n}\n","import { fromZ32, randomBytes, toZ32 } from \"./bytes\";\nimport type { IdentityStatement } from \"./identityProofs\";\n\n/**\n * A Pubky identity proof, by publication (WISP 302, docs/wisps/302-pubky.md). Neither Pubky Ring nor Pubky\n * Passport signs arbitrary text: they approve standard Pubky auth requests. So Ghostly asks, through one of them,\n * for write access to ONE fresh folder of the key's homeserver, `/pub/ghostly.app/proofs/<folder>/`, and writes the\n * identity statement there (the #80 statement: the Pubky key authorizes this profile's per-proof key). A contact's\n * app reads the file back, finding the homeserver through the key's own signed PKDNS records (pkdns.ts), never\n * through a host or URL from the proof. The folder is random, so the path says nothing about the profile.\n */\n\nexport const PUBKY_PROOF_ROOT = \"/pub/ghostly.app/proofs/\";\n/** The file is the statement, a few hundred bytes: anything larger is not ours. */\nexport const PUBKY_PROOF_MAX_BYTES = 1024;\nconst Z32 = /^[ybndrfg8ejkmcpqxot1uwisza345h769]{52}$/;\nconst FOLDER = /^[a-f0-9]{64}$/;\nconst STATEMENT_ID = /^[a-f0-9]{64}$/;\n\n/** A canonical Pubky key: 52 characters of z-base-32 that decode to 32 bytes and back. */\nexport function isPubkyKey(value: unknown): value is string {\n if (typeof value !== \"string\" || !Z32.test(value)) return false;\n try { return toZ32(fromZ32(value)) === value; } catch { return false; }\n}\n\n/** The z-base-32 key for what a person typed or pasted: the bare key, `pubky<key>`, `pk:<key>` or `pubky://<key>`. */\nexport function normalizePubkyKey(input: string): string {\n const match = /^(?:pubky:\\/\\/|pubky|pk:)?([a-z0-9]{52})\\/?$/i.exec(typeof input === \"string\" ? input.trim() : \"\");\n const key = match?.[1].toLowerCase();\n if (!key || !isPubkyKey(key)) throw new Error(\"That is not a Pubky key: 52 characters, like the one Pubky Ring shows\");\n return key;\n}\n\nexport const isPubkyProofFolder = (value: unknown): value is string => typeof value === \"string\" && FOLDER.test(value);\n\n/** A fresh folder name: 32 random bytes, hex. One per proof, so two proofs never share a path. */\nexport const newPubkyProofFolder = () => Array.from(randomBytes(32), b => b.toString(16).padStart(2, \"0\")).join(\"\");\n\n/** The only capability Ghostly ever asks for: write access to one proof folder. */\nexport function pubkyProofCapability(folder: string): `/pub/${string}/:w` {\n if (!isPubkyProofFolder(folder)) throw new Error(\"Invalid Pubky proof folder\");\n return `${PUBKY_PROOF_ROOT}${folder}/:w`;\n}\n\n/** Where the statement goes: in its folder, named by the statement id. */\nexport function pubkyProofPath(folder: string, statementId: string): `/pub/${string}` {\n if (!isPubkyProofFolder(folder) || !STATEMENT_ID.test(statementId)) throw new Error(\"Invalid Pubky proof path\");\n return `${PUBKY_PROOF_ROOT}${folder}/${statementId}.txt`;\n}\n\n/** The file's exact contents: the statement's one line, nothing added. The verifier compares bytes. */\nexport const pubkyProofFile = (statement: IdentityStatement): string => statement.text;\n","import { sha256 } from \"@noble/hashes/sha2.js\";\nimport { bech32, bech32m, createBase58check } from \"@scure/base\";\n\n/** The Bitcoin networks an on-chain wallet can be on. Only `bitcoin` is real money. */\nexport type BitcoinNetwork = \"bitcoin\" | \"signet\" | \"testnet\" | \"regtest\" | \"mutinynet\";\nexport const BITCOIN_NETWORKS: readonly BitcoinNetwork[] = [\"bitcoin\", \"signet\", \"testnet\", \"regtest\", \"mutinynet\"];\n\n/** Mutinynet is a signet: it shares signet's (and testnet's) address prefixes. */\nconst SEGWIT_HRP: Record<BitcoinNetwork, string> = { bitcoin: \"bc\", signet: \"tb\", testnet: \"tb\", mutinynet: \"tb\", regtest: \"bcrt\" };\nconst BASE58_VERSIONS: Record<BitcoinNetwork, readonly number[]> = { bitcoin: [0x00, 0x05], signet: [0x6f, 0xc4], testnet: [0x6f, 0xc4], mutinynet: [0x6f, 0xc4], regtest: [0x6f, 0xc4] };\nconst base58check = createBase58check(sha256);\n\nfunction segwit(address: string, hrp: string): boolean {\n // BIP 173: one case only. The checksum is over the lower-case form.\n if (address !== address.toLowerCase() && address !== address.toUpperCase()) return false;\n const lower = address.toLowerCase();\n if (!lower.startsWith(`${hrp}1`) || lower.length > 90) return false;\n // Version 0 is bech32 (BIP 173), every later version bech32m (BIP 350); the wrong one is refused.\n for (const [coder, v0] of [[bech32, true], [bech32m, false]] as const) {\n let decoded: { prefix: string; words: number[] };\n try { decoded = coder.decode(lower as `${string}1${string}`, 90); } catch { continue; }\n if (decoded.prefix !== hrp || decoded.words.length < 1) return false;\n const version = decoded.words[0];\n if (version > 16 || (version === 0) !== v0) return false;\n let program: Uint8Array;\n try { program = coder.fromWords(decoded.words.slice(1)); } catch { return false; }\n if (version === 0) return program.length === 20 || program.length === 32;\n return program.length >= 2 && program.length <= 40;\n }\n return false;\n}\n\n/** Whether `address` is a valid address on `network`: checksums, witness versions, lengths and prefixes. */\nexport function isBitcoinAddress(address: unknown, network: BitcoinNetwork): boolean {\n if (typeof address !== \"string\" || !BITCOIN_NETWORKS.includes(network) || address.length < 14 || address.length > 90) return false;\n if (segwit(address, SEGWIT_HRP[network])) return true;\n let payload: Uint8Array;\n try { payload = base58check.decode(address); } catch { return false; }\n return payload.length === 21 && BASE58_VERSIONS[network].includes(payload[0]);\n}\n","import { bech32m } from \"@scure/base\";\n\n/**\n * Spark (https://spark.money): a Bitcoin layer 2 where a transfer between two Spark wallets is instant and\n * off-chain. Ghostly's Spark wallets run on Bitcoin (real sats, Breez API key) or on regtest (Breez and\n * Lightspark's hosted test network, worthless sats). Spark's own testnet and signet are not offered by the\n * Breez SDK, so they are not payment networks here.\n */\nexport type SparkNetwork = \"bitcoin\" | \"regtest\";\nexport const SPARK_NETWORKS: readonly SparkNetwork[] = [\"bitcoin\", \"regtest\"];\n/**\n * The `provider` of a Spark target. Spark is one network per chain (its operators are the same for every\n * wallet), so any Spark wallet on the same network pays any Spark address: the payee names no service.\n */\nexport const SPARK_PROVIDER = \"spark\";\n\nconst HRP: Record<SparkNetwork, string> = { bitcoin: \"spark\", regtest: \"sparkrt\" };\n/** Long enough for an invoice with a 140-character memo and its signature. */\nconst MAX_LENGTH = 1024;\n\n/**\n * What a Spark string is on `network`: a wallet's `address` (its identity key, the same every time) or an\n * `invoice` (an address plus an amount, an expiry, a memo and a unique id, signed by the payee). Undefined\n * when it is neither, or belongs to another network. Structure only: the wallet's SDK checks the rest.\n */\nexport function sparkAddressKind(address: unknown, network: SparkNetwork): \"address\" | \"invoice\" | undefined {\n if (typeof address !== \"string\" || !SPARK_NETWORKS.includes(network) || address.length > MAX_LENGTH) return undefined;\n // One case only, and Spark writes lower case.\n if (address !== address.toLowerCase() || !address.startsWith(`${HRP[network]}1`)) return undefined;\n let payload: Uint8Array;\n try {\n const decoded = bech32m.decode(address as `${string}1${string}`, MAX_LENGTH);\n if (decoded.prefix !== HRP[network]) return undefined;\n payload = bech32m.fromWords(decoded.words);\n } catch { return undefined; }\n // Protobuf: field 1, 33 bytes, a compressed public key (the wallet's identity).\n if (payload.length < 35 || payload[0] !== 0x0a || payload[1] !== 0x21 || (payload[2] !== 0x02 && payload[2] !== 0x03)) return undefined;\n if (payload.length === 35) return \"address\";\n // Field 2: the invoice's fields. Anything else after the key is not a Spark string Ghostly knows.\n return payload[35] === 0x12 ? \"invoice\" : undefined;\n}\n\nexport const isSparkAddress = (address: unknown, network: SparkNetwork): boolean => sparkAddressKind(address, network) !== undefined;\n\n/** The Spark network a string belongs to, from its prefix alone (`spark1…` Bitcoin, `sparkrt1…` regtest). */\nexport function sparkNetworkOf(address: string): SparkNetwork | undefined {\n return SPARK_NETWORKS.find((network) => sparkAddressKind(address, network) !== undefined);\n}\n\n/** What a Spark invoice asks for, read from the invoice itself (the payer's wallet checks it again). */\nexport interface SparkInvoiceDetails {\n /** Sats asked; absent when the payer chooses. */\n amount?: number;\n memo?: string;\n /** Milliseconds. */\n expiresAt?: number;\n /** An invoice for a token (not sats): Ghostly does not pay those. */\n token: boolean;\n}\n\n/** Protobuf, just enough of it: `[field, wire type, value]` of each field of a message, or undefined when malformed. */\nfunction fields(bytes: Uint8Array): [number, number, Uint8Array | bigint][] | undefined {\n const out: [number, number, Uint8Array | bigint][] = [];\n let at = 0;\n const varint = (): bigint | undefined => {\n let value = 0n, shift = 0n;\n for (let i = 0; i < 10; i++) {\n if (at >= bytes.length) return undefined;\n const b = bytes[at++];\n value |= BigInt(b & 0x7f) << shift;\n if (!(b & 0x80)) return value;\n shift += 7n;\n }\n return undefined;\n };\n while (at < bytes.length) {\n const key = varint();\n if (key === undefined) return undefined;\n const field = Number(key >> 3n), wire = Number(key & 7n);\n if (wire === 0) { const v = varint(); if (v === undefined) return undefined; out.push([field, wire, v]); }\n else if (wire === 2) {\n const length = varint();\n if (length === undefined || at + Number(length) > bytes.length) return undefined;\n out.push([field, wire, bytes.slice(at, at + Number(length))]); at += Number(length);\n } else return undefined;\n }\n return out;\n}\n\n/**\n * The amount, memo and expiry a Spark invoice carries (SparkInvoiceFields: 4 sats payment, 3 tokens payment, 5 memo,\n * 7 expiry time), or undefined when `invoice` is not an invoice of `network`.\n */\nexport function sparkInvoiceDetails(invoice: string, network: SparkNetwork): SparkInvoiceDetails | undefined {\n if (sparkAddressKind(invoice, network) !== \"invoice\") return undefined;\n const top = fields(bech32m.fromWords(bech32m.decode(invoice as `${string}1${string}`, MAX_LENGTH).words));\n const body = top?.find(([field, wire]) => field === 2 && wire === 2)?.[2];\n const inner = body instanceof Uint8Array ? fields(body) : undefined;\n if (!inner) return undefined;\n const details: SparkInvoiceDetails = { token: false };\n for (const [field, wire, value] of inner) {\n if (wire !== 2 || !(value instanceof Uint8Array)) continue;\n if (field === 3) details.token = true;\n else if (field === 4) {\n const amount = fields(value)?.find(([f, w]) => f === 1 && w === 0)?.[2];\n if (typeof amount === \"bigint\") { if (amount > BigInt(Number.MAX_SAFE_INTEGER)) return undefined; details.amount = Number(amount); }\n } else if (field === 5) details.memo = new TextDecoder().decode(value).slice(0, 140);\n else if (field === 7) {\n const seconds = fields(value)?.find(([f, w]) => f === 1 && w === 0)?.[2];\n if (typeof seconds === \"bigint\" && seconds < 1n << 40n) details.expiresAt = Number(seconds) * 1000;\n }\n }\n return details;\n}\n","import { BITCOIN_NETWORKS, isBitcoinAddress, type BitcoinNetwork } from \"./bitcoinAddress\";\nimport { engineError } from \"./engineErrors\";\nimport { isFederationId } from \"./payments\";\nimport { SPARK_NETWORKS, SPARK_PROVIDER, isSparkAddress, type SparkNetwork } from \"./sparkAddress\";\n\n/** Local wallet contract. Capability support is never authorization to spend. */\nexport type PaymentMethod = \"cashu\" | \"arkade\" | \"usdt\" | \"bark\" | \"bitcoin\" | \"fedimint\" | \"spark\";\nexport type PaymentNetwork = \"bitcoin\" | \"signet\" | \"testnet\" | \"mutinynet\" | \"regtest\" | \"cashu-test\" | \"ethereum\" | \"sepolia\" | \"evm-local\";\n/**\n * Real money, or test coins: what every wallet is, whatever chain it runs on. A payment never crosses from one\n * to the other: a test wallet never settles a request for real money, nor the reverse.\n */\nexport type WalletNetwork = \"mainnet\" | \"testnet\";\nexport const WALLET_NETWORKS: readonly WalletNetwork[] = [\"mainnet\", \"testnet\"];\n/** Bitcoin and Ethereum carry real money; every other chain of a payment target is a test network. */\nexport const walletNetworkOf = (network: PaymentNetwork | string): WalletNetwork => network === \"bitcoin\" || network === \"ethereum\" ? \"mainnet\" : \"testnet\";\nexport const isWalletNetwork = (value: unknown): value is WalletNetwork => value === \"mainnet\" || value === \"testnet\";\n/**\n * The `provider` of an on-chain target. Anyone can pay a Bitcoin address from any wallet, so the payee\n * names no service; which wallet pays is the payer's own choice (its active on-chain source).\n */\nexport const ONCHAIN_PROVIDER = \"onchain\";\nexport type IntentState = \"pending\" | \"submitted\" | \"settled\" | \"failed\" | \"unknown\" | \"cancelled\";\nexport interface PaymentTarget {\n method: PaymentMethod;\n network: PaymentNetwork;\n provider: string;\n asset: \"BTC\" | \"USDT\" | \"TEST-USDT\";\n unit: \"sat\" | \"token-base\";\n chainId?: number;\n token?: string;\n decimals?: number;\n issuedAt?: number;\n address: string;\n expiresAt: number;\n}\nexport interface PaymentReview extends PaymentTarget {\n id: string;\n requestId?: string;\n linkId?: string;\n payee: string;\n /** What the payment is for, as the payer wrote it (at most 140 characters). */\n memo?: string;\n amount: number;\n fee: number;\n feeCap: number;\n createdAt: number;\n /** When it was approved (claimed for spending): how old an attempt whose outcome is unknown is, across a reload. */\n submittedAt?: number;\n state: IntentState;\n txid?: string;\n error?: string;\n evm?: { from: string; nonce: number; gasLimit: string; maxFeePerGas: string; maxPriorityFeePerGas: string; confirmations: number };\n}\nexport interface PaymentAdapter<Prepared = unknown> {\n method: PaymentMethod;\n prepare(target: PaymentTarget, amount: number, feeCap: number): Promise<{ fee: number; prepared: Prepared; evm?: PaymentReview[\"evm\"] }>;\n execute(review: PaymentReview, prepared: Prepared, persist?: () => Promise<void>): Promise<PaymentExecution>;\n reconcile(review: PaymentReview, prepared: Prepared, persist?:()=>Promise<void>): Promise<PaymentExecution>;\n /** A review cancelled before approval: give back what `prepare` reserved (locked coins). Nothing was spent. */\n release?(review: PaymentReview, prepared: Prepared): Promise<void>;\n}\nexport interface PaymentExecution { txid?: string; settled: boolean; pending?: boolean; failed?: boolean; error?: string }\n/** Only use before signing/broadcasting, when no spend could have happened. */\nexport class PaymentPreflightError extends Error {}\nexport const ETHEREUM_USDT = \"0xdAC17F958D2ee523a2206206994597C13D831ec7\";\n/** Each chain's public RPC, which needs no key: the token wallet's defaults, and the `provider` its requests name. */\nexport const USDT_PUBLIC_RPC = { ethereum: \"https://ethereum.publicnode.com\", sepolia: \"https://ethereum-sepolia-rpc.publicnode.com\" } as const;\n/** Test networks for the token wallet and their chain IDs. Their tokens are worthless and not issued by Tether. */\nexport const EVM_TEST_CHAINS = { sepolia: 11155111, \"evm-local\": 31337 } as const;\n/** Aave's Sepolia test USDT (6 decimals): anyone can mint it from Aave's faucet, so a test needs only Sepolia ETH. */\nexport const SEPOLIA_TEST_USDT = \"0xaA8E23Fb1079EA71e0a56F48a2aA51851D8433D0\";\n/** Aave's public test-token faucet on Sepolia: `mint(token, to, amount)`, open to anyone who pays the gas. */\nexport const SEPOLIA_TEST_USDT_FAUCET = \"0xC959483DBa39aa9E78757139af0e9a2EDEb3f42D\";\n/** What one \"Get test USDT\" asks for: 1,000 TEST-USDT (6 decimals). */\nexport const TEST_USDT_FAUCET_AMOUNT = 1_000_000_000;\nexport function parsePaymentAmount(value: string, decimals: number): number {\n if (!Number.isInteger(decimals) || decimals < 0 || decimals > 18 || !/^\\d+(?:\\.\\d+)?$/.test(value)) throw new Error(\"Enter a valid amount\");\n const [whole, fraction = \"\"] = value.split(\".\");\n if (fraction.length > decimals) throw new Error(`Use at most ${decimals} decimal places`);\n const units = BigInt(whole) * 10n ** BigInt(decimals) + BigInt(fraction.padEnd(decimals, \"0\") || \"0\");\n if (units > BigInt(Number.MAX_SAFE_INTEGER)) throw new Error(\"Amount exceeds the wallet limit\");\n return Number(units);\n}\nexport function formatPaymentAmount(value: number | string, decimals = 0): string {\n const digits = BigInt(value).toString().padStart(decimals + 1, \"0\");\n if (!decimals) return digits;\n const fraction = digits.slice(-decimals).replace(/0+$/, \"\");\n return digits.slice(0, -decimals) + (fraction ? `.${fraction}` : \"\");\n}\nexport function assertTokenUnits(amount: number): void {\n if (!Number.isSafeInteger(amount) || amount <= 0) throw new Error(\"Enter a positive amount within the token precision and wallet limit\");\n}\nexport function assertWholeSats(amount: number): void {\n if (!Number.isSafeInteger(amount) || amount <= 0 || amount > 2_100_000_000_000_000) throw new Error(\"Enter a positive whole amount in sats\");\n}\nexport function validatePaymentTarget(value: unknown, now = Date.now()): PaymentTarget {\n if (!value || typeof value !== \"object\") throw new Error(\"Invalid payment target\");\n const t = value as PaymentTarget;\n const evm = t.method === \"usdt\";\n if (t.method === \"bitcoin\") {\n if (!BITCOIN_NETWORKS.includes(t.network as BitcoinNetwork) || t.asset !== \"BTC\" || t.unit !== \"sat\" || t.provider !== ONCHAIN_PROVIDER) throw new Error(\"Unsupported payment method, asset or network\");\n if (!isBitcoinAddress(t.address, t.network as BitcoinNetwork)) throw new Error(`That is not a ${t.network === \"bitcoin\" ? \"Bitcoin\" : t.network} address`);\n } else if (t.method === \"fedimint\") {\n // The provider is the federation (its id); the address is what the payment answers: the request, or the contact.\n if (!BITCOIN_NETWORKS.includes(t.network as BitcoinNetwork) || t.asset !== \"BTC\" || t.unit !== \"sat\") throw new Error(\"Unsupported payment method, asset or network\");\n if (!isFederationId(t.provider)) throw new Error(\"That is not a Fedimint federation id\");\n } else if (t.method === \"spark\") {\n // Any Spark wallet of the network pays any Spark address or invoice of it: no provider to name.\n if (!SPARK_NETWORKS.includes(t.network as SparkNetwork) || t.asset !== \"BTC\" || t.unit !== \"sat\" || t.provider !== SPARK_PROVIDER) throw new Error(\"Unsupported payment method, asset or network\");\n if (!isSparkAddress(t.address, t.network as SparkNetwork)) throw new Error(`That is not a Spark address on ${t.network === \"bitcoin\" ? \"Bitcoin\" : t.network}`);\n } else if (evm) {\n if (![\"ethereum\", \"sepolia\", \"evm-local\"].includes(t.network) || t.unit !== \"token-base\" || !/^0x[0-9a-fA-F]{40}$/.test(t.token ?? \"\") || !/^0x[0-9a-fA-F]{40}$/.test(t.address) || /^0x0{40}$/i.test(t.address)) throw new Error(\"Invalid token, recipient or EVM network\");\n // Only Ethereum carries real USDT; Sepolia and a local chain carry worthless test tokens.\n if (t.network === \"ethereum\" ? t.chainId !== 1 || t.asset !== \"USDT\" || t.token!.toLowerCase() !== ETHEREUM_USDT.toLowerCase() || t.decimals !== 6 : t.chainId !== EVM_TEST_CHAINS[t.network as \"sepolia\" | \"evm-local\"] || t.asset !== \"TEST-USDT\" || !Number.isInteger(t.decimals) || t.decimals! < 0 || t.decimals! > 18) throw new Error(\"Token metadata does not match the network\");\n if (!Number.isSafeInteger(t.issuedAt) || t.issuedAt! < 0 || t.issuedAt! > now + 30000 || t.issuedAt! >= t.expiresAt) throw new Error(\"Invalid token request time\");\n } else if (![\"arkade\", \"cashu\", \"bark\"].includes(t.method) || ![\"bitcoin\", \"signet\", \"mutinynet\", \"regtest\", \"cashu-test\"].includes(t.network) || t.asset !== \"BTC\" || t.unit !== \"sat\") throw new Error(\"Unsupported payment method, asset or network\");\n // Second's Ark servers run on Bitcoin, signet and regtest only.\n if (t.method === \"bark\" && ![\"bitcoin\", \"signet\", \"regtest\"].includes(t.network)) throw new Error(\"Unsupported Bark network\");\n if (typeof t.address !== \"string\" || !t.address || t.address.length > 4096 || typeof t.provider !== \"string\" || t.provider.length > 512) throw new Error(\"Invalid payment destination\");\n if (t.method !== \"bitcoin\" && t.method !== \"fedimint\" && t.method !== \"spark\") {\n const url = new URL(t.provider);\n if (url.username || url.password || url.search || url.hash || (url.protocol !== \"https:\" && !(url.protocol === \"http:\" && [\"localhost\", \"127.0.0.1\", \"[::1]\"].includes(url.hostname)))) throw new Error(\"Use HTTPS or a local test provider\");\n }\n if (!Number.isSafeInteger(t.expiresAt) || t.expiresAt <= now || t.expiresAt > now + 24 * 60 * 60 * 1000) throw engineError(\"paymentExpired\");\n return { method:t.method,network:t.network,provider:t.provider,asset:t.asset,unit:t.unit,address:t.address,expiresAt:t.expiresAt, ...(evm ? {chainId:t.chainId,token:t.token,decimals:t.decimals,issuedAt:t.issuedAt} : {}) };\n}\n","import { ed25519 } from \"@noble/curves/ed25519.js\";\nimport { p256 } from \"@noble/curves/nist.js\";\nimport { secp256k1 } from \"@noble/curves/secp256k1.js\";\nimport { sha256 } from \"@noble/hashes/sha2.js\";\nimport { bytesEqual, fromBase64Url, fromZ32, toBase64Url, toZ32, utf8Encode } from \"./bytes\";\nimport { decodeTxtPacket, encodeTxtPacket, type NsRecord, type TxtRecord } from \"./dns\";\nimport type { Identity } from \"./identity\";\nimport { MAX_DNS_PACKET_BYTES, PacketTooLargeError, openRelayPayload, signRelayPayload } from \"./pkarr\";\nimport { DEFAULT_RELAYS, normalizeRelayUrl } from \"./relay\";\n\n/**\n * did:dht (the DIF method, https://github.com/decentralized-identity/did-dht/blob/main/spec/spec.md): a W3C DID document written as DNS records into the\n * Pkarr packet of an Ed25519 key, the BEP44 mutable item Ghostly already publishes chats under. This\n * module maps a document to that packet and back (strictly: a malformed packet is an error, never a\n * guess) and resolves a did:dht through Pkarr relays. WISP 3xx-did-dht describes Ghostly's use.\n */\n\nexport const DID_DHT_PREFIX = \"did:dht:\";\n/** The TTL the spec recommends: Mainline's default record lifetime. */\nexport const DID_DHT_TTL = 7200;\n/** The spec version this module writes and the only one it reads (`v=0`). */\nexport const DID_DHT_VERSION = \"0\";\n/** A relay answer: 64 bytes of signature, 8 of sequence number, then at most 1000 of DNS packet. */\nexport const DID_DHT_MAX_PAYLOAD = 64 + 8 + MAX_DNS_PACKET_BYTES;\n\nexport interface DidDhtJwk {\n kty: \"OKP\" | \"EC\";\n crv: \"Ed25519\" | \"secp256k1\" | \"P-256\" | \"X25519\";\n x: string;\n y?: string;\n alg: string;\n kid: string;\n}\n\nexport interface DidDhtVerificationMethod {\n id: string;\n type: \"JsonWebKey\";\n controller: string;\n publicKeyJwk: DidDhtJwk;\n}\n\nexport interface DidDhtService {\n id: string;\n type: string;\n serviceEndpoint: string[];\n /** Registered extra properties (the spec's registry): which keys sign and encrypt for it. */\n sig?: string | string[];\n enc?: string | string[];\n}\n\n/** A did:dht document in W3C DID Core JSON: no `@context`, every reference fully qualified. */\nexport interface DidDhtDocument {\n id: string;\n controller?: string | string[];\n alsoKnownAs?: string[];\n verificationMethod: DidDhtVerificationMethod[];\n authentication?: string[];\n assertionMethod?: string[];\n keyAgreement?: string[];\n capabilityInvocation?: string[];\n capabilityDelegation?: string[];\n service?: DidDhtService[];\n}\n\n/** Why a did:dht could not be read: the message is meant for a person. */\nexport class DidDhtError extends Error {\n constructor(message: string, readonly code: \"invalid-did\" | \"not-found\" | \"invalid-signature\" | \"malformed\" | \"unreachable\") {\n super(message);\n this.name = \"DidDhtError\";\n }\n}\n\nconst malformed = (detail: string) => new DidDhtError(`The did:dht document is malformed: ${detail}`, \"malformed\");\n\n// ── Identifiers ───────────────────────────────────────────────────────────────────────────────────────\n\n/** `did:dht:<z-base-32 of the Ed25519 public key>`. */\nexport function didDhtFromPublicKey(publicKey: Uint8Array): string {\n if (publicKey.length !== 32) throw new Error(\"A did:dht identity key is 32 bytes of Ed25519 public key\");\n return DID_DHT_PREFIX + toZ32(publicKey);\n}\n\n/** The identity key of a did:dht. Only the exact DID: no path, query or fragment, canonical z-base-32. */\nexport function didDhtKey(did: string): { z32: string; publicKey: Uint8Array } {\n const invalid = () => new DidDhtError(\"This is not a valid did:dht\", \"invalid-did\");\n if (typeof did !== \"string\" || !did.startsWith(DID_DHT_PREFIX)) throw invalid();\n const z32 = did.slice(DID_DHT_PREFIX.length);\n if (!/^[ybndrfg8ejkmcpqxot1uwisza345h769]{52}$/.test(z32)) throw invalid();\n const publicKey = fromZ32(z32);\n // 52 characters carry 260 bits: the last four must be zero, or two DIDs would name one key.\n if (toZ32(publicKey) !== z32) throw invalid();\n try {\n ed25519.Point.fromBytes(publicKey);\n } catch {\n throw invalid();\n }\n return { z32, publicKey };\n}\n\n// ── Documents ─────────────────────────────────────────────────────────────────────────────────────────\n\nconst KEY_TYPES = [\n { kty: \"OKP\", crv: \"Ed25519\", alg: \"EdDSA\" },\n { kty: \"EC\", crv: \"secp256k1\", alg: \"ES256K\" },\n { kty: \"EC\", crv: \"P-256\", alg: \"ES256\" },\n { kty: \"OKP\", crv: \"X25519\", alg: \"ECDH-ES+A256KW\" },\n] as const;\n\nconst RELATIONSHIPS = [\n [\"auth\", \"authentication\"],\n [\"asm\", \"assertionMethod\"],\n [\"agm\", \"keyAgreement\"],\n [\"inv\", \"capabilityInvocation\"],\n [\"del\", \"capabilityDelegation\"],\n] as const;\n\n/**\n * The document of an identity key alone, as the spec's Create step makes it: the key as `#0`, with the\n * four relationships it requires (authentication, assertion, capability invocation and delegation).\n * `alsoKnownAs` adds other identifiers of the same subject (URIs), which the document makes public.\n */\nexport function didDhtDocument(publicKey: Uint8Array, options: { alsoKnownAs?: string[] } = {}): DidDhtDocument {\n const id = didDhtFromPublicKey(publicKey);\n const key = `${id}#0`;\n return {\n id,\n ...(options.alsoKnownAs?.length ? { alsoKnownAs: [...options.alsoKnownAs] } : {}),\n verificationMethod: [{\n id: key,\n type: \"JsonWebKey\",\n controller: id,\n publicKeyJwk: { kty: \"OKP\", crv: \"Ed25519\", x: toBase64Url(publicKey), alg: \"EdDSA\", kid: \"0\" },\n }],\n authentication: [key],\n assertionMethod: [key],\n capabilityInvocation: [key],\n capabilityDelegation: [key],\n };\n}\n\n/** RFC 7638 thumbprint: SHA-256 of the required members in lexicographic order. */\nexport function jwkThumbprint(jwk: Pick<DidDhtJwk, \"kty\" | \"crv\" | \"x\" | \"y\">): string {\n const members = jwk.kty === \"EC\" ? { crv: jwk.crv, kty: jwk.kty, x: jwk.x, y: jwk.y } : { crv: jwk.crv, kty: jwk.kty, x: jwk.x };\n return toBase64Url(sha256(utf8Encode(JSON.stringify(members))));\n}\n\nfunction publicKeyBytes(type: number, jwk: DidDhtJwk): Uint8Array {\n const x = fromBase64Url(jwk.x);\n if (type === 0 || type === 3) return x;\n // Compressed point: the x-coordinate and the sign of y (the spec's size rule).\n const curve = type === 1 ? secp256k1 : p256;\n return curve.Point.fromAffine({ x: bigintOf(x), y: bigintOf(fromBase64Url(jwk.y ?? \"\")) }).toBytes(true);\n}\n\nfunction bigintOf(bytes: Uint8Array): bigint {\n let n = 0n;\n for (const b of bytes) n = (n << 8n) | BigInt(b);\n return n;\n}\n\nfunction jwkOf(type: number, bytes: Uint8Array, alg: string, kid: string): DidDhtJwk {\n const kind = KEY_TYPES[type];\n if (type === 0) {\n if (bytes.length !== 32) throw malformed(\"an Ed25519 key is not 32 bytes\");\n try { ed25519.Point.fromBytes(bytes); } catch { throw malformed(\"an Ed25519 key is not a point of the curve\"); }\n return { kty: \"OKP\", crv: \"Ed25519\", x: toBase64Url(bytes), alg, kid };\n }\n if (type === 3) {\n if (bytes.length !== 32) throw malformed(\"an X25519 key is not 32 bytes\");\n return { kty: \"OKP\", crv: \"X25519\", x: toBase64Url(bytes), alg, kid };\n }\n if (bytes.length !== 33 && bytes.length !== 65) throw malformed(`a ${kind.crv} key is neither a compressed nor an uncompressed point`);\n let raw: Uint8Array;\n try {\n raw = (type === 1 ? secp256k1 : p256).Point.fromBytes(bytes).toBytes(false);\n } catch {\n throw malformed(`a ${kind.crv} key is not a point of the curve`);\n }\n return { kty: \"EC\", crv: kind.crv, x: toBase64Url(raw.subarray(1, 33)), y: toBase64Url(raw.subarray(33)), alg, kid };\n}\n\n// ── Document → DNS records ────────────────────────────────────────────────────────────────────────────\n\nexport interface DidDhtPacketOptions {\n /** Type indexing (`_typ._did.`), integers from the spec's registry. */\n types?: number[];\n /** Authoritative gateways, as NS records of the root name. */\n gateways?: string[];\n /**\n * Other records of the same key. A key has exactly one packet: whatever else it publishes goes in\n * with the document, and the whole must fit the 1000 bytes.\n */\n extra?: TxtRecord[];\n}\n\nconst fragmentOf = (documentId: string, ref: string, what: string): string => {\n if (!ref.startsWith(`${documentId}#`)) throw new Error(`${what} ${ref} is not a fragment of ${documentId}`);\n const fragment = ref.slice(documentId.length + 1);\n if (!FRAGMENT.test(fragment)) throw new Error(`${what} ${ref} has an unusable fragment`);\n return fragment;\n};\n\nconst listValue = (values: string[], what: string): string => {\n for (const v of values) if (!v || /[,;\\s]/.test(v)) throw new Error(`${what} ${JSON.stringify(v)} cannot be written into a did:dht record`);\n return values.join(\",\");\n};\n\n/**\n * The DNS records of a document, in the spec's layout (names `_did.<ID>.` and `_kN._did.`). The root\n * record comes after the records it names, gateways last: the order @web5/dids writes, and the only one\n * it reads correctly (it resolves `vm`/`auth` references in packet order). Order-free readers don't mind.\n */\nexport function didDhtRecords(document: DidDhtDocument, options: DidDhtPacketOptions = {}): (TxtRecord | NsRecord)[] {\n const { z32, publicKey } = didDhtKey(document.id);\n const methods = document.verificationMethod;\n const first = methods[0];\n if (!first || first.id !== `${document.id}#0` || first.publicKeyJwk.crv !== \"Ed25519\" || !bytesEqual(fromBase64Url(first.publicKeyJwk.x), publicKey)) {\n throw new Error(\"The first verification method of a did:dht document is its identity key, #0\");\n }\n\n const aliases = new Map<string, string>();\n const keyRecords: TxtRecord[] = methods.map((method, index) => {\n const alias = `k${index}`;\n if (aliases.has(method.id)) throw new Error(`Verification method ${method.id} appears twice`);\n aliases.set(method.id, alias);\n const fragment = fragmentOf(document.id, method.id, \"Verification method\");\n const type = KEY_TYPES.findIndex((k) => k.crv === method.publicKeyJwk.crv);\n if (type < 0) throw new Error(`Key type ${method.publicKeyJwk.crv} has no did:dht index`);\n const jwk = method.publicKeyJwk;\n const parts: string[] = [];\n // The identity key's id is always 0, and one that is the thumbprint can be computed back: both are left out.\n if (index > 0 && fragment !== jwkThumbprint(jwk)) parts.push(`id=${fragment}`);\n parts.push(`t=${type}`, `k=${toBase64Url(publicKeyBytes(type, jwk))}`);\n if (jwk.alg !== KEY_TYPES[type].alg) parts.push(`a=${jwk.alg}`);\n if (method.controller !== document.id) parts.push(`c=${listValue([method.controller], \"Controller\")}`);\n return { name: `_${alias}._did.`, value: parts.join(\";\"), ttl: DID_DHT_TTL };\n });\n\n const services = document.service ?? [];\n const serviceRecords: TxtRecord[] = services.map((service, index) => {\n const parts = [\n `id=${fragmentOf(document.id, service.id, \"Service\")}`,\n `t=${listValue([service.type], \"Service type\")}`,\n `se=${listValue(service.serviceEndpoint, \"Service endpoint\")}`,\n ];\n for (const extra of [\"sig\", \"enc\"] as const) {\n const v = service[extra];\n if (v !== undefined) parts.push(`${extra}=${listValue(Array.isArray(v) ? v : [v], `Service ${extra}`)}`);\n }\n return { name: `_s${index}._did.`, value: parts.join(\";\"), ttl: DID_DHT_TTL };\n });\n\n const root = [`v=${DID_DHT_VERSION}`, `vm=${keyRecords.map((_, i) => `k${i}`).join(\",\")}`];\n for (const [short, property] of RELATIONSHIPS) {\n const refs = document[property] ?? [];\n if (refs.length === 0) continue;\n root.push(`${short}=${refs.map((ref) => {\n const alias = aliases.get(ref);\n if (!alias) throw new Error(`${property} refers to ${ref}, which is not a verification method of the document`);\n return alias;\n }).join(\",\")}`);\n }\n if (serviceRecords.length) root.push(`svc=${serviceRecords.map((_, i) => `s${i}`).join(\",\")}`);\n\n const records: (TxtRecord | NsRecord)[] = [];\n if (document.alsoKnownAs?.length) records.push({ name: \"_aka._did.\", value: listValue(document.alsoKnownAs, \"Also-known-as\"), ttl: DID_DHT_TTL });\n if (document.controller !== undefined) {\n const controllers = Array.isArray(document.controller) ? document.controller : [document.controller];\n records.push({ name: \"_cnt._did.\", value: listValue(controllers, \"Controller\"), ttl: DID_DHT_TTL });\n }\n records.push(...keyRecords, ...serviceRecords);\n if (options.types?.length) {\n for (const t of options.types) if (!Number.isSafeInteger(t) || t < 0) throw new Error(`Type ${t} is not a registry integer`);\n records.push({ name: \"_typ._did.\", value: `id=${options.types.join(\",\")}`, ttl: DID_DHT_TTL });\n }\n records.push({ name: `_did.${z32}.`, value: root.join(\";\"), ttl: DID_DHT_TTL });\n for (const host of options.gateways ?? []) records.push({ type: \"NS\", name: `_did.${z32}.`, host, ttl: DID_DHT_TTL });\n records.push(...(options.extra ?? []));\n return records;\n}\n\n/** The compressed, authoritative DNS packet of a document. Throws `PacketTooLargeError` past 1000 bytes. */\nexport function encodeDidDhtPacket(document: DidDhtDocument, options: DidDhtPacketOptions = {}): Uint8Array {\n const packet = encodeTxtPacket(didDhtRecords(document, options), { authoritative: true });\n if (packet.length > MAX_DNS_PACKET_BYTES) throw new PacketTooLargeError(packet.length);\n return packet;\n}\n\n/**\n * The packet that ends a DID: its root record alone, reading `deactivated`. Resolvers then answer with\n * `deactivated: true` and a document holding the id alone, until the record leaves the DHT.\n */\nexport function encodeDidDhtDeactivated(publicKey: Uint8Array): Uint8Array {\n const { z32 } = didDhtKey(didDhtFromPublicKey(publicKey));\n return encodeTxtPacket([{ name: `_did.${z32}.`, value: \"deactivated\", ttl: DID_DHT_TTL }], { authoritative: true });\n}\n\n/**\n * The relay payload of a document: its packet, signed by the identity key. `seq` is the BEP44 sequence\n * number, which did:dht defines as the UNIX time in seconds (Ghostly's chat records use microseconds).\n */\nexport function signDidDhtPacket(identity: Identity, dnsPacket: Uint8Array, seq: number): Uint8Array {\n if (!Number.isSafeInteger(seq) || seq < 0) throw new Error(\"A did:dht sequence number is a UNIX time in seconds\");\n return signRelayPayload(identity, dnsPacket, BigInt(seq));\n}\n\n// ── DNS records → document ────────────────────────────────────────────────────────────────────────────\n\nconst FRAGMENT = /^[A-Za-z0-9._~-]{1,64}$/;\nconst ALG = /^[A-Za-z0-9+._-]{1,32}$/;\nconst DID = /^did:[a-z0-9]+:[A-Za-z0-9._:%-]+$/;\nconst URI = /^[A-Za-z][A-Za-z0-9+.-]*:[^\\s,;]+$/;\n\n/** `a=1;b=x,y`: each property once, names in lower case. */\nfunction properties(value: string, where: string): Map<string, string> {\n const out = new Map<string, string>();\n for (const pair of value.split(\";\")) {\n const at = pair.indexOf(\"=\");\n const name = pair.slice(0, at);\n if (at <= 0 || !/^[a-z]+$/.test(name)) throw malformed(`${where} has a property that is not name=value`);\n if (out.has(name)) throw malformed(`${where} has ${name} twice`);\n out.set(name, pair.slice(at + 1));\n }\n return out;\n}\n\nfunction list(value: string | undefined, where: string, item: RegExp): string[] {\n if (value === undefined) return [];\n const items = value.split(\",\");\n for (const v of items) if (!item.test(v)) throw malformed(`${where} has an unexpected value`);\n return items;\n}\n\nexport interface DecodedDidDht {\n document: DidDhtDocument;\n /** The root record says `deactivated`: the document holds the id alone. */\n deactivated: boolean;\n /** Type indexing integers (`_typ._did.`), when present. */\n types?: number[];\n}\n\n/**\n * The document in a did:dht DNS packet. Other records of the key (anything not ending in `_did`) are\n * ignored, and so are did:dht records this version does not know (`_prv`, future ones); everything it\n * does know must be well formed and consistent, or the packet is refused.\n */\nexport function decodeDidDhtPacket(did: string, dnsPacket: Uint8Array): DecodedDidDht {\n const { z32, publicKey } = didDhtKey(did);\n let txt: TxtRecord[];\n try {\n txt = decodeTxtPacket(dnsPacket);\n } catch (error) {\n throw malformed(`not a DNS packet (${error instanceof Error ? error.message : String(error)})`);\n }\n\n // `_k0._did` as the spec writes it, or `_k0._did.<ID>` as a Pkarr client that appends its origin does.\n const byName = new Map<string, string>();\n let rootValue: string | undefined;\n for (const record of txt) {\n const name = record.name.toLowerCase().replace(/\\.$/, \"\");\n if (name === `_did.${z32}`) {\n if (rootValue !== undefined) throw malformed(\"two root records\");\n rootValue = record.value;\n continue;\n }\n const match = /^(_[a-z0-9]+)\\._did(?:\\.([a-z0-9]+))?$/.exec(name);\n if (!match || (match[2] !== undefined && match[2] !== z32)) continue;\n if (byName.has(match[1])) throw malformed(`two ${match[1]} records`);\n byName.set(match[1], record.value);\n }\n if (rootValue === undefined) throw malformed(\"no root record\");\n if (rootValue === \"deactivated\") return { document: { id: did, verificationMethod: [] }, deactivated: true };\n\n const root = properties(rootValue, \"the root record\");\n if (root.get(\"v\") !== DID_DHT_VERSION) throw malformed(`version ${root.get(\"v\") ?? \"(none)\"} is not supported`);\n const vm = list(root.get(\"vm\"), \"the root record's vm\", /^k\\d{1,3}$/);\n if (!vm.includes(\"k0\")) throw malformed(\"the identity key k0 is not listed\");\n if (new Set(vm).size !== vm.length) throw malformed(\"a key is listed twice\");\n\n const document: DidDhtDocument = { id: did, verificationMethod: [] };\n const methodIds = new Map<string, string>();\n for (const alias of vm) {\n const value = byName.get(`_${alias}`);\n if (value === undefined) throw malformed(`the key ${alias} has no record`);\n const props = properties(value, `the key ${alias}`);\n const type = Number(props.get(\"t\"));\n if (!/^[0-3]$/.test(props.get(\"t\") ?? \"\")) throw malformed(`the key ${alias} has an unknown type`);\n const alg = props.get(\"a\") ?? KEY_TYPES[type].alg;\n if (!ALG.test(alg)) throw malformed(`the key ${alias} has an unusable algorithm`);\n const k = props.get(\"k\") ?? \"\";\n const bytes = /^[A-Za-z0-9_-]+$/.test(k) ? fromBase64Url(k) : new Uint8Array();\n if (bytes.length === 0 || toBase64Url(bytes) !== k) throw malformed(`the key ${alias} is not unpadded base64url`);\n const controller = props.get(\"c\") ?? did;\n if (!DID.test(controller)) throw malformed(`the key ${alias} has a controller that is not a DID`);\n\n let fragment: string;\n let jwk: DidDhtJwk;\n if (alias === \"k0\") {\n if (type !== 0 || !bytesEqual(bytes, publicKey)) throw malformed(\"k0 is not the DID's identity key\");\n if (props.has(\"id\") && props.get(\"id\") !== \"0\") throw malformed(\"the identity key's id is not 0\");\n fragment = \"0\";\n jwk = jwkOf(type, bytes, alg, fragment);\n } else {\n const provisional = jwkOf(type, bytes, alg, \"\");\n fragment = props.get(\"id\") ?? jwkThumbprint(provisional);\n if (!FRAGMENT.test(fragment)) throw malformed(`the key ${alias} has an unusable id`);\n jwk = { ...provisional, kid: fragment };\n }\n const id = `${did}#${fragment}`;\n if ([...methodIds.values()].includes(id)) throw malformed(`two keys have the id ${fragment}`);\n methodIds.set(alias, id);\n document.verificationMethod.push({ id, type: \"JsonWebKey\", controller, publicKeyJwk: jwk });\n }\n\n for (const [short, property] of RELATIONSHIPS) {\n const refs = list(root.get(short), `the root record's ${short}`, /^k\\d{1,3}$/);\n if (refs.length === 0) continue;\n document[property] = refs.map((alias) => {\n const id = methodIds.get(alias);\n if (!id) throw malformed(`${short} names ${alias}, which is not a listed key`);\n return id;\n });\n }\n\n const services = list(root.get(\"svc\"), \"the root record's svc\", /^s\\d{1,3}$/);\n if (new Set(services).size !== services.length) throw malformed(\"a service is listed twice\");\n for (const alias of services) {\n const value = byName.get(`_${alias}`);\n if (value === undefined) throw malformed(`the service ${alias} has no record`);\n const props = properties(value, `the service ${alias}`);\n const fragment = props.get(\"id\") ?? \"\";\n const type = props.get(\"t\") ?? \"\";\n if (!FRAGMENT.test(fragment)) throw malformed(`the service ${alias} has an unusable id`);\n if (!/^[A-Za-z0-9._:/-]{1,64}$/.test(type)) throw malformed(`the service ${alias} has an unusable type`);\n const service: DidDhtService = { id: `${did}#${fragment}`, type, serviceEndpoint: list(props.get(\"se\") ?? \"\", `the service ${alias}'s endpoint`, URI) };\n for (const extra of [\"sig\", \"enc\"] as const) {\n const v = props.get(extra);\n if (v !== undefined) service[extra] = v.includes(\",\") ? list(v, `the service ${alias}'s ${extra}`, /^[^\\s,;]+$/) : v;\n }\n if (document.service?.some((s) => s.id === service.id)) throw malformed(`two services have the id ${fragment}`);\n (document.service ??= []).push(service);\n }\n\n const controller = byName.get(\"_cnt\");\n if (controller !== undefined) {\n const controllers = list(controller, \"the controller record\", DID);\n document.controller = controllers.length === 1 ? controllers[0] : controllers;\n }\n const aka = byName.get(\"_aka\");\n if (aka !== undefined) document.alsoKnownAs = list(aka, \"the also-known-as record\", URI);\n\n const decoded: DecodedDidDht = { document, deactivated: false };\n const typ = byName.get(\"_typ\");\n if (typ !== undefined) {\n const props = properties(typ, \"the type record\");\n decoded.types = list(props.get(\"id\"), \"the type record\", /^\\d{1,9}$/).map(Number);\n }\n return decoded;\n}\n\n// ── Resolution ────────────────────────────────────────────────────────────────────────────────────────\n\nexport interface DidDhtFetchResponse { status: number; bytes: Uint8Array }\n/** A bounded GET (the identity proofs' `ctx.fetch` is one). */\nexport type DidDhtFetch = (url: string, options?: { maxBytes?: number; signal?: AbortSignal; redirect?: \"error\" }) => Promise<DidDhtFetchResponse>;\n\nexport interface ResolveDidDhtOptions {\n fetch: DidDhtFetch;\n /** Pkarr relays (or did:dht gateways, which answer the same GET) to ask in turn. Default: `DEFAULT_RELAYS`. */\n relays?: string[];\n signal?: AbortSignal;\n}\n\nexport interface DidDhtMetadata {\n /** The BEP44 sequence number. */\n versionId: string;\n /** When it was published, from the sequence number. */\n updated: string;\n deactivated?: true;\n types?: number[];\n}\n\nexport interface DidDhtResolution {\n document: DidDhtDocument;\n metadata: DidDhtMetadata;\n /** The relay that answered. */\n relay: string;\n}\n\n/** A packet opened and decoded: the signature is the identity key's, the document well formed. */\nexport function openDidDhtPayload(did: string, payload: Uint8Array): DecodedDidDht & { seq: bigint } {\n const { z32 } = didDhtKey(did);\n let opened: { seq: bigint; dnsPacket: Uint8Array };\n try {\n opened = openRelayPayload(z32, payload);\n } catch (error) {\n if (error instanceof Error && error.message === \"Invalid signature\") {\n throw new DidDhtError(\"The did:dht record is not signed by the DID's key\", \"invalid-signature\");\n }\n throw malformed(error instanceof Error ? error.message : String(error));\n }\n return { ...decodeDidDhtPacket(did, opened.dnsPacket), seq: opened.seq };\n}\n\n/** The spec's `seq` is in seconds; Pkarr clients write microseconds. Either way, a date. */\nfunction seqDate(seq: bigint): string {\n const n = Number(seq);\n const ms = n < 1e11 ? n * 1000 : n < 1e14 ? n : n / 1000;\n const date = new Date(ms);\n return Number.isNaN(date.getTime()) ? new Date(0).toISOString() : date.toISOString();\n}\n\n/**\n * Resolves a did:dht through Pkarr relays: one GET per relay, in turn, the next only when one has\n * nothing, fails or serves a packet that does not check out. The answer is bounded to a BEP44 item,\n * its signature checked against the DID's key and its records parsed strictly. A deactivated DID\n * resolves (`metadata.deactivated`); everything else that goes wrong throws a `DidDhtError`.\n */\nexport async function resolveDidDht(did: string, options: ResolveDidDhtOptions): Promise<DidDhtResolution> {\n const { z32 } = didDhtKey(did);\n const relays = [...new Set((options.relays ?? DEFAULT_RELAYS).map(normalizeRelayUrl).filter((r): r is string => r !== null))];\n if (relays.length === 0) throw new DidDhtError(\"No Pkarr relay to ask\", \"unreachable\");\n\n let notFound = false;\n let refused: DidDhtError | undefined;\n for (const relay of relays) {\n options.signal?.throwIfAborted();\n let response: DidDhtFetchResponse;\n try {\n response = await options.fetch(`${relay}/${z32}`, { maxBytes: DID_DHT_MAX_PAYLOAD, signal: options.signal, redirect: \"error\" });\n } catch (error) {\n if (options.signal?.aborted) throw error;\n continue;\n }\n if (response.status === 404) {\n notFound = true;\n continue;\n }\n if (response.status !== 200) continue;\n try {\n const opened = openDidDhtPayload(did, response.bytes);\n return {\n document: opened.document,\n metadata: {\n versionId: opened.seq.toString(),\n updated: seqDate(opened.seq),\n ...(opened.deactivated ? { deactivated: true as const } : {}),\n ...(opened.types ? { types: opened.types } : {}),\n },\n relay,\n };\n } catch (error) {\n if (!(error instanceof DidDhtError)) throw error;\n refused = error;\n }\n }\n if (refused) throw refused;\n if (notFound) throw new DidDhtError(\"No document is published for this did:dht\", \"not-found\");\n throw new DidDhtError(\"No Pkarr relay answered for this did:dht\", \"unreachable\");\n}\n","import { ed25519 } from \"@noble/curves/ed25519.js\";\nimport { p256 } from \"@noble/curves/nist.js\";\nimport { secp256k1 } from \"@noble/curves/secp256k1.js\";\nimport { base58 } from \"@scure/base\";\nimport { bytesEqual, concatBytes, fromZ32, toBase64Url, toZ32, utf8Decode, utf8Encode } from \"./bytes\";\nimport { normalizeDomain } from \"./domainProofs\";\n\n/**\n * Decentralized identifiers (W3C DID Core 1.0) as external identities, network-free half: DID syntax,\n * the documents did:key and did:jwk carry in the identifier itself, did:web's URLs, strict parsing of a\n * fetched DID document, and JWS / raw signatures by the keys it lists. Fetching did:web documents and\n * did:dht records is the browser's (`packages/browser/src/proofs/did.ts`).\n *\n * Only keys a document lists under `authentication` or `assertionMethod` may sign for the DID: a key\n * listed only for key agreement or capability delegation does not speak for it.\n */\n\nexport class DidError extends Error {}\n\nexport const DID_METHODS = [\"key\", \"jwk\", \"dht\", \"web\"] as const;\nexport type DidMethod = (typeof DID_METHODS)[number];\nexport type DidCurve = \"Ed25519\" | \"secp256k1\" | \"P-256\";\nexport type DidRelationship = \"authentication\" | \"assertionMethod\";\nconst RELATIONSHIPS: readonly DidRelationship[] = [\"authentication\", \"assertionMethod\"];\n\n/** Same bound as an identity proof's subject (printable ASCII, no spaces). */\nexport const DID_MAX_LENGTH = 512;\n/** A fetched did.json. */\nexport const DID_DOCUMENT_MAX_BYTES = 64 * 1024;\n/** The statement file a did:web publishes beside its did.json. */\nexport const DID_FILE_MAX_BYTES = 16 * 1024;\nexport const DID_JWS_MAX_LENGTH = 4096;\n/** Entries read from each list of a document; the rest are ignored. */\nconst MAX_ENTRIES = 64;\n/** The service type a did.json uses to vouch for a Ghostly proof. */\nexport const DID_GHOSTLY_SERVICE = \"GhostlyIdentityProof\";\n\nexport interface DidKey {\n /** The verification method's absolute id: `<did>#<fragment>`. */\n id: string;\n curve: DidCurve;\n /** Ed25519: 32 bytes. secp256k1 and P-256: 33 bytes, compressed. */\n publicKey: Uint8Array;\n /** Empty when the document lists the key but not for authentication or assertionMethod. */\n relationships: DidRelationship[];\n}\n\n/** A `GhostlyIdentityProof` service: the document names a proof key and the statement id it vouches for. */\nexport interface DidGhostlyService { id: string; key: string; proof: string }\n\n/** What a DID document says, as far as Ghostly uses it. */\nexport interface DidDocumentView {\n did: string;\n keys: DidKey[];\n /** Verification methods Ghostly cannot use (other curves, key agreement keys, other DIDs' ids). */\n skipped: number;\n services: DidGhostlyService[];\n}\n\nconst METHOD_NAME = /^[a-z0-9]+$/;\nconst Z32_KEY = /^[ybndrfg8ejkmcpqxot1uwisza345h769]{52}$/;\nconst B64URL = /^[A-Za-z0-9_-]*$/;\nconst SEGMENT = /^[A-Za-z0-9._~-]{1,64}$/;\nconst HEX64 = /^[a-f0-9]{64}$/;\n\nconst METHOD_LIST = \"did:key, did:jwk, did:dht and did:web\";\n\n/** base64url without padding, strictly: decoding and encoding again gives the same text. */\nexport function strictBase64Url(value: string): Uint8Array {\n if (typeof value !== \"string\" || !B64URL.test(value) || value.length % 4 === 1) throw new DidError(\"Not base64url\");\n const pad = \"=\".repeat((4 - (value.length % 4)) % 4);\n const bytes = Uint8Array.from(atob(value.replace(/-/g, \"+\").replace(/_/g, \"/\") + pad), c => c.charCodeAt(0));\n if (toBase64Url(bytes) !== value) throw new DidError(\"Not canonical base64url\");\n return bytes;\n}\n\nconst isObject = (v: unknown): v is Record<string, unknown> => !!v && typeof v === \"object\" && !Array.isArray(v);\n\n// ---------------------------------------------------------------------------------------------\n// Syntax\n\nexport interface ParsedDid { did: string; method: DidMethod; id: string }\n\n/**\n * The DID a person typed, checked and in canonical form, or a message they can act on. Methods other than\n * the four Ghostly checks are refused with \"not supported yet\"; a DID URL (a `#fragment`, `?query` or\n * `/path`) is refused, since it names something inside a DID rather than the DID.\n */\nexport function parseDid(input: string): ParsedDid {\n if (typeof input !== \"string\") throw new DidError(\"Enter a DID, like did:key:z6Mk… or did:web:example.com\");\n const value = input.trim();\n if (!/^did:/i.test(value)) throw new DidError(\"Enter a DID, like did:key:z6Mk… or did:web:example.com\");\n if (value.length > DID_MAX_LENGTH) throw new DidError(`That DID is too long (at most ${DID_MAX_LENGTH} characters)`);\n if (/[#?/]/.test(value)) throw new DidError(\"Enter the DID alone, without a #fragment, ?query or /path\");\n const rest = value.slice(4);\n const colon = rest.indexOf(\":\");\n const method = colon > 0 ? rest.slice(0, colon) : \"\";\n const id = colon > 0 ? rest.slice(colon + 1) : \"\";\n if (!METHOD_NAME.test(method.toLowerCase()) || !id) throw new DidError(\"That is not a DID: it looks like did:<method>:<identifier>\");\n if (method !== method.toLowerCase() && DID_METHODS.includes(method.toLowerCase() as DidMethod)) throw new DidError(`Write the method in lowercase: did:${method.toLowerCase()}:…`);\n if (!DID_METHODS.includes(method as DidMethod)) throw new DidError(`did:${method} is not supported yet. Ghostly checks ${METHOD_LIST}.`);\n if (!/^[\\x21-\\x7e]+$/.test(id)) throw new DidError(\"A DID has no spaces or other special characters\");\n switch (method as DidMethod) {\n case \"key\": staticKey(`did:key:${id}`); return { did: `did:key:${id}`, method: \"key\", id };\n case \"jwk\": staticKey(`did:jwk:${id}`); return { did: `did:jwk:${id}`, method: \"jwk\", id };\n case \"dht\": {\n const key = id.toLowerCase();\n if (!Z32_KEY.test(key) || toZ32(fromZ32(key)) !== key) throw new DidError(\"A did:dht identifier is 52 z-base-32 characters\");\n return { did: `did:dht:${key}`, method: \"dht\", id: key };\n }\n case \"web\": {\n const web = parseWeb(id);\n return { did: `did:web:${web.id}`, method: \"web\", id: web.id };\n }\n }\n}\n\n/** The canonical form of a DID Ghostly checks (a lowercase did:web host, a lowercase did:dht key). */\nexport const normalizeDid = (input: string) => parseDid(input).did;\n\n// ---------------------------------------------------------------------------------------------\n// did:web\n\ninterface WebDid { id: string; host: string; port?: number; path: string[] }\n\nfunction parseWeb(id: string): WebDid {\n const [hostPart, ...path] = id.split(\":\");\n const portMatch = /^(.*)%3a(\\d{1,5})$/i.exec(hostPart);\n const hostInput = portMatch ? portMatch[1] : hostPart;\n if (/%/.test(hostInput)) throw new DidError(\"A did:web domain has no percent-encoding, other than %3A before a port\");\n let host: string;\n try { host = normalizeDomain(hostInput); } catch (e) { throw new DidError(`did:web: ${(e as Error).message}`); }\n if (hostInput.toLowerCase().replace(/\\.$/, \"\") !== host) throw new DidError(\"did:web: write the domain as ASCII (punycode for international names)\");\n let port: number | undefined;\n if (portMatch) {\n port = Number(portMatch[2]);\n if (!(port >= 1 && port <= 65535) || String(port) !== portMatch[2]) throw new DidError(\"did:web: that port is not valid\");\n }\n if (path.length > 8) throw new DidError(\"did:web: at most 8 path segments\");\n for (const segment of path) {\n if (!SEGMENT.test(segment) || segment === \".\" || segment === \"..\") throw new DidError(\"did:web: each path segment is letters, digits, dots, dashes, underscores or tildes\");\n }\n return { id: [`${host}${port ? `%3A${port}` : \"\"}`, ...path].join(\":\"), host, port, path };\n}\n\nconst web = (did: string): WebDid => {\n const parsed = parseDid(did);\n if (parsed.method !== \"web\") throw new DidError(\"Not a did:web\");\n return parseWeb(parsed.id);\n};\n\n/** The host a did:web lives on (no port): the name looked up before contacting it. */\nexport const didWebHost = (did: string) => web(did).host;\nconst origin = (w: WebDid) => `https://${w.host}${w.port ? `:${w.port}` : \"\"}`;\n/** did:web → its did.json: `/.well-known/did.json` for a bare domain, `/<path>/did.json` otherwise. */\nexport function didWebDocumentUrl(did: string): string {\n const w = web(did);\n return `${origin(w)}/${w.path.length ? w.path.join(\"/\") : \".well-known\"}/did.json`;\n}\n/** Where a did:web publishes the statement of proof `proofId`: `ghostly/<id>.json` beside its did.json. */\nexport function didWebFileUrl(did: string, proofId: string): string {\n if (!HEX64.test(proofId)) throw new DidError(\"Invalid proof id\");\n const w = web(did);\n return `${origin(w)}/${w.path.length ? w.path.join(\"/\") : \".well-known\"}/ghostly/${proofId}.json`;\n}\n\n/** The body of that file: the DID and the exact statement text. */\nexport function didWebFile(did: string, statement: string): string {\n return JSON.stringify({ ghostly: 1, did, statement }, null, 2) + \"\\n\";\n}\n/** True when `text` is a statement file for exactly this DID and statement. */\nexport function didWebFileNames(text: string, did: string, statement: string): boolean {\n if (typeof text !== \"string\" || utf8Encode(text).length > DID_FILE_MAX_BYTES) return false;\n let body: unknown;\n try { body = JSON.parse(text); } catch { return false; }\n return isObject(body) && body.ghostly === 1 && body.did === did && body.statement === statement;\n}\n\n/** The service entry a did.json can carry instead of the file. */\nexport function didGhostlyServiceEntry(did: string, key: string, proofId: string): Record<string, unknown> {\n return { id: `${did}#ghostly-${proofId.slice(0, 12)}`, type: DID_GHOSTLY_SERVICE, serviceEndpoint: { key, proof: proofId } };\n}\n\n// ---------------------------------------------------------------------------------------------\n// Keys\n\n/** Multicodec prefixes (unsigned varint) of the public keys Ghostly checks, and X25519 to name it when refused. */\nconst MULTICODEC: { prefix: [number, number]; curve: DidCurve | \"X25519\"; length: number }[] = [\n { prefix: [0xed, 0x01], curve: \"Ed25519\", length: 32 },\n { prefix: [0xe7, 0x01], curve: \"secp256k1\", length: 33 },\n { prefix: [0x80, 0x24], curve: \"P-256\", length: 33 },\n { prefix: [0xec, 0x01], curve: \"X25519\", length: 32 },\n];\n\n/** A public key on its curve, validated; ECDSA keys compressed. */\nfunction curveKey(curve: DidCurve, bytes: Uint8Array): Uint8Array {\n try {\n if (curve === \"Ed25519\") { if (bytes.length !== 32) throw new Error(); ed25519.Point.fromBytes(bytes); return bytes; }\n const point = (curve === \"secp256k1\" ? secp256k1 : p256).Point.fromBytes(bytes);\n return point.toBytes(true);\n } catch { throw new DidError(`That ${curve} key is not a valid point`); }\n}\n\n/** A `z…` multibase (base58btc) multicodec key. X25519 and unknown codecs are `undefined`. */\nfunction multikey(value: string): { curve: DidCurve; publicKey: Uint8Array } | { curve: \"X25519\" } | undefined {\n if (typeof value !== \"string\" || !value.startsWith(\"z\") || value.length > 128) return undefined;\n let bytes: Uint8Array;\n try { bytes = base58.decode(value.slice(1)); } catch { return undefined; }\n const codec = MULTICODEC.find(c => bytes[0] === c.prefix[0] && bytes[1] === c.prefix[1]);\n if (!codec || bytes.length !== codec.length + 2) return undefined;\n if (codec.curve === \"X25519\") return { curve: \"X25519\" };\n return { curve: codec.curve, publicKey: curveKey(codec.curve, bytes.subarray(2)) };\n}\n\nconst JWK_CURVES: Record<string, { kty: string; curve: DidCurve }> = {\n Ed25519: { kty: \"OKP\", curve: \"Ed25519\" },\n secp256k1: { kty: \"EC\", curve: \"secp256k1\" },\n \"P-256\": { kty: \"EC\", curve: \"P-256\" },\n};\n\n/** A public JWK. Other curves are `undefined`; a malformed one throws. */\nfunction jwkKey(jwk: unknown): { curve: DidCurve; publicKey: Uint8Array } | undefined {\n if (!isObject(jwk)) throw new DidError(\"That JWK is not an object\");\n const spec = typeof jwk.crv === \"string\" ? JWK_CURVES[jwk.crv] : undefined;\n if (!spec || jwk.kty !== spec.kty) return undefined;\n const coordinate = (v: unknown) => { const b = strictBase64Url(v as string); if (b.length !== 32) throw new DidError(\"JWK coordinates are 32 bytes\"); return b; };\n try {\n if (spec.curve === \"Ed25519\") return { curve: \"Ed25519\", publicKey: curveKey(\"Ed25519\", coordinate(jwk.x)) };\n return { curve: spec.curve, publicKey: curveKey(spec.curve, concatBytes(Uint8Array.of(4), coordinate(jwk.x), coordinate(jwk.y))) };\n } catch (e) { throw e instanceof DidError && /point/.test(e.message) ? e : new DidError(`That ${spec.curve} JWK is malformed`); }\n}\n\n/**\n * The single key of a did:key or did:jwk, from the identifier itself, as the method specs build their\n * documents: did:key's `#<multibase>` and did:jwk's `#0`, both for authentication and assertionMethod\n * (a did:jwk marked `\"use\": \"enc\"` is for encryption only).\n */\nfunction staticKey(did: string): DidKey {\n if (did.startsWith(\"did:key:\")) {\n const mb = did.slice(8);\n const key = multikey(mb);\n if (!key) throw new DidError(\"That did:key is not an Ed25519, secp256k1 or P-256 key Ghostly can read\");\n if (key.curve === \"X25519\") throw new DidError(\"That did:key is an X25519 encryption key: it cannot sign\");\n return { id: `${did}#${mb}`, curve: key.curve, publicKey: key.publicKey, relationships: [...RELATIONSHIPS] };\n }\n let jwk: unknown;\n try { jwk = JSON.parse(utf8Decode(strictBase64Url(did.slice(8)))); } catch { throw new DidError(\"That did:jwk does not hold a JSON Web Key\"); }\n if (!isObject(jwk)) throw new DidError(\"That did:jwk does not hold a JSON Web Key\");\n if (\"d\" in jwk || \"p\" in jwk || \"k\" in jwk) throw new DidError(\"That did:jwk contains a private key. Never share it: make a new key\");\n const key = jwkKey(jwk);\n if (!key) throw new DidError(\"That did:jwk is not an Ed25519, secp256k1 or P-256 key\");\n return { id: `${did}#0`, curve: key.curve, publicKey: key.publicKey, relationships: jwk.use === \"enc\" ? [] : [...RELATIONSHIPS] };\n}\n\n/** The document of a did:key or did:jwk, which needs no network. */\nexport function staticDidDocument(did: string): DidDocumentView {\n const parsed = parseDid(did);\n if (parsed.method !== \"key\" && parsed.method !== \"jwk\") throw new DidError(`did:${parsed.method} is resolved over the network`);\n return { did: parsed.did, keys: [staticKey(parsed.did)], skipped: 0, services: [] };\n}\n\n// ---------------------------------------------------------------------------------------------\n// Documents\n\n/** An id as absolute `<did>#<fragment>`, from `#frag` or the absolute form; undefined for another DID's. */\nfunction methodId(did: string, id: unknown): string | undefined {\n if (typeof id !== \"string\" || id.length > DID_MAX_LENGTH + 128) return undefined;\n const absolute = id.startsWith(\"#\") ? `${did}${id}` : id;\n return absolute.startsWith(`${did}#`) && absolute.length > did.length + 1 && /^[\\x21-\\x7e]+$/.test(absolute) ? absolute : undefined;\n}\n\n/** Key material of one verification method: `publicKeyJwk`, `publicKeyMultibase` or (Ed25519 2018) `publicKeyBase58`. */\nfunction methodKey(vm: Record<string, unknown>): { curve: DidCurve; publicKey: Uint8Array } | undefined {\n try {\n if (vm.publicKeyJwk !== undefined) return jwkKey(vm.publicKeyJwk);\n if (typeof vm.publicKeyMultibase === \"string\") {\n const key = multikey(vm.publicKeyMultibase);\n if (key && key.curve !== \"X25519\") return key;\n // Ed25519VerificationKey2020 documents written before multicodec prefixes carry the raw 32 bytes.\n if (!key && vm.type === \"Ed25519VerificationKey2020\" && vm.publicKeyMultibase.startsWith(\"z\")) {\n const raw = base58.decode(vm.publicKeyMultibase.slice(1));\n if (raw.length === 32) return { curve: \"Ed25519\", publicKey: curveKey(\"Ed25519\", raw) };\n }\n return undefined;\n }\n if (typeof vm.publicKeyBase58 === \"string\" && vm.type === \"Ed25519VerificationKey2018\" && vm.publicKeyBase58.length <= 64) {\n return { curve: \"Ed25519\", publicKey: curveKey(\"Ed25519\", base58.decode(vm.publicKeyBase58)) };\n }\n } catch { return undefined; }\n return undefined;\n}\n\nconst list = (value: unknown, name: string): unknown[] => {\n if (value === undefined) return [];\n if (!Array.isArray(value)) throw new DidError(`The DID document's ${name} is not a list`);\n return value.slice(0, MAX_ENTRIES);\n};\n\n/**\n * A fetched or resolved DID document, strictly: it must be for exactly `did`; verification methods are\n * read from `verificationMethod` and from `authentication` / `assertionMethod` (references or embedded);\n * `GhostlyIdentityProof` services are kept. Keys Ghostly cannot use are counted in `skipped`.\n */\nexport function parseDidDocument(did: string, raw: unknown): DidDocumentView {\n if (!isObject(raw)) throw new DidError(\"The DID document is not a JSON object\");\n if (raw.id !== did) throw new DidError(typeof raw.id === \"string\" ? `The DID document is for another DID (${raw.id.slice(0, 80)})` : \"The DID document has no id\");\n const keys = new Map<string, DidKey>();\n const unusable = new Set<string>();\n let skipped = 0;\n const add = (vm: unknown): string | undefined => {\n if (!isObject(vm)) { skipped++; return undefined; }\n const id = methodId(did, vm.id);\n if (!id) { skipped++; return undefined; }\n if (keys.has(id) || unusable.has(id)) return id;\n const key = methodKey(vm);\n if (!key) { unusable.add(id); skipped++; return id; }\n keys.set(id, { id, ...key, relationships: [] });\n return id;\n };\n for (const vm of list(raw.verificationMethod, \"verificationMethod\")) add(vm);\n for (const relationship of RELATIONSHIPS) {\n for (const entry of list(raw[relationship], relationship)) {\n const id = typeof entry === \"string\" ? methodId(did, entry) : add(entry);\n const key = id ? keys.get(id) : undefined;\n if (key && !key.relationships.includes(relationship)) key.relationships.push(relationship);\n }\n }\n const services: DidGhostlyService[] = [];\n for (const s of list(raw.service, \"service\")) {\n if (!isObject(s)) continue;\n const types = Array.isArray(s.type) ? s.type : [s.type];\n const endpoint = s.serviceEndpoint;\n if (!types.includes(DID_GHOSTLY_SERVICE) || !isObject(endpoint)) continue;\n if (typeof endpoint.key === \"string\" && Z32_KEY.test(endpoint.key) && typeof endpoint.proof === \"string\" && HEX64.test(endpoint.proof))\n services.push({ id: typeof s.id === \"string\" ? s.id.slice(0, 200) : \"\", key: endpoint.key, proof: endpoint.proof });\n }\n return { did, keys: [...keys.values()].slice(0, MAX_ENTRIES), skipped, services: services.slice(0, 16) };\n}\n\n/** Keys that may sign for the DID. */\nexport const signingKeys = (doc: DidDocumentView) => doc.keys.filter(k => k.relationships.length > 0);\n\n// ---------------------------------------------------------------------------------------------\n// Signatures\n\n/** JWS `alg` values Ghostly checks, and the curve each needs (RFC 8037, RFC 8812, RFC 7518, RFC 9864's Ed25519). */\nexport const DID_JWS_ALGS: Record<string, DidCurve> = { EdDSA: \"Ed25519\", Ed25519: \"Ed25519\", ES256K: \"secp256k1\", ES256: \"P-256\" };\n/** The `alg` a person's tool should write for a key on this curve. */\nexport const didJwsAlg = (curve: DidCurve) => ({ Ed25519: \"EdDSA\", secp256k1: \"ES256K\", \"P-256\": \"ES256\" } as const)[curve];\n\nexport interface DidJws {\n alg: string;\n curve: DidCurve;\n kid?: string;\n /** RFC 7797: false signs the payload as it is (detached only). */\n b64: boolean;\n /** The protected header and payload segments as they were, for the signing input. */\n header: string;\n payload: string;\n signature: Uint8Array;\n}\n\n/** A compact JWS, attached or detached (`header..signature`), checked for shape only. */\nexport function parseDidJws(input: string): DidJws {\n const jws = typeof input === \"string\" ? input.trim() : \"\";\n if (!jws || jws.length > DID_JWS_MAX_LENGTH) throw new DidError(\"That is not a compact JWS\");\n const parts = jws.split(\".\");\n if (parts.length !== 3 || !parts[0] || !parts[2]) throw new DidError(\"A compact JWS has three parts separated by dots\");\n let header: unknown;\n try {\n const bytes = strictBase64Url(parts[0]);\n if (bytes.length > 2048) throw new Error();\n header = JSON.parse(utf8Decode(bytes));\n } catch { throw new DidError(\"The JWS header is not base64url JSON\"); }\n if (!isObject(header)) throw new DidError(\"The JWS header is not a JSON object\");\n const alg = header.alg;\n if (typeof alg !== \"string\" || !DID_JWS_ALGS[alg]) throw new DidError(`The JWS uses ${typeof alg === \"string\" ? alg.slice(0, 16) : \"no algorithm\"}: Ghostly checks EdDSA, ES256K and ES256`);\n if (header.kid !== undefined && (typeof header.kid !== \"string\" || header.kid.length > DID_MAX_LENGTH + 128)) throw new DidError(\"The JWS kid is not a key id\");\n if (header.b64 !== undefined && typeof header.b64 !== \"boolean\") throw new DidError(\"The JWS b64 header is not true or false\");\n const crit = header.crit;\n if (crit !== undefined && (!Array.isArray(crit) || !crit.length || crit.some(c => c !== \"b64\"))) throw new DidError(\"The JWS requires an extension Ghostly does not know (crit)\");\n const b64 = header.b64 !== false;\n if (!b64 && !(Array.isArray(crit) && crit.includes(\"b64\"))) throw new DidError(\"A JWS with b64 false must list it in crit\");\n if (!b64 && parts[1]) throw new DidError(\"A JWS with b64 false must be detached (header..signature)\");\n let signature: Uint8Array;\n try { signature = strictBase64Url(parts[2]); } catch { throw new DidError(\"The JWS signature is not base64url\"); }\n if (signature.length !== 64) throw new DidError(`A ${alg} signature is 64 bytes`);\n if (parts[1]) { try { strictBase64Url(parts[1]); } catch { throw new DidError(\"The JWS payload is not base64url\"); } }\n return { alg, curve: DID_JWS_ALGS[alg], kid: header.kid as string | undefined, b64, header: parts[0], payload: parts[1], signature };\n}\n\n/** What a JWS signs for one candidate message: its own payload when attached (which must be that message). */\nfunction signingInput(jws: DidJws, message: Uint8Array): Uint8Array | undefined {\n if (jws.payload) return bytesEqual(strictBase64Url(jws.payload), message) ? utf8Encode(`${jws.header}.${jws.payload}`) : undefined;\n return jws.b64 ? utf8Encode(`${jws.header}.${toBase64Url(message)}`) : concatBytes(utf8Encode(`${jws.header}.`), message);\n}\n\nfunction verifyWith(key: DidKey, signature: Uint8Array, message: Uint8Array, format: \"compact\" | \"der\" = \"compact\"): boolean {\n try {\n if (key.curve === \"Ed25519\") return format === \"compact\" && ed25519.verify(signature, message, key.publicKey);\n // ECDSA over SHA-256 of the message, either half of s (JOSE does not require low S).\n return (key.curve === \"secp256k1\" ? secp256k1 : p256).verify(signature, message, key.publicKey, { prehash: true, lowS: false, format });\n } catch { return false; }\n}\n\nexport interface DidSignatureCheck { key: DidKey; alg: string }\n\nconst refusedKey = (key: DidKey) => new DidError(`Signed by ${key.id}, which the DID document does not list under authentication or assertionMethod`);\n\n/**\n * Which key of `doc` made this JWS over one of `messages` (the statement, and the statement with a\n * trailing newline some tools add). With a `kid`, only that key is tried. Refused unless the key is\n * listed for authentication or assertionMethod.\n */\nexport function checkDidJws(input: string, messages: Uint8Array[], doc: DidDocumentView): DidSignatureCheck {\n const jws = parseDidJws(input);\n let candidates = doc.keys.filter(k => k.curve === jws.curve);\n if (jws.kid !== undefined) {\n const kid = methodId(doc.did, jws.kid);\n if (!kid) throw new DidError(`The JWS names key ${jws.kid.slice(0, 100)}, which is not a key of ${doc.did}`);\n candidates = candidates.filter(k => k.id === kid);\n if (!candidates.length) throw new DidError(doc.keys.some(k => k.id === kid) ? `Key ${kid} is not a ${jws.curve} key: the JWS says ${jws.alg}` : `The DID document lists no key ${kid}`);\n }\n if (!candidates.length) throw new DidError(`The DID document lists no ${jws.curve} key for ${jws.alg}`);\n let inputs: Uint8Array[];\n try { inputs = messages.map(m => signingInput(jws, m)).filter((m): m is Uint8Array => !!m); } catch { inputs = []; }\n if (!inputs.length) throw new DidError(\"The JWS carries another text: sign exactly the statement shown\");\n for (const key of candidates) {\n if (!inputs.some(m => verifyWith(key, jws.signature, m))) continue;\n if (!key.relationships.length) throw refusedKey(key);\n return { key, alg: jws.alg };\n }\n throw new DidError(\"The JWS signature does not match: sign exactly the statement shown, with a key of this DID\");\n}\n\n/**\n * Which key of `doc` made this raw signature over one of `messages`: Ed25519 (64 bytes), or ECDSA with\n * SHA-256 on secp256k1 or P-256 (64 bytes r‖s, or DER as OpenSSL writes it). `vm` narrows it to one key.\n * Returns the signature in its canonical form (r‖s for ECDSA).\n */\nexport function checkDidSignature(signature: Uint8Array, messages: Uint8Array[], doc: DidDocumentView, vm?: string): DidSignatureCheck & { signature: Uint8Array } {\n let candidates = doc.keys;\n if (vm !== undefined) {\n const id = methodId(doc.did, vm);\n if (!id) throw new DidError(`${vm.slice(0, 100)} is not a key of ${doc.did}`);\n candidates = candidates.filter(k => k.id === id);\n if (!candidates.length) throw new DidError(`The DID document lists no key ${id}`);\n }\n const der = signature.length !== 64 && signature[0] === 0x30;\n if (signature.length !== 64 && !der) throw new DidError(\"A signature is 64 bytes (or DER, for ECDSA)\");\n for (const key of candidates) {\n if (der && key.curve === \"Ed25519\") continue;\n if (!messages.some(m => verifyWith(key, signature, m, der ? \"der\" : \"compact\"))) continue;\n if (!key.relationships.length) throw refusedKey(key);\n const canonical = der ? (key.curve === \"secp256k1\" ? secp256k1 : p256).Signature.fromBytes(signature, \"der\").toBytes(\"compact\") : signature;\n return { key, alg: key.curve === \"Ed25519\" ? \"Ed25519\" : `ECDSA ${key.curve}`, signature: canonical };\n }\n throw new DidError(\"The signature does not match: sign exactly the statement shown, with a key of this DID\");\n}\n\n/**\n * What a person pasted as a signature: a compact JWS, or a raw signature (hex, base64 or base64url),\n * optionally after the verification method id (`did:…#key-1 <signature>` or `#key-1 <signature>`).\n */\nexport type DidSignaturePaste = { kind: \"jws\"; jws: string } | { kind: \"raw\"; vm?: string; signature: Uint8Array };\n\nexport function parseDidSignaturePaste(pasted: string): DidSignaturePaste {\n const tokens = (typeof pasted === \"string\" ? pasted : \"\").trim().split(/\\s+/).filter(Boolean);\n if (!tokens.length) throw new DidError(\"Paste the signature\");\n // A JWS or signature split over several lines (base64 wrapped at 76 characters) comes back together.\n const vm = tokens.length > 1 && /^(did:|#)/.test(tokens[0]) ? tokens[0] : undefined;\n const text = (vm ? tokens.slice(1) : tokens).join(\"\");\n if (text.split(\".\").length === 3) {\n if (vm) throw new DidError(\"Paste the JWS alone: its header names the key (kid)\");\n return { kind: \"jws\", jws: text };\n }\n if (text.length > 400) throw new DidError(\"That is too long for a signature\");\n let signature: Uint8Array | undefined;\n if (/^(?:[0-9a-fA-F]{2})+$/.test(text)) signature = Uint8Array.from(text.match(/../g)!, h => parseInt(h, 16));\n else {\n const url = text.replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n try { signature = strictBase64Url(url); } catch { signature = undefined; }\n }\n if (!signature) throw new DidError(\"That is not a signature: paste a JWS, or a signature in hex or base64\");\n return { kind: \"raw\", vm, signature };\n}\n\n/** The statement bytes a signature may cover: exactly the statement, or with the newline `echo` adds. */\nexport const didSignedMessages = (statement: string) => [utf8Encode(statement), utf8Encode(`${statement}\\n`)];\n","import { p256 } from \"@noble/curves/nist.js\";\nimport { secp256k1 } from \"@noble/curves/secp256k1.js\";\nimport { sha256 } from \"@noble/hashes/sha2.js\";\nimport { base58 } from \"@scure/base\";\n\n/**\n * Checking one record of an AT Protocol repository without trusting the server that serves it: the\n * CAR file `com.atproto.sync.getRecord` returns holds the signed commit, the Merkle Search Tree nodes\n * from the commit's root down to the record, and the record itself. Every block is checked against its\n * hash, the commit's signature against the DID's signing key, and the path through the tree to the key\n * `collection/rkey`. Only what atproto's data model uses is accepted (DAG-CBOR without floats or\n * undefined, CIDv1 with SHA-256), strictly: canonical lengths and map order, so re-encoding the unsigned\n * commit gives exactly the bytes that were signed.\n *\n * Specs: https://atproto.com/specs/repository, https://atproto.com/specs/data-model,\n * https://atproto.com/specs/cryptography. No network here: the caller fetches, bounded.\n */\n\nexport class AtprotoProofError extends Error {}\nconst fail = (message: string): never => { throw new AtprotoProofError(message); };\n\n// ---------------------------------------------------------------------------------------------\n// CIDs\n\nconst CODEC_DAG_CBOR = 0x71, CODEC_RAW = 0x55, HASH_SHA256 = 0x12;\n\n/** A CIDv1 as atproto uses it: dag-cbor or raw, SHA-256. */\nexport class AtprotoCid {\n constructor(readonly bytes: Uint8Array, readonly codec: number, readonly digest: Uint8Array) {}\n /** Stable key for maps: the CID's bytes in hex. */\n get key(): string { return toHex(this.bytes); }\n equals(other: AtprotoCid): boolean { return this.key === other.key; }\n}\n\nconst toHex = (b: Uint8Array) => Array.from(b, x => x.toString(16).padStart(2, \"0\")).join(\"\");\n\nfunction readVarint(bytes: Uint8Array, offset: number): [value: number, next: number] {\n let value = 0, shift = 0, i = offset;\n for (;;) {\n if (i >= bytes.length) fail(\"Truncated varint\");\n const b = bytes[i++];\n if (shift === 49 && b > 0x0f) fail(\"Varint too large\");\n value += (b & 0x7f) * 2 ** shift;\n if (!(b & 0x80)) {\n if (b === 0 && i - offset > 1) fail(\"Varint not minimal\");\n return [value, i];\n }\n shift += 7;\n if (shift > 49) fail(\"Varint too large\");\n }\n}\n\n/** A binary CID at `offset`; returns it and where it ends. */\nexport function readAtprotoCid(bytes: Uint8Array, offset = 0): [AtprotoCid, number] {\n const [version, a] = readVarint(bytes, offset);\n if (version !== 1) fail(\"Only CIDv1 is accepted\");\n const [codec, b] = readVarint(bytes, a);\n if (codec !== CODEC_DAG_CBOR && codec !== CODEC_RAW) fail(\"Unknown CID codec\");\n const [hash, c] = readVarint(bytes, b);\n if (hash !== HASH_SHA256) fail(\"Only SHA-256 CIDs are accepted\");\n const [length, d] = readVarint(bytes, c);\n if (length !== 32 || d + 32 > bytes.length) fail(\"Bad CID digest\");\n return [new AtprotoCid(bytes.slice(offset, d + 32), codec, bytes.slice(d, d + 32)), d + 32];\n}\n\n/** The CID of `data` under `codec` (dag-cbor by default). */\nexport function atprotoCidFor(data: Uint8Array, codec = CODEC_DAG_CBOR): AtprotoCid {\n const digest = sha256(data);\n return new AtprotoCid(new Uint8Array([1, codec, HASH_SHA256, 32, ...digest]), codec, digest);\n}\n\n// ---------------------------------------------------------------------------------------------\n// DAG-CBOR\n\n/** A decoded DAG-CBOR value. Maps are null-prototype objects, links are `AtprotoCid`. */\nexport type CborValue = null | boolean | number | string | Uint8Array | AtprotoCid | CborValue[] | { [key: string]: CborValue };\n\nconst MAX_DEPTH = 32;\nconst textDecoder = new TextDecoder(\"utf-8\", { fatal: true });\nconst textEncoder = new TextEncoder();\n\n/** DAG-CBOR key order: shorter first, then bytewise. */\nfunction compareKeys(a: Uint8Array, b: Uint8Array): number {\n if (a.length !== b.length) return a.length - b.length;\n for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return a[i] - b[i];\n return 0;\n}\n\n/** Decodes exactly one DAG-CBOR value filling all of `bytes`. */\nexport function decodeDagCbor(bytes: Uint8Array): CborValue {\n let offset = 0;\n const need = (n: number) => { if (offset + n > bytes.length) fail(\"Truncated CBOR\"); };\n const head = (): [major: number, value: number] => {\n need(1);\n const b = bytes[offset++], major = b >> 5, info = b & 31;\n if (info < 24) return [major, info];\n if (info === 24) { need(1); const v = bytes[offset++]; if (v < 24) fail(\"CBOR length not minimal\"); return [major, v]; }\n if (info === 25) { need(2); const v = (bytes[offset] << 8) | bytes[offset + 1]; offset += 2; if (v < 0x100) fail(\"CBOR length not minimal\"); return [major, v]; }\n if (info === 26) { need(4); const v = ((bytes[offset] << 24) >>> 0) + (bytes[offset + 1] << 16) + (bytes[offset + 2] << 8) + bytes[offset + 3]; offset += 4; if (v < 0x10000) fail(\"CBOR length not minimal\"); return [major, v]; }\n if (info === 27) {\n need(8);\n const hi = ((bytes[offset] << 24) >>> 0) + (bytes[offset + 1] << 16) + (bytes[offset + 2] << 8) + bytes[offset + 3];\n const lo = ((bytes[offset + 4] << 24) >>> 0) + (bytes[offset + 5] << 16) + (bytes[offset + 6] << 8) + bytes[offset + 7];\n offset += 8;\n if (hi === 0) fail(\"CBOR length not minimal\");\n if (hi > 0x1fffff) fail(\"CBOR integer too large\");\n return [major, hi * 2 ** 32 + lo];\n }\n return fail(info === 31 ? \"Indefinite-length CBOR is not DAG-CBOR\" : \"Reserved CBOR value\");\n };\n const item = (depth: number): CborValue => {\n if (depth > MAX_DEPTH) fail(\"CBOR nested too deeply\");\n const [major, value] = head();\n switch (major) {\n case 0: return value;\n case 1: return -1 - value;\n case 2: { need(value); const out = bytes.slice(offset, offset + value); offset += value; return out; }\n case 3: {\n need(value);\n let text: string;\n try { text = textDecoder.decode(bytes.subarray(offset, offset + value)); } catch { return fail(\"CBOR text is not UTF-8\"); }\n offset += value;\n return text;\n }\n case 4: {\n if (value > bytes.length - offset) fail(\"Truncated CBOR\");\n const out: CborValue[] = [];\n for (let i = 0; i < value; i++) out.push(item(depth + 1));\n return out;\n }\n case 5: {\n if (value * 2 > bytes.length - offset) fail(\"Truncated CBOR\");\n const out = Object.create(null) as { [key: string]: CborValue };\n let previous: Uint8Array | null = null;\n for (let i = 0; i < value; i++) {\n const [keyMajor, keyLength] = head();\n if (keyMajor !== 3) fail(\"CBOR map keys must be text\");\n need(keyLength);\n const raw = bytes.subarray(offset, offset + keyLength);\n if (previous && compareKeys(previous, raw) >= 0) fail(\"CBOR map keys out of order or repeated\");\n previous = raw;\n let key: string;\n try { key = textDecoder.decode(raw); } catch { return fail(\"CBOR text is not UTF-8\"); }\n offset += keyLength;\n out[key] = item(depth + 1);\n }\n return out;\n }\n case 6: {\n if (value !== 42) fail(\"Only CID links (tag 42) are allowed\");\n const [inner, length] = head();\n if (inner !== 2) fail(\"A CID link must be bytes\");\n need(length);\n if (length < 2 || bytes[offset] !== 0) fail(\"Bad CID link\");\n const [cid, end] = readAtprotoCid(bytes.subarray(offset + 1, offset + length));\n if (end !== length - 1) fail(\"Bad CID link\");\n offset += length;\n return cid;\n }\n default: {\n if (value === 20) return false;\n if (value === 21) return true;\n if (value === 22) return null;\n return fail(\"Floats and undefined are not in atproto's data model\");\n }\n }\n };\n const out = item(0);\n if (offset !== bytes.length) fail(\"Trailing bytes after CBOR\");\n return out;\n}\n\n/** Encodes a value as DAG-CBOR (canonical: minimal lengths, sorted keys). */\nexport function encodeDagCbor(value: CborValue): Uint8Array {\n const out: number[] = [];\n const head = (major: number, n: number) => {\n if (!Number.isSafeInteger(n) || n < 0) throw new AtprotoProofError(\"Cannot encode this number\");\n const m = major << 5;\n if (n < 24) out.push(m | n);\n else if (n < 0x100) out.push(m | 24, n);\n else if (n < 0x10000) out.push(m | 25, n >> 8, n & 0xff);\n else if (n < 0x100000000) out.push(m | 26, (n >>> 24) & 0xff, (n >> 16) & 0xff, (n >> 8) & 0xff, n & 0xff);\n else { const hi = Math.floor(n / 2 ** 32), lo = n >>> 0; out.push(m | 27, (hi >>> 24) & 0xff, (hi >> 16) & 0xff, (hi >> 8) & 0xff, hi & 0xff, (lo >>> 24) & 0xff, (lo >> 16) & 0xff, (lo >> 8) & 0xff, lo & 0xff); }\n };\n const bytesOut = (b: Uint8Array) => { for (const x of b) out.push(x); };\n const item = (v: CborValue) => {\n if (v === null) out.push(0xf6);\n else if (v === true) out.push(0xf5);\n else if (v === false) out.push(0xf4);\n else if (typeof v === \"number\") { if (!Number.isSafeInteger(v)) throw new AtprotoProofError(\"Only integers are in atproto's data model\"); if (v >= 0) head(0, v); else head(1, -1 - v); }\n else if (typeof v === \"string\") { const b = textEncoder.encode(v); head(3, b.length); bytesOut(b); }\n else if (v instanceof Uint8Array) { head(2, v.length); bytesOut(v); }\n else if (v instanceof AtprotoCid) { head(6, 42); head(2, v.bytes.length + 1); out.push(0); bytesOut(v.bytes); }\n else if (Array.isArray(v)) { head(4, v.length); v.forEach(item); }\n else {\n const entries = Object.keys(v).map(k => [textEncoder.encode(k), v[k]] as const).sort((a, b) => compareKeys(a[0], b[0]));\n head(5, entries.length);\n for (const [k, x] of entries) { head(3, k.length); bytesOut(k); item(x); }\n }\n };\n item(value);\n return new Uint8Array(out);\n}\n\nconst isMap = (v: CborValue): v is { [key: string]: CborValue } =>\n v !== null && typeof v === \"object\" && !Array.isArray(v) && !(v instanceof Uint8Array) && !(v instanceof AtprotoCid);\n\nfunction exactKeys(v: CborValue, required: readonly string[], optional: readonly string[] = []): { [key: string]: CborValue } {\n if (!isMap(v)) fail(\"Expected a map\");\n const obj = v as { [key: string]: CborValue };\n for (const k of required) if (!(k in obj)) fail(`Missing field ${k}`);\n for (const k of Object.keys(obj)) if (!required.includes(k) && !optional.includes(k)) fail(`Unexpected field ${k}`);\n return obj;\n}\n\n// ---------------------------------------------------------------------------------------------\n// CAR v1\n\nexport interface AtprotoCar { roots: AtprotoCid[]; blocks: Map<string, { cid: AtprotoCid; data: Uint8Array }> }\n\n/** Parses a CARv1 file and checks every block against its CID. */\nexport function parseAtprotoCar(bytes: Uint8Array, { maxBlocks = 1024 }: { maxBlocks?: number } = {}): AtprotoCar {\n const [headerLength, start] = readVarint(bytes, 0);\n if (headerLength === 0 || start + headerLength > bytes.length) fail(\"Bad CAR header\");\n const header = exactKeys(decodeDagCbor(bytes.subarray(start, start + headerLength)), [\"roots\", \"version\"]);\n if (header.version !== 1) fail(\"Only CAR v1 is accepted\");\n const roots = header.roots;\n if (!Array.isArray(roots) || roots.length < 1 || !roots.every(r => r instanceof AtprotoCid)) fail(\"Bad CAR roots\");\n const blocks: AtprotoCar[\"blocks\"] = new Map();\n let offset = start + headerLength;\n while (offset < bytes.length) {\n const [length, next] = readVarint(bytes, offset);\n if (length === 0 || next + length > bytes.length) fail(\"Truncated CAR block\");\n const section = bytes.subarray(next, next + length);\n const [cid, dataStart] = readAtprotoCid(section);\n const data = section.slice(dataStart);\n const digest = sha256(data);\n if (toHex(digest) !== toHex(cid.digest)) fail(\"A block does not match its hash\");\n blocks.set(cid.key, { cid, data });\n if (blocks.size > maxBlocks) fail(\"Too many blocks in the CAR\");\n offset = next + length;\n }\n return { roots: roots as AtprotoCid[], blocks };\n}\n\n// ---------------------------------------------------------------------------------------------\n// Keys (did:key multibase, and the DID document's two encodings)\n\nexport type AtprotoKeyType = \"secp256k1\" | \"p256\";\nexport interface AtprotoPublicKey { type: AtprotoKeyType; /** Compressed SEC1 point (33 bytes). */ key: Uint8Array }\n\nconst MULTICODEC_K256 = [0xe7, 0x01], MULTICODEC_P256 = [0x80, 0x24];\n\nfunction compress(type: AtprotoKeyType, key: Uint8Array): Uint8Array {\n const curve = type === \"secp256k1\" ? secp256k1 : p256;\n try { return curve.Point.fromBytes(key).toBytes(true); } catch { return fail(\"Invalid public key\"); }\n}\n\n/**\n * The atproto signing key of a DID document's verification method: `Multikey` (multicodec-prefixed,\n * compressed) or the legacy `EcdsaSecp256k1VerificationKey2019` / `EcdsaSecp256r1VerificationKey2019`\n * (the bare point). Both are base58btc multibase (\"z…\").\n */\nexport function parseAtprotoVerificationKey(type: string, publicKeyMultibase: string): AtprotoPublicKey {\n if (typeof publicKeyMultibase !== \"string\" || !publicKeyMultibase.startsWith(\"z\") || publicKeyMultibase.length > 200) fail(\"Unsupported key encoding\");\n let raw: Uint8Array;\n try { raw = base58.decode(publicKeyMultibase.slice(1)); } catch { return fail(\"Unsupported key encoding\"); }\n if (type === \"Multikey\") {\n if (raw[0] === MULTICODEC_K256[0] && raw[1] === MULTICODEC_K256[1]) return { type: \"secp256k1\", key: compress(\"secp256k1\", raw.subarray(2)) };\n if (raw[0] === MULTICODEC_P256[0] && raw[1] === MULTICODEC_P256[1]) return { type: \"p256\", key: compress(\"p256\", raw.subarray(2)) };\n return fail(\"Unsupported key type\");\n }\n const legacy = type === \"EcdsaSecp256k1VerificationKey2019\" ? \"secp256k1\" : type === \"EcdsaSecp256r1VerificationKey2019\" ? \"p256\" : null;\n if (!legacy) return fail(\"Unsupported key type\");\n return { type: legacy, key: compress(legacy, raw) };\n}\n\n/** A `did:key:z…` (atproto's form, multicodec-prefixed compressed key). */\nexport function parseAtprotoDidKey(didKey: string): AtprotoPublicKey {\n if (!didKey.startsWith(\"did:key:\")) fail(\"Not a did:key\");\n return parseAtprotoVerificationKey(\"Multikey\", didKey.slice(\"did:key:\".length));\n}\n\n/** atproto signatures: ECDSA over SHA-256 of the bytes, 64-byte compact r‖s, low-S only. */\nexport function verifyAtprotoSignature(key: AtprotoPublicKey, data: Uint8Array, sig: Uint8Array): boolean {\n if (sig.length !== 64) return false;\n const curve = key.type === \"secp256k1\" ? secp256k1 : p256;\n try { return curve.verify(sig, sha256(data), key.key, { prehash: false, lowS: true, format: \"compact\" }); } catch { return false; }\n}\n\n// ---------------------------------------------------------------------------------------------\n// Commits and the Merkle Search Tree\n\nexport interface AtprotoCommit { did: string; version: 3; data: AtprotoCid; rev: string; prev: AtprotoCid | null; sig: Uint8Array }\n\nexport function decodeAtprotoCommit(data: Uint8Array): AtprotoCommit {\n const c = exactKeys(decodeDagCbor(data), [\"data\", \"did\", \"rev\", \"sig\", \"version\"], [\"prev\"]);\n if (c.version !== 3) fail(\"Unsupported commit version\");\n if (typeof c.did !== \"string\" || !(c.data instanceof AtprotoCid) || typeof c.rev !== \"string\" || !(c.sig instanceof Uint8Array)) fail(\"Malformed commit\");\n const prev = c.prev ?? null;\n if (prev !== null && !(prev instanceof AtprotoCid)) fail(\"Malformed commit\");\n return { did: c.did as string, version: 3, data: c.data as AtprotoCid, rev: c.rev as string, prev: prev as AtprotoCid | null, sig: c.sig as Uint8Array };\n}\n\n/** The bytes a commit's signature covers: the commit without `sig`, re-encoded. */\nexport function unsignedCommitBytes(data: Uint8Array): Uint8Array {\n const c = decodeDagCbor(data) as { [key: string]: CborValue };\n const { sig: _sig, ...unsigned } = c;\n return encodeDagCbor(unsigned);\n}\n\ninterface MstEntry { key: Uint8Array; value: AtprotoCid; right: AtprotoCid | null }\n\nfunction decodeMstNode(data: Uint8Array): { left: AtprotoCid | null; entries: MstEntry[] } {\n const node = exactKeys(decodeDagCbor(data), [\"e\", \"l\"]);\n if (node.l !== null && !(node.l instanceof AtprotoCid)) fail(\"Malformed tree node\");\n if (!Array.isArray(node.e)) fail(\"Malformed tree node\");\n const entries: MstEntry[] = [];\n let previous = new Uint8Array(0);\n for (const raw of node.e as CborValue[]) {\n const e = exactKeys(raw, [\"k\", \"p\", \"t\", \"v\"]);\n if (typeof e.p !== \"number\" || !Number.isSafeInteger(e.p) || e.p < 0 || e.p > previous.length || !(e.k instanceof Uint8Array) ||\n !(e.v instanceof AtprotoCid) || (e.t !== null && !(e.t instanceof AtprotoCid))) fail(\"Malformed tree entry\");\n const key = new Uint8Array((e.p as number) + (e.k as Uint8Array).length);\n key.set(previous.subarray(0, e.p as number));\n key.set(e.k as Uint8Array, e.p as number);\n if (key.length > 1024) fail(\"Tree key too long\");\n if (entries.length && compareBytes(entries[entries.length - 1].key, key) >= 0) fail(\"Tree keys out of order\");\n entries.push({ key, value: e.v as AtprotoCid, right: e.t as AtprotoCid | null });\n previous = key;\n }\n return { left: node.l as AtprotoCid | null, entries };\n}\n\nfunction compareBytes(a: Uint8Array, b: Uint8Array): number {\n const n = Math.min(a.length, b.length);\n for (let i = 0; i < n; i++) if (a[i] !== b[i]) return a[i] - b[i];\n return a.length - b.length;\n}\n\n/**\n * Walks the tree from `root` to `key`. Returns the value's CID, or null when the blocks prove the key is\n * absent. Throws when a block on the path is missing: the CAR proves neither.\n */\nexport function findInMst(blocks: AtprotoCar[\"blocks\"], root: AtprotoCid, key: string, { maxDepth = 64 }: { maxDepth?: number } = {}): AtprotoCid | null {\n const target = textEncoder.encode(key);\n let current: AtprotoCid | null = root;\n for (let depth = 0; current; depth++) {\n if (depth > maxDepth) fail(\"Tree too deep\");\n const block = blocks.get(current.key);\n if (!block) return fail(\"The proof is missing part of the repository tree\");\n if (block.cid.codec !== CODEC_DAG_CBOR) fail(\"Malformed tree node\");\n const node = decodeMstNode(block.data);\n let next: AtprotoCid | null = node.left;\n let descended = false;\n for (let i = 0; i < node.entries.length; i++) {\n const order = compareBytes(target, node.entries[i].key);\n if (order === 0) return node.entries[i].value;\n if (order < 0) { next = i === 0 ? node.left : node.entries[i - 1].right; descended = true; break; }\n }\n if (!descended) next = node.entries.length ? node.entries[node.entries.length - 1].right : node.left;\n current = next;\n }\n return null;\n}\n\n/** What a verified record proof says. */\nexport interface AtprotoRecordProof {\n commit: AtprotoCommit;\n /** The record's CID and its decoded value, or null when the repository proves it absent. */\n record: { cid: AtprotoCid; value: { [key: string]: CborValue } } | null;\n}\n\n/**\n * Verifies a `com.atproto.sync.getRecord` CAR for `did`'s `collection/rkey`, signed by `key` (from the DID\n * document, never from the CAR or the server). Throws AtprotoProofError on any mismatch.\n */\nexport function verifyAtprotoRecordProof(car: Uint8Array, options: { did: string; key: AtprotoPublicKey; collection: string; rkey: string }): AtprotoRecordProof {\n const parsed = parseAtprotoCar(car);\n const root = parsed.roots[0];\n const commitBlock = parsed.blocks.get(root.key);\n if (!commitBlock || root.codec !== CODEC_DAG_CBOR) fail(\"The proof has no commit\");\n const commit = decodeAtprotoCommit(commitBlock!.data);\n if (commit.did !== options.did) fail(\"The commit is for another account\");\n if (!verifyAtprotoSignature(options.key, unsignedCommitBytes(commitBlock!.data), commit.sig)) fail(\"The commit is not signed by the account's key\");\n const cid = findInMst(parsed.blocks, commit.data, `${options.collection}/${options.rkey}`);\n if (!cid) return { commit, record: null };\n const block = parsed.blocks.get(cid.key);\n if (!block) return fail(\"The proof is missing the record\");\n if (cid.codec !== CODEC_DAG_CBOR) fail(\"The record is not DAG-CBOR\");\n const value = decodeDagCbor(block.data);\n if (!isMap(value)) fail(\"The record is not an object\");\n return { commit, record: { cid, value: value as { [key: string]: CborValue } } };\n}\n","import { parseAtprotoVerificationKey, type AtprotoPublicKey, type CborValue } from \"./atprotoRepo\";\nimport type { IdentityStatement } from \"./identityProofs\";\n\n/**\n * AT Protocol identity, the pure half (no network): handle and DID syntax, what a DID document says\n * (the signing key, the PDS, the handle it claims), and the Ghostly proof record. Specs:\n * https://atproto.com/specs/handle, https://atproto.com/specs/did, https://atproto.com/specs/record-key.\n */\n\n/** The Ghostly proof record's collection (NSID under ghostly.tools). Lexicon: docs/lexicons/tools.ghostly.proof.json. */\nexport const ATPROTO_PROOF_COLLECTION = \"tools.ghostly.proof\";\n\n/**\n * What the OAuth sign-in asks for. `atproto` is required by every server; the granular repo scope lets\n * Ghostly create and delete records of its own collection only: no posts, no DMs, no profile edits.\n * Servers that predate granular permissions only know `transition:generic` (full access to the account's\n * repository); Ghostly asks for it only when the person picks that option, after being told.\n */\nexport const ATPROTO_PROOF_SCOPE = `atproto repo:${ATPROTO_PROOF_COLLECTION}?action=create&action=delete`;\nexport const ATPROTO_FULL_SCOPE = \"atproto transition:generic\";\n/** Every scope the client metadata declares (a request may only use declared scopes). */\nexport const ATPROTO_DECLARED_SCOPE = `atproto repo:${ATPROTO_PROOF_COLLECTION}?action=create&action=delete transition:generic`;\n\nconst LABEL = \"[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\";\nconst HANDLE = new RegExp(`^(?:${LABEL}\\\\.)+[a-z](?:[a-z0-9-]{0,61}[a-z0-9])?$`);\n/** TLDs the handle spec disallows. `.test` stays allowed: it is how local networks (and the e2e suite) name accounts. */\nconst DISALLOWED_TLDS = new Set([\"alt\", \"arpa\", \"example\", \"internal\", \"invalid\", \"local\", \"localhost\", \"onion\"]);\n\n/** A handle as people type it (\"@Alice.bsky.social\", \"alice.bsky.social\") → canonical, or null. */\nexport function normalizeAtprotoHandle(input: string): string | null {\n const handle = input.trim().replace(/^@/, \"\").toLowerCase();\n if (handle.length > 253 || !HANDLE.test(handle)) return null;\n if (DISALLOWED_TLDS.has(handle.slice(handle.lastIndexOf(\".\") + 1))) return null;\n return handle;\n}\n\nconst DID_PLC = /^did:plc:[a-z2-7]{24}$/;\n/** did:web at hostname level only (atproto does not use paths or ports). */\nconst DID_WEB = new RegExp(`^did:web:(?:${LABEL}\\\\.)+[a-z](?:[a-z0-9-]{0,61}[a-z0-9])?$`);\n\nexport type AtprotoDidMethod = \"plc\" | \"web\";\nexport function atprotoDidMethod(did: string): AtprotoDidMethod | null {\n if (DID_PLC.test(did)) return \"plc\";\n if (did.length <= 261 && DID_WEB.test(did)) return \"web\";\n return null;\n}\nexport const isAtprotoDid = (did: string) => atprotoDidMethod(did) !== null;\n\n/** \"did:plc:ab12cd…wxyz\": the method, the first six and the last four characters. */\nexport function shortAtprotoDid(did: string): string {\n const m = /^(did:[a-z]+:)(.*)$/.exec(did);\n if (!m || m[2].length <= 14) return did;\n return `${m[1]}${m[2].slice(0, 6)}…${m[2].slice(-4)}`;\n}\n\n/** The record key of a proof: its proof key (z-base-32, already a valid record key), so removal finds it. */\nexport function atprotoProofRkey(proofKey: string): string {\n if (!/^[ybndrfg8ejkmcpqxot1uwisza345h769]{52}$/.test(proofKey)) throw new Error(\"Invalid proof key\");\n return proofKey;\n}\n\nexport interface AtprotoDidDocument {\n did: string;\n /** The repository signing key (`#atproto`). */\n key: AtprotoPublicKey;\n /** The PDS (`#atproto_pds`), as an https origin. */\n pds: string;\n /** The handle the document claims (first valid `at://`), normalized. Not verified: resolve it back. */\n handle?: string;\n}\n\nconst fragment = (id: unknown, did: string, name: string) => id === `#${name}` || id === `${did}#${name}`;\n\n/** A DID document (JSON already parsed) for `did`, strictly enough for atproto. Throws a message people can read. */\nexport function parseAtprotoDidDocument(doc: unknown, did: string): AtprotoDidDocument {\n if (!doc || typeof doc !== \"object\" || Array.isArray(doc)) throw new Error(\"The account's DID document is malformed\");\n const d = doc as Record<string, unknown>;\n if (d.id !== did) throw new Error(\"The DID document is for another account\");\n const methods = Array.isArray(d.verificationMethod) ? d.verificationMethod : [];\n const method = methods.find(m => m && typeof m === \"object\" && fragment((m as Record<string, unknown>).id, did, \"atproto\")) as Record<string, unknown> | undefined;\n if (!method || typeof method.type !== \"string\" || typeof method.publicKeyMultibase !== \"string\") throw new Error(\"The account has no atproto signing key\");\n let key: AtprotoPublicKey;\n try { key = parseAtprotoVerificationKey(method.type, method.publicKeyMultibase); } catch { throw new Error(\"The account's signing key is not supported\"); }\n const services = Array.isArray(d.service) ? d.service : [];\n const service = services.find(s => s && typeof s === \"object\" && fragment((s as Record<string, unknown>).id, did, \"atproto_pds\")) as Record<string, unknown> | undefined;\n if (!service || service.type !== \"AtprotoPersonalDataServer\" || typeof service.serviceEndpoint !== \"string\") throw new Error(\"The account names no server (PDS)\");\n let pds: URL;\n try { pds = new URL(service.serviceEndpoint); } catch { throw new Error(\"The account's server address is malformed\"); }\n if (pds.protocol !== \"https:\" || pds.username || pds.password || pds.search || pds.hash || (pds.pathname !== \"/\" && pds.pathname !== \"\")) throw new Error(\"The account's server must be an https address\");\n const aka = Array.isArray(d.alsoKnownAs) ? d.alsoKnownAs : [];\n let handle: string | undefined;\n for (const entry of aka) {\n if (typeof entry !== \"string\" || !entry.startsWith(\"at://\")) continue;\n handle = normalizeAtprotoHandle(entry.slice(5)) ?? undefined;\n break;\n }\n return { did, key, pds: pds.origin, ...(handle ? { handle } : {}) };\n}\n\n/** The record Ghostly writes: the statement, exactly, and when. */\nexport function atprotoProofRecord(statement: IdentityStatement, createdAt: Date): { $type: string; statement: string; createdAt: string } {\n return { $type: ATPROTO_PROOF_COLLECTION, statement: statement.text, createdAt: createdAt.toISOString() };\n}\n\n/** Checks a record read from a repository against the statement it must carry. */\nexport function checkAtprotoProofRecord(value: { [key: string]: CborValue }, statement: IdentityStatement): void {\n const keys = Object.keys(value).sort().join(\",\");\n if (keys !== \"$type,createdAt,statement\") throw new Error(\"The record is not a Ghostly proof\");\n if (value.$type !== ATPROTO_PROOF_COLLECTION) throw new Error(\"The record is not a Ghostly proof\");\n if (typeof value.createdAt !== \"string\" || value.createdAt.length > 64) throw new Error(\"The record is not a Ghostly proof\");\n if (value.statement !== statement.text) throw new Error(\"The record carries another statement\");\n}\n","import { ripemd160 } from \"@noble/hashes/legacy.js\";\nimport { sha256 } from \"@noble/hashes/sha2.js\";\nimport { bech32, bech32m, createBase58check } from \"@scure/base\";\nimport { concatBytes } from \"./bytes\";\n\n/**\n * The little of Bitcoin's address and transaction formats a message-signature verifier needs: decoding an\n * address to its output script, and (de)serialising the two virtual transactions of BIP-322. Nothing here\n * builds, signs or broadcasts a real transaction.\n */\n\n/** Which addresses a proof may name: the real network, or any of the test networks (testnet, signet, regtest…). */\nexport type BitcoinProofNetwork = \"mainnet\" | \"testnet\";\nexport type BitcoinScriptType = \"p2pkh\" | \"p2sh\" | \"p2wpkh\" | \"p2wsh\" | \"p2tr\" | \"witness-unknown\";\n\nexport interface BitcoinAddressInfo {\n address: string;\n type: BitcoinScriptType;\n network: BitcoinProofNetwork;\n /** The output script paying this address. */\n scriptPubKey: Uint8Array;\n /** The key/script hash of a base58 address, or the witness program of a SegWit one. */\n program: Uint8Array;\n witnessVersion?: number;\n}\n\nconst base58check = createBase58check(sha256);\nexport const sha256d = (...parts: Uint8Array[]) => sha256(sha256(concatBytes(...parts)));\nexport const hash160 = (data: Uint8Array) => ripemd160(sha256(data));\n\n/** Base58 versions: P2PKH, P2SH. Every test network (testnet3/4, signet, mutinynet, regtest) shares one pair. */\nconst BASE58 = { mainnet: [0x00, 0x05], testnet: [0x6f, 0xc4] } as const;\nconst HRP = { mainnet: [\"bc\"], testnet: [\"tb\", \"bcrt\"] } as const;\n\nfunction decodeSegwit(address: string): { hrp: string; version: number; program: Uint8Array } | undefined {\n // BIP 173: one case only; the checksum is over the lower-case form.\n if (address !== address.toLowerCase() && address !== address.toUpperCase()) return undefined;\n const lower = address.toLowerCase() as `${string}1${string}`;\n for (const [coder, v0] of [[bech32, true], [bech32m, false]] as const) {\n let decoded: { prefix: string; words: number[] };\n try { decoded = coder.decode(lower, 90); } catch { continue; }\n if (decoded.words.length < 1) return undefined;\n const version = decoded.words[0];\n // Version 0 is bech32 (BIP 173), every later version bech32m (BIP 350); the wrong one is refused.\n if (version > 16 || (version === 0) !== v0) return undefined;\n let program: Uint8Array;\n try { program = coder.fromWords(decoded.words.slice(1)); } catch { return undefined; }\n if (program.length < 2 || program.length > 40 || (version === 0 && program.length !== 20 && program.length !== 32)) return undefined;\n return { hrp: decoded.prefix, version, program };\n }\n return undefined;\n}\n\n/** An address refused, with a reason fit to show the person. */\nexport class BitcoinAddressError extends Error {}\n\n/** Decodes `address` for `network`, or throws a `BitcoinAddressError` saying what is wrong with it. */\nexport function decodeBitcoinAddress(address: string, network: BitcoinProofNetwork): BitcoinAddressInfo {\n if (typeof address !== \"string\" || address.length < 14 || address.length > 90) throw new BitcoinAddressError(\"Not a Bitcoin address\");\n const other: BitcoinProofNetwork = network === \"mainnet\" ? \"testnet\" : \"mainnet\";\n const wrongNetwork = () => new BitcoinAddressError(network === \"mainnet\"\n ? \"This is a test-network address; switch the wallets to Testnet to use it\"\n : \"This is a mainnet address; switch the wallets to Mainnet to use it\");\n const segwit = decodeSegwit(address);\n if (segwit) {\n if (!(HRP[network] as readonly string[]).includes(segwit.hrp)) {\n if ((HRP[other] as readonly string[]).includes(segwit.hrp)) throw wrongNetwork();\n throw new BitcoinAddressError(\"Not a Bitcoin address\");\n }\n const { version, program } = segwit;\n const type: BitcoinScriptType = version === 0 ? (program.length === 20 ? \"p2wpkh\" : \"p2wsh\") : version === 1 && program.length === 32 ? \"p2tr\" : \"witness-unknown\";\n const scriptPubKey = concatBytes(Uint8Array.of(version === 0 ? 0x00 : 0x50 + version, program.length), program);\n return { address, type, network, scriptPubKey, program, witnessVersion: version };\n }\n let payload: Uint8Array;\n try { payload = base58check.decode(address); } catch { throw new BitcoinAddressError(\"Not a Bitcoin address (bad checksum?)\"); }\n if (payload.length !== 21) throw new BitcoinAddressError(\"Not a Bitcoin address\");\n const [pkh, sh] = BASE58[network];\n const hash = payload.slice(1);\n if (payload[0] === pkh) return { address, type: \"p2pkh\", network, program: hash, scriptPubKey: concatBytes(Uint8Array.of(0x76, 0xa9, 0x14), hash, Uint8Array.of(0x88, 0xac)) };\n if (payload[0] === sh) return { address, type: \"p2sh\", network, program: hash, scriptPubKey: concatBytes(Uint8Array.of(0xa9, 0x14), hash, Uint8Array.of(0x87)) };\n if ((BASE58[other] as readonly number[]).includes(payload[0])) throw wrongNetwork();\n throw new BitcoinAddressError(\"Not a Bitcoin address\");\n}\n\n// ---- Transactions (consensus serialisation) ----\n\nexport interface TxInput { txid: Uint8Array; vout: number; scriptSig: Uint8Array; sequence: number; witness: Uint8Array[] }\nexport interface TxOutput { value: bigint; script: Uint8Array }\nexport interface Tx { version: number; inputs: TxInput[]; outputs: TxOutput[]; lockTime: number }\n\nexport function compactSize(n: number): Uint8Array {\n if (n < 0xfd) return Uint8Array.of(n);\n if (n <= 0xffff) return Uint8Array.of(0xfd, n & 0xff, n >>> 8);\n if (n <= 0xffffffff) { const b = new Uint8Array(5); b[0] = 0xfe; new DataView(b.buffer).setUint32(1, n, true); return b; }\n throw new Error(\"Too large\");\n}\nexport const varBytes = (b: Uint8Array) => concatBytes(compactSize(b.length), b);\nexport const u32 = (n: number) => { const b = new Uint8Array(4); new DataView(b.buffer).setUint32(0, n >>> 0, true); return b; };\nexport const u64 = (n: bigint) => { const b = new Uint8Array(8); new DataView(b.buffer).setBigUint64(0, n, true); return b; };\n\nexport function encodeTx(tx: Tx, withWitness = tx.inputs.some(i => i.witness.length > 0)): Uint8Array {\n const parts: Uint8Array[] = [u32(tx.version)];\n if (withWitness) parts.push(Uint8Array.of(0x00, 0x01));\n parts.push(compactSize(tx.inputs.length));\n for (const i of tx.inputs) parts.push(i.txid, u32(i.vout), varBytes(i.scriptSig), u32(i.sequence));\n parts.push(compactSize(tx.outputs.length));\n for (const o of tx.outputs) parts.push(u64(o.value), varBytes(o.script));\n if (withWitness) for (const i of tx.inputs) parts.push(encodeWitness(i.witness));\n parts.push(u32(tx.lockTime));\n return concatBytes(...parts);\n}\nexport const encodeWitness = (stack: Uint8Array[]) => concatBytes(compactSize(stack.length), ...stack.map(varBytes));\n/** The id other transactions refer to it by, in internal byte order (reverse it for display). */\nexport const txHash = (tx: Tx) => sha256d(encodeTx(tx, false));\n\n/** A strict reader: canonical sizes only, and the whole input must be consumed. */\nexport class ByteReader {\n private at = 0;\n constructor(private bytes: Uint8Array) {}\n get done() { return this.at === this.bytes.length; }\n take(n: number): Uint8Array {\n if (!Number.isSafeInteger(n) || n < 0 || this.at + n > this.bytes.length) throw new Error(\"Truncated data\");\n const out = this.bytes.slice(this.at, this.at + n);\n this.at += n;\n return out;\n }\n peek(): number { if (this.done) throw new Error(\"Truncated data\"); return this.bytes[this.at]; }\n u32(): number { return new DataView(this.take(4).buffer).getUint32(0, true); }\n i32(): number { return new DataView(this.take(4).buffer).getInt32(0, true); }\n u64(): bigint { return new DataView(this.take(8).buffer).getBigUint64(0, true); }\n compactSize(): number {\n const first = this.take(1)[0];\n const [n, min] = first < 0xfd ? [first, 0] : first === 0xfd ? [new DataView(this.take(2).buffer).getUint16(0, true), 0xfd]\n : first === 0xfe ? [this.u32(), 0x10000] : [Number(this.u64()), 0x100000000];\n if (n < min || n > 0x02000000) throw new Error(\"Non-canonical size\");\n return n;\n }\n varBytes(): Uint8Array { return this.take(this.compactSize()); }\n end() { if (!this.done) throw new Error(\"Unexpected trailing data\"); }\n}\n\nexport function decodeWitness(bytes: Uint8Array): Uint8Array[] {\n const r = new ByteReader(bytes);\n const stack = readWitness(r);\n r.end();\n return stack;\n}\nfunction readWitness(r: ByteReader): Uint8Array[] {\n const n = r.compactSize();\n return Array.from({ length: n }, () => r.varBytes());\n}\n\nexport function decodeTx(bytes: Uint8Array): Tx {\n const r = new ByteReader(bytes);\n const version = r.i32();\n let segwit = false;\n if (r.peek() === 0x00) {\n r.take(1);\n if (r.take(1)[0] !== 0x01) throw new Error(\"Unknown transaction flag\");\n segwit = true;\n }\n const inputs: TxInput[] = Array.from({ length: r.compactSize() }, () => ({ txid: r.take(32), vout: r.u32(), scriptSig: r.varBytes(), sequence: r.u32(), witness: [] }));\n const outputs: TxOutput[] = Array.from({ length: r.compactSize() }, () => ({ value: r.u64(), script: r.varBytes() }));\n if (segwit) {\n for (const input of inputs) input.witness = readWitness(r);\n // A witness flag with nothing in it is a non-canonical serialisation (Bitcoin Core refuses it).\n if (inputs.every(i => i.witness.length === 0)) throw new Error(\"Superfluous witness flag\");\n }\n const lockTime = r.u32();\n r.end();\n return { version, inputs, outputs, lockTime };\n}\n\n/** The address decoded on whichever network it belongs to (mainnet first), or a `BitcoinAddressError`. */\nexport function decodeAnyBitcoinAddress(address: string): BitcoinAddressInfo {\n try { return decodeBitcoinAddress(address, \"mainnet\"); } catch (e) {\n if (!(e instanceof BitcoinAddressError) || !/test-network/.test(e.message)) throw e;\n return decodeBitcoinAddress(address, \"testnet\");\n }\n}\n","import { schnorr, secp256k1 } from \"@noble/curves/secp256k1.js\";\nimport { sha256 } from \"@noble/hashes/sha2.js\";\nimport { bytesToNumberBE } from \"@noble/curves/utils.js\";\nimport { base64 } from \"@scure/base\";\nimport { bytesEqual, concatBytes, utf8Encode } from \"./bytes\";\nimport {\n type BitcoinAddressInfo, type Tx, decodeTx, decodeWitness, encodeTx, hash160, sha256d, txHash, u32, u64, varBytes,\n} from \"./bitcoinScript\";\n\n/**\n * BIP-322 \"Generic Signed Message Format\", verification only, pinned to **version 2.0.0** of the BIP\n * (bitcoin/bips commit 4061a54418f62a5a1ae44f4604e56373329bfad3, 2026-06-05), whose official test vectors\n * are in packages/core/test/fixtures/bip322/.\n *\n * Without a full script interpreter, this verifier checks the scripts it understands and says\n * \"inconclusive\" for the rest, as the BIP allows:\n *\n * - P2WPKH (bc1q…, 42 chars) simple and full\n * - P2TR (bc1p…) key path simple and full\n * - P2SH-P2WPKH (3…) full only: the simple format is for native SegWit\n * - P2PKH (1…) full only\n *\n * Multisig and other scripts (P2WSH, bare P2SH, Taproot script path) and proofs of funds (`pof`) are\n * inconclusive, which a proof treats as not proven.\n */\nexport const BIP322_REVISION = \"2.0.0 (bitcoin/bips@4061a54418f62a5a1ae44f4604e56373329bfad3)\";\n\nexport type Bip322Format = \"bip322-simple\" | \"bip322-full\";\nexport type BitcoinProofScript = \"p2pkh\" | \"p2wpkh\" | \"p2sh-p2wpkh\" | \"p2tr\";\nexport interface Bip322Valid {\n format: Bip322Format;\n script: BitcoinProofScript;\n /** The full format's nLockTime and first nSequence, when the signer set them (BIP-322: \"valid at time T and age S\"). */\n lockTime?: number;\n sequence?: number;\n}\nexport type BitcoinMessageVerdict =\n | ({ state: \"valid\" } & (Bip322Valid | { format: \"legacy\"; script: \"p2pkh\" }))\n /** Something checked failed: wrong key, message, address or a malformed signature. */\n | { state: \"invalid\"; reason: string }\n /** Maybe valid, but not something this verifier can check (a multisig, a time lock…). Never treat as proven. */\n | { state: \"inconclusive\"; reason: string };\n\n/** A refusal while verifying; `inconclusive` for what cannot be checked rather than what failed. */\nexport class MessageRefusal extends Error {\n constructor(readonly state: \"invalid\" | \"inconclusive\", reason: string) { super(reason); }\n}\nconst invalid = (reason: string) => new MessageRefusal(\"invalid\", reason);\nconst inconclusive = (reason: string) => new MessageRefusal(\"inconclusive\", reason);\n\nconst SIGHASH_DEFAULT = 0x00;\nconst SIGHASH_ALL = 0x01;\nconst OP_RETURN = Uint8Array.of(0x6a);\nconst ZERO32 = new Uint8Array(32);\n\nexport const bip322MessageHash = (message: string) => schnorr.utils.taggedHash(\"BIP0322-signed-message\", utf8Encode(message));\n\n/** The virtual transaction whose only output pays the address; `to_sign` spends it. */\nexport function bip322ToSpend(message: string, scriptPubKey: Uint8Array): Tx {\n return {\n version: 0, lockTime: 0,\n inputs: [{ txid: ZERO32, vout: 0xffffffff, sequence: 0, witness: [], scriptSig: concatBytes(Uint8Array.of(0x00, 0x20), bip322MessageHash(message)) }],\n outputs: [{ value: 0n, script: scriptPubKey }],\n };\n}\n\n/** `to_sign` with every field at the default the simple format assumes. */\nexport function bip322ToSign(toSpend: Tx, witness: Uint8Array[] = []): Tx {\n return {\n version: 0, lockTime: 0,\n inputs: [{ txid: txHash(toSpend), vout: 0, sequence: 0, scriptSig: new Uint8Array(0), witness }],\n outputs: [{ value: 0n, script: OP_RETURN }],\n };\n}\n\n/** Standard base64, whitespace (a pasted line break) ignored; missing padding tolerated. */\nexport function decodeBase64(text: string): Uint8Array {\n const clean = text.replace(/\\s+/g, \"\");\n const padded = clean.length % 4 === 0 ? clean : clean + \"=\".repeat(4 - (clean.length % 4));\n try { return base64.decode(padded); } catch { throw invalid(\"The signature is not valid base64\"); }\n}\n\n// ---- Signature hashes, for the one input BIP-322 signs ----\n\n/** BIP 143, for witness v0. `scriptCode` is the P2PKH script of the key. */\nfunction sighashV0(tx: Tx, scriptCode: Uint8Array, hashType: number): Uint8Array {\n const input = tx.inputs[0];\n return sha256d(\n u32(tx.version),\n sha256d(...tx.inputs.map(i => concatBytes(i.txid, u32(i.vout)))),\n sha256d(...tx.inputs.map(i => u32(i.sequence))),\n input.txid, u32(input.vout), varBytes(scriptCode), u64(0n), u32(input.sequence),\n sha256d(...tx.outputs.map(o => concatBytes(u64(o.value), varBytes(o.script)))),\n u32(tx.lockTime), u32(hashType),\n );\n}\n\n/** BIP 341 key path, SIGHASH_DEFAULT or SIGHASH_ALL, no annex. */\nfunction sighashV1(tx: Tx, scriptPubKey: Uint8Array, hashType: number): Uint8Array {\n const input = tx.inputs[0];\n return schnorr.utils.taggedHash(\"TapSighash\", concatBytes(\n Uint8Array.of(0x00, hashType), u32(tx.version), u32(tx.lockTime),\n sha256(concatBytes(input.txid, u32(input.vout))),\n sha256(u64(0n)),\n sha256(varBytes(scriptPubKey)),\n sha256(u32(input.sequence)),\n sha256(concatBytes(...tx.outputs.map(o => concatBytes(u64(o.value), varBytes(o.script))))),\n Uint8Array.of(0x00), u32(0),\n ));\n}\n\n/** The original sighash, for P2PKH: the input's script replaced by the spent output's, the rest as is. */\nfunction sighashLegacy(tx: Tx, scriptPubKey: Uint8Array, hashType: number): Uint8Array {\n const copy: Tx = { ...tx, inputs: tx.inputs.map((i, n) => ({ ...i, witness: [], scriptSig: n === 0 ? scriptPubKey : new Uint8Array(0) })) };\n return sha256d(encodeTx(copy, false), u32(hashType));\n}\n\n// ---- Script checks ----\n\n/** A check that throws on malformed input (a point off the curve…) simply does not hold. */\nfunction holds(check: () => boolean): boolean {\n try { return check(); } catch { return false; }\n}\n\n/** BIP 66 strict DER, the signature still carrying its sighash byte (Bitcoin Core's IsValidSignatureEncoding). */\nfunction isStrictDer(sig: Uint8Array): boolean {\n if (sig.length < 9 || sig.length > 73 || sig[0] !== 0x30 || sig[1] !== sig.length - 3 || sig[2] !== 0x02) return false;\n const lenR = sig[3];\n if (lenR === 0 || 5 + lenR >= sig.length) return false;\n const lenS = sig[5 + lenR];\n if (lenS === 0 || lenR + lenS + 7 !== sig.length || sig[4 + lenR] !== 0x02) return false;\n if (sig[4] & 0x80 || (lenR > 1 && sig[4] === 0x00 && !(sig[5] & 0x80))) return false;\n if (sig[6 + lenR] & 0x80 || (lenS > 1 && sig[6 + lenR] === 0x00 && !(sig[7 + lenR] & 0x80))) return false;\n return true;\n}\n\n/** An ECDSA check with BIP-322's required rules: SIGHASH_ALL, strict DER, low S, a valid public key. */\nfunction checkEcdsa(sigWithType: Uint8Array, pubkey: Uint8Array, sighash: (hashType: number) => Uint8Array, compressedOnly: boolean) {\n if (sigWithType.length === 0) throw invalid(\"The signature is empty\");\n const valid = pubkey.length === 33 ? pubkey[0] === 0x02 || pubkey[0] === 0x03 : pubkey.length === 65 && !compressedOnly && pubkey[0] === 0x04;\n if (!valid) throw invalid(\"The signature carries a malformed public key\");\n const hashType = sigWithType[sigWithType.length - 1];\n if (hashType !== SIGHASH_ALL) throw invalid(\"BIP-322 signatures must use SIGHASH_ALL\");\n if (!isStrictDer(sigWithType)) throw invalid(\"The signature is not strictly DER-encoded\");\n if (!holds(() => secp256k1.verify(sigWithType.subarray(0, -1), sighash(hashType), pubkey, { prehash: false, lowS: true, format: \"der\" })))\n throw invalid(\"The signature does not match this address and message\");\n}\n\nconst P2PKH_SCRIPT = (hash: Uint8Array) => concatBytes(Uint8Array.of(0x76, 0xa9, 0x14), hash, Uint8Array.of(0x88, 0xac));\n\nfunction checkP2wpkh(tx: Tx, program: Uint8Array) {\n const witness = tx.inputs[0].witness;\n if (witness.length !== 2) throw invalid(\"The signature does not match this address and message\");\n const [sig, pubkey] = witness;\n if (pubkey.length !== 33 || !bytesEqual(hash160(pubkey), program)) throw invalid(\"The signature does not match this address and message\");\n checkEcdsa(sig, pubkey, t => sighashV0(tx, P2PKH_SCRIPT(program), t), true);\n}\n\n/**\n * Whether a script-path witness commits to `outputKey` (BIP 341). When it does, a script runs that this\n * verifier cannot interpret; when it does not, the spend fails whatever the script says.\n */\nfunction commitsToOutputKey(stack: Uint8Array[], outputKey: Uint8Array): boolean {\n const control = stack[stack.length - 1], script = stack[stack.length - 2];\n if (control.length < 33 || (control.length - 33) % 32 !== 0 || control.length > 33 + 32 * 128) return false;\n try {\n const internal = schnorr.utils.lift_x(bytesToNumberBE(control.subarray(1, 33)));\n let node = schnorr.utils.taggedHash(\"TapLeaf\", Uint8Array.of(control[0] & 0xfe), varBytes(script));\n for (let at = 33; at < control.length; at += 32) {\n const sibling = control.subarray(at, at + 32);\n node = schnorr.utils.taggedHash(\"TapBranch\", ...(compareBytes(node, sibling) < 0 ? [node, sibling] : [sibling, node]));\n }\n const tweak = bytesToNumberBE(schnorr.utils.taggedHash(\"TapTweak\", control.subarray(1, 33), node));\n if (tweak >= secp256k1.Point.Fn.ORDER) return false;\n const q = internal.add(secp256k1.Point.BASE.multiply(tweak)).toAffine();\n return q.x === bytesToNumberBE(outputKey) && Number(q.y & 1n) === (control[0] & 1);\n } catch { return false; }\n}\nfunction compareBytes(a: Uint8Array, b: Uint8Array): number {\n for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return a[i] - b[i];\n return 0;\n}\n\nfunction checkP2tr(tx: Tx, scriptPubKey: Uint8Array, outputKey: Uint8Array) {\n const witness = tx.inputs[0].witness;\n if (witness.length === 0) throw invalid(\"The signature does not match this address and message\");\n if (witness.length > 1) {\n if (witness[witness.length - 1][0] === 0x50) throw inconclusive(\"Taproot signatures with an annex cannot be checked here\");\n if (commitsToOutputKey(witness, outputKey)) throw inconclusive(\"Taproot script-path signatures cannot be checked here; sign with the address's key path\");\n throw invalid(\"The signature does not match this address and message\");\n }\n const sig = witness[0];\n let hashType: number;\n if (sig.length === 64) hashType = SIGHASH_DEFAULT;\n else if (sig.length === 65 && sig[64] === SIGHASH_ALL) hashType = SIGHASH_ALL;\n else if (sig.length === 65) throw invalid(\"BIP-322 signatures must use SIGHASH_ALL or SIGHASH_DEFAULT\");\n else throw invalid(\"The signature does not match this address and message\");\n if (!holds(() => schnorr.verify(sig.subarray(0, 64), sighashV1(tx, scriptPubKey, hashType), outputKey)))\n throw invalid(\"The signature does not match this address and message\");\n}\n\n/** Exactly two direct pushes, as P2PKH's scriptSig: `<sig> <pubkey>` (MINIMALDATA holds for these sizes). */\nfunction parseTwoPushes(script: Uint8Array): [Uint8Array, Uint8Array] {\n const items: Uint8Array[] = [];\n for (let at = 0; at < script.length;) {\n const n = script[at];\n if (n < 1 || n > 75 || at + 1 + n > script.length) throw invalid(\"The signature does not match this address and message\");\n items.push(script.subarray(at + 1, at + 1 + n));\n at += 1 + n;\n }\n if (items.length !== 2) throw invalid(\"The signature does not match this address and message\");\n return [items[0], items[1]];\n}\n\nfunction checkInput(tx: Tx, address: BitcoinAddressInfo): BitcoinProofScript {\n const input = tx.inputs[0];\n const { scriptPubKey, program } = address;\n switch (address.type) {\n case \"p2wpkh\":\n if (input.scriptSig.length) throw invalid(\"A native SegWit input carries no scriptSig\");\n checkP2wpkh(tx, program);\n return \"p2wpkh\";\n case \"p2tr\":\n if (input.scriptSig.length) throw invalid(\"A native SegWit input carries no scriptSig\");\n checkP2tr(tx, scriptPubKey, program);\n return \"p2tr\";\n case \"p2pkh\": {\n if (input.witness.length) throw invalid(\"A P2PKH input carries no witness\");\n const [sig, pubkey] = parseTwoPushes(input.scriptSig);\n if (!bytesEqual(hash160(pubkey), program)) throw invalid(\"The signature does not match this address and message\");\n checkEcdsa(sig, pubkey, t => sighashLegacy(tx, scriptPubKey, t), false);\n return \"p2pkh\";\n }\n case \"p2sh\": {\n // Only P2SH-P2WPKH: the scriptSig is exactly one push of `0 <20-byte key hash>`.\n const s = input.scriptSig;\n if (s.length !== 23 || s[0] !== 22 || s[1] !== 0x00 || s[2] !== 0x14)\n throw inconclusive(\"Only P2SH-wrapped single-key SegWit (P2SH-P2WPKH) can be checked here, not a multisig or other script\");\n const redeem = s.subarray(1);\n if (!bytesEqual(hash160(redeem), program)) throw invalid(\"The signature does not match this address and message\");\n checkP2wpkh(tx, redeem.subarray(2));\n return \"p2sh-p2wpkh\";\n }\n case \"p2wsh\":\n throw inconclusive(\"Script (P2WSH) addresses such as multisig cannot be checked here\");\n default:\n throw inconclusive(\"This address type cannot be checked here\");\n }\n}\n\n/** Checks a BIP-322 signature — `smp…`, `ful…`, or an unprefixed one read as simple — or throws a `MessageRefusal`. */\nexport function verifyBip322(address: BitcoinAddressInfo, message: string, signature: string): Bip322Valid {\n const text = signature.trim();\n if (text.startsWith(\"pof\")) throw inconclusive(\"Proof-of-funds signatures are not accepted: Ghostly does not check balances or coins\");\n const format: Bip322Format = text.startsWith(\"ful\") ? \"bip322-full\" : \"bip322-simple\";\n // Before BIP-322 1.0.0 signatures had no prefix; the BIP lets a verifier read those as simple.\n const body = text.startsWith(\"smp\") || text.startsWith(\"ful\") ? text.slice(3) : text;\n const bytes = decodeBase64(body);\n if (bytes.length === 0) throw invalid(\"The signature is empty\");\n const toSpend = bip322ToSpend(message, address.scriptPubKey);\n let toSign: Tx;\n if (format === \"bip322-simple\") {\n if (address.type === \"p2pkh\" || address.type === \"p2sh\")\n throw invalid(\"A simple BIP-322 signature is only for native SegWit addresses; this one needs the full format (ful…)\");\n let witness: Uint8Array[];\n try { witness = decodeWitness(bytes); } catch { throw invalid(\"The signature is not a BIP-322 witness\"); }\n toSign = bip322ToSign(toSpend, witness);\n } else {\n try { toSign = decodeTx(bytes); } catch { throw invalid(\"The signature is not a BIP-322 full transaction\"); }\n if (toSign.inputs.length !== 1) throw inconclusive(\"Signatures over extra coins (proof of funds) are not accepted\");\n const [input] = toSign.inputs;\n if (!bytesEqual(input.txid, txHash(toSpend)) || input.vout !== 0) throw invalid(\"The signature does not match this address and message\");\n if (toSign.outputs.length !== 1 || toSign.outputs[0].value !== 0n || !bytesEqual(toSign.outputs[0].script, OP_RETURN))\n throw invalid(\"The signature is not a BIP-322 transaction\");\n if (toSign.version !== 0 && toSign.version !== 2) throw inconclusive(\"Unknown BIP-322 transaction version\");\n }\n const script = checkInput(toSign, address);\n const { lockTime } = toSign, { sequence } = toSign.inputs[0];\n // \"Valid at time T and age S\". For the single-key scripts checked here a time lock gates nothing but\n // when a real spend could confirm, not who holds the key, so it is reported rather than refused.\n return lockTime || sequence ? { format, script, lockTime, sequence } : { format, script };\n}\n","import { secp256k1 } from \"@noble/curves/secp256k1.js\";\nimport { bytesEqual, concatBytes, utf8Encode } from \"./bytes\";\nimport { type BitcoinMessageVerdict, MessageRefusal, decodeBase64, verifyBip322 } from \"./bip322\";\nimport { type BitcoinProofNetwork, BitcoinAddressError, compactSize, decodeBitcoinAddress, hash160, sha256d } from \"./bitcoinScript\";\n\n/**\n * Verifies a signed message for a Bitcoin address, in either format wallets produce:\n *\n * - **BIP-322** (see bip322.ts): `smp…` / `ful…`, or unprefixed from wallets that predate the prefixes.\n * - **Legacy signmessage** (Bitcoin Core `signmessage`, Electrum, most hardware wallets): a 65-byte\n * recoverable ECDSA signature. BIP-322 restricts it to **P2PKH** addresses (1…, m…, n…), and so does\n * this verifier: for SegWit addresses the legacy format does not commit to the script, and wallets\n * disagree on its header bytes (BIP 137 vs Electrum).\n *\n * A valid result proves the signer could sign for the address when they signed. It does not prove a\n * balance, a past payment, or that they would spend from it.\n */\n\nconst MAX_MESSAGE = 16 * 1024;\nconst MAX_SIGNATURE = 16 * 1024;\nconst MAGIC = utf8Encode(\"Bitcoin Signed Message:\\n\");\n\n/** The digest `signmessage` signs: double SHA-256 of the length-prefixed magic and message. */\nexport function legacyMessageHash(message: string): Uint8Array {\n const bytes = utf8Encode(message);\n return sha256d(compactSize(MAGIC.length), MAGIC, compactSize(bytes.length), bytes);\n}\n\n/** Header bytes 27-34 are P2PKH (uncompressed 27-30, compressed 31-34); 35-42 are BIP 137 SegWit headers. */\nexport const isLegacySignature = (bytes: Uint8Array) => bytes.length === 65 && bytes[0] >= 27 && bytes[0] <= 42;\n\nfunction verifyLegacy(address: ReturnType<typeof decodeBitcoinAddress>, message: string, bytes: Uint8Array) {\n if (address.type !== \"p2pkh\")\n throw new MessageRefusal(\"invalid\", \"This is a legacy (signmessage) signature, which Ghostly accepts only for legacy 1…/m…/n… addresses. Sign with BIP-322 for this address.\");\n const header = bytes[0];\n if (header > 34) throw new MessageRefusal(\"invalid\", \"This legacy signature is marked for a SegWit address, not this one\");\n const compressed = header >= 31;\n const recovery = (header - 27) & 3;\n let pubkey: Uint8Array;\n try {\n const point = secp256k1.Signature.fromBytes(concatBytes(Uint8Array.of(recovery), bytes.subarray(1)), \"recovered\").recoverPublicKey(legacyMessageHash(message));\n pubkey = point.toBytes(compressed);\n } catch { throw new MessageRefusal(\"invalid\", \"The signature does not match this address and message\"); }\n if (!bytesEqual(hash160(pubkey), address.program)) throw new MessageRefusal(\"invalid\", \"The signature does not match this address and message\");\n}\n\nexport interface BitcoinMessageInput {\n address: string;\n message: string;\n signature: string;\n /** Mainnet accepts only real-network addresses; Testnet any test network's (testnet, signet, regtest). */\n network: BitcoinProofNetwork;\n}\n\n/** Never throws: every refusal comes back as `invalid` or `inconclusive` with a reason fit to show. */\nexport function verifyBitcoinMessage({ address, message, signature, network }: BitcoinMessageInput): BitcoinMessageVerdict {\n try {\n if (typeof message !== \"string\" || typeof signature !== \"string\" || typeof address !== \"string\") throw new MessageRefusal(\"invalid\", \"Missing address, message or signature\");\n if (utf8Encode(message).length > MAX_MESSAGE) throw new MessageRefusal(\"invalid\", \"The message is too long\");\n const text = signature.trim();\n if (!text) throw new MessageRefusal(\"invalid\", \"The signature is empty\");\n if (text.length > MAX_SIGNATURE) throw new MessageRefusal(\"invalid\", \"The signature is too long\");\n const decoded = decodeBitcoinAddress(address.trim(), network);\n const prefixed = /^(smp|ful|pof)/.test(text);\n if (!prefixed) {\n const bytes = decodeBase64(text);\n if (isLegacySignature(bytes)) {\n verifyLegacy(decoded, message, bytes);\n return { state: \"valid\", format: \"legacy\", script: \"p2pkh\" };\n }\n }\n return { state: \"valid\", ...verifyBip322(decoded, message, text) };\n } catch (e) {\n if (e instanceof MessageRefusal) return { state: e.state, reason: e.message };\n if (e instanceof BitcoinAddressError) return { state: \"invalid\", reason: e.message };\n return { state: \"invalid\", reason: \"The signature could not be read\" };\n }\n}\n","import { ed25519 } from '@noble/curves/ed25519.js';\nimport { p256, p384, p521 } from '@noble/curves/nist.js';\nimport { sha256, sha512 } from '@noble/hashes/sha2.js';\nimport { bytesEqual, concatBytes, fromBase64, toBase64, utf8Decode, utf8Encode } from './bytes';\n\n/** OpenSSH signatures over a Ghostly statement (`ssh-keygen -Y sign -n ghostly`),\n * following PROTOCOL.sshsig. Everything here is local: parsing, the public key\n * the signature names and the signature itself. Which key a person *should*\n * have signed with is the caller's question (an expected fingerprint, or a\n * key list published by a forge; see the browser proof provider). */\n\nexport const SSHSIG_NAMESPACE = 'ghostly';\n/** A pasted armored signature: an 8192-bit RSA key plus its signature is ~3 KB. */\nexport const SSHSIG_MAX_ARMOR = 6144;\n/** A pasted `.pub` line; comments are dropped. */\nexport const SSH_PUBLIC_KEY_MAX = 4096;\nconst RSA_MIN_BITS = 2048;\nconst RSA_MAX_BITS = 8192;\nconst SK_APPLICATION_MAX = 256;\nconst SK_USER_PRESENT = 0x01;\nconst SK_USER_VERIFIED = 0x04;\n\nexport type SshKeyType =\n | 'ssh-ed25519' | 'ecdsa-sha2-nistp256' | 'ecdsa-sha2-nistp384' | 'ecdsa-sha2-nistp521' | 'ssh-rsa'\n | 'sk-ssh-ed25519@openssh.com' | 'sk-ecdsa-sha2-nistp256@openssh.com';\nexport const SSH_KEY_TYPES: readonly SshKeyType[] = ['ssh-ed25519', 'ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp384', 'ecdsa-sha2-nistp521',\n 'ssh-rsa', 'sk-ssh-ed25519@openssh.com', 'sk-ecdsa-sha2-nistp256@openssh.com'];\n\nexport interface SshPublicKey {\n type: SshKeyType;\n /** The wire-format key blob, which is what fingerprints and forge key lists name. */\n blob: Uint8Array;\n /** `SHA256:` + unpadded base64, exactly as `ssh-keygen -l` prints it. */\n fingerprint: string;\n /** `type base64`, the form `.pub` files and forge key lists use (no comment). */\n line: string;\n /** Security keys only: the FIDO application, `ssh:` unless chosen at creation. */\n application?: string;\n /** RSA modulus size. */\n bits?: number;\n}\nexport interface SshSignatureCheck {\n key: SshPublicKey;\n hashAlgorithm: 'sha256' | 'sha512';\n /** Security keys only: what the authenticator asserted for this signature. */\n securityKey?: { userPresent: boolean; userVerified: boolean; counter: number };\n}\n\nexport class SshSigError extends Error {}\nconst fail = (message: string): never => { throw new SshSigError(message); };\n\nclass Reader {\n private at = 0;\n constructor(private bytes: Uint8Array) {}\n get done() { return this.at === this.bytes.length; }\n raw(n: number): Uint8Array {\n if (n > this.bytes.length - this.at) fail('Truncated SSH data');\n return this.bytes.subarray(this.at, this.at += n);\n }\n u8(): number { return this.raw(1)[0]; }\n u32(): number { const b = this.raw(4); return ((b[0] << 24) >>> 0) + (b[1] << 16) + (b[2] << 8) + b[3]; }\n string(max = this.bytes.length): Uint8Array {\n const n = this.u32();\n if (n > max) fail('SSH field too long');\n return this.raw(n);\n }\n text(max: number): string {\n const b = this.string(max);\n if (!b.every(c => c >= 0x20 && c < 0x7f)) fail('SSH name is not printable ASCII');\n return utf8Decode(b);\n }\n /** Positive mpint in canonical form: no redundant leading zero, sign bit clear. */\n mpint(maxBytes: number): Uint8Array {\n const b = this.string(maxBytes + 1);\n if (!b.length || b[0] & 0x80 || (b[0] === 0 && (b.length === 1 || !(b[1] & 0x80)))) fail('Non-canonical SSH integer');\n return b[0] === 0 ? b.subarray(1) : b;\n }\n end() { if (!this.done) fail('Trailing bytes in SSH data'); }\n}\nconst u32 = (n: number) => Uint8Array.of(n >>> 24, (n >>> 16) & 255, (n >>> 8) & 255, n & 255);\nconst sshString = (b: Uint8Array) => concatBytes(u32(b.length), b);\n\nfunction strictBase64(text: string, max: number): Uint8Array {\n if (text.length > max || !/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(text)) fail('Invalid base64');\n const bytes = fromBase64(text);\n if (toBase64(bytes) !== text) fail('Non-canonical base64');\n return bytes;\n}\nexport function sshFingerprint(blob: Uint8Array): string {\n return `SHA256:${toBase64(sha256(blob)).replace(/=+$/, '')}`;\n}\n\ninterface CurveInfo { name: string; curve: typeof p256 | typeof p384 | typeof p521; size: number }\nconst CURVES: Record<string, CurveInfo> = {\n 'ecdsa-sha2-nistp256': { name: 'nistp256', curve: p256, size: 32 },\n 'ecdsa-sha2-nistp384': { name: 'nistp384', curve: p384, size: 48 },\n 'ecdsa-sha2-nistp521': { name: 'nistp521', curve: p521, size: 66 },\n 'sk-ecdsa-sha2-nistp256@openssh.com': { name: 'nistp256', curve: p256, size: 32 },\n};\ninterface ParsedKey extends SshPublicKey { point?: Uint8Array; rsa?: { n: Uint8Array; e: Uint8Array } }\n\nfunction parseKeyBlob(blob: Uint8Array): ParsedKey {\n if (blob.length > 1100) fail('SSH public key too large');\n const r = new Reader(blob);\n const type = r.text(64) as SshKeyType;\n if (!SSH_KEY_TYPES.includes(type)) fail(`Unsupported SSH key type ${JSON.stringify(type)}`);\n const key: ParsedKey = { type, blob, fingerprint: sshFingerprint(blob), line: `${type} ${toBase64(blob)}` };\n if (type === 'ssh-ed25519' || type === 'sk-ssh-ed25519@openssh.com') {\n key.point = r.string(32);\n if (key.point.length !== 32) fail('Invalid Ed25519 public key');\n try { ed25519.Point.fromBytes(key.point, false).assertValidity(); } catch { fail('Invalid Ed25519 public key'); }\n } else if (type === 'ssh-rsa') {\n const e = r.mpint(8), n = r.mpint(RSA_MAX_BITS / 8);\n const bits = (n.length - 1) * 8 + (32 - Math.clz32(n[0]));\n if (bits < RSA_MIN_BITS || bits > RSA_MAX_BITS) fail(`RSA keys must be ${RSA_MIN_BITS}-${RSA_MAX_BITS} bits`);\n if (!(e[e.length - 1] & 1) || (e.length === 1 && e[0] < 3)) fail('Invalid RSA exponent');\n key.rsa = { n, e }; key.bits = bits;\n } else {\n const info = CURVES[type];\n if (r.text(16) !== info.name) fail('ECDSA curve does not match the key type');\n key.point = r.string(1 + 2 * info.size);\n if (key.point.length !== 1 + 2 * info.size || key.point[0] !== 4) fail('Invalid ECDSA public key');\n try { info.curve.Point.fromBytes(key.point).assertValidity(); } catch { fail('Invalid ECDSA public key'); }\n }\n if (type.startsWith('sk-')) key.application = r.text(SK_APPLICATION_MAX);\n r.end();\n return key;\n}\n\n/** Parses one `.pub` style line (`type base64 [comment]`). */\nexport function parseSshPublicKey(line: string): SshPublicKey {\n if (typeof line !== 'string' || line.length > SSH_PUBLIC_KEY_MAX) fail('SSH public key too long');\n const m = /^\\s*(\\S+)\\s+(\\S+)(?:\\s[^\\n]*)?\\s*$/.exec(line);\n if (!m) fail('Paste one SSH public key line (the contents of a .pub file)');\n const key = parseKeyBlob(strictBase64(m![2], SSH_PUBLIC_KEY_MAX));\n if (key.type !== m![1]) fail('SSH key type does not match its data');\n return publicView(key);\n}\nconst publicView = ({ type, blob, fingerprint, line, application, bits }: ParsedKey): SshPublicKey =>\n ({ type, blob, fingerprint, line, ...(application !== undefined ? { application } : {}), ...(bits ? { bits } : {}) });\n\ninterface ParsedSignature { key: ParsedKey; namespace: string; hashAlgorithm: 'sha256' | 'sha512'; signature: Uint8Array }\n\n/** Unwraps `-----BEGIN SSH SIGNATURE-----` armor and the SSHSIG envelope, without verifying. */\nexport function parseSshSignature(armored: string): ParsedSignature {\n if (typeof armored !== 'string' || armored.length > SSHSIG_MAX_ARMOR) fail('SSH signature too long');\n const lines = armored.replace(/\\r\\n/g, '\\n').trim().split('\\n').map(l => l.trim());\n if (lines.length < 3 || lines[0] !== '-----BEGIN SSH SIGNATURE-----' || lines[lines.length - 1] !== '-----END SSH SIGNATURE-----')\n fail('Paste the whole signature, from -----BEGIN SSH SIGNATURE----- to -----END SSH SIGNATURE-----');\n const r = new Reader(strictBase64(lines.slice(1, -1).join(''), SSHSIG_MAX_ARMOR));\n if (!bytesEqual(r.raw(6), utf8Encode('SSHSIG'))) fail('Not an SSH signature');\n if (r.u32() !== 1) fail('Unsupported SSH signature version');\n const key = parseKeyBlob(r.string(1100));\n const namespace = r.text(64);\n if (r.string(64).length) fail('Unsupported SSH signature extension');\n const hashAlgorithm = r.text(16) as ParsedSignature['hashAlgorithm'];\n if (hashAlgorithm !== 'sha256' && hashAlgorithm !== 'sha512') fail('SSH signature hash must be sha256 or sha512');\n const signature = r.string(1200);\n r.end();\n return { key, namespace, hashAlgorithm, signature };\n}\n\nasync function verifyRsa(key: ParsedKey, hash: 'SHA-256' | 'SHA-512', sig: Uint8Array, data: Uint8Array): Promise<boolean> {\n const n = key.rsa!.n, size = n.length;\n if (sig.length > size) return false;\n const padded = new Uint8Array(size); padded.set(sig, size - sig.length);\n const b64u = (b: Uint8Array) => toBase64(b).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n const cryptoKey = await crypto.subtle.importKey('jwk', { kty: 'RSA', n: b64u(n), e: b64u(key.rsa!.e), ext: true },\n { name: 'RSASSA-PKCS1-v1_5', hash }, false, ['verify']);\n return crypto.subtle.verify('RSASSA-PKCS1-v1_5', cryptoKey, padded, new Uint8Array(data));\n}\n\nfunction ecdsaSignature(blob: Uint8Array, size: number): Uint8Array {\n const r = new Reader(blob);\n const a = r.mpint(size), b = r.mpint(size);\n r.end();\n const out = new Uint8Array(2 * size);\n out.set(a, size - a.length); out.set(b, 2 * size - b.length);\n return out;\n}\n\n/** Verifies an armored SSHSIG over `message` in `namespace` and returns the key that\n * made it. Throws SshSigError for anything malformed, mismatched or unverifiable.\n * Security-key signatures must assert user presence (a touch) unless explicitly\n * allowed otherwise, matching OpenSSH's default for keys created without\n * `no-touch-required`. */\nexport async function verifySshSignature(armored: string, message: Uint8Array | string, options: { namespace?: string; allowNoTouch?: boolean } = {}): Promise<SshSignatureCheck> {\n const parsed = parseSshSignature(armored);\n const namespace = options.namespace ?? SSHSIG_NAMESPACE;\n if (parsed.namespace !== namespace) fail(`Signed for namespace ${JSON.stringify(parsed.namespace)}, not ${JSON.stringify(namespace)}. Use -n ${namespace}.`);\n const bytes = typeof message === 'string' ? utf8Encode(message) : message;\n const digest = parsed.hashAlgorithm === 'sha512' ? sha512(bytes) : sha256(bytes);\n const signed = concatBytes(utf8Encode('SSHSIG'), sshString(utf8Encode(parsed.namespace)), sshString(new Uint8Array()),\n sshString(utf8Encode(parsed.hashAlgorithm)), sshString(digest));\n const { key } = parsed;\n const r = new Reader(parsed.signature);\n const algorithm = r.text(64);\n const blob = r.string(RSA_MAX_BITS / 8);\n let securityKey: SshSignatureCheck['securityKey'];\n let data = signed;\n if (key.type.startsWith('sk-')) {\n const flags = r.u8(), counter = r.u32();\n securityKey = { userPresent: !!(flags & SK_USER_PRESENT), userVerified: !!(flags & SK_USER_VERIFIED), counter };\n data = concatBytes(sha256(utf8Encode(key.application!)), Uint8Array.of(flags), u32(counter), sha256(signed));\n }\n r.end();\n const verifyWith = async (): Promise<boolean> => {\n if (key.type === 'ssh-rsa') {\n if (algorithm !== 'rsa-sha2-512' && algorithm !== 'rsa-sha2-256') fail('RSA signatures must use rsa-sha2-256 or rsa-sha2-512');\n return verifyRsa(key, algorithm === 'rsa-sha2-512' ? 'SHA-512' : 'SHA-256', blob, data);\n }\n if (algorithm !== key.type) fail('Signature algorithm does not match the key');\n if (key.type === 'ssh-ed25519' || key.type === 'sk-ssh-ed25519@openssh.com')\n return blob.length === 64 && ed25519.verify(blob, data, key.point!, { zip215: false });\n const info = CURVES[key.type];\n return info.curve.verify(ecdsaSignature(blob, info.size), data, key.point!, { lowS: false, prehash: true });\n };\n let ok: boolean;\n try { ok = await verifyWith(); } catch (e) { if (e instanceof SshSigError) throw e; ok = false; }\n if (!ok) fail('The signature does not match this statement');\n if (securityKey && !securityKey.userPresent && !options.allowNoTouch) fail('The security key did not confirm a touch for this signature');\n return { key: publicView(key), hashAlgorithm: parsed.hashAlgorithm, ...(securityKey ? { securityKey } : {}) };\n}\n\n/** Whether two keys are the same, comparing wire blobs (comments never matter). */\nexport const sameSshKey = (a: Pick<SshPublicKey, 'blob'>, b: Pick<SshPublicKey, 'blob'>) => bytesEqual(a.blob, b.blob);\n\n/** 32-byte digest of the key blob as lowercase hex: the fingerprint's bytes, in the\n * shape peer-proof external keys already use. */\nexport const sshKeyDigestHex = (key: Pick<SshPublicKey, 'blob'>) =>\n Array.from(sha256(key.blob), b => b.toString(16).padStart(2, '0')).join('');\n\n/** A POSIX shell one-liner that signs `statement` exactly (no trailing newline) and\n * prints the armored signature. Refuses anything that would need escaping, so what\n * is shown is what gets signed. */\nexport function sshSignCommand(statement: string, keyPath = '~/.ssh/id_ed25519'): string {\n if (/['\\\\\\n\\r]/.test(statement)) fail('Statement cannot be quoted for a shell');\n if (!/^[\\w~./@+-]{1,256}$/.test(keyPath)) fail('Use a plain key path without spaces or quotes');\n return `printf '%s' '${statement}' | ssh-keygen -Y sign -n ${SSHSIG_NAMESPACE} -f ${keyPath}`;\n}\n","import { fromBase64Url, toBase64Url, utf8Encode } from \"./bytes\";\nimport { sanitizeAvatar } from \"./avatar\";\nimport { decryptText, encryptText, sha256Hex } from \"./groupCrypto\";\nimport { GROUP_ID, MEMBER_KEY } from \"./groupCommits\";\nimport { parseHubPolicy, wireHubPolicy, type MeshHubPolicy } from \"./groupHubs\";\nimport { publicKeyFromZ32, sign, verify } from \"./identity\";\nimport { sanitizeDisplayText } from \"./text\";\n\n/**\n * A group's metadata (WISP 9xx § Metadata): what the group looks like, beside who is in it: its\n * name and its picture. It is not in the membership chain, so apps that know nothing of it keep\n * verifying the chain as before and drop the frame that carries it.\n *\n * The admin signs a **statement**: the group, a commit of the chain (`e`, `h`) the admin was the\n * admin of, a revision `r`, and the SHA-256 of the **body**, the metadata itself as JSON. The body\n * is the whole metadata: a field left out is unset, so `{}` removes the picture and gives the group\n * back the name it had when this member got in (`groupDisplayName`). It travels in a\n * `group-meta` frame, encrypted under the message key of an epoch the recipient holds (`k`), so\n * someone taken out of the group cannot read what comes after, and any member can hand it on under\n * a later epoch without the admin: the signature covers the body's hash, not its box.\n *\n * Which statement a member keeps is the rule of each profile (the signer is the current admin; a\n * later commit wins, then a higher revision), in `GroupSession` and `CommunitySession`.\n */\n\n/**\n * Longest picture (data URL) a group may have: less than a profile picture may be (`MAX_AVATAR_LENGTH`),\n * so the frame that carries it, sealed and in base64url, stays within the 60 KiB an edge takes.\n * What Ghostly makes (128×128) is a few kilobytes.\n */\nexport const MAX_GROUP_PICTURE_LENGTH = 40_000;\n/** Longest name the admin may give a group, in characters (code points), as a nickname (`MAX_NICK_LENGTH`). */\nexport const MAX_GROUP_NAME_LENGTH = 64;\n/** And in UTF-8 bytes. */\nexport const MAX_GROUP_NAME_BYTES = 256;\n/** Longest body, in UTF-8 bytes: a picture and room for little else. */\nexport const MAX_GROUP_META_BODY = 42_000;\nconst MAX_META_BOX = Math.ceil((MAX_GROUP_META_BODY + 16) * 4 / 3) + 4;\nconst HASH = /^[a-f0-9]{64}$/;\nconst SIG = /^[A-Za-z0-9_-]{86}$/;\nconst B64 = /^[A-Za-z0-9_-]*$/;\n\n/** What the body may say. Every field is optional; one left out is unset. */\nexport interface GroupMetaBody {\n /** The group's name, as `groupName` accepts it. Left out: the name the group had when this member got in. */\n name?: string;\n /** The group's picture: a square JPEG data URL, as `sanitizeAvatar` accepts it. */\n pic?: string;\n /** Private groups: members the admin pins as hubs, and excludes (WISP 9xx · Group Mesh § Hubs). Apps from before ignore it. */\n hubs?: MeshHubPolicy;\n}\n\nexport interface GroupMetaStatement {\n g: string;\n /** The commit the admin signs under: its epoch (index in the chain) and hash. */\n e: number;\n h: string;\n /** Revision: higher wins under the same commit. */\n r: number;\n by: string;\n ts: number;\n /** SHA-256 (hex) of the body's UTF-8 bytes. */\n d: string;\n sig: string;\n}\n\n/** A statement with its body, as a member keeps it. */\nexport interface GroupMeta extends GroupMetaStatement {\n /** The body, exactly as signed (JSON). */\n body: string;\n}\n\n/**\n * `group-meta` on an edge. `k` names the epoch whose message key seals the body: an epoch\n * number in `group-mesh/1`, a commit hash in `group-community/1` (whose frames also carry `v: 2`).\n */\nexport interface GroupMetaFrame extends GroupMetaStatement {\n t: \"group-meta\";\n v?: 2;\n k: number | string;\n nn: string;\n c: string;\n}\n\nconst statementBytes = (s: Omit<GroupMetaStatement, \"sig\">) =>\n utf8Encode(JSON.stringify([\"ghostly-group meta\", s.g, s.e, s.h, s.r, s.by, s.ts, s.d]));\nconst boxAad = (g: string, k: number | string, d: string) => JSON.stringify([\"ghostly-group meta\", g, k, d]);\n\n/**\n * A group's name as the admin gives it and every member shows it: any run of whitespace (a newline,\n * a tab) one space, invisible and direction-changing characters dropped, trimmed. Undefined when it\n * is not a string, nothing visible is left, or it is longer than `MAX_GROUP_NAME_LENGTH` characters\n * or `MAX_GROUP_NAME_BYTES` bytes: a name that does not hold is refused, never cut into another one.\n */\nexport function groupName(raw: unknown): string | undefined {\n if (typeof raw !== \"string\" || raw.length > 4 * MAX_GROUP_NAME_BYTES) return undefined;\n const clean = sanitizeDisplayText(raw.replace(/\\s+/gu, \" \"), Infinity)?.replace(/ {2,}/g, \" \");\n if (!clean || [...clean].length > MAX_GROUP_NAME_LENGTH || utf8Encode(clean).length > MAX_GROUP_NAME_BYTES) return undefined;\n return clean;\n}\n\n/** The body for a name and a picture (or neither). Throws on a name or picture Ghostly would not show. */\nexport function encodeGroupMetaBody(body: GroupMetaBody): string {\n const out: GroupMetaBody = {};\n if (body.name !== undefined) {\n const name = groupName(body.name);\n if (!name) throw new Error(`A group's name is 1 to ${MAX_GROUP_NAME_LENGTH} characters`);\n out.name = name;\n }\n if (body.pic) {\n if (body.pic.length > MAX_GROUP_PICTURE_LENGTH || typeof sanitizeAvatar(body.pic) !== \"string\") throw new Error(\"This picture cannot be used\");\n out.pic = body.pic;\n }\n const hubs = body.hubs && parseHubPolicy(body.hubs);\n if (body.hubs && !hubs) throw new Error(\"This hub setting cannot be used\");\n const wire = hubs && wireHubPolicy(hubs);\n if (wire) out.hubs = wire;\n return JSON.stringify(out);\n}\n\n/**\n * A body as received: null when it is not JSON, too large, or its picture is not one Ghostly shows. Unknown fields\n * are ignored, and so is a name that does not hold (`groupName`): the group keeps its first name, the picture shows.\n */\nexport function parseGroupMetaBody(body: string): GroupMetaBody | null {\n if (utf8Encode(body).length > MAX_GROUP_META_BODY) return null;\n let parsed: unknown;\n try { parsed = JSON.parse(body); } catch { return null; }\n if (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) return null;\n const out: GroupMetaBody = {};\n const name = groupName((parsed as Record<string, unknown>).name);\n if (name) out.name = name;\n const pic = (parsed as Record<string, unknown>).pic;\n if (pic !== undefined) {\n const clean = sanitizeAvatar(pic);\n if (clean === undefined || (clean && clean.length > MAX_GROUP_PICTURE_LENGTH)) return null;\n if (clean) out.pic = clean;\n }\n // A policy that does not hold is no policy: the picture beside it still shows.\n const hubs = parseHubPolicy((parsed as Record<string, unknown>).hubs), wire = hubs && wireHubPolicy(hubs);\n if (wire) out.hubs = wire;\n return out;\n}\n\n/** The admin's signature on a body under commit `h` (epoch `e`). */\nexport function signGroupMeta(fields: { g: string; e: number; h: string; r: number; ts: number }, body: string, seed: Uint8Array, by: string): GroupMeta {\n const unsigned = { ...fields, by, d: sha256Hex(utf8Encode(body)) };\n return { ...unsigned, sig: toBase64Url(sign(statementBytes(unsigned), seed)), body };\n}\n\n/** The statement's shape, from a frame or storage; null when anything is off. */\nexport function parseGroupMetaStatement(raw: unknown): GroupMetaStatement | null {\n if (!raw || typeof raw !== \"object\") return null;\n const f = raw as Record<string, unknown>;\n if (typeof f.g !== \"string\" || !GROUP_ID.test(f.g) || !Number.isSafeInteger(f.e) || (f.e as number) < 0 || typeof f.h !== \"string\" || !HASH.test(f.h) ||\n !Number.isSafeInteger(f.r) || (f.r as number) < 1 || typeof f.by !== \"string\" || !MEMBER_KEY.test(f.by) || !Number.isSafeInteger(f.ts) || (f.ts as number) <= 0 ||\n typeof f.d !== \"string\" || !HASH.test(f.d) || typeof f.sig !== \"string\" || !SIG.test(f.sig)) return null;\n return { g: f.g, e: f.e as number, h: f.h, r: f.r as number, by: f.by, ts: f.ts as number, d: f.d, sig: f.sig };\n}\n\n/** Whether `by` signed this statement. */\nexport function verifyGroupMetaSignature(s: GroupMetaStatement): boolean {\n try { return verify(fromBase64Url(s.sig), statementBytes(s), publicKeyFromZ32(s.by)); } catch { return false; }\n}\n\n/** The frame that carries a statement and its body, sealed under the message key of epoch `k`. */\nexport function wrapGroupMeta(meta: GroupMeta, k: number | string, messageKey: Uint8Array, community = false): GroupMetaFrame {\n const { body, ...statement } = meta;\n const { n, c } = encryptText(messageKey, boxAad(meta.g, k, meta.d), body);\n return { t: \"group-meta\", ...(community ? { v: 2 as const } : {}), ...statement, k, nn: n, c };\n}\n\n/** The frame's statement and its key reference, before anything is opened; null when malformed. */\nexport function parseGroupMetaFrame(raw: unknown): { statement: GroupMetaStatement; k: number | string; nn: string; c: string } | null {\n if (!raw || typeof raw !== \"object\") return null;\n const f = raw as Record<string, unknown>;\n const statement = parseGroupMetaStatement(raw);\n if (f.t !== \"group-meta\" || !statement) return null;\n if (!((Number.isSafeInteger(f.k) && (f.k as number) >= 0) || (typeof f.k === \"string\" && HASH.test(f.k)))) return null;\n if (typeof f.nn !== \"string\" || f.nn.length !== 32 || !B64.test(f.nn) || typeof f.c !== \"string\" || f.c.length > MAX_META_BOX || !B64.test(f.c)) return null;\n return { statement, k: f.k as number | string, nn: f.nn, c: f.c };\n}\n\n/**\n * The body of a parsed frame, with the key of its epoch `k`: it must open, hash to what was signed\n * and be metadata Ghostly accepts. The signature is checked separately (`verifyGroupMetaSignature`).\n */\nexport function openGroupMeta(frame: { statement: GroupMetaStatement; k: number | string; nn: string; c: string }, messageKey: Uint8Array): { meta: GroupMeta; body: GroupMetaBody } | null {\n const body = decryptText(messageKey, boxAad(frame.statement.g, frame.k, frame.statement.d), frame.nn, frame.c);\n if (body === null || sha256Hex(utf8Encode(body)) !== frame.statement.d) return null;\n const parsed = parseGroupMetaBody(body);\n return parsed ? { meta: { ...frame.statement, body }, body: parsed } : null;\n}\n\n/** Is `a` later than `b`: signed under a later commit, or a higher revision under the same one. */\nexport function groupMetaNewer(a: { e: number; r: number }, b: { e: number; r: number } | undefined): boolean {\n return !b || a.e > b.e || (a.e === b.e && a.r > b.r);\n}\n\n/**\n * The revision of the next statement signed under `epoch`: one past the held statement's under the same epoch, else 1.\n * Counting on from a statement of an earlier epoch would let a former admin who signed the highest safe revision leave\n * every later admin with a revision no member accepts.\n */\nexport function nextGroupMetaRevision(before: { e: number; r: number } | undefined, epoch: number): number {\n return before && before.e === epoch ? before.r + 1 : 1;\n}\n\n/** What a sync frame says of the metadata a member holds (`e.r`, \"\" for none): old apps send nothing, and get nothing. */\nexport const groupMetaTag = (meta: { e: number; r: number } | undefined): string => meta ? `${meta.e}.${meta.r}` : \"\";\nexport function parseGroupMetaTag(tag: unknown): { e: number; r: number } | undefined | null {\n if (tag === \"\") return undefined;\n if (typeof tag !== \"string\" || !/^\\d{1,15}\\.\\d{1,15}$/.test(tag)) return null;\n const [e, r] = tag.split(\".\").map(Number);\n return { e, r };\n}\n\n/** What a kept statement's body says, `{}` for none. */\nexport function groupMetaBody(meta: GroupMeta | undefined): GroupMetaBody {\n return (meta && parseGroupMetaBody(meta.body)) || {};\n}\n\n/** The picture a kept statement's body names, if it is one Ghostly shows. */\nexport function groupMetaPicture(meta: GroupMeta | undefined): string | undefined {\n return groupMetaBody(meta).pic;\n}\n\n/**\n * The name members see: the one the admin's statement gives, else the name the group had when this member got in\n * (its creation, or the invitation and welcome that let them in). That first name is also what apps from before\n * names show, and what a member shows until the statement reaches them.\n */\nexport function groupDisplayName(meta: GroupMeta | undefined, first: string): string {\n return groupMetaBody(meta).name ?? first;\n}\n\n/** What a new statement changed that members see (a line in the history); `null` for a field now unset. */\nexport interface GroupMetaChange { name?: string | null; picture?: string | null }\n\n/**\n * The name and picture that differ between two statements; null when neither does (hubs alone, or the same body signed\n * again). With `first` (the name the group had when this member got in), a name is a change only when the name shown\n * changes: a joiner's welcome already gave the name the statement then brings, and a new admin signs it again.\n */\nexport function groupMetaChange(before: GroupMeta | undefined, after: GroupMeta, first?: string): GroupMetaChange | null {\n const a = groupMetaBody(before), b = groupMetaBody(after), change: GroupMetaChange = {};\n if ((a.name ?? first) !== (b.name ?? first)) change.name = b.name ?? null;\n if (a.pic !== b.pic) change.picture = b.pic ?? null;\n return \"name\" in change || \"picture\" in change ? change : null;\n}\n","import { MEMBER_KEY } from \"./groupCommits\";\nimport { utf8Encode } from \"./bytes\";\n\n/*\n * Mentions in a group message (WISP 9xx Group Mesh § Mentions, Group Community § Mentions): which members a\n * message names, bound to their member keys rather than to the names they go by, since names can collide and\n * change. The text keeps a readable \"@Name\" at each place, so an app without mentions shows the message as it\n * was typed; the list says which member each place is.\n */\n\n/** A place in the text that names a member: `k` their member key (\"*\": everyone), `o`/`l` in Unicode code points. */\nexport interface GroupMention { k: string; o: number; l: number }\n\n/** Everyone in the group (a private group's admin only; never in a community). */\nexport const MENTION_EVERYONE = \"*\";\n\nexport const MENTION_LIMITS = {\n /** Mentions in one message. A longer list is refused whole. */\n count: 16,\n /** Code points of one mention in the text, its \"@\" included. */\n chars: 65,\n} as const;\n\n// eslint-disable-next-line no-control-regex\nconst NOT_IN_A_NAME = /[\\u0000-\\u001f\\u007f-\\u009f\\u061c\\u200e\\u200f\\u2028\\u2029\\u202a-\\u202e\\u2066-\\u2069]/;\nconst isInt = (v: unknown): v is number => Number.isSafeInteger(v) && (v as number) >= 0;\n\n/**\n * The mentions of a message as a receiver (or the sender) takes them: at most `count` of them, each naming a\n * member key (or everyone, when `everyone` allows it), at a place of the text that starts with \"@\" and holds no\n * line break or control character, in order and not overlapping. An entry that is none of that is dropped; a list\n * longer than the bound is dropped whole.\n */\nexport function validMentions(raw: unknown, text: string, everyone: boolean): GroupMention[] {\n if (!Array.isArray(raw) || raw.length > MENTION_LIMITS.count) return [];\n const points = Array.from(text);\n const kept: GroupMention[] = [];\n for (const entry of raw) {\n if (!entry || typeof entry !== \"object\") continue;\n const { k, o, l } = entry as Record<string, unknown>;\n if (typeof k !== \"string\" || !(MEMBER_KEY.test(k) || (everyone && k === MENTION_EVERYONE))) continue;\n if (!isInt(o) || !isInt(l) || l < 2 || l > MENTION_LIMITS.chars || o + l > points.length || points[o] !== \"@\") continue;\n // A name has no line break, control or direction character: a mention holding one is not a name.\n if (NOT_IN_A_NAME.test(points.slice(o, o + l).join(\"\"))) continue;\n kept.push({ k, o, l });\n }\n kept.sort((a, b) => a.o - b.o);\n return kept.filter((m, i) => i === 0 || m.o >= kept[i - 1].o + kept[i - 1].l);\n}\n\n/** Whether a message's mentions name this member (by key, or everyone). */\nexport function mentionsMember(mentions: readonly GroupMention[] | undefined, key: string): boolean {\n return !!mentions?.some(m => m.k === key || m.k === MENTION_EVERYONE);\n}\n\n/** The bytes a list of mentions adds to a message, as JSON: counted against the text's bound. */\nexport function mentionsBytes(mentions: readonly GroupMention[]): number {\n return mentions.length ? utf8Encode(JSON.stringify(mentions)).length : 0;\n}\n\n/** Only the wire fields, in a fixed order (what the sender seals). */\nexport const wireMentions = (mentions: readonly GroupMention[]): GroupMention[] => mentions.map(({ k, o, l }) => ({ k, o, l }));\n","import { utf8Encode } from \"./bytes\";\nimport { MEMBER_KEY } from \"./groupCommits\";\nimport { MENTION_LIMITS, validMentions, type GroupMention } from \"./groupMentions\";\nimport { validEditNumber } from \"./pairedEdits\";\nimport { cardEditNumber, readStatusCard, type StatusCard } from \"./statusCards\";\n\n/**\n * Editing a sent text in a group (WISP 9xx § Edits, both profiles). As in a 1:1 chat (WISP 401 § Edits), the author says\n * the whole new text of one of its own messages with an edit number of its own per message, and the highest number\n * wins whatever order edits arrive in. A group has no receipts: the author says an edit to every member it can, and\n * again later (a private group: when an edge opens; a community: its members catch up from each other).\n *\n * - A private group (`group-mesh/1`) sends a `group-edit` frame of its own over each edge, sealed under the key of the\n * epoch the message was sent in and signed by its author (`GroupSession.sendEdit`).\n * - A community (`group-community/1`) sends an application frame through the group, sealed and signed like any payload,\n * so every hub carries it and a member who was away gets it with the catch-up:\n *\n * { \"x\": { \"t\": \"edit\", \"id\": <message id>, \"v\": 3, \"ts\": 1790000000000, \"text\": \"Done: 12 of 12\", \"m\"?: [mentions], \"sc\"?: {card} } }\n *\n * `sc` is the status card of this version (WISP 4xx · Status Cards); an edit with one may be numbered up to\n * `STATUS_CARD_LIMITS.edits`, one without only up to `MAX_EDITS_PER_MESSAGE`.\n *\n * Older apps drop both: a mesh session ignores a `t` it does not know, a community app the `x` frames it does not know.\n */\n\nexport const GROUP_EDIT_FRAME = \"group-edit\";\nexport const COMMUNITY_EDIT_FRAME = \"edit\";\n\n/** One edit of a group message: the message's id in the group, its edit number, when it was made, the whole new text, its mentions. */\nexport interface GroupEdit {\n id: string;\n /** The edit number, 1 for the first edit (`MAX_EDITS_PER_MESSAGE` at most). */\n e: number;\n ts: number;\n /** The whole new text. */\n m: string;\n /** Places of the new text that name members. */\n k?: GroupMention[];\n /** The status card of this version (WISP 4xx · Status Cards). */\n sc?: StatusCard;\n}\n\n/** An edit as a group hands it to the engine: `sender` is the member it is authenticated as. */\nexport interface GroupIncomingEdit extends GroupEdit { sender: string }\n\n/** The text bound of a group edit, as a message's: 16 KiB of UTF-8. */\nexport const GROUP_EDIT_TEXT_BYTES = 16 * 1024;\n\nconst MESH_ID = /^([a-km-uw-z13-9]{52}):(\\d{1,9}):(\\d{1,9})$/;\nconst COMMUNITY_ID = /^([a-km-uw-z13-9]{52}):(\\d{1,9}):([0-9a-f]{16}):(\\d{1,9})$/;\n\n/** A private group's message id (`<sender>:<epoch>:<seq>`), split; null when it is not one. */\nexport function meshMessageRef(id: string): { s: string; e: number; n: number } | null {\n const match = MESH_ID.exec(id);\n if (!match || !MEMBER_KEY.test(match[1])) return null;\n return { s: match[1], e: Number(match[2]), n: Number(match[3]) };\n}\n\n/** Whose message a community's id names (`<sender>:<epoch>:<commit>:<seq>`); null when it is not one. */\nexport function communityMessageAuthor(id: string): string | null {\n const match = COMMUNITY_ID.exec(id);\n return match && MEMBER_KEY.test(match[1]) ? match[1] : null;\n}\n\n/** A text an edit may carry: trimmed, not empty, within the bound. */\nexport function validEditText(text: unknown): text is string {\n return typeof text === \"string\" && !!text && text === text.trim() && utf8Encode(text).length <= GROUP_EDIT_TEXT_BYTES;\n}\n\n/** The `x` frame a community edit travels as. */\nexport function communityEditFrame(edit: GroupEdit): Record<string, unknown> {\n return { t: COMMUNITY_EDIT_FRAME, id: edit.id, v: edit.e, ts: edit.ts, text: edit.m, ...(edit.k?.length ? { m: edit.k } : {}), ...(edit.sc ? { sc: edit.sc } : {}) };\n}\n\n/**\n * The edit a community `x` frame says, from `sender` (whom the group authenticated it as), or null when it is not a\n * valid edit of one of the sender's own messages. Mentions that do not hold are left out, never the edit; a community\n * names no one as everyone.\n */\nexport function parseCommunityEdit(frame: Record<string, unknown>, sender: string): GroupEdit | null {\n if (frame?.t !== COMMUNITY_EDIT_FRAME) return null;\n const { id, v, ts, text } = frame;\n if (typeof id !== \"string\" || communityMessageAuthor(id) !== sender || !cardEditNumber(v)) return null;\n if (typeof ts !== \"number\" || !Number.isSafeInteger(ts) || ts <= 0 || !validEditText(text)) return null;\n const sc = frame.sc === undefined ? undefined : readStatusCard(frame.sc);\n if (!validEditNumber(v) && !sc) return null;\n const k = validMentions(frame.m, text, false);\n return { id, e: v, ts, m: text, ...(k.length ? { k } : {}), ...(sc ? { sc } : {}) };\n}\n\n/**\n * The mentions of an edited text: the places the message named that are still there, found by their words (the text\n * around them may have changed, or their order), and those `added` while editing. Where two overlap, `added` wins.\n */\nexport function carryMentions(before: { text: string; mentions?: readonly GroupMention[] }, after: string, added: readonly GroupMention[], everyone: boolean): GroupMention[] {\n const was = Array.from(before.text), now = Array.from(after);\n const overlaps = (a: GroupMention, b: GroupMention) => a.o < b.o + b.l && b.o < a.o + a.l;\n const carried: GroupMention[] = [];\n for (const m of [...(before.mentions ?? [])].sort((a, b) => a.o - b.o)) {\n const words = was.slice(m.o, m.o + m.l);\n // The first place with its words that no mention carried already holds: \"@Bob … @Bob\" keeps both, in either order.\n for (let at = indexOf(now, words, 0); at >= 0; at = indexOf(now, words, at + 1)) {\n const place = { k: m.k, o: at, l: m.l };\n if (carried.some(c => overlaps(c, place))) continue;\n carried.push(place);\n break;\n }\n }\n const fresh = validMentions(added.map(({ k, o, l }) => ({ k, o, l })), after, everyone);\n const all = [...fresh, ...carried.filter(m => !fresh.some(a => overlaps(a, m)))].sort((a, b) => a.o - b.o);\n return validMentions(all.slice(0, MENTION_LIMITS.count), after, everyone);\n}\n\nfunction indexOf(points: readonly string[], words: readonly string[], from: number): number {\n if (!words.length) return -1;\n outer: for (let i = from; i + words.length <= points.length; i++) {\n for (let j = 0; j < words.length; j++) if (points[i + j] !== words[j]) continue outer;\n return i;\n }\n return -1;\n}\n","import { fromBase64Url, randomBytes, toBase64Url, utf8Encode } from \"./bytes\";\nimport { identityFromSeedB64, publicKeyFromZ32, sign, verify } from \"./identity\";\nimport { sanitizeNick } from \"./text\";\nimport {\n GROUP_PROFILE, confirmationMatches, confirmationTag, decryptText, encryptText, epochKeys, newEpochSecret, openSecret, sealSecret,\n type SealedSecret,\n} from \"./groupCrypto\";\nimport {\n GROUP_ID, MEMBER_KEY, MAX_GROUP_CHAIN, commitHash, commitUntaggedHash, expectedRoster, rosterAdmin, rosterHas, signCommit, verifyChain, verifyCommit, verifyCommitSignature,\n type CommitKind, type GroupCommit, type GroupRole, type Roster,\n} from \"./groupCommits\";\nimport {\n encodeGroupMetaBody, groupDisplayName, groupMetaBody, groupName, groupMetaChange, groupMetaNewer, groupMetaPicture, nextGroupMetaRevision, parseGroupMetaBody, groupMetaTag, openGroupMeta, parseGroupMetaFrame, parseGroupMetaTag, signGroupMeta, verifyGroupMetaSignature, wrapGroupMeta,\n type GroupMeta, type GroupMetaChange, type GroupMetaFrame,\n} from \"./groupMeta\";\nimport { MENTION_LIMITS, validMentions, wireMentions, type GroupMention } from \"./groupMentions\";\nimport { groupReplyAuthor, readReply, REPLY_LIMITS, wireReply, type WireReply } from \"./replies\";\nimport { readForwarded } from \"./forwards\";\nimport { STATUS_CARD_LIMITS, cardEditNumber, readStatusCard, type StatusCard } from \"./statusCards\";\nimport { GROUP_EDIT_FRAME, meshMessageRef, validEditText, type GroupIncomingEdit } from \"./groupEdits\";\nimport { validEditNumber } from \"./pairedEdits\";\nimport { MESH_HUBS, meshRendezvous, NO_HUB_POLICY, type MeshHubPolicy } from \"./groupHubs\";\nimport { readReaction, type WireReaction } from \"./reactions\";\nimport { GROUP_PIN_FRAME, readPin, type WirePin } from \"./pins\";\nimport { RateWindow } from \"./pairedEdits\";\n\n/**\n * One member's view of a `group-mesh/1` group: the membership chain, the epoch\n * secrets it was given, its own bounded log of sent messages and what it has\n * seen from each sender. It is transport-agnostic: frames go out through\n * `send(to, frame)` and come in through `handle(from, frame)`, where `to` and\n * `from` are member keys the caller has authenticated (the pairwise edge of\n * the group is pinned to them). Admission frames travel on the inviter's\n * contact link and are handled by `admit`, `join` and `welcome`.\n */\nexport const GROUP_LIMITS = {\n /** Application text, UTF-8 bytes. */\n textBytes: 16 * 1024,\n /** Own messages kept for catch-up: count and total ciphertext bytes. */\n outlog: 32,\n outlogBytes: 128 * 1024,\n /** Frames waiting for an epoch (or its secret) that has not arrived yet. */\n waiting: 64,\n waitingBytes: 1024 * 1024,\n /** Epoch secrets kept: past this, older epochs cannot be read or handed on. */\n secrets: 32,\n /** Secrets in one `group-secrets` frame: apps from before revision 0.9 drop a frame with more. */\n secretsPerFrame: 16,\n /**\n * Other members' messages kept to hand on to a member who missed them (any member catches up the others): count\n * and total ciphertext bytes. The oldest go first.\n */\n relay: 256,\n relayBytes: 256 * 1024,\n /** Other members' edits kept to hand on, the latest per message (WISP 9xx § Edits): count and ciphertext bytes. */\n relayEdits: 64,\n relayEditBytes: 512 * 1024,\n /** Edits of one member handed on for one sync that asks for them: fewer than a receiver takes in a window. */\n handOnEdits: 16,\n /** Sequence numbers below the highest seen that a sync names as missing, per sender and epoch. */\n miss: 32,\n /** Per sender and epoch, the sequence numbers remembered below the highest seen. */\n window: 256,\n /** Commits ahead of the chain kept while the gap is fetched. */\n pendingCommits: 16,\n /** Commits carried in one welcome or chain frame, and their size: a frame stays well within the 60 KiB an edge carries. */\n chainPiece: 24,\n chainPieceBytes: 40 * 1024,\n /**\n * Syncs of one member answered in `syncWindowMs`: an edge opening, a question every ten seconds at most and a gossip\n * turn a minute fit well within; a member asking more is not answered until the window has room.\n */\n syncAnswers: 8,\n syncWindowMs: 60_000,\n /** Messages delivered from a copy that was not whole, remembered until a whole copy completes them. */\n provisional: 256,\n} as const;\n\n/** What every member should know about who can read what, in the words the apps show. */\nexport const GROUP_READ_NOTE = `Everyone in the group can read everything sent while they are a member. Someone removed keeps what they already received and cannot read what comes after; someone who joins later cannot read what came before. Messages go directly to each member; whoever was away gets what they missed from any member who has it (the group's last ${GROUP_LIMITS.relay} messages) when they meet again.`;\n\n/**\n * `m`: the message's mentions (`GroupMention[]` as JSON), sealed under the same epoch key with a nonce of their own\n * and bound to the header. Outside the signature, which older apps check as it was: only the author's edge ever\n * carries its frame, so the edge vouches for it. Older apps ignore the field.\n */\n/**\n * `xs`: the author's signature over the whole frame, the boxes of `m` and `r` included (revision 0.9), and `f` when\n * there is one. A frame handed on by another member keeps its boxes only with it: the edge it arrives on is not the\n * author's and vouches for nothing.\n * `f`: a forwarded text's hop count (WISP 9xx § Forwards), in the clear like the header: every member reads it anyway.\n * `sc`: a status card (WISP 4xx · Status Cards) as JSON, sealed like the reply in a box of its own, so an older app\n * reads the text, its fallback; `xs` covers it after the rest, only when there is one.\n */\nexport interface GroupMessageFrame { t: \"group-msg\"; g: string; e: number; s: string; n: number; ts: number; nn: string; c: string; sig: string; m?: { n: string; c: string }; r?: { n: string; c: string }; f?: number; sc?: { n: string; c: string }; xs?: string }\n/**\n * An edit of message `<s>:<e>:<n>` by its author (WISP 9xx § Edits): edit number `v`, the new text (and its mentions)\n * as JSON `{ text, m?, sc? }` sealed under the key of the message's epoch `e`, signed by the author. `sc`: the status card\n * of this version (WISP 4xx · Status Cards); with one, `v` may go up to `STATUS_CARD_LIMITS.edits`.\n */\nexport interface GroupEditFrame { t: \"group-edit\"; g: string; e: number; s: string; n: number; v: number; ts: number; nn: string; c: string; sig: string }\nexport interface GroupCommitFrame { t: \"group-commit\"; g: string; commit: GroupCommit; secret?: SealedSecret }\n/**\n * `mt`: which metadata statement I hold (`groupMetaTag`); apps without metadata leave it out. `miss`: sequence numbers\n * below the highest in `have` that never arrived, per sender and epoch. `ask`: members whose messages I want the\n * receiver to hand on (their edges to me are down); apps from before revision 0.9 send neither and are handed nothing.\n */\nexport interface GroupSyncFrame { t: \"group-sync\"; g: string; e: number; h: string; have: Record<string, Record<string, number>>; secrets: number[]; mt?: string; miss?: Record<string, Record<string, number[]>>; ask?: string[] }\nexport interface GroupSecretsFrame { t: \"group-secrets\"; g: string; secrets: { e: number; s: SealedSecret }[] }\nexport interface GroupLeaveFrame { t: \"group-leave\"; g: string }\nexport interface GroupInviteFrame { t: \"group-invite\"; g: string; name: string; admin: string; e: number; n: number }\nexport interface GroupAcceptFrame { t: \"group-accept\"; g: string; key: string }\nexport interface GroupDeclineFrame { t: \"group-decline\"; g: string }\nexport interface GroupChainFrame { t: \"group-chain\"; g: string; commits: GroupCommit[] }\nexport interface GroupWelcomeFrame { t: \"group-welcome\"; g: string; name: string; commits: GroupCommit[]; secrets: { e: number; s: SealedSecret }[] }\nexport interface GroupRemovedFrame { t: \"group-removed\"; g: string }\n/**\n * A member's leave, signed by its member key (WISP 9xx · Group Mesh § Hubs): unlike `group-leave`, which the admin\n * believes from the edge it comes on, hubs can carry it to an admin the member has no edge with. Apps from before drop it.\n */\nexport interface GroupByeFrame { t: \"group-bye\"; g: string; k: string; e: number; ts: number; sig: string }\nexport type GroupEdgeFrame = GroupMessageFrame | GroupEditFrame | GroupCommitFrame | GroupSyncFrame | GroupSecretsFrame | GroupLeaveFrame | GroupMetaFrame | GroupByeFrame;\nexport type GroupAdmissionFrame = GroupInviteFrame | GroupAcceptFrame | GroupDeclineFrame | GroupChainFrame | GroupWelcomeFrame | GroupRemovedFrame;\n\nexport type GroupStatus = \"active\" | \"left\" | \"removed\" | \"forked\";\n\nexport interface GroupState {\n id: string;\n name: string;\n profile: typeof GROUP_PROFILE;\n /** My member key seed for this group and nothing else. Wiped once I am out. */\n seedB64: string;\n createdAt: number;\n chain: GroupCommit[];\n /** Epoch → secret (base64url); only the epochs I was a member of, at most `secrets` of them. */\n secrets: Record<string, string>;\n status: GroupStatus;\n statusReason?: string;\n /** My next sequence number, in `seqEpoch`. */\n seq: number;\n seqEpoch: number;\n /** My own frames, newest last, for members who missed them. */\n sent: GroupMessageFrame[];\n /** Other members' frames, in the order they arrived, for members who missed them. Absent in states from before revision 0.9. */\n relay?: GroupMessageFrame[];\n /** Other members' edits, the latest per message, for members who missed them. Absent in states from before edits were handed on. */\n relayEdits?: GroupEditFrame[];\n /** Sender → epoch → highest sequence seen and the ones seen below it. */\n seen: Record<string, Record<string, { high: number; window: number[] }>>;\n /**\n * Messages delivered from a copy that was not whole, waiting for a whole copy to complete them, oldest first, at most\n * `GROUP_LIMITS.provisional`. Absent in states from before they were saved.\n */\n provisional?: string[];\n /** Names members announced on their edges. */\n nicks: Record<string, string>;\n /** The group's metadata (its picture), as the admin last signed it and I accepted it. */\n meta?: GroupMeta;\n}\n\n/**\n * `completes`: this message was delivered before from a copy another member handed on without its author's whole\n * signature (its text only); this is the whole one, with the mentions, reply and hop count its author put there.\n */\nexport interface GroupIncomingMessage { id: string; sender: string; epoch: number; seq: number; timestamp: number; text: string; mentions?: GroupMention[]; reply?: WireReply; forwarded?: number; card?: StatusCard; completes?: true }\n\nexport interface GroupSessionHooks {\n save(state: GroupState): Promise<void>;\n /** Best effort, over the pairwise edge to that member if it is open. */\n send(to: string, frame: GroupEdgeFrame): void;\n /** Store before it resolves: replay state advances only afterwards. */\n message(message: GroupIncomingMessage): Promise<void> | void;\n /** An edit of a member's message, authenticated as its author (WISP 9xx § Edits). Apps without edits leave it out. */\n edit?(edit: GroupIncomingEdit): Promise<void> | void;\n /** Roster, epoch or status changed. */\n changed(): void;\n /** The group's name or picture changed (set, replaced or removed), by `by`, in a statement signed at `at`. */\n metaChanged?(by: string, change: GroupMetaChange, at: number): void;\n /** The clock the limits read (the engine's, or a simulation's); the wall clock when absent. */\n clock?(): number;\n}\n\nconst MAX_TEXT_BOX = Math.ceil((GROUP_LIMITS.textBytes + 16) * 4 / 3) + 4;\n/** Sixteen mentions as JSON, each a key and two offsets, with room to spare, sealed. */\nconst MAX_MENTIONS_BOX = Math.ceil((MENTION_LIMITS.count * 96 + 16) * 4 / 3) + 4;\nconst mentionsAad = (f: Pick<GroupMessageFrame, \"g\" | \"e\" | \"s\" | \"n\" | \"ts\">) => JSON.stringify([\"ghostly-group/1 mentions\", f.g, f.e, f.s, f.n, f.ts]);\nconst isSealedBox = (max: number) => (v: unknown): v is { n: string; c: string } => !!v && typeof v === \"object\" &&\n typeof (v as Record<string, unknown>).n === \"string\" && (v as Record<string, string>).n.length === 32 && B64.test((v as Record<string, string>).n) &&\n typeof (v as Record<string, unknown>).c === \"string\" && (v as Record<string, string>).c.length <= max && B64.test((v as Record<string, string>).c);\nconst isMentionsBox = isSealedBox(MAX_MENTIONS_BOX);\n/** A reply as JSON: its id, a line of the original as it may arrive before it is cleaned, and a member key; sealed. */\nconst MAX_REPLY_BOX = Math.ceil((REPLY_LIMITS.id + REPLY_LIMITS.raw * 4 + 128) * 4 / 3) + 4;\nconst replyAad = (f: Pick<GroupMessageFrame, \"g\" | \"e\" | \"s\" | \"n\" | \"ts\">) => JSON.stringify([\"ghostly-group/1 reply\", f.g, f.e, f.s, f.n, f.ts]);\nconst isReplyBox = isSealedBox(MAX_REPLY_BOX);\n/** A status card as JSON (`STATUS_CARD_LIMITS.bytes` at most), sealed. */\nconst MAX_CARD_BOX = Math.ceil((STATUS_CARD_LIMITS.bytes + 16) * 4 / 3) + 4;\nconst cardAad = (f: Pick<GroupMessageFrame, \"g\" | \"e\" | \"s\" | \"n\" | \"ts\">) => JSON.stringify([\"ghostly-group/1 card\", f.g, f.e, f.s, f.n, f.ts]);\nconst isCardBox = isSealedBox(MAX_CARD_BOX);\nconst B64 = /^[A-Za-z0-9_-]*$/;\nconst secretAad = (g: string, e: number, member: string) => JSON.stringify([\"ghostly-group/1 secret\", g, e, member]);\nconst messageAad = (f: Pick<GroupMessageFrame, \"g\" | \"e\" | \"s\" | \"n\" | \"ts\">) => JSON.stringify([f.g, f.e, f.s, f.n, f.ts]);\nconst messageSigned = (f: Omit<GroupMessageFrame, \"sig\" | \"t\">) => utf8Encode(JSON.stringify([\"ghostly-group/1 msg\", f.g, f.e, f.s, f.n, f.ts, f.nn, f.c]));\n/**\n * What `xs` covers: the frame and both boxes, so a member handing it on cannot swap or forge them; a hop count after\n * them, only when there is one, so what an app from before forwards signs and checks stays the same; a card's box\n * after that, marked, only when there is one.\n */\nconst messageSignedWhole = (f: Omit<GroupMessageFrame, \"sig\" | \"t\" | \"xs\">) =>\n utf8Encode(JSON.stringify([\"ghostly-group/1 msg+\", f.g, f.e, f.s, f.n, f.ts, f.nn, f.c, f.m?.n ?? \"\", f.m?.c ?? \"\", f.r?.n ?? \"\", f.r?.c ?? \"\", ...(f.f !== undefined ? [f.f] : []),\n ...(f.sc ? [\"sc\", f.sc.n, f.sc.c] : [])]));\nexport const groupMessageId = (sender: string, epoch: number, seq: number) => `${sender}:${epoch}:${seq}`;\n/** An edit's box holds the text, its mentions and its card as JSON: room for a text whose every character JSON escapes. */\nconst MAX_EDIT_PLAIN = GROUP_LIMITS.textBytes * 2 + MENTION_LIMITS.count * 96 + STATUS_CARD_LIMITS.bytes + 64;\nconst MAX_EDIT_BOX = Math.ceil((MAX_EDIT_PLAIN + 16) * 4 / 3) + 4;\nconst editAad = (f: Pick<GroupEditFrame, \"g\" | \"e\" | \"s\" | \"n\" | \"v\" | \"ts\">) => JSON.stringify([\"ghostly-group/1 edit\", f.g, f.e, f.s, f.n, f.v, f.ts]);\n/** What a member signs of its reaction, so that a hub can pass it on (WISP 9xx · Group Mesh § Hubs). */\nconst reactionSigned = (g: string, r: WireReaction) => utf8Encode(JSON.stringify([\"ghostly-group/1 react\", g, r.id, r.e, r.n]));\n/**\n * A member's reaction as a hub passes it on: signed by the member (`k`), unlike `group-react`, which the edge it comes\n * on vouches for. A frame of its own, which apps from before hubs drop: they would take it as the hub's reaction.\n */\nexport const GROUP_REACTED_FRAME = \"group-reacted\";\nexport interface GroupReactedFrame extends WireReaction { t: typeof GROUP_REACTED_FRAME; g: string; k: string; sig: string }\n/** What a member signs of its pin: any member passes it on as it is (WISP 9xx · Group Mesh § Pinned message). */\nconst pinSigned = (g: string, p: WirePin) => utf8Encode(JSON.stringify([\"ghostly-group/1 pin\", g, p.id, p.n]));\nexport interface GroupPinFrame extends WirePin { t: typeof GROUP_PIN_FRAME; g: string; k: string; sig: string }\nconst byeSigned = (f: Omit<GroupByeFrame, \"sig\" | \"t\">) => utf8Encode(JSON.stringify([\"ghostly-group/1 bye\", f.g, f.k, f.e, f.ts]));\nconst editSigned = (f: Omit<GroupEditFrame, \"sig\" | \"t\">) => utf8Encode(JSON.stringify([\"ghostly-group/1 edit\", f.g, f.e, f.s, f.n, f.v, f.ts, f.nn, f.c]));\nfunction isEditFrame(v: unknown): v is GroupEditFrame {\n if (!v || typeof v !== \"object\") return false;\n const f = v as Record<string, unknown>;\n return f.t === GROUP_EDIT_FRAME && typeof f.g === \"string\" && GROUP_ID.test(f.g) && Number.isSafeInteger(f.e) && (f.e as number) >= 0 &&\n typeof f.s === \"string\" && MEMBER_KEY.test(f.s) && Number.isSafeInteger(f.n) && (f.n as number) >= 0 && cardEditNumber(f.v) &&\n Number.isSafeInteger(f.ts) && (f.ts as number) > 0 && typeof f.nn === \"string\" && f.nn.length === 32 && B64.test(f.nn) &&\n typeof f.c === \"string\" && f.c.length <= MAX_EDIT_BOX && B64.test(f.c) && typeof f.sig === \"string\" && f.sig.length === 86 && B64.test(f.sig);\n}\n\n/** What a frame weighs in a log, roughly: its boxes plus the fixed fields around them. */\nconst frameBytes = (f: GroupMessageFrame) => f.c.length + (f.m?.c.length ?? 0) + (f.r?.c.length ?? 0) + (f.sc?.c.length ?? 0) + 400;\n\n/** Only the fields an edit frame has, as `clean` does for a message. */\nconst cleanEdit = (f: GroupEditFrame): GroupEditFrame => ({ t: GROUP_EDIT_FRAME, g: f.g, e: f.e, s: f.s, n: f.n, v: f.v, ts: f.ts, nn: f.nn, c: f.c, sig: f.sig });\n\n/** Only the fields a message frame has: what a member keeps and hands on carries nothing else its author put there. */\nfunction clean(f: GroupMessageFrame): GroupMessageFrame {\n return { t: \"group-msg\", g: f.g, e: f.e, s: f.s, n: f.n, ts: f.ts, nn: f.nn, c: f.c, sig: f.sig,\n ...(isMentionsBox(f.m) ? { m: { n: f.m.n, c: f.m.c } } : {}), ...(isReplyBox(f.r) ? { r: { n: f.r.n, c: f.r.c } } : {}),\n ...(readForwarded(f.f) ? { f: f.f } : {}), ...(isCardBox(f.sc) ? { sc: { n: f.sc.n, c: f.sc.c } } : {}),\n ...(typeof f.xs === \"string\" && f.xs.length === 86 && B64.test(f.xs) ? { xs: f.xs } : {}) };\n}\n\n/** Whether the member whose sync this is lacks a frame: above what it has from that sender, or one it names as missing. */\nfunction missingIn(frame: GroupSyncFrame, from: string): (f: GroupMessageFrame) => boolean {\n const have = frame.have && typeof frame.have === \"object\" ? frame.have as Record<string, Record<string, unknown>> : {};\n const miss = frame.miss && typeof frame.miss === \"object\" ? frame.miss as Record<string, Record<string, unknown>> : {};\n return f => {\n if (f.s === from) return false;\n const high = have[f.s]?.[f.e];\n if (!Number.isSafeInteger(high) || (high as number) < f.n) return true;\n const gaps = miss[f.s]?.[f.e];\n return Array.isArray(gaps) && gaps.length <= GROUP_LIMITS.miss && gaps.includes(f.n);\n };\n}\n\n/**\n * My own messages and the ones I kept of other members, as one list for a member catching up: each log keeps its own\n * order (mine as I sent them, the others' as they reached me, which no member's clock can change), and the two are\n * merged by the time each frame says, mine first when they say the same. A member whose clock is off moves only\n * where its lines fall among mine, never the order within a log.\n */\nexport function asWritten(own: readonly GroupMessageFrame[], kept: readonly GroupMessageFrame[]): GroupMessageFrame[] {\n const out: GroupMessageFrame[] = [];\n let i = 0, j = 0;\n while (i < own.length || j < kept.length) out.push(j >= kept.length || (i < own.length && own[i].ts <= kept[j].ts) ? own[i++] : kept[j++]);\n return out;\n}\n\nfunction isSealed(v: unknown): v is SealedSecret {\n return !!v && typeof v === \"object\" && [\"e\", \"n\", \"c\"].every(k => typeof (v as Record<string, unknown>)[k] === \"string\" && B64.test((v as Record<string, string>)[k]) && (v as Record<string, string>)[k].length <= 128);\n}\nfunction isMessageFrame(v: unknown): v is GroupMessageFrame {\n if (!v || typeof v !== \"object\") return false;\n const f = v as Record<string, unknown>;\n return f.t === \"group-msg\" && typeof f.g === \"string\" && GROUP_ID.test(f.g) && Number.isSafeInteger(f.e) && (f.e as number) >= 0 &&\n typeof f.s === \"string\" && MEMBER_KEY.test(f.s) && Number.isSafeInteger(f.n) && (f.n as number) >= 0 && Number.isSafeInteger(f.ts) && (f.ts as number) > 0 &&\n typeof f.nn === \"string\" && f.nn.length === 32 && B64.test(f.nn) && typeof f.c === \"string\" && f.c.length <= MAX_TEXT_BOX && B64.test(f.c) &&\n typeof f.sig === \"string\" && f.sig.length === 86 && B64.test(f.sig);\n}\n\n/** The chain cut in consecutive pieces of at most `chainPiece` commits and `chainPieceBytes`: a roster of 32 makes a commit ~2.4 KB. */\nexport function chainPieces(chain: GroupCommit[]): GroupCommit[][] {\n const pieces: GroupCommit[][] = [];\n let piece: GroupCommit[] = [], bytes = 0;\n for (const commit of chain) {\n const size = JSON.stringify(commit).length + 1;\n if (piece.length && (piece.length >= GROUP_LIMITS.chainPiece || bytes + size > GROUP_LIMITS.chainPieceBytes)) { pieces.push(piece); piece = []; bytes = 0; }\n piece.push(commit); bytes += size;\n }\n pieces.push(piece);\n return pieces;\n}\n\nexport class GroupSession {\n readonly state: GroupState;\n private readonly identity;\n /** Frames for an epoch, or a secret, not here yet. In memory only. */\n private waiting: { from: string; frame: GroupMessageFrame }[] = [];\n private waitingBytes = 0;\n /** Edits for an epoch, or a secret, not here yet: tried again with the frames above. In memory only. */\n private waitingEdits: { from: string; frame: GroupEditFrame }[] = [];\n private pendingCommits = new Map<number, GroupCommitFrame>();\n /** When each member was last asked to catch me up, so a stream of unreadable frames is one question, not a loop. */\n private asked = new Map<string, number>();\n private queue = Promise.resolve();\n /** One metadata frame that names a commit or an epoch I do not have yet: tried again when the chain moves. */\n private pendingMeta: { from: string; frame: unknown } | undefined;\n /** While `handle` runs: the frames it took that a hub passes on (`handle`'s result). */\n private passOn: GroupEdgeFrame[] | null = null;\n /** Syncs answered per member, a few a minute (`GROUP_LIMITS.syncAnswers`). In memory only. */\n private syncsAnswered = new Map<string, RateWindow>();\n /**\n * Messages delivered from a copy that was not whole (handed on without its author's whole signature): not seen, so\n * a sync still asks for them and a whole copy completes them. Oldest first, saved as `GroupState.provisional`, so a\n * restart does not let a stripped copy take the message's place.\n */\n private provisional: Set<string>;\n\n constructor(state: GroupState, private readonly hooks: GroupSessionHooks) {\n this.state = state;\n this.identity = identityFromSeedB64(state.seedB64);\n this.provisional = new Set((state.provisional ?? []).slice(-GROUP_LIMITS.provisional));\n }\n\n /** A new group: its genesis commit, signed by the creator, who is its admin. */\n static create(name: string, now = Date.now()): GroupState {\n const identity = identityFromSeedB64(toBase64Url(randomBytes(32)));\n const id = toBase64Url(randomBytes(16));\n const secret = newEpochSecret();\n const draft: Omit<GroupCommit, \"sig\" | \"c\"> = { v: 1, g: id, e: 0, p: \"\", k: \"create\", m: [[identity.pubKeyZ32, \"admin\"]], by: identity.pubKeyZ32, ts: now };\n const commit = signCommit({ ...draft, c: confirmationTag(epochKeys(secret, id, 0).confirm, commitUntaggedHash(draft)) }, identity.seed);\n return { id, name: sanitizeNick(name) ?? \"Group\", profile: GROUP_PROFILE, seedB64: identity.seedB64, createdAt: now, chain: [commit],\n secrets: { 0: toBase64Url(secret) }, status: \"active\", seq: 0, seqEpoch: 0, sent: [], seen: {}, nicks: {} };\n }\n\n /**\n * Joining from a welcome: the chain must verify from its genesis, admit me\n * in a commit signed by the admin the invitation named (learned over the\n * inviter's authenticated contact link), end with me in the roster, and\n * come with the secret of the current epoch, confirmed against its commit.\n * Earlier epochs I was a member of may come too; nothing before my admission can.\n */\n static join(invite: { name: string; admin: string }, pieces: unknown[], welcome: unknown, seedB64: string, now = Date.now()): { state: GroupState } | { error: string } {\n if (!welcome || typeof welcome !== \"object\") return { error: \"Malformed welcome\" };\n const w = welcome as Record<string, unknown>;\n if (w.t !== \"group-welcome\" || typeof w.g !== \"string\" || !GROUP_ID.test(w.g) || !Array.isArray(w.commits) || !Array.isArray(w.secrets)) return { error: \"Malformed welcome\" };\n const commits = [...pieces.flatMap(p => (p && typeof p === \"object\" && Array.isArray((p as GroupChainFrame).commits)) ? (p as GroupChainFrame).commits : []), ...w.commits];\n if (commits.length > MAX_GROUP_CHAIN) return { error: \"Membership chain too long\" };\n const verified = verifyChain(commits, w.g);\n if (\"error\" in verified) return verified;\n const chain = verified.chain;\n const identity = identityFromSeedB64(seedB64);\n const me = identity.pubKeyZ32;\n const top = chain[chain.length - 1];\n if (!rosterHas(top.m, me)) return { error: \"The welcome does not admit this member key\" };\n let admitted = -1;\n for (let i = chain.length - 1; i >= 0; i--) if (chain[i].k === \"add\" && chain[i].s === me) { admitted = i; break; }\n // The trust anchor: the contact who invited me, authenticated on our chat, is the admin who admitted me.\n if (admitted < 0 || chain[admitted].by !== invite.admin) return { error: \"The group's admin is not the contact who invited you\" };\n const secrets: Record<string, string> = {};\n for (const entry of w.secrets as unknown[]) {\n if (!entry || typeof entry !== \"object\") continue;\n const { e, s } = entry as { e?: unknown; s?: unknown };\n if (!Number.isSafeInteger(e) || (e as number) < admitted || (e as number) > top.e || !isSealed(s) || !rosterHas(chain[e as number].m, me)) continue;\n const secret = openSecret(identity.seed, me, s, secretAad(w.g, e as number, me));\n if (secret && confirmationMatches(epochKeys(secret, w.g, e as number).confirm, commitUntaggedHash(chain[e as number]), chain[e as number].c)) secrets[e as number] = toBase64Url(secret);\n }\n if (!secrets[top.e]) return { error: \"The welcome carries no usable secret for the current epoch\" };\n return { state: { id: w.g, name: groupName(w.name) ?? groupName(invite.name) ?? \"Group\", profile: GROUP_PROFILE, seedB64, createdAt: now, chain, secrets,\n status: \"active\", seq: 0, seqEpoch: top.e, sent: [], seen: {}, nicks: {} } };\n }\n\n get id(): string { return this.state.id; }\n /** The name the admin gave the group, else the one it had when I got in (WISP 9xx § Metadata). */\n get name(): string { return groupDisplayName(this.state.meta, this.state.name); }\n get myKey(): string { return this.identity.pubKeyZ32; }\n get epoch(): number { return this.state.chain[this.state.chain.length - 1].e; }\n get top(): GroupCommit { return this.state.chain[this.state.chain.length - 1]; }\n get roster(): Roster { return this.top.m; }\n get admin(): string | undefined { return rosterAdmin(this.roster); }\n get isAdmin(): boolean { return this.admin === this.myKey && this.state.status === \"active\"; }\n get status(): GroupStatus { return this.state.status; }\n /** Members other than me, in the current roster. */\n get others(): string[] { return this.roster.map(([k]) => k).filter(k => k !== this.myKey); }\n role(key: string): GroupRole | undefined { return this.roster.find(([k]) => k === key)?.[1]; }\n /** The group's picture, if it has one. */\n get picture(): string | undefined { return groupMetaPicture(this.state.meta); }\n /** Whom the admin pinned as hubs and excluded (WISP 9xx · Group Mesh § Hubs), from the group's metadata. */\n get hubPolicy(): MeshHubPolicy {\n // Parsed once per statement: the body may hold a picture of 40,000 characters, and views ask for this per member.\n const meta = this.state.meta;\n if (!this.policyOf || this.policyOf.sig !== meta?.sig) this.policyOf = { sig: meta?.sig, policy: (meta && parseGroupMetaBody(meta.body)?.hubs) || NO_HUB_POLICY };\n return this.policyOf.policy;\n }\n private policyOf: { sig: string | undefined; policy: MeshHubPolicy } | undefined;\n /** The current epoch's rendezvous secret (its beacon and lobbies), once its secret is here. */\n get rendezvous(): string | undefined {\n const secret = this.secret(this.epoch);\n return secret && meshRendezvous(secret, this.id, this.epoch);\n }\n /** Epochs whose messages this member can still read. */\n get readableEpochs(): number[] { return Object.keys(this.state.secrets).map(Number).sort((a, b) => a - b); }\n /** How many messages, per sender, are known to be missing in the current epoch. */\n missing(sender: string): number {\n const entry = this.state.seen[sender]?.[this.epoch];\n if (!entry) return 0;\n const lowest = Math.max(0, entry.high - GROUP_LIMITS.window + 1);\n return Math.max(0, entry.high - lowest + 1 - (1 + entry.window.filter(n => n >= lowest).length));\n }\n\n private serialize<T>(run: () => Promise<T>): Promise<T> {\n const operation = this.queue.then(run);\n this.queue = operation.then(() => {}, () => {});\n return operation;\n }\n private persist(): Promise<void> { return this.hooks.save(structuredClone(this.state)); }\n private secret(epoch: number): Uint8Array | undefined {\n const s = this.state.secrets[epoch];\n return s ? fromBase64Url(s) : undefined;\n }\n private requireAdmin(): void {\n if (this.state.status !== \"active\") throw new Error(\"You are no longer in this group\");\n if (!this.isAdmin) throw new Error(\"Only the admin can change the members of this group\");\n }\n\n /** What the admin sends a contact over their chat to invite them. */\n inviteFrame(): GroupInviteFrame {\n this.requireAdmin();\n return { t: \"group-invite\", g: this.id, name: this.name, admin: this.myKey, e: this.epoch, n: this.roster.length };\n }\n\n /**\n * Admits a member who accepted: a new epoch whose secret goes to everyone in\n * the new roster. Returns the welcome for the newcomer (to send over the\n * contact link) after telling the existing members over their edges.\n */\n admit(memberKey: string, now = Date.now()): Promise<(GroupChainFrame | GroupWelcomeFrame)[]> {\n return this.serialize(async () => {\n this.requireAdmin();\n if (!MEMBER_KEY.test(memberKey) || memberKey === this.myKey) throw new Error(\"Invalid member key\");\n if (rosterHas(this.roster, memberKey)) throw new Error(\"Already a member\");\n const { secret } = await this.commit(\"add\", memberKey, now);\n const sealed = { e: this.epoch, s: sealSecret(memberKey, secret, secretAad(this.id, this.epoch, memberKey)) };\n const pieces = chainPieces(this.state.chain);\n const tail = pieces.pop()!;\n return [...pieces.map((commits): GroupChainFrame => ({ t: \"group-chain\", g: this.id, commits })),\n { t: \"group-welcome\", g: this.id, name: this.name, commits: tail, secrets: [sealed] }];\n });\n }\n\n remove(memberKey: string, now = Date.now()): Promise<void> {\n return this.serialize(async () => {\n this.requireAdmin();\n if (!rosterHas(this.roster, memberKey) || memberKey === this.myKey) throw new Error(\"Not a member\");\n await this.commit(\"remove\", memberKey, now);\n });\n }\n\n transferAdmin(memberKey: string, now = Date.now()): Promise<void> {\n return this.serialize(async () => {\n this.requireAdmin();\n if (!rosterHas(this.roster, memberKey) || memberKey === this.myKey) throw new Error(\"Not a member\");\n await this.commit(\"role\", memberKey, now);\n });\n }\n\n /** A fresh secret without a membership change: whoever holds the old one and is not a member learns nothing new. */\n rotate(now = Date.now()): Promise<void> {\n return this.serialize(async () => { this.requireAdmin(); await this.commit(\"rotate\", undefined, now); });\n }\n\n private async commit(kind: CommitKind, subject: string | undefined, now: number): Promise<{ secret: Uint8Array }> {\n if (this.state.chain.length >= MAX_GROUP_CHAIN) throw new Error(\"This group has reached its membership history limit. Create a new group.\");\n const previous = this.top;\n const roster = expectedRoster(previous.m, kind, this.myKey, subject);\n if (!roster) throw new Error(\"This change is not allowed\");\n const epoch = previous.e + 1;\n const secret = newEpochSecret();\n const draft: Omit<GroupCommit, \"sig\" | \"c\"> = { v: 1, g: this.id, e: epoch, p: commitHash(previous), k: kind, m: roster, by: this.myKey, ...(subject ? { s: subject } : {}), ts: now };\n const commit = signCommit({ ...draft, c: confirmationTag(epochKeys(secret, this.id, epoch).confirm, commitUntaggedHash(draft)) }, this.identity.seed);\n this.state.chain.push(commit);\n this.state.secrets[epoch] = toBase64Url(secret);\n this.pruneSecrets();\n this.pruneRelay();\n this.state.seq = 0; this.state.seqEpoch = epoch;\n await this.persist();\n // The old roster hears about it, the one removed included (without a secret).\n for (const [key] of previous.m) {\n if (key === this.myKey) continue;\n const member = rosterHas(roster, key);\n this.hooks.send(key, { t: \"group-commit\", g: this.id, commit, ...(member ? { secret: sealSecret(key, secret, secretAad(this.id, epoch, key)) } : {}) });\n }\n this.hooks.changed();\n return { secret };\n }\n\n /** Someone out of the roster is not handed on any more, whatever they sent while in it (WISP 9xx § Catch-up). */\n private pruneRelay(): void {\n if (this.state.relay?.length) this.state.relay = this.state.relay.filter(f => rosterHas(this.roster, f.s));\n if (this.state.relayEdits?.length) this.state.relayEdits = this.state.relayEdits.filter(f => rosterHas(this.roster, f.s));\n }\n\n /** Another member's edit, kept to hand on: the latest per message; the oldest go once the log is full. False: one as new was kept. */\n private keepEdit(frame: GroupEditFrame): boolean {\n const same = (f: GroupEditFrame) => f.s === frame.s && f.e === frame.e && f.n === frame.n;\n const log = this.state.relayEdits ?? [];\n if (log.some(f => same(f) && f.v >= frame.v)) return false;\n const next = [...log.filter(f => !same(f)), cleanEdit(frame)];\n let bytes = next.reduce((sum, f) => sum + f.c.length, 0);\n while (next.length > GROUP_LIMITS.relayEdits || bytes > GROUP_LIMITS.relayEditBytes) bytes -= next.shift()!.c.length;\n this.state.relayEdits = next;\n return true;\n }\n\n /** A frame of another member, kept to hand on; the oldest go once the log is full. */\n private keep(frame: GroupMessageFrame): void {\n const log = (this.state.relay ??= []);\n log.push(frame);\n let bytes = log.reduce((sum, f) => sum + frameBytes(f), 0);\n while (log.length > GROUP_LIMITS.relay || bytes > GROUP_LIMITS.relayBytes) bytes -= frameBytes(log.shift()!);\n }\n\n private pruneSecrets(): void {\n const epochs = Object.keys(this.state.secrets).map(Number).sort((a, b) => a - b);\n for (const e of epochs.slice(0, Math.max(0, epochs.length - GROUP_LIMITS.secrets))) delete this.state.secrets[e];\n }\n\n /** Leaves: my secrets go now; the admin removes me from the roster when it hears. */\n leave(): Promise<void> {\n return this.serialize(async () => {\n if (this.state.status !== \"active\") return;\n if (this.isAdmin && this.others.length) throw new Error(\"Make someone else the admin before leaving\");\n const admin = this.admin;\n this.out(\"left\", \"You left this group\");\n await this.persist();\n if (admin && admin !== this.myKey) this.hooks.send(admin, { t: \"group-leave\", g: this.id });\n this.hooks.changed();\n });\n }\n\n /** Told out of band (the admin's contact chat) that I was removed, before or instead of the commit. */\n markRemoved(): Promise<void> {\n return this.serialize(async () => {\n if (this.state.status !== \"active\") return;\n this.out(\"removed\", \"You were removed from this group\");\n await this.persist();\n this.hooks.changed();\n });\n }\n\n private out(status: GroupStatus, reason: string): void {\n this.state.status = status;\n this.state.statusReason = reason;\n this.state.secrets = {};\n this.state.sent = [];\n this.state.relay = [];\n this.state.relayEdits = [];\n this.waiting = []; this.waitingBytes = 0; this.pendingCommits.clear();\n }\n\n /**\n * Encrypts and signs a text, keeps it for catch-up and sends it to every other member. `mentions`: places of the\n * text that name members (everyone: the admin only); what does not hold is left out. `reply`: the message it\n * answers, sealed apart from the text (`r`) like the mentions, so an older app still reads the text. `forwarded`: the\n * hop count of a forwarded text (`f`, WISP 9xx § Forwards). `card`: a status card, checked by the caller, sealed in a\n * box of its own (`sc`, WISP 4xx · Status Cards); the text is its fallback.\n */\n sendText(text: string, now = Date.now(), mentions: readonly GroupMention[] = [], reply?: WireReply, forwarded?: number, card?: StatusCard): Promise<{ id: string } | { error: string }> {\n return this.serialize(async () => {\n if (this.state.status !== \"active\") return { error: this.state.statusReason ?? \"You are no longer in this group\" };\n const trimmed = text.trim();\n if (!trimmed) return { error: \"Nothing to send\" };\n if (utf8Encode(trimmed).length > GROUP_LIMITS.textBytes) return { error: \"Message exceeds 16 KiB\" };\n const epoch = this.epoch, secret = this.secret(epoch);\n if (!secret) return { error: \"This epoch's key has not arrived yet. Wait for a member to catch you up.\" };\n if (this.state.seqEpoch !== epoch) { this.state.seq = 0; this.state.seqEpoch = epoch; }\n const n = this.state.seq++;\n const header = { g: this.id, e: epoch, s: this.myKey, n, ts: now };\n const key = epochKeys(secret, this.id, epoch).message;\n const { n: nn, c } = encryptText(key, messageAad(header), trimmed);\n const unsigned = { ...header, nn, c };\n const named = validMentions(wireMentions(mentions), trimmed, this.isAdmin);\n const answers = reply && readReply(wireReply(reply), groupReplyAuthor);\n const boxes = { ...(named.length ? { m: encryptText(key, mentionsAad(header), JSON.stringify(named)) } : {}),\n ...(answers ? { r: encryptText(key, replyAad(header), JSON.stringify(answers)) } : {}), ...(readForwarded(forwarded) ? { f: forwarded } : {}),\n ...(card ? { sc: encryptText(key, cardAad(header), JSON.stringify(card)) } : {}) };\n const frame: GroupMessageFrame = { t: \"group-msg\", ...unsigned, sig: toBase64Url(sign(messageSigned(unsigned), this.identity.seed)), ...boxes,\n xs: toBase64Url(sign(messageSignedWhole({ ...unsigned, ...boxes }), this.identity.seed)) };\n this.state.sent.push(frame);\n let bytes = this.state.sent.reduce((sum, f) => sum + f.c.length, 0);\n while (this.state.sent.length > GROUP_LIMITS.outlog || bytes > GROUP_LIMITS.outlogBytes) bytes -= this.state.sent.shift()!.c.length;\n const id = groupMessageId(this.myKey, epoch, n);\n await this.hooks.message({ id, sender: this.myKey, epoch, seq: n, timestamp: now, text: trimmed, ...(named.length ? { mentions: named } : {}), ...(answers ? { reply: answers } : {}), ...(boxes.f ? { forwarded: boxes.f } : {}), ...(card ? { card } : {}) });\n await this.persist();\n for (const key of this.others) this.hooks.send(key, frame);\n return { id };\n });\n }\n\n /**\n * Says an edit of one of my messages (WISP 9xx § Edits): edit `v`, the whole new text and its mentions, sealed under\n * the key of the message's epoch and signed, to every member of that epoch still in the group, or to `to` alone (an\n * edge that just opened). Someone admitted after the message cannot open it and is not sent it. The message's\n * epoch key must still be here: past `GROUP_LIMITS.secrets` epochs a message cannot be edited.\n */\n sendEdit(messageId: string, edit: { v: number; ts: number; text: string; mentions?: readonly GroupMention[]; card?: StatusCard }, to?: string): Promise<{ sent: number } | { error: string }> {\n return this.serialize(async () => {\n if (this.state.status !== \"active\") return { error: this.state.statusReason ?? \"You are no longer in this group\" };\n const ref = meshMessageRef(messageId);\n const commit = ref && this.state.chain[ref.e];\n if (!ref || ref.s !== this.myKey || !commit || !rosterHas(commit.m, this.myKey)) return { error: \"Only your own messages can be edited\" };\n if (!(edit.card ? cardEditNumber(edit.v) : validEditNumber(edit.v))) return { error: \"This message was edited too many times\" };\n const text = edit.text.trim();\n if (!validEditText(text)) return { error: text ? \"Message exceeds 16 KiB\" : \"An edit cannot be empty\" };\n const secret = this.secret(ref.e);\n if (!secret) return { error: \"This message is too old to edit: its epoch's key is gone\" };\n const named = validMentions(wireMentions(edit.mentions ?? []), text, rosterAdmin(commit.m) === this.myKey);\n const plain = JSON.stringify({ text, ...(named.length ? { m: named } : {}), ...(edit.card ? { sc: edit.card } : {}) });\n if (utf8Encode(plain).length > MAX_EDIT_PLAIN) return { error: \"Message exceeds 16 KiB\" };\n const header = { g: this.id, e: ref.e, s: this.myKey, n: ref.n, v: edit.v, ts: edit.ts };\n const { n: nn, c } = encryptText(epochKeys(secret, this.id, ref.e).message, editAad(header), plain);\n const unsigned = { ...header, nn, c };\n const frame: GroupEditFrame = { t: GROUP_EDIT_FRAME, ...unsigned, sig: toBase64Url(sign(editSigned(unsigned), this.identity.seed)) };\n const members = (to === undefined ? this.others : [to]).filter(key => key !== this.myKey && rosterHas(this.roster, key) && rosterHas(commit.m, key));\n for (const key of members) this.hooks.send(key, frame);\n return { sent: members.length };\n });\n }\n\n\n /**\n * What to tell a member whose edge just opened: where I am, what I have from everyone and what never arrived below\n * that. `ask`: the members whose messages I want this one to hand on, since their own edges to me are down.\n */\n syncFrame(ask: readonly string[] = []): GroupSyncFrame {\n const have: Record<string, Record<string, number>> = {};\n const miss: Record<string, Record<string, number[]>> = {};\n for (const [sender, epochs] of Object.entries(this.state.seen)) {\n have[sender] = {};\n for (const [e, entry] of Object.entries(epochs)) {\n have[sender][e] = entry.high;\n const gaps = this.gaps(entry);\n if (gaps.length) (miss[sender] ??= {})[e] = gaps;\n }\n }\n const wanted = ask.filter(k => k !== this.myKey && rosterHas(this.roster, k));\n return { t: \"group-sync\", g: this.id, e: this.epoch, h: commitHash(this.top), have, secrets: this.readableEpochs, mt: groupMetaTag(this.state.meta),\n ...(Object.keys(miss).length ? { miss } : {}), ...(wanted.length ? { ask: wanted } : {}) };\n }\n\n /** Sequence numbers below the highest seen that never arrived, the newest `miss` of them. */\n private gaps(entry: { high: number; window: number[] }): number[] {\n const got = new Set(entry.window), gaps: number[] = [];\n for (let n = entry.high - 1; n >= Math.max(0, entry.high - GROUP_LIMITS.window + 1) && gaps.length < GROUP_LIMITS.miss; n--) if (!got.has(n)) gaps.push(n);\n return gaps;\n }\n\n setNick(key: string, nick: string | undefined): Promise<void> {\n return this.serialize(async () => {\n const clean = sanitizeNick(nick);\n if ((this.state.nicks[key] ?? undefined) === clean) return;\n if (clean) this.state.nicks[key] = clean; else delete this.state.nicks[key];\n await this.persist();\n this.hooks.changed();\n });\n }\n\n /**\n * A frame from an authenticated member over the pairwise edge. Anything malformed or unauthorized is dropped.\n * Resolves to what it took that a hub passes on to its other edges (WISP 9xx · Group Mesh § Hubs): each message,\n * edit, commit (without the secret sealed for me), metadata statement and signed leave, the first time only, so a\n * flood among hubs stops at every member that already has it. Only what its author still in the roster signed.\n */\n handle(from: string, raw: unknown): Promise<GroupEdgeFrame[]> {\n return this.serialize(async () => {\n if (this.state.status !== \"active\" || !raw || typeof raw !== \"object\") return [];\n const frame = raw as Record<string, unknown>;\n if (frame.g !== this.id) return [];\n this.passOn = [];\n try {\n switch (frame.t) {\n case \"group-msg\": await this.receiveMessage(from, raw); break;\n case GROUP_EDIT_FRAME: await this.receiveEdit(from, raw); break;\n case \"group-commit\": await this.receiveCommit(from, raw as GroupCommitFrame); break;\n case \"group-sync\": await this.receiveSync(from, raw as GroupSyncFrame); break;\n case \"group-secrets\": await this.receiveSecrets(raw as GroupSecretsFrame); break;\n case \"group-meta\": await this.receiveMeta(from, raw); break;\n case \"group-bye\": await this.receiveBye(from, raw); break;\n case \"group-leave\": if (this.isAdmin && rosterHas(this.roster, from) && from !== this.myKey) await this.commit(\"remove\", from, Date.now()); break;\n }\n // Removed, the last thing a hub passes on is the commit that says so: it may be the only way the others hear it.\n return this.state.status === \"active\" ? this.passOn : this.state.status === \"removed\" ? this.passOn.filter(f => f.t === \"group-commit\") : [];\n } finally { this.passOn = null; }\n });\n }\n\n /** Taken for the first time: a hub passes it on (see `handle`). */\n private took(frame: GroupEdgeFrame): void { this.passOn?.push(frame); }\n\n /**\n * My leave, signed, for hubs to carry to the admin (see `GroupByeFrame`). Made while I am still a member: `leave`\n * forgets everything but my key.\n */\n byeFrame(now = Date.now()): GroupByeFrame {\n const unsigned = { g: this.id, k: this.myKey, e: this.epoch, ts: now };\n return { t: \"group-bye\", ...unsigned, sig: toBase64Url(sign(byeSigned(unsigned), this.identity.seed)) };\n }\n\n /** My signature on a reaction of mine, beside its wire fields (older apps read only those). */\n signReaction(reaction: WireReaction): { k: string; sig: string } {\n return { k: this.myKey, sig: toBase64Url(sign(reactionSigned(this.id, reaction), this.identity.seed)) };\n }\n\n /**\n * A reaction signed by a member still in the roster (not me), with the member and the reaction, or null: what a hub\n * passes on, and what a member takes from a hub.\n */\n signedReaction(raw: unknown): { member: string; reaction: WireReaction; frame: GroupReactedFrame } | null {\n if (this.state.status !== \"active\" || !raw || typeof raw !== \"object\") return null;\n const f = raw as Record<string, unknown>, reaction = readReaction(f);\n if (!reaction || f.g !== this.id || typeof f.k !== \"string\" || !MEMBER_KEY.test(f.k) || f.k === this.myKey || !rosterHas(this.roster, f.k)) return null;\n if (typeof f.sig !== \"string\" || f.sig.length !== 86 || !B64.test(f.sig)) return null;\n try { if (!verify(fromBase64Url(f.sig), reactionSigned(this.id, reaction), publicKeyFromZ32(f.k))) return null; } catch { return null; }\n return { member: f.k, reaction, frame: { t: GROUP_REACTED_FRAME, g: this.id, k: f.k, ...reaction, sig: f.sig } };\n }\n\n /** My pin, signed, as it goes over every edge. */\n pinFrame(pin: WirePin): GroupPinFrame {\n return { t: GROUP_PIN_FRAME, g: this.id, id: pin.id, n: pin.n, k: this.myKey, sig: toBase64Url(sign(pinSigned(this.id, pin), this.identity.seed)) };\n }\n\n /** A pin signed by a member still in the roster (me too: it may come back through another member), or null. */\n signedPin(raw: unknown): { member: string; pin: WirePin; frame: GroupPinFrame } | null {\n if (this.state.status !== \"active\" || !raw || typeof raw !== \"object\") return null;\n const f = raw as Record<string, unknown>, pin = readPin(f);\n if (!pin || f.g !== this.id || typeof f.k !== \"string\" || !MEMBER_KEY.test(f.k) || !rosterHas(this.roster, f.k)) return null;\n if (typeof f.sig !== \"string\" || f.sig.length !== 86 || !B64.test(f.sig)) return null;\n try { if (!verify(fromBase64Url(f.sig), pinSigned(this.id, pin), publicKeyFromZ32(f.k))) return null; } catch { return null; }\n return { member: f.k, pin, frame: { t: GROUP_PIN_FRAME, g: this.id, id: pin.id, n: pin.n, k: f.k, sig: f.sig } };\n }\n\n /** A signed leave: the admin removes its member; anyone else passes it on while that member is in the roster. */\n private async receiveBye(from: string, raw: unknown): Promise<void> {\n const f = raw as Record<string, unknown>;\n if (typeof f.k !== \"string\" || !MEMBER_KEY.test(f.k) || f.k === this.myKey || !rosterHas(this.roster, f.k) || !rosterHas(this.roster, from) ||\n !Number.isSafeInteger(f.e) || (f.e as number) < 0 || !Number.isSafeInteger(f.ts) || (f.ts as number) <= 0 || typeof f.sig !== \"string\" || f.sig.length !== 86 || !B64.test(f.sig)) return;\n const bye = { t: \"group-bye\" as const, g: this.id, k: f.k, e: f.e as number, ts: f.ts as number, sig: f.sig };\n try { if (!verify(fromBase64Url(bye.sig), byeSigned(bye), publicKeyFromZ32(bye.k))) return; } catch { return; }\n if (this.isAdmin) { await this.commit(\"remove\", bye.k, Date.now()); return; }\n this.took(bye);\n }\n\n /**\n * A message, from its author's edge or handed on by another member. Handed on, it is taken only while both are in\n * the roster (someone removed is neither heard from nor handed on, #300), and its boxes only with the author's `xs`.\n */\n private async receiveMessage(from: string, raw: unknown): Promise<void> {\n if (!isMessageFrame(raw) || raw.s === this.myKey) return;\n const relayed = raw.s !== from;\n if (relayed && (!rosterHas(this.roster, from) || !rosterHas(this.roster, raw.s))) return;\n // Signed by its author before it may wait, so a frame no member wrote never takes the place of one that was.\n if (!verify(fromBase64Url(raw.sig), messageSigned(raw), publicKeyFromZ32(raw.s))) return;\n if (raw.e > this.epoch) { this.park(from, raw); return; }\n // Its replay window is gone (markSeen), even if a sparse set of secrets still holds its secret.\n if (raw.e < this.epoch - GROUP_LIMITS.secrets) return;\n const commit = this.state.chain[raw.e];\n // Not from a member of that epoch, or from before I was one: nothing to read, nothing to ask for.\n if (!commit || !rosterHas(commit.m, raw.s) || !rosterHas(commit.m, this.myKey)) return;\n if (this.isDuplicate(raw)) return;\n // Whole: from its author's own edge, or handed on with the author's signature over every field. Anything else a\n // member handing it on may have stripped (the mentions, the reply, the hop count): its text is shown, but it is\n // neither seen nor kept nor handed on, so a sync still asks for it and a whole copy completes it.\n const id = groupMessageId(raw.s, raw.e, raw.n);\n const whole = !relayed || this.wholeSigned(raw);\n if (!whole) {\n if (this.provisional.has(id)) return;\n delete raw.m; delete raw.r; delete raw.f; delete raw.sc; delete raw.xs;\n }\n const secret = this.secret(raw.e);\n if (!secret) { this.park(from, raw); return; }\n const key = epochKeys(secret, this.id, raw.e).message;\n const text = decryptText(key, messageAad(raw), raw.nn, raw.c);\n if (text === null) return;\n // Everyone is named only by the admin of the message's epoch; a box that does not open is no mentions, not no message.\n const mentions = this.openMentions(key, raw, text, rosterAdmin(commit.m) === raw.s);\n const reply = this.openReply(key, raw);\n const forwarded = readForwarded(raw.f);\n const card = this.openCard(key, raw);\n const completes = whole && this.provisional.delete(id);\n await this.hooks.message({ id, sender: raw.s, epoch: raw.e, seq: raw.n, timestamp: raw.ts, text, ...(mentions.length ? { mentions } : {}), ...(reply ? { reply } : {}), ...(forwarded ? { forwarded } : {}), ...(card ? { card } : {}), ...(completes ? { completes: true as const } : {}) });\n if (!whole) {\n this.provisional.add(id);\n if (this.provisional.size > GROUP_LIMITS.provisional) this.provisional.delete(this.provisional.values().next().value!);\n this.state.provisional = [...this.provisional];\n await this.persist();\n return;\n }\n if (completes) this.state.provisional = [...this.provisional];\n this.markSeen(raw);\n this.keep(clean(raw));\n await this.persist();\n // Passed on only while its author is a member: someone removed is not carried for, even for epochs it was in.\n if (rosterHas(this.roster, raw.s)) this.took(clean(raw));\n }\n\n /**\n * An edit of a member's own message, from its author's edge or handed on by another member: believed only while its\n * author is still in the roster (someone removed edits nothing), and the member handing it on too; for an epoch the\n * author and I were both members of, with the author's signature over the whole frame and a box that opens. One for\n * an epoch or a secret not here yet waits, as a message does. Taken, it is kept to hand on in turn.\n */\n private async receiveEdit(from: string, raw: unknown): Promise<void> {\n if (!isEditFrame(raw) || raw.s === this.myKey || !rosterHas(this.roster, raw.s) || !rosterHas(this.roster, from)) return;\n if (!verify(fromBase64Url(raw.sig), editSigned(raw), publicKeyFromZ32(raw.s))) return;\n if (raw.e > this.epoch) { this.parkEdit(from, raw); return; }\n const commit = this.state.chain[raw.e];\n if (!commit || !rosterHas(commit.m, raw.s) || !rosterHas(commit.m, this.myKey)) return;\n const secret = this.secret(raw.e);\n if (!secret) { if (raw.e >= this.epoch - GROUP_LIMITS.secrets) this.parkEdit(from, raw); return; }\n const plain = decryptText(epochKeys(secret, this.id, raw.e).message, editAad(raw), raw.nn, raw.c);\n if (plain === null) return;\n let body: { text?: unknown; m?: unknown; sc?: unknown };\n try { body = JSON.parse(plain) as typeof body; } catch { return; }\n if (!body || typeof body !== \"object\" || !validEditText(body.text)) return;\n const k = validMentions(body.m, body.text, rosterAdmin(commit.m) === raw.s);\n // A card that does not hold is left out; an edit numbered past a text's bound stands only with one.\n const sc = body.sc === undefined ? undefined : readStatusCard(body.sc);\n if (!validEditNumber(raw.v) && !sc) return;\n await this.hooks.edit?.({ id: groupMessageId(raw.s, raw.e, raw.n), sender: raw.s, e: raw.v, ts: raw.ts, m: body.text, ...(k.length ? { k } : {}), ...(sc ? { sc } : {}) });\n if (this.keepEdit(raw)) { await this.persist(); this.took(cleanEdit(raw)); }\n }\n\n private parkEdit(from: string, frame: GroupEditFrame): void {\n if (frame.e > this.epoch + GROUP_LIMITS.secrets) return;\n this.waitingEdits = [...this.waitingEdits.filter(w => !(w.frame.s === frame.s && w.frame.e === frame.e && w.frame.n === frame.n && w.frame.v <= frame.v)), { from, frame }]\n .slice(-GROUP_LIMITS.outlog);\n this.ask(from);\n }\n\n private wholeSigned(raw: GroupMessageFrame): boolean {\n if (typeof raw.xs !== \"string\" || raw.xs.length !== 86 || !B64.test(raw.xs)) return false;\n try { return verify(fromBase64Url(raw.xs), messageSignedWhole(raw), publicKeyFromZ32(raw.s)); } catch { return false; }\n }\n\n private openMentions(key: Uint8Array, raw: GroupMessageFrame, text: string, everyone: boolean): GroupMention[] {\n if (!isMentionsBox(raw.m)) return [];\n const plain = decryptText(key, mentionsAad(raw), raw.m.n, raw.m.c);\n if (plain === null) return [];\n try { return validMentions(JSON.parse(plain), text, everyone); } catch { return []; }\n }\n\n /** A message's status card; a box that does not open, or a card that does not hold, is no card: the text shows. */\n private openCard(key: Uint8Array, raw: GroupMessageFrame): StatusCard | undefined {\n if (!isCardBox(raw.sc)) return undefined;\n const plain = decryptText(key, cardAad(raw), raw.sc.n, raw.sc.c);\n if (plain === null) return undefined;\n try { return readStatusCard(JSON.parse(plain)); } catch { return undefined; }\n }\n\n /** The message a text answers; a box that does not open, or does not hold one, is no reply, not no message. */\n private openReply(key: Uint8Array, raw: GroupMessageFrame): WireReply | undefined {\n if (!isReplyBox(raw.r)) return undefined;\n const plain = decryptText(key, replyAad(raw), raw.r.n, raw.r.c);\n if (plain === null) return undefined;\n try { return readReply(JSON.parse(plain), groupReplyAuthor); } catch { return undefined; }\n }\n\n private isDuplicate(f: GroupMessageFrame): boolean {\n const entry = this.state.seen[f.s]?.[f.e];\n if (!entry) return false;\n if (f.n > entry.high) return false;\n if (f.n <= entry.high - GROUP_LIMITS.window) return true;\n return f.n === entry.high || entry.window.includes(f.n);\n }\n private markSeen(f: GroupMessageFrame): void {\n const bySender = (this.state.seen[f.s] ??= {});\n const entry = (bySender[f.e] ??= { high: -1, window: [] });\n if (f.n > entry.high) {\n if (entry.high >= 0) entry.window.push(entry.high);\n entry.high = f.n;\n } else entry.window.push(f.n);\n entry.window = entry.window.filter(n => n > entry.high - GROUP_LIMITS.window);\n // Epochs older than what I can read are not worth a replay window.\n for (const e of Object.keys(bySender)) if (Number(e) < this.epoch - GROUP_LIMITS.secrets) delete bySender[e];\n }\n\n private park(from: string, frame: GroupMessageFrame): void {\n if (frame.e > this.epoch + GROUP_LIMITS.secrets) return;\n if (this.waiting.some(w => w.frame.s === frame.s && w.frame.e === frame.e && w.frame.n === frame.n)) return;\n this.waiting.push({ from, frame });\n this.waitingBytes += frame.c.length;\n while (this.waiting.length > GROUP_LIMITS.waiting || this.waitingBytes > GROUP_LIMITS.waitingBytes) this.waitingBytes -= this.waiting.shift()!.frame.c.length;\n this.ask(from);\n }\n\n /** Whoever sent something I cannot read yet is ahead of me, or holds a secret I lack: ask, but not in a loop. */\n private ask(from: string): void {\n const now = Date.now();\n if (now - (this.asked.get(from) ?? 0) < 10_000) return;\n this.asked.set(from, now);\n this.hooks.send(from, this.syncFrame());\n }\n\n private async replayWaiting(): Promise<void> {\n await this.metaFollowsChain();\n const ready = this.waiting.filter(w => w.frame.e <= this.epoch && !!this.state.secrets[w.frame.e]);\n this.waiting = this.waiting.filter(w => !ready.includes(w));\n this.waitingBytes = this.waiting.reduce((sum, w) => sum + w.frame.c.length, 0);\n for (const { from, frame } of ready) await this.receiveMessage(from, frame);\n // Edits after the messages they change.\n const edits = this.waitingEdits.filter(w => w.frame.e <= this.epoch && !!this.state.secrets[w.frame.e]);\n this.waitingEdits = this.waitingEdits.filter(w => !edits.includes(w));\n for (const { from, frame } of edits) await this.receiveEdit(from, frame);\n }\n\n private async receiveCommit(from: string, frame: GroupCommitFrame): Promise<void> {\n const raw = frame.commit as unknown as Record<string, unknown> | undefined;\n if (!raw || typeof raw !== \"object\" || !Number.isSafeInteger(raw.e)) return;\n const e = raw.e as number;\n if (e <= this.epoch) {\n // Known, or a different history of the same epoch: that is a fork, and nothing here picks a winner.\n const known = this.state.chain[e];\n const result = verifyCommit(raw, this.state.chain[e - 1] ?? null, this.id);\n // Evidence of a fork must come from whoever signed my commit for that epoch; at epoch 0 anyone can sign a genesis.\n if (\"error\" in result || result.commit.by !== known.by) return;\n if (commitHash(result.commit) !== commitHash(known)) await this.fork(`Member ${from.slice(0, 8)} holds a different membership history for epoch ${e}`);\n return;\n }\n if (e > this.epoch + 1) {\n // Kept to wait for the ones before it only when signed, by a member now: its place is not for anyone's junk.\n const signed = verifyCommitSignature(raw);\n if (signed && rosterHas(this.roster, signed.by) && this.pendingCommits.size < GROUP_LIMITS.pendingCommits) this.pendingCommits.set(e, frame);\n this.ask(from);\n return;\n }\n const result = verifyCommit(raw, this.top, this.id);\n if (\"error\" in result) {\n // The admin's valid signature on a commit that does not follow my chain is evidence of a fork; noise is just dropped.\n if (result.error.startsWith(\"Commit does not follow\") && verifyCommitSignature(raw)?.by === this.admin) await this.fork(`Member ${from.slice(0, 8)} holds a different membership history for epoch ${e}`);\n return;\n }\n await this.apply(result.commit, frame.secret);\n this.took({ t: \"group-commit\", g: this.id, commit: result.commit });\n // Handed on by a hub, the commit carries no secret sealed for me: whoever handed it on holds it, and seals it to me.\n if (this.state.status === \"active\" && !this.state.secrets[this.epoch]) this.ask(from);\n const next = this.pendingCommits.get(this.epoch + 1);\n if (next) { this.pendingCommits.delete(this.epoch + 1); await this.receiveCommit(from, next); }\n }\n\n private async apply(commit: GroupCommit, sealed?: SealedSecret): Promise<void> {\n this.state.chain.push(commit);\n this.state.seq = 0; this.state.seqEpoch = commit.e;\n if (!rosterHas(commit.m, this.myKey)) {\n this.out(\"removed\", \"You were removed from this group\");\n await this.persist();\n this.hooks.changed();\n return;\n }\n if (sealed) this.takeSecret(commit.e, sealed);\n this.pruneSecrets();\n this.pruneRelay();\n await this.persist();\n this.hooks.changed();\n await this.replayWaiting();\n }\n\n /** A sealed secret for an epoch of my chain: kept only when it confirms against that epoch's commit. */\n private takeSecret(epoch: number, sealed: unknown): boolean {\n const commit = this.state.chain[epoch];\n if (!commit || this.state.secrets[epoch] || !isSealed(sealed) || !rosterHas(commit.m, this.myKey)) return false;\n if (epoch < this.epoch - GROUP_LIMITS.secrets) return false;\n const secret = openSecret(this.identity.seed, this.myKey, sealed, secretAad(this.id, epoch, this.myKey));\n if (!secret || !confirmationMatches(epochKeys(secret, this.id, epoch).confirm, commitUntaggedHash(commit), commit.c)) return false;\n this.state.secrets[epoch] = toBase64Url(secret);\n return true;\n }\n\n private async receiveSecrets(frame: GroupSecretsFrame): Promise<void> {\n if (!Array.isArray(frame.secrets) || frame.secrets.length > GROUP_LIMITS.secrets) return;\n let taken = false;\n for (const entry of frame.secrets) {\n if (!entry || typeof entry !== \"object\" || !Number.isSafeInteger(entry.e) || entry.e < 0 || entry.e > this.epoch) continue;\n taken = this.takeSecret(entry.e, entry.s) || taken;\n }\n if (!taken) return;\n await this.persist();\n this.hooks.changed();\n await this.replayWaiting();\n }\n\n private async receiveSync(from: string, frame: GroupSyncFrame): Promise<void> {\n if (!rosterHas(this.roster, from) || !Number.isSafeInteger(frame.e) || frame.e < 0 || typeof frame.h !== \"string\") return;\n // Each answer may carry the chain, secrets and logs: a few a minute per member, however often it asks.\n let answered = this.syncsAnswered.get(from);\n if (!answered) this.syncsAnswered.set(from, answered = new RateWindow(GROUP_LIMITS.syncAnswers, GROUP_LIMITS.syncWindowMs, () => this.hooks.clock?.() ?? Date.now()));\n if (!answered.take()) return;\n if (frame.e > this.epoch) {\n this.ask(from);\n this.offerMeta(from, frame.mt);\n // Behind on the chain, I still hold what they lack: my own messages, sent before I heard of their commits (a\n // joiner's first words, sent before any edge opened, while the admin admitted the next one). Nobody asks again\n // once I have caught up, so they go now. Only mine, and only for epochs my chain says they were in: whom to\n // hand on for is the newer roster's to say, which I do not have yet.\n this.handOwn(from, frame);\n return;\n }\n if (frame.h !== commitHash(this.state.chain[frame.e])) {\n // A claim is not a fork: they get my commit for that epoch, and fork on it if their own is validly signed and different.\n this.hooks.send(from, { t: \"group-commit\", g: this.id, commit: this.state.chain[frame.e] });\n return;\n }\n // Commits they lack, each with its secret sealed for them when they were in that roster and I still hold it.\n for (let e = frame.e + 1; e <= this.epoch; e++) {\n const commit = this.state.chain[e], secret = this.secret(e);\n this.hooks.send(from, { t: \"group-commit\", g: this.id, commit, ...(secret && rosterHas(commit.m, from) ? { secret: sealSecret(from, secret, secretAad(this.id, e, from)) } : {}) });\n }\n // Secrets of epochs they were in but do not hold, sixteen to a frame (what apps from before revision 0.9 take).\n const theirs = new Set(Array.isArray(frame.secrets) ? frame.secrets.filter(n => Number.isSafeInteger(n)) : []);\n const secrets = this.readableEpochs.filter(e => e <= frame.e && !theirs.has(e) && rosterHas(this.state.chain[e].m, from))\n .map(e => ({ e, s: sealSecret(from, this.secret(e)!, secretAad(this.id, e, from)) }));\n for (let i = 0; i < secrets.length; i += GROUP_LIMITS.secretsPerFrame) this.hooks.send(from, { t: \"group-secrets\", g: this.id, secrets: secrets.slice(i, i + GROUP_LIMITS.secretsPerFrame) });\n // Messages they have not seen, for epochs they were in: my own, from my bounded log, and those of the members they\n // asked me for (whose edges to them are down), from what I received. Signed by their authors, so nothing to trust me for.\n const lacks = missingIn(frame, from);\n const asked = new Set(Array.isArray(frame.ask) ? frame.ask.filter(k => typeof k === \"string\" && k !== from && k !== this.myKey && rosterHas(this.roster, k)) : []);\n const kept = asked.size ? (this.state.relay ?? []).filter(f => asked.has(f.s) && this.state.chain[f.e] && rosterHas(this.state.chain[f.e].m, from) && lacks(f)) : [];\n // In the order they were written, as far as two logs can say: the receiver places each where it arrives, and my own\n // first then the others' showed it a conversation as all my lines, then all of theirs.\n for (const next of asWritten(this.ownLacked(from, frame), kept)) this.hooks.send(from, next);\n // Their latest edits too, after the messages they change (a sync says nothing of edits: one they have changes nothing).\n for (const author of asked) {\n const edits = (this.state.relayEdits ?? []).filter(f => f.s === author && this.state.chain[f.e] && rosterHas(this.state.chain[f.e].m, from));\n for (const edit of edits.slice(-GROUP_LIMITS.handOnEdits)) this.hooks.send(from, edit);\n }\n // The group's picture, when theirs is older (after the commits and secrets above, which it may need).\n this.offerMeta(from, frame.mt);\n }\n\n /** My own messages a member's sync says it lacks, from my bounded log, for the epochs it was a member of. */\n private handOwn(to: string, frame: GroupSyncFrame): void {\n for (const sent of this.ownLacked(to, frame)) this.hooks.send(to, sent);\n }\n private ownLacked(to: string, frame: GroupSyncFrame): GroupMessageFrame[] {\n const lacks = missingIn(frame, to);\n return this.state.sent.filter(sent => this.state.chain[sent.e] && rosterHas(this.state.chain[sent.e].m, to) && lacks(sent));\n }\n\n // -- metadata (WISP 9xx § Metadata) ---------------------------------------\n\n /** Sets (or, with null, removes) the group's picture: only the admin, signed under the current commit. */\n setPicture(picture: string | null, now = Date.now()): Promise<void> {\n return this.serialize(async () => {\n if (this.state.status !== \"active\") throw new Error(\"You are no longer in this group\");\n if (!this.isAdmin) throw new Error(\"Only the admin can change the group's picture\");\n await this.publishMeta(encodeGroupMetaBody({ ...groupMetaBody(this.state.meta), pic: picture ?? undefined }), now);\n });\n }\n\n /** Renames the group: only the admin, signed under the current commit, beside the picture and the hubs. */\n rename(name: string, now = Date.now()): Promise<void> {\n return this.serialize(async () => {\n if (this.state.status !== \"active\") throw new Error(\"You are no longer in this group\");\n if (!this.isAdmin) throw new Error(\"Only the admin can rename the group\");\n const body = encodeGroupMetaBody({ ...groupMetaBody(this.state.meta), name });\n if (body !== this.state.meta?.body) await this.publishMeta(body, now);\n });\n }\n\n /**\n * Pins a member as a hub, excludes one from being a hub, or leaves it to the member's app (`null`): only the admin,\n * in the group's metadata beside the picture (WISP 9xx · Group Mesh § Hubs).\n */\n setHub(key: string, role: \"pin\" | \"exclude\" | null, now = Date.now()): Promise<void> {\n return this.serialize(async () => {\n if (this.state.status !== \"active\") throw new Error(\"You are no longer in this group\");\n if (!this.isAdmin) throw new Error(\"Only the admin can choose the group's hubs\");\n if (!rosterHas(this.roster, key)) throw new Error(\"Not a member\");\n const current = this.hubPolicy;\n const policy = { pin: current.pin.filter(k => k !== key && rosterHas(this.roster, k)), no: current.no.filter(k => k !== key && rosterHas(this.roster, k)) };\n if (role === \"pin\") policy.pin.push(key);\n if (role === \"exclude\") policy.no.push(key);\n if (policy.pin.length > MESH_HUBS.pinned) throw new Error(`At most ${MESH_HUBS.pinned} members can be pinned as hubs`);\n await this.publishMeta(encodeGroupMetaBody({ ...groupMetaBody(this.state.meta), hubs: policy }), now);\n });\n }\n\n /** Signs a body under my current commit, keeps it and sends it to every member. Inside `serialize`. */\n private async publishMeta(body: string, now: number): Promise<void> {\n const before = this.state.meta;\n const meta = signGroupMeta({ g: this.id, e: this.epoch, h: commitHash(this.top), r: nextGroupMetaRevision(before, this.epoch), ts: now }, body, this.identity.seed, this.myKey);\n this.state.meta = meta;\n await this.persist();\n const secret = this.secret(this.epoch);\n if (secret) {\n const frame = wrapGroupMeta(meta, this.epoch, epochKeys(secret, this.id, this.epoch).message);\n for (const key of this.others) this.hooks.send(key, frame);\n }\n const change = groupMetaChange(before, meta, this.state.name);\n if (change) this.hooks.metaChanged?.(this.myKey, change, meta.ts);\n this.hooks.changed();\n }\n\n /** My statement, to a member whose sync says it holds an older one (or none); nothing to an app that says nothing. */\n private offerMeta(to: string, theirTag: unknown): void {\n const meta = this.state.meta, theirs = parseGroupMetaTag(theirTag);\n if (!meta || theirs === null || !groupMetaNewer(meta, theirs) || !rosterHas(this.roster, to)) return;\n const secret = this.secret(this.epoch);\n if (secret) this.hooks.send(to, wrapGroupMeta(meta, this.epoch, epochKeys(secret, this.id, this.epoch).message));\n }\n\n /**\n * A metadata statement: kept when it is newer than mine, its commit is on my chain, its signer was\n * the admin of that commit and is the admin now, its signature holds, and it opens under the epoch\n * it names into metadata Ghostly shows. One naming what I do not have yet waits for the chain.\n */\n private async receiveMeta(from: string, raw: unknown): Promise<void> {\n const frame = parseGroupMetaFrame(raw);\n if (!frame || frame.statement.g !== this.id || typeof frame.k !== \"number\" || !rosterHas(this.roster, from)) return;\n const s = frame.statement;\n if (!groupMetaNewer(s, this.state.meta)) return;\n if (s.e > this.epoch || frame.k > this.epoch) { this.pendingMeta = { from, frame: raw }; this.ask(from); return; }\n const commit = this.state.chain[s.e];\n if (commitHash(commit) !== s.h || rosterAdmin(commit.m) !== s.by || this.admin !== s.by || !verifyGroupMetaSignature(s)) return;\n const secret = this.secret(frame.k);\n if (!secret) { this.pendingMeta = { from, frame: raw }; this.ask(from); return; }\n const opened = openGroupMeta(frame, epochKeys(secret, this.id, frame.k).message);\n if (!opened) return;\n const before = this.state.meta;\n this.state.meta = opened.meta;\n await this.persist();\n // A change of hubs alone is no line in the history, nor what the group looked like when I got in: the first\n // statement I take, signed under a commit before mine, is no change made while I was a member.\n const change = !before && !rosterHas(commit.m, this.myKey) ? null : groupMetaChange(before, opened.meta, this.state.name);\n if (change) this.hooks.metaChanged?.(s.by, change, opened.meta.ts);\n this.hooks.changed();\n this.took({ t: \"group-meta\", ...s, k: frame.k as number, nn: frame.nn, c: frame.c });\n }\n\n /** After the chain or my secrets moved: the waiting statement, and, if I became the admin, the name and picture signed again as mine. */\n private async metaFollowsChain(): Promise<void> {\n const pending = this.pendingMeta;\n this.pendingMeta = undefined;\n if (pending) await this.receiveMeta(pending.from, pending.frame);\n // Joiners accept only the current admin's statement: a new admin signs the picture again.\n if (this.isAdmin && this.state.meta && this.state.meta.by !== this.myKey) await this.publishMeta(this.state.meta.body, Date.now());\n }\n\n private async fork(reason: string): Promise<void> {\n if (this.state.status !== \"active\") return;\n this.state.status = \"forked\";\n this.state.statusReason = `${reason}. Membership changes are halted; the admin must re-form the group.`;\n await this.persist();\n this.hooks.changed();\n }\n}\n","import { MENTION_EVERYONE, validMentions, wireMentions, type GroupMention } from \"./groupMentions\";\nimport { WAKE_INTERVAL_MS, parseWakeFrame, wakeFrame, type WakeTarget } from \"./pairedWake\";\n\n/**\n * Wake-up push in a private group (WISP 9xx · Group Mesh § Wake-up push): a member whose web app is closed is woken\n * when a message names it. `wake/1` as a 1:1 chat has it (WISP 401 § Wake-up push), carried on the group's edges: each\n * member shares its subscription with each other member on their own edge, under a token of that edge alone, and\n * only in a group it has not muted:\n *\n * {\"t\":\"group-wake\",\"g\":\"<group id>\",\"w\":{\"e\":…,\"p\":…,\"a\":…,\"vp\":…,\"vk\":…,\"k\":\"<token>\"}}\n * {\"t\":\"group-wake\",\"g\":\"<group id>\",\"w\":null} (muted, or stopped: forget it)\n *\n * The author of a message wakes the members it names by key (never `*`, everyone), and only those it cannot reach now.\n * The push is the 1:1 one, `{\"wake\":1,\"k\":\"<token>\"}`: no text, no group, no author. A community has no edge between\n * two members, so it has none of this (§ Wake-up push, not yet).\n */\n\nexport const GROUP_WAKE_FRAME = \"group-wake\";\n\n/** At most one wake-up to a member in this long, whatever group names it: a mention storm is one push. */\nexport const GROUP_WAKE_MEMBER_INTERVAL_MS = WAKE_INTERVAL_MS;\n/** At most this many wake-ups from one group in `GROUP_WAKE_GROUP_WINDOW_MS`: a message naming 16 wakes four. */\nexport const GROUP_WAKE_GROUP_LIMIT = 4;\nexport const GROUP_WAKE_GROUP_WINDOW_MS = 60_000;\n/** Frames a member reads on one edge in a minute: it changes when the other member rotates or mutes. */\nexport const GROUP_WAKE_RECEIVE_LIMIT = 6;\n\nexport type GroupWakeFrame = { t: typeof GROUP_WAKE_FRAME; g: string; w: ReturnType<typeof wakeFrame>[\"w\"] };\n\nexport function groupWakeFrame(groupId: string, target: WakeTarget | null): GroupWakeFrame {\n return { t: GROUP_WAKE_FRAME, g: groupId, w: wakeFrame(target).w };\n}\n\n/** What a `group-wake` frame says for this group: a target, null (forget it), or undefined (malformed, or another group's). */\nexport function parseGroupWakeFrame(frame: Record<string, unknown>, groupId: string): WakeTarget | null | undefined {\n if (frame.t !== GROUP_WAKE_FRAME || frame.g !== groupId) return undefined;\n return parseWakeFrame(frame);\n}\n\n/**\n * The members a message wakes, as its author sends it: those it names by key (the mentions that hold on the trimmed\n * text), each once, in order. Never `*`: everyone is a group's whole roster, and a wake-up is for one person.\n */\nexport function mentionedMembers(mentions: readonly GroupMention[] | undefined, text: string): string[] {\n const named = validMentions(wireMentions(mentions ?? []), text.trim(), false);\n return [...new Set(named.map(m => m.k).filter(k => k !== MENTION_EVERYONE))];\n}\n\n/**\n * The limits of group wake-ups on the author's side: one per member per `GROUP_WAKE_MEMBER_INTERVAL_MS` (across\n * groups), and `GROUP_WAKE_GROUP_LIMIT` per group per `GROUP_WAKE_GROUP_WINDOW_MS`. In memory, as the 1:1 limiter.\n */\nexport class GroupWakeLimiter {\n private readonly members = new Map<string, number>();\n private readonly groups = new Map<string, number[]>();\n constructor(\n private readonly memberInterval = GROUP_WAKE_MEMBER_INTERVAL_MS,\n private readonly groupLimit = GROUP_WAKE_GROUP_LIMIT,\n private readonly groupWindow = GROUP_WAKE_GROUP_WINDOW_MS,\n ) {}\n /** Whether a wake-up to `member` for `group` may go now; if so, it counts against both. */\n take(group: string, member: string, now = Date.now()): boolean {\n const before = this.members.get(member);\n if (before !== undefined && now - before < this.memberInterval) return false;\n const recent = (this.groups.get(group) ?? []).filter(at => now - at < this.groupWindow);\n if (recent.length >= this.groupLimit) { this.groups.set(group, recent); return false; }\n this.members.set(member, now);\n this.groups.set(group, [...recent, now]);\n return true;\n }\n /** The member is reachable again: the next time it is away, a mention may wake it at once. */\n reset(member: string): void {\n this.members.delete(member);\n }\n}\n\n/**\n * Who the author of a message wakes: the members it names, that shared how, that it cannot reach now, within the\n * limits. `target` answers a member's shared target (its edge's), `reachable` whether it is live (its edge, or a hub).\n */\nexport function groupWakes<T>(options: {\n group: string; mentions: readonly GroupMention[] | undefined; text: string;\n target: (member: string) => T | undefined; reachable: (member: string) => boolean;\n limiter: GroupWakeLimiter; now?: number;\n}): { member: string; target: T }[] {\n const wakes: { member: string; target: T }[] = [];\n for (const member of mentionedMembers(options.mentions, options.text)) {\n const target = options.target(member);\n if (target === undefined || options.reachable(member)) continue;\n if (!options.limiter.take(options.group, member, options.now)) continue;\n wakes.push({ member, target });\n }\n return wakes;\n}\n","import { xchacha20poly1305 } from \"@noble/ciphers/chacha.js\";\nimport { hkdf } from \"@noble/hashes/hkdf.js\";\nimport { sha256 } from \"@noble/hashes/sha2.js\";\nimport { concatBytes, fromBase64Url, randomBytes, toBase64Url, utf8Decode, utf8Encode } from \"./bytes\";\nimport { edgeParams } from \"./groupCrypto\";\nimport { GROUP_ID, MEMBER_KEY } from \"./groupCommits\";\nimport { identityFromSeed, publicKeyFromZ32, type Identity } from \"./identity\";\nimport type { LinkParams } from \"./invite\";\nimport type { GhostRecord } from \"./pkarr\";\n\n/**\n * A group's public entry link (`group-entry/1`, WISP 9xx § Entry link): one\n * address the admin can hand to anyone, so people who are not their contacts\n * can join, without a contact chat and without saying who they are.\n *\n * The link names the group and an **entry key** of the admin's, made for the\n * link and nothing else. From those two, everyone holding the link derives\n * the same **knock identity**: a Pkarr key whose seed is known to every\n * holder, where a joiner leaves its fresh member key. The admin's app reads\n * it; for every knock it opens an **entry session**, a paired link derived like\n * a group edge from the entry key and the joiner's member key and pinned to\n * both, and runs the ordinary admission over it (invite, accept, welcome).\n * The joiner's trust anchor is the entry key the link named: whoever holds\n * its seed is the admin who must admit it.\n *\n * A link is a bearer capability. Anyone who has it can join while it is on;\n * the admin turns it off, or replaces it, and the old one stops working.\n */\nexport const GROUP_ENTRY_PROFILE = \"group-entry/1\" as const;\nconst PREFIX = \"group1\";\n/** A knock older than this is not answered: the joiner republishes while it waits. */\nexport const KNOCK_TTL_MS = 3 * 60_000;\n/** Knocks a record keeps: joiners share one Pkarr packet, so it holds the latest few. */\nexport const MAX_KNOCKS = 6;\nconst KNOCK_LABEL = \"_knock\";\nconst NONCE_LENGTH = 24;\n\nexport interface GroupEntryLink {\n /** The group id. */\n g: string;\n /** The admin's entry key (z-base-32 Ed25519), made for this link. */\n host: string;\n}\n\nexport interface Knock {\n /** The joiner's member key for this group. */\n key: string;\n /** When the joiner last said it is waiting (ms). */\n ts: number;\n}\n\nconst info = (label: string) => utf8Encode(`ghostly-group-entry/1 ${label}`);\n\n/** `group1/<group id>/<entry key>` */\nexport function encodeGroupEntryLink(link: GroupEntryLink): string {\n return `${PREFIX}/${link.g}/${link.host}`;\n}\n\n/** Where the link opens the web app: its join route. */\nexport function groupEntryUrl(origin: string, link: GroupEntryLink): string {\n return `${origin.replace(/\\/+$/, \"\")}/#/join/${encodeGroupEntryLink(link)}`;\n}\n\n/** A pasted link, a `/join/…` path or the bare code; null when it is none of them. */\nexport function decodeGroupEntryLink(input: string): GroupEntryLink | null {\n const clean = input.trim().replace(/^.*#/, \"\").replace(/^.*?\\/join\\//, \"\").replace(/^\\/+/, \"\");\n const parts = clean.split(\"/\");\n if (parts.length !== 3 || parts[0] !== PREFIX) return null;\n const [, g, host] = parts;\n if (!GROUP_ID.test(g) || !MEMBER_KEY.test(host)) return null;\n try { publicKeyFromZ32(host); } catch { return null; }\n return { g, host };\n}\n\nfunction knockMaterial(link: GroupEntryLink, label: string): Uint8Array {\n return hkdf(sha256, publicKeyFromZ32(link.host), utf8Encode(link.g), info(label), 32);\n}\n\n/** The Pkarr identity every holder of the link can publish knocks under. */\nexport function knockIdentity(link: GroupEntryLink): Identity {\n return identityFromSeed(knockMaterial(link, \"knock seed\"));\n}\n\n/**\n * The knocks under the knock identity, sealed with a key from the link: a\n * relay sees an opaque value on a key it cannot tie to the group.\n */\nexport function knockRecords(link: GroupEntryLink, knocks: Knock[]): GhostRecord[] {\n const nonce = randomBytes(NONCE_LENGTH);\n const body = utf8Encode(JSON.stringify(knocks.slice(-MAX_KNOCKS).map(k => [k.key, k.ts])));\n const box = xchacha20poly1305(knockMaterial(link, \"knock key\"), nonce, utf8Encode(link.g)).encrypt(body);\n return [{ label: KNOCK_LABEL, value: toBase64Url(concatBytes(nonce, box)) }];\n}\n\n/** Knocks that open with the link's key and are well formed; anything else is nobody. */\nexport function readKnocks(link: GroupEntryLink, records: GhostRecord[]): Knock[] {\n const value = records.find(r => r.label === KNOCK_LABEL)?.value;\n if (!value || value.length > 900) return [];\n try {\n const bytes = fromBase64Url(value);\n const body = xchacha20poly1305(knockMaterial(link, \"knock key\"), bytes.slice(0, NONCE_LENGTH), utf8Encode(link.g)).decrypt(bytes.slice(NONCE_LENGTH));\n const parsed: unknown = JSON.parse(utf8Decode(body));\n if (!Array.isArray(parsed)) return [];\n return parsed.slice(0, MAX_KNOCKS).flatMap(entry => Array.isArray(entry) && typeof entry[0] === \"string\" && MEMBER_KEY.test(entry[0]) && Number.isSafeInteger(entry[1])\n ? [{ key: entry[0], ts: entry[1] as number }] : []);\n } catch { return []; }\n}\n\n/** The knocks to publish: mine refreshed, others' still fresh, newest last, at most `MAX_KNOCKS`. */\nexport function mergeKnocks(existing: Knock[], mine: Knock, now = Date.now()): Knock[] {\n const fresh = existing.filter(k => k.key !== mine.key && now - k.ts < KNOCK_TTL_MS && k.ts <= now + 60_000);\n return [...fresh.sort((a, b) => a.ts - b.ts).slice(-(MAX_KNOCKS - 1)), mine];\n}\n\n/**\n * The entry session between the admin's entry key and a joiner's member key:\n * the same derivation as a group edge, under a salt of its own, so it can\n * never be confused with an edge of the same group.\n */\nexport function entryParams(link: GroupEntryLink, mySeed: Uint8Array, myZ32: string, peerZ32: string): LinkParams {\n return edgeParams(`entry/${link.g}`, mySeed, myZ32, peerZ32);\n}\n","import { hkdf } from \"@noble/hashes/hkdf.js\";\nimport { sha256 } from \"@noble/hashes/sha2.js\";\nimport { concatBytes, fromBase64Url, randomBytes, toBase64Url, utf8Decode, utf8Encode } from \"./bytes\";\nimport { identityFromSeedB64, publicKeyFromZ32, sign, verify } from \"./identity\";\nimport { sanitizeNick } from \"./text\";\nimport {\n confirmationMatches, confirmationTag, decryptText, encryptText, epochKeys, newEpochSecret, openPair, openSecret, sealPair, sealSecret, sha256Hex,\n type SealedSecret,\n} from \"./groupCrypto\";\nimport { GROUP_ID, MEMBER_KEY, rosterAdmin, rosterHas, sortRoster, type GroupRole, type Roster } from \"./groupCommits\";\nimport { mentionsBytes, validMentions, wireMentions, type GroupMention } from \"./groupMentions\";\nimport { groupReplyAuthor, readReply, wireReply, type WireReply } from \"./replies\";\nimport { readForwarded } from \"./forwards\";\nimport { cardEditNumber, readStatusCard, statusCardBytes, type StatusCard } from \"./statusCards\";\nimport { communityEditFrame, communityMessageAuthor, validEditText } from \"./groupEdits\";\nimport { RateWindow, validEditNumber } from \"./pairedEdits\";\nimport {\n encodeGroupMetaBody, groupDisplayName, groupMetaBody, groupMetaChange, groupMetaNewer, groupMetaPicture, nextGroupMetaRevision, groupMetaTag, groupName, openGroupMeta, parseGroupMetaFrame, parseGroupMetaTag, signGroupMeta, verifyGroupMetaSignature, wrapGroupMeta,\n type GroupMeta, type GroupMetaChange, type GroupMetaFrame,\n} from \"./groupMeta\";\n\n/**\n * `group-community/1` (WISP 9xx · Group Community): a group whose link is the way in, for hundreds\n * of members, where the admin need not be online for anyone to join.\n *\n * What differs from `group-mesh/1`:\n * - any member may commit `add` (admission) and `leave` (on the leaver's signed request); the admin\n * keeps `remove`, `role`, `rotate` and `link`;\n * - a commit carries the hash of the roster after it, not the roster, and members replay the chain;\n * - `add`, `role` and `link` derive the next epoch's secret from the previous one and the commit, so\n * only a newcomer is sent it; `leave`, `remove` and `rotate` get a fresh secret sealed to everyone;\n * - concurrent commits are a race with a deterministic winner, not a fork: the branch that keeps the\n * admin's changes (remove, role, rotate, link) since the common parent, then the longest, then the\n * lowest hash of the first commit after the common parent. The admin signing changes on two\n * branches (after the same commit or not) is still a fork and halts the group;\n * - messages name the commit of their epoch, so messages under a commit that lost stay readable;\n * every member keeps recent frames from everyone and re-sends them to whoever was away.\n *\n * Transport-agnostic, like `GroupSession`: frames go out through the hooks and come in through\n * `handle(from, frame)`, where `from` is the member key of the edge the frame arrived on (which\n * may be a hub relaying someone else's frame: everything relayed is signed by its author).\n */\nexport const COMMUNITY_PROFILE = \"group-community/1\" as const;\n\nexport const COMMUNITY_LIMITS = {\n members: 256,\n chain: 2048,\n textBytes: 16 * 1024,\n /**\n * An application frame the group carries (`x`: a payment note, a request to the group) or one member's\n * payload for another (`p`: a payment between the two), as JSON. Room for a Cashu token of many proofs,\n * sealed twice (to the member, then to the epoch) within a frame.\n */\n appBytes: 8 * 1024,\n /** Frames from everyone kept for whoever was away. */\n store: 256,\n storeBytes: 1024 * 1024,\n /** Epoch secrets kept (by commit), main branch and losing ones together. */\n secrets: 96,\n /** How far back a competing branch may start; older ones are refused and their authors commit again. */\n window: 64,\n /** Commits known off the main branch. */\n side: 512,\n /** Commits waiting for their parent. */\n pendingCommits: 64,\n waiting: 64,\n waitingBytes: 1024 * 1024,\n replay: 256,\n /** Commits in one welcome or chain piece. */\n chainPiece: 100,\n pendingLeaves: 64,\n /**\n * Syncs of one member answered in `syncWindowMs`: each answer may carry the chain's tail, secrets and the whole\n * store. A member catching up asks every five seconds at most, and every member syncs every thirty; one asking more\n * waits for the window.\n */\n syncAnswers: 16,\n syncWindowMs: 60_000,\n} as const;\n\n/** What every member should know about who can read what, and what the link does, in the words the apps show. */\nexport const GROUP_READ_NOTE_COMMUNITY = `Anyone with the group's link can join, and any member can let people in. Everyone in the group can read everything sent while they are a member; someone removed cannot read what comes after, someone who joins later cannot read what came before. Members who were away get the last ${COMMUNITY_LIMITS.store} messages from whoever is there when they return.`;\n\n/** A community group's link: `group2/<group id>/<entry key>` (web: `#/join/group2/…`). */\nexport function encodeCommunityLink(link: { g: string; host: string }): string { return `group2/${link.g}/${link.host}`; }\n/** A pasted link, a `/join/…` path or the bare code; null when it is not a community group's link. */\nexport function decodeCommunityLink(input: string): { g: string; host: string } | null {\n const clean = input.trim().replace(/^.*#/, \"\").replace(/^.*?\\/join\\//, \"\").replace(/^\\/+/, \"\");\n const parts = clean.split(\"/\");\n if (parts.length !== 3 || parts[0] !== \"group2\" || !GROUP_ID.test(parts[1]) || !MEMBER_KEY.test(parts[2])) return null;\n try { publicKeyFromZ32(parts[2]); return { g: parts[1], host: parts[2] }; } catch { return null; }\n}\n\nexport type CommunityKind = \"create\" | \"add\" | \"leave\" | \"remove\" | \"role\" | \"rotate\" | \"link\";\nconst KINDS: CommunityKind[] = [\"create\", \"add\", \"leave\", \"remove\", \"role\", \"rotate\", \"link\"];\n/** Kinds whose epoch secret is fresh: they take someone out, or start or re-key the group. */\nconst FRESH: ReadonlySet<CommunityKind> = new Set([\"create\", \"leave\", \"remove\", \"rotate\"]);\n/**\n * Kinds only the admin commits. They are final: a branch that drops one never replaces the branch\n * that has it, however long, so no member can undo a removal, a hand-over, a rotation or a link change\n * by outgrowing it with admissions.\n */\nconst ADMIN_ONLY: ReadonlySet<CommunityKind> = new Set([\"remove\", \"role\", \"rotate\", \"link\"]);\n\nexport interface CommunityCommit {\n v: 2;\n g: string;\n e: number;\n /** Hash of the parent commit; empty for the genesis. */\n p: string;\n k: CommunityKind;\n /** SHA-256 (hex) of the canonical roster after this commit. */\n m: string;\n by: string;\n s?: string;\n /** Entry key (`create`, `link`); empty in a `link` turns the link off. */\n x?: string;\n /** Genesis nonce: the group id derives from it and the creator. */\n n?: string;\n /** The leaver's signature, in a `leave`. */\n ls?: string;\n ts: number;\n c: string;\n sig: string;\n}\n\nconst HASH = /^[a-f0-9]{64}$/;\nconst TAG = /^[A-Za-z0-9_-]{43}$/;\nconst SIG = /^[A-Za-z0-9_-]{86}$/;\nconst NONCE = /^[A-Za-z0-9_-]{22}$/;\nconst B64 = /^[A-Za-z0-9_-]*$/;\n\nconst tuple = (c: Omit<CommunityCommit, \"sig\">, tag = c.c) =>\n JSON.stringify([2, c.g, c.e, c.p, c.k, c.m, c.by, c.s ?? \"\", c.x ?? \"\", c.n ?? \"\", c.ls ?? \"\", c.ts, tag]);\nexport function communityCommitHash(c: Omit<CommunityCommit, \"sig\">): string { return sha256Hex(utf8Encode(tuple(c))); }\nexport function communityUntaggedHash(c: Omit<CommunityCommit, \"sig\" | \"c\">): string { return sha256Hex(utf8Encode(tuple({ ...c, c: \"\" }, \"\"))); }\nexport const shortHash = (hash: string) => hash.slice(0, 16);\nexport function rosterHash(roster: Roster): string { return sha256Hex(utf8Encode(JSON.stringify(roster))); }\n\n/** The id of a community group: from its creator and a nonce, so no other genesis can claim it. */\nexport function communityGroupId(creator: string, nonce: string): string {\n return toBase64Url(sha256(concatBytes(utf8Encode(\"ghostly-group-community/1 id\"), publicKeyFromZ32(creator), fromBase64Url(nonce))).slice(0, 16));\n}\n\nexport const leaveStatement = (g: string, s: string) => utf8Encode(JSON.stringify([\"ghostly-group/2 leave\", g, s]));\n\n/** The secret of an epoch that adds, re-roles or re-links: from the previous one and the commit. */\nexport function derivedSecret(previous: Uint8Array, untaggedHash: string): Uint8Array {\n return hkdf(sha256, previous, utf8Encode(untaggedHash), utf8Encode(\"ghostly-group/2 derived\"), 32);\n}\n\nfunction parseCommit(raw: unknown): CommunityCommit | null {\n if (!raw || typeof raw !== \"object\") return null;\n const c = raw as Record<string, unknown>;\n const key = (v: unknown) => typeof v === \"string\" && MEMBER_KEY.test(v);\n if (c.v !== 2 || typeof c.g !== \"string\" || !GROUP_ID.test(c.g) || !Number.isSafeInteger(c.e) || (c.e as number) < 0 ||\n typeof c.p !== \"string\" || (c.p !== \"\" && !HASH.test(c.p)) || !KINDS.includes(c.k as CommunityKind) ||\n typeof c.m !== \"string\" || !HASH.test(c.m) || !key(c.by) || (c.s !== undefined && !key(c.s)) ||\n (c.x !== undefined && !(c.x === \"\" || key(c.x))) || (c.n !== undefined && (typeof c.n !== \"string\" || !NONCE.test(c.n))) ||\n (c.ls !== undefined && (typeof c.ls !== \"string\" || !SIG.test(c.ls))) ||\n !Number.isSafeInteger(c.ts) || (c.ts as number) <= 0 || typeof c.c !== \"string\" || !TAG.test(c.c) || typeof c.sig !== \"string\" || !SIG.test(c.sig)) return null;\n const out: CommunityCommit = { v: 2, g: c.g, e: c.e as number, p: c.p, k: c.k as CommunityKind, m: c.m, by: c.by as string, ts: c.ts as number, c: c.c, sig: c.sig };\n if (c.s !== undefined) out.s = c.s as string;\n if (c.x !== undefined) out.x = c.x as string;\n if (c.n !== undefined) out.n = c.n as string;\n if (c.ls !== undefined) out.ls = c.ls as string;\n return out;\n}\n\n/** The roster a commit of this kind makes of the previous one; null when it is not allowed. */\nexport function communityRoster(previous: Roster | null, c: Pick<CommunityCommit, \"k\" | \"by\" | \"s\" | \"x\" | \"n\" | \"ls\" | \"g\">): Roster | null {\n if (c.k === \"create\") return previous === null && !c.s && !!c.x && !!c.n ? [[c.by, \"admin\"]] : null;\n if (!previous || !rosterHas(previous, c.by)) return null;\n const admin = rosterAdmin(previous);\n switch (c.k) {\n case \"add\":\n if (!c.s || c.x !== undefined || rosterHas(previous, c.s) || previous.length >= COMMUNITY_LIMITS.members) return null;\n return sortRoster([...previous, [c.s, \"member\"]]);\n case \"leave\":\n if (!c.s || c.s === c.by || c.s === admin || !rosterHas(previous, c.s) || !c.ls) return null;\n if (!verify(fromBase64Url(c.ls), leaveStatement(c.g, c.s), publicKeyFromZ32(c.s))) return null;\n return previous.filter(([k]) => k !== c.s);\n case \"remove\":\n if (c.by !== admin || !c.s || c.s === c.by || !rosterHas(previous, c.s)) return null;\n return previous.filter(([k]) => k !== c.s);\n case \"role\":\n if (c.by !== admin || !c.s || c.s === c.by || !rosterHas(previous, c.s)) return null;\n return previous.map(([k]) => [k, k === c.s ? \"admin\" : \"member\"] as [string, GroupRole]);\n case \"rotate\":\n return c.by === admin && !c.s && c.x === undefined ? previous : null;\n case \"link\":\n return c.by === admin && !c.s && c.x !== undefined ? previous : null;\n default:\n return null;\n }\n}\n\n/**\n * One commit against its parent (null for a genesis) and the parent's roster: shape, id, link,\n * authority, roster arithmetic, roster hash and signature. The confirmation tag is checked once\n * the secret is known.\n */\nexport function verifyCommunityCommit(raw: unknown, parent: CommunityCommit | null, parentRoster: Roster | null, groupId?: string): { commit: CommunityCommit; roster: Roster } | { error: string } {\n const commit = parseCommit(raw);\n if (!commit) return { error: \"Malformed membership commit\" };\n if (groupId && commit.g !== groupId) return { error: \"Commit for another group\" };\n if (parent) {\n if (commit.g !== parent.g) return { error: \"Commit for another group\" };\n if (commit.e !== parent.e + 1) return { error: \"Commit out of sequence\" };\n if (commit.p !== communityCommitHash(parent)) return { error: \"Commit does not follow its parent\" };\n } else {\n if (commit.e !== 0 || commit.p !== \"\" || commit.k !== \"create\") return { error: \"The chain does not start with the creation of the group\" };\n if (communityGroupId(commit.by, commit.n ?? \"\") !== commit.g) return { error: \"The group id is not this genesis's\" };\n }\n if (commit.k !== \"create\" && commit.n !== undefined) return { error: \"Malformed membership commit\" };\n if (commit.k !== \"leave\" && commit.ls !== undefined) return { error: \"Malformed membership commit\" };\n const roster = communityRoster(parent ? parentRoster : null, commit);\n if (!roster) return { error: \"Unauthorized or inconsistent membership change\" };\n if (rosterHash(roster) !== commit.m) return { error: \"The roster hash does not match the change\" };\n if (!verify(fromBase64Url(commit.sig), utf8Encode(tuple(commit)), publicKeyFromZ32(commit.by))) return { error: \"Membership commit signature is invalid\" };\n return { commit, roster };\n}\n\n/** A whole chain from its genesis; the rosters after each commit and the entry key at the end. */\nexport function verifyCommunityChain(raw: unknown, groupId?: string): { chain: CommunityCommit[]; roster: Roster; entry: string; rosters: Roster[] } | { error: string } {\n if (!Array.isArray(raw) || raw.length === 0 || raw.length > COMMUNITY_LIMITS.chain) return { error: \"Malformed membership chain\" };\n const chain: CommunityCommit[] = [], rosters: Roster[] = [];\n let entry = \"\";\n for (const entryRaw of raw) {\n const result = verifyCommunityCommit(entryRaw, chain[chain.length - 1] ?? null, rosters[rosters.length - 1] ?? null, groupId);\n if (\"error\" in result) return result;\n chain.push(result.commit); rosters.push(result.roster);\n if (result.commit.x !== undefined) entry = result.commit.x;\n }\n return { chain, roster: rosters[rosters.length - 1], entry, rosters };\n}\n\n// -- frames ---------------------------------------------------------------------------------------\n\nexport interface CommunityMessageFrame { t: \"group-msg\"; v: 2; g: string; e: number; h: string; s: string; n: number; ts: number; nn: string; c: string; sig: string }\nexport interface CommunityCommitFrame { t: \"group-commit\"; v: 2; g: string; commit: CommunityCommit }\n/** An epoch secret sealed to one member (`to`): relayed by hubs to whoever holds that member's edge. */\nexport interface CommunitySecretFrame { t: \"group-secret\"; v: 2; g: string; to: string; h: string; s: SealedSecret }\nexport interface CommunitySecretsFrame { t: \"group-secrets\"; v: 2; g: string; secrets: { h: string; s: SealedSecret }[] }\n/** `loc`: hashes of my branch at 1, 2, 4, 8… commits back from my tip, so whoever is on another branch finds where we part. */\nexport interface CommunitySyncFrame { t: \"group-sync\"; v: 2; g: string; e: number; h: string; loc?: string[]; have: Record<string, Record<string, number>>; secrets: string[]; mt?: string }\nexport interface CommunityLeaveFrame { t: \"group-leave\"; v: 2; g: string; s: string; ls: string }\n/** The seed of the link's current entry key, sealed to one member: after a `link` commit, or at a sync. */\nexport interface CommunityEntryFrame { t: \"group-entry\"; v: 2; g: string; to: string; x: string; s: SealedSecret }\nexport interface CommunityInviteFrame { t: \"group-invite\"; v: 2; g: string; name: string; admin: string; e: number; n: number }\nexport interface CommunityChainFrame { t: \"group-chain\"; v: 2; g: string; commits: CommunityCommit[] }\nexport interface CommunityWelcomeFrame {\n t: \"group-welcome\"; v: 2; g: string; name: string; commits: CommunityCommit[];\n secrets: { h: string; s: SealedSecret }[]; rv: SealedSecret; entry: SealedSecret;\n}\nexport type CommunityFrame = CommunityMessageFrame | CommunityCommitFrame | CommunitySecretFrame | CommunitySecretsFrame | CommunitySyncFrame | CommunityLeaveFrame | CommunityEntryFrame | GroupMetaFrame;\n\nexport type CommunityStatus = \"active\" | \"left\" | \"removed\" | \"forked\" | \"lost\";\n\nexport interface CommunityState {\n id: string;\n name: string;\n profile: typeof COMMUNITY_PROFILE;\n seedB64: string;\n createdAt: number;\n /** The branch followed, from the genesis. */\n chain: CommunityCommit[];\n /** Commits known off it, within the window: losing branches, or a better one being assembled. */\n side: CommunityCommit[];\n /** Commit hash (full) → epoch secret (base64url). */\n secrets: Record<string, string>;\n /** The group's link: its entry key and seed (every member holds the seed); empty key when off. */\n entry: { key: string; seedB64: string };\n /** Rendezvous secret: the beacon and lobbies derive from it. */\n rv: string;\n status: CommunityStatus;\n statusReason?: string;\n /** My next sequence number under the commit `seqH` (full hash). */\n seq: number;\n seqH: string;\n /** Sender → `e:h16` → highest seen and the window below it. */\n seen: Record<string, Record<string, { high: number; window: number[] }>>;\n nicks: Record<string, string>;\n /** Recent frames from everyone (mine included), oldest first. */\n store: CommunityMessageFrame[];\n /**\n * Frames of mine no edge took when I said them (none was up), as `e:h16:n`, oldest first: the first member I reach\n * may have joined after I sealed them, and carries them all the same (see `receiveSync`). Absent in older states.\n */\n unheard?: string[];\n /** Leave requests waiting for a member to commit them. */\n pendingLeaves: { s: string; ls: string }[];\n /** The group's metadata (its picture), as the admin last signed it and I accepted it. */\n meta?: GroupMeta;\n}\n\nexport interface CommunityIncomingMessage { id: string; sender: string; epoch: number; seq: number; timestamp: number; text: string; mentions?: GroupMention[]; reply?: WireReply; forwarded?: number; card?: StatusCard }\n/**\n * What an application sends through the group rather than as text: `frame` for everyone in the epoch (the group\n * reads it, as it reads text), or, in `pair`, a payload only `to` can open. Delivered once, like a message: the\n * frame identity (sender, epoch, commit, sequence) deduplicates it across hubs and catch-ups.\n */\nexport interface CommunityIncomingApp { id: string; sender: string; epoch: number; timestamp: number; frame: Record<string, unknown> }\nexport interface CommunityIncomingPair { id: string; sender: string; epoch: number; timestamp: number; payload: Record<string, unknown> }\n\nexport interface CommunitySessionHooks {\n save(state: CommunityState): Promise<void>;\n /** To everyone this member has an edge to (hubs relay it on). Returns how many edges took it; nothing when the host does not say. */\n broadcast(frame: CommunityFrame): number | void;\n /** To the member at the other end of an edge, if there is one. */\n direct(to: string, frame: CommunityFrame): void;\n /** To one member, wherever they are: over their edge if I have it, else to the hubs with `to`. */\n addressed(to: string, frame: CommunitySecretFrame | CommunityEntryFrame): void;\n message(message: CommunityIncomingMessage): Promise<void> | void;\n /** An application frame for the group (see `sendApp`). */\n app?(message: CommunityIncomingApp): Promise<void> | void;\n /** A payload another member sealed to me (see `sendPair`). Payloads to others are carried, never opened. */\n pair?(message: CommunityIncomingPair): Promise<void> | void;\n changed(): void;\n /** The group's name or picture changed (set, replaced or removed), by `by`, in a statement signed at `at`. */\n metaChanged?(by: string, change: GroupMetaChange, at: number): void;\n /** A frame that waited here (a commit ahead of its parent) and is now placed: a hub passes it on. */\n relay?(frame: CommunityFrame): void;\n /** The engine's clock, for how often a member is asked for what I lack (defaults to Date.now). */\n clock?(): number;\n}\n\nconst MAX_TEXT_BOX = Math.ceil((COMMUNITY_LIMITS.textBytes + 256 + 16) * 4 / 3) + 4;\nconst secretAad = (g: string, h: string, member: string) => JSON.stringify([\"ghostly-group/2 secret\", g, h, member]);\nconst rvAad = (g: string, member: string) => JSON.stringify([\"ghostly-group/2 rendezvous\", g, member]);\nconst entryAad = (g: string, member: string) => JSON.stringify([\"ghostly-group/2 entry\", g, member]);\nconst messageAad = (f: Pick<CommunityMessageFrame, \"g\" | \"e\" | \"h\" | \"s\" | \"n\" | \"ts\">) => JSON.stringify([f.g, f.e, f.h, f.s, f.n, f.ts]);\n/** A pair payload is bound to the frame that carries it, and to whom it is for. */\nconst pairAad = (f: Pick<CommunityMessageFrame, \"g\" | \"e\" | \"h\" | \"s\" | \"n\" | \"ts\">, to: string) => JSON.stringify([\"ghostly-group/2 pair\", f.g, f.e, f.h, f.s, f.n, f.ts, to]);\nconst isObject = (v: unknown): v is Record<string, unknown> => !!v && typeof v === \"object\" && !Array.isArray(v);\nconst messageSigned = (f: Omit<CommunityMessageFrame, \"sig\" | \"t\" | \"v\">) => utf8Encode(JSON.stringify([\"ghostly-group/2 msg\", f.g, f.e, f.h, f.s, f.n, f.ts, f.nn, f.c]));\nexport const communityMessageId = (sender: string, epoch: number, h: string, seq: number) => `${sender}:${epoch}:${h}:${seq}`;\nconst seenKey = (e: number, h: string) => `${e}:${h}`;\n/** One of a member's frames, among its own. */\nconst frameKey = (f: { e: number; h: string; n: number }) => `${f.e}:${f.h}:${f.n}`;\n/** Frames of mine remembered as not heard by anyone yet. */\nconst UNHEARD_KEPT = 64;\n/** Whether a frame, by its identity, is one this state took already (or is too old to be taken again). */\nfunction seenIn(seen: CommunityState[\"seen\"], f: { s: string; e: number; h: string; n: number }): boolean {\n const entry = seen[f.s]?.[seenKey(f.e, f.h)];\n if (!entry || f.n > entry.high) return false;\n if (f.n <= entry.high - COMMUNITY_LIMITS.replay) return true;\n return f.n === entry.high || entry.window.includes(f.n);\n}\n/**\n * Whether a stored community state holds the frame a beacon's head names (WISP 9xx § Head), read without running\n * the group: its own frame, one it took, or one of an epoch so far behind its own that it would not be taken now.\n */\nexport function communityHasFrame(state: Pick<CommunityState, \"seen\" | \"seedB64\" | \"chain\">, f: { s: string; e: number; h: string; n: number }): boolean {\n if (f.s === identityFromSeedB64(state.seedB64).pubKeyZ32) return true;\n const epoch = state.chain.length ? state.chain[state.chain.length - 1].e : 0;\n if (f.e < epoch - COMMUNITY_LIMITS.window) return true;\n return seenIn(state.seen, f);\n}\n\nfunction isSealed(v: unknown): v is SealedSecret {\n return !!v && typeof v === \"object\" && [\"e\", \"n\", \"c\"].every(k => typeof (v as Record<string, unknown>)[k] === \"string\" && B64.test((v as Record<string, string>)[k]) && (v as Record<string, string>)[k].length <= 128);\n}\n/** A pair payload's box: an ephemeral key, a nonce and a ciphertext of at most `appBytes` sealed. */\nconst MAX_PAIR_BOX = Math.ceil((COMMUNITY_LIMITS.appBytes + 16) * 4 / 3) + 4;\nfunction isPairBox(v: Record<string, unknown>): v is Record<string, unknown> & SealedSecret & { to: string } {\n return typeof v.to === \"string\" && MEMBER_KEY.test(v.to) && typeof v.e === \"string\" && v.e.length === 43 && B64.test(v.e) &&\n typeof v.n === \"string\" && v.n.length === 32 && B64.test(v.n) && typeof v.c === \"string\" && v.c.length <= MAX_PAIR_BOX && B64.test(v.c);\n}\nfunction isMessageFrame(v: unknown): v is CommunityMessageFrame {\n if (!v || typeof v !== \"object\") return false;\n const f = v as Record<string, unknown>;\n return f.t === \"group-msg\" && f.v === 2 && typeof f.g === \"string\" && GROUP_ID.test(f.g) && Number.isSafeInteger(f.e) && (f.e as number) >= 0 &&\n typeof f.h === \"string\" && /^[a-f0-9]{16}$/.test(f.h) && typeof f.s === \"string\" && MEMBER_KEY.test(f.s) && Number.isSafeInteger(f.n) && (f.n as number) >= 0 &&\n Number.isSafeInteger(f.ts) && (f.ts as number) > 0 && typeof f.nn === \"string\" && f.nn.length === 32 && B64.test(f.nn) &&\n typeof f.c === \"string\" && f.c.length <= MAX_TEXT_BOX && B64.test(f.c) && typeof f.sig === \"string\" && f.sig.length === 86 && B64.test(f.sig);\n}\n\n/**\n * One member's view of a community group: the branch it follows and the ones it knows, the\n * secrets it holds, what it has seen, and the recent frames it keeps for others.\n */\nexport class CommunitySession {\n readonly state: CommunityState;\n private readonly identity;\n /** Commit hash → commit, for everything known (main and side). */\n private known = new Map<string, CommunityCommit>();\n /** Commit hash → roster after it, for the window of the main branch and the side commits. */\n private rosters = new Map<string, Roster>();\n private mainIndex = new Map<string, number>();\n private pendingCommits = new Map<string, { from: string; commit: unknown }>();\n private pendingSecrets = new Map<string, SealedSecret>();\n private waiting: { from: string; frame: CommunityMessageFrame }[] = [];\n private waitingBytes = 0;\n private asked = new Map<string, number>();\n /** Syncs answered per member, a few a minute (`COMMUNITY_LIMITS.syncAnswers`). In memory only. */\n private syncsAnswered = new Map<string, RateWindow>();\n private queue = Promise.resolve();\n private saveTimer: ReturnType<typeof setTimeout> | undefined;\n /** One metadata frame naming a commit or a secret I do not have yet: tried again when the chain or my secrets move. */\n private pendingMeta: { from: string; frame: unknown } | undefined;\n\n constructor(state: CommunityState, private readonly hooks: CommunitySessionHooks) {\n this.state = state;\n this.identity = identityFromSeedB64(state.seedB64);\n this.rebuild();\n // A branch kept under the older rule (longest first) may be the better one now: it keeps an admin change mine dropped.\n if ((state.status === \"active\" || state.status === \"lost\") && this.bestBranch().tip !== this.topHash) void this.serialize(() => this.chooseBranch());\n }\n\n // -- creation and joining ----------------------------------------------------------------------\n\n static create(name: string, now = Date.now()): CommunityState {\n const me = identityFromSeedB64(toBase64Url(randomBytes(32)));\n const entry = identityFromSeedB64(toBase64Url(randomBytes(32)));\n const n = toBase64Url(randomBytes(16));\n const g = communityGroupId(me.pubKeyZ32, n);\n const secret = newEpochSecret();\n const roster: Roster = [[me.pubKeyZ32, \"admin\"]];\n const draft: Omit<CommunityCommit, \"sig\" | \"c\"> = { v: 2, g, e: 0, p: \"\", k: \"create\", m: rosterHash(roster), by: me.pubKeyZ32, x: entry.pubKeyZ32, n, ts: now };\n const commit = signCommunity({ ...draft, c: confirmationTag(epochKeys(secret, g, 0).confirm, communityUntaggedHash(draft)) }, me.seed);\n const h = communityCommitHash(commit);\n return { id: g, name: sanitizeNick(name) ?? \"Group\", profile: COMMUNITY_PROFILE, seedB64: me.seedB64, createdAt: now, chain: [commit], side: [],\n secrets: { [h]: toBase64Url(secret) }, entry: { key: entry.pubKeyZ32, seedB64: entry.seedB64 }, rv: toBase64Url(randomBytes(32)),\n status: \"active\", seq: 0, seqH: h, seen: {}, nicks: {}, store: [], pendingLeaves: [] };\n }\n\n /**\n * Joining from a welcome over the link's entry session: the whole chain verifies from its\n * genesis (which produces the group id), the link's entry key is the chain's current one, the\n * chain admits me in an `add` (signed by a member, as the chain rules require), and the secrets,\n * rendezvous secret and entry seed open and confirm. Who sent the welcome does not matter: the\n * entry session is pinned to the link's entry key, which only members hold, and a welcome whose\n * add was made by another member (the first one to answer me went away) is as good.\n */\n static join(link: { g: string; host: string }, pieces: unknown[], welcome: unknown, seedB64: string, now = Date.now()): { state: CommunityState } | { error: string } {\n if (!welcome || typeof welcome !== \"object\") return { error: \"Malformed welcome\" };\n const w = welcome as Record<string, unknown>;\n if (w.t !== \"group-welcome\" || w.v !== 2 || w.g !== link.g || !Array.isArray(w.commits) || !Array.isArray(w.secrets) || !isSealed(w.rv) || !isSealed(w.entry)) return { error: \"Malformed welcome\" };\n const commits = [...pieces.flatMap(p => (p && typeof p === \"object\" && Array.isArray((p as CommunityChainFrame).commits)) ? (p as CommunityChainFrame).commits : []), ...w.commits];\n if (commits.length > COMMUNITY_LIMITS.chain) return { error: \"Membership chain too long\" };\n const verified = verifyCommunityChain(commits, link.g);\n if (\"error\" in verified) return verified;\n const { chain, roster, entry } = verified;\n const me = identityFromSeedB64(seedB64);\n if (!rosterHas(roster, me.pubKeyZ32)) return { error: \"The welcome does not admit this member key\" };\n if (entry !== link.host) return { error: \"This link was replaced or turned off\" };\n let admitted = -1;\n for (let i = chain.length - 1; i >= 0; i--) if (chain[i].k === \"add\" && chain[i].s === me.pubKeyZ32) { admitted = i; break; }\n if (admitted < 0) return { error: \"The welcome does not admit this member key\" };\n const secrets: Record<string, string> = {};\n const byHash = new Map(chain.map((c, i) => [communityCommitHash(c), i]));\n for (const entryRaw of w.secrets as unknown[]) {\n const { h, s } = (entryRaw ?? {}) as { h?: unknown; s?: unknown };\n if (typeof h !== \"string\" || !isSealed(s)) continue;\n const i = byHash.get(h);\n if (i === undefined || i < admitted) continue;\n const secret = openSecret(me.seed, me.pubKeyZ32, s, secretAad(link.g, h, me.pubKeyZ32));\n if (secret && confirmationMatches(epochKeys(secret, link.g, chain[i].e).confirm, communityUntaggedHash(chain[i]), chain[i].c)) secrets[h] = toBase64Url(secret);\n }\n // Later derived epochs follow from the admitting one.\n for (let i = admitted + 1; i < chain.length; i++) {\n const h = communityCommitHash(chain[i]), before = secrets[communityCommitHash(chain[i - 1])];\n if (!secrets[h] && before && !FRESH.has(chain[i].k)) secrets[h] = toBase64Url(derivedSecret(fromBase64Url(before), communityUntaggedHash(chain[i])));\n }\n const top = communityCommitHash(chain[chain.length - 1]);\n if (!secrets[top]) return { error: \"The welcome carries no usable secret for the current epoch\" };\n const rv = openSecret(me.seed, me.pubKeyZ32, w.rv as SealedSecret, rvAad(link.g, me.pubKeyZ32));\n const entrySeed = openSecret(me.seed, me.pubKeyZ32, w.entry as SealedSecret, entryAad(link.g, me.pubKeyZ32));\n if (!rv || !entrySeed || identityFromSeedB64(toBase64Url(entrySeed)).pubKeyZ32 !== entry) return { error: \"The welcome's keys do not open\" };\n return { state: { id: link.g, name: groupName(w.name) ?? \"Group\", profile: COMMUNITY_PROFILE, seedB64, createdAt: now,\n chain, side: [], secrets, entry: { key: entry, seedB64: toBase64Url(entrySeed) }, rv: toBase64Url(rv), status: \"active\", seq: 0, seqH: top,\n seen: {}, nicks: {}, store: [], pendingLeaves: [] } };\n }\n\n // -- views ---------------------------------------------------------------------------------------\n\n get id(): string { return this.state.id; }\n /** The name the admin gave the group, else the one it had when I got in (WISP 9xx § Metadata). */\n get name(): string { return groupDisplayName(this.state.meta, this.state.name); }\n get myKey(): string { return this.identity.pubKeyZ32; }\n get top(): CommunityCommit { return this.state.chain[this.state.chain.length - 1]; }\n get topHash(): string { return communityCommitHash(this.top); }\n get epoch(): number { return this.top.e; }\n get roster(): Roster { return this.rosters.get(this.topHash)!; }\n get admin(): string | undefined { return rosterAdmin(this.roster); }\n get isAdmin(): boolean { return this.admin === this.myKey && this.state.status === \"active\"; }\n get status(): CommunityStatus { return this.state.status; }\n get others(): string[] { return this.roster.map(([k]) => k).filter(k => k !== this.myKey); }\n get isMember(): boolean { return this.state.status === \"active\" && rosterHas(this.roster, this.myKey); }\n get canSend(): boolean { return this.isMember && !!this.state.secrets[this.topHash]; }\n /** The entry key of the group's link, or \"\" when it is off. */\n get entryKey(): string { return this.state.entry.key; }\n role(key: string): GroupRole | undefined { return this.roster.find(([k]) => k === key)?.[1]; }\n /** The group's picture, if it has one. */\n get picture(): string | undefined { return groupMetaPicture(this.state.meta); }\n /** Members of any epoch in the window: whom a frame may come from. */\n isRecentMember(key: string): boolean {\n for (const roster of this.rosters.values()) if (rosterHas(roster, key)) return true;\n return false;\n }\n /** Someone the chain took out (removed or left) and who is not back in. */\n wasRemoved(key: string): boolean { return this.outIndex(key) >= 0; }\n /** When the chain took them out (the commit's time, by its signer's clock), if it did and they are not back in. */\n outAt(key: string): number | undefined { const i = this.outIndex(key); return i < 0 ? undefined : this.state.chain[i].ts; }\n private outIndex(key: string): number {\n if (rosterHas(this.roster, key)) return -1;\n for (let i = this.state.chain.length - 1; i >= 0; i--) { const c = this.state.chain[i]; if ((c.k === \"remove\" || c.k === \"leave\") && c.s === key) return i; }\n return -1;\n }\n /**\n * What someone the chain took out is told when it comes back (WISP 9xx § Leaving and removal): the commits from where\n * it says it is (`sync`: its tip, or its locator) up to the one that took it out, and nothing after. Later commits\n * are not its business (a `link` commit names the new entry key); no secret, no stored frame, no seed goes with them.\n * Empty when it is a member, was never one, or already holds that commit.\n */\n farewell(key: string, sync: { h?: unknown; loc?: unknown }): CommunityCommitFrame[] {\n if (!this.isMember) return [];\n const chain = this.state.chain, out = this.outIndex(key);\n if (out < 0) return [];\n let start: number;\n if (typeof sync.h === \"string\" && this.mainIndex.has(sync.h)) start = this.mainIndex.get(sync.h)! + 1;\n else {\n const path = typeof sync.h === \"string\" && this.known.has(sync.h) ? this.pathToMain(sync.h) : null;\n const common = (Array.isArray(sync.loc) ? sync.loc : []).find(x => typeof x === \"string\" && this.mainIndex.has(x)) as string | undefined;\n start = path ? this.mainIndex.get(this.known.get(path[0])!.p)! + 1 : common !== undefined ? this.mainIndex.get(common)! + 1 : Math.max(0, out + 1 - COMMUNITY_LIMITS.side);\n }\n return chain.slice(start, out + 1).map(commit => ({ t: \"group-commit\" as const, v: 2 as const, g: this.id, commit }));\n }\n missing(sender: string): number {\n const entry = this.state.seen[sender]?.[seenKey(this.epoch, shortHash(this.topHash))];\n if (!entry) return 0;\n const lowest = Math.max(0, entry.high - COMMUNITY_LIMITS.replay + 1);\n return Math.max(0, entry.high - lowest + 1 - (1 + entry.window.filter(n => n >= lowest).length));\n }\n\n // -- the tree of commits -------------------------------------------------------------------------\n\n /** Replays the main branch (rosters kept for its window) and places the side commits. */\n private rebuild(): void {\n this.known.clear(); this.rosters.clear(); this.mainIndex.clear();\n let roster: Roster | null = null;\n const keepFrom = Math.max(0, this.state.chain.length - 1 - COMMUNITY_LIMITS.window);\n this.state.chain.forEach((commit, i) => {\n roster = communityRoster(roster, commit);\n if (!roster) throw new Error(\"Stored membership chain does not replay\");\n const h = communityCommitHash(commit);\n this.known.set(h, commit); this.mainIndex.set(h, i);\n if (i >= keepFrom) this.rosters.set(h, roster);\n });\n for (const commit of this.state.side) this.placeSide(commit);\n }\n\n private placeSide(commit: CommunityCommit): boolean {\n const parent = this.known.get(commit.p), parentRoster = parent ? this.rosterAt(commit.p) : undefined;\n if (!parentRoster || !parent) return false;\n const result = verifyCommunityCommit(commit, parent, parentRoster, this.id);\n if (\"error\" in result) return false;\n const h = communityCommitHash(result.commit);\n this.known.set(h, result.commit); this.rosters.set(h, result.roster);\n return true;\n }\n\n private rosterOf(h: string): Roster | undefined { return this.rosterAt(h); }\n /** The roster after a commit: cached for the window and the side, replayed for an older commit of the main branch. */\n private rosterAt(h: string): Roster | undefined {\n const cached = this.rosters.get(h);\n if (cached) return cached;\n const i = this.mainIndex.get(h);\n if (i === undefined) return undefined;\n let roster: Roster | null = null;\n for (let j = 0; j <= i; j++) roster = communityRoster(roster, this.state.chain[j]);\n if (roster) this.rosters.set(h, roster);\n return roster ?? undefined;\n }\n commitByShort(e: number, h16: string): { commit: CommunityCommit; hash: string } | undefined {\n const i = this.state.chain.length - 1 - (this.epoch - e);\n const candidates = [...(i >= 0 && i < this.state.chain.length ? [this.state.chain[i]] : []), ...this.state.side.filter(c => c.e === e)];\n for (const commit of candidates) {\n const hash = communityCommitHash(commit);\n if (hash.startsWith(h16) && this.rosters.has(hash)) return { commit, hash };\n }\n return undefined;\n }\n\n /** The commits from `hash` back to the main branch (exclusive), oldest first; null when it does not reach it. */\n private pathToMain(hash: string): string[] | null {\n const path: string[] = [];\n let h = hash;\n while (!this.mainIndex.has(h)) {\n const c = this.known.get(h);\n if (!c) return null;\n path.unshift(h);\n h = c.p;\n }\n return path;\n }\n\n /** A branch: its tip, the main-branch index it leaves from, and its commits off the main branch (oldest first). */\n private branch(tip: string): Branch | null {\n if (this.mainIndex.has(tip)) return { tip, fork: this.mainIndex.get(tip)!, path: [] };\n const path = this.pathToMain(tip);\n return path ? { tip, fork: this.mainIndex.get(this.known.get(path[0])!.p)!, path } : null;\n }\n\n /** How many admin changes the main branch holds up to each index. */\n private adminCounts(): number[] {\n const counts: number[] = [];\n this.state.chain.forEach((c, i) => counts.push((i ? counts[i - 1] : 0) + (ADMIN_ONLY.has(c.k) ? 1 : 0)));\n return counts;\n }\n\n /** What each of two branches holds after their common parent: whether it has an admin change, and its first commit. */\n private parting(a: Branch, b: Branch, counts: number[]): { parent: number; admin: [boolean, boolean]; first: [string, string] } {\n const m = Math.min(a.fork, b.fork);\n // Off the same main commit, the side paths may share their start too.\n let shared = 0;\n if (a.fork === b.fork) while (shared < a.path.length && shared < b.path.length && a.path[shared] === b.path[shared]) shared++;\n const parent = shared ? this.known.get(a.path[shared - 1])!.e : this.state.chain[m].e;\n const side = (x: Branch) => {\n const path = x.path.slice(shared);\n const admin = counts[x.fork] - counts[m] > 0 || path.some(h => ADMIN_ONLY.has(this.known.get(h)!.k));\n const first = x.fork > m ? communityCommitHash(this.state.chain[m + 1]) : path[0] ?? \"\";\n return { admin, first };\n };\n const sa = side(a), sb = side(b);\n return { parent, admin: [sa.admin, sb.admin], first: [sa.first, sb.first] };\n }\n\n /**\n * Is branch `a` better than branch `b`? After their common parent: the one that keeps an admin\n * change first, however short and however far back it parts; then the longer; then, parting within\n * the window, the lower hash of the first commit. Both holding admin changes means the admin signed\n * two histories: `b` stays (a fresh one halts the group, see `receiveCommit`).\n */\n private better(a: Branch, b: Branch, counts: number[]): boolean {\n if (a.tip === b.tip) return false;\n const { parent, admin, first } = this.parting(a, b, counts);\n if (admin[0] !== admin[1]) return admin[0];\n if (admin[0]) return false;\n const ea = this.known.get(a.tip)!.e, eb = this.known.get(b.tip)!.e;\n if (ea !== eb) return ea > eb;\n if (parent < this.epoch - COMMUNITY_LIMITS.window) return false;\n return first[0] < first[1];\n }\n\n /**\n * Takes in a commit (from a frame or from me): placed in the tree if it verifies against its\n * parent, a fork if the admin signed another commit after the same parent, and then the best\n * branch is followed. Returns whether it was new.\n */\n private async receiveCommit(from: string, raw: unknown): Promise<boolean> {\n if (!raw || typeof raw !== \"object\") return false;\n const p = (raw as { p?: unknown }).p, e = (raw as { e?: unknown }).e;\n if (typeof p !== \"string\" || !Number.isSafeInteger(e)) return false;\n const parent = this.known.get(p), parentRoster = parent ? this.rosterAt(p) : undefined;\n if (!parent || !parentRoster) {\n // Its parent is not here yet: kept, within bounds, while I ask for what comes before.\n if (this.pendingCommits.size < COMMUNITY_LIMITS.pendingCommits) {\n this.pendingCommits.set(p + \":\" + String(e), { from, commit: raw });\n this.ask(from);\n }\n return false;\n }\n const result = verifyCommunityCommit(raw, parent, parentRoster, this.id);\n if (\"error\" in result) return false;\n const commit = result.commit, h = communityCommitHash(commit);\n if (this.known.has(h)) return false;\n // Two commits by the admin after the same commit: equivocation.\n const admin = rosterAdmin(parentRoster);\n if (commit.by === admin) {\n for (const [other, c] of this.known) if (other !== h && c.p === commit.p && c.by === admin) {\n this.known.set(h, commit); this.rosters.set(h, result.roster);\n await this.fork(`The admin signed two different changes after epoch ${parent.e}`);\n return true;\n }\n }\n this.known.set(h, commit); this.rosters.set(h, result.roster);\n const branch = this.branch(h);\n if (branch && branch.path.length) {\n const { admin } = this.parting(branch, this.branch(this.topHash)!, this.adminCounts());\n // The admin's change on a branch parting from one that holds another of theirs: two histories.\n if (ADMIN_ONLY.has(commit.k) && admin[1]) {\n await this.fork(`The admin signed changes on two branches after epoch ${this.state.chain[branch.fork].e}`);\n return true;\n }\n // A branch that drops an admin change never wins: from someone the main branch took out, it is not kept or passed on.\n if (admin[1] && !admin[0] && !rosterHas(this.roster, commit.by)) { this.known.delete(h); this.rosters.delete(h); return false; }\n }\n if (!this.mainIndex.has(h)) this.state.side.push(commit);\n this.takeDerived(h);\n const sealed = this.pendingSecrets.get(h);\n if (sealed) { this.pendingSecrets.delete(h); this.takeSecret(h, sealed); }\n await this.chooseBranch();\n // Anything that was waiting for this one; hubs relay those too.\n for (const [key, pending] of [...this.pendingCommits]) if (key.startsWith(h + \":\")) {\n this.pendingCommits.delete(key);\n if (await this.receiveCommit(pending.from, pending.commit)) this.hooks.relay?.({ t: \"group-commit\", v: 2, g: this.id, commit: pending.commit as CommunityCommit });\n }\n return true;\n }\n\n /**\n * Follows the best branch among the known tips (see `better`); reorganizes the main branch when it\n * changed. A branch that keeps the admin's changes, or a longer one, wins however far back it parts\n * (a member away through a race must end up where everyone is); a branch of the same length only\n * within the window.\n */\n private async chooseBranch(): Promise<void> {\n const best = this.bestBranch();\n if (best.tip !== this.topHash) await this.adopt(best.tip);\n if (this.state.status === \"lost\" && rosterHas(this.roster, this.myKey)) { this.state.status = \"active\"; delete this.state.statusReason; }\n this.followEntry();\n this.pruneSide();\n await this.persist();\n this.hooks.changed();\n await this.replayWaiting();\n }\n\n private bestBranch(): Branch {\n const counts = this.adminCounts();\n let best = this.branch(this.topHash)!;\n const parents = new Set(this.state.side.map(c => c.p));\n for (const c of this.state.side) {\n const h = communityCommitHash(c);\n // Only tips: a commit's children keep what it has and are longer.\n if (this.mainIndex.has(h) || parents.has(h)) continue;\n const branch = this.branch(h);\n if (branch && this.better(branch, best, counts)) best = branch;\n }\n return best;\n }\n\n private async adopt(tip: string): Promise<void> {\n const path = this.pathToMain(tip)!;\n const ancestor = this.mainIndex.get(this.known.get(path[0])!.p)!;\n const dropped = this.state.chain.splice(ancestor + 1);\n for (const c of dropped) { const h = communityCommitHash(c); this.mainIndex.delete(h); this.state.side.push(c); }\n for (const h of path) {\n const c = this.known.get(h)!;\n this.state.side = this.state.side.filter(x => communityCommitHash(x) !== h);\n this.mainIndex.set(h, this.state.chain.length);\n this.state.chain.push(c);\n this.takeDerived(h);\n }\n const lostMine = !rosterHas(this.roster, this.myKey);\n if (lostMine && this.state.status === \"active\") {\n const out = [...path].reverse().map(h => this.known.get(h)!).find(c => (c.k === \"remove\" || c.k === \"leave\") && c.s === this.myKey);\n if (out) this.out(out.k === \"remove\" ? \"removed\" : \"left\", out.k === \"remove\" ? \"You were removed from this group\" : \"You left this group\");\n else { this.state.status = \"lost\"; this.state.statusReason = \"Two members let people in at the same moment and yours did not count. Asking to be let in again…\"; }\n }\n }\n\n /** The entry key is the one the main branch names; its seed comes with a `group-entry` from whoever holds it. */\n private followEntry(): void {\n const key = this.currentEntryKey();\n if (key !== this.state.entry.key) this.state.entry = { key, seedB64: \"\" };\n }\n\n /** Side commits stay up to a bound, the most recent ones (a longer branch being assembled included). */\n private pruneSide(): void {\n this.state.side = this.state.side.slice(-COMMUNITY_LIMITS.side);\n const keep = new Set(this.state.side.map(c => communityCommitHash(c)));\n for (const [h, c] of [...this.known]) {\n if (this.mainIndex.has(h)) { if (this.mainIndex.get(h)! < this.state.chain.length - 1 - COMMUNITY_LIMITS.window) this.rosters.delete(h); continue; }\n if (!keep.has(h)) { this.known.delete(h); this.rosters.delete(h); void c; }\n }\n this.pruneSecrets();\n }\n\n private pruneSecrets(): void {\n const order = [...this.state.chain.slice(-COMMUNITY_LIMITS.window - 1), ...this.state.side].map(c => communityCommitHash(c));\n const keep = new Set(order.slice(-COMMUNITY_LIMITS.secrets));\n for (const h of Object.keys(this.state.secrets)) if (!keep.has(h)) delete this.state.secrets[h];\n }\n\n /** A derived epoch's secret, from its parent's, when that is held. */\n private takeDerived(h: string): void {\n const c = this.known.get(h);\n if (!c || this.state.secrets[h] || FRESH.has(c.k)) return;\n const parent = this.state.secrets[c.p];\n if (!parent || !rosterHas(this.rosterAt(h) ?? [], this.myKey)) return;\n const secret = derivedSecret(fromBase64Url(parent), communityUntaggedHash(c));\n if (confirmationMatches(epochKeys(secret, this.id, c.e).confirm, communityUntaggedHash(c), c.c)) {\n this.state.secrets[h] = toBase64Url(secret);\n // …and the derived ones after it, on any branch.\n for (const [child, cc] of this.known) if (cc.p === h) this.takeDerived(child);\n }\n }\n\n /** A sealed secret for a known commit I am in the roster of: kept only when it confirms. */\n private takeSecret(h: string, sealed: unknown): boolean {\n const c = this.known.get(h);\n if (!c) { if (isSealed(sealed) && this.pendingSecrets.size < 32) this.pendingSecrets.set(h, sealed); return false; }\n if (this.state.secrets[h] || !isSealed(sealed) || !rosterHas(this.rosterAt(h) ?? [], this.myKey)) return false;\n const secret = openSecret(this.identity.seed, this.myKey, sealed, secretAad(this.id, h, this.myKey));\n if (!secret || !confirmationMatches(epochKeys(secret, this.id, c.e).confirm, communityUntaggedHash(c), c.c)) return false;\n this.state.secrets[h] = toBase64Url(secret);\n for (const [child, cc] of this.known) if (cc.p === h) this.takeDerived(child);\n return true;\n }\n\n // -- what a member does --------------------------------------------------------------------------\n\n private serialize<T>(run: () => Promise<T>): Promise<T> {\n const operation = this.queue.then(run);\n this.queue = operation.then(() => {}, () => {});\n return operation;\n }\n private persist(): Promise<void> {\n if (this.saveTimer) { clearTimeout(this.saveTimer); this.saveTimer = undefined; }\n return this.hooks.save(structuredClone(this.state));\n }\n /**\n * A save within a second, for what may happen many times a second (every message received, in a\n * group of hundreds): the state holds the chain and the store, and is not worth writing each time.\n */\n private persistSoon(): void {\n if (this.saveTimer) return;\n this.saveTimer = setTimeout(() => { this.saveTimer = undefined; void this.hooks.save(structuredClone(this.state)); }, 1000);\n }\n /** Writes what is waiting to be saved now (before a restart, in tests). */\n async flush(): Promise<void> { if (this.saveTimer) await this.persist(); }\n private requireMember(): void { if (!this.isMember) throw new Error(this.state.statusReason ?? \"You are not in this group\"); }\n private requireAdmin(): void { this.requireMember(); if (!this.isAdmin) throw new Error(\"Only the admin can do that\"); }\n\n private async commit(kind: CommunityKind, fields: { s?: string; x?: string; ls?: string }, now: number): Promise<{ commit: CommunityCommit; hash: string; secret: Uint8Array }> {\n if (this.state.chain.length >= COMMUNITY_LIMITS.chain) throw new Error(\"This group has reached its membership history limit. Create a new group.\");\n const parent = this.top, parentHash = this.topHash;\n const draftBase = { v: 2 as const, g: this.id, e: parent.e + 1, p: parentHash, k: kind, by: this.myKey, ...fields, ts: now };\n const roster = communityRoster(this.roster, draftBase);\n if (!roster) throw new Error(\"This change is not allowed\");\n const draft: Omit<CommunityCommit, \"sig\" | \"c\"> = { ...draftBase, m: rosterHash(roster) };\n const untagged = communityUntaggedHash(draft);\n let secret: Uint8Array;\n if (FRESH.has(kind)) secret = newEpochSecret();\n else {\n const previous = this.state.secrets[parentHash];\n if (!previous) throw new Error(\"This epoch's key has not arrived yet. Wait for a member to catch you up.\");\n secret = derivedSecret(fromBase64Url(previous), untagged);\n }\n const commit = signCommunity({ ...draft, c: confirmationTag(epochKeys(secret, this.id, draft.e).confirm, untagged) }, this.identity.seed);\n const hash = communityCommitHash(commit);\n this.known.set(hash, commit); this.rosters.set(hash, roster);\n this.mainIndex.set(hash, this.state.chain.length);\n this.state.chain.push(commit);\n this.state.secrets[hash] = toBase64Url(secret);\n this.state.seq = 0; this.state.seqH = hash;\n this.pruneSide();\n await this.persist();\n this.hooks.broadcast({ t: \"group-commit\", v: 2, g: this.id, commit });\n if (FRESH.has(kind)) for (const [key] of roster) if (key !== this.myKey) this.hooks.addressed(key, { t: \"group-secret\", v: 2, g: this.id, to: key, h: hash, s: sealSecret(key, secret, secretAad(this.id, hash, key)) });\n this.hooks.changed();\n return { commit, hash, secret };\n }\n\n /** What an entry session sends a joiner first. */\n inviteFrame(): CommunityInviteFrame {\n this.requireMember();\n return { t: \"group-invite\", v: 2, g: this.id, name: this.name, admin: this.myKey, e: this.epoch, n: this.roster.length };\n }\n\n /** Admits a joiner who knocked with the link: an `add`, then the welcome for them (in pieces). */\n admit(memberKey: string, now = Date.now()): Promise<(CommunityChainFrame | CommunityWelcomeFrame)[]> {\n return this.serialize(async () => {\n this.requireMember();\n if (!MEMBER_KEY.test(memberKey) || memberKey === this.myKey) throw new Error(\"Invalid member key\");\n if (rosterHas(this.roster, memberKey)) throw new Error(\"Already a member\");\n if (this.roster.length >= COMMUNITY_LIMITS.members) throw new Error(`The group is full (${COMMUNITY_LIMITS.members} members)`);\n const { hash } = await this.commit(\"add\", { s: memberKey }, now);\n return this.welcomeFor(memberKey, hash);\n });\n }\n\n /**\n * A welcome for someone already in the roster who knocks again: their admission reached the\n * roster but its welcome never reached them. Nothing is committed; what they get is sealed to their key.\n */\n rewelcome(memberKey: string): Promise<(CommunityChainFrame | CommunityWelcomeFrame)[]> {\n return this.serialize(async () => {\n this.requireMember();\n if (!rosterHas(this.roster, memberKey) || memberKey === this.myKey) throw new Error(\"Not a member\");\n for (let i = this.state.chain.length - 1; i >= 0; i--) {\n const c = this.state.chain[i];\n if (c.k === \"add\" && c.s === memberKey) return this.welcomeFor(memberKey, communityCommitHash(c));\n }\n throw new Error(\"Not a member\");\n });\n }\n\n /** The chain, the secrets from the admitting commit on, the rendezvous secret and the entry seed, for one newcomer. */\n private welcomeFor(memberKey: string, admittedAt: string): (CommunityChainFrame | CommunityWelcomeFrame)[] {\n const chain = this.state.chain, from = this.mainIndex.get(admittedAt)!;\n const secrets = chain.slice(from).map(c => communityCommitHash(c)).filter(h => this.state.secrets[h])\n .map(h => ({ h, s: sealSecret(memberKey, fromBase64Url(this.state.secrets[h]), secretAad(this.id, h, memberKey)) }));\n const piece = COMMUNITY_LIMITS.chainPiece, frames: (CommunityChainFrame | CommunityWelcomeFrame)[] = [];\n for (let i = 0; i < chain.length - piece; i += piece) frames.push({ t: \"group-chain\", v: 2, g: this.id, commits: chain.slice(i, i + piece) });\n const tail = chain.length % piece || piece;\n frames.push({ t: \"group-welcome\", v: 2, g: this.id, name: this.name, commits: chain.slice(chain.length - tail), secrets,\n rv: sealSecret(memberKey, fromBase64Url(this.state.rv), rvAad(this.id, memberKey)),\n entry: sealSecret(memberKey, fromBase64Url(this.state.entry.seedB64), entryAad(this.id, memberKey)) });\n return frames;\n }\n\n remove(memberKey: string, now = Date.now()): Promise<void> {\n return this.serialize(async () => {\n this.requireAdmin();\n if (!rosterHas(this.roster, memberKey) || memberKey === this.myKey) throw new Error(\"Not a member\");\n await this.commit(\"remove\", { s: memberKey }, now);\n });\n }\n transferAdmin(memberKey: string, now = Date.now()): Promise<void> {\n return this.serialize(async () => {\n this.requireAdmin();\n if (!rosterHas(this.roster, memberKey) || memberKey === this.myKey) throw new Error(\"Not a member\");\n await this.commit(\"role\", { s: memberKey }, now);\n });\n }\n rotate(now = Date.now()): Promise<void> { return this.serialize(async () => { this.requireAdmin(); await this.commit(\"rotate\", {}, now); }); }\n\n /** A new entry key (the old link reaches nobody), or none (`off`): a `link` commit every member follows. */\n replaceLink(off = false, now = Date.now()): Promise<string> {\n return this.serialize(async () => {\n this.requireAdmin();\n const entry = off ? null : identityFromSeedB64(toBase64Url(randomBytes(32)));\n await this.commit(\"link\", { x: entry?.pubKeyZ32 ?? \"\" }, now);\n this.state.entry = { key: entry?.pubKeyZ32 ?? \"\", seedB64: entry?.seedB64 ?? \"\" };\n await this.persist();\n // Every member can answer the new link: its seed, sealed to each.\n if (entry) for (const key of this.others) this.hooks.addressed(key, this.entryFrame(key)!);\n this.hooks.changed();\n return this.state.entry.key;\n });\n }\n\n /**\n * Leaves: a signed request any other member commits, and my secrets go now. The admin must\n * hand over the role first (the engine does it for a leaving admin).\n */\n leave(): Promise<CommunityLeaveFrame | null> {\n return this.serialize(async () => {\n if (this.state.status !== \"active\") return null;\n if (this.isAdmin && this.others.length) throw new Error(\"Make someone else the admin before leaving\");\n const frame: CommunityLeaveFrame = { t: \"group-leave\", v: 2, g: this.id, s: this.myKey, ls: toBase64Url(sign(leaveStatement(this.id, this.myKey), this.identity.seed)) };\n this.hooks.broadcast(frame);\n this.out(\"left\", \"You left this group\");\n await this.persist();\n this.hooks.changed();\n return frame;\n });\n }\n\n /** Commits the leave requests I hold whose members are still in: the engine calls this on hubs. */\n commitPendingLeaves(now = Date.now()): Promise<number> {\n return this.serialize(async () => {\n if (!this.isMember) return 0;\n let done = 0;\n for (const request of [...this.state.pendingLeaves]) {\n if (!rosterHas(this.roster, request.s) || request.s === this.myKey || request.s === this.admin) { this.state.pendingLeaves = this.state.pendingLeaves.filter(r => r !== request); continue; }\n try { await this.commit(\"leave\", { s: request.s, ls: request.ls }, now); done++; } catch { /* not allowed right now */ }\n this.state.pendingLeaves = this.state.pendingLeaves.filter(r => r !== request);\n }\n if (done) await this.persist();\n return done;\n });\n }\n\n private out(status: CommunityStatus, reason: string): void {\n this.state.status = status;\n this.state.statusReason = reason;\n this.state.secrets = {};\n this.state.store = [];\n this.state.entry = { key: this.state.entry.key, seedB64: \"\" };\n this.waiting = []; this.waitingBytes = 0; this.pendingCommits.clear(); this.pendingSecrets.clear();\n }\n\n /**\n * `mentions`: places of the text that name members, sealed with it (`m`). Never everyone in a community. They\n * count against the text's 16 KiB, so the box stays within what older apps accept. `reply`: the message it\n * answers (`r`), counted the same way. `forwarded`: a forwarded text's hop count (`fw`, WISP 9xx § Forwards). `card`:\n * a status card, checked by the caller (`sc`, WISP 4xx · Status Cards), counted the same way; the text is its fallback.\n */\n sendText(text: string, nick?: string, now = Date.now(), mentions: readonly GroupMention[] = [], reply?: WireReply, forwarded?: number, card?: StatusCard): Promise<{ id: string } | { error: string }> {\n return this.serialize(async () => {\n if (!this.isMember) return { error: this.state.statusReason ?? \"You are not in this group\" };\n const trimmed = text.trim();\n if (!trimmed) return { error: \"Nothing to send\" };\n const named = validMentions(wireMentions(mentions), trimmed, false);\n const answers = reply && readReply(wireReply(reply), groupReplyAuthor);\n const replyBytes = answers ? utf8Encode(JSON.stringify(answers)).length : 0;\n const hops = readForwarded(forwarded);\n if (utf8Encode(trimmed).length + mentionsBytes(named) + replyBytes + (hops ? 16 : 0) + (card ? statusCardBytes(card) + 8 : 0) > COMMUNITY_LIMITS.textBytes) return { error: \"Message exceeds 16 KiB\" };\n const sent = await this.sendPayload(() => ({ text: trimmed, ...(named.length ? { m: named } : {}), ...(answers ? { r: answers } : {}), ...(hops ? { fw: hops } : {}), ...(card ? { sc: card } : {}) }), nick, now);\n if (\"error\" in sent) return sent;\n await this.hooks.message({ id: sent.id, sender: this.myKey, epoch: sent.frame.e, seq: sent.frame.n, timestamp: now, text: trimmed, ...(named.length ? { mentions: named } : {}), ...(answers ? { reply: answers } : {}), ...(hops ? { forwarded: hops } : {}), ...(card ? { card } : {}) });\n await this.say(sent.frame);\n return { id: sent.id };\n });\n }\n\n /**\n * An application frame to everyone in the group (a note about a payment, a request anyone may pay): sealed to\n * the epoch like text, signed, kept and caught up like text, and handed to the other members' `app` hook\n * instead of their history.\n */\n sendApp(frame: Record<string, unknown>, nick?: string, now = Date.now()): Promise<{ id: string } | { error: string }> {\n return this.serialize(async () => {\n if (!this.isMember) return { error: this.state.statusReason ?? \"You are not in this group\" };\n if (!isObject(frame) || utf8Encode(JSON.stringify(frame)).length > COMMUNITY_LIMITS.appBytes) return { error: \"Too large for the group\" };\n const sent = await this.sendPayload(() => ({ x: frame }), nick, now);\n if (\"error\" in sent) return sent;\n await this.say(sent.frame);\n return { id: sent.id };\n });\n }\n\n /**\n * An edit of one of my messages (WISP 9xx · Group Community § Edits): an application frame like `sendApp`'s, sealed\n * to the current epoch, but with a text's room rather than an application frame's, since it carries a whole text.\n * The nick is left out when the text leaves no room for it.\n */\n sendEdit(edit: { id: string; v: number; ts: number; text: string; mentions?: readonly GroupMention[]; card?: StatusCard }, nick?: string, now = Date.now()): Promise<{ id: string } | { error: string }> {\n return this.serialize(async () => {\n if (!this.isMember) return { error: this.state.statusReason ?? \"You are not in this group\" };\n if (communityMessageAuthor(edit.id) !== this.myKey) return { error: \"Only your own messages can be edited\" };\n if (!(edit.card ? cardEditNumber(edit.v) : validEditNumber(edit.v))) return { error: \"This message was edited too many times\" };\n const text = edit.text.trim();\n if (!validEditText(text)) return { error: text ? \"Message exceeds 16 KiB\" : \"An edit cannot be empty\" };\n const named = validMentions(wireMentions(edit.mentions ?? []), text, false);\n const frame = communityEditFrame({ id: edit.id, e: edit.v, ts: edit.ts, m: text, k: named, ...(edit.card && { sc: edit.card }) });\n // What a message's box holds (`isMessageFrame`): the text's bound and a little more.\n const room = COMMUNITY_LIMITS.textBytes + 256, clean = sanitizeNick(nick);\n const size = (withNick: boolean) => utf8Encode(JSON.stringify(withNick && clean ? { x: frame, nick: clean } : { x: frame })).length;\n if (size(false) > room) return { error: \"Too long to edit in this group\" };\n const sent = await this.sendPayload(() => ({ x: frame }), size(true) <= room ? nick : undefined, now);\n if (\"error\" in sent) return sent;\n await this.say(sent.frame);\n return { id: sent.id };\n });\n }\n\n /**\n * A payload for one member only (`to`), carried by the group like any frame: every hub relays it and every\n * member keeps it for whoever was away, but only `to` can open it, and only I can have made it (see\n * `sealPair`). The group learns that I sent `to` something, and when; not what.\n */\n sendPair(to: string, payload: Record<string, unknown>, nick?: string, now = Date.now()): Promise<{ id: string } | { error: string }> {\n return this.serialize(async () => {\n if (!this.isMember) return { error: this.state.statusReason ?? \"You are not in this group\" };\n if (to === this.myKey || !rosterHas(this.roster, to)) return { error: \"Not a member of this group\" };\n if (!isObject(payload)) return { error: \"Nothing to send\" };\n const plain = utf8Encode(JSON.stringify(payload));\n if (plain.length > COMMUNITY_LIMITS.appBytes) return { error: \"Too large for the group\" };\n const sent = await this.sendPayload(header => ({ p: { to, ...sealPair(this.identity.seed, this.myKey, to, plain, pairAad(header, to)) } }), nick, now);\n if (\"error\" in sent) return sent;\n await this.say(sent.frame);\n return { id: sent.id };\n });\n }\n\n /** Seals, signs and keeps a frame under the current epoch (the caller holds the queue, persists and broadcasts). */\n private async sendPayload(body: (header: Pick<CommunityMessageFrame, \"g\" | \"e\" | \"h\" | \"s\" | \"n\" | \"ts\">) => Record<string, unknown>, nick: string | undefined, now: number): Promise<{ id: string; frame: CommunityMessageFrame } | { error: string }> {\n const h = this.topHash, secret = this.state.secrets[h];\n if (!secret) return { error: \"This epoch's key has not arrived yet. Wait for a member to catch you up.\" };\n if (this.state.seqH !== h) { this.state.seq = 0; this.state.seqH = h; }\n const n = this.state.seq++;\n const header = { g: this.id, e: this.epoch, h: shortHash(h), s: this.myKey, n, ts: now };\n const clean = sanitizeNick(nick);\n const payload = JSON.stringify(clean ? { ...body(header), nick: clean } : body(header));\n const { n: nn, c } = encryptText(epochKeys(fromBase64Url(secret), this.id, this.epoch).message, messageAad(header), payload);\n const unsigned = { ...header, nn, c };\n const frame: CommunityMessageFrame = { t: \"group-msg\", v: 2, ...unsigned, sig: toBase64Url(sign(messageSigned(unsigned), this.identity.seed)) };\n this.markSeen(frame);\n this.keep(frame);\n return { id: communityMessageId(this.myKey, header.e, header.h, n), frame };\n }\n\n /**\n * Saves, then says my frame on every edge. One no edge took (none is up: I just joined, or my app just opened) is\n * remembered until a member it was written for is handed it: I may be behind on the chain without knowing, and the\n * first member I reach may be one let in since, who cannot read it but can carry it (`receiveSync`).\n */\n private async say(frame: CommunityMessageFrame): Promise<void> {\n const inStore = new Set(this.state.store.filter(f => f.s === this.myKey).map(frameKey));\n this.state.unheard = [...(this.state.unheard ?? []).filter(key => inStore.has(key)), frameKey(frame)].slice(-UNHEARD_KEPT);\n await this.persist();\n if (this.hooks.broadcast(frame) === 0) return;\n this.state.unheard = this.state.unheard.filter(key => key !== frameKey(frame));\n this.persistSoon();\n }\n\n private keep(frame: CommunityMessageFrame): void {\n this.state.store.push(frame);\n let bytes = this.state.store.reduce((sum, f) => sum + f.c.length, 0);\n while (this.state.store.length > COMMUNITY_LIMITS.store || bytes > COMMUNITY_LIMITS.storeBytes) bytes -= this.state.store.shift()!.c.length;\n }\n\n /** Where I am and what I have, for the member at the other end of an edge that just opened. */\n syncFrame(): CommunitySyncFrame {\n const have: Record<string, Record<string, number>> = {};\n const recent = new Set(this.state.store.map(f => seenKey(f.e, f.h)));\n for (const [sender, byKey] of Object.entries(this.state.seen)) for (const [key, entry] of Object.entries(byKey)) {\n if (!recent.has(key)) continue;\n (have[sender] ??= {})[key] = entry.high;\n }\n const secrets = this.state.chain.slice(-COMMUNITY_LIMITS.window - 1).map(c => communityCommitHash(c)).filter(h => this.state.secrets[h]).map(shortHash);\n const loc: string[] = [];\n for (let back = 1; back < this.state.chain.length && loc.length < 12; back *= 2) loc.push(communityCommitHash(this.state.chain[this.state.chain.length - 1 - back]));\n return { t: \"group-sync\", v: 2, g: this.id, e: this.epoch, h: this.topHash, loc, have, secrets, mt: groupMetaTag(this.state.meta) };\n }\n\n // -- frames from others --------------------------------------------------------------------------\n\n /**\n * A frame from the edge of member `from` (the author, or a hub relaying it). Returns whether it\n * was new and valid, so a hub knows to relay it on.\n */\n handle(from: string, raw: unknown): Promise<boolean> {\n return this.serialize(async () => {\n if (!raw || typeof raw !== \"object\") return false;\n const frame = raw as Record<string, unknown>;\n if (frame.g !== this.id || frame.v !== 2) return false;\n // A member who lost its admission still hears commits: the next one may take it in.\n if (this.state.status !== \"active\" && !(this.state.status === \"lost\" && (frame.t === \"group-commit\" || frame.t === \"group-secret\" || frame.t === \"group-secrets\"))) return false;\n switch (frame.t) {\n case \"group-msg\": return this.receiveMessage(from, raw);\n case \"group-commit\": return this.receiveCommit(from, frame.commit);\n case \"group-secret\": {\n if (frame.to !== this.myKey || typeof frame.h !== \"string\") return false;\n const took = this.takeSecret(frame.h, frame.s);\n if (took) { await this.persist(); this.hooks.changed(); await this.replayWaiting(); }\n return took;\n }\n case \"group-secrets\": {\n if (!Array.isArray(frame.secrets) || frame.secrets.length > COMMUNITY_LIMITS.secrets) return false;\n let took = false;\n for (const entry of frame.secrets as { h?: unknown; s?: unknown }[]) if (entry && typeof entry.h === \"string\") took = this.takeSecret(entry.h, entry.s) || took;\n if (took) { await this.persist(); this.hooks.changed(); await this.replayWaiting(); }\n return false;\n }\n case \"group-sync\": await this.receiveSync(from, frame as unknown as CommunitySyncFrame); return false;\n case \"group-meta\": return this.receiveMeta(from, raw);\n case \"group-leave\": return this.receiveLeave(frame);\n case \"group-entry\": {\n if (frame.to !== this.myKey || !isSealed(frame.s) || frame.x !== this.currentEntryKey()) return false;\n const seed = openSecret(this.identity.seed, this.myKey, frame.s, entryAad(this.id, this.myKey));\n return !!seed && this.takeEntrySeed(toBase64Url(seed));\n }\n default: return false;\n }\n });\n }\n\n private receiveLeave(frame: Record<string, unknown>): boolean {\n const s = frame.s, ls = frame.ls;\n if (typeof s !== \"string\" || !MEMBER_KEY.test(s) || typeof ls !== \"string\" || !SIG.test(ls) || !rosterHas(this.roster, s)) return false;\n if (this.state.pendingLeaves.some(r => r.s === s)) return false;\n if (!verify(fromBase64Url(ls), leaveStatement(this.id, s), publicKeyFromZ32(s))) return false;\n this.state.pendingLeaves = [...this.state.pendingLeaves, { s, ls }].slice(-COMMUNITY_LIMITS.pendingLeaves);\n this.persistSoon();\n return true;\n }\n\n private async receiveMessage(from: string, raw: unknown): Promise<boolean> {\n if (!isMessageFrame(raw) || raw.s === this.myKey) return false;\n // Signed by its author before anything else, so a frame no member wrote never takes a place among those waiting.\n if (!verify(fromBase64Url(raw.sig), messageSigned(raw), publicKeyFromZ32(raw.s))) return false;\n // Someone the chain took out is heard no more, even for an epoch it was in (whose secret it still holds).\n if (this.wasRemoved(raw.s)) return false;\n const found = this.commitByShort(raw.e, raw.h);\n if (!found) { this.park(from, raw); return false; }\n const roster = this.rosterOf(found.hash)!;\n // Not from a member of that epoch: nothing to read, nothing to relay.\n if (!rosterHas(roster, raw.s)) return false;\n if (this.isDuplicate(raw)) return false;\n // From before I was let in: nothing for me to read, but its author may have reached nobody else yet (it wrote\n // before it heard of me, with no edge up, and I am its hub). Kept and passed on like any frame, never opened, so\n // the members it was written for get it; the store hands it to those only.\n if (!rosterHas(roster, this.myKey)) {\n this.markSeen(raw);\n this.keep(raw);\n this.persistSoon();\n return true;\n }\n const secret = this.state.secrets[found.hash];\n if (!secret) { this.park(from, raw); return true; }\n const payload = decryptText(epochKeys(fromBase64Url(secret), this.id, raw.e).message, messageAad(raw), raw.nn, raw.c);\n if (payload === null) return false;\n let parsed: { text?: unknown; nick?: unknown; x?: unknown; p?: unknown; m?: unknown; r?: unknown; fw?: unknown; sc?: unknown };\n try { parsed = JSON.parse(payload) as typeof parsed; } catch { return false; }\n if (!isObject(parsed)) return false;\n const text = typeof parsed.text === \"string\" ? parsed.text.slice(0, COMMUNITY_LIMITS.textBytes) : undefined;\n // Exactly one of: text, an application frame for the group, a payload for one member.\n if ([text !== undefined, isObject(parsed.x), isObject(parsed.p)].filter(Boolean).length !== 1) return false;\n const nick = typeof parsed.nick === \"string\" ? sanitizeNick(parsed.nick) : undefined;\n if (nick && this.state.nicks[raw.s] !== nick) { this.state.nicks[raw.s] = nick; this.hooks.changed(); }\n const id = communityMessageId(raw.s, raw.e, raw.h, raw.n);\n if (text !== undefined) {\n const mentions = validMentions(parsed.m, text, false);\n // A reply that does not hold is left out, never the text.\n const reply = parsed.r === undefined ? undefined : readReply(parsed.r, groupReplyAuthor);\n const forwarded = readForwarded(parsed.fw);\n // A card that does not hold is left out: the text, its fallback, shows.\n const card = parsed.sc === undefined ? undefined : readStatusCard(parsed.sc);\n await this.hooks.message({ id, sender: raw.s, epoch: raw.e, seq: raw.n, timestamp: raw.ts, text, ...(mentions.length ? { mentions } : {}), ...(reply ? { reply } : {}), ...(forwarded ? { forwarded } : {}), ...(card ? { card } : {}) });\n }\n else if (isObject(parsed.x)) await this.hooks.app?.({ id, sender: raw.s, epoch: raw.e, timestamp: raw.ts, frame: parsed.x });\n else {\n // Someone else's is carried and kept for them, never opened; mine opens only if its sender sealed it to me here.\n const box = parsed.p as Record<string, unknown>;\n if (!isPairBox(box)) return false;\n if (box.to === this.myKey) {\n const plain = openPair(this.identity.seed, this.myKey, raw.s, box, pairAad(raw, this.myKey));\n let opened: unknown = null;\n try { opened = plain && plain.length <= COMMUNITY_LIMITS.appBytes ? JSON.parse(utf8Decode(plain)) : null; } catch { /* not JSON: dropped */ }\n if (isObject(opened)) await this.hooks.pair?.({ id, sender: raw.s, epoch: raw.e, timestamp: raw.ts, payload: opened });\n }\n }\n this.markSeen(raw);\n this.keep(raw);\n // The message itself is stored already; what changed here (seen, store) is saved in a batch.\n this.persistSoon();\n return true;\n }\n\n private isDuplicate(f: CommunityMessageFrame): boolean { return seenIn(this.state.seen, f); }\n private markSeen(f: CommunityMessageFrame): void {\n const bySender = (this.state.seen[f.s] ??= {});\n const entry = (bySender[seenKey(f.e, f.h)] ??= { high: -1, window: [] });\n if (f.n > entry.high) { if (entry.high >= 0) entry.window.push(entry.high); entry.high = f.n; }\n else entry.window.push(f.n);\n entry.window = entry.window.filter(n => n > entry.high - COMMUNITY_LIMITS.replay);\n for (const key of Object.keys(bySender)) if (Number(key.split(\":\")[0]) < this.epoch - COMMUNITY_LIMITS.window) delete bySender[key];\n if (!Object.keys(bySender).length) delete this.state.seen[f.s];\n }\n\n private park(from: string, frame: CommunityMessageFrame): void {\n if (frame.e > this.epoch + COMMUNITY_LIMITS.window) return;\n if (this.waiting.some(w => w.frame.s === frame.s && w.frame.e === frame.e && w.frame.h === frame.h && w.frame.n === frame.n)) return;\n this.waiting.push({ from, frame });\n this.waitingBytes += frame.c.length;\n while (this.waiting.length > COMMUNITY_LIMITS.waiting || this.waitingBytes > COMMUNITY_LIMITS.waitingBytes) this.waitingBytes -= this.waiting.shift()!.frame.c.length;\n this.ask(from);\n }\n\n /** Something is missing here: the current epoch's secret, or the epoch of frames waiting. */\n get needsCatchUp(): boolean { return this.isMember && (!this.state.secrets[this.topHash] || this.waiting.length > 0 || this.pendingCommits.size > 0); }\n /** Asks a member I have an edge to for what I lack (the engine calls it now and then while `needsCatchUp`). */\n catchUp(to: string): void { this.ask(to); }\n\n private ask(from: string): void {\n const now = this.hooks.clock?.() ?? Date.now();\n if (now - (this.asked.get(from) ?? 0) < 5_000) return;\n this.asked.set(from, now);\n this.hooks.direct(from, this.syncFrame());\n }\n\n private async replayWaiting(): Promise<void> {\n await this.metaFollowsChain();\n const ready = this.waiting.filter(w => { const found = this.commitByShort(w.frame.e, w.frame.h); return !!found && !!this.state.secrets[found.hash]; });\n if (!ready.length) return;\n this.waiting = this.waiting.filter(w => !ready.includes(w));\n this.waitingBytes = this.waiting.reduce((sum, w) => sum + w.frame.c.length, 0);\n for (const { from, frame } of ready) await this.receiveMessage(from, frame);\n }\n\n private async receiveSync(from: string, frame: CommunitySyncFrame): Promise<void> {\n if (!this.isRecentMember(from) || !Number.isSafeInteger(frame.e) || typeof frame.h !== \"string\") return;\n let answered = this.syncsAnswered.get(from);\n if (!answered) this.syncsAnswered.set(from, answered = new RateWindow(COMMUNITY_LIMITS.syncAnswers, COMMUNITY_LIMITS.syncWindowMs, () => this.hooks.clock?.() ?? Date.now()));\n if (!answered.take()) return;\n const theirs = this.known.get(frame.h);\n let start: number;\n if (theirs && this.mainIndex.has(frame.h)) start = this.mainIndex.get(frame.h)! + 1;\n else if (theirs) {\n // On a branch I did not follow: mine from where they part.\n const path = this.pathToMain(frame.h);\n start = path ? this.mainIndex.get(this.known.get(path[0])!.p)! + 1 : this.state.chain.length;\n } else {\n // Unknown to me: they may be ahead, or on a branch I never saw. From where our branches part (their\n // locator says), or my recent commits; my sync makes them send theirs.\n const common = (Array.isArray(frame.loc) ? frame.loc : []).find(x => typeof x === \"string\" && this.mainIndex.has(x));\n start = common !== undefined ? this.mainIndex.get(common)! + 1 : Math.max(0, this.state.chain.length - COMMUNITY_LIMITS.side);\n this.ask(from);\n }\n for (let i = start; i < this.state.chain.length; i++) {\n const commit = this.state.chain[i], h = communityCommitHash(commit);\n this.hooks.direct(from, { t: \"group-commit\", v: 2, g: this.id, commit });\n const secret = this.state.secrets[h];\n if (secret && rosterHas(this.rosterAt(h) ?? [], from) && FRESH.has(commit.k))\n this.hooks.direct(from, { t: \"group-secret\", v: 2, g: this.id, to: from, h, s: sealSecret(from, fromBase64Url(secret), secretAad(this.id, h, from)) });\n }\n // Secrets of the window they were in and lack (derived ones included: they may lack the parent).\n const held = new Set(Array.isArray(frame.secrets) ? frame.secrets.filter(x => typeof x === \"string\") : []);\n const secrets = this.state.chain.slice(-COMMUNITY_LIMITS.window - 1).map(c => communityCommitHash(c))\n .filter(h => this.state.secrets[h] && !held.has(shortHash(h)) && rosterHas(this.rosterAt(h) ?? [], from))\n .map(h => ({ h, s: sealSecret(from, fromBase64Url(this.state.secrets[h]), secretAad(this.id, h, from)) }));\n if (secrets.length) this.hooks.direct(from, { t: \"group-secrets\", v: 2, g: this.id, secrets: secrets.slice(-COMMUNITY_LIMITS.secrets) });\n // What was said while they were away, by anyone, for epochs they were in.\n const have = frame.have && typeof frame.have === \"object\" ? frame.have as Record<string, Record<string, unknown>> : {};\n // A frame sealed on a branch I do not follow (two members committed one leave apart, each with a fresh secret,\n // and that branch lost) goes with that branch's commits and secret: the commits above are my branch's only, and a\n // member who was never on the other one could not place the frame, and kept it waiting for good (2026-10-02).\n const handed = new Set<string>();\n // Mine that no edge took when I said them go to a member let in since too: it cannot read them, and carries them\n // to the members they were written for (a hub passes them on). Anyone else's it could not read stays here.\n const unheard = new Set(this.state.unheard ?? []);\n for (const stored of this.state.store) {\n if (stored.s === from || this.wasRemoved(stored.s)) continue;\n const found = this.commitByShort(stored.e, stored.h);\n if (!found) continue;\n const reads = rosterHas(this.rosterAt(found.hash) ?? [], from), mine = stored.s === this.myKey && unheard.has(frameKey(stored));\n if (!reads && !(mine && rosterHas(this.roster, from))) continue;\n const high = have[stored.s]?.[seenKey(stored.e, stored.h)];\n if (Number.isSafeInteger(high) && (high as number) >= stored.n) continue;\n if (!this.mainIndex.has(found.hash)) this.handSide(from, found.hash, handed);\n this.hooks.direct(from, stored);\n // Handed to a member it was written for: heard.\n if (mine && reads) unheard.delete(frameKey(stored));\n }\n if (unheard.size !== (this.state.unheard?.length ?? 0)) { this.state.unheard = [...unheard]; this.persistSoon(); }\n // The link's seed, so they can answer it too.\n if (rosterHas(this.roster, from)) { const entry = this.entryFrame(from); if (entry) this.hooks.direct(from, entry); }\n // Pending leaves travel too, so whoever commits next can.\n for (const request of this.state.pendingLeaves) this.hooks.direct(from, { t: \"group-leave\", v: 2, g: this.id, ...request });\n // The group's picture, when theirs is older (after the commits and secrets above, which it may need).\n this.offerMeta(from, frame.mt);\n // And where I am, so they can hand me what I lack (asked once in a while, not in a loop).\n this.ask(from);\n }\n\n /** The commits of a branch I do not follow, from where it leaves mine, and the secrets I hold of those `to` was in. */\n private handSide(to: string, tip: string, handed: Set<string>): void {\n for (const h of this.pathToMain(tip) ?? []) {\n if (handed.has(h)) continue;\n handed.add(h);\n this.hooks.direct(to, { t: \"group-commit\", v: 2, g: this.id, commit: this.known.get(h)! });\n const secret = this.state.secrets[h];\n if (secret && rosterHas(this.rosterAt(h) ?? [], to)) this.hooks.direct(to, { t: \"group-secret\", v: 2, g: this.id, to, h, s: sealSecret(to, fromBase64Url(secret), secretAad(this.id, h, to)) });\n }\n }\n\n /** The current entry seed sealed to a member, when I hold it. */\n private entryFrame(to: string): CommunityEntryFrame | null {\n const { key, seedB64 } = this.state.entry;\n if (!key || !seedB64) return null;\n return { t: \"group-entry\", v: 2, g: this.id, to, x: key, s: sealSecret(to, fromBase64Url(seedB64), entryAad(this.id, to)) };\n }\n\n /** A newer entry seed, from a member who holds it (see `replaceLink`): only the one the chain names. */\n takeEntrySeed(seedB64: string): boolean {\n try {\n if (!this.isMember || identityFromSeedB64(seedB64).pubKeyZ32 !== this.currentEntryKey() || this.state.entry.seedB64 === seedB64) return false;\n } catch { return false; }\n this.state.entry = { key: this.currentEntryKey(), seedB64 };\n void this.persist();\n this.hooks.changed();\n return true;\n }\n /** The entry key the main branch names now. */\n currentEntryKey(): string {\n for (let i = this.state.chain.length - 1; i >= 0; i--) if (this.state.chain[i].x !== undefined) return this.state.chain[i].x!;\n return \"\";\n }\n\n setNick(key: string, nick: string | undefined): Promise<void> {\n return this.serialize(async () => {\n const clean = sanitizeNick(nick);\n if ((this.state.nicks[key] ?? undefined) === clean) return;\n if (clean) this.state.nicks[key] = clean; else delete this.state.nicks[key];\n await this.persist();\n this.hooks.changed();\n });\n }\n\n // -- metadata (WISP 9xx § Metadata) ------------------------------------------------------------\n\n /** Sets (or, with null, removes) the group's picture: only the admin, signed under the current commit. */\n setPicture(picture: string | null, now = Date.now()): Promise<void> {\n return this.serialize(async () => {\n this.requireMember();\n if (!this.isAdmin) throw new Error(\"Only the admin can change the group's picture\");\n await this.publishMeta(encodeGroupMetaBody({ ...groupMetaBody(this.state.meta), pic: picture ?? undefined }), now);\n });\n }\n\n /** Renames the group: only the admin, signed under the current commit, beside the picture. */\n rename(name: string, now = Date.now()): Promise<void> {\n return this.serialize(async () => {\n this.requireMember();\n if (!this.isAdmin) throw new Error(\"Only the admin can rename the group\");\n const body = encodeGroupMetaBody({ ...groupMetaBody(this.state.meta), name });\n if (body !== this.state.meta?.body) await this.publishMeta(body, now);\n });\n }\n\n /** Signs a body under my current commit, keeps it and sends it to everyone (hubs relay it). Inside `serialize`. */\n private async publishMeta(body: string, now: number): Promise<void> {\n const before = this.state.meta;\n const meta = signGroupMeta({ g: this.id, e: this.epoch, h: this.topHash, r: nextGroupMetaRevision(before, this.epoch), ts: now }, body, this.identity.seed, this.myKey);\n this.state.meta = meta;\n await this.persist();\n const frame = this.metaFrame();\n if (frame) this.hooks.broadcast(frame);\n const change = groupMetaChange(before, meta, this.state.name);\n if (change) this.hooks.metaChanged?.(this.myKey, change, meta.ts);\n this.hooks.changed();\n }\n\n /** My statement sealed under my current epoch, when I hold its secret. */\n private metaFrame(): GroupMetaFrame | null {\n const meta = this.state.meta, secret = this.state.secrets[this.topHash];\n return meta && secret ? wrapGroupMeta(meta, this.topHash, epochKeys(fromBase64Url(secret), this.id, this.epoch).message, true) : null;\n }\n\n /** My statement, to a member whose sync says it holds an older one (or none); nothing to an app that says nothing. */\n private offerMeta(to: string, theirTag: unknown): void {\n const meta = this.state.meta, theirs = parseGroupMetaTag(theirTag);\n if (!meta || theirs === null || !groupMetaNewer(meta, theirs) || !this.isMember || !rosterHas(this.roster, to)) return;\n const frame = this.metaFrame();\n if (frame) this.hooks.direct(to, frame);\n }\n\n /**\n * A metadata statement (from its author's edge or a hub's): kept when it is newer than mine, its\n * commit is on my main branch, its signer was the admin after that commit and is the admin now,\n * its signature holds, and it opens under the epoch it names into metadata Ghostly shows. Returns\n * whether it was new, so a hub relays it. One naming what I do not have yet waits for the chain.\n */\n private async receiveMeta(from: string, raw: unknown): Promise<boolean> {\n const frame = parseGroupMetaFrame(raw);\n if (!frame || frame.statement.g !== this.id || typeof frame.k !== \"string\" || !this.isRecentMember(from)) return false;\n const s = frame.statement;\n if (!groupMetaNewer(s, this.state.meta)) return false;\n const at = this.mainIndex.get(s.h);\n if (at === undefined) {\n if (s.e >= this.epoch - COMMUNITY_LIMITS.window) { this.pendingMeta = { from, frame: raw }; this.ask(from); }\n return false;\n }\n if (at !== s.e || rosterAdmin(this.rosterAt(s.h) ?? []) !== s.by || this.admin !== s.by || !verifyGroupMetaSignature(s)) return false;\n // The epoch it is sealed under: a commit I know and hold the secret of, or one that may still come.\n const secret = this.state.secrets[frame.k], sealedUnder = this.known.get(frame.k);\n if (!secret || !sealedUnder) { this.pendingMeta = { from, frame: raw }; this.ask(from); return false; }\n const opened = openGroupMeta(frame, epochKeys(fromBase64Url(secret), this.id, sealedUnder.e).message);\n if (!opened) return false;\n const before = this.state.meta;\n this.state.meta = opened.meta;\n await this.persist();\n // What the group looked like when I got in is no change: the first statement I take, signed under a commit before\n // mine, makes no line, nor does a new admin's signing again the name the welcome gave me.\n const change = !before && !rosterHas(this.rosterAt(s.h) ?? [], this.myKey) ? null : groupMetaChange(before, opened.meta, this.state.name);\n if (change) this.hooks.metaChanged?.(s.by, change, opened.meta.ts);\n this.hooks.changed();\n return true;\n }\n\n /** After the chain or my secrets moved: the waiting statement, and, if I became the admin, the name and picture signed again as mine. */\n private async metaFollowsChain(): Promise<void> {\n const pending = this.pendingMeta;\n this.pendingMeta = undefined;\n if (pending && await this.receiveMeta(pending.from, pending.frame)) this.hooks.relay?.(pending.frame as GroupMetaFrame);\n // Joiners accept only the current admin's statement: a new admin signs the picture again.\n if (this.isAdmin && this.state.meta && this.state.meta.by !== this.myKey) await this.publishMeta(this.state.meta.body, this.hooks.clock?.() ?? Date.now());\n }\n\n private async fork(reason: string): Promise<void> {\n if (this.state.status !== \"active\") return;\n this.state.status = \"forked\";\n this.state.statusReason = `${reason}. Membership changes are halted; the admin must re-form the group.`;\n await this.persist();\n this.hooks.changed();\n }\n}\n\ninterface Branch { tip: string; fork: number; path: string[] }\n\nexport function signCommunity(commit: Omit<CommunityCommit, \"sig\">, seed: Uint8Array): CommunityCommit {\n return { ...commit, sig: toBase64Url(sign(utf8Encode(tuple(commit)), seed)) };\n}\n\n","import { fromBase64Url } from \"./bytes\";\nimport { decryptText, encryptText, epochKeys } from \"./groupCrypto\";\nimport { GROUP_ID } from \"./groupCommits\";\nimport { TYPING_FRAME, TypingReceiver, type TypingActivity, type TypingFrame } from \"./pairedTyping\";\n\n/**\n * The typing indicator of a private group (WISP 9xx · Group Mesh § Typing): \"Ana is typing\", said on the group's\n * edges only, the way a 1:1 chat says it on its session (WISP 401 § Typing), with the same kinds, limits and wording.\n *\n * {\"t\":\"group-typing\",\"g\":\"<group>\",\"e\":3,\"n\":\"<nonce>\",\"c\":\"<sealed>\"}\n *\n * `c` is the 1:1 frame's word (`{\"s\":\"start\",\"kind\":\"recording\"}`, `{\"s\":\"stop\"}`) sealed under the message key of\n * epoch `e`, bound to the group, the epoch and the sender (the member the edge is pinned to): a frame another member\n * replays on its own edge does not open. It is presence, not a message: no id, no sequence number, never stored,\n * never handed on in a catch-up, never in the chain, never passed on by a hub. Older apps drop the frame.\n *\n * Community groups do not carry it yet: everything a community carries is a stored message that hubs relay and\n * members hand on in catch-ups, and typing is not one.\n */\n\nexport const GROUP_TYPING_FRAME = \"group-typing\";\n\nexport interface GroupTypingFrame { t: typeof GROUP_TYPING_FRAME; g: string; e: number; n: string; c: string }\n\n/** A sealed word is a few dozen bytes; anything much longer is not one and is dropped before it is opened. */\nconst MAX_BOX = 512;\n\nconst typingAad = (groupId: string, epoch: number, sender: string) => JSON.stringify([\"ghostly-group typing\", groupId, epoch, sender]);\n\n/** A 1:1 typing frame (from a `TypingSender`, one per group), sealed for the group under the epoch's secret (base64url). */\nexport function sealGroupTyping(groupId: string, epoch: number, secretB64: string, sender: string, word: TypingFrame): GroupTypingFrame {\n const { t: _t, ...inner } = word;\n const { n, c } = encryptText(epochKeys(fromBase64Url(secretB64), groupId, epoch).message, typingAad(groupId, epoch, sender), JSON.stringify(inner));\n return { t: GROUP_TYPING_FRAME, g: groupId, e: epoch, n, c };\n}\n\n/** The epoch a `group-typing` frame says it is sealed under, or null when it is not one for this group. */\nexport function groupTypingEpoch(raw: unknown, groupId: string): number | null {\n if (!raw || typeof raw !== \"object\") return null;\n const f = raw as Record<string, unknown>;\n if (f.t !== GROUP_TYPING_FRAME || f.g !== groupId || !GROUP_ID.test(groupId)) return null;\n if (!Number.isSafeInteger(f.e) || (f.e as number) < 0 || typeof f.n !== \"string\" || typeof f.c !== \"string\" || f.c.length > MAX_BOX) return null;\n return f.e as number;\n}\n\n/**\n * The word inside a `group-typing` frame from `sender`, as a 1:1 typing frame (`TypingReceiver` reads it, kind and\n * status checked there), or null when it does not open under that epoch's secret or is not a word.\n */\nexport function openGroupTyping(raw: GroupTypingFrame, secretB64: string, sender: string): TypingFrame | null {\n const plain = decryptText(epochKeys(fromBase64Url(secretB64), raw.g, raw.e).message, typingAad(raw.g, raw.e, sender), raw.n, raw.c);\n if (plain === null) return null;\n try {\n const inner = JSON.parse(plain) as Record<string, unknown>;\n if (!inner || typeof inner !== \"object\" || (inner.s !== \"start\" && inner.s !== \"stop\")) return null;\n return { t: TYPING_FRAME, s: inner.s, ...(typeof inner.kind === \"string\" ? { kind: inner.kind as TypingFrame[\"kind\"] } : {}), ...(typeof inner.status === \"string\" ? { status: inner.status } : {}) };\n } catch { return null; }\n}\n\n/** Who is typing in one group: member key → what they are doing, in the order they started. */\nexport type GroupTyping = { member: string; activity: TypingActivity }[];\n\n/**\n * The members' words in one group: one `TypingReceiver` per member, so each has the 1:1 timeout (6 s without a new\n * `start`) and its own rate limit (a member flooding frames is held where it was, and nobody else is). The caller\n * says who is a member: a frame from anyone else never reaches it. `onChange` hears every change.\n */\nexport class GroupTypingBoard {\n private readonly members = new Map<string, TypingReceiver>();\n\n constructor(private readonly onChange: () => void, private readonly now: () => number = Date.now) {}\n\n /** A word from `member`, already opened. False when it was dropped (malformed, or over the member's limit). */\n receive(member: string, word: Record<string, unknown>): boolean {\n let receiver = this.members.get(member);\n if (!receiver) this.members.set(member, (receiver = new TypingReceiver(() => this.onChange(), this.now)));\n return receiver.receive(word);\n }\n\n /** A message from `member` arrived, or it left the group: it is not typing any more. */\n clear(member: string): void { this.members.get(member)?.clear(); }\n\n /** Nobody is typing: the group was left, or this app stopped. */\n clearAll(): void { for (const receiver of this.members.values()) receiver.clear(); this.members.clear(); }\n\n /** Who is typing now, among `members` when given (someone the roster took out is not shown). */\n typing(members?: readonly string[]): GroupTyping {\n const out: GroupTyping = [];\n for (const [member, receiver] of this.members) {\n const activity = receiver.peerActivity;\n if (activity && (!members || members.includes(member))) out.push({ member, activity });\n }\n return out;\n }\n}\n"],"mappings":"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"}