@ractive-ch/tab-mcp 0.6.0 → 0.6.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.
package/testing.js.map CHANGED
@@ -1,10 +1,34 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../src/store/conformance.ts"],
3
+ "sources": ["../src/ip.ts", "../src/log.ts", "../src/address.ts", "../src/config.ts", "../src/exchange.ts", "../src/icons.ts", "../src/jwt.ts", "../src/identity.ts", "../src/protocol.ts", "../src/notopen.ts", "../src/oauth/server.ts", "../src/ratelimit.ts", "../src/store/memory.ts", "../src/tokens.ts", "../src/oauth/domain-cache.ts", "../src/oauth/cimd.ts", "../src/oauth/client-auth.ts", "../src/oauth/html.ts", "../src/oauth/oidc.ts", "../src/oauth/pairing.ts", "../src/session.ts", "../src/store/sqlite.ts", "../src/relay.ts", "../src/store/conformance.ts", "../src/testing.ts"],
4
4
  "sourcesContent": [
5
- "import { afterEach, beforeEach, describe, expect, test } from \"bun:test\";\nimport type { TokenRecord, TokenStore } from \"./types.ts\";\n\n/**\n * The conformance suite every `TokenStore` must pass (DEC-041). Call it from a\n * `bun test` file with a factory that returns a fresh, empty store:\n *\n * ```ts\n * import { tokenStoreConformance } from \"@ractive-ch/tab-mcp/testing\";\n * tokenStoreConformance(\"RedisTokenStore\", async () => new RedisTokenStore(…));\n * ```\n */\nexport function tokenStoreConformance(\n\tname: string,\n\tfactory: () => TokenStore | Promise<TokenStore>,\n): void {\n\tdescribe(`TokenStore conformance: ${name}`, () => {\n\t\tlet store: TokenStore;\n\t\tconst now = 1_800_000_000_000;\n\t\tlet seq = 0;\n\n\t\tconst until = { now, keepUntil: now + 86_400_000 };\n\n\t\tconst record = (over: Partial<TokenRecord> = {}): TokenRecord => {\n\t\t\tseq++;\n\t\t\treturn {\n\t\t\t\thash: `hash-${seq}`,\n\t\t\t\tkind: \"access\",\n\t\t\t\tgrantId: \"g_1\",\n\t\t\t\taddressId: \"addr-a\",\n\t\t\t\tclientId: \"https://client.example/cimd\",\n\t\t\t\tclientName: \"Client\",\n\t\t\t\tscopes: [\"tools\"],\n\t\t\t\tgrantCreatedAt: now - 1000,\n\t\t\t\tissuedAt: now,\n\t\t\t\texpiresAt: now + 3_600_000,\n\t\t\t\t...over,\n\t\t\t};\n\t\t};\n\n\t\tbeforeEach(async () => {\n\t\t\tstore = await factory();\n\t\t});\n\n\t\tafterEach(async () => {\n\t\t\tawait store.close?.();\n\t\t});\n\n\t\ttest(\"put then get returns an equal record\", async () => {\n\t\t\tconst r = record({ scopes: [\"tools\", \"extra\"] });\n\t\t\texpect(await store.put(r)).toBe(true);\n\t\t\texpect(await store.get(r.hash)).toEqual(r);\n\t\t});\n\n\t\ttest(\"get of an unknown hash is undefined\", async () => {\n\t\t\texpect(await store.get(\"nope\")).toBeUndefined();\n\t\t});\n\n\t\ttest(\"put replaces the record with the same hash\", async () => {\n\t\t\tconst r = record();\n\t\t\tawait store.put(r);\n\t\t\tawait store.put({ ...r, clientName: \"Renamed\", expiresAt: now + 5 });\n\t\t\tconst got = await store.get(r.hash);\n\t\t\texpect(got?.clientName).toBe(\"Renamed\");\n\t\t\texpect(got?.expiresAt).toBe(now + 5);\n\t\t});\n\n\t\ttest(\"get returns expired and used records unchanged\", async () => {\n\t\t\tconst expired = record({ expiresAt: now - 1 });\n\t\t\tconst used = record({ kind: \"refresh\", usedAt: now - 10 });\n\t\t\tawait store.put(expired);\n\t\t\tawait store.put(used);\n\t\t\texpect(await store.get(expired.hash)).toEqual(expired);\n\t\t\texpect(await store.get(used.hash)).toEqual(used);\n\t\t});\n\n\t\ttest(\"a record without usedAt comes back without the key\", async () => {\n\t\t\tconst r = record();\n\t\t\tawait store.put(r);\n\t\t\tconst got = await store.get(r.hash);\n\t\t\texpect(got !== undefined && \"usedAt\" in got).toBe(false);\n\t\t});\n\n\t\ttest(\"the subject of an identity-bound grant round-trips (DEC-077)\", async () => {\n\t\t\tconst bound = record({ grantId: \"g_bound\", subject: \"key-of-alice\" });\n\t\t\tconst plain = record({ grantId: \"g_plain\" });\n\t\t\tawait store.put(bound);\n\t\t\tawait store.put(plain);\n\t\t\texpect(await store.get(bound.hash)).toEqual(bound);\n\t\t\tconst got = await store.get(plain.hash);\n\t\t\texpect(got !== undefined && \"subject\" in got).toBe(false);\n\t\t\tconst grants = await store.listGrants(\"addr-a\", now);\n\t\t\texpect(grants.find((g) => g.grantId === \"g_bound\")?.subject).toBe(\n\t\t\t\t\"key-of-alice\",\n\t\t\t);\n\t\t\tconst unbound = grants.find((g) => g.grantId === \"g_plain\");\n\t\t\texpect(unbound !== undefined && \"subject\" in unbound).toBe(false);\n\t\t});\n\n\t\ttest(\"the client authentication binding round-trips (DEC-106)\", async () => {\n\t\t\tconst bound = record({\n\t\t\t\tgrantId: \"g_key\",\n\t\t\t\tkind: \"refresh\",\n\t\t\t\tclientAuth: \"private_key_jwt\",\n\t\t\t});\n\t\t\tconst plain = record({ grantId: \"g_public\", kind: \"refresh\" });\n\t\t\tawait store.put(bound);\n\t\t\tawait store.put(plain);\n\t\t\texpect(await store.get(bound.hash)).toEqual(bound);\n\t\t\tconst got = await store.get(plain.hash);\n\t\t\texpect(got !== undefined && \"clientAuth\" in got).toBe(false);\n\t\t\texpect(await store.markUsed(bound.hash, now, now + 1000)).toBe(true);\n\t\t\texpect((await store.get(bound.hash))?.clientAuth).toBe(\"private_key_jwt\");\n\t\t});\n\n\t\ttest(\"claimNonce, if implemented: once until it expires, then again (DEC-106)\", async () => {\n\t\t\tif (store.claimNonce === undefined) return;\n\t\t\texpect(await store.claimNonce(\"n1\", now + 1000, now)).toBe(true);\n\t\t\texpect(await store.claimNonce(\"n1\", now + 5000, now + 1)).toBe(false);\n\t\t\texpect(await store.claimNonce(\"n2\", now + 1000, now)).toBe(true);\n\t\t\t// Expired, whether or not deleteExpired has run yet.\n\t\t\texpect(await store.claimNonce(\"n1\", now + 9000, now + 1000)).toBe(true);\n\t\t\texpect(await store.claimNonce(\"n1\", now + 9000, now + 2000)).toBe(false);\n\t\t\tawait store.deleteExpired(now + 9000);\n\t\t\texpect(await store.claimNonce(\"n1\", now + 20_000, now + 9000)).toBe(true);\n\t\t});\n\n\t\ttest(\"claimNonce, if implemented: of concurrent calls exactly one wins\", async () => {\n\t\t\tif (store.claimNonce === undefined) return;\n\t\t\tconst claim = store.claimNonce.bind(store);\n\t\t\tconst results = await Promise.all(\n\t\t\t\tArray.from({ length: 10 }, () => claim(\"race\", now + 1000, now)),\n\t\t\t);\n\t\t\texpect(results.filter(Boolean)).toHaveLength(1);\n\t\t});\n\n\t\ttest(\"markUsed succeeds once, then fails\", async () => {\n\t\t\tconst r = record({ kind: \"refresh\" });\n\t\t\tawait store.put(r);\n\t\t\texpect(await store.markUsed(r.hash, now + 1, r.expiresAt)).toBe(true);\n\t\t\texpect(await store.markUsed(r.hash, now + 2, r.expiresAt)).toBe(false);\n\t\t\texpect((await store.get(r.hash))?.usedAt).toBe(now + 1);\n\t\t});\n\n\t\ttest(\"markUsed cuts expiresAt to keepUntil, so deleteExpired drops the used token then\", async () => {\n\t\t\tconst r = record({ kind: \"refresh\", expiresAt: now + 30 * 86_400_000 });\n\t\t\tawait store.put(r);\n\t\t\texpect(await store.markUsed(r.hash, now, now + 86_400_000)).toBe(true);\n\t\t\tconst used = await store.get(r.hash);\n\t\t\texpect(used?.usedAt).toBe(now);\n\t\t\texpect(used?.expiresAt).toBe(now + 86_400_000);\n\t\t\tawait store.deleteExpired(now + 86_400_000 - 1);\n\t\t\texpect(await store.get(r.hash)).toBeDefined();\n\t\t\tawait store.deleteExpired(now + 86_400_000);\n\t\t\texpect(await store.get(r.hash)).toBeUndefined();\n\t\t});\n\n\t\ttest(\"markUsed never extends expiresAt, and a failed call changes nothing\", async () => {\n\t\t\tconst r = record({ kind: \"refresh\", expiresAt: now + 1000 });\n\t\t\tawait store.put(r);\n\t\t\texpect(await store.markUsed(r.hash, now, now + 86_400_000)).toBe(true);\n\t\t\texpect((await store.get(r.hash))?.expiresAt).toBe(now + 1000);\n\t\t\texpect(await store.markUsed(r.hash, now + 1, now + 10)).toBe(false);\n\t\t\tconst got = await store.get(r.hash);\n\t\t\texpect(got?.expiresAt).toBe(now + 1000);\n\t\t\texpect(got?.usedAt).toBe(now);\n\t\t});\n\n\t\ttest(\"markUsed of an unknown hash fails\", async () => {\n\t\t\texpect(await store.markUsed(\"nope\", now, now + 1000)).toBe(false);\n\t\t});\n\n\t\ttest(\"concurrent markUsed calls: exactly one wins\", async () => {\n\t\t\tconst r = record({ kind: \"refresh\" });\n\t\t\tawait store.put(r);\n\t\t\tconst results = await Promise.all(\n\t\t\t\tArray.from({ length: 10 }, (_, i) =>\n\t\t\t\t\tstore.markUsed(r.hash, now + i, r.expiresAt),\n\t\t\t\t),\n\t\t\t);\n\t\t\texpect(results.filter(Boolean)).toHaveLength(1);\n\t\t});\n\n\t\ttest(\"revoke by grant deletes only that grant's tokens\", async () => {\n\t\t\tconst a = record({ grantId: \"g_a\" });\n\t\t\tconst a2 = record({ grantId: \"g_a\", kind: \"refresh\" });\n\t\t\tconst b = record({ grantId: \"g_b\" });\n\t\t\tfor (const r of [a, a2, b]) await store.put(r);\n\t\t\texpect(await store.revoke({ grantId: \"g_a\" }, until)).toBe(2);\n\t\t\texpect(await store.get(a.hash)).toBeUndefined();\n\t\t\texpect(await store.get(a2.hash)).toBeUndefined();\n\t\t\texpect(await store.get(b.hash)).toEqual(b);\n\t\t});\n\n\t\ttest(\"revoke by address deletes only that address's tokens\", async () => {\n\t\t\tconst a = record({ addressId: \"addr-a\", grantId: \"g_a\" });\n\t\t\tconst b = record({ addressId: \"addr-b\", grantId: \"g_b\" });\n\t\t\tawait store.put(a);\n\t\t\tawait store.put(b);\n\t\t\texpect(await store.revoke({ addressId: \"addr-a\" }, until)).toBe(1);\n\t\t\texpect(await store.get(a.hash)).toBeUndefined();\n\t\t\texpect(await store.get(b.hash)).toEqual(b);\n\t\t});\n\n\t\ttest(\"revoke of nothing returns 0\", async () => {\n\t\t\texpect(await store.revoke({ grantId: \"none\" }, until)).toBe(0);\n\t\t});\n\n\t\ttest(\"a revoked grant gets no new tokens until its tombstone expires\", async () => {\n\t\t\tawait store.revoke({ grantId: \"g_dead\" }, until);\n\t\t\tconst late = record({ grantId: \"g_dead\", kind: \"refresh\" });\n\t\t\texpect(await store.put(late)).toBe(false);\n\t\t\texpect(await store.get(late.hash)).toBeUndefined();\n\t\t\texpect(await store.put(record({ grantId: \"g_alive\" }))).toBe(true);\n\t\t\tawait store.deleteExpired(until.keepUntil);\n\t\t\texpect(await store.put(record({ grantId: \"g_dead\" }))).toBe(true);\n\t\t});\n\n\t\ttest(\"a revoked address refuses grants created up to the revocation, not later ones\", async () => {\n\t\t\tawait store.revoke({ addressId: \"addr-a\" }, until);\n\t\t\texpect(\n\t\t\t\tawait store.put(record({ grantId: \"g_before\", grantCreatedAt: now })),\n\t\t\t).toBe(false);\n\t\t\texpect(\n\t\t\t\tawait store.put(\n\t\t\t\t\trecord({ grantId: \"g_after\", grantCreatedAt: now + 1 }),\n\t\t\t\t),\n\t\t\t).toBe(true);\n\t\t\texpect(\n\t\t\t\tawait store.put(\n\t\t\t\t\trecord({\n\t\t\t\t\t\tgrantId: \"g_elsewhere\",\n\t\t\t\t\t\taddressId: \"addr-b\",\n\t\t\t\t\t\tgrantCreatedAt: now,\n\t\t\t\t\t}),\n\t\t\t\t),\n\t\t\t).toBe(true);\n\t\t});\n\n\t\ttest(\"a put racing a revocation of its grant never survives it\", async () => {\n\t\t\tfor (let i = 0; i < 20; i++) {\n\t\t\t\tconst r = record({ grantId: `g_race_${i}`, kind: \"refresh\" });\n\t\t\t\tawait Promise.all([\n\t\t\t\t\tstore.put(r),\n\t\t\t\t\tstore.revoke({ grantId: r.grantId }, until),\n\t\t\t\t]);\n\t\t\t\texpect(await store.get(r.hash)).toBeUndefined();\n\t\t\t}\n\t\t});\n\n\t\ttest(\"deleteExpired removes records with expiresAt <= now only\", async () => {\n\t\t\tconst gone = record({ expiresAt: now - 1 });\n\t\t\tconst edge = record({ expiresAt: now });\n\t\t\tconst kept = record({ expiresAt: now + 1 });\n\t\t\tfor (const r of [gone, edge, kept]) await store.put(r);\n\t\t\texpect(await store.deleteExpired(now)).toBe(2);\n\t\t\texpect(await store.get(gone.hash)).toBeUndefined();\n\t\t\texpect(await store.get(edge.hash)).toBeUndefined();\n\t\t\texpect(await store.get(kept.hash)).toEqual(kept);\n\t\t});\n\n\t\ttest(\"listGrants groups live tokens of one address by grant\", async () => {\n\t\t\tawait store.put(\n\t\t\t\trecord({\n\t\t\t\t\tgrantId: \"g_new\",\n\t\t\t\t\tgrantCreatedAt: now - 10,\n\t\t\t\t\tclientName: \"New\",\n\t\t\t\t}),\n\t\t\t);\n\t\t\tawait store.put(\n\t\t\t\trecord({\n\t\t\t\t\tgrantId: \"g_old\",\n\t\t\t\t\tgrantCreatedAt: now - 500,\n\t\t\t\t\tclientName: \"Old\",\n\t\t\t\t\tscopes: [\"tools\"],\n\t\t\t\t}),\n\t\t\t);\n\t\t\tawait store.put(\n\t\t\t\trecord({\n\t\t\t\t\tgrantId: \"g_old\",\n\t\t\t\t\tkind: \"refresh\",\n\t\t\t\t\tgrantCreatedAt: now - 500,\n\t\t\t\t\tclientName: \"Old\",\n\t\t\t\t}),\n\t\t\t);\n\t\t\tawait store.put(record({ grantId: \"g_other\", addressId: \"addr-b\" }));\n\t\t\texpect(await store.listGrants(\"addr-a\", now)).toEqual([\n\t\t\t\t{\n\t\t\t\t\tgrantId: \"g_old\",\n\t\t\t\t\tclientId: \"https://client.example/cimd\",\n\t\t\t\t\tclientName: \"Old\",\n\t\t\t\t\tscopes: [\"tools\"],\n\t\t\t\t\tcreatedAt: now - 500,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tgrantId: \"g_new\",\n\t\t\t\t\tclientId: \"https://client.example/cimd\",\n\t\t\t\t\tclientName: \"New\",\n\t\t\t\t\tscopes: [\"tools\"],\n\t\t\t\t\tcreatedAt: now - 10,\n\t\t\t\t},\n\t\t\t]);\n\t\t});\n\n\t\ttest(\"listGrants leaves out grants without a usable token\", async () => {\n\t\t\tawait store.put(record({ grantId: \"g_expired\", expiresAt: now }));\n\t\t\tawait store.put(\n\t\t\t\trecord({ grantId: \"g_used\", kind: \"refresh\", usedAt: now - 1 }),\n\t\t\t);\n\t\t\tawait store.put(\n\t\t\t\trecord({ grantId: \"g_live\", kind: \"refresh\", expiresAt: now + 1 }),\n\t\t\t);\n\t\t\tconst grants = await store.listGrants(\"addr-a\", now);\n\t\t\texpect(grants.map((g) => g.grantId)).toEqual([\"g_live\"]);\n\t\t});\n\n\t\ttest(\"listGrants of an unknown address is empty\", async () => {\n\t\t\texpect(await store.listGrants(\"nobody\", now)).toEqual([]);\n\t\t});\n\n\t\ttest(\"changing a returned record does not change the store\", async () => {\n\t\t\tconst r = record();\n\t\t\tawait store.put(r);\n\t\t\tconst got = await store.get(r.hash);\n\t\t\tif (got === undefined) throw new Error(\"missing\");\n\t\t\tgot.scopes.push(\"mutated\");\n\t\t\tgot.expiresAt = 0;\n\t\t\texpect(await store.get(r.hash)).toEqual(r);\n\t\t});\n\t});\n}\n"
5
+ "/**\n * IP address parsing and the public-address check used by the CIMD fetcher's\n * SSRF guard (threat model A3) and by log truncation (A9).\n */\n\n/** Parse dotted-quad IPv4 into four octets, or undefined. */\nexport function parseIpv4(ip: string): number[] | undefined {\n\tconst parts = ip.split(\".\");\n\tif (parts.length !== 4) return undefined;\n\tconst octets = parts.map((p) => (/^\\d{1,3}$/.test(p) ? Number(p) : -1));\n\treturn octets.every((o) => o >= 0 && o <= 255) ? octets : undefined;\n}\n\n/** Parse IPv6 (with `::`, an optional zone, an embedded IPv4 tail) into 8 groups. */\nexport function parseIpv6(ip: string): number[] | undefined {\n\tconst address = ip.split(\"%\")[0] ?? \"\";\n\tconst halves = address.split(\"::\");\n\tif (halves.length > 2) return undefined;\n\tconst parseSide = (side: string): number[] | undefined => {\n\t\tif (side === \"\") return [];\n\t\tconst groups: number[] = [];\n\t\tconst items = side.split(\":\");\n\t\tfor (const [i, item] of items.entries()) {\n\t\t\tif (i === items.length - 1 && item.includes(\".\")) {\n\t\t\t\tconst v4 = parseIpv4(item);\n\t\t\t\tif (v4 === undefined) return undefined;\n\t\t\t\tgroups.push(\n\t\t\t\t\t((v4[0] ?? 0) << 8) | (v4[1] ?? 0),\n\t\t\t\t\t((v4[2] ?? 0) << 8) | (v4[3] ?? 0),\n\t\t\t\t);\n\t\t\t} else if (/^[0-9a-fA-F]{1,4}$/.test(item)) {\n\t\t\t\tgroups.push(Number.parseInt(item, 16));\n\t\t\t} else {\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t}\n\t\treturn groups;\n\t};\n\tconst head = parseSide(halves[0] ?? \"\");\n\tconst tail = halves.length === 2 ? parseSide(halves[1] ?? \"\") : [];\n\tif (head === undefined || tail === undefined) return undefined;\n\tif (halves.length === 1) return head.length === 8 ? head : undefined;\n\tconst missing = 8 - head.length - tail.length;\n\tif (missing < 1) return undefined;\n\treturn [...head, ...new Array<number>(missing).fill(0), ...tail];\n}\n\n/** IPv4 ranges that are not globally reachable unicast (RFC 6890 and friends). */\nconst PRIVATE_V4: [number, number, number, number, number][] = [\n\t[0, 0, 0, 0, 8], // \"this network\"\n\t[10, 0, 0, 0, 8], // private\n\t[100, 64, 0, 0, 10], // CGNAT\n\t[127, 0, 0, 0, 8], // loopback\n\t[169, 254, 0, 0, 16], // link-local (cloud metadata lives here)\n\t[172, 16, 0, 0, 12], // private\n\t[192, 0, 0, 0, 24], // IETF protocol assignments\n\t[192, 0, 2, 0, 24], // TEST-NET-1\n\t[192, 88, 99, 0, 24], // 6to4 relay anycast\n\t[192, 168, 0, 0, 16], // private\n\t[198, 18, 0, 0, 15], // benchmarking\n\t[198, 51, 100, 0, 24], // TEST-NET-2\n\t[203, 0, 113, 0, 24], // TEST-NET-3\n\t[224, 0, 0, 0, 4], // multicast\n\t[240, 0, 0, 0, 4], // reserved, broadcast\n];\n\nfunction v4Number(o: number[]): number {\n\treturn (\n\t\t(((o[0] ?? 0) << 24) >>> 0) +\n\t\t((o[1] ?? 0) << 16) +\n\t\t((o[2] ?? 0) << 8) +\n\t\t(o[3] ?? 0)\n\t);\n}\n\nfunction isPublicV4(octets: number[]): boolean {\n\tconst n = v4Number(octets);\n\treturn PRIVATE_V4.every(([a, b, c, d, bits]) => {\n\t\tconst mask = bits === 0 ? 0 : (0xffffffff << (32 - bits)) >>> 0;\n\t\treturn (n & mask) >>> 0 !== (v4Number([a, b, c, d]) & mask) >>> 0;\n\t});\n}\n\nfunction isPublicV6(g: number[]): boolean {\n\tconst [g0 = 0, g1 = 0] = g;\n\t// IPv4-mapped (::ffff:a.b.c.d), IPv4-compatible (::a.b.c.d) and NAT64\n\t// (64:ff9b::a.b.c.d): judge the embedded IPv4 address.\n\tconst embedded = [\n\t\t(g[6] ?? 0) >> 8,\n\t\t(g[6] ?? 0) & 0xff,\n\t\t(g[7] ?? 0) >> 8,\n\t\t(g[7] ?? 0) & 0xff,\n\t];\n\tif (g.slice(0, 5).every((x) => x === 0) && (g[5] === 0xffff || g[5] === 0)) {\n\t\treturn g[5] === 0xffff && isPublicV4(embedded);\n\t}\n\tif (g0 === 0x64 && g1 === 0xff9b && g.slice(2, 6).every((x) => x === 0)) {\n\t\treturn isPublicV4(embedded);\n\t}\n\t// Only global unicast (2000::/3) ...\n\tif ((g0 & 0xe000) !== 0x2000) return false;\n\t// ... without the special-purpose blocks inside it.\n\tif (g0 === 0x2001 && g1 < 0x0200) return false; // 2001::/23 (Teredo, benchmarking, ORCHID)\n\tif (g0 === 0x2001 && g1 === 0x0db8) return false; // documentation\n\tif (g0 === 0x2002) return false; // 6to4 (may embed a private IPv4)\n\tif (g0 === 0x3fff && g1 < 0x1000) return false; // documentation (3fff::/20)\n\treturn true;\n}\n\n/**\n * Whether an IP address (v4 or v6 text) is a globally reachable unicast\n * address. Anything that does not parse counts as not public.\n */\nexport function isPublicIp(ip: string): boolean {\n\tconst v4 = parseIpv4(ip);\n\tif (v4 !== undefined) return isPublicV4(v4);\n\tconst v6 = parseIpv6(ip.replace(/^\\[|\\]$/g, \"\"));\n\tif (v6 !== undefined) return isPublicV6(v6);\n\treturn false;\n}\n\n/** Whether a URL hostname is a loopback name or address. */\nexport function isLoopbackHost(hostname: string): boolean {\n\tconst h = hostname.replace(/^\\[|\\]$/g, \"\").toLowerCase();\n\tif (h === \"localhost\" || h.endsWith(\".localhost\")) return true;\n\tconst v4 = parseIpv4(h);\n\tif (v4 !== undefined) return v4[0] === 127;\n\tconst v6 = parseIpv6(h);\n\treturn v6?.slice(0, 7).every((x) => x === 0) === true && v6[7] === 1;\n}\n\n/**\n * The key per-IP limits count under (threat model A8, DEC-061): an IPv4\n * address as is (/32, since many users share one behind NAT or CGNAT\n * anyway), an IPv6 address by its /64, which one subscriber usually gets\n * whole. IPv4-mapped IPv6 counts as IPv4. Anything else is used verbatim.\n */\nexport function ipPrefixKey(ip: string): string {\n\tconst v4 = parseIpv4(ip);\n\tif (v4 !== undefined) return v4.join(\".\");\n\tconst v6 = parseIpv6(ip.replace(/^\\[|\\]$/g, \"\"));\n\tif (v6 === undefined) return ip;\n\tif (v6.slice(0, 5).every((g) => g === 0) && v6[5] === 0xffff) {\n\t\tconst hi = v6[6] ?? 0;\n\t\tconst lo = v6[7] ?? 0;\n\t\treturn `${hi >> 8}.${hi & 0xff}.${lo >> 8}.${lo & 0xff}`;\n\t}\n\treturn `${v6\n\t\t.slice(0, 4)\n\t\t.map((g) => g.toString(16))\n\t\t.join(\":\")}::/64`;\n}\n",
6
+ "import type { LogLevel } from \"./config.ts\";\nimport { parseIpv4, parseIpv6 } from \"./ip.ts\";\n\n/**\n * Structured JSON-line logging (threat model A9). Callers pass only metadata:\n * never request or response bodies, tool names, arguments, results, tokens,\n * pairing codes or page secrets. There is deliberately no helper that accepts\n * a body.\n */\n\nexport type LogFields = Record<string, string | number | boolean | null>;\n\nexport interface Logger {\n\terror(event: string, fields?: LogFields): void;\n\twarn(event: string, fields?: LogFields): void;\n\tinfo(event: string, fields?: LogFields): void;\n\tdebug(event: string, fields?: LogFields): void;\n}\n\nconst ORDER: Record<LogLevel, number> = {\n\terror: 0,\n\twarn: 1,\n\tinfo: 2,\n\tdebug: 3,\n};\n\n/** A logger that writes one JSON object per line to `sink` (stdout by default). */\nexport function createLogger(\n\tlevel: LogLevel,\n\tsink: (line: string) => void = (line) => {\n\t\tprocess.stdout.write(`${line}\\n`);\n\t},\n): Logger {\n\tconst max = ORDER[level];\n\tconst write = (lvl: LogLevel, event: string, fields?: LogFields) => {\n\t\tif (ORDER[lvl] > max) return;\n\t\tsink(\n\t\t\tJSON.stringify({\n\t\t\t\ttime: new Date().toISOString(),\n\t\t\t\tlevel: lvl,\n\t\t\t\tevent,\n\t\t\t\t...fields,\n\t\t\t}),\n\t\t);\n\t};\n\treturn {\n\t\terror: (e, f) => write(\"error\", e, f),\n\t\twarn: (e, f) => write(\"warn\", e, f),\n\t\tinfo: (e, f) => write(\"info\", e, f),\n\t\tdebug: (e, f) => write(\"debug\", e, f),\n\t};\n}\n\n/**\n * Truncate an IP for logs: IPv4 to /24, IPv6 to /48. IPv4-mapped IPv6\n * (`::ffff:a.b.c.d`) is treated as IPv4. `?` if it does not parse.\n */\nexport function truncateIp(ip: string): string {\n\tconst v4 = parseIpv4(ip);\n\tif (v4 !== undefined) return `${v4.slice(0, 3).join(\".\")}.0/24`;\n\tconst v6 = parseIpv6(ip);\n\tif (v6 === undefined) return \"?\";\n\tconst mapped = v6.slice(0, 5).every((g) => g === 0) && v6[5] === 0xffff;\n\tif (mapped) {\n\t\tconst hi = v6[6] ?? 0;\n\t\treturn `${hi >> 8}.${hi & 0xff}.${(v6[7] ?? 0) >> 8}.0/24`;\n\t}\n\treturn `${v6\n\t\t.slice(0, 3)\n\t\t.map((g) => g.toString(16))\n\t\t.join(\":\")}::/48`;\n}\n\n/**\n * An error for a log line: its name only. Messages can carry request data,\n * and a custom store's can hold token hashes or a connection string.\n */\nexport function errorName(error: unknown): string {\n\treturn error instanceof Error ? error.name : typeof error;\n}\n\n/**\n * Defence in depth (DEC-047). The relay catches errors per message and per\n * request; anything that still escapes is logged, never silently fatal:\n * - an unhandled promise rejection is logged and the relay keeps running, so one\n * bad request or tab cannot end every other session;\n * - an uncaught exception is logged and the process exits with 1, because the\n * relay's state may be inconsistent; the supervisor (Docker, systemd) restarts\n * it and AI apps keep their tokens (they live in the store).\n * Only the error's name is logged: messages can carry request data.\n * Returns a function that removes the handlers.\n */\nexport function installCrashLogging(\n\tlogger: Logger,\n\texit: (code: number) => void = (code) => process.exit(code),\n): () => void {\n\tconst onRejection = (error: unknown) => {\n\t\tlogger.error(\"process.unhandled_rejection\", { error: errorName(error) });\n\t};\n\tconst onException = (error: unknown) => {\n\t\tlogger.error(\"process.uncaught_exception\", { error: errorName(error) });\n\t\texit(1);\n\t};\n\tprocess.on(\"unhandledRejection\", onRejection);\n\tprocess.on(\"uncaughtException\", onException);\n\treturn () => {\n\t\tprocess.off(\"unhandledRejection\", onRejection);\n\t\tprocess.off(\"uncaughtException\", onException);\n\t};\n}\n",
7
+ "/**\n * Stable addresses (DEC-027): the page keeps a random secret, the address id is\n * `base32(sha256(secret))`. The relay stores nothing to recognise an address.\n */\n\nconst BASE32_ALPHABET = \"abcdefghijklmnopqrstuvwxyz234567\";\n\n/** RFC 4648 base32, lower case, without padding. */\nexport function base32(bytes: Uint8Array): string {\n\tlet out = \"\";\n\tlet buffer = 0;\n\tlet bits = 0;\n\tfor (const byte of bytes) {\n\t\tbuffer = (buffer << 8) | byte;\n\t\tbits += 8;\n\t\twhile (bits >= 5) {\n\t\t\tbits -= 5;\n\t\t\tout += BASE32_ALPHABET[(buffer >>> bits) & 31];\n\t\t}\n\t\tbuffer &= (1 << bits) - 1;\n\t}\n\tif (bits > 0) out += BASE32_ALPHABET[(buffer << (5 - bits)) & 31];\n\treturn out;\n}\n\n/** 32 random bytes as base64url without padding: 43 characters. */\nconst SECRET_PATTERN = /^[A-Za-z0-9_-]{43}$/;\n\n/** An address id: base32 of a SHA-256 digest, 52 characters. */\nconst ADDRESS_ID_PATTERN = /^[a-z2-7]{52}$/;\n\nexport function isWellFormedSecret(secret: string): boolean {\n\treturn SECRET_PATTERN.test(secret);\n}\n\nexport function isAddressId(id: string): boolean {\n\treturn ADDRESS_ID_PATTERN.test(id);\n}\n\n/** The address id for a page secret: base32(sha256(UTF-8 secret)). */\nexport function addressIdFromSecret(secret: string): string {\n\tconst digest = new Bun.CryptoHasher(\"sha256\").update(secret).digest();\n\treturn base32(\n\t\tnew Uint8Array(digest.buffer, digest.byteOffset, digest.length),\n\t);\n}\n\n/** A new page secret (what the browser library does; used by tests and tools). */\nexport function newSecret(): string {\n\treturn crypto\n\t\t.getRandomValues(new Uint8Array(32))\n\t\t.toBase64({ alphabet: \"base64url\", omitPadding: true });\n}\n\n/** Shortened address id for logs: enough to correlate, too short to use. */\nexport function shortAddress(id: string): string {\n\treturn id.slice(0, 8);\n}\n",
8
+ "/**\n * Relay configuration: defaults (DEC-034), validation, and reading `TAB_MCP_*`\n * environment variables. The table lives in `kb/design/architecture.md`.\n */\n\nexport type TrustProxy = \"none\" | \"x-real-ip\" | \"x-forwarded-for\";\nexport type LogLevel = \"error\" | \"warn\" | \"info\" | \"debug\";\nexport type StoreKind = \"sqlite\" | \"memory\";\n\n/**\n * How a browser at `/authorize` proves it belongs to the tab's user (DEC-042):\n * the 6-digit code shown in the tab (DEC-028), or identity binding at an\n * OpenID Connect provider (DEC-075).\n */\nexport type PairingKind = \"code\" | \"oidc\";\n\n/** Claims the relay may compare as the subject (DEC-079); never mutable ones. */\nexport type SubjectClaim = \"sub\" | \"oid\";\n\n/** The OpenID Connect provider and the tab assertion signer (DEC-079). */\nexport interface OidcConfig {\n\t/** The provider's issuer (discovery at `/.well-known/openid-configuration`). */\n\tissuer: string;\n\t/** The relay's client at the provider. */\n\tclientId: string;\n\t/** Its secret; absent for a public client (PKCE only). */\n\tclientSecret?: string;\n\t/** The ID token claim compared with the tab's subject; default `sub`. */\n\tsubjectClaim?: SubjectClaim;\n\t/** Expected `iss` of the tab assertions (the web app's backend). */\n\tassertionIssuer: string;\n\t/** JWKS of the web app's backend that signs tab assertions. */\n\tassertionJwksUrl: string;\n}\n\n/** The limits a site may set for its own tab sessions (DEC-045). */\nexport interface SiteLimits {\n\tmaxMessageBytes: number;\n\tmaxConcurrentRequests: number;\n\trequestTimeoutMs: number;\n\tkeepAliveMs: number;\n\tidleTimeoutMs: number;\n\tsessionLifetimeMs: number;\n\theartbeatMs: number;\n}\n\n/** One site (page origin) the relay serves, as configured (DEC-045). */\nexport interface SiteConfig {\n\t/** The page origin, e.g. `https://ractive.ch`. */\n\torigin: string;\n\t/** The app name `/authorize` shows; wins over the name the page sends. */\n\tname?: string;\n\t/** Pairing method; default `identity` of the instance (`code`). */\n\tpairing?: PairingKind;\n\t/** Overrides of the instance-wide limits for this site's sessions. */\n\tlimits?: Partial<SiteLimits>;\n}\n\n/** A site with everything filled in. */\nexport interface Site {\n\t/** The origin, or `*` for the open relay's default site. */\n\torigin: string;\n\tname?: string;\n\tpairing: PairingKind;\n\tlimits: SiteLimits;\n}\n\n/**\n * The smallest `maxMessageBytes` the relay accepts: MCP requests need room,\n * and the browser library refuses a session that cannot frame a response\n * chunk (below 896 bytes).\n */\nexport const MIN_MESSAGE_BYTES = 16_384;\n\n/** Longest grace period for a page to (re)connect (DEC-074). */\nexport const MAX_PAGE_WAIT_MS = 60_000;\n\n/** Longest app name (configured or sent by the page). */\nexport const MAX_APP_NAME = 64;\n\nexport interface RelayConfig {\n\t/** External origin, e.g. `https://relay.ractive.ch`. Optional only in development. */\n\tpublicUrl?: string;\n\tport: number;\n\thost: string;\n\t/**\n\t * Page origins allowed to open tab sessions with the instance-wide limits;\n\t * `\"*\"` admits any (open relay). Shorthand for `sites` without overrides.\n\t */\n\tallowedOrigins: string[] | \"*\";\n\t/** Sites with their own name, pairing method or limits (DEC-045). */\n\tsites: SiteConfig[];\n\t/**\n\t * The pairing of sites without their own (`allowedOrigins` included):\n\t * `code` (default, DEC-028) or `oidc` (identity binding, DEC-079).\n\t */\n\tidentity: PairingKind;\n\t/** The provider and assertion signer; required when a site uses `oidc`. */\n\toidc?: OidcConfig;\n\t/** Hosts whose Client ID Metadata Documents are accepted; empty = any. */\n\tclientIdHosts: string[];\n\ttrustProxy: TrustProxy;\n\t/** CORS without credentials on the MCP endpoint (DEC-036). */\n\tcors: boolean;\n\tstore: StoreKind;\n\tsqlitePath: string;\n\tmaxSessions: number;\n\tmaxSessionsPerIp: number;\n\tmaxConcurrentRequests: number;\n\trequestTimeoutMs: number;\n\tkeepAliveMs: number;\n\tidleTimeoutMs: number;\n\tsessionLifetimeMs: number;\n\theartbeatMs: number;\n\thelloTimeoutMs: number;\n\t/** Deadline for receiving an MCP request body (Bun's idle timer does not cover it). */\n\tbodyTimeoutMs: number;\n\t/**\n\t * How long an MCP request waits for its page to (re)connect before the relay\n\t * answers \"the page is not open\" (DEC-074); 0 answers at once.\n\t */\n\tpageWaitMs: number;\n\t/** Limit for MCP request bodies and for messages from the tab. */\n\tmaxMessageBytes: number;\n\taccessTokenTtlMs: number;\n\trefreshTokenTtlMs: number;\n\t/**\n\t * Absolute end of a grant after pairing: refreshes slide by\n\t * `refreshTokenTtlMs` but never beyond this (DEC-062).\n\t */\n\tgrantLifetimeMs: number;\n\t/** How often expired tokens are deleted from the store. */\n\tsweepIntervalMs: number;\n\t/** Per-socket WebSocket send buffer; beyond it sends are dropped (DEC-021). */\n\tbackpressureLimitBytes: number;\n\t/** Bun's HTTP idle timeout in seconds; MCP requests switch it off per request. */\n\thttpIdleTimeoutS: number;\n\tlogLevel: LogLevel;\n\t/** Allows `http:` public URLs and a derived public URL. */\n\tdevelopment: boolean;\n}\n\nexport const DEFAULT_CONFIG: Readonly<RelayConfig> = {\n\tport: 8080,\n\thost: \"0.0.0.0\",\n\tallowedOrigins: [],\n\tsites: [],\n\tidentity: \"code\",\n\tclientIdHosts: [],\n\ttrustProxy: \"none\",\n\tcors: true,\n\tstore: \"sqlite\",\n\tsqlitePath: \"tab-mcp.db\",\n\tmaxSessions: 500,\n\tmaxSessionsPerIp: 5,\n\tmaxConcurrentRequests: 8,\n\trequestTimeoutMs: 300_000,\n\tkeepAliveMs: 20_000,\n\tidleTimeoutMs: 3_600_000,\n\tsessionLifetimeMs: 43_200_000,\n\theartbeatMs: 25_000,\n\thelloTimeoutMs: 10_000,\n\tbodyTimeoutMs: 10_000,\n\tpageWaitMs: 10_000,\n\tmaxMessageBytes: 1_048_576,\n\taccessTokenTtlMs: 3_600_000,\n\trefreshTokenTtlMs: 2_592_000_000,\n\tgrantLifetimeMs: 7_776_000_000,\n\tsweepIntervalMs: 600_000,\n\tbackpressureLimitBytes: 4 * 1_048_576,\n\thttpIdleTimeoutS: 30,\n\tlogLevel: \"info\",\n\tdevelopment: false,\n};\n\nexport class ConfigError extends Error {\n\toverride name = \"ConfigError\";\n}\n\n/** Normalise an origin (`scheme://host[:port]`), or throw. */\nexport function normalizeOrigin(value: string): string {\n\tlet url: URL;\n\ttry {\n\t\turl = new URL(value);\n\t} catch {\n\t\tthrow new ConfigError(`not an origin: ${value}`);\n\t}\n\tif (url.protocol !== \"https:\" && url.protocol !== \"http:\") {\n\t\tthrow new ConfigError(`origin must be http or https: ${value}`);\n\t}\n\tif (\n\t\t(url.pathname !== \"/\" && url.pathname !== \"\") ||\n\t\turl.search !== \"\" ||\n\t\turl.hash !== \"\" ||\n\t\turl.username !== \"\" ||\n\t\turl.password !== \"\"\n\t) {\n\t\tthrow new ConfigError(\n\t\t\t`an origin has no path, query or credentials (browsers send only the origin): ${value}`,\n\t\t);\n\t}\n\treturn url.origin;\n}\n\n/** Fill in defaults and validate. Throws `ConfigError` on bad values. */\nexport function resolveConfig(input: Partial<RelayConfig> = {}): RelayConfig {\n\tconst config: RelayConfig = { ...DEFAULT_CONFIG, ...input };\n\n\tif (config.publicUrl !== undefined) {\n\t\tconst origin = normalizeOrigin(config.publicUrl);\n\t\tif (!origin.startsWith(\"https:\") && !config.development) {\n\t\t\tthrow new ConfigError(\n\t\t\t\t\"TAB_MCP_PUBLIC_URL must be https outside development\",\n\t\t\t);\n\t\t}\n\t\tconfig.publicUrl = origin;\n\t} else if (!config.development) {\n\t\tthrow new ConfigError(\"TAB_MCP_PUBLIC_URL is required\");\n\t}\n\n\tif (config.allowedOrigins !== \"*\") {\n\t\tconfig.allowedOrigins = config.allowedOrigins.map(normalizeOrigin);\n\t}\n\tconfig.sites = config.sites.map((site) => validateSite(site));\n\tconst seen = new Set(\n\t\tconfig.allowedOrigins === \"*\" ? [] : config.allowedOrigins,\n\t);\n\tfor (const site of config.sites) {\n\t\tif (seen.has(site.origin)) {\n\t\t\tthrow new ConfigError(`origin configured twice: ${site.origin}`);\n\t\t}\n\t\tseen.add(site.origin);\n\t}\n\tvalidateIdentity(config);\n\tconfig.clientIdHosts = config.clientIdHosts.map((h) => {\n\t\tconst host = h.trim().toLowerCase();\n\t\tif (!/^[a-z0-9.-]+(:\\d+)?$/.test(host)) {\n\t\t\tthrow new ConfigError(`not a host name: ${h}`);\n\t\t}\n\t\treturn host;\n\t});\n\n\tconst positive: (keyof RelayConfig)[] = [\n\t\t\"maxSessions\",\n\t\t\"maxSessionsPerIp\",\n\t\t\"maxConcurrentRequests\",\n\t\t\"requestTimeoutMs\",\n\t\t\"keepAliveMs\",\n\t\t\"idleTimeoutMs\",\n\t\t\"sessionLifetimeMs\",\n\t\t\"heartbeatMs\",\n\t\t\"helloTimeoutMs\",\n\t\t\"bodyTimeoutMs\",\n\t\t\"maxMessageBytes\",\n\t\t\"accessTokenTtlMs\",\n\t\t\"refreshTokenTtlMs\",\n\t\t\"grantLifetimeMs\",\n\t\t\"sweepIntervalMs\",\n\t\t\"backpressureLimitBytes\",\n\t\t\"httpIdleTimeoutS\",\n\t];\n\tfor (const key of positive) {\n\t\tconst v = config[key];\n\t\tif (typeof v !== \"number\" || !Number.isFinite(v) || v <= 0) {\n\t\t\tthrow new ConfigError(`${key} must be a positive number`);\n\t\t}\n\t}\n\tif (\n\t\ttypeof config.pageWaitMs !== \"number\" ||\n\t\t!Number.isFinite(config.pageWaitMs) ||\n\t\tconfig.pageWaitMs < 0 ||\n\t\tconfig.pageWaitMs > MAX_PAGE_WAIT_MS\n\t) {\n\t\tthrow new ConfigError(\n\t\t\t`pageWaitMs must be 0-${MAX_PAGE_WAIT_MS} (0-${MAX_PAGE_WAIT_MS / 1000} seconds)`,\n\t\t);\n\t}\n\tif (config.maxMessageBytes < MIN_MESSAGE_BYTES) {\n\t\tthrow new ConfigError(\n\t\t\t`maxMessageBytes must be at least ${MIN_MESSAGE_BYTES} (16 KiB)`,\n\t\t);\n\t}\n\tif (config.httpIdleTimeoutS > 255) {\n\t\tthrow new ConfigError(\"httpIdleTimeoutS must be at most 255\");\n\t}\n\tif (\n\t\t!Number.isInteger(config.port) ||\n\t\tconfig.port < 0 ||\n\t\tconfig.port > 65535\n\t) {\n\t\tthrow new ConfigError(\"port must be 0-65535\");\n\t}\n\treturn config;\n}\n\nconst SITE_KEYS = new Set([\"origin\", \"name\", \"pairing\", \"limits\"]);\nconst LIMIT_KEYS: readonly (keyof SiteLimits)[] = [\n\t\"maxMessageBytes\",\n\t\"maxConcurrentRequests\",\n\t\"requestTimeoutMs\",\n\t\"keepAliveMs\",\n\t\"idleTimeoutMs\",\n\t\"sessionLifetimeMs\",\n\t\"heartbeatMs\",\n];\n\n/** An app name: 1-64 characters, no control characters. */\nexport function isAppName(value: unknown): value is string {\n\treturn (\n\t\ttypeof value === \"string\" &&\n\t\tvalue.trim().length > 0 &&\n\t\tvalue.length <= MAX_APP_NAME &&\n\t\t![...value].some((ch) => {\n\t\t\tconst c = ch.codePointAt(0) ?? 0;\n\t\t\treturn c < 0x20 || (c >= 0x7f && c <= 0x9f);\n\t\t})\n\t);\n}\n\nfunction validateSite(input: unknown): SiteConfig {\n\tif (typeof input !== \"object\" || input === null || Array.isArray(input)) {\n\t\tthrow new ConfigError(\"a site must be an object with an origin\");\n\t}\n\tconst site = input as Record<string, unknown>;\n\tfor (const key of Object.keys(site)) {\n\t\tif (!SITE_KEYS.has(key)) throw new ConfigError(`unknown site key: ${key}`);\n\t}\n\tif (typeof site[\"origin\"] !== \"string\") {\n\t\tthrow new ConfigError(\"a site needs an origin\");\n\t}\n\tconst out: SiteConfig = { origin: normalizeOrigin(site[\"origin\"]) };\n\tif (site[\"name\"] !== undefined) {\n\t\tif (!isAppName(site[\"name\"])) {\n\t\t\tthrow new ConfigError(\n\t\t\t\t`site name must be 1-${MAX_APP_NAME} characters: ${out.origin}`,\n\t\t\t);\n\t\t}\n\t\tout.name = site[\"name\"];\n\t}\n\tconst pairing = site[\"pairing\"];\n\tif (pairing !== undefined) {\n\t\tif (pairing !== \"code\" && pairing !== \"oidc\") {\n\t\t\tthrow new ConfigError(`pairing must be \"code\" or \"oidc\": ${out.origin}`);\n\t\t}\n\t\tout.pairing = pairing;\n\t}\n\tconst limits = site[\"limits\"];\n\tif (limits !== undefined) {\n\t\tif (typeof limits !== \"object\" || limits === null) {\n\t\t\tthrow new ConfigError(`site limits must be an object: ${out.origin}`);\n\t\t}\n\t\tconst given = limits as Record<string, unknown>;\n\t\tconst parsed: Partial<SiteLimits> = {};\n\t\tfor (const [key, value] of Object.entries(given)) {\n\t\t\tif (!(LIMIT_KEYS as readonly string[]).includes(key)) {\n\t\t\t\tthrow new ConfigError(`unknown site limit: ${key}`);\n\t\t\t}\n\t\t\tif (typeof value !== \"number\" || !Number.isFinite(value) || value <= 0) {\n\t\t\t\tthrow new ConfigError(`site limit ${key} must be a positive number`);\n\t\t\t}\n\t\t\tparsed[key as keyof SiteLimits] = value;\n\t\t}\n\t\tif (\n\t\t\tparsed.maxMessageBytes !== undefined &&\n\t\t\tparsed.maxMessageBytes < MIN_MESSAGE_BYTES\n\t\t) {\n\t\t\tthrow new ConfigError(\n\t\t\t\t`site limit maxMessageBytes must be at least ${MIN_MESSAGE_BYTES}`,\n\t\t\t);\n\t\t}\n\t\tout.limits = parsed;\n\t}\n\treturn out;\n}\n\n/** An https URL (http only in development) without query or fragment. */\nfunction checkUrl(value: unknown, name: string, development: boolean): string {\n\tif (typeof value !== \"string\" || value === \"\") {\n\t\tthrow new ConfigError(`${name} is required for identity binding`);\n\t}\n\tlet url: URL;\n\ttry {\n\t\turl = new URL(value);\n\t} catch {\n\t\tthrow new ConfigError(`${name} is not a URL: ${value}`);\n\t}\n\tif (url.protocol !== \"https:\" && !(development && url.protocol === \"http:\")) {\n\t\tthrow new ConfigError(`${name} must be https: ${value}`);\n\t}\n\tif (\n\t\turl.search !== \"\" ||\n\t\turl.hash !== \"\" ||\n\t\turl.username !== \"\" ||\n\t\turl.password !== \"\"\n\t) {\n\t\tthrow new ConfigError(\n\t\t\t`${name} has no query, fragment or credentials: ${value}`,\n\t\t);\n\t}\n\treturn value;\n}\n\n/** Whether any site pairs by identity, and the OIDC settings it needs (DEC-079). */\nfunction validateIdentity(config: RelayConfig): void {\n\tif (config.identity !== \"code\" && config.identity !== \"oidc\") {\n\t\tthrow new ConfigError('identity must be \"code\" or \"oidc\"');\n\t}\n\tconst used =\n\t\tconfig.sites.some((s) => (s.pairing ?? config.identity) === \"oidc\") ||\n\t\t(config.identity === \"oidc\" &&\n\t\t\tconfig.allowedOrigins !== \"*\" &&\n\t\t\tconfig.allowedOrigins.length > 0);\n\tif (config.identity === \"oidc\" && config.allowedOrigins === \"*\") {\n\t\tthrow new ConfigError(\n\t\t\t\"TAB_MCP_IDENTITY=oidc cannot be used with TAB_MCP_ALLOWED_ORIGINS=* (one provider cannot vouch for any website's users)\",\n\t\t);\n\t}\n\tconst oidc = config.oidc;\n\tif (oidc === undefined) {\n\t\tif (used) {\n\t\t\tthrow new ConfigError(\n\t\t\t\t\"identity binding (oidc) needs TAB_MCP_OIDC_ISSUER, TAB_MCP_OIDC_CLIENT_ID, TAB_MCP_TAB_ASSERTION_ISSUER and TAB_MCP_TAB_ASSERTION_JWKS_URL\",\n\t\t\t);\n\t\t}\n\t\treturn;\n\t}\n\tconst dev = config.development;\n\tcheckUrl(oidc.issuer, \"TAB_MCP_OIDC_ISSUER\", dev);\n\tcheckUrl(oidc.assertionJwksUrl, \"TAB_MCP_TAB_ASSERTION_JWKS_URL\", dev);\n\tif (typeof oidc.clientId !== \"string\" || oidc.clientId === \"\") {\n\t\tthrow new ConfigError(\n\t\t\t\"TAB_MCP_OIDC_CLIENT_ID is required for identity binding\",\n\t\t);\n\t}\n\tif (\n\t\toidc.clientSecret !== undefined &&\n\t\ttypeof oidc.clientSecret !== \"string\"\n\t) {\n\t\tthrow new ConfigError(\"TAB_MCP_OIDC_CLIENT_SECRET must be a string\");\n\t}\n\tif (typeof oidc.assertionIssuer !== \"string\" || oidc.assertionIssuer === \"\") {\n\t\tthrow new ConfigError(\n\t\t\t\"TAB_MCP_TAB_ASSERTION_ISSUER is required for identity binding\",\n\t\t);\n\t}\n\tconst claim = oidc.subjectClaim ?? \"sub\";\n\tif (claim !== \"sub\" && claim !== \"oid\") {\n\t\tthrow new ConfigError(\n\t\t\t'TAB_MCP_OIDC_SUBJECT_CLAIM must be \"sub\" or \"oid\" (a stable identifier, never email or a user name)',\n\t\t);\n\t}\n\tconfig.oidc = { ...oidc, subjectClaim: claim };\n}\n\n/** The instance-wide limits, the defaults of every site. */\nexport function defaultLimits(config: RelayConfig): SiteLimits {\n\treturn {\n\t\tmaxMessageBytes: config.maxMessageBytes,\n\t\tmaxConcurrentRequests: config.maxConcurrentRequests,\n\t\trequestTimeoutMs: config.requestTimeoutMs,\n\t\tkeepAliveMs: config.keepAliveMs,\n\t\tidleTimeoutMs: config.idleTimeoutMs,\n\t\tsessionLifetimeMs: config.sessionLifetimeMs,\n\t\theartbeatMs: config.heartbeatMs,\n\t};\n}\n\n/**\n * The site for a page origin, or `undefined` if the relay does not serve it.\n * Configured sites win; `allowedOrigins` origins and, on an open relay, any\n * other origin get the instance-wide defaults.\n */\nexport function siteFor(\n\tconfig: RelayConfig,\n\torigin: string | null,\n): Site | undefined {\n\tconst defaults = defaultLimits(config);\n\tconst configured =\n\t\torigin === null ? undefined : config.sites.find((s) => s.origin === origin);\n\tif (configured !== undefined) {\n\t\tconst site: Site = {\n\t\t\torigin: configured.origin,\n\t\t\tpairing: configured.pairing ?? config.identity,\n\t\t\tlimits: { ...defaults, ...configured.limits },\n\t\t};\n\t\tif (configured.name !== undefined) site.name = configured.name;\n\t\treturn site;\n\t}\n\tif (\n\t\tconfig.allowedOrigins === \"*\" ||\n\t\t(origin !== null && config.allowedOrigins.includes(origin))\n\t) {\n\t\treturn {\n\t\t\torigin: config.allowedOrigins === \"*\" ? \"*\" : (origin ?? \"\"),\n\t\t\tpairing: config.allowedOrigins === \"*\" ? \"code\" : config.identity,\n\t\t\tlimits: defaults,\n\t\t};\n\t}\n\treturn undefined;\n}\n\n/** The largest message limit of any site (Bun's server-wide limits). */\nexport function largestMessageBytes(config: RelayConfig): number {\n\treturn Math.max(\n\t\tconfig.maxMessageBytes,\n\t\t...config.sites.map((s) => s.limits?.maxMessageBytes ?? 0),\n\t);\n}\n\ntype Env = Record<string, string | undefined>;\n\nfunction seconds(env: Env, name: string): number | undefined {\n\tconst raw = env[name];\n\tif (raw === undefined || raw === \"\") return undefined;\n\tconst n = Number(raw);\n\tif (!Number.isFinite(n) || n <= 0) {\n\t\tthrow new ConfigError(`${name} must be a positive number of seconds`);\n\t}\n\treturn Math.round(n * 1000);\n}\n\n/** Like `seconds`, but 0 is allowed (it switches the feature off). */\nfunction secondsOrZero(env: Env, name: string): number | undefined {\n\tconst raw = env[name];\n\tif (raw === undefined || raw === \"\") return undefined;\n\tconst n = Number(raw);\n\tif (!Number.isFinite(n) || n < 0) {\n\t\tthrow new ConfigError(`${name} must be 0 or a positive number of seconds`);\n\t}\n\treturn Math.round(n * 1000);\n}\n\nfunction integer(env: Env, name: string): number | undefined {\n\tconst raw = env[name];\n\tif (raw === undefined || raw === \"\") return undefined;\n\tconst n = Number(raw);\n\tif (!Number.isInteger(n) || n < 0) {\n\t\tthrow new ConfigError(`${name} must be a whole number`);\n\t}\n\treturn n;\n}\n\nfunction oneOf<T extends string>(\n\tenv: Env,\n\tname: string,\n\tvalues: readonly T[],\n): T | undefined {\n\tconst raw = env[name];\n\tif (raw === undefined || raw === \"\") return undefined;\n\tif (!(values as readonly string[]).includes(raw)) {\n\t\tthrow new ConfigError(`${name} must be one of ${values.join(\", \")}`);\n\t}\n\treturn raw as T;\n}\n\n/** Read `TAB_MCP_*` variables (and `NODE_ENV`) into a partial config. */\nexport function configFromEnv(env: Env): Partial<RelayConfig> {\n\tconst out: Partial<RelayConfig> = {};\n\tconst set = <K extends keyof RelayConfig>(\n\t\tkey: K,\n\t\tvalue: RelayConfig[K] | undefined,\n\t) => {\n\t\tif (value !== undefined) out[key] = value;\n\t};\n\n\tset(\"development\", env[\"NODE_ENV\"] === \"development\" ? true : undefined);\n\tconst publicUrl = env[\"TAB_MCP_PUBLIC_URL\"];\n\tif (publicUrl !== undefined && publicUrl !== \"\") out.publicUrl = publicUrl;\n\tset(\"port\", integer(env, \"TAB_MCP_PORT\"));\n\tconst host = env[\"TAB_MCP_HOST\"];\n\tif (host !== undefined && host !== \"\") out.host = host;\n\n\tconst origins = env[\"TAB_MCP_ALLOWED_ORIGINS\"];\n\tif (origins !== undefined) {\n\t\tconst trimmed = origins.trim();\n\t\tout.allowedOrigins =\n\t\t\ttrimmed === \"*\"\n\t\t\t\t? \"*\"\n\t\t\t\t: trimmed\n\t\t\t\t\t\t.split(\",\")\n\t\t\t\t\t\t.map((s) => s.trim())\n\t\t\t\t\t\t.filter((s) => s !== \"\");\n\t}\n\n\tconst sites = env[\"TAB_MCP_SITES\"];\n\tif (sites !== undefined && sites.trim() !== \"\") {\n\t\tlet parsed: unknown;\n\t\ttry {\n\t\t\tparsed = JSON.parse(sites);\n\t\t} catch {\n\t\t\tthrow new ConfigError(\"TAB_MCP_SITES must be a JSON array of sites\");\n\t\t}\n\t\tif (!Array.isArray(parsed)) {\n\t\t\tthrow new ConfigError(\"TAB_MCP_SITES must be a JSON array of sites\");\n\t\t}\n\t\tout.sites = parsed as SiteConfig[];\n\t}\n\tconst hosts = env[\"TAB_MCP_CLIENT_ID_HOSTS\"];\n\tif (hosts !== undefined && hosts.trim() !== \"\") {\n\t\tout.clientIdHosts = hosts\n\t\t\t.split(\",\")\n\t\t\t.map((h) => h.trim())\n\t\t\t.filter((h) => h !== \"\");\n\t}\n\n\tset(\"identity\", oneOf(env, \"TAB_MCP_IDENTITY\", [\"code\", \"oidc\"] as const));\n\tconst oidcVars = {\n\t\tissuer: env[\"TAB_MCP_OIDC_ISSUER\"],\n\t\tclientId: env[\"TAB_MCP_OIDC_CLIENT_ID\"],\n\t\tclientSecret: env[\"TAB_MCP_OIDC_CLIENT_SECRET\"],\n\t\tsubjectClaim: env[\"TAB_MCP_OIDC_SUBJECT_CLAIM\"],\n\t\tassertionIssuer: env[\"TAB_MCP_TAB_ASSERTION_ISSUER\"],\n\t\tassertionJwksUrl: env[\"TAB_MCP_TAB_ASSERTION_JWKS_URL\"],\n\t};\n\tif (Object.values(oidcVars).some((v) => v !== undefined && v !== \"\")) {\n\t\tconst oidc: OidcConfig = {\n\t\t\tissuer: oidcVars.issuer ?? \"\",\n\t\t\tclientId: oidcVars.clientId ?? \"\",\n\t\t\tassertionIssuer: oidcVars.assertionIssuer ?? \"\",\n\t\t\tassertionJwksUrl: oidcVars.assertionJwksUrl ?? \"\",\n\t\t};\n\t\tif (oidcVars.clientSecret !== undefined && oidcVars.clientSecret !== \"\") {\n\t\t\toidc.clientSecret = oidcVars.clientSecret;\n\t\t}\n\t\tif (oidcVars.subjectClaim !== undefined && oidcVars.subjectClaim !== \"\") {\n\t\t\toidc.subjectClaim = oidcVars.subjectClaim as SubjectClaim;\n\t\t}\n\t\tout.oidc = oidc;\n\t}\n\tset(\n\t\t\"trustProxy\",\n\t\toneOf(env, \"TAB_MCP_TRUST_PROXY\", [\n\t\t\t\"none\",\n\t\t\t\"x-real-ip\",\n\t\t\t\"x-forwarded-for\",\n\t\t] as const),\n\t);\n\tconst cors = oneOf(env, \"TAB_MCP_CORS\", [\"on\", \"off\"] as const);\n\tif (cors !== undefined) out.cors = cors === \"on\";\n\tset(\"store\", oneOf(env, \"TAB_MCP_STORE\", [\"sqlite\", \"memory\"] as const));\n\tconst sqlitePath = env[\"TAB_MCP_SQLITE_PATH\"];\n\tif (sqlitePath !== undefined && sqlitePath !== \"\")\n\t\tout.sqlitePath = sqlitePath;\n\n\tset(\"maxSessions\", integer(env, \"TAB_MCP_MAX_SESSIONS\"));\n\tset(\"maxSessionsPerIp\", integer(env, \"TAB_MCP_MAX_SESSIONS_PER_IP\"));\n\tset(\"maxConcurrentRequests\", integer(env, \"TAB_MCP_MAX_CONCURRENT_REQUESTS\"));\n\tset(\"requestTimeoutMs\", seconds(env, \"TAB_MCP_REQUEST_TIMEOUT_S\"));\n\tset(\"keepAliveMs\", seconds(env, \"TAB_MCP_KEEPALIVE_S\"));\n\tset(\"idleTimeoutMs\", seconds(env, \"TAB_MCP_IDLE_TIMEOUT_S\"));\n\tset(\"sessionLifetimeMs\", seconds(env, \"TAB_MCP_SESSION_LIFETIME_S\"));\n\tset(\"heartbeatMs\", seconds(env, \"TAB_MCP_HEARTBEAT_S\"));\n\tset(\"bodyTimeoutMs\", seconds(env, \"TAB_MCP_BODY_TIMEOUT_S\"));\n\tset(\"pageWaitMs\", secondsOrZero(env, \"TAB_MCP_PAGE_WAIT_S\"));\n\tset(\"maxMessageBytes\", integer(env, \"TAB_MCP_MAX_MESSAGE_BYTES\"));\n\tset(\"accessTokenTtlMs\", seconds(env, \"TAB_MCP_ACCESS_TOKEN_TTL_S\"));\n\tset(\"refreshTokenTtlMs\", seconds(env, \"TAB_MCP_REFRESH_TOKEN_TTL_S\"));\n\tset(\"grantLifetimeMs\", seconds(env, \"TAB_MCP_GRANT_LIFETIME_S\"));\n\tset(\n\t\t\"logLevel\",\n\t\toneOf(env, \"TAB_MCP_LOG_LEVEL\", [\n\t\t\t\"error\",\n\t\t\t\"warn\",\n\t\t\t\"info\",\n\t\t\t\"debug\",\n\t\t] as const),\n\t);\n\treturn out;\n}\n",
9
+ "import type {\n\tHttpResponseMessage,\n\tHttpResponseStartMessage,\n} from \"./protocol.ts\";\n\n/**\n * One tunnelled MCP request: the AI app's HTTP response, built from the tab's\n * `http.response*` messages, with the request timer, SSE keep-alives (DEC-020)\n * and the relay-started event stream (DEC-039).\n */\n\nexport type RpcId = string | number | null;\n\n/** JSON-RPC error codes the relay itself uses (outside the reserved range). */\nexport const RelayErrorCode = {\n\tpageNotOpen: -31001,\n\ttimeout: -31002,\n\ttabGone: -31003,\n\toverloaded: -31004,\n\tsendFailed: -31005,\n\ttooLarge: -31006,\n\tbadResponse: -31007,\n\tstoreUnavailable: -31008,\n\tsignedOut: -31009,\n\trestarting: -31010,\n} as const;\n\nexport type CancelReason = \"client_closed\" | \"timeout\" | \"error\";\n\n/**\n * What to do when the page goes away mid-request (DEC-074): send the request\n * to the page again once it is back (only while nothing was sent to the AI\n * app), answer with this JSON-RPC message at HTTP 200, or fail with this\n * error (no body and no error: 503, `-31003`).\n */\nexport type PageGoneReply =\n\t| { retry: true }\n\t| { retry: false; body?: string }\n\t| {\n\t\t\tretry: false;\n\t\t\terror: {\n\t\t\t\tstatus: number;\n\t\t\t\tcode: number;\n\t\t\t\tmessage: string;\n\t\t\t\theaders: Record<string, string>;\n\t\t\t};\n\t };\n\nexport interface ExchangeOptions {\n\tid: string;\n\t/** JSON-RPC id of the request; `undefined` for a notification. */\n\trpcId: RpcId | undefined;\n\t/** `subscriptions/listen`: exempt from the request timer. */\n\tisListen: boolean;\n\trequestTimeoutMs: number;\n\tkeepAliveMs: number;\n\t/**\n\t * Limit for the response body the tab sends (not for listen streams), and\n\t * the bound for bytes queued towards an AI app that does not read.\n\t */\n\tmaxBodyBytes: number;\n\t/** Extra headers for every response (CORS). */\n\textraHeaders: Record<string, string>;\n\t/** Tell the tab to stop working on this request. */\n\tcancel: (reason: CancelReason) => void;\n\t/** Called once when the exchange is over. */\n\tonDone: (exchange: Exchange) => void;\n\t/**\n\t * Decides the answer when the page goes away mid-request; `waiting` is true\n\t * while nothing was sent to the AI app. Without it: 503, `-31003`.\n\t */\n\tpageGone?: (waiting: boolean) => PageGoneReply;\n}\n\nconst encoder = new TextEncoder();\n\n/** Statuses that must not carry a body (Fetch \"null body status\", 101/103 excluded by the parser). */\nconst NULL_BODY_STATUSES = new Set([204, 205, 304]);\n\n/** Response headers the relay passes on from the tab. */\nconst PASSED_RESPONSE_HEADERS = new Set([\"content-type\", \"cache-control\"]);\n\nfunction isEventStream(headers: Record<string, string>): boolean {\n\treturn (headers[\"content-type\"] ?? \"\")\n\t\t.toLowerCase()\n\t\t.startsWith(\"text/event-stream\");\n}\n\nfunction endsAtBoundary(text: string): boolean {\n\treturn (\n\t\ttext.endsWith(\"\\n\\n\") || text.endsWith(\"\\r\\n\\r\\n\") || text.endsWith(\"\\r\\r\")\n\t);\n}\n\n/** One SSE `message` event carrying `data` (split into `data:` lines). */\nexport function sseEvent(data: string): string {\n\tconst lines = data.split(/\\r\\n|\\r|\\n/);\n\treturn `event: message\\n${lines.map((l) => `data: ${l}`).join(\"\\n\")}\\n\\n`;\n}\n\nexport function rpcErrorBody(\n\trpcId: RpcId | undefined,\n\tcode: number,\n\tmessage: string,\n\tdata?: unknown,\n): string {\n\tconst error: Record<string, unknown> = { code, message };\n\tif (data !== undefined) error[\"data\"] = data;\n\treturn JSON.stringify({ jsonrpc: \"2.0\", id: rpcId ?? null, error });\n}\n\n/**\n * Where an exchange is. While `waiting` nothing was sent to the AI app.\n * `tab_stream`: the tab's `http.response.start` was passed on. The relay may\n * start the event stream itself (`relay_stream`, DEC-039); the tab's response\n * then becomes events: a tab event stream passes through (`relay_passed`), any\n * other streamed body is buffered and sent as one event (`relay_wrapped`).\n */\ntype State =\n\t| \"waiting\"\n\t| \"tab_stream\"\n\t| \"relay_stream\"\n\t| \"relay_passed\"\n\t| \"relay_wrapped\"\n\t| \"done\";\n\n/** States in which the tab sent `http.response.start`. */\nconst TAB_STARTED: ReadonlySet<State> = new Set([\n\t\"tab_stream\",\n\t\"relay_passed\",\n\t\"relay_wrapped\",\n]);\n\nexport class Exchange {\n\treadonly id: string;\n\treadonly isListen: boolean;\n\treadonly response: Promise<Response>;\n\t/** HTTP status sent to the AI app (for logs); 0 while waiting. */\n\tstatus = 0;\n\t/** How the exchange ended (for logs). */\n\toutcome = \"pending\";\n\t/** Why the tab's response was refused, for the tab's `error` message. */\n\tbadResponseReason: string | undefined;\n\n\treadonly #options: ExchangeOptions;\n\t#resolve!: (response: Response) => void;\n\t#state: State = \"waiting\";\n\t/** The buffered body in `relay_wrapped`. */\n\t#wrapped = \"\";\n\t#controller: ReadableStreamDefaultController<Uint8Array> | undefined;\n\t/** The last write to the AI app ended an SSE event (or nothing was sent). */\n\t#atBoundary = true;\n\t/** Response body bytes received from the tab so far. */\n\t#bodyBytes = 0;\n\t#startTimer: Timer | undefined;\n\t#requestTimer: Timer | undefined;\n\t#keepAliveTimer: Timer | undefined;\n\n\tconstructor(options: ExchangeOptions) {\n\t\tthis.#options = options;\n\t\tthis.id = options.id;\n\t\tthis.isListen = options.isListen;\n\t\tthis.response = new Promise((resolve) => {\n\t\t\tthis.#resolve = resolve;\n\t\t});\n\t\tif (options.rpcId !== undefined) {\n\t\t\tthis.#startTimer = setTimeout(\n\t\t\t\t() => this.#startRelayStream(),\n\t\t\t\toptions.keepAliveMs,\n\t\t\t);\n\t\t}\n\t\tif (!options.isListen) {\n\t\t\tthis.#requestTimer = setTimeout(\n\t\t\t\t() => this.#timeout(),\n\t\t\t\toptions.requestTimeoutMs,\n\t\t\t);\n\t\t}\n\t}\n\n\tget done(): boolean {\n\t\treturn this.#state === \"done\";\n\t}\n\n\t/** A complete response from the tab. */\n\tonResponse(msg: HttpResponseMessage): void {\n\t\tif (TAB_STARTED.has(this.#state)) {\n\t\t\tthis.#badResponse(\"http.response after http.response.start\");\n\t\t\treturn;\n\t\t}\n\t\tif (this.#state === \"waiting\") {\n\t\t\tif (!this.#account(msg.body)) return;\n\t\t\tif (NULL_BODY_STATUSES.has(msg.status) && msg.body !== \"\") {\n\t\t\t\tthis.#badResponse(`status ${msg.status} must not have a body`);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tlet response: Response;\n\t\t\ttry {\n\t\t\t\tresponse = new Response(msg.body === \"\" ? null : msg.body, {\n\t\t\t\t\tstatus: msg.status,\n\t\t\t\t\theaders: this.#headers(msg.headers),\n\t\t\t\t});\n\t\t\t} catch {\n\t\t\t\tthis.#badResponse(\"the response could not be built\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.#respond(response, msg.status);\n\t\t\tthis.#finish(\"ok\");\n\t\t} else if (this.#state === \"relay_stream\") {\n\t\t\tif (!this.#account(msg.body)) return;\n\t\t\tif (msg.body !== \"\") this.#write(sseEvent(msg.body));\n\t\t\tthis.#finish(\"ok\");\n\t\t}\n\t}\n\n\tonStart(msg: HttpResponseStartMessage): void {\n\t\tif (this.#state === \"done\") return;\n\t\tif (TAB_STARTED.has(this.#state)) {\n\t\t\tthis.#badResponse(\"http.response.start sent twice\");\n\t\t\treturn;\n\t\t}\n\t\t// A listen stream has no total limit, so it must be an event stream: any\n\t\t// other body would be buffered or passed on without bound.\n\t\tconst eventStream = isEventStream(msg.headers);\n\t\tif (this.isListen && !eventStream) {\n\t\t\tthis.#badResponse(\"a listen response must be an event stream\");\n\t\t\treturn;\n\t\t}\n\t\tif (this.#state === \"waiting\") {\n\t\t\tif (NULL_BODY_STATUSES.has(msg.status)) {\n\t\t\t\tthis.#badResponse(`status ${msg.status} cannot be streamed`);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tlet headers: Headers;\n\t\t\ttry {\n\t\t\t\theaders = this.#headers(msg.headers);\n\t\t\t} catch {\n\t\t\t\tthis.#badResponse(\"the response headers are invalid\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.#openStream(msg.status, headers, \"tab_stream\");\n\t\t} else {\n\t\t\tthis.#state = eventStream ? \"relay_passed\" : \"relay_wrapped\";\n\t\t}\n\t}\n\n\tonChunk(data: string): void {\n\t\tif (!this.#streaming) return;\n\t\tif (this.#state === \"relay_stream\") {\n\t\t\tthis.#badResponse(\"http.response.chunk before http.response.start\");\n\t\t\treturn;\n\t\t}\n\t\tif (!this.#account(data)) return;\n\t\tif (this.#state === \"relay_wrapped\") this.#wrapped += data;\n\t\telse this.#write(data);\n\t}\n\n\tonEnd(): void {\n\t\tif (!this.#streaming) return;\n\t\tif (this.#state === \"relay_wrapped\" && this.#wrapped !== \"\")\n\t\t\tthis.#write(sseEvent(this.#wrapped));\n\t\tthis.#finish(\"ok\");\n\t}\n\n\t/** The AI app went away: tell the tab, send nothing more. */\n\tclientClosed(): void {\n\t\tif (this.#state === \"done\") return;\n\t\tif (this.#state === \"waiting\")\n\t\t\tthis.#respond(new Response(null, { status: 503 }), 0);\n\t\tthis.#options.cancel(\"client_closed\");\n\t\tthis.#finish(\"client_closed\", false);\n\t}\n\n\t/**\n\t * The page's session ended. With a retry the exchange ends without an\n\t * answer (its `response` never settles; the relay sends the request again),\n\t * else the AI app gets the `pageGone` answer at HTTP 200 (DEC-074).\n\t */\n\ttabGone(): void {\n\t\tif (this.#state === \"done\") return;\n\t\tconst waiting = this.#state === \"waiting\";\n\t\tconst reply = this.#options.pageGone?.(waiting);\n\t\tif (reply?.retry === true && waiting) {\n\t\t\tclearTimeout(this.#startTimer);\n\t\t\tthis.#finish(\"page_gone_retry\");\n\t\t\treturn;\n\t\t}\n\t\tif (reply !== undefined && \"error\" in reply) {\n\t\t\tconst e = reply.error;\n\t\t\tthis.fail(e.status, e.code, e.message, false, e.headers);\n\t\t\treturn;\n\t\t}\n\t\tconst body = reply?.retry === false ? reply.body : undefined;\n\t\tif (body === undefined) {\n\t\t\tthis.fail(\n\t\t\t\t503,\n\t\t\t\tRelayErrorCode.tabGone,\n\t\t\t\t\"The page closed its connection to the relay.\",\n\t\t\t\tfalse,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tif (waiting) {\n\t\t\tthis.#respond(\n\t\t\t\tnew Response(body, {\n\t\t\t\t\tstatus: 200,\n\t\t\t\t\theaders: this.#headers({ \"content-type\": \"application/json\" }),\n\t\t\t\t}),\n\t\t\t\t200,\n\t\t\t);\n\t\t} else if (this.#atBoundary) {\n\t\t\tthis.#write(sseEvent(body));\n\t\t}\n\t\tthis.#finish(\"page_gone\");\n\t}\n\n\t/**\n\t * End the exchange with an error the AI app can read: an HTTP error while\n\t * nothing was sent, else a JSON-RPC error event if the stream is at an\n\t * event boundary.\n\t */\n\tfail(\n\t\tstatus: number,\n\t\tcode: number,\n\t\tmessage: string,\n\t\tcancelTab: boolean,\n\t\theaders: Record<string, string> = {},\n\t): void {\n\t\tif (this.#state === \"done\") return;\n\t\tif (this.#state === \"waiting\") {\n\t\t\tthis.#respond(\n\t\t\t\tthis.#errorResponse(status, code, message, headers),\n\t\t\t\tstatus,\n\t\t\t);\n\t\t} else if (this.#atBoundary && this.#options.rpcId !== undefined) {\n\t\t\tthis.#write(sseEvent(rpcErrorBody(this.#options.rpcId, code, message)));\n\t\t}\n\t\tif (cancelTab) this.#options.cancel(\"error\");\n\t\tthis.#finish(`error_${code}`);\n\t}\n\n\t/**\n\t * The tab sent a response the relay cannot pass on: 502 to the AI app (or an\n\t * error event), cancel the tab's work. `reason` goes to the tab, not the app.\n\t */\n\t#badResponse(reason: string): void {\n\t\tthis.badResponseReason = reason;\n\t\tthis.fail(\n\t\t\t502,\n\t\t\tRelayErrorCode.badResponse,\n\t\t\t\"The page sent an invalid response.\",\n\t\t\ttrue,\n\t\t);\n\t}\n\n\t/**\n\t * Count response bytes from the tab; past `maxBodyBytes` fail with 502 /\n\t * -31006 and cancel the tab. False when failed. Only a listen request's\n\t * event stream is exempt; a buffered body is always bounded.\n\t */\n\t#account(data: string): boolean {\n\t\tthis.#bodyBytes += Buffer.byteLength(data);\n\t\tconst unbounded =\n\t\t\tthis.isListen &&\n\t\t\t(this.#state === \"tab_stream\" || this.#state === \"relay_passed\");\n\t\tif (unbounded || this.#bodyBytes <= this.#options.maxBodyBytes) {\n\t\t\treturn true;\n\t\t}\n\t\tthis.fail(\n\t\t\t502,\n\t\t\tRelayErrorCode.tooLarge,\n\t\t\t\"The page sent a response that is too large.\",\n\t\t\ttrue,\n\t\t);\n\t\treturn false;\n\t}\n\n\t#timeout(): void {\n\t\tif (this.#state === \"done\") return;\n\t\tthis.fail(\n\t\t\t504,\n\t\t\tRelayErrorCode.timeout,\n\t\t\t\"The page did not answer in time.\",\n\t\t\tfalse,\n\t\t);\n\t\tthis.#options.cancel(\"timeout\");\n\t}\n\n\t#startRelayStream(): void {\n\t\tif (this.#state !== \"waiting\") return;\n\t\tthis.#openStream(\n\t\t\t200,\n\t\t\tthis.#headers({ \"content-type\": \"text/event-stream\" }),\n\t\t\t\"relay_stream\",\n\t\t);\n\t}\n\n\t#openStream(\n\t\tstatus: number,\n\t\theaders: Headers,\n\t\tstate: \"tab_stream\" | \"relay_stream\",\n\t): void {\n\t\t// Bounded queue: if the AI app stops reading, desiredSize goes negative\n\t\t// once maxBodyBytes are queued, and the exchange ends (see #write).\n\t\tconst stream = new ReadableStream<Uint8Array>(\n\t\t\t{\n\t\t\t\tstart: (controller) => {\n\t\t\t\t\tthis.#controller = controller;\n\t\t\t\t},\n\t\t\t\tcancel: () => this.clientClosed(),\n\t\t\t},\n\t\t\tnew ByteLengthQueuingStrategy({\n\t\t\t\thighWaterMark: this.#options.maxBodyBytes,\n\t\t\t}),\n\t\t);\n\t\tif (isEventStream(Object.fromEntries(headers))) {\n\t\t\theaders.set(\"x-accel-buffering\", \"no\");\n\t\t\tif (!headers.has(\"cache-control\"))\n\t\t\t\theaders.set(\"cache-control\", \"no-cache\");\n\t\t}\n\t\tlet response: Response;\n\t\ttry {\n\t\t\tresponse = new Response(stream, { status, headers });\n\t\t} catch {\n\t\t\tthis.#badResponse(\"the response could not be built\");\n\t\t\treturn;\n\t\t}\n\t\tthis.#state = state;\n\t\tthis.#respond(response, status);\n\t\tthis.#scheduleKeepAlive();\n\t}\n\n\t#write(text: string): void {\n\t\tif (!this.#streaming || this.#controller === undefined || text === \"\")\n\t\t\treturn;\n\t\ttry {\n\t\t\tthis.#controller.enqueue(encoder.encode(text));\n\t\t} catch {\n\t\t\tthis.clientClosed();\n\t\t\treturn;\n\t\t}\n\t\tthis.#atBoundary = endsAtBoundary(text);\n\t\tconst room = this.#controller.desiredSize;\n\t\tif (room !== null && room < 0) {\n\t\t\t// The AI app is not reading: stop the tab, drop the stream. No error\n\t\t\t// event, since nothing more would reach the reader anyway.\n\t\t\tthis.#options.cancel(\"error\");\n\t\t\tthis.#finish(\"backpressure\");\n\t\t\treturn;\n\t\t}\n\t\tthis.#scheduleKeepAlive();\n\t}\n\n\t/** The AI app's response is a stream that is still open. */\n\tget #streaming(): boolean {\n\t\treturn this.#state !== \"waiting\" && this.#state !== \"done\";\n\t}\n\n\t/** A keep-alive comment `keepAliveMs` after the last write, at a boundary. */\n\t#scheduleKeepAlive(): void {\n\t\tclearTimeout(this.#keepAliveTimer);\n\t\tif (!this.#streaming) return;\n\t\tif (this.#state === \"relay_wrapped\") {\n\t\t\tthis.#keepAliveTimer = setTimeout(() => {\n\t\t\t\t// While buffering a wrapped body nothing has been forwarded since\n\t\t\t\t// the last boundary, so a comment is safe.\n\t\t\t\tthis.#write(\": keep-alive\\n\\n\");\n\t\t\t}, this.#options.keepAliveMs);\n\t\t\treturn;\n\t\t}\n\t\tthis.#keepAliveTimer = setTimeout(() => {\n\t\t\tif (this.#atBoundary) this.#write(\": keep-alive\\n\\n\");\n\t\t\telse this.#scheduleKeepAlive();\n\t\t}, this.#options.keepAliveMs);\n\t}\n\n\t#headers(fromTab: Record<string, string>): Headers {\n\t\tconst headers = new Headers(this.#options.extraHeaders);\n\t\tfor (const [k, v] of Object.entries(fromTab)) {\n\t\t\tif (PASSED_RESPONSE_HEADERS.has(k)) headers.set(k, v);\n\t\t}\n\t\treturn headers;\n\t}\n\n\t#errorResponse(\n\t\tstatus: number,\n\t\tcode: number,\n\t\tmessage: string,\n\t\textra: Record<string, string>,\n\t): Response {\n\t\tconst headers = this.#headers({ \"content-type\": \"application/json\" });\n\t\tfor (const [k, v] of Object.entries(extra)) headers.set(k, v);\n\t\treturn new Response(rpcErrorBody(this.#options.rpcId, code, message), {\n\t\t\tstatus,\n\t\t\theaders,\n\t\t});\n\t}\n\n\t#respond(response: Response, status: number): void {\n\t\tclearTimeout(this.#startTimer);\n\t\tthis.status = status;\n\t\tthis.#resolve(response);\n\t}\n\n\t#finish(outcome: string, closeStream = true): void {\n\t\tif (this.#state === \"done\") return;\n\t\tconst wasStreaming = this.#streaming;\n\t\tthis.#state = \"done\";\n\t\tthis.outcome = outcome;\n\t\tclearTimeout(this.#startTimer);\n\t\tclearTimeout(this.#requestTimer);\n\t\tclearTimeout(this.#keepAliveTimer);\n\t\tif (wasStreaming && closeStream) {\n\t\t\ttry {\n\t\t\t\tthis.#controller?.close();\n\t\t\t} catch {\n\t\t\t\t// already closed by the client\n\t\t\t}\n\t\t}\n\t\tthis.#options.onDone(this);\n\t}\n}\n",
10
+ "// Generated by scripts/favicon.ts from scripts/favicon.svg; do not edit.\n\n/** The relay's icon: a browser tab with two linked dots. */\nexport const FAVICON_SVG: string =\n\t'<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 64 64\"><title>tab-mcp relay</title><rect width=\"64\" height=\"64\" rx=\"14\" fill=\"#1d4ed8\"/><path d=\"M32 22l3.4-7A3.5 3.5 0 0 1 38.6 13h9.8a3.5 3.5 0 0 1 3.2 2l3.4 7z\" fill=\"#fff\" fill-opacity=\".45\"/><path d=\"M9 49V26a5 5 0 0 1 5-5h.5l3.7-7.4A3.5 3.5 0 0 1 21.3 12h9.4a3.5 3.5 0 0 1 3.1 1.6L37.5 21H50a5 5 0 0 1 5 5v23a5 5 0 0 1-5 5H14a5 5 0 0 1-5-5z\" fill=\"#fff\"/><rect x=\"22\" y=\"35.5\" width=\"20\" height=\"5\" fill=\"#1d4ed8\"/><circle cx=\"21\" cy=\"38\" r=\"6\" fill=\"#1d4ed8\"/><circle cx=\"43\" cy=\"38\" r=\"6\" fill=\"#1d4ed8\"/></svg>';\n\n/** favicon.ico: 16, 32 and 48 px. */\nexport const FAVICON_ICO: Uint8Array<ArrayBuffer> = new Uint8Array(\n\tBuffer.from(\n\t\t\"AAABAAMAEBAAAAEAIABoBAAANgAAACAgAAABACAAqBAAAJ4EAAAwMAAAAQAgAKglAABGFQAAKAAAABAAAAAgAAAAAQAgAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAANhOHQfYTh1x2E4d5dhOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3n2E4dd9hOHQnYTh1u2E4d89hNG//XTBr/2Ewa/9hMGv/YTBr/2Ewa/9hMGv/YTBr/2Ewa/9hMGv/XTBr/2E0b/9hOHfXYTh132E4d39hNHP/ea0L/66WM/+2slP/tq5T/7auU/+2rlP/tq5T/7auU/+2rlP/trJT/66aN/99uRv/YTRz/2E4d59hOHf7YTx//8b2r///////////////////////////////////////////////////////yw7P/2VEg/9hOHf/YTh3/2VIi//TMvf///////fbz//vs5////v7/////////////////++3o//318v//////9dHE/9lUJP/YTh3/2E4d/9lSIv/0zL7//PLu/+SGZf/dZjz/7a6X//fZzv/32c7/7rGc/95nPf/kg2D/++/r//XSxf/ZUyT/2E4d/9hOHf/ZUiL/9M7A//bWy//ZUiL/10oX/9lUJf/cYjf/3GI3/9pVJv/XShj/2FAf//XRxP/108f/2VMk/9hOHf/YTh3/2VIi//TMvv/88/D/5Ytr/95qQf/us57/+N7U//je1P/vt6P/32xD/+WHZv/88e3/9dLF/9lTJP/YTh3/2E4d/9lSIv/0y73///////349v/88Ov///////////////////////zw7P/99/X///////XRxP/ZVCT/2E4d/9hOHf/YUCD/8sa2///////////////////////////////////////////////////////zy7z/2VIi/9hOHf/YTh3/2E0c/+J9WP/21cn////+///////////////////////77Ob/+eLa//nj2//44dn/6Jh7/9hNHP/YTh3/2E4d/9hOHf/XTBr/3WY8//nm3//////////////////88/D/7a+Y/+qghv/qoYf/6p2D/95oPv/YTRz/2E4d/9hOHf3YTh3/2E4d/9hNHP/pm3//+unj//rq5P/66eP/7KiQ/+N/W//khmX/5Idl/+F4U//ZUiL/2E4d/9hOHf/YTh3d2E4d/9hOHf/YTh3/2VIi/9xhNv/dZDn/3GI2/9lSIv/YTh3/2FAf/9hQH//YTh3/2E4d/9hOHf/YTh3l2E4daNhOHfHYTh3/2E4d/9hOHf/YTRv/2E0b/9hNG//YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3z2E4dcdhOHQXYTh1o2E4d3dhOHf3YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf7YTh3f2E4dbthOHQcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAACAAAABAAAAAAQAgAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAANhOHQDYTh0A2E4dANhOHTrYTh2l2E4d6thOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d7thOHazYTh1C2E4dAdhOHQDYTh0A2E4dANhOHQTYTh1p2E4d5NhOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHenYTh112E4dB9hOHQDYTh0A2E4dZthOHfbYTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHfrYTh112E4dAdhOHTTYTh3g2E4d/9hOHf/YTh3/2E4d/9hNHP/YTBv/2Ewb/9hMG//YTBv/2Ewb/9hMG//YTBv/2Ewb/9hMG//YTBv/2Ewb/9hMG//YTBv/2Ewb/9hMG//YTBv/2Ewb/9hMG//YTRv/2E4d/9hOHf/YTh3/2E4d/9hOHenYTh1C2E4dmNhOHf/YTh3/2E4d/9hNHP/YTx7/3F8z/95pQP/eaUD/3mlA/95pQP/eaUD/3mlA/95pQP/eaUD/3mlA/95pQP/eaUD/3mlA/95pQP/eaUD/3mlA/95pQP/eaUD/3mlA/9xhNf/YTx//2E0c/9hOHf/YTh3/2E4d/9hOHazYTh3e2E4d/9hOHf/YTh3/2VMj/+qghv/55N3/++/q//vv6v/77+r/++/q//vv6v/77+r/++/q//vv6v/77+r/++/q//vv6v/77+r/++/q//vv6v/77+r/++/q//vv6v/77+v/+ubf/+yokP/aVSb/2E0c/9hOHf/YTh3/2E4d7thOHfnYTh3/2E4d/9dMGv/jf1z//fj2/////////////////////////////////////////////////////////////////////////////////////////////////////////////vz7/+WKav/XSxr/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/10sZ/+qghv//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////7a2W/9hMG//YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/XSxr/66SL///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////usZv/2Ewb/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9dLGv/rpIv///////////////////////zz8P/55d7//PLu/////////////////////////////////////////////PPw//nl3v/88u7//////////////////////+6xm//YTBv/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/10sa/+uki//////////////////zx7j/4XVO/9xeMv/gcUn/8cCu//////////////////////////////////PHuP/gdE7/3F4y/+BxSf/xv67///79////////////7rGb/9hMG//YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/XSxr/66SL////////////+N7U/9xhNf/XSxn/2E0c/9dLGf/bWy7/7rCa//LDs//ywrH/8sKx//LDsv/vtaD/3GE1/9dLGf/YTRz/10sZ/9tbLv/21cn////////////usZv/2Ewb/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9dLGv/rpIv////////////usZz/2Ewb/9hOHf/YTh3/2E4d/9hNHP/YTh3/2E8e/9hPHv/YTx7/2E8e/9hOHf/YTRz/2E4d/9hOHf/YTh3/10sZ/+uljP///////////+6xm//YTBv/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/10sa/+uki////////////+6znv/YTBv/2E4d/9hOHf/YTh3/2E0c/9lQIP/ZUiP/2VIi/9lSIv/ZUiP/2VEh/9hNHP/YTh3/2E4d/9hOHf/XSxn/7KeO////////////7rGb/9hMG//YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/XSxr/66SL////////////+eLa/91mPP/XShj/2Ewb/9dLGf/cYDT/8Luo//TPwf/0zcD/9M3A//TOwf/xwK//3WU7/9dKGP/YTBv/10sZ/9xgM//32s/////////////usZv/2Ewb/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9dLGv/rpIv/////////////////9dDD/+N/W//dZjv/4npW//PJuv/////////////////////////////////10MP/439b/91mO//ie1b/88m6/////////////////+6xm//YTBv/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/10sa/+uki////////////////////////fj2//vt5//99/T////////////////////////////////////////////9+Pb/++3n//339P//////////////////////7rGb/9hMG//YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/XSxr/66SL///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////usZv/2Ewb/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9dLGv/rpIv//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////+6wm//YTBv/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/10sZ/+eSdP//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////6p6D/9dLGv/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTRv/3GE2//XTxv////////////////////////////////////////////////////////////////////////////////////////////////////////////nj2//fbkb/2Ewb/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTRz/3GE2/+eSc//21sv///////////////////////////////////////////////////////vs5//0zb//9M2///TNv//0zb//9M2///TNv//yxbX/66aN/91kOf/YTRz/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTRv/10kX/+N/W//99/T////////////////////////////////////////////+/fz/8b6s/+mbf//pnIH/6ZyB/+mcgf/pnIH/6ZyB/+mdgv/liWn/2VMj/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YThz/2VMk//LGtv////////////////////////////////////////////nl3v/ro4n/6Z2C/+qeg//qnoP/6p6D/+qeg//qnoP/6Z2B/95rQv/YTRz/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/XTBr/439b//339f/////////////////////////////////++vn/6p+F/+iWef/qn4T/6p+E/+qfhP/qn4T/6p+E/+qfhP/liGf/2VMk/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d+NhOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/ZUiP/7KiP//nm3//66uT/+urk//rq5P/66uT/+ufg/+6wmv/aVij/3WQ5/+B0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Hf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3y2E4dUNhOHQDYTh0L2E4dr9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4dxdhOHRfYTh1M2E4d8thOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hNHP/XSxn/2Ewb/9hNHP/YTRz/2E0c/9hNHP/YTRz/2E0c/9hNHP/YTRz/2E0c/9hNHP/YTRz/2E0c/9hNHP/YTRz/2E0c/9hNHP/YTRz/2E0c/9hNHP/YTRz/2E0c/9hNHP/YTRz/2E0c/9hNHP/YTRz/2E0b/9dLGf/YTRv/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d+thOHWfYTh2f2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E0b/9xhNv/nlHb/7rCa/++1oP/vtaD/77Wg/++1oP/vtaD/77Wg/++1oP/vtaD/77Wg/++1oP/vtaD/77Wg/++1oP/vtaD/77Wg/++1oP/vtaD/77Wg/++1oP/vtaD/77Wg/++1oP/vtaD/77Wg/++1oP/vtaD/7rGc/+iYfP/eZz3/2E0c/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHbzYTh3Y2E4d/9hOHf/YTh3/2E4d/9hOHf/YTBv/4HFJ//fZz//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////54tr/4nxY/9hMG//YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHfDYTh3z2E4d/9hOHf/YTh3/2E4d/9hNHP/aVif/88q7////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////99nO/9tdMP/YTRz/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3+2E4d/9hOHf/YTh3/2E4d/9hMG//fbkX//PLu/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////vn4/+J8WP/XSxr/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9dLGv/ifVn//vn4//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////7+/+aObv/XSxn/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9dLGv/jflr//vr4//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////7+/+aPcP/XSxn/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9dLGv/jflr//vr4//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////7+/+aPcP/XSxn/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9dLGv/jflr//vr4//////////////////////////////////zy7v/44dn/+ebf//759//////////////////////////////////////////////////////////////////++/r/+ujh//jg2P/87+v///////////////////////////////////7+/+aPcP/XSxn/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9dLGv/jflr//vr4///////////////////////99fP/7a+Y/99wSP/bXC//3GA0/+OBX//0z8H///////////////////////////////////////////////////////bYzf/liWj/3GI3/9tcLv/fbEP/66WM//zw7P////////////////////////7+/+aPcP/XSxn/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9dLGv/jflr//vr4//////////////////759//ol3v/2E4d/9dMGv/YTRz/2E0b/9dLGf/bXTD/88i4////////////////////////////////////////////9tXK/91lOv/XSxn/2E0b/9hNHP/YTBr/2E0b/+WJaP/88/D///////////////////7+/+aPcP/XSxn/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9dLGv/jflr//vr4//////////////////PHuP/ZUyP/2E0c/9hOHf/YTh3/2E4d/9hOHf/YTBr/3mk//+ynjv/sqpL/7KmS/+ypkv/sqZL/7KmS/+ypkv/sqZH/4HJL/9dMGv/YTh3/2E4d/9hOHf/YTh3/2E4d/9hPHv/vtqL///////////////////7+/+aPcP/XSxn/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9dLGv/jflr//vr4/////////////////+eUd//XSxn/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E0c/9dMGv/XTBr/10wa/9dMGv/XTBr/10wa/9dMGv/XTBr/2E0b/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9dLGf/kg2D//vv6//////////////7+/+aPcP/XSxn/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9dLGv/jflr//vr4/////////////vz8/+SFY//XSxn/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9dMGv/hdU7//fb0//////////////7+/+aPcP/XSxn/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9dLGv/jflr//vr4/////////////////+iZfP/XSxn/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E0c/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E0c/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9dLGf/lh2b//vz7//////////////7+/+aPcP/XSxn/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9dLGv/jflr//vr4//////////////////TOwf/aVyj/2E0c/9hOHf/YTh3/2E4d/9hOHf/XSxr/4HJK//C5pv/wvKr/8Lyp//C8qf/wvKn/8Lyp//C8qv/wvKn/4nxY/9dLGv/YTh3/2E4d/9hOHf/YTh3/2E0c/9lRIf/xvq3///////////////////7+/+aPcP/XSxn/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9dLGv/jflr//vr4//////////////////78+//rpYz/2VIj/9dLGf/YTBv/2Ewa/9dLGf/dZTv/9dPG////////////////////////////////////////////+N/W/99vR//XSxn/2Ewa/9hMG//XSxr/2E8f/+iWef/9+Pb///////////////////7+/+aPcP/XSxn/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9dLGv/jflr//vr4///////////////////////++vn/8b+u/+N/W//eZz3/32xE/+eSdP/33NL///////////////////////////////////////////////////////nk3P/pmn7/329H/91mPP/ielX/77ai//329P////////////////////////7+/+aPcP/XSxn/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9dLGv/jflr//vr4//////////////////////////////////759//77ej//PHt///9/f////////////////////////////////////////////////////////////////////7//PLu//vs5//99/X///////////////////////////////////7+/+aPcP/XSxn/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9dLGv/jflr//vr4//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////7+/+aPcP/XSxn/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9dLGv/jflr//vr4//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////7+/+aPcP/XSxn/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9dLGv/jflr//vr4///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////+/+aPcP/XSxn/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9dLGv/heVT//fj2//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////39/+WKaf/XSxn/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hNG//dZTr/+urk/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////PPw/+BySv/YTBr/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTx//7KuU////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////8Luo/9lTJP/YThz/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTRz/21os/+yrlP/66uT//vn3//////////////////////////////////////////////////////////////////////////////////////////////38//78+//+/Pv//vz7//78+//+/Pv//vz7//78+//+/Pv//vv6//318v/21cn/5oxs/9pYKv/YTRz/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E0c/9hPH//dZDn/44Jf//nk3f/////////////////////////////////////////////////////////////////////////////////99fP/8cGv/++3pP/vuKT/77ik/++4pP/vuKT/77ik/++4pP/vuKT/77ai/+2slf/qoIb/5Ytr/9lUJf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTBv/10oZ/+qfhf/////////////////////////////////////////////////////////////////////////////////21Mj/6Z2C/+mcgf/pnIH/6ZyB/+mcgf/pnIH/6ZyB/+mcgf/pnIH/6ZyB/+mdgv/pnIH/32xD/9hNHP/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E0b/9xiN//54tr///////////////////////////////////////////////////////////////////////318v/tr5n/6Z2B/+qeg//qnoP/6p6D/+qeg//qnoP/6p6D/+qeg//qnoP/6p6D/+qfhP/mjG3/2VQl/9hOHP/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hMG//qoIX///////////////////////////////////////////////////////////////////////bUyP/qnoP/6p6D/+qeg//qnoP/6p6D/+qeg//qnoP/6p6D/+qeg//qnoP/6p6D/+mdgv/fbkb/2E0c/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hNG//cYjf/+eLa////////////////////////////////////////////////////////////++zn/+iVeP/pnYL/6p6D/+qeg//qnoP/6p6D/+qeg//qnoP/6p6D/+qeg//qnoP/6p+F/+aNbv/aVSb/2E4c/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3+2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTBv/6Z2C////////////////////////////////////////////////////////////7a2X/9pWJ//kgmD/6Zl9/+mbgP/pm4D/6ZuA/+mbgP/pm4D/6ZuA/+mbgP/pm3//5pBx/91kOf/YTRz/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3x2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTRz/2lcp/+yokP/44dj/+urk//rq5P/66uT/+urk//rq5P/66uT/+urk//nj2//us57/210w/9hMG//ZUiL/3F8y/91jOP/dZDn/3WQ5/91kOf/dZDn/3WQ5/91kOf/cYjb/2lYo/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3T2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E0c/9hPHv/bXC//3WM4/91jOP/dYzj/3WM4/91jOP/dYzj/3WM4/9teMf/YUB//2E0c/9hOHf/YTh3/2E0c/9hNG//YTRv/2E0b/9hNG//YTRv/2E0b/9hNG//YTRz/2E0c/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHevYTh2W2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTRz/2E0b/9hNG//YTRv/2E0b/9hNG//YTRv/2E0b/9hNHP/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHbPYTh1D2E4d7thOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d99hOHVzYTh0H2E4do9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4dudhOHRDYTh0A2E4dL9hOHd7YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3q2E4dQdhOHQDYTh0A2E4dANhOHVPYTh3r2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHfTYTh1o2E4dANhOHQDYTh0A2E4dANhOHQDYTh1T2E4d3thOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d59hOHWXYTh0D2E4dANhOHQAAAAAA2E4dANhOHQDYTh0A2E4dL9hOHaPYTh3u2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHfLYTh2v2E4dO9hOHQDYTh0A2E4dAAAAAAAAAAAAAAAAANhOHQDYTh0A2E4dANhOHQfYTh1D2E4dlthOHdPYTh3x2E4d/thOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/9hOHf/YTh3/2E4d/thOHfPYTh3Y2E4dn9hOHUzYTh0L2E4dANhOHQDYTh0AAAAAAAAAAADYAAAAABsAALAAAAAABQAAQAAAAAACAADAAAAAAAEAAIAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAQAAwAAAAAADAABgAAAAAAIAALAAAAAADQAA2AAAAAAbAAA=\",\n\t\t\"base64\",\n\t),\n);\n\n/** apple-touch-icon.png: 180 px. */\nexport const APPLE_TOUCH_ICON: Uint8Array<ArrayBuffer> = new Uint8Array(\n\tBuffer.from(\n\t\t\"iVBORw0KGgoAAAANSUhEUgAAALQAAAC0EAYAAABtXdpxAAA5HElEQVR42u2dZ2AUVReGn5lt6Z1OqKF3BFEICEqVKqICCggKgoqIHxYURSmCCgoooiBFBRUQlCagIkhTegdp0lsChPSy2Znvx9yhrEYBk2w2uc+fN7szO3t3NvvOmXPPvVfBS4mM7NDhjz/+8rRFqCuLl7UzJGKBoaXFfuWTDb2zhqHFPjc0vK54vMTQIqcMDRHPO1aK437o6fMhkeRhMg1xXTY06ZKhF78x9FxXQ2N6GHr5KUP3fmzorvmGntojNM3QjO5ZvJ8qVDOfOHVq8eLKlT19Gm4dxdMNuFncDNlst/73e1vFF1pujqF1xT9Cs6OGVhthaKWhhvodF2oer5OnP69EInFWNzR1gqHnvzJ0bydDN48zdH2gofvWGnol6d+O7C2GnWcN2s2QrUIz/37vYu8b2jTR0A4tDL37HUNL3m2o5eWbfHvTqPPs+ZFI8iG3+btLGG7onlWGrhQR9rJPDd07wdCM2VkdIa8adp4zIGHMf7lFEUwypMp9hj7S0dBOcw2tWstQmwWJRFLAuSDuoNeIyHvOs4aufs/QpNVuL/ARRp3m6ZabeNyg3SJlP6EpN+5VqbehvUVuuGtzQ0uvzOKwGULtnv58Eokk1zEDO/XGp1O2GrpmsqGfNjD0px8MTV3idhyLMOys+rRynFw3aDdDzqJTL3yAoT3XG/r0UkOjSmVx2Cy+EIlEIrka8Pm5PS06JRe1N/SDzwzdYgZ2y4Q6hKbndiok1wzazZjNEyAiXaW0oU1Fjvj1GYY2WW6oJcLtcKahy1SGRCK5VbII6M5MNPTjSoZOEb4TV9/t9aowao0cJscNWhhzFlUXQZGGDhSPn/va0MKN3A4jDVkikWQ3WXRKauUM/XGkoa91MHR7kNvrHcKo03OqgTlm0P/c2VfRx9ARuw19UNx6WEuIHcwymYCcap9EIpG4kUXf1WHhRyNE3fXX5Q11TRA7XO07y+4USLYZ9M3VKTcQ5XCThBHf+bDbYczeU5/s+4gSiUSSHSRFGzpaRMwThc+lbhE7+AtNzi6jzu5OtSyMucV2Q2c+YuhfjNncXxqzRCLJa4iRxgGiaOFNMdLxrS8MDXLbD/8sRjrfMv/ZoN1SGW7G3NZp6FRRp1yluNvL5YAQiUSS1zEj41RDHE8a+kJjQ8fcYWiw0GtG/V/f+LaN8Z87/1rsMHSqGHJdZrTYIAwbW3aePYlEIslF3HLVrm2GfiSSGq8J/0sWBn77A2Bu2aD/OXRvIMbGm6mMKiXFBmnMEokkv+Hma87nDB3zu6GjxAhGZwWx3y0PfLnpFEcWdcyCiqIXc5IYSHLVmE2kMUskkvyG6WsiMraJqShejDW0306xfZhQ163mpm81B23WIYsQP0gY8Yhdht75kNv++s0cVCKRSLwYs7hB5J59jxn6xmBD73PPVFhv5qBwEwbt5vgiNDdH/g0Us8s96F59ITv/JBJJQcPsFBQBbGExf/wY8biM2XmYKXzV998OeLMRtNsY9qZiHuXnvjXUaqY0zCS4NGaJRFJQcUsB1x9r6IuHxOaNYkOqW7HFX8hyw9+PBDQnMZovei2bbRLb5cg/iUQiuRG3TEKKWNGpZ1FDF0x3f4H7AJe/RNBuKQ23Ido91xjaZJnby6QxSyQSyY2YAbBIBfuJWTn/Jx4X3ey2/1/mGsoqxeGWxDbnY35aGPPV2eU8Nk+qRCKReAlufnr3YkMf7+q231+qPK4atNsGc2kpUTbSWywVE1XW7YBydjmJRCK5Ocy6abF4bu/WhpYf5rbf1dSzmtUGgypi5ZKu97rtJyNniUQiuTXcxoNUFCu7dF/ott/V8mTlnwun3xQjYIYf8vQnk0gkknyCmaEQqY/9vxraWqyheGqauWMWOWhzlexO37htyPEVBCQSiSSf45aTrjzE0FYZbvvZTIN2yyU3jTfUXCX7KnLNP4lEIskeRCStivmkHxQDAP0+ENudpuGKnLJ1nqEdWhpqcxvaLZFIJJJswi2SvlNUx1XvmsUO5cTsS3ebServhdqRSCQSSQ4SJhZnbS4mndvsnrKoK3LOJe/2dFMlEomkYBK93NCA4aZBiyGIzY4aannZ002USCSSgkntOEOjVgqDjhB1eNVGebppEolEUrAJEyux1OkhctClRSdhpVc83TSJRCIp2DhEH2B0qDDo8omG+omJpqnp6SZKbpIzxOIE/W79Sf4EPVWvw27Qz+tR7AS9Mf35E0ghDR3vnQhWQ0cHqlEOH1Bcykaqg+Kr7KAGKMHspRYoY5RnKAL44ZBFoRIvw232u3J7hEHfWcNQv0883UJJFtiwooDeRx/FGdBGaIFMAmtdawtlBYSmBYar6yDk8cCllksQ2iVokqUoBPj5PqdGgDKTYZQEkknzyuFGUUpJ7ODq7PqGdyG+R9Ia130Q91ViXVdNiNucONrlgMThKbu1laBH6h04CGojdTV9QLmTqvgCLq/89JKCgVv4VLuwMOhiX4hnRP2zXBHF4yji3MeRiAtcMVoPZoN/uM8CdRXUX1U13LcFND9Z/5OAk1DfUnW073gIHu3fSx0BwbUC1lu+B//9vsPUbaBMYSPFMCJpb7SosqzHDq5qWjprIeHp5HquLpDwTPI6rRfEjrxSP/NlWH1oW3jybljzwLbeyQdh3/xjD6eVA22z/jmLQb1LSeJOwEmmXJBNkkcRKecQu5iLY0WM8USrQp5umURwnktkgjZdj2UdlFtT/Dn7HnjmzS51wsZDu86NXIE6+G/xbaz+68I5BY+zly5+lPkmzLq0bG3cszDHtvKX+PYQvzopyNUT1HeUetwHpJL+n4xaNRIp+kf6fC6D3pSnOQacJgYncEZoIileeWH8N/zwQQVKUBgrUIRQrKDMZwylQBmjDKCIpxvpvZgRtDkB/+OebpDEwLVJa84MuLNO1Rd8u8OITv02FvkCagSWb+jwAcTg0KuRoEiBFFh0YbPizqN4eMSz1jfhZaWHtdArUC2l3DKfMjCi/fQOMTa4MOvSi5nfgnpOfYMuQDoZN2XUqnF8fZD+AedA78hLnATHPttipSYEvOL7slob7FbbD4ov+G/0HaQeARuWWOUoEE8yGqCjeXUEH0kRbJAZ5XLqXSC5ddpUvSJk9HUmaKcguXJqcb0spMVnVNcmAB1oTBAovypTKPtf37zgICLo8yOMh0Ve93SDCiwObCigtda+YSlElYxsZW8EE355/qNiO6D2yopVfPpft78m4jFVdoX9Lek40bl6Xk3mlF0ZEF8BRqXOOBLzKSQVT12oJYBySfmRKlwzehMz1XSZBFygd9Zf5iT4fuTzlvo1lHqsiGYLhajRJafa20PEoZBx1oFgGammMQjUtupW+oFSjCNKbcAlbl+92Zy5dl71SjzMIdCWaHfo00Arqo/gW7jcPaGGKwn+7HbmSIYNTiSd+z5jNiTekZKubQE+VV6hOCjlKIH9b867BLhq0GmLjIeODp5uUEFFr6P35Aj4v+rrUp0wfuxzIUWbQtvvGrUMDLl+R6EFOVq+FTJxoQNWLCjgcrhO0QlGPT+rUcxC+OzAovfjloOyUplIGaAIYVi5asz6IU6SAbyof8h5KDom/E5rI2gwqtpiv/5QuEnYs9YDYLmotuEt0O16NPuAPzlLBnCWWDKBJFLzVYrD/D/0w4ECFKcQNqA4EVhB2aPMoQJob+l7+QUuvxY/3LUNtp34IzW1Jhz3O5eWMQz0N5hKDChtaUgAshPXDWHQ+lPisaziyG0sRgTsKq79xJvQZsHdSwOXw4d+/6tc7HHwDXIUVSZ4upH5ADOF4cCOAocdp+7NeAF6vTii+el2cDL+/EvOnaAuVrvQlquds5SjOHYovbHYSfswuMtW/bjfPAhZEtBO/QH0b/QENgDHOUcG1yJuiYE/vqigNDA6Z1MrpIfoQ2Fzh/0JKT/DwcknymbUBe0pfbX+IygNqIYv3n+HkU2Yt8fSmD2EHqI35wAEFfP3VwdBt8ktHw++XxpztiOM2aRCl8jG9hegw/jGq4L6gDJZeZHiXI24tQS9OruhSFhYqNUJDR01/udngZB+AcHqXNBH6wdYDZzgvDTmfyDZuHPQV+gav4HvcPvXystwV9lqNfxGQpl1xY7bXgPdrjdmH5ApI+jrkflLT2EaQbD+GvMgenHNNf7toeHxGl38Mv/74SVZ4BKdc3N4i5LwYFCzgKCHIHJ+kWhbaXDt0u5nFvhWsj+h9oc6r1da6/suBH3u/63aHfS9emG2YqRMJDePXeSsC9GaA+B43x6rdIDajSv+7vMgBEf5h1ueB72R3o8/Pd3YvIM0aA+hV9C7cAiCzvidVPtB19YtKgRHg++XjnHKPE+3Lh9jMeswDCo8E9nR/iJ0eKXxt0E9QWlFAwIgqlXkAPu9ULJUYbvtMOhb9CC2wI2vltwy5oCr2Xoc66HwjJDL1p+gaq2yfo7WoJRWjlMHSBDVLgUcadC5jSiu0nz1l5kHjcbV+tK/FTQ8X7O7n8y75R5m1cAb9KEwdC7T9IGg6lClbdmujrIQNajkWPv9YJmoWhgMbOMPUj3d6HxEKhlo1zphy58o8YDdBqEbA8tbzoBeWG/DAQp86kgadC6jt9Sf4zgEzvdbqj4G3Ua3eDC4JfhudsxTvkeOcMst3H74UQMiR9vbQN/tHRPD+kL4oeANlmagn9HLsYMCbxTZjnk2o+nHnxCw289PvRMqaJHpjj6gKMomalDgI2lp0LmFyMFpVv0FvoHoDbV2+neChlVqvu/nd91+BX3ASW4jyrrUzsou+sG9o+oV9X8VIt4KbmHtAfofejG2YRi0/F6yH1HHrwQTgAXKh5Z8yR4IIX4BEyzrQQ/TWxbkSFoadC6htxCR8/t+M9RHoFtsyy+Du4Lv5453lbkYQ44LcKTgMSw3/gYi9gS/bXkWqjYuW8hxPzCUnhQCLhKP7LzNMXQRSZu/j6ieka/ZWwG/KB9RBrjIFXOGioKENOicRkTE2gW9B7Mh+vdau/wfgIauGk/7XX/2feX0mB7FHJnZgzaEQI3BUVN8/oCQJoErLNtAL6m346CnG5mPEQGK8oDSlCAon1Gir90Xgj/3f9EyBvQy+gMcpMBF0tISchi9sN6GPyDokv959SnotqHl28EPgO/jjq7KDK6VfUk8i9uQ+ULfh9SzTIJqo8t2cDwCPExzgrk2hFySvZgjNyvpD3EYgn8MeMfyLpT/qcQddg0YxCOEA5eJL0iRtDTonEJUCWi6/hxfQ/TWmnv9H4CGp2o87Hf9P5hFFm7lKcxI+mkeJAxqfhW1xuc4hDQPXGXZDnopvWNBjORyjTNcxAmKTdlKTYg6VbKkvQ4ERvnfb2kBejP9GY5RYKY6kAadQ+hFRORc3Bgh2PWZlk2D7wXfzxxjlLmebp0kS9wi6YiFIXUtk6DaorKDHD2BR0Qk/V+nKZX8PeZsgUH6feyHkBmBVssxKPde8Ra2zUBZY+g9qTc5+6CXIw06uzFHCAboQ5kLjV6r+bF/c2i4v0Y7P6enGye5acw66RbcSQDUnBK1zOdPCHkkcKtlN+g+YmiyrO7IGY4Zk02pk9VoWkCFbZF7HN0gYJFvZdUKeg/9TU55upE5jzTobEavr/fmKAT95rdF7Q3d+reIDm4GvksdnyoLuHYLLcnbuNluxLshhS3vQbXpZR93PAo8QQdCgUuyuiNHMCNpXW/AXgirGtTQUh7KDCs+2D4N+I09pJLvU03SoLMLUa6lOfQhzIVGn9Za4N8GGsbV7H19mbOcv9nLMG+ju3AvwVDj0ajRPnsg9MXA05YDoKv63exFRtI5xSljRRo1WJ3Oo1Dxs8gh9jfAb5JPa3UP6P30MZz1dCNzDmkW2YTeXh/CSQj8ym+R+ih0u9Dy8+Cu4HvEsUJZAqTJnKVX4ma6hb4NqWOZCFVHlm3reBgYQT+KABe4LCPpHMCMpC3GhTB8Vkgv6xNQZmSxhbbngWks4jLk11ooadD/FXMllGT9KeZA9Le1fvZvBw2Dawz1s163n4+Y2FzinZipqcdpSyjU6BY1ymcvhHQM3GjZCXohvXVBHvGW44g1Hi2TVTsvQMUqpU46JoPvlz7j1Hmg99Xf5oynG5n9SIP+j+hV9W4chqAf/FapPaGb1nJx8GPg29/RU5kFpBWM3uZ8j3ud9LKQuyyToNoLZZs4HgT60pFQCvzcETmGWSedrtdlNxQ6HtLdOg1KP1dkn60p6AMZzzmuraCTT7D+90MUEMx/kA3sIgVoog/gT3DFah/zJjRaVkv3PwYN59Vo49cR6C5e53PjRPESL8c9kr4/6nWfw7BvzbGT6Rsh7pXEba5gUE6zhMqebqxHMWzSvHN8TGlNCCituYtAjBG2t8NODpEGlk6W1xkHFfuW3uEYD8eXny/uPAdpQ9LnadNAmaAMppinT8F/Rxq0eUuaQhoa6C30gRwHrYu+iB+Ap/R3OAs0U+7AH6w1LBeVOeD3p69dOQ5BP/kHWQpBd0urN4KfAt+xjv3Ks57+UJIcwz2S/jiktGU81Him/IMOB2z9+cCIVCcoZ5WpSjkKbspDTF2gv86n+gVwDnP+SiBkHnN9pN8D+hj9C2IBVTEmS9rKTMqDslgZR2mgDMWuLiZ7fURsBkrxenV2QeGSoWWsu6DU4CKxtlpwcPHJV9JPgjIOF0W5uhalt2KuSZiPbgpuEnOFh/v0ZzkGLGItiRBw3q+E2hCK14342dYNipYPP2etCtXeLVfcMQoqXIyMcfSCYr3Di1rrQ0j5gNmWTVAppvRQx3Kw/WYdk59usSRZYHZKiU6stMB0TX8RLl5JCHMtA1bpW0gGCsyYNzfCCcIC+gUq6Dsh+b1UH80O8RuSwrRecOZK7BTnW3BlWGKoqyIklE+O0j6D5DppY7VyoL2thfAhKGeVpVTmrxG3mIVQ6a8UpwmcWRQz3Xk//Nhz80tJTkhXM97Wt4AyWOlGhKdPxu1TcAzajHxOch4nuI5rD/MFBHziG6ECnZ66Jz3oOHRS75kbtAPKVyzxi/00hFQM+ErdDPaZttpKFFCXSvj+w/tkiLkaxAVAks8x5++W08TeEtoUPZLJkLkuc6HeAq44k4ZojeH0UzFPOKNhk21ffMoouDjiSqLrJWCR8i6lQXmf5ykKuMSMHM2ohz+4/LWFvA5rzmzfmnQFDjc4VSfja1AbKInc6elPe/vkf4MWVRZ6gH4v+0HvzUjOQOWPSuuOvTDwo4d2h1+ANkvvjg8YB/ZJtgpKOU83WiIp2Fx6OP5T1zRY33BX7ZTvYe+Eo3ekPQRp85zN9J9BjVBm8SgQakTqak8ljMZwalLMz5lF4RfXtvZJ08CpZhbRo0BpSl38Pf2pbp38m4O2i7XPIo3Jbfzq+mSqG6D9uOiJgY3gWetDn4SvhjItixW2LQRaUhi4VnUhO/ckEo8RPi/4KUtfaHuo0ZrAhRD0tn9fFZgWuujLuDT4c8HZshmLQJnKUEoAzanPPnDV0HrqsUAZ/SUmAcuZQwVPf5rbJ99G0Pon+kIugy3Gekg5CK/Oe7xNIR166fc/FdoXbMutQ28YWOCWU5RIJB4ki9TRH0HH49Lj4LnPP6hwLgb2LjzaNr07qK+olxkISm0q4gskKr9QBZTKlMGB106xkP/qoM3JiurqH/M9tGjfoGVACnT9ocWrwR2vM2b3tf+kMUskeQfTmF03GmvlhDKhjlAY6HjoyfBdENwkIE79HJRoauEPSknlT2qDUpFS3mzMJvnHoM01/3TtOb6BMvcVe93WF54b9dDG8LMQgG8/NYxrS0vJTh2JJO9j+XuPahN8912B06Dz5qbBQaUwFlLQQN9h1EnnF/JNDlp/Sh/LWXCcs89SmkP/Bx+4GBYI1RqVszpGXbejXFpKIvFeRERsbWTpxE8w4JfOhBeG7QkHM9Iegt37jvinbQaLom6jH3h7yav3W5Xo1DNXLrlvQP0tAV9A56PNIoPLXrefl9/qSCQS/jJQqOTrhTtZY+BZR5fdYa9AwHBfRc0EvZc+ktOebmx2fFwvR/+JTSSBbbd1o7IP7jlep4n/IfDv6XNOSeHqCEE5zadEko9wm72u/ntVr/g1gVLdim6wNQZtsP4bP3u6kf8d7zet4fpnXAD/8j4r1YtQvW756Y5Z1233wycffEqJRHI9bp36EUOCoy2PQsUnI1s7hoDeXp/PUnAbKO51eL116T56Y/ZD+PjgsZZ6EB4WHGdt6+lWSSSSXEFUbama2pVREHl3kVM2f1BDlZk8xrU7aC/F6zsJtUh9DN9CBWukyz4WCmWGPG/xB2yebplEIslx3CZDqq1U/MSnNdj32RRFAedpV4zuBKUSpXF4urG3jvdG0KJzUFGVTdSAyONF+tkqg8NmG67cCWS41TlLJJJ8T8lThf6w7YSAAN9BaiHgEX2YNy8u670GHU8SLrA8qy5kCkReKvKN7fpbGbusc5ZIChohrQPXWHZC2M7gny33gL5dD2WLp1t1+3ivQYuyOeVL5U2lJNjft3ZXkj3dKIlE4kks9S0jGQ/WCZbNynTQf2MPKZ5u1e3jvQZtkioWY13COhI83RiJROJR9nCENGA7B0nFGInoxXfS3m/QEolEkk+RBi2RSCR5FGnQEolEkkeRBi2RSCR5FGnQEolEkkeRBi2RSCR5FGnQEolEkkeRBi2RSCR5FGnQEolEkkeRBi2RSCR5FGnQEolEkkeRBi2RSCR5FGnQ3oac47pg4eVLNkn+G16/okq+xVwU030uLreZuVxPa3XoA8l9Uxdr1SDtp4y39Q9Bu6D10L8Em269oBwFvws+69QfwHeO431l3j+8r1hCyH2lCkkOY6467764sXLjt5BZ2VWfqpD8ROr3WjVIezhD1xeCvl8vom8F+ybbWAXwu8tHUdeBT2v7MOW5f3hf+X3naaRB5xXMSMn8QboZc2yHK6qrGRy4eGxvegxsuePAV6mn4PSamCrOMnD6x5hRzhS4sPDy6Mye4Hwsc7L+KwS+5v+B2gVKLIuIse2F4k0LNbXGQsU3SlV0zIH6bask+dqh3GMlLtgvg880+ypl2HVvnEo6GuCLQ95vZSMucQk2p8M0jXkGS4mDczsutsj8Gvby57tp/WHrB3/8kFYGzu6KneMcCafXxux3loGLla5sdh2CzCPae/o+CNkWEG1ZDCXbFnrHthqKDYlYbz0H1azlFjkGQp3OlT73HQll+xR7zPYx2HZZ31eu/09LN1YqwoFdGrbnkQbtaZxiaS7bjSvAHJl4+uWMH2DJJ+t6J86BH5M2pSdZ4M/zZ9tnNICkeamttXagr9J/ZAko1ZSz2EBpTRdUoB59sIFeFZXPYG+9o5vS94HuzzrqgX2eta6SAaFxQQssCtTtUHG6zwhov6hx58A0uLdVvfMBz0Owr/9G9ZPr2usSEZdFRly3hPsFWBizHqEPZz7sOXb0+fQn4Pthv8YkBMCvd+8YmlwNTi48f6/zf5AyNa231hr09vyPiaAUV45SG5RyfEQX4Am6cRpOvsBEZzHY9cPh3Wk1gQ4MYT44itjGK2cgvHTwS5bH4K6K1ef4dYNOA+5pHbQbGg6vudOvKPgVdZxXfrmu3S4R2Vvk5dkTSIPObdxTF8KYL82NP+yaB19/92NA/MfwjfbTyPidcCLj/MSMGaCN1HexCtQzygd0AUtZ9UkKA8UpTh9gB8UAsPIRADvF+11b4Xyr0HYA2ip9rp4EF+1XHs08BCte+T0jaTqsSd0+OdkH7vyyWgPfd2DA2Acyw5zQOK72Cv9RoF5W2/EWV9eExEdGWv9IFgZ39qOLHTIHw8yHl56PawQLYleHJDwOFwZeXpmZDvTkfkJAvV+5kzfA8oKlG+HAFgKxADolxKHGAHBAPOp79S2OA1CIOwAyv9X66vFwPvlyrcw9sLDHmmkJKfDThs2tkrpC02J1x/vHwpP7O/YM3Qv1I6t86lsVCMAXFUjHKSJrm/y+cw9p0LlFFjnGnV0OnUibCuPSv6p8MQPWltwxJvkr0I5pD/EFqHHqT1QByxuKL9UAeAWAYkQAt92JpNxHPQJAQTlBXVBVy0nqgnOuq48eD+sm7vRNeRr2pf4Zkh4BvS60LRIyDp6a0Glj2EIIfN7vbbUzkCyWtffHR8ZY15HFndGGrbu/SNkEY6p9/nbsENhV+HCZtOOg/059qoJlqlqZZsAnrAJAFxpMgHh8a9+3CASULtxLsAjgo0FVLa8SDSld04Zpp2DJqfVPJr4P29se/CQV6J/2wP6w3+DRhNYNQxaDzzl7uvIOMuWVy8hTnNPoVyPmG871atf2wsm/w8DV4xecmwKrz2+zJq8H/VWmcAHUg+r/6My1CCanEZG98hD3EQyWk2oz3oS48YmRrirw0eX5j13uBsP7TWsVswNi68ZddDXmmjGbP9yCjpkCEsas/4Y/m+H7j9ZaE0vDC7aJ284Phx2vHrozbQjQTokmENTXlMrcy7WIO6drN8w1Pb9SRhAJ1paWiUyEc60vzc78GUaXmLUmdguMOfD56dj7IPF4ygrtZ64Zs/y+cwUZQec0br3wv0bvCE7WYWjvj5ULs+FU1wuznXXAskxtw8tAtNhRFz90T5Fi/ADV15Vq3Auu312t9PIw/5lfusSHgdOZWUYHRnTud6lwIoQuDDxoCSTLyDHfY16I3XLzi6etnZo4FN6MnBYccxZiA6/8kfkIWGLUNjwJVBbn61ryyzMkkYIG6kKlE/eDs0RmhH4QZn22bOSVnqDdo++kOQyt23NFod7gd9lngnKKgvt95xIygs4pnFd/eADs23isQvq78Eba1KoxE+DU0guBzniw/KxGMZRrP8+8VveaYeQelSXKOEoDX+s/Eg+LK6xzJE6ESd3nlbkcAelHnQP0DVz7oea1z5HTuF2If+ux50hqEIxp+sWg2CoQ2+XKjsxBYGmubuRJ/vL/kWcQuWbllLKYSqBt1prrM+DLAT8suXIQpg77fvjl10AP019nPgX3+84lpEFnN2auWfzjJkxKXqKNhA+Off3UpdfgyIunq2QcBcs6tSqv4j23iqJeVvlCGU5J0I5qD+pfwJdFlkfHpcGiRr+OSYy+bn+lgMRTbnHv+X6Xfsr8FcaM+mJ2bH04Nfj8Qec5sHRQt9P32nnM84gLiLJN+ZwoyOylVdEfh5nfLF0XVw/WLNjePznsuv0Lyvedy0iDzm7ccs3fW9Y+mlAXVhXfOi1pCKgnlKE8CPjnUm45uzF/uGnKWqpCatF0h/4STJ21aNrlt+DkJxdqO3/1dCNzEbd69dlXVpy9Egk7Bx1KSZ0J6l5LA4ZxLbfsbYjvW/1QaUQLiP35SlnXWzClw8K7Lq+Dy0US6rhWebqR+ReZg84hzr57sVXmIJgdt/zIld6Q4XL20+PA8qn6CiUwbgm9IZLKilTS0cFSV11Bbzj488nN6Ukws+OSH+PKQrdhLUeFtAFtqZ7JRvLtEHV1inIPLeHULxcKOZNgvs+qVgknQWuv9eRJUBupR0Spo3dEzlkhyiotuyz1GQabf9vfL3UsLPtuQ/fEntCDNoR4uo35EGnQOcSqXVv6JPWHg/1PfpfRFtRi6ha+BcZRz6t/qO6EEYQFSDQ6mWb9+cMbVybDPMcvhxMSgFACqeXpRuYcysscIwmcMzNf1u+GlCLp1bWRoHZSmxCE96Q0bvbzPk5bQsFZLvMr3Q++e+pXv4RA6KA0XhTUGIL1gDHqOk+3Mv8gDTqbSZ2Q/pneC1bU+n17Ujhkts4cptcCSxHLSlHJmq9+sOZ9gNKXjoRB5neuX/V2kPB60qeu+p5uXC4QILLQG3DhB8pkpR7+gG8++55NzJTH48oRBsDeU0ed6U/AzqhDYam/wD3Uxd/TbcxHSIPOZo70PF004xDsr3Fsffo5UC6pI3kbqJhPf7AmYm4JpQONCQRQZhtaQNgr1BeHp5uSGyhvKE9QGJJXpDXQKsHakJ3NU/rAPdSt6/+lp1uXf/DGbqo8zdHvzjyScR4ul07AFQNqVeUC9TzdKokkmxGRtL5Mz+Q3OBB7fG/6BUjV0hP0FzzduPyDNOhsZn/4sapp68F1h2u7PgrIyKP1rhJJNqBuVQbQEU49GTPFGQKxX1xpntnJ063KP0iDzib0S1RmJ5yoeH6/cwFoS/U7mMZf53OWSPIRShMljQYQuz9uYGZ3uLIysamrjqdblX+QBp1N6NHaTJZA5unMTfr9yJFVkoJBYcKwgn6PPovFkHnctYH7Pd2o/IPsJMwm9F/ZTjLwHj4kAXYxDWeap1smkeQ8uhgirm/T53LJ063JP8gIOptQv1La0gbUN9Qk5TngFBdwerpVEkkOY87dsZTxlAZrC8sHTPB0o/IP0qCzCeV5pQz3QNEz4T9Yy4CyTHmItshUhyRfo4fSnP0QEhg4ybIBAgP87lKberpV+QeZ4shmqv9R7hHHRLCkqCVQADsniCHvzl4mkfwHtBLaaBZAqeNFStqqQ+Hnw/pajwPwEBU93TrvR0bQ2Uy51BJT7UcgoJ7fBEtJ0JvrAznm6VZJJNmMWMJL6aW0IQTKRZZYbj8FAQ7fZ9RwTzcu/yANOpuptLJUF8d3ULFsqSP2j0A7onfmC2S5nSRfof/OXlLB0cg2T2kKTSrU+dZ/JTCIRwhHpvayCZniyGaCZvkPUd+G+xLrBQTUhG2RB8qlvgusYySZQFGjLCnfcZ5LZILeSX+JE6C0V2w0BALxy9dhQIDx+fRaPMYRYI6+knhQpigvURzvme/7ZhHT6WoHtI7Mgqh3y8ywvwT1Hqxcz+cBYKnYT84PnS3kR6vIE7R+5y5HQDf42vHjz1cscDLi/BvO+aBa1Sl0J//McmY1lnjSvtVT2ABt5zZMDzwFnbR7DgR9DnpFvQ9HgXw64aj6oRpNCziVfmG0MwAmbZ7X81IyXLmc2MxVBxQ/ZRc1yTcRpX6YU2SA+r7yDh2hbXzD9MAzUGRpWA1rQZgcK5eRBp1DVNgTud/eHR6c2Kx3cDOY8Nrc+y8uAD2RFBJAuZ+7CcR7bUusGKM9oi9hORRvGbHGNgkG1nr42/CmUL10ufcdcz3dyFxgCocAtCl6SSbDkfjTfhkrYI66ouEVGygJygaqA4H4e/WdhFgsVuuvpTIeqrYr+4KjPTzQrGnVoFAA3gWu/T/L+Dlb8OZ/mbyJueTVIt6jNHT/qVWr4GFwR6NKj/jOB22BVpOpgHrj4qJeg+gc0t/T53AR1IeUvTwFPX5rMzfEAtXnlqvreMfTjcxFxPetDlBO8ww8Edx+cmhRKGcvMcd+DFz9tTTGA35i9XNvQ6Q09EJ6aw6A/wyfzuo+6Ptgh0mhnaBUUJF3bbWv298b/6fzMN74L5O3cVvyqljf8FPWPjD4l26FIj6HQjNDKlo+AK2YNoL5eM/SVyKnqG/lD9JA304wW6BNeMNGgZ9Bj8NtVobYgZd4jAiuXajyO27fd8WvSrWwz4fBk7olhk+FkAoBX1k2gTZc380qwI7NK0xMtFH/le2kgJ6h38EeeODTpueCCkHHlk0KBy28bn/Prkmeb/EGa/BO3HKO9/Svc9xvPbx0d49PCvWDoE3+W9XeoHXWv+MH8m6EpV415gOkgrZTa8NMuGtGtQl+n8LQP3vOLXQYQvoF/KHu5eoq4O7Gle9xuyB1CGpcNugVeGZil5fCngTf6fbV6uugzdeT2QA4xFQAeQ3zQryBPaSAXkf7mO+hxRMNugakwwsPdns34idwrLAHKA8B6cZSWLJKKWcoWD+i3MTsxTY7A1fxEWWhq6P5leA6MLRxr8RCFyG4X0Cw5Rtw9dWSGQ8kGUtHebwXXHT+6X300ZwBfaUOv0OjcTUP+RWH0Xf3f7iIC8o0LXavbeJ1r/OWCDG7MS9I4gJluU99nwnwxIcdMkNrwcDaD48KOwABUb4j1GKgpWj9+IqrQ6U9/n2LHLNeWu/IQdCn6zGsgxanG/wcEAyjij31YpGtUCQ8LMzi5OoahXn2QpNPkAad0wijM41anakGMQQec7QODzkJY+c93bTIAKj4YCmbYzq4fF0LeR30eL06u657fU5jGowol3Pt1dozC6zHLD7KfOj0c5P/Ba2F8W2e+7poH6i0oHS4/aCnT24exLxACQNzNLF9qzSFp3c/eDz8Phh+4slehc9AsQ8jfKxPg6u3lsA40FvoAznOVaPMccwL8C9sJRlcma7ZvAq+b/oMVmdDzy5tjoe0gndOPFOv6Ewo0aFQH+sPXLtD8pHGnBvIKo7cws2olZVKd9pBe6K7BwIVa0TudAyHKRMXWi+vhx+XbxqU+CXEz05K07qB8rPag/agPqoEE821iOt2y7dEFYa+kDUkgHZSe4QvQFnG15SGcqtLRNh3Q59P2qeEpsBDhZodDhoBAbP8AtUIrg1dF8eRuGEamIiQrT9alrILutGCYKDS46UaOKJh8uBvx12aDr/+uOOF5AhI+TJtuN4NLA3VX+gDSlnlJHWvHee2ERcOfbQ+k1jQ1uhN+AwsseoKJkNNWwU/n3h4Ynb75aFloMPqxksCU8F+zNZb2cO1eu7cuoBIAGnQuY9p1GbOUkSulfaUrm0/Bu/se/aJIuugY9km4wM3wbczfvksYTtsrLjng5REuNj3iiOzLLie1JIYD0or5S4CQOmphNEYlEB2UxO4V6mPP9BLH8EZ0FbrVn4HfaPux2ZQtiufEwWOzbY1SgMoWyjygv0ctJ571/yAu6Dzvc1aB02D8o4S39qLAQmi/eatrYygbg6HiKjFmo1YjGxt3a2VNvmsh4mpL3xY7CKsfnvb/uQ5MH/eKi1+AeyIOdQ07XOI25/4vmsdaGiD+AaUycqLFAelhxJKNCip/EpVoKJSCgfQQn+O46CJpaj0U3o5doBSQ4nlDvDd6xihvA2VV5Wu5rBD+7hoLagStK/auEhgayg+I6KFdep17Te/b2nMHkEatKdw70RzGZG1o5ptutIYmnEH/kCj9bU+8dsBB988sT6jPmx55sDu1Adg0x17H03pA+feuNQ8cwNcfP3KlcyHIO7pxM6uIMgck3mG+8GvuM9mZTlElA95w/oOhAcGx1ruhwqbIz91jIaGcTW7+X0Bd7St9ITPAigaFz7Y2gaAHm7tMy4o0phvD4tbN5q48wnw9R2oRkD7fdEDA5+D5oPrjwroDvs/OBaVvhY2r94fmzIbNg/c/3ZqNMQ64x7L7AyxW68sdvWH+F+S2rh6gmu61o6PIaCnb5KqQERKyFnLmxDeIPiw5T2o/ki5eJ/XoMG8ap/6FYO6RSp96TMSwr8JrmU5BcCZG9prVmXI79ujSIPOK1j+Ptdsb2Q9oYRDjUblcfwJNSiPA+g19f5nQp6DpDdTHtVC4FLF+KjMLhC/M7mTNhAy27lq6d+Bb6JDVUdBWGhQmqUmhLYNHGc5Dr5jHVuVY8Bo9lIE2OP2xmbqxEylWGT8lK1cS1GZjwHw/cAxTPkK7qAyPsAdVO7qA/Sd2HEMwyBhbnJP1xNw6VL8EFcvSFyY0lT7HLQaWkV9AfgN8qmuboOwoOAeligI7R8Qa1kIjrV2uzJGvNcYYB2v3tAe9+9bVmXkCaRBeymWdupkJkFwuwBUIJgA7MamWD5023n+3xyg07+8gaerCgoKN3mWrYMsQ1kPYQRhAcIKB2HpB+xkK/3cdo4CIB6ARUD1m2qH/L7zIDIqkkgkkjyKNGiJRCLJo0iDlkgkkjyKNGiJRCLJo0iDlkgkkjyKNGiJRCLJo0iDlkgkkjyKNGiJRCLJo0iDlkgkkjyKNGiJRCLJo3i/QZtDVOVsWxKJxJz+1pY/Fo7wfktLIQ0N9Dms5IqnGyORSDzKVv0PUoGN7CaFLCch8xa816DFYquu1zS7PhFiX4kr72pz3XZXAVm0VCKRXCVpQ+oz2mlICE8uqn0KykllEZU83arbx3sNOgA/VNDu104wFk5ZL7RzAtSmB0eQ02NKJAWQi+Pi17vmQFyTxOOukqD4KDuo4elW3T6miXlftCnmq1WeUArTGA7fdapu+tdw8bErM1yfebpxEokkV3BbBmx3jSORaUUgfX/GE9o6UO6iGn6ebuTtIwzaFe/phtz2BxislKUpnLkr1pHZG+KaJ55xRXq6VRKJJFdwyy8fqXL6csYuyCzjSuMhIIRAb76XFk1POu/phtw2k3mR4pC0O/Ul11k42ud0iYzD120Xi7RKJJL8S8IXyau10XAs/mzJjHagfM87lOKvS415GcKgL87xdENuF+VJpQOhkPZ+eh/9PdhS5cCM1BPX7WD17l5ciUTyN5iL2QpO9D7X2PkeHL37tDNjD6in1dfo4ulG/neEQZ97zNMNuW2KEYEVGKs8TRFYsmD94IRvYPOK/a+nfnHdfrKqQyLxfrQbFy9OnZU+V+8D06cuiYuzwcXzV+7LHAuKRdnizZ2DJsKgYx51e957kgJisUv1daUa98KZNrHhmU/Ch8vnDbgUBXHTEh93wbWqDs2LPptEIrkR9caM8qKaa5sn9INl7204l+gE2hFNIFCCQl684urVYFJ82Mv93XbwvqSA+OLUwWoMz8La4Tv3pTwBs0cunxjvB+zkMGlcW61YGrVE4j04ybz+F3vwvZPFMurAx1ELi17+CVLWpq/T+oOyV/mKCnj779ss3nhR/LH3E3EWbmb937yJiKSV3rQjFFy/aUP1F2Dml8vGxvWCDTN2v5Gy5oZTYBh1Ok6v/iolkvyI5paSFEO4k15Irabtg0l15ymXAuHP+05vyVDBUln9nl5ARn76PR98QRj0rrmGpk7wdJP+M+JKq/5PKU9TiNkWNz2zGrzRc+rQmC/gh4CN7ZJCIbO3qw/1AEf+GLMvkeQLzM4/t1TG8bRzMc6hMPzhaY1itsKKh34rnPgSKP2V4jQBAvH35nI6gVs/2YHi4iOd2m3o+dmebmG2IeboUD9Q7qIF/BF6YmV6CRhSftJL547CW2OmJ8TUgDOfxY7LbOnpxkokEuBq558zOnMYCiyesG5S4svQr8WYXWfawVzXz4fjMyFjiPMb3R8UURxg3kF7OcKPtQGG7o1QIiM7dPjjD/tXxhNf+xvauYOnW5rtiFsk/bheiu3AUzxAGNSsHlXFJw0GrXtkTXgkNBlTJ81/M/gMsu9WWgOLeI/Snm68RJK/cR3QxjAEzvSMedQZDZ8EfjflcggsfGCNI+EcJBZPOaNtBLWDuoN+oFQgEjt/TYXkCxKeNPSBCsKgzQ0vNzZ07FpPNzHHELdO+mFOkgHaJq05MyAkIeB1yxqo7lv+dcdUqOxTurvjPNRsFvWCz04o9mtENetgqOCKTHf0Af/ffQarR0FB+Z3qQBIp+fFfRSK5LWzYAFjKBhIh80tXD/1ZONM7tqczGo5NOTvD2R+Otz/3ZMajsHP0oV1pO+DgqBPh6bXg6IAzFzJ8gPmsIgGU08piKgEZN3YW5k92iKq69m3cDLpRO0N/qGto0FuebmqOI+aR1kvpHTkI2j6tPbOAj5QXKQaWFWoZXoGAD/0OWapD0WZhd1otEOYfVM4SCfa7bceUcOB39pLi6Q8jkeQRihKODbT39CP6akjsnNxGmw0xoXG7MxvApZPxDtcSyGjgHKoroC/XNX4DxaVspDqoKeq7PAyEEICF/JLCuEk+/d7QAa+6GXRIoKFLaxvaKP9G0v+GuRDAGWJxgl5Zf4TDoI3VD7IaeJ+vuQQkishZdjRKJH/FggWAypTGAUox5Qi1QWmrWGgISjdaEuzpRuYVnGJap64ixbxwrlnKLZLTVxINXZlmaCPzldqN+xUAzCt2cSKwgZKgrKIqWJ5WoCoAjT3dRInEazhz9a8KAOSfcoRs5JC4VG26aD6TxXSjy0Rd9IU5bvtJJBKJJHvIuPHh8iqGnvnFfCYL4937gaFrvnLbILOsEolEkj3YDblcy9CFvcTzZr5duZo5vTEXbdL+B0O/KWSoXz1PfyKJRCLxckQKGR9D5onpnnveY2j6IXPHf0ldrH7X0DUfuW2QBWUSiURyewhjThxs6AwR+F4z5lOnFi+uXPk6gzafuJGkNYZ+eqehKT3FBvN1Baj0RSKRSLKT5ecM/bVBVnvcZOffTysNXdTJbYMsLpNIJJJ/xi3jEDPL0A+nGJq2UGywuAfKfzFotx1EKJ662NAPphp6ZqLbyzKQSCQSyd/h5rMz2xi6cYjbfq5/eeE1hFGn3fjsFlE3/XGUoVpZscHu6TMgkUgkeYykGx9uFTpFGLQ2Qzxh//sU883XN1uELhNvUNjQH8d6+gxIJBJJHsPMKAQYktDc0JF7DT2xQ2w3UxpZZiD+1aDFAczQ22FInOg0fK29oYfdrhQke/oMSSQSSS5j5ppFRkETRRUfiPK5ZQOy2D9LbnWEYPqND7eL6UlHdDU0yRwbLp4n1YMnSyKRSHIDs5rNzU8XiyHbE42Z/XCtFxusIvD91yq4mzZotxyJ2+u+Fjnp0SJUT58uNvgKlZ2IEokkv+JWzbZe5JaHiBHZcUPFBofw0cybPfAtz7Eh3sAMzUXKwyWqOiaK402KFs9HiP3MTkSnZ86fRCKR5DS7Vhg6aL6hR927/dJv6XBkQx2zGCIupskz5+oIFKH7WDGv9FMiorbUEfu5DXWUSCSSPM/VOTJufPqAmB6033FD1+9xe51ysykNd7JrljpzEiWRe04UH+BVUZb3kZilyTlQ7Gcas+xMlEgkeR0zY+BmzLuWG9rvqKHZZ8wm/9mg3XLTpuEKo47fYuhrogBwzEZDU8veuJ/MUUskkjyH6UtuPrl+pqF9PhSP97u/8L8as0m2D9V2mxXPNGBh3LbDhvYTV5o3Bhla+KTbYbK4lZBIJJIcxywbFnXMZrnc4hhDh0ww9GgVt9f954jZnRw3QGHYZkrDzD0PM+Q+8XCMKMerP87t5WZvpxWJRCLJGbJYMSqhpaFmHbNZBBH3qtvrs92Yrx44t86AMGpzRKLbmPMyYqmtF/809PHXDPVb4nYYswrEllvtlkgkBY2twrBHHjDUHGDiWid2ENVrpGc1RDu7yPUUglsKxIyMRaRs/83Q9p8Z+j+RA7rbNOo4t8PJCFsikdwsWVSPxXxp6MxWhk4RemKn2+utt1rH/F/xeI5XGLY5oMVt5GHR3w19XIxU7N3O0IofZnE4adgSicTE7ORzm8wt8XlDl4uVTD6cZuhGMYG+Zg60M+/4tZxKYfwbHjdoE2HUZnuyOBHl3zC0+wJDu042tLKYtk/d4unPIZFI8hrmmn8/i3ntZ9xhqDlRftp3bi+w/9skRrlFnjFoE7cUSBY566t79zW0lRih82ApQ+8UayiGPXeTbyurRiSSvEsWnXjuOMWAuUMhhpqrZC8UVRjb3zb02tJSgqs+k9M55VslzxuSm2H/S4Tt976h1UVKpLmYdS9aFJTXvmJoWBNDHVnNJmVeECxIJJLc4l+MWOtvaJJIZR4VKdHNDxv641xDN8UaemaVeGGWqYm8Zsju5HmDzgo34zarOrKY6yNApEaifjS0zuOGRotymXJiftbaRQ0NkQsQSCQewyVSlgdfNPSA+F3uDTV07ctiewlDz44xVN+fxQGvBnZ53ZDd+T9zjIe7yzf0WQAAAABJRU5ErkJggg==\",\n\t\t\"base64\",\n\t),\n);\n",
11
+ "/**\n * JWT signature checks on WebCrypto, without a JOSE library (DEC-076): the tab\n * assertion and the provider's ID token. Only asymmetric algorithms, each tied\n * to its key type; keys come only from a configured JWKS URL, never from the\n * token's header (`jwk`, `jku`, `x5u` are ignored).\n */\n\n/** The accepted algorithms; never `none` or HMAC. */\nexport const JWT_ALGORITHMS = [\n\t\"RS256\",\n\t\"RS384\",\n\t\"RS512\",\n\t\"PS256\",\n\t\"PS384\",\n\t\"PS512\",\n\t\"ES256\",\n\t\"ES384\",\n\t\"ES512\",\n\t\"EdDSA\",\n] as const;\nexport type JwtAlgorithm = (typeof JWT_ALGORITHMS)[number];\n\n/** Longest JWT the relay reads. */\nexport const MAX_JWT_BYTES = 8192;\nconst MAX_JWKS_BYTES = 65_536;\nconst JWKS_TTL_MS = 10 * 60_000;\nconst JWKS_REFETCH_MS = 30_000;\n/** An unknown kid after a good fetch: a rotation is looked for this soon. */\nconst UNKNOWN_KID_REFETCH_MS = 5_000;\n/** How long the last good keys are served while the JWKS cannot be fetched. */\nconst JWKS_GRACE_MS = 60 * 60_000;\nconst FETCH_TIMEOUT_MS = 5_000;\n\nexport class JwtError extends Error {\n\toverride name = \"JwtError\";\n\t/** A short reason for the log. */\n\treadonly reason: string;\n\tconstructor(reason: string, message: string) {\n\t\tsuper(message);\n\t\tthis.reason = reason;\n\t}\n}\n\nexport interface Jwk {\n\tkty?: string;\n\tkid?: string;\n\tuse?: string;\n\talg?: string;\n\tcrv?: string;\n\tn?: string;\n\te?: string;\n\tx?: string;\n\ty?: string;\n\t[key: string]: unknown;\n}\n\nexport interface DecodedJwt {\n\theader: Record<string, unknown>;\n\tpayload: Record<string, unknown>;\n\tsigningInput: Uint8Array<ArrayBuffer>;\n\tsignature: Uint8Array<ArrayBuffer>;\n}\n\nfunction fromBase64Url(part: string): Uint8Array<ArrayBuffer> {\n\tif (!/^[A-Za-z0-9_-]*$/.test(part)) {\n\t\tthrow new JwtError(\"malformed\", \"the token is not base64url\");\n\t}\n\treturn Uint8Array.fromBase64(part, { alphabet: \"base64url\" });\n}\n\nfunction jsonPart(part: string, what: string): Record<string, unknown> {\n\tlet value: unknown;\n\ttry {\n\t\tvalue = JSON.parse(\n\t\t\tnew TextDecoder(\"utf-8\", { fatal: true }).decode(fromBase64Url(part)),\n\t\t);\n\t} catch {\n\t\tthrow new JwtError(\"malformed\", `the token's ${what} is not JSON`);\n\t}\n\tif (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n\t\tthrow new JwtError(\"malformed\", `the token's ${what} is not a JSON object`);\n\t}\n\treturn value as Record<string, unknown>;\n}\n\n/** Split and decode a compact JWS (no signature check). */\nexport function decodeJwt(token: string): DecodedJwt {\n\tif (typeof token !== \"string\" || token.length > MAX_JWT_BYTES) {\n\t\tthrow new JwtError(\"too_large\", \"the token is too large\");\n\t}\n\tconst parts = token.split(\".\");\n\tif (parts.length !== 3) {\n\t\tthrow new JwtError(\"malformed\", \"the token is not a signed JWT\");\n\t}\n\tconst [h = \"\", p = \"\", s = \"\"] = parts;\n\treturn {\n\t\theader: jsonPart(h, \"header\"),\n\t\tpayload: jsonPart(p, \"payload\"),\n\t\tsigningInput: new TextEncoder().encode(`${h}.${p}`),\n\t\tsignature: fromBase64Url(s),\n\t};\n}\n\ntype ImportParams = Parameters<typeof crypto.subtle.importKey>[2];\ntype VerifyParams = Parameters<typeof crypto.subtle.verify>[0];\n\ninterface AlgorithmSpec {\n\tkty: \"RSA\" | \"EC\" | \"OKP\";\n\tcrv?: string;\n\timportParams: ImportParams;\n\tverifyParams: VerifyParams;\n}\n\nfunction spec(alg: JwtAlgorithm): AlgorithmSpec {\n\tconst bits = alg.slice(2);\n\tconst hash = `SHA-${bits}`;\n\tswitch (alg.slice(0, 2)) {\n\t\tcase \"RS\":\n\t\t\treturn {\n\t\t\t\tkty: \"RSA\",\n\t\t\t\timportParams: { name: \"RSASSA-PKCS1-v1_5\", hash },\n\t\t\t\tverifyParams: { name: \"RSASSA-PKCS1-v1_5\" },\n\t\t\t};\n\t\tcase \"PS\":\n\t\t\treturn {\n\t\t\t\tkty: \"RSA\",\n\t\t\t\timportParams: { name: \"RSA-PSS\", hash },\n\t\t\t\tverifyParams: {\n\t\t\t\t\tname: \"RSA-PSS\",\n\t\t\t\t\tsaltLength: Number(bits) / 8,\n\t\t\t\t} as VerifyParams,\n\t\t\t};\n\t\tcase \"ES\": {\n\t\t\tconst crv = bits === \"512\" ? \"P-521\" : `P-${bits}`;\n\t\t\treturn {\n\t\t\t\tkty: \"EC\",\n\t\t\t\tcrv,\n\t\t\t\timportParams: { name: \"ECDSA\", namedCurve: crv },\n\t\t\t\tverifyParams: { name: \"ECDSA\", hash } as VerifyParams,\n\t\t\t};\n\t\t}\n\t\tdefault:\n\t\t\treturn {\n\t\t\t\tkty: \"OKP\",\n\t\t\t\tcrv: \"Ed25519\",\n\t\t\t\timportParams: { name: \"Ed25519\" },\n\t\t\t\tverifyParams: { name: \"Ed25519\" },\n\t\t\t};\n\t}\n}\n\nexport function isAlgorithm(v: unknown): v is JwtAlgorithm {\n\treturn (JWT_ALGORITHMS as readonly unknown[]).includes(v);\n}\n\n/** Whether a key may verify `alg` (type, curve, use and its own `alg`). */\nfunction keyFits(jwk: Jwk, alg: JwtAlgorithm): boolean {\n\tconst s = spec(alg);\n\tif (jwk.kty !== s.kty) return false;\n\tif (s.crv !== undefined && jwk.crv !== s.crv) return false;\n\tif (jwk.use !== undefined && jwk.use !== \"sig\") return false;\n\tif (jwk.alg !== undefined && jwk.alg !== alg) return false;\n\treturn true;\n}\n\n/** Only the public members: a private key in a JWKS must not become a verify key. */\nfunction publicJwk(jwk: Jwk): Record<string, string> {\n\tconst out: Record<string, string> = { kty: jwk.kty as string };\n\tfor (const k of [\"n\", \"e\", \"crv\", \"x\", \"y\"] as const) {\n\t\tconst v = jwk[k];\n\t\tif (typeof v === \"string\") out[k] = v;\n\t}\n\treturn out;\n}\n\n/** A source of verification keys (a JWKS URL in production). */\nexport interface KeySource {\n\t/** Keys for `kid`; may fetch again when the kid is unknown. */\n\tkeys(kid: string | undefined): Promise<Jwk[]>;\n}\n\n/** Fetch JSON from an operator-configured URL with a timeout and a size limit. */\nexport async function fetchJson(\n\turl: string,\n\tinit: RequestInit = {},\n\tmaxBytes: number = MAX_JWKS_BYTES,\n): Promise<unknown> {\n\tconst res = await fetch(url, {\n\t\t...init,\n\t\tredirect: \"error\",\n\t\tsignal: AbortSignal.timeout(FETCH_TIMEOUT_MS),\n\t\theaders: { accept: \"application/json\", ...(init.headers ?? {}) },\n\t});\n\tconst length = Number(res.headers.get(\"content-length\") ?? \"0\");\n\tif (length > maxBytes) throw new Error(`response too large (${url})`);\n\t// Counted while it arrives: a body without Content-Length (chunked) is cut\n\t// off at the limit instead of being buffered whole first.\n\tconst chunks: Uint8Array[] = [];\n\tlet size = 0;\n\tconst reader = res.body?.getReader();\n\twhile (reader !== undefined) {\n\t\tconst { done, value } = await reader.read();\n\t\tif (done) break;\n\t\tsize += value.byteLength;\n\t\tif (size > maxBytes) {\n\t\t\tawait reader.cancel();\n\t\t\tthrow new Error(`response too large (${url})`);\n\t\t}\n\t\tchunks.push(value);\n\t}\n\tconst bytes = new Uint8Array(size);\n\tlet offset = 0;\n\tfor (const chunk of chunks) {\n\t\tbytes.set(chunk, offset);\n\t\toffset += chunk.byteLength;\n\t}\n\tconst text = new TextDecoder().decode(bytes);\n\tlet value: unknown;\n\ttry {\n\t\tvalue = JSON.parse(text);\n\t} catch {\n\t\tthrow new Error(`not JSON (${url}, HTTP ${res.status})`);\n\t}\n\tif (!res.ok) {\n\t\tconst error = (value as Record<string, unknown> | null)?.[\"error\"];\n\t\tthrow new HttpJsonError(\n\t\t\tres.status,\n\t\t\ttypeof error === \"string\" ? error : \"\",\n\t\t\turl,\n\t\t);\n\t}\n\treturn value;\n}\n\nexport class HttpJsonError extends Error {\n\toverride name = \"HttpJsonError\";\n\treadonly status: number;\n\treadonly error: string;\n\tconstructor(status: number, error: string, url: string) {\n\t\tsuper(`HTTP ${status}${error === \"\" ? \"\" : ` ${error}`} (${url})`);\n\t\tthis.status = status;\n\t\tthis.error = error;\n\t}\n}\n\n/**\n * A JWKS URL, cached 10 minutes, fetched again for an unknown kid at most every\n * 5 s. After a failed fetch the last good keys are served for up to an hour\n * (an outage must not refuse every token at once), and the next attempt waits\n * 30 s, so a dead endpoint is not hit (and waited for) on every check. An\n * unknown kid that could not be looked for again throws (the keys are\n * unavailable) rather than answering \"no such key\".\n */\nexport class JwksCache implements KeySource {\n\treadonly #url: () => Promise<string>;\n\treadonly #now: () => number;\n\treadonly #load: (url: string) => Promise<unknown>;\n\t#keys: Jwk[] = [];\n\t#fetchedAt = 0;\n\t#attemptedAt = Number.NEGATIVE_INFINITY;\n\t#lastError: Error | undefined;\n\t#inflight: Promise<void> | undefined;\n\n\t/**\n\t * `load` fetches the document; the default suits an operator-configured\n\t * URL. A URL a client chose needs the SSRF-guarded fetch (DEC-106).\n\t */\n\tconstructor(\n\t\turl: string | (() => Promise<string>),\n\t\toptions: {\n\t\t\tnow?: () => number;\n\t\t\tload?: (url: string) => Promise<unknown>;\n\t\t} = {},\n\t) {\n\t\tthis.#url = typeof url === \"string\" ? () => Promise.resolve(url) : url;\n\t\tthis.#now = options.now ?? Date.now;\n\t\tthis.#load = options.load ?? ((u) => fetchJson(u));\n\t}\n\n\tasync keys(kid: string | undefined): Promise<Jwk[]> {\n\t\tconst entered = this.#now();\n\t\t// A fetch already running answers everyone who asks meanwhile (many tabs\n\t\t// after a relay restart, a new kid during a rotation): without waiting,\n\t\t// they would find the throttle set and read the old (or no) keys.\n\t\tif (this.#inflight !== undefined) await this.#inflight.catch(() => {});\n\t\tconst now = this.#now();\n\t\tconst stale = now - this.#fetchedAt >= JWKS_TTL_MS;\n\t\tconst unknown = kid !== undefined && !this.#has(kid);\n\t\t// An unknown kid may be a rotation: after a good fetch it may look again\n\t\t// soon; after a failed one it waits the full 30 s.\n\t\tconst floor =\n\t\t\tunknown && this.#lastError === undefined\n\t\t\t\t? UNKNOWN_KID_REFETCH_MS\n\t\t\t\t: JWKS_REFETCH_MS;\n\t\tif ((stale || unknown) && now - this.#attemptedAt >= floor) {\n\t\t\ttry {\n\t\t\t\tawait this.#refresh();\n\t\t\t} catch (error) {\n\t\t\t\tthis.#lastError = error instanceof Error ? error : new Error(\"JWKS\");\n\t\t\t}\n\t\t}\n\t\tif (this.#now() - this.#fetchedAt >= JWKS_TTL_MS + JWKS_GRACE_MS) {\n\t\t\t// Nothing usable: no keys yet, or keys past their grace period.\n\t\t\tthrow this.#lastError ?? new Error(\"the JWKS could not be loaded\");\n\t\t}\n\t\tif (kid === undefined) return this.#keys;\n\t\t// Unknown, and the keys were not fetched again for this call (throttled\n\t\t// or failed): the key may exist and not be loaded yet, so this is \"the\n\t\t// keys could not be loaded\", not \"no such key\" (review of PR 65).\n\t\tif (!this.#has(kid) && this.#fetchedAt < entered) {\n\t\t\tthrow (\n\t\t\t\tthis.#lastError ??\n\t\t\t\tnew Error(\"an unknown key id; the JWKS was fetched too recently\")\n\t\t\t);\n\t\t}\n\t\treturn this.#keys.filter((k) => k.kid === kid);\n\t}\n\n\t#has(kid: string): boolean {\n\t\treturn this.#keys.some((k) => k.kid === kid);\n\t}\n\n\t#refresh(): Promise<void> {\n\t\tthis.#inflight ??= (async () => {\n\t\t\tthis.#attemptedAt = this.#now();\n\t\t\ttry {\n\t\t\t\tconst doc = await this.#load(await this.#url());\n\t\t\t\tconst keys = (doc as { keys?: unknown } | null)?.keys;\n\t\t\t\tif (!Array.isArray(keys)) throw new Error(\"a JWKS needs keys\");\n\t\t\t\tthis.#keys = keys.filter(\n\t\t\t\t\t(k): k is Jwk => typeof k === \"object\" && k !== null,\n\t\t\t\t);\n\t\t\t\tthis.#fetchedAt = this.#now();\n\t\t\t\tthis.#lastError = undefined;\n\t\t\t} finally {\n\t\t\t\tthis.#inflight = undefined;\n\t\t\t}\n\t\t})();\n\t\treturn this.#inflight;\n\t}\n}\n\nexport interface VerifyOptions {\n\t/** Required header `typ` (compared exactly), if any. */\n\ttyp?: string;\n\t/** Allowed algorithms; default all of `JWT_ALGORITHMS`. */\n\talgorithms?: readonly JwtAlgorithm[];\n}\n\n/** Check the signature of a compact JWS; returns the decoded token. */\nexport async function verifyJwt(\n\ttoken: string,\n\tsource: KeySource,\n\toptions: VerifyOptions = {},\n): Promise<DecodedJwt> {\n\tconst jwt = decodeJwt(token);\n\tconst { header } = jwt;\n\tconst alg = header[\"alg\"];\n\tconst allowed = options.algorithms ?? JWT_ALGORITHMS;\n\tif (!isAlgorithm(alg) || !allowed.includes(alg)) {\n\t\tthrow new JwtError(\n\t\t\t\"alg\",\n\t\t\t`the token's algorithm is not accepted (${String(alg).slice(0, 20)})`,\n\t\t);\n\t}\n\tif (header[\"crit\"] !== undefined) {\n\t\tthrow new JwtError(\"crit\", \"the token has critical header parameters\");\n\t}\n\tif (options.typ !== undefined && header[\"typ\"] !== options.typ) {\n\t\tthrow new JwtError(\"typ\", `the token's type must be ${options.typ}`);\n\t}\n\tconst kid = header[\"kid\"];\n\tif (kid !== undefined && typeof kid !== \"string\") {\n\t\tthrow new JwtError(\"malformed\", \"the token's kid is not a string\");\n\t}\n\tlet candidates: Jwk[];\n\ttry {\n\t\tcandidates = (await source.keys(kid)).filter((k) => keyFits(k, alg));\n\t} catch (error) {\n\t\tthrow new JwtError(\n\t\t\t\"jwks_unavailable\",\n\t\t\t`the signing keys could not be loaded: ${error instanceof Error ? error.message : \"unknown\"}`,\n\t\t);\n\t}\n\t// Without a kid, only an unambiguous key is used.\n\tif (candidates.length === 0 || (kid === undefined && candidates.length > 1)) {\n\t\tthrow new JwtError(\"key\", \"no matching signing key\");\n\t}\n\tconst s = spec(alg);\n\tfor (const jwk of candidates) {\n\t\tlet key: CryptoKey;\n\t\ttry {\n\t\t\tkey = await crypto.subtle.importKey(\n\t\t\t\t\"jwk\",\n\t\t\t\tpublicJwk(jwk),\n\t\t\t\ts.importParams,\n\t\t\t\tfalse,\n\t\t\t\t[\"verify\"],\n\t\t\t);\n\t\t} catch {\n\t\t\tcontinue; // a broken key in the set: try the others\n\t\t}\n\t\tconst ok = await crypto.subtle\n\t\t\t.verify(s.verifyParams, key, jwt.signature, jwt.signingInput)\n\t\t\t.catch(() => false);\n\t\tif (ok) return jwt;\n\t}\n\tthrow new JwtError(\"signature\", \"the token's signature is not valid\");\n}\n\n/** Claim helpers shared by the assertion and ID token checks. */\nexport function numericDate(\n\tpayload: Record<string, unknown>,\n\tname: string,\n): number | undefined {\n\tconst v = payload[name];\n\tif (v === undefined) return undefined;\n\tif (typeof v !== \"number\" || !Number.isFinite(v)) {\n\t\tthrow new JwtError(\"claims\", `${name} must be a number`);\n\t}\n\treturn v * 1000;\n}\n\n/** `aud` as a list (a string or an array of strings). */\nexport function audiences(payload: Record<string, unknown>): string[] {\n\tconst aud = payload[\"aud\"];\n\tif (typeof aud === \"string\") return [aud];\n\tif (Array.isArray(aud) && aud.every((a) => typeof a === \"string\")) {\n\t\treturn aud as string[];\n\t}\n\tthrow new JwtError(\"claims\", \"aud is missing\");\n}\n",
12
+ "/**\n * Identity binding (DEC-075 to DEC-079): the tab assertion the web app's\n * backend signs for its user, and the key an address is bound to.\n */\n\nimport type { OidcConfig } from \"./config.ts\";\nimport {\n\taudiences,\n\tJwksCache,\n\tJwtError,\n\ttype KeySource,\n\tnumericDate,\n\tverifyJwt,\n} from \"./jwt.ts\";\n\n/** The `typ` header of a tab assertion (explicit typing, RFC 8725 §3.11). */\nexport const TAB_ASSERTION_TYP = \"tab-mcp-identity+jwt\";\n/** Longest lifetime of a tab assertion. */\nexport const TAB_ASSERTION_MAX_LIFETIME_MS = 60_000;\n/** Clock leeway for `exp`, `iat` and `nbf`. */\nexport const CLOCK_LEEWAY_MS = 5_000;\nconst MAX_SUBJECT = 255;\nconst MAX_JTI = 128;\nconst MAX_SEEN_JTI = 100_000;\n/**\n * Assertions one person may have in the replay window (about a minute): two\n * a second. A page refreshes far less often; the cap keeps one signed-in user\n * from filling `MAX_SEEN_JTI` and refusing everyone else.\n */\nconst MAX_JTI_PER_PERSON = 120;\n\n/** The text for a person other than the page's user (DEC-077, DEC-079). */\nexport const MISMATCH_TEXT =\n\t\"This AI tool is logged in as someone other than the page.\";\n\n/**\n * The binding key: SHA-256 over the provider's issuer and the subject, so the\n * subject is never stored in the clear (DEC-077).\n */\nexport function identityKey(issuer: string, subject: string): string {\n\treturn new Bun.CryptoHasher(\"sha256\")\n\t\t.update(`${issuer}\\n${subject}`)\n\t\t.digest()\n\t\t.toBase64({ alphabet: \"base64url\", omitPadding: true });\n}\n\n/** Verifies tab assertions (DEC-076) and remembers used `jti`s. */\nexport class TabAssertionVerifier {\n\treadonly #oidc: OidcConfig;\n\treadonly #keys: KeySource;\n\t/** (binding key, jti) → expiry; insertion order is roughly expiry order (all live ≤ 65 s). */\n\t/** (person, jti) → until when it counts as used. */\n\treadonly #seen = new Map<string, number>();\n\t/** Entries in `#seen` per person. */\n\treadonly #perPerson = new Map<string, number>();\n\t#sweptAt = Number.NEGATIVE_INFINITY;\n\treadonly #maxSeen: number;\n\n\t/** `maxSeen` replaces `MAX_SEEN_JTI` (tests only). */\n\tconstructor(\n\t\toidc: OidcConfig,\n\t\tkeys?: KeySource,\n\t\tmaxSeen: number = MAX_SEEN_JTI,\n\t) {\n\t\tthis.#oidc = oidc;\n\t\tthis.#keys = keys ?? new JwksCache(oidc.assertionJwksUrl);\n\t\tthis.#maxSeen = maxSeen;\n\t}\n\n\t/**\n\t * Check an assertion for an MCP address; returns the binding key. Throws\n\t * `JwtError` with a short reason for the log.\n\t */\n\tasync verify(\n\t\ttoken: string,\n\t\taddress: string,\n\t\tnow: number = Date.now(),\n\t): Promise<string> {\n\t\tconst { payload } = await verifyJwt(token, this.#keys, {\n\t\t\ttyp: TAB_ASSERTION_TYP,\n\t\t});\n\t\tif (payload[\"iss\"] !== this.#oidc.assertionIssuer) {\n\t\t\tthrow new JwtError(\n\t\t\t\t\"iss\",\n\t\t\t\t\"the assertion's issuer is not the configured one\",\n\t\t\t);\n\t\t}\n\t\tconst aud = audiences(payload);\n\t\tif (aud.length !== 1 || aud[0] !== address) {\n\t\t\tthrow new JwtError(\"aud\", \"the assertion is not for this address\");\n\t\t}\n\t\tconst exp = numericDate(payload, \"exp\");\n\t\tif (exp === undefined)\n\t\t\tthrow new JwtError(\"exp\", \"the assertion has no exp\");\n\t\tif (exp <= now - CLOCK_LEEWAY_MS) {\n\t\t\tthrow new JwtError(\"expired\", \"the assertion has expired\");\n\t\t}\n\t\tif (exp > now + TAB_ASSERTION_MAX_LIFETIME_MS + CLOCK_LEEWAY_MS) {\n\t\t\tthrow new JwtError(\n\t\t\t\t\"lifetime\",\n\t\t\t\t\"the assertion lives longer than 60 seconds\",\n\t\t\t);\n\t\t}\n\t\tconst iat = numericDate(payload, \"iat\");\n\t\tif (iat !== undefined && iat > now + CLOCK_LEEWAY_MS) {\n\t\t\tthrow new JwtError(\"iat\", \"the assertion was issued in the future\");\n\t\t}\n\t\tconst nbf = numericDate(payload, \"nbf\");\n\t\tif (nbf !== undefined && nbf > now + CLOCK_LEEWAY_MS) {\n\t\t\tthrow new JwtError(\"nbf\", \"the assertion is not valid yet\");\n\t\t}\n\t\tconst sub = payload[\"sub\"];\n\t\tif (typeof sub !== \"string\" || sub === \"\" || sub.length > MAX_SUBJECT) {\n\t\t\tthrow new JwtError(\"sub\", \"the assertion needs a subject\");\n\t\t}\n\t\tconst jti = payload[\"jti\"];\n\t\tif (typeof jti !== \"string\" || jti === \"\" || jti.length > MAX_JTI) {\n\t\t\tthrow new JwtError(\"jti\", \"the assertion needs a jti\");\n\t\t}\n\t\tthis.#sweep(now);\n\t\t// Keyed by the person too, so one user's jti cannot block another's.\n\t\tconst key = identityKey(this.#oidc.issuer, sub);\n\t\tconst seenKey = `${key}\\n${jti}`;\n\t\tif (this.#seen.has(seenKey)) {\n\t\t\tthrow new JwtError(\"replayed\", \"the assertion was already used\");\n\t\t}\n\t\tconst mine = this.#perPerson.get(key) ?? 0;\n\t\tif (mine >= MAX_JTI_PER_PERSON) {\n\t\t\tthrow new JwtError(\"busy\", \"too many assertions at once\");\n\t\t}\n\t\t// Full for everyone: says nothing about this person (a transient refusal).\n\t\tif (this.#seen.size >= this.#maxSeen) {\n\t\t\tthrow new JwtError(\n\t\t\t\t\"relay_busy\",\n\t\t\t\t\"the relay is checking too many assertions; try again shortly\",\n\t\t\t);\n\t\t}\n\t\tthis.#seen.set(seenKey, exp + CLOCK_LEEWAY_MS);\n\t\tthis.#perPerson.set(key, mine + 1);\n\t\treturn key;\n\t}\n\n\t/**\n\t * Forgets the expired entries. They are not in expiry order (each\n\t * assertion has its own `exp`), so the whole map is checked, at most once a\n\t * second, and always when it is full.\n\t */\n\t#sweep(now: number): void {\n\t\tif (now - this.#sweptAt < 1000 && this.#seen.size < this.#maxSeen) return;\n\t\tthis.#sweptAt = now;\n\t\tfor (const [seenKey, until] of this.#seen) {\n\t\t\tif (until > now) continue;\n\t\t\tthis.#seen.delete(seenKey);\n\t\t\tconst person = seenKey.slice(0, seenKey.indexOf(\"\\n\"));\n\t\t\tconst left = (this.#perPerson.get(person) ?? 1) - 1;\n\t\t\tif (left > 0) this.#perPerson.set(person, left);\n\t\t\telse this.#perPerson.delete(person);\n\t\t}\n\t}\n}\n",
13
+ "/**\n * The tab↔relay protocol, version 1 (`tab-mcp.v1`).\n *\n * One WebSocket per tab session; text frames with one JSON object each. The\n * normative description is `kb/design/protocol.md` (later `docs/protocol.md`).\n */\n\nimport { isAppName, MAX_APP_NAME, type PairingKind } from \"./config.ts\";\nimport { MAX_JWT_BYTES } from \"./jwt.ts\";\n\nexport const SUBPROTOCOL = \"tab-mcp.v1\";\n\n/** The only MCP revision the relay serves (DEC-033). */\nexport const MCP_PROTOCOL_VERSION = \"2026-07-28\";\n\n/** `_meta` key that carries the protocol version in every 2026-07-28 request. */\nexport const PROTOCOL_VERSION_META_KEY =\n\t\"io.modelcontextprotocol/protocolVersion\";\n\nexport const CloseCode = {\n\tnormal: 1000,\n\tpolicyViolation: 1008,\n\ttooLarge: 1009,\n\tsessionExpired: 4001,\n\tidleTimeout: 4002,\n\theartbeatLost: 4003,\n\tshuttingDown: 4004,\n\treplaced: 4005,\n\t/** Reserved in the protocol (`rate_limited`); the relay does not send it yet. */\n\trateLimited: 4008,\n\t/** The address id in the `/tab` URL is not the secret's (DEC-098). */\n\taddressMismatch: 4009,\n} as const;\n\nexport type ByeReason = \"replaced\" | \"expired\" | \"idle\" | \"shutdown\" | \"user\";\n\nexport interface Limits {\n\tmaxMessageBytes: number;\n\tmaxBodyBytes: number;\n\trequestTimeoutMs: number;\n\tidleTimeoutMs: number;\n\theartbeatMs: number;\n\tkeepAliveMs: number;\n\tmaxConcurrentRequests: number;\n}\n\n/** Who is calling, as the tab sees it (passed to the MCP handler as `authInfo`). */\nexport interface GrantRef {\n\tgrantId: string;\n\tclientId: string;\n\tscopes: string[];\n}\n\n/** A connected AI app as listed to the tab. */\nexport interface GrantInfo {\n\tgrantId: string;\n\tclient: { id: string; name: string; host: string };\n\tcreatedAt: string;\n}\n\n// Relay → tab\n\nexport interface SessionMessage {\n\ttype: \"session\";\n\tsessionId: string;\n\taddress: string;\n\torigin: string;\n\t/** The name `/authorize` shows: the site's configured name, else the page's. */\n\tappName?: string;\n\t/** How AI apps pair with this page: `code` or `oidc` (DEC-079). */\n\tpairing: PairingKind;\n\texpiresAt: string;\n\tlimits: Limits;\n\tgrants: GrantInfo[];\n}\nexport interface PingMessage {\n\ttype: \"ping\";\n\tt: number;\n}\nexport interface ByeMessage {\n\ttype: \"bye\";\n\treason: string;\n}\nexport interface HttpRequestMessage {\n\ttype: \"http.request\";\n\tid: string;\n\tmethod: string;\n\tpath: string;\n\theaders: Record<string, string>;\n\tbody: string;\n\tgrant: GrantRef;\n}\nexport interface HttpCancelMessage {\n\ttype: \"http.cancel\";\n\tid: string;\n\treason: \"client_closed\" | \"timeout\" | \"error\";\n}\nexport interface PairingCodeMessage {\n\ttype: \"pairing.code\";\n\tcode: string;\n\texpiresAt: string;\n}\nexport interface GrantAddedMessage {\n\ttype: \"grant.added\";\n\tgrant: GrantInfo;\n}\nexport interface GrantRemovedMessage {\n\ttype: \"grant.removed\";\n\tgrantId: string;\n\treason: \"revoked\" | \"reuse_detected\" | \"expired\";\n}\n/** The page's identity assertion was accepted (DEC-076). */\nexport interface IdentityBoundMessage {\n\ttype: \"identity.bound\";\n\t/** The `round` of the `identity` this answers, echoed when it had one. */\n\tround?: number;\n}\nexport interface ErrorMessage {\n\ttype: \"error\";\n\tcode: ErrorCode;\n\tmessage: string;\n\tid?: string;\n\t/** On `identity_refused`: the `round` of the `identity` it answers, if it had one. */\n\tround?: number;\n}\nexport type RelayMessage =\n\t| SessionMessage\n\t| PingMessage\n\t| ByeMessage\n\t| HttpRequestMessage\n\t| HttpCancelMessage\n\t| PairingCodeMessage\n\t| GrantAddedMessage\n\t| GrantRemovedMessage\n\t| IdentityBoundMessage\n\t| ErrorMessage;\n\nexport type ErrorCode =\n\t| \"bad_message\"\n\t| \"unknown_type\"\n\t| \"unknown_id\"\n\t| \"too_large\"\n\t| \"rate_limited\"\n\t| \"identity_refused\"\n\t| \"address_mismatch\";\n\n// Tab → relay\n\nexport interface HelloMessage {\n\ttype: \"hello\";\n\tlibrary: { name: string; version: string };\n\tpage: { title: string };\n\t/** Self-declared app name (DEC-045), unverified. */\n\tapp?: { name: string };\n\tsecret: string;\n\t/**\n\t * Revoke every grant of the address as the session claims it, before the\n\t * `session` answer (which then lists none): a page taking its address back\n\t * from whoever took it over (DEC-095).\n\t */\n\trevokeAll?: boolean;\n}\nexport interface PongMessage {\n\ttype: \"pong\";\n\tt: number;\n}\nexport interface HttpResponseMessage {\n\ttype: \"http.response\";\n\tid: string;\n\tstatus: number;\n\theaders: Record<string, string>;\n\tbody: string;\n}\nexport interface HttpResponseStartMessage {\n\ttype: \"http.response.start\";\n\tid: string;\n\tstatus: number;\n\theaders: Record<string, string>;\n}\nexport interface HttpResponseChunkMessage {\n\ttype: \"http.response.chunk\";\n\tid: string;\n\tdata: string;\n}\nexport interface HttpResponseEndMessage {\n\ttype: \"http.response.end\";\n\tid: string;\n}\nexport interface TabErrorMessage {\n\ttype: \"error\";\n\tcode: string;\n\tmessage: string;\n\tid?: string;\n}\nexport interface PairingStartMessage {\n\ttype: \"pairing.start\";\n}\nexport interface PairingStopMessage {\n\ttype: \"pairing.stop\";\n}\nexport interface GrantRevokeMessage {\n\ttype: \"grant.revoke\";\n\tgrantId: string;\n}\nexport interface GrantsRevokeAllMessage {\n\ttype: \"grants.revokeAll\";\n}\n/** The web app's signed statement of who the page's user is (DEC-076). */\nexport interface IdentityMessage {\n\ttype: \"identity\";\n\tassertion: string;\n\t/** The page's number for this assertion, echoed in the answer (a lost answer cannot be mistaken for a later one). */\n\tround?: number;\n}\n/** The page's user signed out: end every AI connection (DEC-078). */\nexport interface IdentityLogoutMessage {\n\ttype: \"identity.logout\";\n}\nexport type TabMessage =\n\t| HelloMessage\n\t| PairingStartMessage\n\t| PairingStopMessage\n\t| GrantRevokeMessage\n\t| GrantsRevokeAllMessage\n\t| IdentityMessage\n\t| IdentityLogoutMessage\n\t| PongMessage\n\t| ByeMessage\n\t| HttpResponseMessage\n\t| HttpResponseStartMessage\n\t| HttpResponseChunkMessage\n\t| HttpResponseEndMessage\n\t| TabErrorMessage;\n\n/** Result of parsing one frame from the tab. */\nexport type ParseResult =\n\t| { ok: true; message: TabMessage }\n\t/** A well-formed message of a type this relay does not know. */\n\t| { ok: true; unknown: true; type: string; id?: string }\n\t| { ok: false; error: string; id?: string };\n\nconst MAX_TITLE = 300;\nconst MAX_NAME = 200;\nconst MAX_ID = 128;\nconst MAX_HEADER_COUNT = 32;\nconst MAX_HEADER_VALUE = 8192;\n\n/**\n * RFC 9110 §5.5 field value: visible ASCII, obs-text (0x80-0xFF), space and\n * tab; no other control characters (CR, LF, NUL, DEL, …) and nothing beyond\n * 0xFF, which `Headers` would reject.\n */\nconst FIELD_VALUE = /^[\\t\\x20-\\x7E\\x80-\\xFF]*$/;\n\ntype Json = Record<string, unknown>;\n\nfunction isObject(v: unknown): v is Json {\n\treturn typeof v === \"object\" && v !== null && !Array.isArray(v);\n}\n\nfunction isString(v: unknown, max: number): v is string {\n\treturn typeof v === \"string\" && v.length <= max;\n}\n\nfunction isId(v: unknown): v is string {\n\treturn typeof v === \"string\" && v.length > 0 && v.length <= MAX_ID;\n}\n\nfunction isStatus(v: unknown): v is number {\n\treturn typeof v === \"number\" && Number.isInteger(v) && v >= 200 && v <= 599;\n}\n\nfunction isHeaders(v: unknown): v is Record<string, string> {\n\tif (!isObject(v)) return false;\n\tconst entries = Object.entries(v);\n\tif (entries.length > MAX_HEADER_COUNT) return false;\n\treturn entries.every(\n\t\t([k, val]) =>\n\t\t\t/^[a-z0-9-]{1,64}$/.test(k) &&\n\t\t\tisString(val, MAX_HEADER_VALUE) &&\n\t\t\tFIELD_VALUE.test(val),\n\t);\n}\n\nfunction idOf(v: Json): string | undefined {\n\treturn isId(v[\"id\"]) ? v[\"id\"] : undefined;\n}\n\n/**\n * Parse and validate one text frame from the tab. Hand-written on purpose: the\n * relay has no schema library (DEC-014).\n */\nexport function parseTabMessage(text: string): ParseResult {\n\tlet v: unknown;\n\ttry {\n\t\tv = JSON.parse(text);\n\t} catch {\n\t\treturn { ok: false, error: \"not JSON\" };\n\t}\n\tif (!isObject(v)) return { ok: false, error: \"not a JSON object\" };\n\tconst type = v[\"type\"];\n\tif (typeof type !== \"string\" || type.length === 0 || type.length > 64) {\n\t\treturn { ok: false, error: \"missing type\" };\n\t}\n\tconst id = idOf(v);\n\tconst bad = (error: string): ParseResult =>\n\t\tid === undefined ? { ok: false, error } : { ok: false, error, id };\n\n\tswitch (type) {\n\t\tcase \"hello\": {\n\t\t\tconst library = v[\"library\"];\n\t\t\tconst page = v[\"page\"];\n\t\t\tif (\n\t\t\t\t!isObject(library) ||\n\t\t\t\t!isString(library[\"name\"], MAX_NAME) ||\n\t\t\t\t!isString(library[\"version\"], MAX_NAME)\n\t\t\t) {\n\t\t\t\treturn bad(\"hello.library must be {name, version}\");\n\t\t\t}\n\t\t\tif (!isObject(page) || !isString(page[\"title\"], MAX_TITLE)) {\n\t\t\t\treturn bad(\"hello.page must be {title}\");\n\t\t\t}\n\t\t\tif (typeof v[\"secret\"] !== \"string\") return bad(\"hello.secret missing\");\n\t\t\tconst app = v[\"app\"];\n\t\t\tif (app !== undefined && (!isObject(app) || !isAppName(app[\"name\"]))) {\n\t\t\t\treturn bad(`hello.app.name must be 1-${MAX_APP_NAME} characters`);\n\t\t\t}\n\t\t\tconst revokeAll = v[\"revokeAll\"];\n\t\t\tif (revokeAll !== undefined && typeof revokeAll !== \"boolean\") {\n\t\t\t\treturn bad(\"hello.revokeAll must be a boolean\");\n\t\t\t}\n\t\t\tconst hello: HelloMessage = {\n\t\t\t\ttype,\n\t\t\t\tlibrary: { name: library[\"name\"], version: library[\"version\"] },\n\t\t\t\tpage: { title: page[\"title\"] },\n\t\t\t\tsecret: v[\"secret\"],\n\t\t\t};\n\t\t\tif (isObject(app) && isAppName(app[\"name\"])) {\n\t\t\t\thello.app = { name: app[\"name\"] };\n\t\t\t}\n\t\t\tif (revokeAll === true) hello.revokeAll = true;\n\t\t\treturn { ok: true, message: hello };\n\t\t}\n\t\tcase \"pong\": {\n\t\t\tconst t = v[\"t\"];\n\t\t\tif (typeof t !== \"number\") return bad(\"pong.t must be a number\");\n\t\t\treturn { ok: true, message: { type, t } };\n\t\t}\n\t\tcase \"bye\": {\n\t\t\tconst reason = v[\"reason\"];\n\t\t\treturn {\n\t\t\t\tok: true,\n\t\t\t\tmessage: {\n\t\t\t\t\ttype,\n\t\t\t\t\treason: isString(reason, MAX_NAME) ? reason : \"\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tcase \"http.response\": {\n\t\t\tif (id === undefined) return bad(\"http.response.id missing\");\n\t\t\tconst { status, headers, body } = v;\n\t\t\tif (!isStatus(status)) return bad(\"http.response.status invalid\");\n\t\t\tif (!isHeaders(headers)) return bad(\"http.response.headers invalid\");\n\t\t\tif (typeof body !== \"string\") return bad(\"http.response.body invalid\");\n\t\t\treturn { ok: true, message: { type, id, status, headers, body } };\n\t\t}\n\t\tcase \"http.response.start\": {\n\t\t\tif (id === undefined) return bad(\"http.response.start.id missing\");\n\t\t\tconst { status, headers } = v;\n\t\t\tif (!isStatus(status)) return bad(\"http.response.start.status invalid\");\n\t\t\tif (!isHeaders(headers))\n\t\t\t\treturn bad(\"http.response.start.headers invalid\");\n\t\t\treturn { ok: true, message: { type, id, status, headers } };\n\t\t}\n\t\tcase \"http.response.chunk\": {\n\t\t\tif (id === undefined) return bad(\"http.response.chunk.id missing\");\n\t\t\tconst data = v[\"data\"];\n\t\t\tif (typeof data !== \"string\")\n\t\t\t\treturn bad(\"http.response.chunk.data invalid\");\n\t\t\treturn { ok: true, message: { type, id, data } };\n\t\t}\n\t\tcase \"http.response.end\": {\n\t\t\tif (id === undefined) return bad(\"http.response.end.id missing\");\n\t\t\treturn { ok: true, message: { type, id } };\n\t\t}\n\t\tcase \"pairing.start\":\n\t\tcase \"pairing.stop\":\n\t\tcase \"grants.revokeAll\":\n\t\tcase \"identity.logout\":\n\t\t\treturn { ok: true, message: { type } };\n\t\tcase \"identity\": {\n\t\t\tconst assertion = v[\"assertion\"];\n\t\t\tif (\n\t\t\t\ttypeof assertion !== \"string\" ||\n\t\t\t\tassertion === \"\" ||\n\t\t\t\tassertion.length > MAX_JWT_BYTES\n\t\t\t) {\n\t\t\t\treturn bad(\n\t\t\t\t\t`identity.assertion must be a JWT of at most ${MAX_JWT_BYTES} bytes`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst round = v[\"round\"];\n\t\t\tif (\n\t\t\t\tround !== undefined &&\n\t\t\t\t!(Number.isSafeInteger(round) && (round as number) >= 0)\n\t\t\t) {\n\t\t\t\treturn bad(\"identity.round must be a non-negative integer\");\n\t\t\t}\n\t\t\treturn round === undefined\n\t\t\t\t? { ok: true, message: { type, assertion } }\n\t\t\t\t: { ok: true, message: { type, assertion, round: round as number } };\n\t\t}\n\t\tcase \"grant.revoke\": {\n\t\t\tconst grantId = v[\"grantId\"];\n\t\t\tif (!isId(grantId)) return bad(\"grant.revoke.grantId missing\");\n\t\t\treturn { ok: true, message: { type, grantId } };\n\t\t}\n\t\tcase \"error\": {\n\t\t\tconst code = v[\"code\"];\n\t\t\tconst message = v[\"message\"];\n\t\t\tif (!isString(code, MAX_NAME)) return bad(\"error.code invalid\");\n\t\t\tconst m: TabErrorMessage = {\n\t\t\t\ttype,\n\t\t\t\tcode,\n\t\t\t\tmessage: isString(message, 1000) ? message : \"\",\n\t\t\t};\n\t\t\tif (id !== undefined) m.id = id;\n\t\t\treturn { ok: true, message: m };\n\t\t}\n\t\tdefault:\n\t\t\treturn id === undefined\n\t\t\t\t? { ok: true, unknown: true, type }\n\t\t\t\t: { ok: true, unknown: true, type, id };\n\t}\n}\n",
14
+ "import { RelayErrorCode, type RpcId } from \"./exchange.ts\";\nimport { MCP_PROTOCOL_VERSION } from \"./protocol.ts\";\n\n/**\n * The relay's answers when the page is not open (DEC-074): HTTP 200 with a\n * JSON-RPC message the AI app's model can act on, instead of an HTTP error\n * status that makes MCP clients re-probe the server's protocol era.\n */\n\n/**\n * Why the page is not there: never came, went away mid-request, or is open\n * but nobody is signed in to it (identity binding, DEC-077). While the relay\n * stops, `restarting` (the request never reached the page) or\n * `restarting_gone` (it was in flight).\n */\nexport type NotOpenPhase =\n\t| \"not_open\"\n\t| \"gone\"\n\t| \"signed_out\"\n\t| \"restarting\"\n\t| \"restarting_gone\";\n\n/** What the relay says about itself in a stand-in `server/discover` result. */\nexport interface RelayInfo {\n\tname: string;\n\tversion: string;\n}\n\n/**\n * Methods the relay sends to the page again after it went away mid-request:\n * read-only and not the era probe (`server/discover` must answer quickly).\n */\nconst RETRYABLE = new Set([\"tools/list\"]);\n\nexport function isRetryable(method: unknown): boolean {\n\treturn typeof method === \"string\" && RETRYABLE.has(method);\n}\n\nfunction pageName(appName: string | undefined): string {\n\treturn appName ?? \"the web page\";\n}\n\n/** The text for \"the page is not open\" (tool results and JSON-RPC errors). */\nexport function notOpenText(appName: string | undefined): string {\n\treturn `The page is not open, or not in front. Ask the user to open ${pageName(appName)} and keep it in front (on a phone, in split screen), then try again.`;\n}\n\n/** The text for a page that is open but not signed in (DEC-077). */\nexport function signedOutText(appName: string | undefined): string {\n\treturn `The page is open, but nobody is signed in to it. Ask the user to sign in to ${pageName(appName)}, then try again.`;\n}\n\n/** The text while the relay stops: the AI app should try again, not send the user. */\nexport function restartingText(): string {\n\treturn \"The relay is restarting. Try again in a few seconds.\";\n}\n\nfunction phaseText(phase: NotOpenPhase, appName: string | undefined): string {\n\tif (phase === \"restarting\" || phase === \"restarting_gone\") {\n\t\treturn restartingText();\n\t}\n\treturn phase === \"signed_out\" ? signedOutText(appName) : notOpenText(appName);\n}\n\n/** The text for a tool call whose page went away while it ran. */\nexport function goneDuringCallText(appName: string | undefined): string {\n\treturn `The page closed while the tool was running, so it may or may not have finished. Ask the user to open ${pageName(appName)} and keep it in front (on a phone, in split screen), then check the result before trying again.`;\n}\n\n/** The text for a tool call that was running when the relay stopped. */\nexport function restartedDuringCallText(): string {\n\treturn \"The relay restarted while the tool was running, so it may or may not have finished. Check the result before trying again in a few seconds.\";\n}\n\n/**\n * The JSON-RPC message the relay answers with (at HTTP 200) when the page is\n * not open, or `undefined` for a notification (that keeps an HTTP error\n * status: the spec requires one for a notification the server cannot accept).\n */\nexport function notOpenAnswer(\n\tbody: Record<string, unknown>,\n\trpcId: RpcId | undefined,\n\tappName: string | undefined,\n\tphase: NotOpenPhase,\n\trelay: RelayInfo,\n): string | undefined {\n\tif (rpcId === undefined) return undefined;\n\tconst id = rpcId;\n\tconst method = body[\"method\"];\n\tif (method === \"tools/call\") {\n\t\tconst text =\n\t\t\tphase === \"gone\"\n\t\t\t\t? goneDuringCallText(appName)\n\t\t\t\t: phase === \"restarting_gone\"\n\t\t\t\t\t? restartedDuringCallText()\n\t\t\t\t\t: phaseText(phase, appName);\n\t\treturn JSON.stringify({\n\t\t\tjsonrpc: \"2.0\",\n\t\t\tid,\n\t\t\tresult: {\n\t\t\t\tresultType: \"complete\",\n\t\t\t\tcontent: [{ type: \"text\", text }],\n\t\t\t\tisError: true,\n\t\t\t},\n\t\t});\n\t}\n\tif (method === \"server/discover\") {\n\t\t// A stand-in for the page's own result: the era probe must succeed (a\n\t\t// non-modern error makes dual-era clients fall back to `initialize`), and\n\t\t// `ttlMs: 0` makes clients ask the page again as soon as they need it.\n\t\treturn JSON.stringify({\n\t\t\tjsonrpc: \"2.0\",\n\t\t\tid,\n\t\t\tresult: {\n\t\t\t\tresultType: \"complete\",\n\t\t\t\tsupportedVersions: [MCP_PROTOCOL_VERSION],\n\t\t\t\tcapabilities: { tools: { listChanged: true } },\n\t\t\t\tinstructions: phaseText(phase, appName),\n\t\t\t\tttlMs: 0,\n\t\t\t\tcacheScope: \"private\",\n\t\t\t\t_meta: {\n\t\t\t\t\t\"io.modelcontextprotocol/serverInfo\": {\n\t\t\t\t\t\tname: relay.name,\n\t\t\t\t\t\tversion: relay.version,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t});\n\t}\n\treturn JSON.stringify({\n\t\tjsonrpc: \"2.0\",\n\t\tid,\n\t\terror: {\n\t\t\tcode:\n\t\t\t\tphase === \"gone\"\n\t\t\t\t\t? RelayErrorCode.tabGone\n\t\t\t\t\t: phase === \"restarting\" || phase === \"restarting_gone\"\n\t\t\t\t\t\t? RelayErrorCode.restarting\n\t\t\t\t\t\t: RelayErrorCode.pageNotOpen,\n\t\t\tmessage: phaseText(phase, appName),\n\t\t},\n\t});\n}\n",
15
+ "/**\n * The relay's OAuth 2.1 authorization server (DEC-006, DEC-015, DEC-031),\n * written without an OAuth library:\n *\n * - RFC 9728 protected-resource metadata per address, RFC 8414 server metadata;\n * - clients only through Client ID Metadata Documents (`cimd.ts`);\n * - `/authorize` with PKCE S256 only, exact redirect URIs (loopback port\n * relaxed), `resource` bound to one address (RFC 8707), `iss` in every\n * response (RFC 9207), and the pairing step of the address's site (DEC-042);\n * - `/token` for the authorization code and rotating refresh tokens (DEC-029);\n * - client authentication `none` or `private_key_jwt` (DEC-106);\n * - `/revoke` (RFC 7009).\n */\n\nimport { timingSafeEqual } from \"node:crypto\";\nimport { isAddressId, shortAddress } from \"../address.ts\";\nimport type { PairingKind, RelayConfig, Site } from \"../config.ts\";\nimport { JWT_ALGORITHMS, JwtError } from \"../jwt.ts\";\nimport type { Logger } from \"../log.ts\";\nimport type { GrantInfo } from \"../protocol.ts\";\nimport { RateLimiter } from \"../ratelimit.ts\";\nimport { NonceSet } from \"../store/memory.ts\";\nimport type { TokenRecord, TokenStore } from \"../store/types.ts\";\nimport { hashToken, issueTokenPair, newId } from \"../tokens.ts\";\nimport {\n\ttype CimdTestMode,\n\ttype ClientAuth,\n\ttype ClientMetadata,\n\tClientMetadataError,\n\tClientMetadataFetcher,\n\tisTransient,\n\tredirectUriMatches,\n} from \"./cimd.ts\";\nimport {\n\tassertionSubject,\n\tCLIENT_ASSERTION_TYPE,\n\tClientKeySets,\n\tNonceQuota,\n\tquotaKey,\n\tverifyClientAssertion,\n} from \"./client-auth.ts\";\nimport { AUTHORIZE_CSS, escapeHtml, pageHeaders, renderPage } from \"./html.ts\";\nimport { OidcPairing } from \"./oidc.ts\";\nimport {\n\tPAIRING_METHODS,\n\ttype PairingMethod,\n\ttype PairingResult,\n\ttype PairingTarget,\n} from \"./pairing.ts\";\n\n/** The only scope: calling the page's tools. */\nexport const SCOPE = \"tools\";\n\n/** How long a user has to finish the `/authorize` page. */\nconst PENDING_TTL_MS = 10 * 60_000;\n/** Authorization codes are single-use and short-lived (RFC 6749 §4.1.2). */\nconst AUTH_CODE_TTL_MS = 60_000;\nconst MAX_PENDING = 10_000;\n/** Open authorization requests per IP (prefix) and per address (DEC-061). */\nconst MAX_PENDING_PER_IP = 10;\nconst MAX_PENDING_PER_ADDRESS = 20;\n/** One network's share of an address's open requests (DEC-088). */\nconst MAX_PENDING_PER_ADDRESS_IP = 5;\n/** How long a used refresh token is kept to detect its reuse (DEC-091). */\nconst REFRESH_REUSE_WINDOW_MS = 24 * 60 * 60_000;\nconst MAX_CODES = 10_000;\nconst MAX_FORM_BYTES = 8192;\nconst MAX_STATE_LENGTH = 1024;\n/**\n * Live client assertion ids per AI app host (`quotaKey`), and in all, on one instance\n * (DEC-106). An assertion is usable for at most five minutes (plus leeway),\n * so a host's share allows about 30 token requests a second.\n */\nconst MAX_NONCES_PER_HOST = 10_000;\nconst MAX_NONCES = 100_000;\n/** The one text of every client authentication failure; the reason is logged. */\nconst CLIENT_AUTH_FAILED = \"client authentication failed\";\n\n/** A tab session as the authorization server sees it. */\nexport interface AuthorizeSession extends PairingTarget {\n\treadonly origin: string;\n\treadonly appName: string | undefined;\n\treadonly site: Site;\n}\n\nexport interface AuthorizationHost {\n\treadonly config: RelayConfig;\n\treadonly logger: Logger;\n\treadonly store: TokenStore;\n\tpublicUrl(): string;\n\t/** The connected (ready) tab session of an address. */\n\tsession(addressId: string): AuthorizeSession | undefined;\n\t/** Tell the address's tab, if connected. */\n\tgrantAdded(addressId: string, grant: GrantInfo): void;\n\tgrantRemoved(addressId: string, grantId: string, reason: string): void;\n}\n\n/** Test-only settings; never reachable from configuration. */\nexport interface AuthorizationTestMode {\n\tcimd?: CimdTestMode;\n\tauthCodeTtlMs?: number;\n\t/** Replace the pairing methods (a stub method proves the server needs no codes). */\n\tpairingMethods?: Partial<Record<PairingKind, PairingMethod>>;\n\t/** A smaller cap on open OIDC sign-ins. */\n\toidcMaxFlows?: number;\n}\n\ninterface AuthorizationRequest {\n\tclient: ClientMetadata;\n\tredirectUri: string;\n\tstate: string | undefined;\n\tcodeChallenge: string;\n\taddressId: string;\n\tresource: string;\n\tscopes: string[];\n}\n\ninterface Pending extends AuthorizationRequest {\n\tid: string;\n\texpiresAt: number;\n\t/** The IP (prefix) key that opened it, for the per-IP cap. */\n\tip: string;\n}\n\ninterface IssuedCode extends AuthorizationRequest {\n\texpiresAt: number;\n\tused: boolean;\n\tgrantId?: string;\n\t/** The binding key the grant is made for (identity binding, DEC-077). */\n\tsubject?: string;\n}\n\n/** The client of a token or revocation request (DEC-106). */\ninterface RequestClient {\n\tclientId: string;\n\t/** It proved itself with a valid `private_key_jwt` assertion. */\n\tassertion: boolean;\n}\n\n/**\n * Whether a client must authenticate for every code: `private_key_jwt`\n * without `none` among its methods. With `none` listed (DEC-104) a grant may\n * start public; one made with an assertion stays bound to it (DEC-106).\n */\nfunction assertionRequired(auth: ClientAuth): boolean {\n\treturn auth.method === \"private_key_jwt\" && !auth.publicAllowed;\n}\n\nclass OAuthError extends Error {\n\treadonly status: number;\n\treadonly code: string;\n\tconstructor(status: number, code: string, description: string) {\n\t\tsuper(description);\n\t\tthis.status = status;\n\t\tthis.code = code;\n\t}\n}\n\nfunction constantTimeEqual(a: string, b: string): boolean {\n\tconst x = Buffer.from(a);\n\tconst y = Buffer.from(b);\n\treturn x.length === y.length && timingSafeEqual(x, y);\n}\n\n/** BASE64URL(SHA256(verifier)) (RFC 7636 §4.6). */\nexport function s256(verifier: string): string {\n\treturn new Bun.CryptoHasher(\"sha256\")\n\t\t.update(verifier)\n\t\t.digest()\n\t\t.toBase64({ alphabet: \"base64url\", omitPadding: true });\n}\n\n/** RFC 7636 §4.1: 43-128 unreserved characters. */\nconst VERIFIER = /^[A-Za-z0-9\\-._~]{43,128}$/;\n\n/** Parameters, refusing duplicates (RFC 6749 §3.1, §3.2). */\nfunction single(\n\tparams: URLSearchParams,\n\tnames: readonly string[],\n): Record<string, string | undefined> | { duplicate: string } {\n\tconst out: Record<string, string | undefined> = {};\n\tfor (const name of names) {\n\t\tconst values = params.getAll(name);\n\t\tif (values.length > 1) return { duplicate: name };\n\t\tconst value = values[0];\n\t\tout[name] = value === undefined || value === \"\" ? undefined : value;\n\t}\n\treturn out;\n}\n\nfunction random(bytes: number): string {\n\treturn crypto\n\t\t.getRandomValues(new Uint8Array(bytes))\n\t\t.toBase64({ alphabet: \"base64url\", omitPadding: true });\n}\n\n/** The origin a redirect URI sends the browser to, for CSP `form-action`. */\nfunction formTarget(redirectUri: string): string {\n\ttry {\n\t\tconst u = new URL(redirectUri);\n\t\treturn u.protocol === \"http:\" || u.protocol === \"https:\"\n\t\t\t? u.origin\n\t\t\t: u.protocol;\n\t} catch {\n\t\treturn \"'none'\";\n\t}\n}\n\nfunction hostOf(url: string): string {\n\ttry {\n\t\tconst u = new URL(url);\n\t\treturn u.host || u.protocol;\n\t} catch {\n\t\treturn url;\n\t}\n}\n\n/** A client id's host for the log; not the text itself: it is anyone's choice. */\nfunction logHost(clientId: string | undefined): string {\n\treturn clientId !== undefined && URL.canParse(clientId)\n\t\t? hostOf(clientId).slice(0, 100)\n\t\t: \"(not a URL)\";\n}\n\n/** The OAuth endpoints and pages, mounted by the relay. */\nexport class AuthorizationServer {\n\treadonly #host: AuthorizationHost;\n\treadonly #cimd: ClientMetadataFetcher;\n\treadonly #clientKeys: ClientKeySets;\n\t/** Used client assertion ids when the store has no `claimNonce` (per instance). */\n\treadonly #nonces = new NonceSet();\n\treadonly #nonceQuota = new NonceQuota(MAX_NONCES_PER_HOST, MAX_NONCES);\n\t/** Insertion order is expiry order (one lifetime for all), so sweeps stop early. */\n\treadonly #pending = new Map<string, Pending>();\n\treadonly #pendingPerIp = new Map<string, number>();\n\treadonly #pendingPerAddress = new Map<string, number>();\n\treadonly #codes = new Map<string, IssuedCode>();\n\treadonly #authCodeTtlMs: number;\n\treadonly #methods: Partial<Record<PairingKind, PairingMethod>>;\n\treadonly #csrfSecret = random(32);\n\t/** `/authorize` page loads per IP. */\n\treadonly #viewsPerIp = new RateLimiter({\n\t\tcapacity: 30,\n\t\trefillPerSecond: 0.5,\n\t});\n\t/** Form posts per IP (every action; locks out only that network). */\n\treadonly #attemptsPerIp = new RateLimiter({\n\t\tcapacity: 20,\n\t\trefillPerSecond: 0.1,\n\t});\n\t/**\n\t * Code guesses (well-formed, against an active code) per address and IP:\n\t * one network cannot use up an address's allowance alone (DEC-061).\n\t */\n\treadonly #guessesPerAddressIp = new RateLimiter({\n\t\tcapacity: 10,\n\t\trefillPerSecond: 1 / 60,\n\t});\n\t/** Code guesses per address from everywhere: the brute-force cap (A1). */\n\treadonly #guessesPerAddress = new RateLimiter({\n\t\tcapacity: 30,\n\t\trefillPerSecond: 1 / 30,\n\t});\n\treadonly #tokenPerIp = new RateLimiter({ capacity: 30, refillPerSecond: 1 });\n\t/** Refreshes per grant: each leaves a row for the reuse window (DEC-091). */\n\treadonly #refreshesPerGrant = new RateLimiter({\n\t\tcapacity: 20,\n\t\trefillPerSecond: 1 / 60,\n\t});\n\n\tconstructor(host: AuthorizationHost, testMode?: AuthorizationTestMode) {\n\t\tthis.#host = host;\n\t\tthis.#authCodeTtlMs = testMode?.authCodeTtlMs ?? AUTH_CODE_TTL_MS;\n\t\tconst oidc = host.config.oidc;\n\t\tthis.#methods = {\n\t\t\t...PAIRING_METHODS,\n\t\t\t...(oidc === undefined\n\t\t\t\t? {}\n\t\t\t\t: {\n\t\t\t\t\t\toidc: new OidcPairing({\n\t\t\t\t\t\t\tconfig: oidc,\n\t\t\t\t\t\t\tpublicUrl: () => host.publicUrl(),\n\t\t\t\t\t\t\tlogger: host.logger,\n\t\t\t\t\t\t\tdevelopment: host.config.development,\n\t\t\t\t\t\t\t...(testMode?.oidcMaxFlows === undefined\n\t\t\t\t\t\t\t\t? {}\n\t\t\t\t\t\t\t\t: { maxFlows: testMode.oidcMaxFlows }),\n\t\t\t\t\t\t}),\n\t\t\t\t\t}),\n\t\t\t...testMode?.pairingMethods,\n\t\t};\n\t\tthis.#cimd = new ClientMetadataFetcher({\n\t\t\tallowedHosts: host.config.clientIdHosts,\n\t\t\tlogger: host.logger,\n\t\t\t...(testMode?.cimd === undefined ? {} : { testMode: testMode.cimd }),\n\t\t});\n\t\tconst cimd = this.#cimd;\n\t\tthis.#clientKeys = new ClientKeySets((url) => cimd.fetchJwks(url));\n\t}\n\n\tget #issuer(): string {\n\t\treturn this.#host.publicUrl();\n\t}\n\n\t#cors(): Record<string, string> {\n\t\treturn this.#host.config.cors ? { \"access-control-allow-origin\": \"*\" } : {};\n\t}\n\n\t#json(\n\t\tstatus: number,\n\t\tvalue: unknown,\n\t\theaders: Record<string, string> = {},\n\t): Response {\n\t\treturn new Response(JSON.stringify(value), {\n\t\t\tstatus,\n\t\t\theaders: {\n\t\t\t\t\"content-type\": \"application/json\",\n\t\t\t\t...this.#cors(),\n\t\t\t\t...headers,\n\t\t\t},\n\t\t});\n\t}\n\n\t/** CORS preflight for the metadata, token and revoke endpoints. */\n\tpreflight(methods: string): Response {\n\t\tif (!this.#host.config.cors) {\n\t\t\treturn new Response(null, { status: 405, headers: { allow: methods } });\n\t\t}\n\t\treturn new Response(null, {\n\t\t\tstatus: 204,\n\t\t\theaders: {\n\t\t\t\t...this.#cors(),\n\t\t\t\t\"access-control-allow-methods\": `${methods}, OPTIONS`,\n\t\t\t\t\"access-control-allow-headers\":\n\t\t\t\t\t\"authorization, content-type, mcp-protocol-version\",\n\t\t\t\t\"access-control-max-age\": \"600\",\n\t\t\t},\n\t\t});\n\t}\n\n\t// RFC 9728 and RFC 8414\n\n\t/** `/.well-known/oauth-protected-resource/a/{id}/mcp`. */\n\tprotectedResource(addressId: string): Response {\n\t\tif (!isAddressId(addressId))\n\t\t\treturn new Response(\"Not found\", { status: 404 });\n\t\treturn this.#json(\n\t\t\t200,\n\t\t\t{\n\t\t\t\tresource: `${this.#issuer}/a/${addressId}/mcp`,\n\t\t\t\tauthorization_servers: [this.#issuer],\n\t\t\t\tscopes_supported: [SCOPE],\n\t\t\t\tbearer_methods_supported: [\"header\"],\n\t\t\t\tresource_name: \"Tools of a web page (tab-mcp)\",\n\t\t\t},\n\t\t\t{ \"cache-control\": \"public, max-age=3600\" },\n\t\t);\n\t}\n\n\t/** `/.well-known/oauth-authorization-server`. */\n\tmetadata(): Response {\n\t\tconst issuer = this.#issuer;\n\t\treturn this.#json(\n\t\t\t200,\n\t\t\t{\n\t\t\t\tissuer,\n\t\t\t\tauthorization_endpoint: `${issuer}/authorize`,\n\t\t\t\ttoken_endpoint: `${issuer}/token`,\n\t\t\t\trevocation_endpoint: `${issuer}/revoke`,\n\t\t\t\tresponse_types_supported: [\"code\"],\n\t\t\t\tresponse_modes_supported: [\"query\"],\n\t\t\t\tgrant_types_supported: [\"authorization_code\", \"refresh_token\"],\n\t\t\t\tcode_challenge_methods_supported: [\"S256\"],\n\t\t\t\ttoken_endpoint_auth_methods_supported: [\"none\", \"private_key_jwt\"],\n\t\t\t\ttoken_endpoint_auth_signing_alg_values_supported: JWT_ALGORITHMS,\n\t\t\t\trevocation_endpoint_auth_methods_supported: [\"none\", \"private_key_jwt\"],\n\t\t\t\trevocation_endpoint_auth_signing_alg_values_supported: JWT_ALGORITHMS,\n\t\t\t\tscopes_supported: [SCOPE],\n\t\t\t\tclient_id_metadata_document_supported: true,\n\t\t\t\tauthorization_response_iss_parameter_supported: true,\n\t\t\t\tservice_documentation: \"https://github.com/ractive/tab-mcp\",\n\t\t\t},\n\t\t\t{ \"cache-control\": \"public, max-age=3600\" },\n\t\t);\n\t}\n\n\tstylesheet(): Response {\n\t\treturn new Response(AUTHORIZE_CSS, {\n\t\t\theaders: {\n\t\t\t\t\"content-type\": \"text/css; charset=utf-8\",\n\t\t\t\t\"cache-control\": \"public, max-age=3600\",\n\t\t\t\t\"x-content-type-options\": \"nosniff\",\n\t\t\t},\n\t\t});\n\t}\n\n\t// /authorize\n\n\t#page(\n\t\tstatus: number,\n\t\ttitle: string,\n\t\tbody: string,\n\t\tformTargets: readonly string[] = [],\n\t\textraHeaders: Record<string, string> = {},\n\t): Response {\n\t\tconst headers = pageHeaders(formTargets);\n\t\tfor (const [k, v] of Object.entries(extraHeaders)) headers.append(k, v);\n\t\treturn new Response(renderPage(title, body), { status, headers });\n\t}\n\n\t/** An error shown on the page: the redirect URI is not trusted (yet). */\n\t#errorPage(status: number, message: string): Response {\n\t\treturn this.#page(\n\t\t\tstatus,\n\t\t\t\"This connection cannot be made\",\n\t\t\t`<h1>This connection cannot be made</h1>\n<p role=\"alert\">${escapeHtml(message)}</p>\n<p>Go back to your AI app and try again. If it keeps happening, the AI app may not work with this relay.</p>`,\n\t\t);\n\t}\n\n\t/** The client's redirect URI with `error` (RFC 6749 §4.1.2.1) and `iss`. */\n\t#errorUrl(\n\t\tredirectUri: string,\n\t\tstate: string | undefined,\n\t\terror: string,\n\t\tdescription: string,\n\t): string {\n\t\tconst url = new URL(redirectUri);\n\t\tconst params: Record<string, string> = {\n\t\t\terror,\n\t\t\terror_description: description,\n\t\t\t...(state === undefined ? {} : { state }),\n\t\t\tiss: this.#issuer,\n\t\t};\n\t\tfor (const [k, v] of Object.entries(params)) url.searchParams.append(k, v);\n\t\treturn url.href;\n\t}\n\n\t/**\n\t * An invalid request from a client: not redirected automatically, so the\n\t * relay is no open redirector for self-published clients (RFC 9700\n\t * §4.11.2). The page names the destination and links to it (DEC-064).\n\t */\n\t#invalidRequestPage(\n\t\tredirectUri: string,\n\t\tstate: string | undefined,\n\t\terror: string,\n\t\tdescription: string,\n\t): Response {\n\t\tconst url = this.#errorUrl(redirectUri, state, error, description);\n\t\tconst host = hostOf(redirectUri);\n\t\treturn this.#page(\n\t\t\t400,\n\t\t\t\"This connection cannot be made\",\n\t\t\t`<h1>This connection cannot be made</h1>\n<p role=\"alert\">The AI app sent a request this relay cannot accept: ${escapeHtml(description)} (${escapeHtml(error)}).</p>\n<p>You can go back to the AI app at ${escapeHtml(host)} to tell it, or close this page.</p>\n<div class=\"actions\"><a class=\"button\" href=\"${escapeHtml(url)}\">Go back to ${escapeHtml(host)}</a></div>`,\n\t\t);\n\t}\n\n\t/** The user's own Cancel: back to the client at once (`access_denied`). */\n\t#redirectError(\n\t\tredirectUri: string,\n\t\tstate: string | undefined,\n\t\terror: string,\n\t\tdescription: string,\n\t): Response {\n\t\treturn new Response(null, {\n\t\t\tstatus: 303,\n\t\t\theaders: {\n\t\t\t\tlocation: this.#errorUrl(redirectUri, state, error, description),\n\t\t\t\t\"cache-control\": \"no-store\",\n\t\t\t\t\"referrer-policy\": \"no-referrer\",\n\t\t\t},\n\t\t});\n\t}\n\n\t#redirect(redirectUri: string, params: Record<string, string>): Response {\n\t\tconst url = new URL(redirectUri);\n\t\tfor (const [k, v] of Object.entries(params)) url.searchParams.append(k, v);\n\t\treturn new Response(null, {\n\t\t\tstatus: 303,\n\t\t\theaders: {\n\t\t\t\tlocation: url.href,\n\t\t\t\t\"cache-control\": \"no-store\",\n\t\t\t\t\"referrer-policy\": \"no-referrer\",\n\t\t\t},\n\t\t});\n\t}\n\n\t/** GET `/authorize`: check the request, then show the pairing page. */\n\tasync authorize(req: Request, ip: string): Promise<Response> {\n\t\tif (!this.#viewsPerIp.take(ip)) {\n\t\t\treturn this.#errorPage(\n\t\t\t\t429,\n\t\t\t\t\"Too many requests from your network. Wait a minute and try again.\",\n\t\t\t);\n\t\t}\n\t\tconst query = new URL(req.url).searchParams;\n\t\tconst p = single(query, [\n\t\t\t\"response_type\",\n\t\t\t\"client_id\",\n\t\t\t\"redirect_uri\",\n\t\t\t\"code_challenge\",\n\t\t\t\"code_challenge_method\",\n\t\t\t\"state\",\n\t\t\t\"resource\",\n\t\t\t\"scope\",\n\t\t]);\n\t\tif (\"duplicate\" in p) {\n\t\t\tif (p.duplicate === \"client_id\" || p.duplicate === \"redirect_uri\") {\n\t\t\t\treturn this.#errorPage(400, `The request names ${p.duplicate} twice.`);\n\t\t\t}\n\t\t}\n\t\tconst clientId = \"duplicate\" in p ? query.get(\"client_id\") : p[\"client_id\"];\n\t\tconst redirectUri =\n\t\t\t\"duplicate\" in p ? query.get(\"redirect_uri\") : p[\"redirect_uri\"];\n\t\tif (clientId === undefined || clientId === null) {\n\t\t\treturn this.#errorPage(400, \"The request has no client_id.\");\n\t\t}\n\t\tlet client: ClientMetadata;\n\t\ttry {\n\t\t\tclient = await this.#cimd.get(clientId);\n\t\t} catch (error) {\n\t\t\tif (error instanceof ClientMetadataError) {\n\t\t\t\tthis.#host.logger.info(\"oauth.client_refused\", {\n\t\t\t\t\treason: error.reason,\n\t\t\t\t\t// Not the text itself: it is anyone's choice.\n\t\t\t\t\thost: URL.canParse(clientId)\n\t\t\t\t\t\t? hostOf(clientId).slice(0, 100)\n\t\t\t\t\t\t: \"(not a URL)\",\n\t\t\t\t});\n\t\t\t\treturn this.#errorPage(400, error.message);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t\tif (redirectUri === undefined || redirectUri === null) {\n\t\t\treturn this.#errorPage(400, \"The request has no redirect_uri.\");\n\t\t}\n\t\tif (!client.redirectUris.some((r) => redirectUriMatches(redirectUri, r))) {\n\t\t\tthis.#host.logger.info(\"oauth.client_refused\", {\n\t\t\t\treason: \"redirect_uri\",\n\t\t\t\thost: client.host,\n\t\t\t});\n\t\t\treturn this.#errorPage(\n\t\t\t\t400,\n\t\t\t\t`${client.name} asked to return to an address it has not registered (${hostOf(redirectUri)}).`,\n\t\t\t);\n\t\t}\n\t\t// From here on errors go back to the (now trusted) redirect URI.\n\t\tconst state = \"duplicate\" in p ? undefined : p[\"state\"];\n\t\tconst fail = (error: string, description: string) =>\n\t\t\tthis.#invalidRequestPage(redirectUri, state, error, description);\n\t\tif (\"duplicate\" in p) {\n\t\t\treturn fail(\"invalid_request\", `${p.duplicate} was sent more than once`);\n\t\t}\n\t\tif (state !== undefined && state.length > MAX_STATE_LENGTH) {\n\t\t\treturn fail(\"invalid_request\", \"state is too long\");\n\t\t}\n\t\tif (p[\"response_type\"] !== \"code\") {\n\t\t\treturn fail(\n\t\t\t\t\"unsupported_response_type\",\n\t\t\t\t\"only response_type=code is supported\",\n\t\t\t);\n\t\t}\n\t\tconst challenge = p[\"code_challenge\"];\n\t\tif (challenge === undefined) {\n\t\t\treturn fail(\"invalid_request\", \"code_challenge is required (PKCE)\");\n\t\t}\n\t\tif (p[\"code_challenge_method\"] !== \"S256\") {\n\t\t\treturn fail(\"invalid_request\", \"code_challenge_method must be S256\");\n\t\t}\n\t\tif (!/^[A-Za-z0-9\\-._~]{43,128}$/.test(challenge)) {\n\t\t\treturn fail(\"invalid_request\", \"code_challenge is malformed\");\n\t\t}\n\t\tconst resource = p[\"resource\"];\n\t\tconst addressId = this.#addressOf(resource);\n\t\tif (resource === undefined || addressId === undefined) {\n\t\t\treturn fail(\n\t\t\t\t\"invalid_target\",\n\t\t\t\t\"resource must be the MCP address of a page on this relay\",\n\t\t\t);\n\t\t}\n\t\tconst scopes = this.#grantedScopes(p[\"scope\"]);\n\t\tif (scopes === undefined) {\n\t\t\treturn fail(\"invalid_scope\", `the only scope is \"${SCOPE}\"`);\n\t\t}\n\n\t\t// No connected tab, or its dialog is closed: nothing to pair with, so\n\t\t// nothing is kept (DEC-061, DEC-088). Pairing needs the dialog open\n\t\t// anyway, so a stranger who knows the address cannot fill its slots.\n\t\tconst session = this.#host.session(addressId);\n\t\tif (session === undefined || !session.pairingOpen) {\n\t\t\treturn this.#offlinePage(\n\t\t\t\tclient,\n\t\t\t\t`/authorize${new URL(req.url).search}`,\n\t\t\t\tthis.#errorUrl(\n\t\t\t\t\tredirectUri,\n\t\t\t\t\tstate,\n\t\t\t\t\t\"access_denied\",\n\t\t\t\t\t\"the user cancelled the connection\",\n\t\t\t\t),\n\t\t\t\tsession !== undefined,\n\t\t\t);\n\t\t}\n\t\tconst method = this.#methods[session.site.pairing];\n\t\tif (method?.prepare !== undefined) {\n\t\t\ttry {\n\t\t\t\tawait method.prepare();\n\t\t\t} catch (error) {\n\t\t\t\tthis.#host.logger.warn(\"oidc.discovery_failed\", {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\terror instanceof Error ? error.message.slice(0, 200) : \"unknown\",\n\t\t\t\t});\n\t\t\t\treturn this.#errorPage(\n\t\t\t\t\t503,\n\t\t\t\t\t\"The sign-in provider of this page cannot be reached right now. Wait a minute and try again.\",\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tthis.#sweepPending();\n\t\tif (\n\t\t\tthis.#pending.size >= MAX_PENDING ||\n\t\t\t(this.#pendingPerIp.get(ip) ?? 0) >= MAX_PENDING_PER_IP\n\t\t) {\n\t\t\treturn this.#errorPage(\n\t\t\t\t429,\n\t\t\t\t\"Too many connection requests are open right now. Wait a few minutes and try again.\",\n\t\t\t);\n\t\t}\n\t\tthis.#makeRoom(addressId, ip);\n\t\tconst pending: Pending = {\n\t\t\tid: random(24),\n\t\t\texpiresAt: Date.now() + PENDING_TTL_MS,\n\t\t\tip,\n\t\t\tclient,\n\t\t\tredirectUri,\n\t\t\tstate,\n\t\t\tcodeChallenge: challenge,\n\t\t\taddressId,\n\t\t\tresource,\n\t\t\tscopes,\n\t\t};\n\t\tthis.#addPending(pending);\n\t\tconst browser = this.#browserId(req);\n\t\treturn this.#pairingPage(\n\t\t\tpending,\n\t\t\tbrowser.id,\n\t\t\tundefined,\n\t\t\t200,\n\t\t\tbrowser.cookie,\n\t\t);\n\t}\n\n\t#addPending(p: Pending): void {\n\t\tthis.#pending.set(p.id, p);\n\t\tthis.#pendingPerIp.set(p.ip, (this.#pendingPerIp.get(p.ip) ?? 0) + 1);\n\t\tthis.#pendingPerAddress.set(\n\t\t\tp.addressId,\n\t\t\t(this.#pendingPerAddress.get(p.addressId) ?? 0) + 1,\n\t\t);\n\t}\n\n\t/**\n\t * An address at its caps evicts instead of refusing (DEC-088): a network\n\t * past its share loses its own oldest request; a full address loses the\n\t * oldest request of the network holding the most, so a flood from a few\n\t * networks does not push out the owner's one request.\n\t */\n\t#makeRoom(addressId: string, ip: string): void {\n\t\tconst count = this.#pendingPerAddress.get(addressId) ?? 0;\n\t\tif (count < MAX_PENDING_PER_ADDRESS_IP) return;\n\t\tconst byIp = new Map<string, Pending[]>();\n\t\tfor (const p of this.#pending.values()) {\n\t\t\tif (p.addressId !== addressId) continue;\n\t\t\tconst list = byIp.get(p.ip);\n\t\t\tif (list === undefined) byIp.set(p.ip, [p]);\n\t\t\telse list.push(p);\n\t\t}\n\t\tconst own = byIp.get(ip) ?? [];\n\t\tif (own.length >= MAX_PENDING_PER_ADDRESS_IP) {\n\t\t\tif (own[0] !== undefined) this.#dropPending(own[0]);\n\t\t\treturn;\n\t\t}\n\t\tif (count < MAX_PENDING_PER_ADDRESS) return;\n\t\t// Networks come in order of their first request: `>=` breaks a tie\n\t\t// toward the one that came last, so an equal flood never evicts the\n\t\t// owner who was there first (review of PR 32).\n\t\tlet most: Pending[] = [];\n\t\tfor (const list of byIp.values()) {\n\t\t\tif (list.length >= most.length) most = list;\n\t\t}\n\t\tif (most[0] !== undefined) this.#dropPending(most[0]);\n\t}\n\n\t#dropPending(p: Pending): void {\n\t\tif (!this.#pending.delete(p.id)) return;\n\t\tconst dec = (map: Map<string, number>, key: string) => {\n\t\t\tconst n = (map.get(key) ?? 1) - 1;\n\t\t\tif (n <= 0) map.delete(key);\n\t\t\telse map.set(key, n);\n\t\t};\n\t\tdec(this.#pendingPerIp, p.ip);\n\t\tdec(this.#pendingPerAddress, p.addressId);\n\t\tfor (const method of Object.values(this.#methods)) method?.forget?.(p.id);\n\t}\n\n\t/**\n\t * The tab is not connected, or (`connected`) its dialog is closed: try\n\t * again by loading the page again.\n\t */\n\t#offlinePage(\n\t\tclient: ClientMetadata,\n\t\tretryUrl: string,\n\t\tcancelUrl: string,\n\t\tconnected = false,\n\t): Response {\n\t\tconst who = `<strong>${escapeHtml(client.name)}</strong> (${escapeHtml(client.host)})`;\n\t\tconst title = connected\n\t\t\t? \"Open “Connect your AI app” first\"\n\t\t\t: \"Open the page first\";\n\t\tconst intro = connected\n\t\t\t? `<p>${who} wants to use the tools of a web page, but that page is not connecting right now.</p>\n<p>In the page, click “Connect your AI app”, then try again here.</p>`\n\t\t\t: `<p>${who} wants to use the tools of a web page, but that page is not open right now.</p>\n<p>Open the page, click “Connect your AI app”, then try again here.</p>`;\n\t\tconst body = `<h1>${title}</h1>\n${intro}\n<div class=\"actions\">\n<a class=\"button primary\" href=\"${escapeHtml(retryUrl)}\">Try again</a>\n<a class=\"button\" href=\"${escapeHtml(cancelUrl)}\">Cancel</a>\n</div>`;\n\t\treturn this.#page(200, title, body);\n\t}\n\n\t/** POST `/authorize`: the pairing form (connect, cancel, try again). */\n\tasync authorizeSubmit(req: Request, ip: string): Promise<Response> {\n\t\t// Only our own form may post here (defence in depth beside the token).\n\t\tconst origin = req.headers.get(\"origin\");\n\t\tconst site = req.headers.get(\"sec-fetch-site\");\n\t\tif (\n\t\t\t(origin !== null && origin !== this.#issuer) ||\n\t\t\t(site !== null && site !== \"same-origin\")\n\t\t) {\n\t\t\treturn this.#errorPage(403, \"This form can only be sent from this page.\");\n\t\t}\n\t\tif (!this.#attemptsPerIp.take(ip)) {\n\t\t\treturn this.#errorPage(\n\t\t\t\t429,\n\t\t\t\t\"Too many attempts from your network. Wait a few minutes, then start again from your AI app.\",\n\t\t\t);\n\t\t}\n\t\tconst type = (req.headers.get(\"content-type\") ?? \"\").split(\";\")[0]?.trim();\n\t\tif (type !== \"application/x-www-form-urlencoded\") {\n\t\t\treturn this.#errorPage(400, \"The form was not sent correctly.\");\n\t\t}\n\t\tconst text = await this.#readForm(req);\n\t\tif (text === undefined) {\n\t\t\treturn this.#errorPage(413, \"The form was too large.\");\n\t\t}\n\t\tconst form = new URLSearchParams(text);\n\t\tconst pending = this.#pending.get(form.get(\"request_id\") ?? \"\");\n\t\tif (pending === undefined || pending.expiresAt <= Date.now()) {\n\t\t\treturn this.#errorPage(\n\t\t\t\t400,\n\t\t\t\t\"This connection request has expired or was already used.\",\n\t\t\t);\n\t\t}\n\t\tconst browser = this.#browserId(req);\n\t\tconst csrf = form.get(\"csrf\") ?? \"\";\n\t\tif (\n\t\t\tbrowser.cookie !== undefined ||\n\t\t\t!Bun.CSRF.verify(csrf, {\n\t\t\t\tsecret: this.#csrfSecret,\n\t\t\t\tsessionId: `${pending.id}.${browser.id}`,\n\t\t\t\tmaxAge: PENDING_TTL_MS,\n\t\t\t})\n\t\t) {\n\t\t\t// A new cookie means the browser did not send ours: not the same visit.\n\t\t\treturn this.#errorPage(\n\t\t\t\t403,\n\t\t\t\t\"The form could not be checked. Your browser may block cookies for this site.\",\n\t\t\t);\n\t\t}\n\n\t\tconst action = form.get(\"action\");\n\t\tif (action === \"cancel\") {\n\t\t\tthis.#dropPending(pending);\n\t\t\tthis.#host.logger.info(\"oauth.cancelled\", {\n\t\t\t\taddress: shortAddress(pending.addressId),\n\t\t\t\tclient: pending.client.host,\n\t\t\t});\n\t\t\treturn this.#redirectError(\n\t\t\t\tpending.redirectUri,\n\t\t\t\tpending.state,\n\t\t\t\t\"access_denied\",\n\t\t\t\t\"the user cancelled the connection\",\n\t\t\t);\n\t\t}\n\t\tif (action !== \"connect\") {\n\t\t\treturn this.#pairingPage(pending, browser.id);\n\t\t}\n\n\t\tconst session = this.#host.session(pending.addressId);\n\t\tif (session === undefined) {\n\t\t\treturn this.#pairingPage(\n\t\t\t\tpending,\n\t\t\t\tbrowser.id,\n\t\t\t\t\"The page is still not open.\",\n\t\t\t);\n\t\t}\n\t\tconst method = this.#methods[session.site.pairing];\n\t\tif (method === undefined) {\n\t\t\treturn this.#errorPage(\n\t\t\t\t500,\n\t\t\t\t\"This page's pairing method is not available.\",\n\t\t\t);\n\t\t}\n\t\t// Only a real guess is charged: the method calls this right before it\n\t\t// compares a well-formed answer (DEC-061).\n\t\tconst admit = () =>\n\t\t\tthis.#guessesPerAddressIp.take(`${pending.addressId}|${ip}`) &&\n\t\t\tthis.#guessesPerAddress.take(pending.addressId);\n\t\tconst result = await method.check(session, form, admit, {\n\t\t\trequestId: pending.id,\n\t\t\tbrowserId: browser.id,\n\t\t\taddressId: pending.addressId,\n\t\t});\n\t\tif (result.ok === \"redirect\") {\n\t\t\tthis.#host.logger.info(\"oauth.pairing_redirect\", {\n\t\t\t\taddress: shortAddress(pending.addressId),\n\t\t\t\tclient: pending.client.host,\n\t\t\t\tmethod: method.kind,\n\t\t\t});\n\t\t\treturn new Response(null, {\n\t\t\t\tstatus: 303,\n\t\t\t\theaders: {\n\t\t\t\t\tlocation: result.location,\n\t\t\t\t\t\"cache-control\": \"no-store\",\n\t\t\t\t\t\"referrer-policy\": \"no-referrer\",\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\t\tif (!result.ok) {\n\t\t\tthis.#host.logger.info(\"oauth.pairing_failed\", {\n\t\t\t\taddress: shortAddress(pending.addressId),\n\t\t\t\tclient: pending.client.host,\n\t\t\t\treason: result.reason,\n\t\t\t});\n\t\t\treturn this.#pairingPage(\n\t\t\t\tpending,\n\t\t\t\tbrowser.id,\n\t\t\t\tresult.message,\n\t\t\t\tresult.reason === \"rate_limited\" || result.reason === \"busy\"\n\t\t\t\t\t? 429\n\t\t\t\t\t: 400,\n\t\t\t);\n\t\t}\n\n\t\treturn this.#paired(pending, method.kind, result);\n\t}\n\n\t/** Pairing succeeded: issue the authorization code and send the browser back. */\n\t#paired(\n\t\tpending: Pending,\n\t\tkind: PairingKind,\n\t\tresult: Extract<PairingResult, { ok: true }>,\n\t): Response {\n\t\tthis.#dropPending(pending);\n\t\t// The address id first, so a load balancer that reads the `/token` body\n\t\t// can route the exchange to this instance, which holds the code (DEC-098).\n\t\tconst code = `${pending.addressId}.${random(32)}`;\n\t\tif (this.#codes.size >= MAX_CODES) this.#sweepCodes(true);\n\t\tconst { id: _id, expiresAt: _expires, ip: _ip, ...request } = pending;\n\t\tthis.#codes.set(hashToken(code), {\n\t\t\t...request,\n\t\t\texpiresAt: Date.now() + this.#authCodeTtlMs,\n\t\t\tused: false,\n\t\t\t...(result.subject === undefined ? {} : { subject: result.subject }),\n\t\t});\n\t\tthis.#host.logger.info(\"oauth.paired\", {\n\t\t\taddress: shortAddress(pending.addressId),\n\t\t\tclient: pending.client.host,\n\t\t\tmethod: kind,\n\t\t});\n\t\treturn this.#redirect(pending.redirectUri, {\n\t\t\tcode,\n\t\t\t...(pending.state === undefined ? {} : { state: pending.state }),\n\t\t\tiss: this.#issuer,\n\t\t});\n\t}\n\n\t/** GET `/authorize/callback`: the provider sends the browser back (DEC-079). */\n\tasync authorizeCallback(req: Request, ip: string): Promise<Response> {\n\t\tif (!this.#viewsPerIp.take(ip)) {\n\t\t\treturn this.#errorPage(\n\t\t\t\t429,\n\t\t\t\t\"Too many requests from your network. Wait a minute and try again.\",\n\t\t\t);\n\t\t}\n\t\tconst method = this.#methods.oidc;\n\t\tif (method?.callback === undefined) {\n\t\t\treturn new Response(\"Not found\", { status: 404 });\n\t\t}\n\t\tconst browser = this.#browserId(req);\n\t\tif (browser.cookie !== undefined) {\n\t\t\t// No visit cookie: not the browser that started the sign-in.\n\t\t\treturn this.#errorPage(\n\t\t\t\t403,\n\t\t\t\t\"The sign-in could not be checked. Your browser may block cookies for this site; start again from your AI app.\",\n\t\t\t);\n\t\t}\n\t\tthis.#sweepPending();\n\t\tconst params = new URL(req.url).searchParams;\n\t\tconst { requestId, result } = await method.callback(\n\t\t\tparams,\n\t\t\tbrowser.id,\n\t\t\t(id) => {\n\t\t\t\tconst p = this.#pending.get(id);\n\t\t\t\treturn p === undefined ? undefined : this.#host.session(p.addressId);\n\t\t\t},\n\t\t);\n\t\tconst pending =\n\t\t\trequestId === undefined ? undefined : this.#pending.get(requestId);\n\t\tif (pending === undefined || pending.expiresAt <= Date.now()) {\n\t\t\tconst message =\n\t\t\t\tresult.ok === false\n\t\t\t\t\t? result.message\n\t\t\t\t\t: \"This connection request has expired or was already used.\";\n\t\t\treturn this.#errorPage(400, message);\n\t\t}\n\t\tif (result.ok === true) return this.#paired(pending, method.kind, result);\n\t\tconst message =\n\t\t\tresult.ok === false ? result.message : \"Something went wrong.\";\n\t\tconst reason = result.ok === false ? result.reason : \"unexpected\";\n\t\tthis.#host.logger.info(\"oauth.pairing_failed\", {\n\t\t\taddress: shortAddress(pending.addressId),\n\t\t\tclient: pending.client.host,\n\t\t\treason,\n\t\t});\n\t\treturn this.#pairingPage(\n\t\t\tpending,\n\t\t\tbrowser.id,\n\t\t\tmessage,\n\t\t\treason === \"mismatch\" ? 403 : 400,\n\t\t);\n\t}\n\n\t#addressOf(resource: string | undefined): string | undefined {\n\t\tif (resource === undefined) return undefined;\n\t\tconst prefix = `${this.#issuer}/a/`;\n\t\tif (!resource.startsWith(prefix) || !resource.endsWith(\"/mcp\")) {\n\t\t\treturn undefined;\n\t\t}\n\t\tconst id = resource.slice(prefix.length, -\"/mcp\".length);\n\t\treturn isAddressId(id) ? id : undefined;\n\t}\n\n\t/** Requested scopes reduced to what exists; `undefined` = none of them. */\n\t#grantedScopes(scope: string | undefined): string[] | undefined {\n\t\tif (scope === undefined) return [SCOPE];\n\t\tconst requested = scope.split(\" \").filter((s) => s !== \"\");\n\t\tif (requested.length === 0) return [SCOPE];\n\t\treturn requested.includes(SCOPE) ? [SCOPE] : undefined;\n\t}\n\n\t/** The browser's visit id from our cookie, or a new one with its cookie. */\n\t#browserId(req: Request): { id: string; cookie?: string } {\n\t\tconst name = this.#cookieName();\n\t\tconst header = req.headers.get(\"cookie\") ?? \"\";\n\t\tfor (const part of header.split(\";\")) {\n\t\t\tconst [k, v] = part.trim().split(\"=\");\n\t\t\tif (k === name && v !== undefined && /^[A-Za-z0-9_-]{22}$/.test(v)) {\n\t\t\t\treturn { id: v };\n\t\t\t}\n\t\t}\n\t\tconst id = random(16);\n\t\tconst secure = this.#issuer.startsWith(\"https:\") ? \"; Secure\" : \"\";\n\t\treturn {\n\t\t\tid,\n\t\t\tcookie: `${name}=${id}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${PENDING_TTL_MS / 1000}${secure}`,\n\t\t};\n\t}\n\n\t#cookieName(): string {\n\t\treturn this.#issuer.startsWith(\"https:\")\n\t\t\t? \"__Host-tab-mcp-authorize\"\n\t\t\t: \"tab-mcp-authorize\";\n\t}\n\n\tasync #readForm(req: Request): Promise<string | undefined> {\n\t\tconst length = Number(req.headers.get(\"content-length\") ?? \"0\");\n\t\tif (length > MAX_FORM_BYTES) return undefined;\n\t\tconst bytes = await req.arrayBuffer().catch(() => new ArrayBuffer(0));\n\t\tif (bytes.byteLength > MAX_FORM_BYTES) return undefined;\n\t\treturn new TextDecoder().decode(bytes);\n\t}\n\n\t#pairingPage(\n\t\tpending: Pending,\n\t\tbrowserId: string,\n\t\terror?: string,\n\t\tstatus = 200,\n\t\tcookie?: string,\n\t): Response {\n\t\tconst csrf = Bun.CSRF.generate(this.#csrfSecret, {\n\t\t\tsessionId: `${pending.id}.${browserId}`,\n\t\t\texpiresIn: PENDING_TTL_MS,\n\t\t});\n\t\tconst client = pending.client;\n\t\tconst clientLabel = `<strong>${escapeHtml(client.name)}</strong> (${escapeHtml(client.host)})`;\n\t\tconst hidden = `<input type=\"hidden\" name=\"request_id\" value=\"${escapeHtml(pending.id)}\">\n<input type=\"hidden\" name=\"csrf\" value=\"${escapeHtml(csrf)}\">`;\n\t\tconst errorHtml =\n\t\t\terror === undefined\n\t\t\t\t? \"\"\n\t\t\t\t: `<p class=\"error\" role=\"alert\" id=\"error\">${escapeHtml(error)}</p>`;\n\t\tconst extra = cookie === undefined ? {} : { \"set-cookie\": cookie };\n\t\tconst targets: string[] = [formTarget(pending.redirectUri)];\n\t\tconst cancel = `<button type=\"submit\" class=\"secondary\" name=\"action\" value=\"cancel\" formnovalidate>Cancel</button>`;\n\n\t\tconst session = this.#host.session(pending.addressId);\n\t\tif (session === undefined) {\n\t\t\t// The tab went away after the page was opened: keep the request,\n\t\t\t// so Try again (a post) can continue once the tab is back.\n\t\t\tconst body = `<h1>Open the page first</h1>\n${errorHtml}\n<p>${clientLabel} wants to use the tools of a web page, but that page is not open right now.</p>\n<p>Open the page, click “Connect your AI app”, then try again here.</p>\n<form method=\"post\" action=\"/authorize?a=${pending.addressId}\">\n${hidden}\n<div class=\"actions\">\n<button type=\"submit\" class=\"primary\" name=\"action\" value=\"retry\">Try again</button>\n${cancel}\n</div>\n</form>`;\n\t\t\treturn this.#page(status, \"Open the page first\", body, targets, extra);\n\t\t}\n\n\t\tconst method = this.#methods[session.site.pairing];\n\t\tif (method === undefined) {\n\t\t\treturn this.#errorPage(\n\t\t\t\t500,\n\t\t\t\t\"This page's pairing method is not available.\",\n\t\t\t);\n\t\t}\n\t\ttargets.push(...(method.formTargets?.() ?? []));\n\t\t// A browser always sends an Origin; only a non-browser tab on an open\n\t\t// relay has none.\n\t\tconst page =\n\t\t\tsession.origin === \"\"\n\t\t\t\t? \"a page that did not say where it runs\"\n\t\t\t\t: `<span class=\"origin\">${escapeHtml(session.origin)}</span>`;\n\t\tconst target =\n\t\t\tsession.appName === undefined\n\t\t\t\t? `tools from ${page}`\n\t\t\t\t: `<strong>${escapeHtml(session.appName)}</strong> on ${page}`;\n\t\t// The verified host is part of the heading and title, so a\n\t\t// self-declared name never stands alone (DEC-065).\n\t\tconst heading = `Connect ${client.name} from ${client.host}`;\n\t\tconst body = `<h1>${escapeHtml(heading)}</h1>\n<p>You're connecting ${clientLabel} to ${target}.</p>\n<p>${escapeHtml(client.name)} can then use this page's tools while the page is open. You can disconnect it in the page at any time.</p>\n${errorHtml}\n<form method=\"post\" action=\"/authorize?a=${pending.addressId}\">\n${hidden}\n${method.render(session)}\n<div class=\"actions\">\n<button type=\"submit\" class=\"primary\" name=\"action\" value=\"connect\">Connect</button>\n${cancel}\n</div>\n</form>\n<p class=\"help\">After you connect, your browser goes back to ${escapeHtml(hostOf(pending.redirectUri))}. Only connect AI apps you started yourself.</p>`;\n\t\treturn this.#page(status, heading, body, targets, extra);\n\t}\n\n\t// /token\n\n\t/** POST `/token`. */\n\tasync token(req: Request, ip: string): Promise<Response> {\n\t\tconst noStore = { \"cache-control\": \"no-store\", pragma: \"no-cache\" };\n\t\ttry {\n\t\t\tif (!this.#tokenPerIp.take(ip)) {\n\t\t\t\tthrow new OAuthError(\n\t\t\t\t\t429,\n\t\t\t\t\t\"temporarily_unavailable\",\n\t\t\t\t\t\"too many requests; try again shortly\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst params = await this.#formParams(req);\n\t\t\tconst p = single(params, [\n\t\t\t\t\"grant_type\",\n\t\t\t\t\"client_id\",\n\t\t\t\t\"code\",\n\t\t\t\t\"redirect_uri\",\n\t\t\t\t\"code_verifier\",\n\t\t\t\t\"refresh_token\",\n\t\t\t\t\"resource\",\n\t\t\t\t\"scope\",\n\t\t\t\t\"client_assertion\",\n\t\t\t\t\"client_assertion_type\",\n\t\t\t]);\n\t\t\tif (\"duplicate\" in p) {\n\t\t\t\tthrow new OAuthError(\n\t\t\t\t\t400,\n\t\t\t\t\t\"invalid_request\",\n\t\t\t\t\t`${p.duplicate} was sent more than once`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst client = await this.#authenticateClient(p, [\n\t\t\t\t`${this.#issuer}/token`,\n\t\t\t]);\n\t\t\tlet body: Record<string, unknown>;\n\t\t\tif (p[\"grant_type\"] === \"authorization_code\") {\n\t\t\t\tbody = await this.#codeGrant(p, client);\n\t\t\t} else if (p[\"grant_type\"] === \"refresh_token\") {\n\t\t\t\tbody = await this.#refreshGrant(p, client);\n\t\t\t} else if (p[\"grant_type\"] === undefined) {\n\t\t\t\tthrow new OAuthError(400, \"invalid_request\", \"grant_type is required\");\n\t\t\t} else {\n\t\t\t\tthrow new OAuthError(\n\t\t\t\t\t400,\n\t\t\t\t\t\"unsupported_grant_type\",\n\t\t\t\t\t\"only authorization_code and refresh_token are supported\",\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn this.#json(200, body, noStore);\n\t\t} catch (error) {\n\t\t\treturn this.#oauthErrorResponse(error, noStore);\n\t\t}\n\t}\n\n\t#oauthErrorResponse(\n\t\terror: unknown,\n\t\theaders: Record<string, string>,\n\t): Response {\n\t\tif (error instanceof OAuthError) {\n\t\t\tconst extra: Record<string, string> = { ...headers };\n\t\t\tif (error.status === 401)\n\t\t\t\textra[\"www-authenticate\"] = 'Basic realm=\"tab-mcp\"';\n\t\t\tif (error.status === 429 || error.status === 503)\n\t\t\t\textra[\"retry-after\"] = \"10\";\n\t\t\treturn this.#json(\n\t\t\t\terror.status,\n\t\t\t\t{ error: error.code, error_description: error.message },\n\t\t\t\textra,\n\t\t\t);\n\t\t}\n\t\tthis.#host.logger.error(\"oauth.error\", {\n\t\t\tmessage: error instanceof Error ? error.name : \"unknown\",\n\t\t});\n\t\treturn this.#json(\n\t\t\t503,\n\t\t\t{\n\t\t\t\terror: \"temporarily_unavailable\",\n\t\t\t\terror_description: \"the relay cannot issue tokens right now\",\n\t\t\t},\n\t\t\t{ ...headers, \"retry-after\": \"10\" },\n\t\t);\n\t}\n\n\t/**\n\t * Form parameters of a token or revocation request. Client secrets, in\n\t * the body or as HTTP Basic, are refused: the methods are `none` and\n\t * `private_key_jwt` (DEC-106).\n\t */\n\tasync #formParams(req: Request): Promise<URLSearchParams> {\n\t\tconst type = (req.headers.get(\"content-type\") ?? \"\").split(\";\")[0]?.trim();\n\t\tif (type !== \"application/x-www-form-urlencoded\") {\n\t\t\tthrow new OAuthError(\n\t\t\t\t400,\n\t\t\t\t\"invalid_request\",\n\t\t\t\t\"the body must be application/x-www-form-urlencoded\",\n\t\t\t);\n\t\t}\n\t\tif (req.headers.get(\"authorization\") !== null) {\n\t\t\t// RFC 6749 §5.2: a client that tried to authenticate gets 401.\n\t\t\tthrow new OAuthError(\n\t\t\t\t401,\n\t\t\t\t\"invalid_client\",\n\t\t\t\t\"HTTP authentication is not supported (token_endpoint_auth_method none or private_key_jwt)\",\n\t\t\t);\n\t\t}\n\t\tconst text = await this.#readForm(req);\n\t\tif (text === undefined) {\n\t\t\tthrow new OAuthError(413, \"invalid_request\", \"the request is too large\");\n\t\t}\n\t\tconst params = new URLSearchParams(text);\n\t\tif (params.has(\"client_secret\")) {\n\t\t\tthrow new OAuthError(\n\t\t\t\t400,\n\t\t\t\t\"invalid_client\",\n\t\t\t\t\"client secrets are not supported (token_endpoint_auth_method none or private_key_jwt)\",\n\t\t\t);\n\t\t}\n\t\treturn params;\n\t}\n\n\t/**\n\t * The client of a token or revocation request (RFC 7521 §4.2, DEC-106).\n\t * Without an assertion it is the `client_id` of a public client, which\n\t * the grant checks. With one, the assertion is verified against the\n\t * client's current metadata, with `aud` the issuer or one of `endpoints`;\n\t * `client_id` may then be left out (the assertion's `sub` names it).\n\t */\n\tasync #authenticateClient(\n\t\tp: Record<string, string | undefined>,\n\t\tendpoints: readonly string[],\n\t): Promise<RequestClient> {\n\t\tconst assertion = p[\"client_assertion\"];\n\t\tconst type = p[\"client_assertion_type\"];\n\t\tif (assertion === undefined && type === undefined) {\n\t\t\tconst clientId = p[\"client_id\"];\n\t\t\tif (clientId === undefined) {\n\t\t\t\tthrow new OAuthError(400, \"invalid_request\", \"client_id is required\");\n\t\t\t}\n\t\t\treturn { clientId, assertion: false };\n\t\t}\n\t\tif (assertion === undefined || type === undefined) {\n\t\t\tthrow new OAuthError(\n\t\t\t\t400,\n\t\t\t\t\"invalid_request\",\n\t\t\t\t\"client_assertion and client_assertion_type go together\",\n\t\t\t);\n\t\t}\n\t\tconst clientId = p[\"client_id\"] ?? assertionSubject(assertion);\n\t\tif (type !== CLIENT_ASSERTION_TYPE) {\n\t\t\tthrow this.#clientAuthFailed(clientId, \"assertion_type\");\n\t\t}\n\t\tif (clientId === undefined) {\n\t\t\tthrow this.#clientAuthFailed(clientId, \"no_client_id\");\n\t\t}\n\t\tlet client: ClientMetadata;\n\t\ttry {\n\t\t\t// A recent copy if the host cannot be reached right now (DEC-106).\n\t\t\tclient = await this.#cimd.get(clientId, { allowStale: true });\n\t\t} catch (error) {\n\t\t\tif (!(error instanceof ClientMetadataError)) throw error;\n\t\t\tconst reason = `metadata_${error.reason}`;\n\t\t\t// A private address answers like an unreachable one, so these\n\t\t\t// endpoints do not tell where a name points (review of PR 65).\n\t\t\tthrow isTransient(error) || error.reason === \"private_address\"\n\t\t\t\t? this.#clientAuthUnavailable(clientId, reason)\n\t\t\t\t: this.#clientAuthFailed(clientId, reason);\n\t\t}\n\t\tif (client.auth.method !== \"private_key_jwt\") {\n\t\t\tthrow this.#clientAuthFailed(clientId, \"not_private_key_jwt\");\n\t\t}\n\t\tconst domain = quotaKey(clientId);\n\t\ttry {\n\t\t\tawait verifyClientAssertion(assertion, {\n\t\t\t\tclientId,\n\t\t\t\tauth: client.auth,\n\t\t\t\tkeys: this.#clientKeys.for(client.auth.jwksUri),\n\t\t\t\taudiences: [this.#issuer, ...endpoints],\n\t\t\t\tclaimNonce: (key, until, now) =>\n\t\t\t\t\tthis.#claimNonce(clientId, domain, key, until, now),\n\t\t\t\tnow: Date.now(),\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tif (!(error instanceof JwtError)) throw error;\n\t\t\t// The keys could not be loaded: that says nothing about the client.\n\t\t\tthrow error.reason === \"jwks_unavailable\"\n\t\t\t\t? this.#clientAuthUnavailable(clientId, error.reason)\n\t\t\t\t: this.#clientAuthFailed(clientId, error.reason);\n\t\t}\n\t\treturn { clientId, assertion: true };\n\t}\n\n\t/** `invalid_client` (401) with one text for every reason, which goes to the log. */\n\t#clientAuthFailed(clientId: string | undefined, reason: string): OAuthError {\n\t\tthis.#host.logger.info(\"oauth.client_auth_failed\", {\n\t\t\tclient: logHost(clientId),\n\t\t\treason,\n\t\t});\n\t\treturn new OAuthError(401, \"invalid_client\", CLIENT_AUTH_FAILED);\n\t}\n\n\t/**\n\t * `temporarily_unavailable` (503, with Retry-After) when the client's\n\t * metadata or keys cannot be had right now, or its share of the replay\n\t * store is full: the client is not told its credentials are bad, so it\n\t * keeps its grant and tries again (DEC-106).\n\t */\n\t#clientAuthUnavailable(clientId: string, reason: string): OAuthError {\n\t\tthis.#host.logger.warn(\"oauth.client_auth_unavailable\", {\n\t\t\tclient: logHost(clientId),\n\t\t\treason,\n\t\t});\n\t\treturn new OAuthError(\n\t\t\t503,\n\t\t\t\"temporarily_unavailable\",\n\t\t\t\"the client cannot be authenticated right now; try again shortly\",\n\t\t);\n\t}\n\n\t/**\n\t * The replay store for assertion ids: the token store's, shared by every\n\t * instance; else this process's memory. Either way one AI app host\n\t * holds at most its share of live entries on this instance (DEC-106).\n\t */\n\tasync #claimNonce(\n\t\tclientId: string,\n\t\tdomain: string,\n\t\tkey: string,\n\t\tuntil: number,\n\t\tnow: number,\n\t): Promise<boolean> {\n\t\tif (this.#nonceQuota.sweep(now)) this.#nonces.sweep(now);\n\t\tconst refusal = this.#nonceQuota.refusal(domain, now);\n\t\tif (refusal !== undefined) {\n\t\t\tthrow this.#clientAuthUnavailable(clientId, `nonces_${refusal}`);\n\t\t}\n\t\tconst store = this.#host.store;\n\t\tconst claimed =\n\t\t\tstore.claimNonce === undefined\n\t\t\t\t? this.#nonces.claim(key, until, now)\n\t\t\t\t: await store.claimNonce(key, until, now);\n\t\tif (claimed) this.#nonceQuota.add(domain, until);\n\t\treturn claimed;\n\t}\n\n\tasync #codeGrant(\n\t\tp: Record<string, string | undefined>,\n\t\tclient: RequestClient,\n\t): Promise<Record<string, unknown>> {\n\t\tconst clientId = client.clientId;\n\t\tconst code = p[\"code\"];\n\t\tconst redirectUri = p[\"redirect_uri\"];\n\t\tconst verifier = p[\"code_verifier\"];\n\t\tif (\n\t\t\tcode === undefined ||\n\t\t\tredirectUri === undefined ||\n\t\t\tverifier === undefined\n\t\t) {\n\t\t\tthrow new OAuthError(\n\t\t\t\t400,\n\t\t\t\t\"invalid_request\",\n\t\t\t\t\"code, redirect_uri and code_verifier are required\",\n\t\t\t);\n\t\t}\n\t\tconst key = hashToken(code);\n\t\tconst issued = this.#codes.get(key);\n\t\tconst invalid = (why: string) => new OAuthError(400, \"invalid_grant\", why);\n\t\tif (issued === undefined) throw invalid(\"the code is not valid\");\n\t\t// Before anything changes: another client (even one with a valid\n\t\t// assertion of its own), or this one without its key, neither burns\n\t\t// the code nor revokes what it produced (DEC-106).\n\t\tif (!constantTimeEqual(clientId, issued.client.clientId)) {\n\t\t\tthrow invalid(\"the code was issued to another client\");\n\t\t}\n\t\tif (assertionRequired(issued.client.auth) && !client.assertion) {\n\t\t\tthrow this.#clientAuthFailed(clientId, \"assertion_required\");\n\t\t}\n\t\tif (issued.used) {\n\t\t\t// RFC 6749 §4.1.2: a code used twice revokes what it produced.\n\t\t\tif (issued.grantId !== undefined) {\n\t\t\t\tawait this.#revokeGrant(\n\t\t\t\t\tissued.addressId,\n\t\t\t\t\tissued.grantId,\n\t\t\t\t\t\"reuse_detected\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow invalid(\"the code was already used\");\n\t\t}\n\t\tissued.used = true; // a failed check below burns the code too\n\t\tif (issued.expiresAt <= Date.now()) throw invalid(\"the code has expired\");\n\t\tif (redirectUri !== issued.redirectUri) {\n\t\t\tthrow invalid(\"redirect_uri does not match the authorization request\");\n\t\t}\n\t\tif (p[\"resource\"] !== undefined && p[\"resource\"] !== issued.resource) {\n\t\t\tthrow new OAuthError(\n\t\t\t\t400,\n\t\t\t\t\"invalid_target\",\n\t\t\t\t\"resource does not match the authorization request\",\n\t\t\t);\n\t\t}\n\t\tif (\n\t\t\t!VERIFIER.test(verifier) ||\n\t\t\t!constantTimeEqual(s256(verifier), issued.codeChallenge)\n\t\t) {\n\t\t\tthrow invalid(\"code_verifier does not match the code_challenge\");\n\t\t}\n\t\t// Identity binding: the person paired must still be the page's user, so\n\t\t// a code issued before a sign-out or a change of user buys nothing.\n\t\tif (\n\t\t\tissued.subject !== undefined &&\n\t\t\tthis.#host.session(issued.addressId)?.identity !== issued.subject\n\t\t) {\n\t\t\tthrow invalid(\"the page's user signed out or changed\");\n\t\t}\n\n\t\tconst now = Date.now();\n\t\tconst grantId = newId(\"g\");\n\t\tissued.grantId = grantId;\n\t\tconst config = this.#host.config;\n\t\tconst tokens = await issueTokenPair(\n\t\t\tthis.#host.store,\n\t\t\t{\n\t\t\t\tgrantId,\n\t\t\t\taddressId: issued.addressId,\n\t\t\t\tclientId: issued.client.clientId,\n\t\t\t\tclientName: issued.client.name,\n\t\t\t\tscopes: issued.scopes,\n\t\t\t\tgrantCreatedAt: now,\n\t\t\t\t...(issued.subject === undefined ? {} : { subject: issued.subject }),\n\t\t\t\t// Made with the client's key: its refreshes need it too (DEC-106).\n\t\t\t\t...(client.assertion ? { clientAuth: \"private_key_jwt\" as const } : {}),\n\t\t\t},\n\t\t\t{\n\t\t\t\taccessTtlMs: config.accessTokenTtlMs,\n\t\t\t\trefreshTtlMs: config.refreshTokenTtlMs,\n\t\t\t\tgrantEndsAt: now + config.grantLifetimeMs,\n\t\t\t\tnow,\n\t\t\t},\n\t\t);\n\t\tif (tokens === undefined) {\n\t\t\t// Revoked while the tokens were issued (code reuse, New address).\n\t\t\tthrow invalid(\"the connection was revoked\");\n\t\t}\n\t\tthis.#host.logger.info(\"oauth.grant_created\", {\n\t\t\taddress: shortAddress(issued.addressId),\n\t\t\tclient: issued.client.host,\n\t\t});\n\t\tthis.#host.grantAdded(issued.addressId, {\n\t\t\tgrantId,\n\t\t\tclient: {\n\t\t\t\tid: issued.client.clientId,\n\t\t\t\tname: issued.client.name,\n\t\t\t\thost: issued.client.host,\n\t\t\t},\n\t\t\tcreatedAt: new Date(now).toISOString(),\n\t\t});\n\t\treturn this.#tokenBody(tokens, issued.scopes);\n\t}\n\n\t#tokenBody(\n\t\ttokens: { accessToken: string; refreshToken: string; expiresIn: number },\n\t\tscopes: string[],\n\t): Record<string, unknown> {\n\t\treturn {\n\t\t\taccess_token: tokens.accessToken,\n\t\t\ttoken_type: \"Bearer\",\n\t\t\texpires_in: tokens.expiresIn,\n\t\t\trefresh_token: tokens.refreshToken,\n\t\t\tscope: scopes.join(\" \"),\n\t\t};\n\t}\n\n\tasync #refreshGrant(\n\t\tp: Record<string, string | undefined>,\n\t\tclient: RequestClient,\n\t): Promise<Record<string, unknown>> {\n\t\tconst clientId = client.clientId;\n\t\tconst token = p[\"refresh_token\"];\n\t\tif (token === undefined) {\n\t\t\tthrow new OAuthError(400, \"invalid_request\", \"refresh_token is required\");\n\t\t}\n\t\tconst invalid = (why: string) => new OAuthError(400, \"invalid_grant\", why);\n\t\tconst hash = hashToken(token);\n\t\tconst record = await this.#host.store.get(hash);\n\t\tif (record === undefined || record.kind !== \"refresh\") {\n\t\t\tthrow invalid(\"the refresh token is not valid\");\n\t\t}\n\t\tif (!constantTimeEqual(clientId, record.clientId)) {\n\t\t\tthrow invalid(\"the refresh token was issued to another client\");\n\t\t}\n\t\t// A grant bound to the client's key is never redeemed as a public\n\t\t// client; checked before reuse detection, so a stolen token alone\n\t\t// cannot revoke the grant either (DEC-106).\n\t\tif (record.clientAuth === \"private_key_jwt\" && !client.assertion) {\n\t\t\tthrow this.#clientAuthFailed(clientId, \"assertion_required\");\n\t\t}\n\t\tconst now = Date.now();\n\t\tif (record.expiresAt <= now) throw invalid(\"the refresh token has expired\");\n\t\tif (record.usedAt !== undefined) {\n\t\t\tawait this.#reuseDetected(record);\n\t\t\tthrow invalid(\n\t\t\t\t\"the refresh token was already used; the connection was revoked\",\n\t\t\t);\n\t\t}\n\t\tif (\n\t\t\tp[\"resource\"] !== undefined &&\n\t\t\tp[\"resource\"] !== `${this.#issuer}/a/${record.addressId}/mcp`\n\t\t) {\n\t\t\tthrow new OAuthError(\n\t\t\t\t400,\n\t\t\t\t\"invalid_target\",\n\t\t\t\t\"resource is not the address this token belongs to\",\n\t\t\t);\n\t\t}\n\t\tconst scope = p[\"scope\"];\n\t\tif (scope !== undefined) {\n\t\t\tconst wanted = scope.split(\" \").filter((s) => s !== \"\");\n\t\t\tif (!wanted.every((s) => record.scopes.includes(s))) {\n\t\t\t\tthrow new OAuthError(400, \"invalid_scope\", \"scope exceeds the grant\");\n\t\t\t}\n\t\t}\n\t\tif (!this.#refreshesPerGrant.take(record.grantId, now)) {\n\t\t\tthrow new OAuthError(\n\t\t\t\t429,\n\t\t\t\t\"temporarily_unavailable\",\n\t\t\t\t\"too many refreshes for this connection; try again in a minute\",\n\t\t\t);\n\t\t}\n\t\t// Atomic: of two refreshes with the same token only one gets here.\n\t\t// The used token is kept for the reuse window only (DEC-091).\n\t\tif (\n\t\t\t!(await this.#host.store.markUsed(\n\t\t\t\thash,\n\t\t\t\tnow,\n\t\t\t\tnow + REFRESH_REUSE_WINDOW_MS,\n\t\t\t))\n\t\t) {\n\t\t\tawait this.#reuseDetected(record);\n\t\t\tthrow invalid(\n\t\t\t\t\"the refresh token was already used; the connection was revoked\",\n\t\t\t);\n\t\t}\n\t\tconst tokens = await issueTokenPair(\n\t\t\tthis.#host.store,\n\t\t\t{\n\t\t\t\tgrantId: record.grantId,\n\t\t\t\taddressId: record.addressId,\n\t\t\t\tclientId: record.clientId,\n\t\t\t\tclientName: record.clientName,\n\t\t\t\tscopes: record.scopes,\n\t\t\t\tgrantCreatedAt: record.grantCreatedAt,\n\t\t\t\t...(record.subject === undefined ? {} : { subject: record.subject }),\n\t\t\t\t// Once bound, always bound; a public grant refreshed with the\n\t\t\t\t// client's key becomes bound (DEC-106).\n\t\t\t\t...(record.clientAuth === \"private_key_jwt\" || client.assertion\n\t\t\t\t\t? { clientAuth: \"private_key_jwt\" as const }\n\t\t\t\t\t: {}),\n\t\t\t},\n\t\t\t{\n\t\t\t\taccessTtlMs: this.#host.config.accessTokenTtlMs,\n\t\t\t\trefreshTtlMs: this.#host.config.refreshTokenTtlMs,\n\t\t\t\t// Refreshes slide, but never beyond the grant's absolute end (DEC-062).\n\t\t\t\tgrantEndsAt: record.grantCreatedAt + this.#host.config.grantLifetimeMs,\n\t\t\t\tnow,\n\t\t\t},\n\t\t);\n\t\tif (tokens === undefined) {\n\t\t\t// The grant was revoked while this refresh ran (DEC-063).\n\t\t\tthrow invalid(\"the connection was revoked\");\n\t\t}\n\t\tthis.#host.logger.debug(\"oauth.refreshed\", {\n\t\t\taddress: shortAddress(record.addressId),\n\t\t\tclient: hostOf(record.clientId),\n\t\t});\n\t\treturn this.#tokenBody(tokens, record.scopes);\n\t}\n\n\tasync #reuseDetected(record: TokenRecord): Promise<void> {\n\t\tthis.#host.logger.warn(\"oauth.refresh_reuse\", {\n\t\t\taddress: shortAddress(record.addressId),\n\t\t\tclient: hostOf(record.clientId),\n\t\t});\n\t\tawait this.#revokeGrant(record.addressId, record.grantId, \"reuse_detected\");\n\t}\n\n\tasync #revokeGrant(\n\t\taddressId: string,\n\t\tgrantId: string,\n\t\treason: string,\n\t): Promise<void> {\n\t\tconst now = Date.now();\n\t\tconst n = await this.#host.store.revoke(\n\t\t\t{ grantId },\n\t\t\t{ now, keepUntil: now + this.#host.config.grantLifetimeMs },\n\t\t);\n\t\tif (n > 0) this.#host.grantRemoved(addressId, grantId, reason);\n\t}\n\n\t// /revoke\n\n\t/** POST `/revoke` (RFC 7009). */\n\tasync revoke(req: Request, ip: string): Promise<Response> {\n\t\tconst noStore = { \"cache-control\": \"no-store\" };\n\t\ttry {\n\t\t\tif (!this.#tokenPerIp.take(ip)) {\n\t\t\t\tthrow new OAuthError(\n\t\t\t\t\t429,\n\t\t\t\t\t\"temporarily_unavailable\",\n\t\t\t\t\t\"too many requests; try again shortly\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst params = await this.#formParams(req);\n\t\t\tconst p = single(params, [\n\t\t\t\t\"token\",\n\t\t\t\t\"client_id\",\n\t\t\t\t\"token_type_hint\",\n\t\t\t\t\"client_assertion\",\n\t\t\t\t\"client_assertion_type\",\n\t\t\t]);\n\t\t\tif (\"duplicate\" in p) {\n\t\t\t\tthrow new OAuthError(\n\t\t\t\t\t400,\n\t\t\t\t\t\"invalid_request\",\n\t\t\t\t\t`${p.duplicate} was sent more than once`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst token = p[\"token\"];\n\t\t\tif (token === undefined) {\n\t\t\t\tthrow new OAuthError(400, \"invalid_request\", \"token is required\");\n\t\t\t}\n\t\t\t// An assertion, if sent, must be valid; a public grant's tokens are\n\t\t\t// revoked without one, a bound grant's only with one (RFC 7009 §2.1,\n\t\t\t// DEC-106).\n\t\t\tconst client = await this.#authenticateClient(p, [\n\t\t\t\t`${this.#issuer}/revoke`,\n\t\t\t\t`${this.#issuer}/token`,\n\t\t\t]);\n\t\t\tconst record = await this.#host.store.get(hashToken(token));\n\t\t\t// RFC 7009 §2.2: an unknown token is not an error.\n\t\t\tif (record === undefined)\n\t\t\t\treturn new Response(null, {\n\t\t\t\t\tstatus: 200,\n\t\t\t\t\theaders: { ...this.#cors(), ...noStore },\n\t\t\t\t});\n\t\t\tif (!constantTimeEqual(client.clientId, record.clientId)) {\n\t\t\t\tthrow new OAuthError(\n\t\t\t\t\t400,\n\t\t\t\t\t\"invalid_client\",\n\t\t\t\t\t\"the token was issued to another client\",\n\t\t\t\t);\n\t\t\t}\n\t\t\t// A stolen token of a bound grant does not end it either.\n\t\t\tif (record.clientAuth === \"private_key_jwt\" && !client.assertion) {\n\t\t\t\tthrow this.#clientAuthFailed(client.clientId, \"assertion_required\");\n\t\t\t}\n\t\t\t// The grant is the connection: revoking either token ends it.\n\t\t\tawait this.#revokeGrant(record.addressId, record.grantId, \"revoked\");\n\t\t\tthis.#host.logger.info(\"oauth.revoked\", {\n\t\t\t\taddress: shortAddress(record.addressId),\n\t\t\t\tclient: hostOf(record.clientId),\n\t\t\t\tby: \"client\",\n\t\t\t});\n\t\t\treturn new Response(null, {\n\t\t\t\tstatus: 200,\n\t\t\t\theaders: { ...this.#cors(), ...noStore },\n\t\t\t});\n\t\t} catch (error) {\n\t\t\treturn this.#oauthErrorResponse(error, noStore);\n\t\t}\n\t}\n\n\t// Housekeeping\n\n\t/** Drop expired requests; insertion order is expiry order, so stop at the first live one. */\n\t#sweepPending(): void {\n\t\tconst now = Date.now();\n\t\tfor (const p of this.#pending.values()) {\n\t\t\tif (p.expiresAt > now) break;\n\t\t\tthis.#dropPending(p);\n\t\t}\n\t}\n\n\t#sweepCodes(force = false): void {\n\t\tconst now = Date.now();\n\t\t// Keep used codes until they expire, to detect reuse.\n\t\tfor (const [key, c] of this.#codes) {\n\t\t\tif (c.expiresAt <= now) this.#codes.delete(key);\n\t\t}\n\t\tif (force && this.#codes.size >= MAX_CODES) {\n\t\t\tconst drop = this.#codes.size - MAX_CODES + 1;\n\t\t\tfor (const key of [...this.#codes.keys()].slice(0, drop)) {\n\t\t\t\tthis.#codes.delete(key);\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Drop expired state; the relay calls it periodically. */\n\tsweep(): void {\n\t\tthis.#sweepPending();\n\t\tthis.#sweepCodes();\n\t\tconst now = Date.now();\n\t\tthis.#viewsPerIp.sweep(now);\n\t\tthis.#attemptsPerIp.sweep(now);\n\t\tthis.#guessesPerAddressIp.sweep(now);\n\t\tthis.#guessesPerAddress.sweep(now);\n\t\tthis.#tokenPerIp.sweep(now);\n\t\tthis.#refreshesPerGrant.sweep(now);\n\t\tthis.#nonces.sweep(now);\n\t}\n}\n",
16
+ "/**\n * In-memory token buckets keyed by IP, address or host (threat model A8).\n * Bounded: when full, the least recently created bucket is dropped, which at\n * worst gives that key a fresh, full bucket.\n */\n\ninterface Bucket {\n\ttokens: number;\n\tupdatedAt: number;\n}\n\nexport interface RateLimitOptions {\n\t/** Burst size: how many requests a fresh key may make at once. */\n\tcapacity: number;\n\t/** Tokens added per second. */\n\trefillPerSecond: number;\n\t/** Most keys kept in memory. */\n\tmaxKeys?: number;\n}\n\nexport class RateLimiter {\n\treadonly #buckets = new Map<string, Bucket>();\n\treadonly #capacity: number;\n\treadonly #refill: number;\n\treadonly #maxKeys: number;\n\n\tconstructor(options: RateLimitOptions) {\n\t\tthis.#capacity = options.capacity;\n\t\tthis.#refill = options.refillPerSecond;\n\t\tthis.#maxKeys = options.maxKeys ?? 10_000;\n\t}\n\n\t/** Take one token for `key`; false when the bucket is empty. */\n\ttake(key: string, now: number = Date.now()): boolean {\n\t\tlet bucket = this.#buckets.get(key);\n\t\tif (bucket === undefined) {\n\t\t\tif (this.#buckets.size >= this.#maxKeys) {\n\t\t\t\tconst oldest = this.#buckets.keys().next().value;\n\t\t\t\tif (oldest !== undefined) this.#buckets.delete(oldest);\n\t\t\t}\n\t\t\tbucket = { tokens: this.#capacity, updatedAt: now };\n\t\t\tthis.#buckets.set(key, bucket);\n\t\t} else {\n\t\t\tconst elapsed = Math.max(0, now - bucket.updatedAt) / 1000;\n\t\t\tbucket.tokens = Math.min(\n\t\t\t\tthis.#capacity,\n\t\t\t\tbucket.tokens + elapsed * this.#refill,\n\t\t\t);\n\t\t\tbucket.updatedAt = now;\n\t\t}\n\t\tif (bucket.tokens < 1) return false;\n\t\tbucket.tokens -= 1;\n\t\treturn true;\n\t}\n\n\t/** Whether `take` would succeed for `key` now; takes nothing. */\n\tavailable(key: string, now: number = Date.now()): boolean {\n\t\tconst bucket = this.#buckets.get(key);\n\t\tif (bucket === undefined) return this.#capacity >= 1;\n\t\tconst elapsed = Math.max(0, now - bucket.updatedAt) / 1000;\n\t\treturn (\n\t\t\tMath.min(this.#capacity, bucket.tokens + elapsed * this.#refill) >= 1\n\t\t);\n\t}\n\n\t/** Drop buckets that are full again (nothing to remember). */\n\tsweep(now: number = Date.now()): void {\n\t\tfor (const [key, b] of this.#buckets) {\n\t\t\tconst elapsed = Math.max(0, now - b.updatedAt) / 1000;\n\t\t\tif (b.tokens + elapsed * this.#refill >= this.#capacity) {\n\t\t\t\tthis.#buckets.delete(key);\n\t\t\t}\n\t\t}\n\t}\n}\n",
17
+ "import type {\n\tGrantSummary,\n\tRevokeOptions,\n\tRevokeSelector,\n\tTokenRecord,\n\tTokenStore,\n} from \"./types.ts\";\n\nfunction copy(record: TokenRecord): TokenRecord {\n\treturn { ...record, scopes: [...record.scopes] };\n}\n\nfunction matches(record: TokenRecord, selector: RevokeSelector): boolean {\n\treturn \"grantId\" in selector\n\t\t? record.grantId === selector.grantId\n\t\t: record.addressId === selector.addressId;\n}\n\n/** Usable at `now`: unexpired, and for refresh tokens not yet used. */\nexport function isLive(record: TokenRecord, now: number): boolean {\n\treturn (\n\t\trecord.expiresAt > now &&\n\t\t(record.kind === \"access\" || record.usedAt === undefined)\n\t);\n}\n\n/** Group live records into grant summaries, oldest grant first. */\nexport function summarizeGrants(\n\trecords: Iterable<TokenRecord>,\n\tnow: number,\n): GrantSummary[] {\n\tconst grants = new Map<string, GrantSummary>();\n\tfor (const r of records) {\n\t\tif (!isLive(r, now) || grants.has(r.grantId)) continue;\n\t\tconst summary: GrantSummary = {\n\t\t\tgrantId: r.grantId,\n\t\t\tclientId: r.clientId,\n\t\t\tclientName: r.clientName,\n\t\t\tscopes: [...r.scopes],\n\t\t\tcreatedAt: r.grantCreatedAt,\n\t\t};\n\t\tif (r.subject !== undefined) summary.subject = r.subject;\n\t\tgrants.set(r.grantId, summary);\n\t}\n\treturn [...grants.values()].sort(\n\t\t(a, b) => a.createdAt - b.createdAt || a.grantId.localeCompare(b.grantId),\n\t);\n}\n\n/**\n * One-time values (client assertion `jti`s, DEC-106) until their expiry, in\n * process memory: `TokenStore.claimNonce` for the memory store, and the\n * relay's own fallback for stores without it.\n */\nexport class NonceSet {\n\treadonly #until = new Map<string, number>();\n\n\tget size(): number {\n\t\treturn this.#until.size;\n\t}\n\n\tclaim(key: string, expiresAt: number, now: number): boolean {\n\t\tconst until = this.#until.get(key);\n\t\tif (until !== undefined && until > now) return false;\n\t\tthis.#until.set(key, expiresAt);\n\t\treturn true;\n\t}\n\n\tsweep(now: number): number {\n\t\tlet n = 0;\n\t\tfor (const [key, until] of this.#until) {\n\t\t\tif (until <= now) {\n\t\t\t\tthis.#until.delete(key);\n\t\t\t\tn++;\n\t\t\t}\n\t\t}\n\t\treturn n;\n\t}\n\n\tclear(): void {\n\t\tthis.#until.clear();\n\t}\n}\n\n/** Tokens in process memory; everything is lost on restart. */\nexport class MemoryTokenStore implements TokenStore {\n\treadonly #records = new Map<string, TokenRecord>();\n\treadonly #nonces = new NonceSet();\n\t/** Revoked grants: grant id → keep until. */\n\treadonly #revokedGrants = new Map<string, number>();\n\t/** Revoked addresses: address id → { at, keep until }. */\n\treadonly #revokedAddresses = new Map<string, { at: number; until: number }>();\n\n\tasync put(record: TokenRecord): Promise<boolean> {\n\t\tif (this.#revokedGrants.has(record.grantId)) return false;\n\t\tconst address = this.#revokedAddresses.get(record.addressId);\n\t\tif (address !== undefined && record.grantCreatedAt <= address.at) {\n\t\t\treturn false;\n\t\t}\n\t\tthis.#records.set(record.hash, copy(record));\n\t\treturn true;\n\t}\n\n\tasync get(hash: string): Promise<TokenRecord | undefined> {\n\t\tconst r = this.#records.get(hash);\n\t\treturn r === undefined ? undefined : copy(r);\n\t}\n\n\tasync markUsed(\n\t\thash: string,\n\t\tnow: number,\n\t\tkeepUntil: number,\n\t): Promise<boolean> {\n\t\tconst r = this.#records.get(hash);\n\t\tif (r === undefined || r.usedAt !== undefined) return false;\n\t\tr.usedAt = now;\n\t\tr.expiresAt = Math.min(r.expiresAt, keepUntil);\n\t\treturn true;\n\t}\n\n\tasync revoke(\n\t\tselector: RevokeSelector,\n\t\toptions: RevokeOptions,\n\t): Promise<number> {\n\t\tif (\"grantId\" in selector) {\n\t\t\tthis.#revokedGrants.set(selector.grantId, options.keepUntil);\n\t\t} else {\n\t\t\tconst old = this.#revokedAddresses.get(selector.addressId);\n\t\t\tthis.#revokedAddresses.set(selector.addressId, {\n\t\t\t\tat: Math.max(old?.at ?? 0, options.now),\n\t\t\t\tuntil: Math.max(old?.until ?? 0, options.keepUntil),\n\t\t\t});\n\t\t}\n\t\tlet n = 0;\n\t\tfor (const [hash, r] of this.#records) {\n\t\t\tif (matches(r, selector)) {\n\t\t\t\tthis.#records.delete(hash);\n\t\t\t\tn++;\n\t\t\t}\n\t\t}\n\t\treturn n;\n\t}\n\n\tasync listGrants(addressId: string, now: number): Promise<GrantSummary[]> {\n\t\tconst own = [...this.#records.values()].filter(\n\t\t\t(r) => r.addressId === addressId,\n\t\t);\n\t\treturn summarizeGrants(own, now);\n\t}\n\n\tasync deleteExpired(now: number): Promise<number> {\n\t\tfor (const [id, until] of this.#revokedGrants) {\n\t\t\tif (until <= now) this.#revokedGrants.delete(id);\n\t\t}\n\t\tfor (const [id, t] of this.#revokedAddresses) {\n\t\t\tif (t.until <= now) this.#revokedAddresses.delete(id);\n\t\t}\n\t\tthis.#nonces.sweep(now);\n\t\tlet n = 0;\n\t\tfor (const [hash, r] of this.#records) {\n\t\t\tif (r.expiresAt <= now) {\n\t\t\t\tthis.#records.delete(hash);\n\t\t\t\tn++;\n\t\t\t}\n\t\t}\n\t\treturn n;\n\t}\n\n\tasync claimNonce(\n\t\tkey: string,\n\t\texpiresAt: number,\n\t\tnow: number,\n\t): Promise<boolean> {\n\t\treturn this.#nonces.claim(key, expiresAt, now);\n\t}\n\n\tasync close(): Promise<void> {\n\t\tthis.#nonces.clear();\n\t\tthis.#records.clear();\n\t\tthis.#revokedGrants.clear();\n\t\tthis.#revokedAddresses.clear();\n\t}\n}\n",
18
+ "import type { TokenKind, TokenRecord, TokenStore } from \"./store/types.ts\";\n\n/** Prefixes make leaked tokens recognisable to secret scanners. */\nconst PREFIX: Record<TokenKind, string> = {\n\taccess: \"tmcp_at_\",\n\trefresh: \"tmcp_rt_\",\n};\n\nfunction random(bytes: number): string {\n\treturn crypto\n\t\t.getRandomValues(new Uint8Array(bytes))\n\t\t.toBase64({ alphabet: \"base64url\", omitPadding: true });\n}\n\n/** A new opaque token with 256 bits of randomness. */\nexport function newToken(kind: TokenKind): string {\n\treturn PREFIX[kind] + random(32);\n}\n\n/** A random identifier with a readable prefix, e.g. `g_…`, `s_…`. */\nexport function newId(prefix: string): string {\n\treturn `${prefix}_${random(12)}`;\n}\n\n/** SHA-256 of a token, base64url without padding: the store key. */\nexport function hashToken(token: string): string {\n\treturn new Bun.CryptoHasher(\"sha256\")\n\t\t.update(token)\n\t\t.digest()\n\t\t.toBase64({ alphabet: \"base64url\", omitPadding: true });\n}\n\n/** The parts of a grant needed to issue tokens for it. */\nexport interface GrantInput {\n\tgrantId?: string;\n\taddressId: string;\n\tclientId: string;\n\tclientName: string;\n\tscopes?: string[];\n\tgrantCreatedAt?: number;\n\t/** The hashed identity of an identity-bound grant (DEC-077). */\n\tsubject?: string;\n\t/** Refreshes need a client assertion (DEC-106). */\n\tclientAuth?: \"private_key_jwt\";\n}\n\n/**\n * Issue an access token for a grant and store its hash, without OAuth. An\n * internal helper for tests that need a token but not the flow; the token\n * endpoint issues through `issueTokenPair`.\n */\nexport async function issueAccessToken(\n\tstore: TokenStore,\n\tgrant: GrantInput,\n\toptions: { ttlMs: number; now?: number },\n): Promise<{ token: string; record: TokenRecord }> {\n\tconst now = options.now ?? Date.now();\n\tconst token = newToken(\"access\");\n\tconst record: TokenRecord = {\n\t\thash: hashToken(token),\n\t\tkind: \"access\",\n\t\tgrantId: grant.grantId ?? newId(\"g\"),\n\t\taddressId: grant.addressId,\n\t\tclientId: grant.clientId,\n\t\tclientName: grant.clientName,\n\t\tscopes: grant.scopes ?? [\"tools\"],\n\t\tgrantCreatedAt: grant.grantCreatedAt ?? now,\n\t\tissuedAt: now,\n\t\texpiresAt: now + options.ttlMs,\n\t};\n\tif (grant.subject !== undefined) record.subject = grant.subject;\n\tawait store.put(record);\n\treturn { token, record };\n}\n\n/**\n * A new access token and refresh token for a grant (DEC-029). Neither lives\n * beyond `grantEndsAt` (DEC-062). Returns `undefined` if the store refuses\n * them because the grant was revoked meanwhile (its tombstone, DEC-063).\n */\nexport async function issueTokenPair(\n\tstore: TokenStore,\n\tgrant: Required<Omit<GrantInput, \"scopes\" | \"subject\" | \"clientAuth\">> & {\n\t\tscopes: string[];\n\t\tsubject?: string;\n\t\tclientAuth?: \"private_key_jwt\";\n\t},\n\toptions: {\n\t\taccessTtlMs: number;\n\t\trefreshTtlMs: number;\n\t\tgrantEndsAt: number;\n\t\tnow?: number;\n\t},\n): Promise<\n\t{ accessToken: string; refreshToken: string; expiresIn: number } | undefined\n> {\n\tconst now = options.now ?? Date.now();\n\tconst accessToken = newToken(\"access\");\n\tconst refreshToken = newToken(\"refresh\");\n\tconst base = {\n\t\tgrantId: grant.grantId,\n\t\taddressId: grant.addressId,\n\t\tclientId: grant.clientId,\n\t\tclientName: grant.clientName,\n\t\tscopes: grant.scopes,\n\t\tgrantCreatedAt: grant.grantCreatedAt,\n\t\tissuedAt: now,\n\t\t...(grant.subject === undefined ? {} : { subject: grant.subject }),\n\t\t...(grant.clientAuth === undefined ? {} : { clientAuth: grant.clientAuth }),\n\t};\n\tconst accessExpires = Math.min(\n\t\tnow + options.accessTtlMs,\n\t\toptions.grantEndsAt,\n\t);\n\t// The refresh token first: a grant whose access token exists always has\n\t// a way to be listed and revoked. If a revocation lands between the two\n\t// puts, it deletes the first and its tombstone refuses the second.\n\tconst refreshStored = await store.put({\n\t\t...base,\n\t\thash: hashToken(refreshToken),\n\t\tkind: \"refresh\",\n\t\texpiresAt: Math.min(now + options.refreshTtlMs, options.grantEndsAt),\n\t});\n\tif (!refreshStored) return undefined;\n\tconst accessStored = await store.put({\n\t\t...base,\n\t\thash: hashToken(accessToken),\n\t\tkind: \"access\",\n\t\texpiresAt: accessExpires,\n\t});\n\tif (!accessStored) return undefined;\n\treturn {\n\t\taccessToken,\n\t\trefreshToken,\n\t\texpiresIn: Math.max(0, Math.floor((accessExpires - now) / 1000)),\n\t};\n}\n",
19
+ "/**\n * A bounded least-recently-used map whose entries each belong to a domain\n * (DEC-106, review of PR 65). Anyone can publish client documents, so the\n * caches of documents and keys must not let one party push out the others':\n *\n * - one domain holds at most `perDomain` entries; a new one replaces that\n * domain's least recently used entry;\n * - when the map is full, a new entry replaces one that `isDead` says may go\n * (past its use), else the least recently used entry of the domain that\n * holds the most.\n *\n * So many documents of one attacker push out their own. What is left: an\n * attacker with as many domains as the map has room for, one entry each,\n * ties with everyone, and least recently used goes first.\n */\nexport class DomainCache<V> {\n\treadonly #entries = new Map<string, { domain: string; value: V }>();\n\treadonly #counts = new Map<string, number>();\n\treadonly #max: number;\n\treadonly #perDomain: number;\n\treadonly #isDead: (value: V, now: number) => boolean;\n\n\tconstructor(\n\t\tmax: number,\n\t\tperDomain: number,\n\t\tisDead: (value: V, now: number) => boolean = () => false,\n\t) {\n\t\tthis.#max = max;\n\t\tthis.#perDomain = perDomain;\n\t\tthis.#isDead = isDead;\n\t}\n\n\tget size(): number {\n\t\treturn this.#entries.size;\n\t}\n\n\t/** The value, marked as most recently used. */\n\tget(key: string): V | undefined {\n\t\tconst entry = this.#entries.get(key);\n\t\tif (entry === undefined) return undefined;\n\t\tthis.#entries.delete(key);\n\t\tthis.#entries.set(key, entry);\n\t\treturn entry.value;\n\t}\n\n\tdelete(key: string): void {\n\t\tconst entry = this.#entries.get(key);\n\t\tif (entry === undefined) return;\n\t\tthis.#entries.delete(key);\n\t\tconst left = (this.#counts.get(entry.domain) ?? 1) - 1;\n\t\tif (left > 0) this.#counts.set(entry.domain, left);\n\t\telse this.#counts.delete(entry.domain);\n\t}\n\n\tset(key: string, domain: string, value: V, now: number): void {\n\t\tthis.delete(key);\n\t\tif ((this.#counts.get(domain) ?? 0) >= this.#perDomain) {\n\t\t\tthis.#evictOldestOf(domain);\n\t\t} else if (this.#entries.size >= this.#max) {\n\t\t\tthis.#evict(now);\n\t\t}\n\t\tthis.#entries.set(key, { domain, value });\n\t\tthis.#counts.set(domain, (this.#counts.get(domain) ?? 0) + 1);\n\t}\n\n\t#evict(now: number): void {\n\t\tfor (const [key, entry] of this.#entries) {\n\t\t\tif (this.#isDead(entry.value, now)) {\n\t\t\t\tthis.delete(key);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tconst most = Math.max(0, ...this.#counts.values());\n\t\t// The least recently used entry among the domains that hold the most.\n\t\tfor (const [key, entry] of this.#entries) {\n\t\t\tif (this.#counts.get(entry.domain) === most) {\n\t\t\t\tthis.delete(key);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Insertion order is use order: the first entry of a domain is its oldest. */\n\t#evictOldestOf(domain: string): void {\n\t\tfor (const [key, entry] of this.#entries) {\n\t\t\tif (entry.domain === domain) {\n\t\t\t\tthis.delete(key);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n}\n",
20
+ "/**\n * Client ID Metadata Documents (CIMD, draft-ietf-oauth-client-id-metadata-document;\n * MCP 2026-07-28 client registration): the `client_id` is an HTTPS URL, and\n * the relay fetches it to learn the client's name and redirect URIs.\n *\n * The URL is chosen by an unauthenticated party, so the fetch is guarded\n * against SSRF (threat model A3): https only, default port, no credentials,\n * the host resolved first and every address checked to be public, no\n * redirects, 5 s, 64 KiB, `application/json`, results cached and fetches per\n * domain and in total rate-limited. A `private_key_jwt` client's `jwks_uri`\n * is fetched under the same rules (DEC-106).\n */\n\nimport { isLoopbackHost, isPublicIp, parseIpv4, parseIpv6 } from \"../ip.ts\";\nimport { isAlgorithm, JWT_ALGORITHMS, type JwtAlgorithm } from \"../jwt.ts\";\nimport type { Logger } from \"../log.ts\";\nimport { RateLimiter } from \"../ratelimit.ts\";\nimport { DomainCache } from \"./domain-cache.ts\";\n\n/** What the relay needs to know about a client. */\nexport interface ClientMetadata {\n\t/** The client id: the document's URL. */\n\tclientId: string;\n\t/** Self-declared, cleaned and shortened for display. */\n\tname: string;\n\t/** Host of the client id URL: the verified part. */\n\thost: string;\n\tredirectUris: string[];\n\t/** How the client authenticates at `/token` (DEC-104, DEC-106). */\n\tauth: ClientAuth;\n}\n\n/**\n * `none`: a public client. `private_key_jwt`: it signs a client assertion\n * with a key from `jwksUri`; `publicAllowed` when its document also lists\n * `none` (DEC-104), so a grant may start without one (DEC-106).\n */\nexport type ClientAuth = { method: \"none\" } | PrivateKeyJwtAuth;\n\nexport interface PrivateKeyJwtAuth {\n\tmethod: \"private_key_jwt\";\n\tjwksUri: string;\n\t/** `token_endpoint_auth_signing_alg` if the document names one, else all. */\n\talgorithms: readonly JwtAlgorithm[];\n\tpublicAllowed: boolean;\n}\n\n/** Why a client id was refused; `message` is plain English for the page. */\nexport class ClientMetadataError extends Error {\n\toverride name = \"ClientMetadataError\";\n\treadonly reason: string;\n\tconstructor(reason: string, message: string) {\n\t\tsuper(message);\n\t\tthis.reason = reason;\n\t}\n}\n\n/**\n * Test-only relaxations (never from configuration): loopback hosts, any port,\n * `http:`, and an extra CA for a self-signed test server.\n */\nexport interface CimdTestMode {\n\tca?: string;\n}\n\nexport interface ClientMetadataFetcherOptions {\n\t/** Accepted client id hosts; empty = any. */\n\tallowedHosts?: readonly string[];\n\tlogger?: Logger;\n\ttimeoutMs?: number;\n\tmaxBytes?: number;\n\t/** Resolve a host name to its addresses (tests replace it). */\n\tlookup?: (hostname: string) => Promise<string[]>;\n\tfetch?: typeof fetch;\n\ttestMode?: CimdTestMode;\n\tnow?: () => number;\n}\n\n/** How `validateDocument` reads a document. */\nexport interface DocumentOptions {\n\t/** Test mode: a `jwks_uri` may use `http:` and any port, like the client id. */\n\ttest?: boolean;\n\t/** A `private_key_jwt` document fell back to `none` (DEC-104), and why. */\n\tonFallback?: (reason: string) => void;\n}\n\n/**\n * Failures that say nothing about the client, only that its document could\n * not be had right now: worth a retry, and a reason to serve a recent copy.\n */\nconst TRANSIENT_REASONS = new Set([\n\t\"rate_limited\",\n\t\"fetch_failed\",\n\t\"unresolvable\",\n\t\"too_large\",\n]);\n\nexport function isTransient(error: ClientMetadataError): boolean {\n\treturn TRANSIENT_REASONS.has(error.reason);\n}\n\n/**\n * How long past its cache lifetime a document is still served to the token\n * and revocation endpoints when a fresh fetch fails transiently (DEC-106):\n * the hour `JwksCache` gives keys.\n */\nconst STALE_GRACE_MS = 60 * 60_000;\n\nconst MAX_CLIENT_ID_LENGTH = 2048;\nconst MAX_NAME_LENGTH = 64;\nconst MAX_REDIRECT_URIS = 20;\nconst MIN_CACHE_MS = 5 * 60_000;\nconst DEFAULT_CACHE_MS = 60 * 60_000;\nconst MAX_CACHE_MS = 24 * 60 * 60_000;\nconst MAX_CACHE_ENTRIES = 1000;\n/** One domain's share of the cache (`DomainCache`, review of PR 65). */\nconst MAX_CACHE_PER_DOMAIN = 100;\n/**\n * A failed fetch is remembered this long per client id (review of PR 65), so\n * made-up paths on a real AI app's host cost one fetch a minute each, not\n * one per request.\n */\nconst FAILURE_CACHE_MS = 60_000;\nconst MAX_FAILURES = 1000;\n\nasync function defaultLookup(hostname: string): Promise<string[]> {\n\tconst records = await Bun.dns.lookup(hostname, {});\n\treturn records.map((r) => r.address);\n}\n\n/**\n * Invisible characters that are not format characters (`Cf`): Hangul and\n * other fillers, the braille blank, combining grapheme joiner, variation\n * selectors.\n */\nconst INVISIBLE =\n\t/[\\u034F\\u115F\\u1160\\u17B4\\u17B5\\u2800\\u3164\\uFFA0]|\\p{Variation_Selector}/gu;\n\n/**\n * Clean a self-declared name for display: control and line/paragraph\n * separators become spaces; format characters (`Cf`: zero-width, bidi,\n * word joiner, BOM, soft hyphen, …) and other invisible characters are\n * removed; whitespace collapses; at most 64 characters (DEC-065).\n */\nexport function displayName(raw: string): string {\n\tlet cleaned = raw\n\t\t.normalize(\"NFC\")\n\t\t.replace(/[\\p{Cc}\\p{Zl}\\p{Zp}]/gu, \" \")\n\t\t.replace(/\\p{Cf}/gu, \"\")\n\t\t.replace(INVISIBLE, \"\");\n\tcleaned = cleaned.replace(/\\s+/g, \" \").trim();\n\tconst chars = [...cleaned];\n\treturn chars.length > MAX_NAME_LENGTH\n\t\t? `${chars.slice(0, MAX_NAME_LENGTH - 1).join(\"\")}…`\n\t\t: cleaned;\n}\n\n/** Seconds of `max-age` in a Cache-Control header, or a marker. */\nfunction cacheMs(header: string | null): number {\n\tif (header === null) return DEFAULT_CACHE_MS;\n\tconst directives = header\n\t\t.toLowerCase()\n\t\t.split(\",\")\n\t\t.map((d) => d.trim());\n\tif (directives.includes(\"no-store\") || directives.includes(\"no-cache\")) {\n\t\treturn MIN_CACHE_MS;\n\t}\n\tconst maxAge = directives\n\t\t.map((d) => /^max-age=(\\d+)$/.exec(d)?.[1])\n\t\t.find((v) => v !== undefined);\n\tif (maxAge === undefined) return DEFAULT_CACHE_MS;\n\treturn Math.min(MAX_CACHE_MS, Math.max(MIN_CACHE_MS, Number(maxAge) * 1000));\n}\n\nfunction isIpLiteral(hostname: string): boolean {\n\tconst h = hostname.replace(/^\\[|\\]$/g, \"\");\n\treturn parseIpv4(h) !== undefined || parseIpv6(h) !== undefined;\n}\n\n/**\n * The key fetches are limited under (DEC-089): the last two labels of a host\n * name, so `*.evil.example` is one key however many subdomains it has; an IP\n * literal as is. Without the Public Suffix List, clients under a multi-label\n * suffix (`co.uk`, `pages.dev`) share one key, so many hosts there can slow\n * the others' uncached fetches; one host alone cannot (its own limit is lower).\n */\nexport function limitKey(hostname: string): string {\n\tconst h = hostname.replace(/^\\[|\\]$/g, \"\").replace(/\\.$/, \"\");\n\tif (isIpLiteral(h)) return h;\n\treturn h.split(\".\").slice(-2).join(\".\");\n}\n\n/** Per host and per domain as for new client ids; a higher total. */\nfunction knownBudget(): {\n\tperHost: RateLimiter;\n\tperDomain: RateLimiter;\n\tglobal: RateLimiter;\n} {\n\treturn {\n\t\tperHost: new RateLimiter({ capacity: 5, refillPerSecond: 0.2 }),\n\t\tperDomain: new RateLimiter({ capacity: 15, refillPerSecond: 0.6 }),\n\t\tglobal: new RateLimiter({ capacity: 100, refillPerSecond: 10 }),\n\t};\n}\n\n/** Fetches, validates and caches Client ID Metadata Documents. */\nexport class ClientMetadataFetcher {\n\t/** Kept past its lifetime for the stale grace; dead after it. */\n\treadonly #cache = new DomainCache<{ meta: ClientMetadata; until: number }>(\n\t\tMAX_CACHE_ENTRIES,\n\t\tMAX_CACHE_PER_DOMAIN,\n\t\t(entry, now) => entry.until + STALE_GRACE_MS <= now,\n\t);\n\t/** Failed fetches per client id, answered again for a minute (insertion order is expiry order). */\n\treadonly #failures = new Map<\n\t\tstring,\n\t\t{ error: ClientMetadataError; until: number }\n\t>();\n\treadonly #perHost = new RateLimiter({ capacity: 5, refillPerSecond: 0.2 });\n\t/** Three hosts' worth: one host alone cannot use up its domain (DEC-089). */\n\treadonly #perDomain = new RateLimiter({ capacity: 15, refillPerSecond: 0.6 });\n\t/** All fetches together: many attacker-owned hosts cannot add up (DEC-061). */\n\treadonly #global = new RateLimiter({ capacity: 30, refillPerSecond: 1 });\n\t/**\n\t * The same limits again, twice, for clients already known (review of PR\n\t * 65): refreshing a cached document, and fetching keys. Requests for\n\t * client ids nobody has seen, which anyone can send, cannot use these\n\t * up; key fetches (an unknown kid may look again every 5 s, so anyone\n\t * can make a real client's keys be fetched) cannot use up its document\n\t * refreshes. The totals are higher: the per-host and per-domain shares\n\t * are what keep one party from taking them.\n\t */\n\treadonly #refreshes = knownBudget();\n\treadonly #keys = knownBudget();\n\treadonly #allowedHosts: readonly string[];\n\treadonly #logger: Logger | undefined;\n\treadonly #timeoutMs: number;\n\treadonly #maxBytes: number;\n\treadonly #lookup: (hostname: string) => Promise<string[]>;\n\treadonly #fetch: typeof fetch;\n\treadonly #testMode: CimdTestMode | undefined;\n\treadonly #now: () => number;\n\n\tconstructor(options: ClientMetadataFetcherOptions = {}) {\n\t\tthis.#allowedHosts = options.allowedHosts ?? [];\n\t\tthis.#logger = options.logger;\n\t\tthis.#timeoutMs = options.timeoutMs ?? 5000;\n\t\tthis.#maxBytes = options.maxBytes ?? 65_536;\n\t\tthis.#lookup = options.lookup ?? defaultLookup;\n\t\tthis.#fetch = options.fetch ?? fetch;\n\t\tthis.#testMode = options.testMode;\n\t\tthis.#now = options.now ?? Date.now;\n\t}\n\n\t/**\n\t * The client's metadata, from the cache or freshly fetched. Throws\n\t * `ClientMetadataError`. With `allowStale` (the token and revocation\n\t * endpoints, DEC-106) a document up to an hour past its cache lifetime is\n\t * served when the fresh fetch fails transiently or is rate-limited, so a\n\t * busy or briefly unreachable host does not cut off apps that already\n\t * have grants. `/authorize` always wants a current document.\n\t */\n\tasync get(\n\t\tclientId: string,\n\t\toptions: { allowStale?: boolean } = {},\n\t): Promise<ClientMetadata> {\n\t\tconst url = this.#checkUrl(clientId);\n\t\tconst now = this.#now();\n\t\t// A hit moves the entry to the end: least recently used goes first.\n\t\tconst cached = this.#cache.get(clientId);\n\t\tif (cached !== undefined && cached.until > now) return cached.meta;\n\t\tconst known = cached !== undefined && now < cached.until + STALE_GRACE_MS;\n\t\tconst stale =\n\t\t\toptions.allowStale === true && known ? cached?.meta : undefined;\n\t\ttry {\n\t\t\tconst failed = this.#failures.get(clientId);\n\t\t\tif (failed !== undefined && failed.until > now) throw failed.error;\n\t\t\treturn await this.#refresh(clientId, url, now, known);\n\t\t} catch (error) {\n\t\t\tconst transient =\n\t\t\t\terror instanceof ClientMetadataError && isTransient(error);\n\t\t\t// A document the host now answers with something unacceptable is\n\t\t\t// not kept as a fallback either.\n\t\t\tif (!transient) this.#cache.delete(clientId);\n\t\t\tif (stale === undefined || !transient) throw error;\n\t\t\tthis.#logger?.info(\"oauth.cimd_stale\", {\n\t\t\t\thost: url.host,\n\t\t\t\treason: error.reason,\n\t\t\t});\n\t\t\treturn stale;\n\t\t}\n\t}\n\n\tasync #refresh(\n\t\tclientId: string,\n\t\turl: URL,\n\t\tnow: number,\n\t\tknown: boolean,\n\t): Promise<ClientMetadata> {\n\t\tthis.#admit(url, now, known ? \"refresh\" : \"new\");\n\t\tlet meta: ClientMetadata;\n\t\tlet ttlMs: number;\n\t\ttry {\n\t\t\tconst address = await this.#vettedAddress(url);\n\t\t\tconst response = await this.#fetchJson(url, address, [\n\t\t\t\t\"application/json\",\n\t\t\t]);\n\t\t\tmeta = validateDocument(response.doc, clientId, url, {\n\t\t\t\ttest: this.#testMode !== undefined,\n\t\t\t\tonFallback: (reason) =>\n\t\t\t\t\tthis.#logger?.info(\"oauth.cimd_auth_fallback\", {\n\t\t\t\t\t\thost: url.host,\n\t\t\t\t\t\treason,\n\t\t\t\t\t}),\n\t\t\t});\n\t\t\tttlMs = cacheMs(response.cacheControl);\n\t\t} catch (error) {\n\t\t\tif (error instanceof ClientMetadataError) {\n\t\t\t\tthis.#rememberFailure(clientId, error, now);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t\tthis.#failures.delete(clientId);\n\t\tthis.#cache.set(\n\t\t\tclientId,\n\t\t\tlimitKey(url.hostname),\n\t\t\t{ meta, until: now + ttlMs },\n\t\t\tnow,\n\t\t);\n\t\treturn meta;\n\t}\n\n\t/** At most 1000, oldest first; fetches are rate-limited, so few are live. */\n\t#rememberFailure(\n\t\tclientId: string,\n\t\terror: ClientMetadataError,\n\t\tnow: number,\n\t): void {\n\t\tthis.#failures.delete(clientId);\n\t\tif (this.#failures.size >= MAX_FAILURES) {\n\t\t\tconst oldest = this.#failures.keys().next().value;\n\t\t\tif (oldest !== undefined) this.#failures.delete(oldest);\n\t\t}\n\t\tthis.#failures.set(clientId, { error, until: now + FAILURE_CACHE_MS });\n\t}\n\n\t/**\n\t * A client's JWKS document, under the CIMD fetch's SSRF rules and rate\n\t * limits (DEC-106). `validateDocument` checked `jwksUri` (https, the\n\t * client id's host); this checks it again before any network use. Not\n\t * cached here: `JwksCache` does that. Only a document that was fetched\n\t * and accepted names a `jwksUri`, so this is charged to the budget of\n\t * known clients. Throws `ClientMetadataError`.\n\t */\n\tasync fetchJwks(jwksUri: string): Promise<unknown> {\n\t\tconst url = this.#checkJwksUri(jwksUri);\n\t\tthis.#admit(url, this.#now(), \"keys\");\n\t\tconst address = await this.#vettedAddress(url);\n\t\tconst response = await this.#fetchJson(url, address, [\n\t\t\t\"application/json\",\n\t\t\t\"application/jwk-set+json\",\n\t\t]);\n\t\treturn response.doc;\n\t}\n\n\t/**\n\t * Charge a fetch to its host, its domain and the global limit; per host\n\t * and per domain only once the global limit admits it, so refusals cost\n\t * the others nothing (DEC-089). `budget`: new client ids, refreshes of\n\t * known ones, or keys (review of PR 65).\n\t */\n\t#admit(url: URL, now: number, budget: \"new\" | \"refresh\" | \"keys\"): void {\n\t\tconst { perHost, perDomain, global } =\n\t\t\tbudget === \"refresh\"\n\t\t\t\t? this.#refreshes\n\t\t\t\t: budget === \"keys\"\n\t\t\t\t\t? this.#keys\n\t\t\t\t\t: {\n\t\t\t\t\t\t\tperHost: this.#perHost,\n\t\t\t\t\t\t\tperDomain: this.#perDomain,\n\t\t\t\t\t\t\tglobal: this.#global,\n\t\t\t\t\t\t};\n\t\tconst key = limitKey(url.hostname);\n\t\tif (!perHost.available(url.host, now) || !perDomain.available(key, now)) {\n\t\t\tthrow new ClientMetadataError(\n\t\t\t\t\"rate_limited\",\n\t\t\t\t\"Too many requests for this AI app's details. Try again in a minute.\",\n\t\t\t);\n\t\t}\n\t\tif (!global.take(\"all\", now)) {\n\t\t\tthrow new ClientMetadataError(\n\t\t\t\t\"rate_limited\",\n\t\t\t\t\"The relay is busy. Try again in a minute.\",\n\t\t\t);\n\t\t}\n\t\tperHost.take(url.host, now);\n\t\tperDomain.take(key, now);\n\t}\n\n\t#checkJwksUri(jwksUri: string): URL {\n\t\tconst url = parseJwksUri(jwksUri, this.#testMode !== undefined);\n\t\tif (url === undefined) {\n\t\t\tthrow new ClientMetadataError(\n\t\t\t\t\"invalid_jwks_uri\",\n\t\t\t\t\"The AI app's jwks_uri is not accepted.\",\n\t\t\t);\n\t\t}\n\t\tif (\n\t\t\tthis.#allowedHosts.length > 0 &&\n\t\t\t!this.#allowedHosts.includes(url.host)\n\t\t) {\n\t\t\tthrow new ClientMetadataError(\n\t\t\t\t\"host_not_allowed\",\n\t\t\t\t`This relay does not accept AI apps from ${url.host}.`,\n\t\t\t);\n\t\t}\n\t\tif (isIpLiteral(url.hostname) && !this.#addressAllowed(url.hostname)) {\n\t\t\tthrow this.#notLoaded(\"private_address\", url);\n\t\t}\n\t\treturn url;\n\t}\n\n\t/** The URL rules of the CIMD draft plus our SSRF rules, before any network use. */\n\t#checkUrl(clientId: string): URL {\n\t\tconst refuse = (message: string) =>\n\t\t\tnew ClientMetadataError(\"invalid_client_id\", message);\n\t\tif (clientId.length === 0 || clientId.length > MAX_CLIENT_ID_LENGTH) {\n\t\t\tthrow refuse(\"The AI app's client_id is missing or too long.\");\n\t\t}\n\t\tlet url: URL;\n\t\ttry {\n\t\t\turl = new URL(clientId);\n\t\t} catch {\n\t\t\tthrow refuse(\"The AI app's client_id is not a URL.\");\n\t\t}\n\t\tconst test = this.#testMode !== undefined;\n\t\tconst scheme =\n\t\t\turl.protocol === \"https:\" || (test && url.protocol === \"http:\");\n\t\tif (!scheme) {\n\t\t\tthrow refuse(\"The AI app's client_id must be an https:// URL.\");\n\t\t}\n\t\t// Canonical form only: this rejects dot segments, fragments' oddities,\n\t\t// an explicit default port, upper-case hosts and other aliases.\n\t\tif (url.href !== clientId) {\n\t\t\tthrow refuse(\"The AI app's client_id is not a canonical URL.\");\n\t\t}\n\t\tif (url.username !== \"\" || url.password !== \"\" || url.hash !== \"\") {\n\t\t\tthrow refuse(\n\t\t\t\t\"The AI app's client_id must not contain credentials or a fragment.\",\n\t\t\t);\n\t\t}\n\t\tif (url.pathname === \"/\" || url.pathname === \"\") {\n\t\t\tthrow refuse(\"The AI app's client_id must have a path.\");\n\t\t}\n\t\tif (url.port !== \"\" && !test) {\n\t\t\tthrow refuse(\"The AI app's client_id must use the default port.\");\n\t\t}\n\t\tif (\n\t\t\tthis.#allowedHosts.length > 0 &&\n\t\t\t!this.#allowedHosts.includes(url.host)\n\t\t) {\n\t\t\tthrow new ClientMetadataError(\n\t\t\t\t\"host_not_allowed\",\n\t\t\t\t`This relay does not accept AI apps from ${url.host}.`,\n\t\t\t);\n\t\t}\n\t\tif (isIpLiteral(url.hostname) && !this.#addressAllowed(url.hostname)) {\n\t\t\tthrow this.#notLoaded(\"private_address\", url);\n\t\t}\n\t\treturn url;\n\t}\n\n\t#addressAllowed(address: string): boolean {\n\t\tif (isPublicIp(address)) return true;\n\t\treturn this.#testMode !== undefined && isLoopbackHost(address);\n\t}\n\n\t/**\n\t * One fixed message for every failure from resolving to parsing, so the\n\t * page tells nothing about what answered (threat model A3, DEC-066). The\n\t * reason goes to the log.\n\t */\n\t#notLoaded(reason: string, url: URL, detail = reason): ClientMetadataError {\n\t\tthis.#logger?.info(\"oauth.cimd_failed\", { host: url.host, reason: detail });\n\t\treturn new ClientMetadataError(\n\t\t\treason,\n\t\t\t`The AI app's details could not be loaded from ${url.host}.`,\n\t\t);\n\t}\n\n\t/**\n\t * Resolve the host, refuse it if any address is not public, and return the\n\t * address to connect to (IPv4 first). The fetch is pinned to it, so a DNS\n\t * answer that changes between check and connect is never used.\n\t */\n\tasync #vettedAddress(url: URL): Promise<string> {\n\t\tconst hostname = url.hostname.replace(/^\\[|\\]$/g, \"\");\n\t\tif (isIpLiteral(hostname)) return hostname; // checked in #checkUrl\n\t\tlet addresses: string[];\n\t\ttry {\n\t\t\taddresses = await this.#lookup(hostname);\n\t\t} catch {\n\t\t\tthrow this.#notLoaded(\"unresolvable\", url);\n\t\t}\n\t\tif (\n\t\t\taddresses.length === 0 ||\n\t\t\t!addresses.every((a) => this.#addressAllowed(a))\n\t\t) {\n\t\t\tthrow this.#notLoaded(\"private_address\", url);\n\t\t}\n\t\treturn (\n\t\t\taddresses.find((a) => parseIpv4(a) !== undefined) ?? addresses[0] ?? \"\"\n\t\t);\n\t}\n\n\t/** GET a JSON document from the vetted address; one of `types` only. */\n\tasync #fetchJson(\n\t\turl: URL,\n\t\taddress: string,\n\t\ttypes: readonly string[],\n\t): Promise<{ doc: unknown; cacheControl: string | null }> {\n\t\tconst fail = (detail: string) =>\n\t\t\tthis.#notLoaded(\"fetch_failed\", url, detail);\n\t\t// Connect to the vetted address; Host and the TLS server name stay the\n\t\t// original host, so the certificate is checked against it.\n\t\tconst pinned = new URL(url.href);\n\t\tpinned.hostname = address.includes(\":\") ? `[${address}]` : address;\n\t\tconst hostname = url.hostname.replace(/^\\[|\\]$/g, \"\");\n\t\tconst tls =\n\t\t\turl.protocol === \"https:\"\n\t\t\t\t? {\n\t\t\t\t\t\ttls: {\n\t\t\t\t\t\t\t...(isIpLiteral(hostname) ? {} : { serverName: hostname }),\n\t\t\t\t\t\t\t...(this.#testMode?.ca === undefined\n\t\t\t\t\t\t\t\t? {}\n\t\t\t\t\t\t\t\t: { ca: this.#testMode.ca }),\n\t\t\t\t\t\t},\n\t\t\t\t\t}\n\t\t\t\t: {};\n\t\tlet response: Response;\n\t\ttry {\n\t\t\tresponse = await this.#fetch(pinned.href, {\n\t\t\t\tmethod: \"GET\",\n\t\t\t\tredirect: \"manual\",\n\t\t\t\theaders: { accept: \"application/json\", host: url.host },\n\t\t\t\tsignal: AbortSignal.timeout(this.#timeoutMs),\n\t\t\t\t...tls,\n\t\t\t} as RequestInit);\n\t\t} catch (error) {\n\t\t\tthrow fail(\n\t\t\t\t`fetch_failed:${error instanceof Error ? error.name : \"unknown\"}`,\n\t\t\t);\n\t\t}\n\t\ttry {\n\t\t\tif (response.status !== 200) throw fail(`status_${response.status}`);\n\t\t\tconst type = (response.headers.get(\"content-type\") ?? \"\")\n\t\t\t\t.split(\";\")[0]\n\t\t\t\t?.trim()\n\t\t\t\t.toLowerCase();\n\t\t\tif (type === undefined || !types.includes(type)) {\n\t\t\t\tthrow fail(\"not_json\");\n\t\t\t}\n\t\t\tconst text = await this.#readLimited(response, url);\n\t\t\tlet doc: unknown;\n\t\t\ttry {\n\t\t\t\tdoc = JSON.parse(text);\n\t\t\t} catch {\n\t\t\t\tthrow fail(\"bad_json\");\n\t\t\t}\n\t\t\treturn { doc, cacheControl: response.headers.get(\"cache-control\") };\n\t\t} finally {\n\t\t\tif (!response.bodyUsed) response.body?.cancel().catch(() => {});\n\t\t}\n\t}\n\n\tasync #readLimited(response: Response, url: URL): Promise<string> {\n\t\tconst tooLarge = () => this.#notLoaded(\"too_large\", url);\n\t\tconst length = Number(response.headers.get(\"content-length\") ?? \"0\");\n\t\tif (length > this.#maxBytes) throw tooLarge();\n\t\tconst reader = response.body?.getReader();\n\t\tif (reader === undefined) return \"\";\n\t\tconst chunks: Uint8Array[] = [];\n\t\tlet total = 0;\n\t\tfor (;;) {\n\t\t\tconst { done, value } = await reader.read();\n\t\t\tif (done) break;\n\t\t\ttotal += value.byteLength;\n\t\t\tif (total > this.#maxBytes) {\n\t\t\t\tawait reader.cancel().catch(() => {});\n\t\t\t\tthrow tooLarge();\n\t\t\t}\n\t\t\tchunks.push(value);\n\t\t}\n\t\ttry {\n\t\t\treturn new TextDecoder(\"utf-8\", { fatal: true }).decode(\n\t\t\t\tBuffer.concat(chunks),\n\t\t\t);\n\t\t} catch {\n\t\t\tthrow this.#notLoaded(\"fetch_failed\", url, \"bad_utf8\");\n\t\t}\n\t}\n}\n\n/** Labels separated by dots, ending in a label of 2-63 letters: `claude.ai`, `x.co.uk`. */\nconst DOMAIN_SHAPED =\n\t/(?:[\\p{L}\\p{N}](?:[\\p{L}\\p{N}-]*[\\p{L}\\p{N}])?\\.)+\\p{L}{2,63}(?![\\p{L}\\p{N}])/gu;\n\n/**\n * A name as the address checks read it: compatibility forms folded (NFKC),\n * combining marks removed, so \"claude.a̲i\" reads \"claude.ai\", the\n * ideographic full stops browsers read as dots made dots, lower case.\n */\nfunction comparable(name: string): string {\n\treturn name\n\t\t.normalize(\"NFKC\")\n\t\t.replace(/\\p{M}/gu, \"\")\n\t\t.replace(/[。.。]/g, \".\")\n\t\t.toLowerCase();\n}\n\n/** A host name in its ASCII (punycode) form, as `URL` gives it. */\nfunction asciiHost(token: string): string {\n\ttry {\n\t\treturn new URL(`https://${token}/`).hostname;\n\t} catch {\n\t\treturn token;\n\t}\n}\n\n/**\n * Whether a name contains a domain-shaped token that is not the verified\n * host, a subdomain of it or a parent of it (DEC-092). Internationalised\n * names are compared in their ASCII form, as the host is.\n */\nexport function foreignDomain(name: string, hostname: string): boolean {\n\tconst host = hostname.toLowerCase();\n\tfor (const [match] of comparable(name).matchAll(DOMAIN_SHAPED)) {\n\t\tconst token = asciiHost(match);\n\t\tconst own =\n\t\t\ttoken === host ||\n\t\t\thost.endsWith(`.${token}`) ||\n\t\t\ttoken.endsWith(`.${host}`);\n\t\tif (!own) return true;\n\t}\n\treturn false;\n}\n\n/** Check a fetched document against the CIMD rules and what the relay supports. */\nexport function validateDocument(\n\tdoc: unknown,\n\tclientId: string,\n\turl: URL,\n\toptions: DocumentOptions = {},\n): ClientMetadata {\n\tconst invalid = (message: string) =>\n\t\tnew ClientMetadataError(\"invalid_metadata\", message);\n\tif (typeof doc !== \"object\" || doc === null || Array.isArray(doc)) {\n\t\tthrow invalid(\"The AI app's details are not a JSON object.\");\n\t}\n\tconst d = doc as Record<string, unknown>;\n\tif (d[\"client_id\"] !== clientId) {\n\t\tthrow invalid(\"The AI app's details name a different client_id.\");\n\t}\n\tif (\n\t\td[\"client_secret\"] !== undefined ||\n\t\td[\"client_secret_expires_at\"] !== undefined\n\t) {\n\t\tthrow invalid(\"The AI app's details must not contain a client secret.\");\n\t}\n\tconst name = d[\"client_name\"];\n\tif (typeof name !== \"string\" || displayName(name) === \"\") {\n\t\tthrow invalid(\"The AI app's details have no client_name.\");\n\t}\n\t// \"Claude (claude.ai)\" from evil.example would read like a verified host.\n\tif (\n\t\t/\\(\\s*[^()\\s]*[a-z0-9-]\\.[a-z]{2,}[^()]*\\)/i.test(\n\t\t\tcomparable(displayName(name)),\n\t\t)\n\t) {\n\t\tthrow invalid(\n\t\t\t\"The AI app's name contains a web address in parentheses, which this relay does not show.\",\n\t\t);\n\t}\n\t// \"Claude · claude.ai\" from evil.example reads the same way (DEC-092).\n\tif (foreignDomain(displayName(name), url.hostname)) {\n\t\tthrow invalid(\n\t\t\t\"The AI app's name contains a web address other than its own, which this relay does not show.\",\n\t\t);\n\t}\n\tconst uris = d[\"redirect_uris\"];\n\tif (\n\t\t!Array.isArray(uris) ||\n\t\turis.length === 0 ||\n\t\turis.length > MAX_REDIRECT_URIS ||\n\t\t!uris.every((u) => typeof u === \"string\" && isRedirectUri(u))\n\t) {\n\t\tthrow invalid(\"The AI app's details have no valid redirect_uris.\");\n\t}\n\tconst auth = clientAuth(d, url, options, invalid);\n\tconst grants = d[\"grant_types\"];\n\tif (\n\t\tgrants !== undefined &&\n\t\t(!Array.isArray(grants) || !grants.includes(\"authorization_code\"))\n\t) {\n\t\tthrow invalid(\n\t\t\t\"The AI app's details do not allow the authorization code grant.\",\n\t\t);\n\t}\n\tconst responses = d[\"response_types\"];\n\tif (\n\t\tresponses !== undefined &&\n\t\t(!Array.isArray(responses) || !responses.includes(\"code\"))\n\t) {\n\t\tthrow invalid('The AI app\\'s details do not allow response type \"code\".');\n\t}\n\treturn {\n\t\tclientId,\n\t\tname: displayName(name),\n\t\thost: url.host,\n\t\tredirectUris: uris as string[],\n\t\tauth,\n\t};\n}\n\n/**\n * How the client authenticates at `/token` (DEC-104, DEC-106):\n *\n * - `none` or no method: a public client;\n * - `private_key_jwt` with a `jwks_uri` on the client id's own host; an\n * inline `jwks` is not accepted (one key path, rotated through the JWKS\n * cache); `token_endpoint_auth_signing_alg`, if given, must be one the\n * relay verifies and is then the only one accepted;\n * - any other method, or `private_key_jwt` whose keys the relay cannot use\n * (no `jwks_uri`, one elsewhere or not canonical, `jwks` beside it, an\n * algorithm it does not verify): accepted as a public client only when\n * `token_endpoint_auth_methods_supported` lists `none` (DEC-104), else\n * refused. `client_secret_*` never authenticates.\n */\nfunction clientAuth(\n\td: Record<string, unknown>,\n\turl: URL,\n\toptions: DocumentOptions,\n\tinvalid: (message: string) => ClientMetadataError,\n): ClientAuth {\n\tconst method = d[\"token_endpoint_auth_method\"];\n\tconst supported = d[\"token_endpoint_auth_methods_supported\"];\n\tconst listsNone = Array.isArray(supported) && supported.includes(\"none\");\n\tif (method === undefined || method === \"none\") return { method: \"none\" };\n\tconst checked: KeysOrProblem =\n\t\tmethod === \"private_key_jwt\"\n\t\t\t? privateKeyJwt(d, url, options.test === true)\n\t\t\t: {\n\t\t\t\t\tproblem: \"unsupported_method\",\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t'The AI app must use token_endpoint_auth_method \"none\" or \"private_key_jwt\" with this relay, or list \"none\" in token_endpoint_auth_methods_supported.',\n\t\t\t\t};\n\tif (\"jwksUri\" in checked) {\n\t\treturn { method: \"private_key_jwt\", ...checked, publicAllowed: listsNone };\n\t}\n\t// Any problem with the method falls back to \"none\" when it is listed,\n\t// as in relay 0.5.1 (DEC-104); the caller logs why.\n\tif (listsNone) {\n\t\toptions.onFallback?.(checked.problem);\n\t\treturn { method: \"none\" };\n\t}\n\tthrow invalid(checked.message);\n}\n\ntype KeysOrProblem =\n\t| { jwksUri: string; algorithms: readonly JwtAlgorithm[] }\n\t| { problem: string; message: string };\n\n/**\n * A `private_key_jwt` document's keys: a `jwks_uri` with the client id's\n * scheme and host (port included), in the form the relay fetches it, and\n * the algorithms. Or what is wrong with them.\n */\nfunction privateKeyJwt(\n\td: Record<string, unknown>,\n\turl: URL,\n\ttest: boolean,\n): KeysOrProblem {\n\tconst jwksUri = d[\"jwks_uri\"];\n\tif (jwksUri === undefined) {\n\t\treturn {\n\t\t\tproblem: \"no_jwks_uri\",\n\t\t\tmessage:\n\t\t\t\t\"The AI app uses private_key_jwt but publishes no jwks_uri (an inline jwks is not accepted).\",\n\t\t};\n\t}\n\tif (d[\"jwks\"] !== undefined) {\n\t\t// RFC 7591 §2: the two must not both be present.\n\t\treturn {\n\t\t\tproblem: \"jwks_and_jwks_uri\",\n\t\t\tmessage: \"The AI app's details have both jwks and jwks_uri.\",\n\t\t};\n\t}\n\t// The scheme and host of the client id, which passed the client id rules:\n\t// the keys are the verified host's own.\n\tconst parsed =\n\t\ttypeof jwksUri === \"string\" ? parseJwksUri(jwksUri, test) : undefined;\n\tif (\n\t\tparsed === undefined ||\n\t\tparsed.protocol !== url.protocol ||\n\t\tparsed.host !== url.host\n\t) {\n\t\treturn {\n\t\t\tproblem: \"bad_jwks_uri\",\n\t\t\tmessage:\n\t\t\t\t\"The AI app's jwks_uri must be an https:// URL on the host of its client_id.\",\n\t\t};\n\t}\n\tconst alg = d[\"token_endpoint_auth_signing_alg\"];\n\tif (alg !== undefined && !isAlgorithm(alg)) {\n\t\treturn {\n\t\t\tproblem: \"signing_alg\",\n\t\t\tmessage:\n\t\t\t\t\"The AI app's token_endpoint_auth_signing_alg is not one this relay accepts.\",\n\t\t};\n\t}\n\t// Only the one it names, or all the relay verifies.\n\treturn {\n\t\tjwksUri: parsed.href,\n\t\talgorithms: alg === undefined ? JWT_ALGORITHMS : [alg],\n\t};\n}\n\n/**\n * A `jwks_uri` as the relay accepts it: an absolute `https:` URL (`http:`\n * only in test mode) of at most 2048 characters, in canonical form, without\n * credentials or fragment, on the default port (any port in test mode).\n */\nfunction parseJwksUri(value: string, test: boolean): URL | undefined {\n\tif (value.length > MAX_CLIENT_ID_LENGTH) return undefined;\n\tlet url: URL;\n\ttry {\n\t\turl = new URL(value);\n\t} catch {\n\t\treturn undefined;\n\t}\n\tconst scheme =\n\t\turl.protocol === \"https:\" || (test && url.protocol === \"http:\");\n\tif (!scheme || url.href !== value) return undefined;\n\tif (url.username !== \"\" || url.password !== \"\" || url.hash !== \"\") {\n\t\treturn undefined;\n\t}\n\tif (url.port !== \"\" && !test) return undefined;\n\treturn url;\n}\n\n/** A private-use URI scheme in reverse-domain form (RFC 8252 §7.1), e.g. `com.example.app:`. */\nconst PRIVATE_USE_SCHEME = /^[a-z][a-z0-9+-]*(\\.[a-z0-9+-]+)+:$/;\n\n/**\n * A redirect URI the relay accepts (DEC-067): absolute, no fragment or\n * credentials, and `https:`; `http:` only for loopback hosts (RFC 8252 §7.3);\n * or a reverse-domain private-use scheme for native apps (RFC 8252 §7.1).\n * Everything else (`file:`, `blob:`, `data:`, `javascript:`, plain `http:`,\n * one-word custom schemes) is refused.\n */\nexport function isRedirectUri(value: string): boolean {\n\tif (value.length > 2048 || value.includes(\"#\")) return false;\n\tlet u: URL;\n\ttry {\n\t\tu = new URL(value);\n\t} catch {\n\t\treturn false;\n\t}\n\tif (u.username !== \"\" || u.password !== \"\") return false;\n\tif (u.protocol === \"https:\") return u.hostname !== \"\";\n\tif (u.protocol === \"http:\") return LOOPBACK_REDIRECT_HOSTS.has(u.hostname);\n\treturn PRIVATE_USE_SCHEME.test(u.protocol);\n}\n\nconst LOOPBACK_REDIRECT_HOSTS = new Set([\"localhost\", \"127.0.0.1\", \"[::1]\"]);\n\n/**\n * Whether a requested redirect URI matches a registered one: exact string\n * equality, except that for an `http:` loopback redirect the port is ignored\n * (RFC 8252 §7.3; OAuth 2.1 §8.4.2). Host, path and query must still match.\n */\nexport function redirectUriMatches(\n\trequested: string,\n\tregistered: string,\n): boolean {\n\tif (requested === registered) return true;\n\tlet a: URL;\n\tlet b: URL;\n\ttry {\n\t\ta = new URL(requested);\n\t\tb = new URL(registered);\n\t} catch {\n\t\treturn false;\n\t}\n\tif (a.protocol !== \"http:\" || b.protocol !== \"http:\") return false;\n\tif (!LOOPBACK_REDIRECT_HOSTS.has(b.hostname) || a.hostname !== b.hostname) {\n\t\treturn false;\n\t}\n\t// The requested URI must be in canonical form, so the parser cannot\n\t// have changed what the comparison sees (dot segments, case, …).\n\tif (\n\t\ta.href !== requested ||\n\t\ta.username !== \"\" ||\n\t\ta.password !== \"\" ||\n\t\ta.hash !== \"\"\n\t) {\n\t\treturn false;\n\t}\n\ta.port = \"\";\n\tb.port = \"\";\n\treturn a.href === b.href;\n}\n",
21
+ "/**\n * `private_key_jwt` client authentication at `/token` and `/revoke` (RFC 7521\n * §4.2, RFC 7523 §2.2 and §3, OpenID Connect Core §9; DEC-106). The client\n * signs a short JWT with a key it publishes at the `jwks_uri` of its Client\n * ID Metadata Document; the signature is checked with `jwt.ts` (asymmetric\n * algorithms only, each tied to its key type, keys never from the header).\n */\n\nimport { ipPrefixKey } from \"../ip.ts\";\nimport {\n\taudiences,\n\tdecodeJwt,\n\tJwksCache,\n\tJwtError,\n\ttype KeySource,\n\tnumericDate,\n\tverifyJwt,\n} from \"../jwt.ts\";\nimport { hashToken } from \"../tokens.ts\";\nimport { limitKey, type PrivateKeyJwtAuth } from \"./cimd.ts\";\nimport { DomainCache } from \"./domain-cache.ts\";\n\n/** The only `client_assertion_type` (RFC 7523 §2.2). */\nexport const CLIENT_ASSERTION_TYPE =\n\t\"urn:ietf:params:oauth:client-assertion-type:jwt-bearer\";\n/**\n * Latest `exp` accepted: an hour ahead, the ceiling other authorization\n * servers set (Okta) and a lifetime libraries default to; ChatGPT's own is\n * not known, so a shorter `exp` limit could refuse it.\n */\nexport const CLIENT_ASSERTION_MAX_LIFETIME_MS: number = 60 * 60_000;\n/**\n * How long an assertion is usable (DEC-106): five minutes from its `iat`,\n * or, without `iat`, `exp` at most five minutes ahead. A client signs just\n * before it sends, so this costs no client anything, and it bounds how long\n * a `jti` must be remembered whatever `exp` says.\n */\nexport const CLIENT_ASSERTION_MAX_AGE_MS: number = 5 * 60_000;\n/** Clock leeway for `exp`, `iat` and `nbf`. */\nexport const CLIENT_ASSERTION_LEEWAY_MS = 30_000;\nconst MAX_JTI = 256;\nconst MAX_KEY_SETS = 1000;\nconst MAX_KEY_SETS_PER_DOMAIN = 100;\nconst SWEEP_INTERVAL_MS = 1000;\n/**\n * `typ` values accepted, compared case-insensitively (RFC 7515 §4.1.9):\n * none, `JWT`, or the explicit type of draft-ietf-oauth-rfc7523bis. Others\n * (`at+jwt`, `tab-mcp-identity+jwt`, …) are other kinds of token.\n */\nconst ACCEPTED_TYPES = new Set([\"jwt\", \"client-authentication+jwt\"]);\n\n/** Records a one-time key until `expiresAt`; `false` if already recorded. */\nexport type ClaimNonce = (\n\tkey: string,\n\texpiresAt: number,\n\tnow: number,\n) => Promise<boolean>;\n\nexport interface AssertionCheck {\n\t/** The client id the assertion must be for (`iss` and `sub`). */\n\tclientId: string;\n\tauth: PrivateKeyJwtAuth;\n\tkeys: KeySource;\n\t/** What `aud` must contain one of: the issuer, the endpoint's URL. */\n\taudiences: readonly string[];\n\tclaimNonce: ClaimNonce;\n\tnow: number;\n}\n\n/**\n * The `sub` of an assertion, unverified: the client id when the request has\n * no `client_id` (RFC 7521 §4.2). Verification then checks it like any other.\n */\nexport function assertionSubject(assertion: string): string | undefined {\n\ttry {\n\t\tconst sub = decodeJwt(assertion).payload[\"sub\"];\n\t\treturn typeof sub === \"string\" && sub !== \"\" ? sub : undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\n/** The replay store's key: the client and its `jti`, hashed. */\nexport function nonceKey(clientId: string, jti: string): string {\n\treturn hashToken(JSON.stringify([\"client_assertion\", clientId, jti]));\n}\n\n/**\n * Check a client assertion; throws `JwtError` with a short reason for the\n * log. The `jti` is recorded only once everything else passed, so only the\n * holder of the client's key can add entries to the replay store.\n */\nexport async function verifyClientAssertion(\n\tassertion: string,\n\tcheck: AssertionCheck,\n): Promise<void> {\n\tconst { header, payload } = await verifyJwt(assertion, check.keys, {\n\t\talgorithms: check.auth.algorithms,\n\t});\n\tconst typ = header[\"typ\"];\n\tif (\n\t\ttyp !== undefined &&\n\t\t(typeof typ !== \"string\" || !ACCEPTED_TYPES.has(typ.toLowerCase()))\n\t) {\n\t\tthrow new JwtError(\"typ\", \"the assertion's type is not a client assertion\");\n\t}\n\tif (payload[\"iss\"] !== check.clientId) {\n\t\tthrow new JwtError(\"iss\", \"the assertion's issuer is not the client\");\n\t}\n\tif (payload[\"sub\"] !== check.clientId) {\n\t\tthrow new JwtError(\"sub\", \"the assertion's subject is not the client\");\n\t}\n\tif (!audiences(payload).some((a) => check.audiences.includes(a))) {\n\t\tthrow new JwtError(\"aud\", \"the assertion is not for this relay\");\n\t}\n\tconst usableUntil = checkTimes(payload, check.now);\n\tconst jti = payload[\"jti\"];\n\tif (typeof jti !== \"string\" || jti === \"\" || jti.length > MAX_JTI) {\n\t\tthrow new JwtError(\"jti\", \"the assertion needs a jti\");\n\t}\n\tconst key = nonceKey(check.clientId, jti);\n\tif (!(await check.claimNonce(key, usableUntil, check.now))) {\n\t\tthrow new JwtError(\"replayed\", \"the assertion was already used\");\n\t}\n}\n\n/**\n * `exp` required, live and at most an hour ahead; `iat`, if given, not in\n * the future and at most five minutes old, else `exp` at most five minutes\n * ahead; `nbf`, if given, passed. Returns when the assertion stops being\n * usable (leeway included): how long its `jti` must be remembered.\n */\nfunction checkTimes(payload: Record<string, unknown>, now: number): number {\n\tconst leeway = CLIENT_ASSERTION_LEEWAY_MS;\n\tconst exp = numericDate(payload, \"exp\");\n\tif (exp === undefined) throw new JwtError(\"exp\", \"the assertion has no exp\");\n\tif (exp <= now - leeway) {\n\t\tthrow new JwtError(\"expired\", \"the assertion has expired\");\n\t}\n\tif (exp > now + CLIENT_ASSERTION_MAX_LIFETIME_MS + leeway) {\n\t\tthrow new JwtError(\"lifetime\", \"the assertion lives longer than an hour\");\n\t}\n\tconst iat = numericDate(payload, \"iat\");\n\tlet end = exp;\n\tif (iat === undefined) {\n\t\tif (exp > now + CLIENT_ASSERTION_MAX_AGE_MS + leeway) {\n\t\t\tthrow new JwtError(\n\t\t\t\t\"lifetime\",\n\t\t\t\t\"an assertion without iat may live five minutes\",\n\t\t\t);\n\t\t}\n\t} else {\n\t\tif (iat > now + leeway) {\n\t\t\tthrow new JwtError(\"iat\", \"the assertion was issued in the future\");\n\t\t}\n\t\tif (iat + CLIENT_ASSERTION_MAX_AGE_MS <= now - leeway) {\n\t\t\tthrow new JwtError(\"too_old\", \"the assertion was issued too long ago\");\n\t\t}\n\t\tend = Math.min(exp, iat + CLIENT_ASSERTION_MAX_AGE_MS);\n\t}\n\tconst nbf = numericDate(payload, \"nbf\");\n\tif (nbf !== undefined && nbf > now + leeway) {\n\t\tthrow new JwtError(\"nbf\", \"the assertion is not valid yet\");\n\t}\n\treturn end + leeway;\n}\n\n/**\n * The key a client's replay share is counted under (review of PR 65): its\n * full host name, so clients under one shared suffix (`github.io`,\n * `pages.dev`, `co.uk`) do not share one; an IPv4 literal as is, an IPv6\n * literal by its /64, which one party usually holds whole.\n */\nexport function quotaKey(clientId: string): string {\n\treturn ipPrefixKey(new URL(clientId).hostname.replace(/\\.$/, \"\"));\n}\n\n/**\n * The replay store's live entries per AI app host on this instance\n * (DEC-106, keyed by `quotaKey`). Anyone can be a client by publishing a\n * document, so one host may fill only its own share, whatever the store;\n * the total bounds memory. Expired entries are dropped at most once a\n * second.\n */\nexport class NonceQuota {\n\treadonly #until = new Map<string, number[]>();\n\treadonly #perDomain: number;\n\treadonly #max: number;\n\t#total = 0;\n\t#sweptAt = Number.NEGATIVE_INFINITY;\n\n\tconstructor(perDomain: number, max: number) {\n\t\tthis.#perDomain = perDomain;\n\t\tthis.#max = max;\n\t}\n\n\t/** Why `domain` may not add an entry now, or `undefined` if it may. */\n\trefusal(domain: string, now: number): \"domain_full\" | \"full\" | undefined {\n\t\tthis.sweep(now);\n\t\tif ((this.#until.get(domain)?.length ?? 0) >= this.#perDomain) {\n\t\t\treturn \"domain_full\";\n\t\t}\n\t\treturn this.#total >= this.#max ? \"full\" : undefined;\n\t}\n\n\tadd(domain: string, until: number): void {\n\t\tconst list = this.#until.get(domain);\n\t\tif (list === undefined) this.#until.set(domain, [until]);\n\t\telse list.push(until);\n\t\tthis.#total++;\n\t}\n\n\t/** Drops expired entries; returns whether it ran (at most once a second). */\n\tsweep(now: number): boolean {\n\t\tif (now - this.#sweptAt < SWEEP_INTERVAL_MS) return false;\n\t\tthis.#sweptAt = now;\n\t\tthis.#total = 0;\n\t\tfor (const [domain, list] of this.#until) {\n\t\t\tconst live = list.filter((until) => until > now);\n\t\t\tif (live.length === 0) this.#until.delete(domain);\n\t\t\telse this.#until.set(domain, live);\n\t\t\tthis.#total += live.length;\n\t\t}\n\t\treturn true;\n\t}\n}\n\n/**\n * One `JwksCache` per client `jwks_uri` (10 minutes, an unknown `kid`\n * fetched again after 5 s, the last good keys for an hour after a failure),\n * loaded with the SSRF-guarded fetch. At most 1000, 100 per domain; a new\n * one pushes out the least recently used of the domain that holds the most,\n * so one party's many documents push out their own keys (review of PR 65).\n */\nexport class ClientKeySets {\n\treadonly #sets = new DomainCache<JwksCache>(\n\t\tMAX_KEY_SETS,\n\t\tMAX_KEY_SETS_PER_DOMAIN,\n\t);\n\treadonly #load: (url: string) => Promise<unknown>;\n\treadonly #now: () => number;\n\n\tconstructor(\n\t\tload: (url: string) => Promise<unknown>,\n\t\tnow: () => number = Date.now,\n\t) {\n\t\tthis.#load = load;\n\t\tthis.#now = now;\n\t}\n\n\tfor(jwksUri: string): KeySource {\n\t\tconst known = this.#sets.get(jwksUri);\n\t\tif (known !== undefined) return known;\n\t\tconst set = new JwksCache(jwksUri, { load: this.#load, now: this.#now });\n\t\tconst domain = limitKey(new URL(jwksUri).hostname);\n\t\tthis.#sets.set(jwksUri, domain, set, this.#now());\n\t\treturn set;\n\t}\n}\n",
22
+ "/**\n * Server-rendered HTML for `/authorize` (threat model A7): every value is\n * escaped, the page has no scripts, and its headers forbid framing, inline\n * code and foreign form targets.\n */\n\nconst ESCAPES: Record<string, string> = {\n\t\"&\": \"&amp;\",\n\t\"<\": \"&lt;\",\n\t\">\": \"&gt;\",\n\t'\"': \"&quot;\",\n\t\"'\": \"&#39;\",\n};\n\n/** Escape text for HTML element content and quoted attribute values. */\nexport function escapeHtml(text: string): string {\n\treturn text.replace(/[&<>\"']/g, (c) => ESCAPES[c] ?? c);\n}\n\n/** The stylesheet at `/authorize.css` (`style-src 'self'`). */\nexport const AUTHORIZE_CSS = `:root { color-scheme: light dark; --accent: #1d4ed8; --error: #b91c1c; }\n@media (prefers-color-scheme: dark) { :root { --accent: #93b4ff; --error: #fca5a5; } }\n* { box-sizing: border-box; }\nbody { margin: 0; font: 1rem/1.5 system-ui, -apple-system, \"Segoe UI\", sans-serif; background: Canvas; color: CanvasText; }\nmain { max-width: 34rem; margin: 3rem auto; padding: 0 1rem; }\nh1 { font-size: 1.5rem; line-height: 1.25; margin: 0 0 1rem; }\np { margin: 0 0 1rem; }\ncode, .origin { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; overflow-wrap: anywhere; }\n.help { color: GrayText; font-size: 0.9rem; }\n.error { color: var(--error); font-weight: 600; }\nlabel { display: block; font-weight: 600; margin-bottom: 0.25rem; }\ninput[type=\"text\"] { font: 1.5rem/1.2 ui-monospace, SFMono-Regular, Menlo, monospace; letter-spacing: 0.2em; width: 10ch; padding: 0.4rem 0.5rem; border: 2px solid GrayText; border-radius: 0.4rem; background: Field; color: FieldText; }\ninput[type=\"text\"]:focus-visible, button:focus-visible, a:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }\n.actions { display: flex; gap: 0.75rem; flex-wrap: wrap; margin: 1.5rem 0 1.25rem; }\nbutton, .button { font: inherit; padding: 0.5rem 1.25rem; border-radius: 0.4rem; border: 2px solid var(--accent); cursor: pointer; text-decoration: none; display: inline-block; }\nbutton.primary, .button.primary { background: var(--accent); color: Canvas; }\nbutton.secondary, .button:not(.primary) { background: transparent; color: var(--accent); }\nfooter { margin-top: 2.5rem; font-size: 0.85rem; color: GrayText; }\n`;\n\n/** Security headers of every `/authorize` response. */\nexport function pageHeaders(formTargets: readonly string[] = []): Headers {\n\t// Chrome applies form-action to the redirect after a POST, so the client's\n\t// redirect origin must be allowed explicitly.\n\tconst formAction = [\"'self'\", ...formTargets].join(\" \");\n\treturn new Headers({\n\t\t\"content-type\": \"text/html; charset=utf-8\",\n\t\t\"content-security-policy\": `default-src 'none'; style-src 'self'; img-src 'self'; base-uri 'none'; frame-ancestors 'none'; form-action ${formAction}`,\n\t\t\"x-frame-options\": \"DENY\",\n\t\t\"x-content-type-options\": \"nosniff\",\n\t\t// Not no-referrer: browsers then send `Origin: null` on the form post,\n\t\t// which the origin check must refuse. same-origin still sends nothing\n\t\t// to the client's redirect.\n\t\t\"referrer-policy\": \"same-origin\",\n\t\t\"cache-control\": \"no-store\",\n\t});\n}\n\n/** A complete page; `body` must already be escaped. */\nexport function renderPage(title: string, body: string): string {\n\treturn `<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<meta name=\"robots\" content=\"noindex\">\n<title>${escapeHtml(title)}</title>\n<link rel=\"stylesheet\" href=\"/authorize.css\">\n<link rel=\"icon\" href=\"/favicon.ico\" sizes=\"32x32\">\n<link rel=\"icon\" href=\"/favicon.svg\" type=\"image/svg+xml\">\n<link rel=\"apple-touch-icon\" href=\"/apple-touch-icon.png\">\n</head>\n<body>\n<main>\n${body}\n<footer>tab-mcp relay. It passes requests between AI apps and web pages and does not store their contents.</footer>\n</main>\n</body>\n</html>`;\n}\n",
23
+ "/**\n * The OIDC pairing method (DEC-075, DEC-079): at `/authorize` the user signs in\n * at the configured OpenID Connect provider, and the relay pairs the AI app if\n * that person is the page's user. The relay is an ordinary OIDC client there\n * (authorization code flow, PKCE S256, `state`, `nonce`).\n */\n\nimport type { OidcConfig } from \"../config.ts\";\nimport { CLOCK_LEEWAY_MS, identityKey, MISMATCH_TEXT } from \"../identity.ts\";\nimport {\n\taudiences,\n\tfetchJson,\n\tHttpJsonError,\n\tJwksCache,\n\tJwtError,\n\tnumericDate,\n\tverifyJwt,\n} from \"../jwt.ts\";\nimport type { Logger } from \"../log.ts\";\nimport { escapeHtml } from \"./html.ts\";\nimport type {\n\tPairingContext,\n\tPairingMethod,\n\tPairingResult,\n\tPairingTarget,\n} from \"./pairing.ts\";\n\n/** How long a sign-in at the provider may take (the pending request lives 10 minutes). */\nconst FLOW_TTL_MS = 10 * 60_000;\nconst MAX_FLOWS = 10_000;\nconst METADATA_TTL_MS = 60 * 60_000;\nconst MAX_TOKEN_RESPONSE_BYTES = 65_536;\n\n/** The provider's metadata the relay uses (OIDC Discovery §3). */\nexport interface ProviderMetadata {\n\tissuer: string;\n\tauthorizationEndpoint: string;\n\ttokenEndpoint: string;\n\tjwksUri: string;\n\t/** `client_secret_basic` (default) or `client_secret_post`. */\n\ttokenAuth: \"client_secret_basic\" | \"client_secret_post\";\n}\n\ninterface Flow {\n\trequestId: string;\n\tbrowserId: string;\n\tnonce: string;\n\tverifier: string;\n\texpiresAt: number;\n}\n\nexport class OidcError extends Error {\n\toverride name = \"OidcError\";\n\treadonly reason: string;\n\tconstructor(reason: string, message: string) {\n\t\tsuper(message);\n\t\tthis.reason = reason;\n\t}\n}\n\nfunction random(bytes: number): string {\n\treturn crypto\n\t\t.getRandomValues(new Uint8Array(bytes))\n\t\t.toBase64({ alphabet: \"base64url\", omitPadding: true });\n}\n\nfunction s256(verifier: string): string {\n\treturn new Bun.CryptoHasher(\"sha256\")\n\t\t.update(verifier)\n\t\t.digest()\n\t\t.toBase64({ alphabet: \"base64url\", omitPadding: true });\n}\n\nfunction str(v: unknown): string | undefined {\n\treturn typeof v === \"string\" && v !== \"\" ? v : undefined;\n}\n\nfunction originOf(url: string): string | undefined {\n\ttry {\n\t\treturn new URL(url).origin;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\n/** Discovery for the configured issuer, cached an hour (OIDC Discovery §4). */\nexport class ProviderDiscovery {\n\treadonly #issuer: string;\n\treadonly #development: boolean;\n\t#metadata: ProviderMetadata | undefined;\n\t#fetchedAt = 0;\n\t#inflight: Promise<ProviderMetadata> | undefined;\n\n\tconstructor(issuer: string, development: boolean) {\n\t\tthis.#issuer = issuer;\n\t\tthis.#development = development;\n\t}\n\n\t/** The cached metadata, if loaded. */\n\tget cached(): ProviderMetadata | undefined {\n\t\treturn this.#metadata;\n\t}\n\n\tasync get(): Promise<ProviderMetadata> {\n\t\tif (\n\t\t\tthis.#metadata !== undefined &&\n\t\t\tDate.now() - this.#fetchedAt < METADATA_TTL_MS\n\t\t) {\n\t\t\treturn this.#metadata;\n\t\t}\n\t\tthis.#inflight ??= this.#load().finally(() => {\n\t\t\tthis.#inflight = undefined;\n\t\t});\n\t\treturn this.#inflight;\n\t}\n\n\tasync #load(): Promise<ProviderMetadata> {\n\t\tconst url = `${this.#issuer.replace(/\\/+$/, \"\")}/.well-known/openid-configuration`;\n\t\tconst doc = (await fetchJson(url)) as Record<string, unknown> | null;\n\t\tif (typeof doc !== \"object\" || doc === null) {\n\t\t\tthrow new OidcError(\n\t\t\t\t\"discovery\",\n\t\t\t\t\"the provider's metadata is not an object\",\n\t\t\t);\n\t\t}\n\t\t// OIDC Discovery §4.3: the issuer must be exactly the configured one.\n\t\tif (doc[\"issuer\"] !== this.#issuer) {\n\t\t\tthrow new OidcError(\n\t\t\t\t\"discovery_issuer\",\n\t\t\t\t\"the provider's metadata names another issuer\",\n\t\t\t);\n\t\t}\n\t\tconst endpoint = (name: string): string => {\n\t\t\tconst v = str(doc[name]);\n\t\t\tconst ok =\n\t\t\t\tv !== undefined &&\n\t\t\t\t(v.startsWith(\"https://\") ||\n\t\t\t\t\t(this.#development && v.startsWith(\"http://\")));\n\t\t\tif (!ok) {\n\t\t\t\tthrow new OidcError(\n\t\t\t\t\t\"discovery\",\n\t\t\t\t\t`the provider's metadata lacks ${name}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn v;\n\t\t};\n\t\tconst methods = doc[\"token_endpoint_auth_methods_supported\"];\n\t\tconst tokenAuth =\n\t\t\tArray.isArray(methods) &&\n\t\t\t!methods.includes(\"client_secret_basic\") &&\n\t\t\tmethods.includes(\"client_secret_post\")\n\t\t\t\t? \"client_secret_post\"\n\t\t\t\t: \"client_secret_basic\";\n\t\tconst metadata: ProviderMetadata = {\n\t\t\tissuer: this.#issuer,\n\t\t\tauthorizationEndpoint: endpoint(\"authorization_endpoint\"),\n\t\t\ttokenEndpoint: endpoint(\"token_endpoint\"),\n\t\t\tjwksUri: endpoint(\"jwks_uri\"),\n\t\t\ttokenAuth,\n\t\t};\n\t\tthis.#metadata = metadata;\n\t\tthis.#fetchedAt = Date.now();\n\t\treturn metadata;\n\t}\n}\n\nexport interface OidcPairingOptions {\n\tconfig: OidcConfig;\n\t/** The relay's public URL (the callback is `<url>/authorize/callback`). */\n\tpublicUrl: () => string;\n\tlogger: Logger;\n\tdevelopment: boolean;\n\t/** At most this many open sign-ins (tests lower it). */\n\tmaxFlows?: number;\n}\n\n/** The identity-binding pairing method. */\nexport class OidcPairing implements PairingMethod {\n\treadonly kind = \"oidc\" as const;\n\treadonly #config: OidcConfig;\n\treadonly #maxFlows: number;\n\treadonly #publicUrl: () => string;\n\treadonly #logger: Logger;\n\treadonly #discovery: ProviderDiscovery;\n\treadonly #jwks: JwksCache;\n\t/** state → flow; insertion order is expiry order. */\n\treadonly #flows = new Map<string, Flow>();\n\t/**\n\t * Pending request → its one flow's state (DEC-090): a new Connect\n\t * replaces the earlier sign-in, so flows are bounded by the pending\n\t * requests, which are capped per network and per address.\n\t */\n\treadonly #flowOf = new Map<string, string>();\n\t/** Requests that met a mismatch: the next sign-in asks for the account again. */\n\treadonly #mismatched = new Set<string>();\n\n\tconstructor(options: OidcPairingOptions) {\n\t\tthis.#config = options.config;\n\t\tthis.#maxFlows = options.maxFlows ?? MAX_FLOWS;\n\t\tthis.#publicUrl = options.publicUrl;\n\t\tthis.#logger = options.logger;\n\t\tthis.#discovery = new ProviderDiscovery(\n\t\t\toptions.config.issuer,\n\t\t\toptions.development,\n\t\t);\n\t\tthis.#jwks = new JwksCache(\n\t\t\tasync () => (await this.#discovery.get()).jwksUri,\n\t\t);\n\t}\n\n\tget #providerHost(): string {\n\t\ttry {\n\t\t\treturn new URL(this.#config.issuer).host;\n\t\t} catch {\n\t\t\treturn this.#config.issuer;\n\t\t}\n\t}\n\n\tget callbackUrl(): string {\n\t\treturn `${this.#publicUrl()}/authorize/callback`;\n\t}\n\n\t/** Load the provider's metadata before the page is shown. */\n\tasync prepare(): Promise<void> {\n\t\tawait this.#discovery.get();\n\t}\n\n\t/** The provider's origins, for the page's CSP `form-action` (the redirect after the post). */\n\tformTargets(): string[] {\n\t\tconst out = new Set<string>();\n\t\tconst issuer = originOf(this.#config.issuer);\n\t\tif (issuer !== undefined) out.add(issuer);\n\t\tconst auth = this.#discovery.cached?.authorizationEndpoint;\n\t\tconst origin = auth === undefined ? undefined : originOf(auth);\n\t\tif (origin !== undefined) out.add(origin);\n\t\treturn [...out];\n\t}\n\n\trender(target: PairingTarget): string {\n\t\tconst host = escapeHtml(this.#providerHost);\n\t\tif (target.identity === undefined) {\n\t\t\treturn `<p class=\"help\">Nobody is signed in to the page. Sign in there first, then click Connect.</p>`;\n\t\t}\n\t\treturn `<p>When you click Connect, sign in at <strong>${host}</strong> with the account you use in the page. No code is needed.</p>`;\n\t}\n\n\tasync check(\n\t\ttarget: PairingTarget,\n\t\t_form: URLSearchParams,\n\t\tadmit: () => boolean,\n\t\tcontext?: PairingContext,\n\t): Promise<PairingResult> {\n\t\tif (!target.pairingOpen) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\tmessage:\n\t\t\t\t\t\"The page is not connecting right now. In the page, open “Connect your AI app”, then click Connect here again.\",\n\t\t\t\treason: \"inactive\",\n\t\t\t};\n\t\t}\n\t\tif (target.identity === undefined) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\tmessage:\n\t\t\t\t\t\"Nobody is signed in to the page. Sign in there first, then click Connect here again.\",\n\t\t\t\treason: \"signed_out\",\n\t\t\t};\n\t\t}\n\t\tif (context === undefined) {\n\t\t\treturn { ok: false, message: \"Something went wrong.\", reason: \"context\" };\n\t\t}\n\t\tlet metadata: ProviderMetadata;\n\t\ttry {\n\t\t\tmetadata = await this.#discovery.get();\n\t\t} catch (error) {\n\t\t\treturn this.#unavailable(error);\n\t\t}\n\t\tthis.#sweep();\n\t\tthis.#dropFlow(context.requestId);\n\t\tif (this.#flows.size >= this.#maxFlows) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\tmessage:\n\t\t\t\t\t\"Too many sign-ins are open right now. Wait a few minutes and try again.\",\n\t\t\t\treason: \"busy\",\n\t\t\t};\n\t\t}\n\t\t// A sign-in could pair: charged like a code guess (DEC-061, DEC-090),\n\t\t// and only once one can start.\n\t\tif (!admit()) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\tmessage:\n\t\t\t\t\t\"Too many attempts for this page. Wait a few minutes and try again.\",\n\t\t\t\treason: \"rate_limited\",\n\t\t\t};\n\t\t}\n\t\t// The provider sends the state back to /authorize/callback, whose URL is\n\t\t// fixed: the address id in front lets a load balancer route the callback\n\t\t// to this instance, which holds the flow (DEC-098).\n\t\tconst state = `${context.addressId}.${random(24)}`;\n\t\tconst nonce = random(24);\n\t\tconst verifier = random(32);\n\t\tthis.#flowOf.set(context.requestId, state);\n\t\tthis.#flows.set(state, {\n\t\t\trequestId: context.requestId,\n\t\t\tbrowserId: context.browserId,\n\t\t\tnonce,\n\t\t\tverifier,\n\t\t\texpiresAt: Date.now() + FLOW_TTL_MS,\n\t\t});\n\t\tconst url = new URL(metadata.authorizationEndpoint);\n\t\tconst params: Record<string, string> = {\n\t\t\tresponse_type: \"code\",\n\t\t\tclient_id: this.#config.clientId,\n\t\t\tredirect_uri: this.callbackUrl,\n\t\t\tscope: \"openid\",\n\t\t\tstate,\n\t\t\tnonce,\n\t\t\tcode_challenge: s256(verifier),\n\t\t\tcode_challenge_method: \"S256\",\n\t\t};\n\t\tif (this.#mismatched.has(context.requestId)) params[\"prompt\"] = \"login\";\n\t\tfor (const [k, v] of Object.entries(params)) url.searchParams.set(k, v);\n\t\treturn { ok: \"redirect\", location: url.href };\n\t}\n\n\t/**\n\t * The provider sent the browser back. Returns the pending request the\n\t * sign-in belongs to (if the state is known) and the result.\n\t */\n\tasync callback(\n\t\tparams: URLSearchParams,\n\t\tbrowserId: string,\n\t\ttargetFor: (requestId: string) => PairingTarget | undefined,\n\t): Promise<{ requestId?: string; result: PairingResult }> {\n\t\tthis.#sweep();\n\t\tconst state = params.get(\"state\") ?? \"\";\n\t\tconst flow = this.#flows.get(state);\n\t\tif (flow === undefined || flow.expiresAt <= Date.now()) {\n\t\t\treturn {\n\t\t\t\tresult: {\n\t\t\t\t\tok: false,\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t\"This sign-in has expired or was already used. Start again from your AI app.\",\n\t\t\t\t\treason: \"unknown_state\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tthis.#flows.delete(state); // single use\n\t\tconst requestId = flow.requestId;\n\t\tthis.#flowOf.delete(requestId);\n\t\tconst fail = (reason: string, message: string) => ({\n\t\t\trequestId,\n\t\t\tresult: { ok: false as const, message, reason },\n\t\t});\n\t\tif (flow.browserId !== browserId) {\n\t\t\treturn fail(\n\t\t\t\t\"browser\",\n\t\t\t\t\"This sign-in was started in another browser. Start again from your AI app.\",\n\t\t\t);\n\t\t}\n\t\t// RFC 9207: a provider that names itself must be the configured one.\n\t\tconst iss = params.get(\"iss\");\n\t\tif (iss !== null && iss !== this.#config.issuer) {\n\t\t\treturn fail(\"iss\", \"The sign-in came back from an unexpected provider.\");\n\t\t}\n\t\tconst error = params.get(\"error\");\n\t\tif (error !== null) {\n\t\t\tthis.#logger.info(\"oidc.provider_error\", { error: error.slice(0, 64) });\n\t\t\treturn fail(\n\t\t\t\t`provider_${error.slice(0, 32)}`,\n\t\t\t\t`The sign-in at ${this.#providerHost} did not finish. Click Connect to try again.`,\n\t\t\t);\n\t\t}\n\t\tconst code = params.get(\"code\");\n\t\tif (code === null || code === \"\") {\n\t\t\treturn fail(\n\t\t\t\t\"no_code\",\n\t\t\t\t\"The provider sent no sign-in result. Click Connect to try again.\",\n\t\t\t);\n\t\t}\n\t\tlet subject: string;\n\t\ttry {\n\t\t\tsubject = await this.#signIn(code, flow);\n\t\t} catch (e) {\n\t\t\tconst reason =\n\t\t\t\te instanceof OidcError || e instanceof JwtError ? e.reason : \"exchange\";\n\t\t\tthis.#logger.warn(\"oidc.sign_in_failed\", {\n\t\t\t\treason,\n\t\t\t\tmessage: e instanceof Error ? e.message.slice(0, 200) : \"unknown\",\n\t\t\t});\n\t\t\treturn fail(\n\t\t\t\treason,\n\t\t\t\t`The sign-in at ${this.#providerHost} could not be checked. Click Connect to try again.`,\n\t\t\t);\n\t\t}\n\t\t// Compared now, so a page that signed out meanwhile pairs nobody.\n\t\tconst target = targetFor(requestId);\n\t\tif (target === undefined) {\n\t\t\treturn fail(\n\t\t\t\t\"page_closed\",\n\t\t\t\t\"The page is not open any more. Open it and try again.\",\n\t\t\t);\n\t\t}\n\t\tif (target.identity === undefined) {\n\t\t\treturn fail(\n\t\t\t\t\"signed_out\",\n\t\t\t\t\"Nobody is signed in to the page any more. Sign in there first, then click Connect here again.\",\n\t\t\t);\n\t\t}\n\t\tconst key = identityKey(this.#config.issuer, subject);\n\t\tif (key !== target.identity) {\n\t\t\tthis.#mismatched.add(requestId);\n\t\t\tif (this.#mismatched.size > MAX_FLOWS) {\n\t\t\t\tconst oldest = this.#mismatched.values().next().value;\n\t\t\t\tif (oldest !== undefined) this.#mismatched.delete(oldest);\n\t\t\t}\n\t\t\treturn fail(\n\t\t\t\t\"mismatch\",\n\t\t\t\t`${MISMATCH_TEXT} Click Connect to sign in with the page's account, or Cancel.`,\n\t\t\t);\n\t\t}\n\t\tthis.#mismatched.delete(requestId);\n\t\treturn { requestId, result: { ok: true, subject: key } };\n\t}\n\n\t/** Exchange the code and validate the ID token (OIDC Core §3.1.3.7); returns the subject. */\n\tasync #signIn(code: string, flow: Flow): Promise<string> {\n\t\tconst metadata = await this.#discovery.get();\n\t\tconst body = new URLSearchParams({\n\t\t\tgrant_type: \"authorization_code\",\n\t\t\tcode,\n\t\t\tredirect_uri: this.callbackUrl,\n\t\t\tcode_verifier: flow.verifier,\n\t\t});\n\t\tconst headers: Record<string, string> = {\n\t\t\t\"content-type\": \"application/x-www-form-urlencoded\",\n\t\t};\n\t\tconst secret = this.#config.clientSecret;\n\t\tif (secret !== undefined && metadata.tokenAuth === \"client_secret_basic\") {\n\t\t\t// RFC 6749 §2.3.1: both parts form-encoded before Base64.\n\t\t\tconst enc = (v: string) => encodeURIComponent(v).replace(/%20/g, \"+\");\n\t\t\theaders[\"authorization\"] =\n\t\t\t\t`Basic ${btoa(`${enc(this.#config.clientId)}:${enc(secret)}`)}`;\n\t\t} else {\n\t\t\tbody.set(\"client_id\", this.#config.clientId);\n\t\t\tif (secret !== undefined) body.set(\"client_secret\", secret);\n\t\t}\n\t\tlet response: unknown;\n\t\ttry {\n\t\t\tresponse = await fetchJson(\n\t\t\t\tmetadata.tokenEndpoint,\n\t\t\t\t{ method: \"POST\", headers, body },\n\t\t\t\tMAX_TOKEN_RESPONSE_BYTES,\n\t\t\t);\n\t\t} catch (e) {\n\t\t\tif (e instanceof HttpJsonError) {\n\t\t\t\tthrow new OidcError(`token_${e.error || e.status}`, e.message);\n\t\t\t}\n\t\t\tthrow e;\n\t\t}\n\t\tconst idToken = str(\n\t\t\t(response as Record<string, unknown> | null)?.[\"id_token\"],\n\t\t);\n\t\tif (idToken === undefined) {\n\t\t\tthrow new OidcError(\"no_id_token\", \"the token response has no id_token\");\n\t\t}\n\t\tconst { payload } = await verifyJwt(idToken, this.#jwks);\n\t\tconst now = Date.now();\n\t\tif (payload[\"iss\"] !== metadata.issuer) {\n\t\t\tthrow new OidcError(\"id_token_iss\", \"the ID token's issuer is wrong\");\n\t\t}\n\t\tconst aud = audiences(payload);\n\t\tif (!aud.includes(this.#config.clientId)) {\n\t\t\tthrow new OidcError(\"id_token_aud\", \"the ID token is not for this relay\");\n\t\t}\n\t\tconst azp = payload[\"azp\"];\n\t\tif (\n\t\t\t(aud.length > 1 || azp !== undefined) &&\n\t\t\tazp !== this.#config.clientId\n\t\t) {\n\t\t\tthrow new OidcError(\n\t\t\t\t\"id_token_azp\",\n\t\t\t\t\"the ID token's azp is not this relay\",\n\t\t\t);\n\t\t}\n\t\tconst exp = numericDate(payload, \"exp\");\n\t\tif (exp === undefined || exp <= now - CLOCK_LEEWAY_MS) {\n\t\t\tthrow new OidcError(\"id_token_exp\", \"the ID token has expired\");\n\t\t}\n\t\tconst iat = numericDate(payload, \"iat\");\n\t\tif (iat === undefined || iat > now + CLOCK_LEEWAY_MS) {\n\t\t\tthrow new OidcError(\n\t\t\t\t\"id_token_iat\",\n\t\t\t\t\"the ID token's iat is missing or in the future\",\n\t\t\t);\n\t\t}\n\t\tif (payload[\"nonce\"] !== flow.nonce) {\n\t\t\tthrow new OidcError(\n\t\t\t\t\"id_token_nonce\",\n\t\t\t\t\"the ID token's nonce does not match\",\n\t\t\t);\n\t\t}\n\t\tconst claim = this.#config.subjectClaim ?? \"sub\";\n\t\tconst subject = payload[claim];\n\t\tif (typeof subject !== \"string\" || subject === \"\" || subject.length > 255) {\n\t\t\tthrow new OidcError(\"id_token_subject\", `the ID token has no ${claim}`);\n\t\t}\n\t\treturn subject;\n\t}\n\n\t#unavailable(error: unknown): PairingResult {\n\t\tthis.#logger.warn(\"oidc.discovery_failed\", {\n\t\t\treason: error instanceof OidcError ? error.reason : \"fetch\",\n\t\t\tmessage: error instanceof Error ? error.message.slice(0, 200) : \"unknown\",\n\t\t});\n\t\treturn {\n\t\t\tok: false,\n\t\t\tmessage: `The sign-in provider ${this.#providerHost} cannot be reached right now. Try again in a minute.`,\n\t\t\treason: \"provider_unavailable\",\n\t\t};\n\t}\n\n\t/** The pending request ended: drop its sign-in and its mismatch mark. */\n\tforget(requestId: string): void {\n\t\tthis.#dropFlow(requestId);\n\t\tthis.#mismatched.delete(requestId);\n\t}\n\n\t#dropFlow(requestId: string): void {\n\t\tconst state = this.#flowOf.get(requestId);\n\t\tif (state !== undefined) this.#flows.delete(state);\n\t\tthis.#flowOf.delete(requestId);\n\t}\n\n\t#sweep(): void {\n\t\tconst now = Date.now();\n\t\tfor (const [state, flow] of this.#flows) {\n\t\t\tif (flow.expiresAt > now) break;\n\t\t\tthis.#flows.delete(state);\n\t\t\tif (this.#flowOf.get(flow.requestId) === state) {\n\t\t\t\tthis.#flowOf.delete(flow.requestId);\n\t\t\t}\n\t\t}\n\t}\n}\n",
24
+ "/**\n * Pairing (DEC-028, DEC-042): the step at `/authorize` that proves the browser\n * belongs to the tab's user. The authorization server only knows the\n * `PairingMethod` interface; which method a session uses comes from its site\n * (DEC-045): the 6-digit code shown in the tab, or identity binding at an\n * OpenID Connect provider (`oidc.ts`, DEC-079).\n */\n\nimport { timingSafeEqual } from \"node:crypto\";\nimport type { PairingKind } from \"../config.ts\";\n\n/** What a pairing method sees of a tab session. */\nexport interface PairingTarget {\n\t/** The code state of the session (used by the code method). */\n\treadonly codes: PairingCodes;\n\t/** The binding key of the page's user, if signed in (DEC-077). */\n\treadonly identity: string | undefined;\n\t/** Whether the page's \"Connect your AI app\" dialog is open. */\n\treadonly pairingOpen: boolean;\n}\n\n/** The pending request a check belongs to (for methods that leave the page). */\nexport interface PairingContext {\n\trequestId: string;\n\tbrowserId: string;\n\t/** The address being paired: the routing key of the return trip (DEC-098). */\n\taddressId: string;\n}\n\nexport type PairingResult =\n\t/** Paired; `subject` is recorded on the grant (identity binding, DEC-077). */\n\t| { ok: true; subject?: string }\n\t/** Continue elsewhere (the provider's sign-in); the answer comes to `callback`. */\n\t| { ok: \"redirect\"; location: string }\n\t| {\n\t\t\tok: false;\n\t\t\t/** Plain English for the page (plain text; the page escapes it). */\n\t\t\tmessage: string;\n\t\t\t/** A short reason for the log. */\n\t\t\treason: string;\n\t };\n\n/**\n * A way to pair a browser at `/authorize` with an address. The authorization\n * server renders `render()` inside its form (CSRF token, buttons and the\n * request id are its own) and passes the submitted form to `check()`.\n */\nexport interface PairingMethod {\n\treadonly kind: PairingKind;\n\t/** HTML of the method's step (escaped by the method). */\n\trender(target: PairingTarget): string;\n\t/**\n\t * Check the submitted form fields. Call `admit()` right before judging an\n\t * answer that could be right (a real guess); if it returns false, refuse\n\t * with reason `rate_limited` without judging. Malformed input and a state\n\t * that cannot succeed are answered without calling it (DEC-061).\n\t */\n\tcheck(\n\t\ttarget: PairingTarget,\n\t\tform: URLSearchParams,\n\t\tadmit: () => boolean,\n\t\tcontext?: PairingContext,\n\t): PairingResult | Promise<PairingResult>;\n\t/** Work before the page is shown (e.g. provider discovery); throws if unavailable. */\n\tprepare?(): Promise<void>;\n\t/** Extra origins the page's form may lead to (CSP `form-action`). */\n\tformTargets?(): readonly string[];\n\t/** The pending request ended (paired, cancelled, expired, evicted): drop its state. */\n\tforget?(requestId: string): void;\n\t/** The answer of a method that redirected (`/authorize/callback`). */\n\tcallback?(\n\t\tparams: URLSearchParams,\n\t\tbrowserId: string,\n\t\ttargetFor: (requestId: string) => PairingTarget | undefined,\n\t): Promise<{ requestId?: string; result: PairingResult }>;\n}\n\n/** Code lifetime and attempts (DEC-028). */\nexport const PAIRING_CODE_TTL_MS = 120_000;\nexport const PAIRING_MAX_ATTEMPTS = 5;\n\nexport type CodeCheck =\n\t| \"ok\"\n\t| \"limited\"\n\t| \"wrong\"\n\t| \"expired\"\n\t| \"locked\"\n\t| \"inactive\"\n\t| \"malformed\";\n\n/** Six uniformly distributed digits. */\nexport function newPairingCode(): string {\n\t// 4 294 000 000 is the largest multiple of 10^6 below 2^32: rejecting the\n\t// values above it keeps every code equally likely.\n\tconst buffer = new Uint32Array(1);\n\tfor (;;) {\n\t\tcrypto.getRandomValues(buffer);\n\t\tconst n = buffer[0] ?? 0;\n\t\tif (n < 4_294_000_000) return String(n % 1_000_000).padStart(6, \"0\");\n\t}\n}\n\nfunction sameCode(a: string, b: string): boolean {\n\tconst x = Buffer.from(a);\n\tconst y = Buffer.from(b);\n\treturn x.length === y.length && timingSafeEqual(x, y);\n}\n\n/**\n * The pairing codes of one tab session: on while the tab's dialog is open\n * (`pairing.start` … `pairing.stop`); a fresh code after expiry, after use and\n * after too many wrong attempts. Codes are never logged.\n */\nexport class PairingCodes {\n\treadonly #send: (code: string, expiresAt: number) => void;\n\treadonly #ttlMs: number;\n\treadonly #maxAttempts: number;\n\t#current: { code: string; expiresAt: number; attempts: number } | undefined;\n\t#previous: string | undefined;\n\t#timer: Timer | undefined;\n\n\tconstructor(\n\t\tsend: (code: string, expiresAt: number) => void,\n\t\toptions: { ttlMs?: number; maxAttempts?: number } = {},\n\t) {\n\t\tthis.#send = send;\n\t\tthis.#ttlMs = options.ttlMs ?? PAIRING_CODE_TTL_MS;\n\t\tthis.#maxAttempts = options.maxAttempts ?? PAIRING_MAX_ATTEMPTS;\n\t}\n\n\t/** Whether the tab is showing codes. */\n\tget active(): boolean {\n\t\treturn this.#current !== undefined;\n\t}\n\n\t/** Attempts left for the current code. */\n\tget attemptsLeft(): number {\n\t\treturn this.#current === undefined\n\t\t\t? 0\n\t\t\t: this.#maxAttempts - this.#current.attempts;\n\t}\n\n\tstart(): void {\n\t\tif (this.#current !== undefined) {\n\t\t\t// Already on (e.g. a second dialog): show the same code again.\n\t\t\tthis.#send(this.#current.code, this.#current.expiresAt);\n\t\t\treturn;\n\t\t}\n\t\tthis.#issue();\n\t}\n\n\tstop(): void {\n\t\tclearTimeout(this.#timer);\n\t\tthis.#timer = undefined;\n\t\tthis.#current = undefined;\n\t\tthis.#previous = undefined;\n\t}\n\n\t/**\n\t * Check a typed code; a correct one is used up and replaced. `admit` is\n\t * asked only for a well-formed code while a code is active.\n\t */\n\tcheck(\n\t\tinput: string,\n\t\tnow: number = Date.now(),\n\t\tadmit: () => boolean = () => true,\n\t): CodeCheck {\n\t\tconst current = this.#current;\n\t\tif (current === undefined) return \"inactive\";\n\t\tconst typed = input.replace(/[\\s-]/g, \"\");\n\t\tif (!/^\\d{6}$/.test(typed)) return \"malformed\";\n\t\tif (!admit()) return \"limited\";\n\t\tif (now >= current.expiresAt) {\n\t\t\tthis.#previous = current.code;\n\t\t\tthis.#issue();\n\t\t\treturn \"expired\";\n\t\t}\n\t\tif (sameCode(typed, current.code)) {\n\t\t\tthis.#previous = undefined;\n\t\t\tthis.#issue();\n\t\t\treturn \"ok\";\n\t\t}\n\t\tcurrent.attempts++;\n\t\tconst expired =\n\t\t\tthis.#previous !== undefined && sameCode(typed, this.#previous);\n\t\tif (current.attempts >= this.#maxAttempts) {\n\t\t\tthis.#previous = undefined;\n\t\t\tthis.#issue();\n\t\t\treturn \"locked\";\n\t\t}\n\t\treturn expired ? \"expired\" : \"wrong\";\n\t}\n\n\t#issue(): void {\n\t\tclearTimeout(this.#timer);\n\t\tconst expiresAt = Date.now() + this.#ttlMs;\n\t\tthis.#current = { code: newPairingCode(), expiresAt, attempts: 0 };\n\t\tthis.#timer = setTimeout(() => {\n\t\t\tif (this.#current === undefined) return;\n\t\t\tthis.#previous = this.#current.code;\n\t\t\tthis.#issue();\n\t\t}, this.#ttlMs);\n\t\tthis.#send(this.#current.code, expiresAt);\n\t}\n}\n\nconst MESSAGES: Record<Exclude<CodeCheck, \"ok\">, string> = {\n\twrong:\n\t\t\"That code is not right. Check the code shown in the page and try again.\",\n\texpired:\n\t\t\"That code has expired. The page now shows a new code: enter that one.\",\n\tlocked:\n\t\t\"Too many wrong codes. The page now shows a new code: enter that one.\",\n\tinactive:\n\t\t\"The page is not showing a code right now. In the page, open “Connect your AI app” to see one, then enter it here.\",\n\tmalformed: \"Enter the 6 digits shown in the page.\",\n\tlimited:\n\t\t\"Too many attempts for this page. Wait a few minutes and try again. If it keeps happening, someone else may be guessing: use “New address” in the page.\",\n};\n\n/** The 6-digit code typed at `/authorize` (DEC-028). */\nexport const codePairing: PairingMethod = {\n\tkind: \"code\",\n\trender(): string {\n\t\treturn `<p><label for=\"code\">Code shown in the page</label>\n<input id=\"code\" name=\"code\" type=\"text\" inputmode=\"numeric\" autocomplete=\"one-time-code\" pattern=\"[0-9 ]{6,7}\" maxlength=\"7\" required autofocus aria-describedby=\"code-help\"></p>\n<p id=\"code-help\" class=\"help\">In the page, “Connect your AI app” shows a 6-digit code. It changes every 2 minutes.</p>`;\n\t},\n\tcheck(target, form, admit): PairingResult {\n\t\tconst result = target.codes.check(\n\t\t\tform.get(\"code\") ?? \"\",\n\t\t\tDate.now(),\n\t\t\tadmit,\n\t\t);\n\t\tif (result === \"ok\") return { ok: true };\n\t\tlet message = MESSAGES[result];\n\t\tif (result === \"wrong\") {\n\t\t\tconst left = target.codes.attemptsLeft;\n\t\t\tmessage += ` ${left === 1 ? \"1 try is\" : `${left} tries are`} left for this code.`;\n\t\t}\n\t\treturn {\n\t\t\tok: false,\n\t\t\tmessage,\n\t\t\treason: result === \"limited\" ? \"rate_limited\" : result,\n\t\t};\n\t},\n};\n\n/** The methods without settings; `oidc` is added by the server when configured. */\nexport const PAIRING_METHODS: Partial<Record<PairingKind, PairingMethod>> = {\n\tcode: codePairing,\n};\n",
25
+ "import {\n\taddressIdFromSecret,\n\tisWellFormedSecret,\n\tshortAddress,\n} from \"./address.ts\";\nimport type { RelayConfig, Site, SiteLimits } from \"./config.ts\";\nimport {\n\tExchange,\n\ttype PageGoneReply,\n\tRelayErrorCode,\n\ttype RpcId,\n} from \"./exchange.ts\";\nimport { errorName, type Logger } from \"./log.ts\";\nimport { PairingCodes } from \"./oauth/pairing.ts\";\nimport {\n\ttype ByeReason,\n\tCloseCode,\n\ttype ErrorCode,\n\ttype GrantInfo,\n\ttype GrantRef,\n\ttype Limits,\n\tparseTabMessage,\n\ttype RelayMessage,\n\ttype TabMessage,\n} from \"./protocol.ts\";\nimport type { TokenStore } from \"./store/types.ts\";\nimport { newId } from \"./tokens.ts\";\n\n/** The part of Bun's `ServerWebSocket` a session uses (a fake in unit tests). */\nexport interface SocketLike {\n\t/** Bytes sent, -1 when queued under backpressure, 0 when dropped. */\n\tsend(data: string): number;\n\tclose(code?: number, reason?: string): void;\n}\n\n/** What a session needs from its relay. */\nexport interface SessionHost {\n\treadonly config: RelayConfig;\n\treadonly logger: Logger;\n\treadonly store: TokenStore;\n\t/** The external origin, e.g. `https://relay.ractive.ch`. */\n\tpublicUrl(): string;\n\t/** Register a session for its address; returns the session it replaced. */\n\tclaimAddress(session: TabSession): TabSession | undefined;\n\t/** Called once when a session is over. */\n\treleased(session: TabSession): void;\n\t/** Called once when a session is ready for MCP requests (DEC-074). */\n\tready(session: TabSession): void;\n\t/** The tab pressed Disconnect for one app; false if the grant is not this address's. */\n\trevokeGrant(session: TabSession, grantId: string): Promise<boolean>;\n\t/** The tab revokes every grant of its address (\"New address\"). */\n\trevokeAll(session: TabSession): Promise<void>;\n\t/** The page proves who its user is (DEC-076); answers the tab itself. */\n\tbindIdentity(\n\t\tsession: TabSession,\n\t\tassertion: string,\n\t\tround?: number,\n\t): Promise<void>;\n\t/** The page's user signed out (DEC-078). */\n\tsignOut(session: TabSession): Promise<void>;\n\t/** Pairing code lifetime (tests shorten it). */\n\treadonly pairingCodeTtlMs?: number;\n}\n\n/** One MCP request for the tab: always `POST /mcp` (protocol: tunnelled MCP requests). */\nexport interface ForwardInput {\n\theaders: Record<string, string>;\n\tbody: string;\n\trpcId: RpcId | undefined;\n\tisListen: boolean;\n\tgrant: GrantRef;\n\textraHeaders: Record<string, string>;\n\t/** Called once with the finished exchange (for the request log line). */\n\tonDone: (exchange: Exchange) => void;\n\t/** The answer when the page goes away mid-request (DEC-074). */\n\tpageGone?: (waiting: boolean) => PageGoneReply;\n}\n\n/** The limits announced to the tab (protocol: `session.limits`). */\nexport function limitsOf(limits: SiteLimits | RelayConfig): Limits {\n\treturn {\n\t\tmaxMessageBytes: limits.maxMessageBytes,\n\t\tmaxBodyBytes: limits.maxMessageBytes,\n\t\trequestTimeoutMs: limits.requestTimeoutMs,\n\t\tidleTimeoutMs: limits.idleTimeoutMs,\n\t\theartbeatMs: limits.heartbeatMs,\n\t\tkeepAliveMs: limits.keepAliveMs,\n\t\tmaxConcurrentRequests: limits.maxConcurrentRequests,\n\t};\n}\n\nfunction clientHost(clientId: string): string {\n\ttry {\n\t\treturn new URL(clientId).host;\n\t} catch {\n\t\treturn \"\";\n\t}\n}\n\n/**\n * The request id of a frame too large to parse, from a cheap match at its\n * start (the tab library sends `type` and `id` first); `undefined` if absent.\n */\nfunction frameId(data: string): string | undefined {\n\treturn /\"id\"\\s*:\\s*\"([^\"\\\\]{1,64})\"/.exec(data.slice(0, 256))?.[1];\n}\n\n/** One tab's WebSocket session, from open to close. */\nexport class TabSession {\n\treadonly sessionId: string = newId(\"s\");\n\treadonly ip: string;\n\treadonly origin: string;\n\t/** The site this session's origin belongs to (DEC-045). */\n\treadonly site: Site;\n\treadonly openedAt: number = Date.now();\n\t/**\n\t * The address id the `/tab` URL named (`?a=`, DEC-098), checked against\n\t * the secret in `hello`; `undefined` for libraries that do not send it.\n\t */\n\treadonly expectedAddress: string | undefined;\n\t/** Set once `hello` was accepted. */\n\taddressId: string | undefined;\n\t/** The app name `/authorize` shows: the site's name, else the page's claim. */\n\tappName: string | undefined;\n\t/** Pairing codes while the tab's dialog is open (DEC-028). */\n\treadonly codes: PairingCodes;\n\t/**\n\t * The binding key of the page's user, once an assertion was accepted\n\t * (DEC-077); `undefined` while nobody is signed in.\n\t */\n\tidentity: string | undefined;\n\t/**\n\t * The last assertion could not be checked (keys or store unavailable): while\n\t * nobody is bound, AI apps are told to try again rather than to sign in.\n\t */\n\tidentityUnchecked = false;\n\treadonly pending: Map<string, Exchange> = new Map();\n\t/** The grant of each pending listen stream (a cap per grant). */\n\treadonly #listenGrants = new Map<string, string>();\n\n\treadonly #ws: SocketLike;\n\treadonly #host: SessionHost;\n\t#state: \"new\" | \"hello\" | \"ready\" | \"ended\" = \"new\";\n\t/** The \"Connect your AI app\" dialog is open (OIDC pairing, DEC-079). */\n\t#dialogOpen = false;\n\t#identityChain: Promise<void> = Promise.resolve();\n\t#lastRequestAt = Date.now();\n\t#missedPongs = 0;\n\t#nextRequest = 0;\n\t#helloTimer: Timer | undefined;\n\t#heartbeatTimer: Timer | undefined;\n\t#lifetimeTimer: Timer | undefined;\n\n\tconstructor(\n\t\tws: SocketLike,\n\t\tip: string,\n\t\torigin: string,\n\t\tsite: Site,\n\t\thost: SessionHost,\n\t\texpectedAddress?: string,\n\t) {\n\t\tthis.#ws = ws;\n\t\tthis.ip = ip;\n\t\tthis.origin = origin;\n\t\tthis.site = site;\n\t\tthis.#host = host;\n\t\tthis.expectedAddress = expectedAddress;\n\t\tthis.codes = new PairingCodes(\n\t\t\t(code, expiresAt) => {\n\t\t\t\tthis.send({\n\t\t\t\t\ttype: \"pairing.code\",\n\t\t\t\t\tcode,\n\t\t\t\t\texpiresAt: new Date(expiresAt).toISOString(),\n\t\t\t\t});\n\t\t\t},\n\t\t\thost.pairingCodeTtlMs === undefined\n\t\t\t\t? {}\n\t\t\t\t: { ttlMs: host.pairingCodeTtlMs },\n\t\t);\n\t\tthis.#helloTimer = setTimeout(() => {\n\t\t\tif (this.#state === \"new\") {\n\t\t\t\tthis.#protocolError(\"bad_message\", \"no hello in time\");\n\t\t\t}\n\t\t}, host.config.helloTimeoutMs);\n\t}\n\n\tget ready(): boolean {\n\t\treturn this.#state === \"ready\";\n\t}\n\n\t/** Whether the page's \"Connect your AI app\" dialog is open. */\n\tget pairingOpen(): boolean {\n\t\treturn this.site.pairing === \"code\" ? this.codes.active : this.#dialogOpen;\n\t}\n\n\t/** The limits of this session (its site's). */\n\tget limits(): SiteLimits {\n\t\treturn this.site.limits;\n\t}\n\n\t/** Send one message; false if Bun dropped it under backpressure (DEC-021). */\n\tsend(message: RelayMessage): boolean {\n\t\tif (this.#state === \"ended\") return false;\n\t\tlet sent: number;\n\t\ttry {\n\t\t\tsent = this.#ws.send(JSON.stringify(message));\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t\tif (sent === 0) {\n\t\t\tthis.#host.logger.warn(\"tab.send_dropped\", {\n\t\t\t\tsession: this.sessionId,\n\t\t\t\ttype: message.type,\n\t\t\t});\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\n\t/** A frame arrived from the tab. */\n\thandleFrame(data: string | Uint8Array): void {\n\t\tif (this.#state === \"ended\") return;\n\t\tif (typeof data !== \"string\") {\n\t\t\tthis.#protocolError(\"bad_message\", \"binary frames are not allowed\");\n\t\t\treturn;\n\t\t}\n\t\t// Refused by length before parsing: a page of a site with a small limit\n\t\t// must not make the relay parse frames up to the largest site's.\n\t\tif (Buffer.byteLength(data) > this.limits.maxMessageBytes) {\n\t\t\tconst id = frameId(data);\n\t\t\tthis.#sendError(\"too_large\", \"message exceeds maxMessageBytes\", id);\n\t\t\tconst exchange = id === undefined ? undefined : this.pending.get(id);\n\t\t\texchange?.fail(\n\t\t\t\t502,\n\t\t\t\tRelayErrorCode.tooLarge,\n\t\t\t\t\"The page sent a response that is too large.\",\n\t\t\t\ttrue,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tconst parsed = parseTabMessage(data);\n\t\tif (!parsed.ok) {\n\t\t\tif (this.#state === \"new\") {\n\t\t\t\tthis.#protocolError(\"bad_message\", parsed.error);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.#sendError(\"bad_message\", parsed.error, parsed.id);\n\t\t\t// A malformed response must not leave its request hanging.\n\t\t\tconst exchange =\n\t\t\t\tparsed.id === undefined ? undefined : this.pending.get(parsed.id);\n\t\t\texchange?.fail(\n\t\t\t\t502,\n\t\t\t\tRelayErrorCode.badResponse,\n\t\t\t\t\"The page sent an invalid response.\",\n\t\t\t\ttrue,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tif (\"unknown\" in parsed) {\n\t\t\tif (this.#state === \"new\") {\n\t\t\t\tthis.#protocolError(\"bad_message\", \"hello expected\");\n\t\t\t} else if (parsed.id !== undefined) {\n\t\t\t\tthis.#sendError(\n\t\t\t\t\t\"unknown_type\",\n\t\t\t\t\t`unknown type ${parsed.type}`,\n\t\t\t\t\tparsed.id,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\t// One bad frame must never become an unhandled rejection (it would end\n\t\t// the process): log it, tell the tab, keep the session.\n\t\tthis.#handle(parsed.message).catch((error: unknown) => {\n\t\t\tthis.#host.logger.error(\"tab.handler_error\", {\n\t\t\t\tsession: this.sessionId,\n\t\t\t\ttype: parsed.message.type,\n\t\t\t\tmessage: error instanceof Error ? error.name : \"unknown\",\n\t\t\t});\n\t\t\tthis.#sendError(\"bad_message\", \"the relay could not handle this message\");\n\t\t});\n\t}\n\n\tasync #handle(msg: TabMessage): Promise<void> {\n\t\tif (this.#state === \"new\") {\n\t\t\tif (msg.type !== \"hello\") {\n\t\t\t\tthis.#protocolError(\"bad_message\", \"hello expected\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait this.#hello(\n\t\t\t\tmsg.secret,\n\t\t\t\tmsg.library,\n\t\t\t\tmsg.app?.name,\n\t\t\t\tmsg.revokeAll === true,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tif (this.#state === \"hello\") return; // still answering hello\n\t\tswitch (msg.type) {\n\t\t\tcase \"hello\":\n\t\t\t\tthis.#sendError(\"bad_message\", \"hello was already sent\");\n\t\t\t\treturn;\n\t\t\tcase \"pong\":\n\t\t\t\tthis.#missedPongs = 0;\n\t\t\t\treturn;\n\t\t\tcase \"bye\":\n\t\t\t\tthis.end(CloseCode.normal, \"bye\");\n\t\t\t\treturn;\n\t\t\tcase \"pairing.start\":\n\t\t\t\tif (this.site.pairing === \"code\") this.codes.start();\n\t\t\t\telse this.#dialogOpen = true;\n\t\t\t\treturn;\n\t\t\tcase \"pairing.stop\":\n\t\t\t\tthis.codes.stop();\n\t\t\t\tthis.#dialogOpen = false;\n\t\t\t\treturn;\n\t\t\tcase \"identity\":\n\t\t\t\tawait this.#serialIdentity(() =>\n\t\t\t\t\tthis.#host.bindIdentity(this, msg.assertion, msg.round),\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\tcase \"identity.logout\":\n\t\t\t\tawait this.#serialIdentity(() => this.#host.signOut(this));\n\t\t\t\treturn;\n\t\t\tcase \"grant.revoke\":\n\t\t\t\tif (!(await this.#host.revokeGrant(this, msg.grantId))) {\n\t\t\t\t\tthis.#sendError(\"unknown_id\", \"unknown grant\");\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\tcase \"grants.revokeAll\":\n\t\t\t\tawait this.#host.revokeAll(this);\n\t\t\t\treturn;\n\t\t\tcase \"error\":\n\t\t\t\tthis.#host.logger.debug(\"tab.error\", {\n\t\t\t\t\tsession: this.sessionId,\n\t\t\t\t\tcode: msg.code,\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\tcase \"http.response\":\n\t\t\tcase \"http.response.start\":\n\t\t\tcase \"http.response.chunk\":\n\t\t\tcase \"http.response.end\": {\n\t\t\t\tconst exchange = this.pending.get(msg.id);\n\t\t\t\tif (exchange === undefined) return; // finished or cancelled: drop\n\t\t\t\tif (msg.type === \"http.response\") exchange.onResponse(msg);\n\t\t\t\telse if (msg.type === \"http.response.start\") exchange.onStart(msg);\n\t\t\t\telse if (msg.type === \"http.response.chunk\") exchange.onChunk(msg.data);\n\t\t\t\telse exchange.onEnd();\n\t\t\t\tif (exchange.badResponseReason !== undefined) {\n\t\t\t\t\tthis.#sendError(\"bad_message\", exchange.badResponseReason, msg.id);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tasync #hello(\n\t\tsecret: string,\n\t\tlibrary: { name: string; version: string },\n\t\tappName: string | undefined,\n\t\trevokeAll = false,\n\t): Promise<void> {\n\t\tif (!isWellFormedSecret(secret)) {\n\t\t\tthis.#protocolError(\n\t\t\t\t\"bad_message\",\n\t\t\t\t\"secret must be 43 base64url characters\",\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tconst addressId = addressIdFromSecret(secret);\n\t\tif (\n\t\t\tthis.expectedAddress !== undefined &&\n\t\t\tthis.expectedAddress !== addressId\n\t\t) {\n\t\t\t// A load balancer routed this socket by the URL's id (DEC-098): a\n\t\t\t// session for another address must not open on this instance.\n\t\t\t// Nothing was claimed, so the address's real session stays.\n\t\t\tthis.#host.logger.warn(\"tab.refused\", { reason: \"address_mismatch\" });\n\t\t\tthis.#sendError(\n\t\t\t\t\"address_mismatch\",\n\t\t\t\t\"the address id in the URL is not the one of the secret\",\n\t\t\t);\n\t\t\tthis.end(CloseCode.addressMismatch, \"address mismatch\");\n\t\t\treturn;\n\t\t}\n\t\tclearTimeout(this.#helloTimer);\n\t\tthis.#state = \"hello\";\n\t\tthis.addressId = addressId;\n\t\tthis.appName = this.site.name ?? appName;\n\t\tconst replaced = this.#host.claimAddress(this);\n\t\tif (replaced !== undefined) {\n\t\t\treplaced.end(\n\t\t\t\tCloseCode.replaced,\n\t\t\t\t\"replaced by a newer session\",\n\t\t\t\t\"replaced\",\n\t\t\t);\n\t\t}\n\t\tif (revokeAll) {\n\t\t\t// The page takes its address back (DEC-095): every grant goes in the\n\t\t\t// same step as the claim, whatever claims the address next, and the\n\t\t\t// `session` below lists none, which confirms it to the page.\n\t\t\ttry {\n\t\t\t\tawait this.#host.revokeAll(this);\n\t\t\t} catch (error) {\n\t\t\t\tthis.#host.logger.error(\"store.error\", {\n\t\t\t\t\top: \"revokeAll\",\n\t\t\t\t\terror: errorName(error),\n\t\t\t\t});\n\t\t\t\tthis.end(CloseCode.policyViolation, \"could not revoke the grants\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tlet grants: GrantInfo[] = [];\n\t\ttry {\n\t\t\tconst list = await this.#host.store.listGrants(\n\t\t\t\tthis.addressId,\n\t\t\t\tDate.now(),\n\t\t\t);\n\t\t\tgrants = list.map((g) => ({\n\t\t\t\tgrantId: g.grantId,\n\t\t\t\tclient: {\n\t\t\t\t\tid: g.clientId,\n\t\t\t\t\tname: g.clientName,\n\t\t\t\t\thost: clientHost(g.clientId),\n\t\t\t\t},\n\t\t\t\tcreatedAt: new Date(g.createdAt).toISOString(),\n\t\t\t}));\n\t\t} catch (error) {\n\t\t\tthis.#host.logger.error(\"store.error\", {\n\t\t\t\top: \"listGrants\",\n\t\t\t\terror: errorName(error),\n\t\t\t});\n\t\t}\n\t\tif (this.#state !== \"hello\") return; // closed meanwhile\n\n\t\tconst limits = this.limits;\n\t\tconst expiresAt = this.openedAt + limits.sessionLifetimeMs;\n\t\tthis.#state = \"ready\";\n\t\tconst sent = this.send({\n\t\t\ttype: \"session\",\n\t\t\tsessionId: this.sessionId,\n\t\t\taddress: `${this.#host.publicUrl()}/a/${this.addressId}/mcp`,\n\t\t\torigin: this.origin,\n\t\t\t...(this.appName === undefined ? {} : { appName: this.appName }),\n\t\t\tpairing: this.site.pairing,\n\t\t\texpiresAt: new Date(expiresAt).toISOString(),\n\t\t\tlimits: limitsOf(limits),\n\t\t\tgrants,\n\t\t});\n\t\tif (!sent) {\n\t\t\tthis.end(CloseCode.policyViolation, \"send failed\");\n\t\t\treturn;\n\t\t}\n\t\tthis.#heartbeatTimer = setInterval(() => this.#tick(), limits.heartbeatMs);\n\t\tthis.#lifetimeTimer = setTimeout(\n\t\t\t() =>\n\t\t\t\tthis.end(\n\t\t\t\t\tCloseCode.sessionExpired,\n\t\t\t\t\t\"session lifetime reached\",\n\t\t\t\t\t\"expired\",\n\t\t\t\t),\n\t\t\tMath.max(0, expiresAt - Date.now()),\n\t\t);\n\t\tthis.#host.logger.info(\"tab.session\", {\n\t\t\tsession: this.sessionId,\n\t\t\taddress: shortAddress(this.addressId),\n\t\t\torigin: this.origin,\n\t\t\tlibrary: `${library.name}@${library.version}`,\n\t\t\treplaced: replaced !== undefined,\n\t\t});\n\t\tthis.#host.ready(this);\n\t}\n\n\t/**\n\t * Identity messages run one after another, in the order they came: frames\n\t * are handled concurrently, and a slow assertion check must not finish\n\t * after a later sign-out (review of PR #13).\n\t */\n\t#serialIdentity(run: () => Promise<void>): Promise<void> {\n\t\tconst next = this.#identityChain.then(run);\n\t\tthis.#identityChain = next.catch(() => {});\n\t\treturn next;\n\t}\n\n\t#tick(): void {\n\t\tif (this.#state !== \"ready\") return;\n\t\tif (this.#missedPongs >= 2) {\n\t\t\tthis.end(CloseCode.heartbeatLost, \"heartbeat lost\");\n\t\t\treturn;\n\t\t}\n\t\t// Idle = no MCP request and nothing in flight, listen streams included:\n\t\t// a listen stream may stay open for the session's lifetime (protocol.md),\n\t\t// which still caps it.\n\t\tif (\n\t\t\tthis.pending.size === 0 &&\n\t\t\tDate.now() - this.#lastRequestAt >= this.limits.idleTimeoutMs\n\t\t) {\n\t\t\tthis.end(CloseCode.idleTimeout, \"idle\", \"idle\");\n\t\t\treturn;\n\t\t}\n\t\tthis.#missedPongs++;\n\t\tthis.send({ type: \"ping\", t: Date.now() });\n\t}\n\n\t/** The listen streams one grant has open on this session. */\n\tlistenStreams(grantId: string): number {\n\t\tlet n = 0;\n\t\tfor (const g of this.#listenGrants.values()) if (g === grantId) n++;\n\t\treturn n;\n\t}\n\n\t/** Tunnel one MCP request to the tab. Resolves with the AI app's response. */\n\tforward(input: ForwardInput): Exchange {\n\t\tthis.#lastRequestAt = Date.now();\n\t\tconst id = `r${++this.#nextRequest}`;\n\t\tconst limits = this.limits;\n\t\tconst exchange = new Exchange({\n\t\t\tid,\n\t\t\trpcId: input.rpcId,\n\t\t\tisListen: input.isListen,\n\t\t\trequestTimeoutMs: limits.requestTimeoutMs,\n\t\t\tkeepAliveMs: limits.keepAliveMs,\n\t\t\tmaxBodyBytes: limitsOf(limits).maxBodyBytes,\n\t\t\textraHeaders: input.extraHeaders,\n\t\t\tcancel: (reason) => {\n\t\t\t\tif (!this.send({ type: \"http.cancel\", id, reason })) {\n\t\t\t\t\tthis.#host.logger.debug(\"tab.cancel_not_sent\", {\n\t\t\t\t\t\tsession: this.sessionId,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t},\n\t\t\tonDone: (e) => {\n\t\t\t\tthis.pending.delete(id);\n\t\t\t\tthis.#listenGrants.delete(id);\n\t\t\t\tthis.#lastRequestAt = Date.now();\n\t\t\t\tinput.onDone(e);\n\t\t\t},\n\t\t\t...(input.pageGone === undefined ? {} : { pageGone: input.pageGone }),\n\t\t});\n\t\tthis.pending.set(id, exchange);\n\t\tif (input.isListen) this.#listenGrants.set(id, input.grant.grantId);\n\t\tconst sent = this.send({\n\t\t\ttype: \"http.request\",\n\t\t\tid,\n\t\t\tmethod: \"POST\",\n\t\t\tpath: \"/mcp\",\n\t\t\theaders: input.headers,\n\t\t\tbody: input.body,\n\t\t\tgrant: input.grant,\n\t\t});\n\t\tif (!sent) {\n\t\t\texchange.fail(\n\t\t\t\t503,\n\t\t\t\tRelayErrorCode.sendFailed,\n\t\t\t\t\"The connection to the page is overloaded. Try again.\",\n\t\t\t\tfalse,\n\t\t\t);\n\t\t}\n\t\treturn exchange;\n\t}\n\n\t/** End the session: optional `bye`, then close the socket. Idempotent. */\n\tend(code: number, reason: string, bye?: ByeReason): void {\n\t\tif (this.#state === \"ended\") return;\n\t\tif (bye !== undefined) this.send({ type: \"bye\", reason: bye });\n\t\tthis.#cleanup(code, reason);\n\t\ttry {\n\t\t\tthis.#ws.close(code, reason);\n\t\t} catch {\n\t\t\t// already closed\n\t\t}\n\t}\n\n\t/** The socket closed (from Bun's `close` handler). */\n\tclosed(code: number): void {\n\t\tthis.#cleanup(code, \"closed\");\n\t}\n\n\t#cleanup(code: number, reason: string): void {\n\t\tif (this.#state === \"ended\") return;\n\t\tconst wasReady = this.#state === \"ready\" || this.#state === \"hello\";\n\t\tthis.#state = \"ended\";\n\t\tthis.codes.stop();\n\t\tthis.#dialogOpen = false;\n\t\tclearTimeout(this.#helloTimer);\n\t\tclearInterval(this.#heartbeatTimer);\n\t\tclearTimeout(this.#lifetimeTimer);\n\t\t// Released first, so a request the relay sends again waits for the next\n\t\t// session instead of finding this one.\n\t\tthis.#host.released(this);\n\t\tfor (const exchange of [...this.pending.values()]) exchange.tabGone();\n\t\tif (wasReady) {\n\t\t\tthis.#host.logger.info(\"tab.closed\", {\n\t\t\t\tsession: this.sessionId,\n\t\t\t\tcode,\n\t\t\t\treason,\n\t\t\t\tdurationMs: Date.now() - this.openedAt,\n\t\t\t});\n\t\t}\n\t}\n\n\t#sendError(code: ErrorCode, message: string, id?: string): void {\n\t\tthis.send(\n\t\t\tid === undefined\n\t\t\t\t? { type: \"error\", code, message }\n\t\t\t\t: { type: \"error\", code, message, id },\n\t\t);\n\t}\n\n\t#protocolError(code: ErrorCode, message: string): void {\n\t\tthis.#sendError(code, message);\n\t\tthis.end(CloseCode.policyViolation, message);\n\t}\n}\n",
26
+ "import { Database } from \"bun:sqlite\";\nimport { summarizeGrants } from \"./memory.ts\";\nimport type {\n\tGrantSummary,\n\tRevokeOptions,\n\tRevokeSelector,\n\tTokenKind,\n\tTokenRecord,\n\tTokenStore,\n} from \"./types.ts\";\n\nconst SCHEMA_VERSION = 5;\n/**\n * The oldest schema a relay must know to use a file of this version safely,\n * kept in the file's `meta` table (review of PR 65). A relay refuses a file\n * whose value is above its own `SCHEMA_VERSION`: a later release that adds\n * a column an older relay would drop raises it, and older relays from this\n * one on refuse to open that file rather than quietly lose the column.\n * Relays before this one do not read it: one of them drops `client_auth`\n * (DEC-106), so bound grants come back unbound.\n */\nconst MIN_READER_VERSION = 5;\n/** The reuse window of used refresh tokens (DEC-091), for the version 4 step. */\nconst REUSE_WINDOW_MS = 86_400_000;\n\ninterface Row {\n\thash: string;\n\tkind: string;\n\tgrant_id: string;\n\taddress_id: string;\n\tclient_id: string;\n\tclient_name: string;\n\tscopes: string;\n\tgrant_created_at: number;\n\tissued_at: number;\n\texpires_at: number;\n\tused_at: number | null;\n\tsubject: string | null;\n\tclient_auth: string | null;\n}\n\nfunction toRecord(row: Row): TokenRecord {\n\tconst record: TokenRecord = {\n\t\thash: row.hash,\n\t\tkind: row.kind as TokenKind,\n\t\tgrantId: row.grant_id,\n\t\taddressId: row.address_id,\n\t\tclientId: row.client_id,\n\t\tclientName: row.client_name,\n\t\tscopes: JSON.parse(row.scopes) as string[],\n\t\tgrantCreatedAt: row.grant_created_at,\n\t\tissuedAt: row.issued_at,\n\t\texpiresAt: row.expires_at,\n\t};\n\tif (row.used_at !== null) record.usedAt = row.used_at;\n\tif (row.subject !== null) record.subject = row.subject;\n\tif (row.client_auth === \"private_key_jwt\")\n\t\trecord.clientAuth = \"private_key_jwt\";\n\treturn record;\n}\n\n/**\n * Tokens in a SQLite file via `bun:sqlite` (the default store, DEC-030). Uses\n * WAL mode so another process (or a test) can read and write the same file.\n */\nexport class SqliteTokenStore implements TokenStore {\n\treadonly #db: Database;\n\n\t/** `path` is a file path, or `:memory:`. */\n\tconstructor(path: string) {\n\t\tthis.#db = new Database(path, { create: true, strict: true });\n\t\tthis.#db.run(\"PRAGMA busy_timeout = 5000\");\n\t\tif (path !== \":memory:\") this.#db.run(\"PRAGMA journal_mode = WAL\");\n\t\tthis.#checkReader();\n\t\tthis.#migrate();\n\t\tthis.#db.run(`CREATE TABLE IF NOT EXISTS meta (\n\t\t\tkey TEXT PRIMARY KEY,\n\t\t\tvalue INTEGER NOT NULL\n\t\t) STRICT`);\n\t\tthis.#db\n\t\t\t.query(\n\t\t\t\t`INSERT INTO meta (key, value) VALUES ('min_reader_version', $v)\n\t\t\t\tON CONFLICT (key) DO UPDATE SET value = max(value, excluded.value)`,\n\t\t\t)\n\t\t\t.run({ v: MIN_READER_VERSION });\n\t}\n\n\t/** Refuse a file that a newer relay marked as needing at least its schema. */\n\t#checkReader(): void {\n\t\tconst table = this.#db\n\t\t\t.query(\n\t\t\t\t\"SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'meta'\",\n\t\t\t)\n\t\t\t.get();\n\t\tif (table === null) return;\n\t\tconst row = this.#db\n\t\t\t.query(\"SELECT value FROM meta WHERE key = 'min_reader_version'\")\n\t\t\t.get() as { value: number } | null;\n\t\tif (row !== null && row.value > SCHEMA_VERSION) {\n\t\t\tthis.#db.close();\n\t\t\tthrow new Error(\n\t\t\t\t`the token store file needs a newer relay (schema ${row.value}; this relay knows ${SCHEMA_VERSION})`,\n\t\t\t);\n\t\t}\n\t}\n\n\t#migrate(): void {\n\t\tconst row = this.#db.query(\"PRAGMA user_version\").get() as {\n\t\t\tuser_version: number;\n\t\t};\n\t\tif (row.user_version >= SCHEMA_VERSION) return;\n\t\tthis.#db.transaction(() => {\n\t\t\t// Version 2: tombstones of revoked grants and addresses.\n\t\t\tthis.#db.run(`CREATE TABLE IF NOT EXISTS revocations (\n\t\t\t\tkind TEXT NOT NULL CHECK (kind IN ('grant', 'address')),\n\t\t\t\tid TEXT NOT NULL,\n\t\t\t\tat INTEGER NOT NULL,\n\t\t\t\tkeep_until INTEGER NOT NULL,\n\t\t\t\tPRIMARY KEY (kind, id)\n\t\t\t) STRICT`);\n\t\t\tthis.#db.run(`CREATE TABLE IF NOT EXISTS tokens (\n\t\t\t\thash TEXT PRIMARY KEY,\n\t\t\t\tkind TEXT NOT NULL CHECK (kind IN ('access', 'refresh')),\n\t\t\t\tgrant_id TEXT NOT NULL,\n\t\t\t\taddress_id TEXT NOT NULL,\n\t\t\t\tclient_id TEXT NOT NULL,\n\t\t\t\tclient_name TEXT NOT NULL,\n\t\t\t\tscopes TEXT NOT NULL,\n\t\t\t\tgrant_created_at INTEGER NOT NULL,\n\t\t\t\tissued_at INTEGER NOT NULL,\n\t\t\t\texpires_at INTEGER NOT NULL,\n\t\t\t\tused_at INTEGER,\n\t\t\t\tsubject TEXT,\n\t\t\t\tclient_auth TEXT\n\t\t\t) STRICT`);\n\t\t\t// Version 3: the subject of identity-bound grants (DEC-077).\n\t\t\tconst columns = this.#db.query(\"PRAGMA table_info(tokens)\").all() as {\n\t\t\t\tname: string;\n\t\t\t}[];\n\t\t\tif (!columns.some((c) => c.name === \"subject\")) {\n\t\t\t\tthis.#db.run(\"ALTER TABLE tokens ADD COLUMN subject TEXT\");\n\t\t\t}\n\t\t\t// Version 5: the client authentication binding of a grant, and the\n\t\t\t// used client assertion ids (DEC-106).\n\t\t\tif (!columns.some((c) => c.name === \"client_auth\")) {\n\t\t\t\tthis.#db.run(\"ALTER TABLE tokens ADD COLUMN client_auth TEXT\");\n\t\t\t}\n\t\t\tthis.#db.run(`CREATE TABLE IF NOT EXISTS nonces (\n\t\t\t\tkey TEXT PRIMARY KEY,\n\t\t\t\texpires_at INTEGER NOT NULL\n\t\t\t) STRICT`);\n\t\t\tthis.#db.run(\n\t\t\t\t\"CREATE INDEX IF NOT EXISTS tokens_grant ON tokens (grant_id)\",\n\t\t\t);\n\t\t\tthis.#db.run(\n\t\t\t\t\"CREATE INDEX IF NOT EXISTS tokens_address ON tokens (address_id)\",\n\t\t\t);\n\t\t\tthis.#db.run(\n\t\t\t\t\"CREATE INDEX IF NOT EXISTS tokens_expires ON tokens (expires_at)\",\n\t\t\t);\n\t\t\t// Version 4: refresh tokens used before the upgrade get the reuse\n\t\t\t// window that markUsed now gives (DEC-091), so they go too.\n\t\t\tthis.#db\n\t\t\t\t.query(\n\t\t\t\t\t`UPDATE tokens SET expires_at = min(expires_at, used_at + $window)\n\t\t\t\t\tWHERE kind = 'refresh' AND used_at IS NOT NULL`,\n\t\t\t\t)\n\t\t\t\t.run({ window: REUSE_WINDOW_MS });\n\t\t\tthis.#db.run(`PRAGMA user_version = ${SCHEMA_VERSION}`);\n\t\t})();\n\t}\n\n\tasync put(r: TokenRecord): Promise<boolean> {\n\t\treturn this.#db.transaction(() => {\n\t\t\tconst revoked = this.#db\n\t\t\t\t.query(\n\t\t\t\t\t`SELECT 1 FROM revocations WHERE (kind = 'grant' AND id = $grant_id)\n\t\t\t\t\tOR (kind = 'address' AND id = $address_id AND at >= $created)`,\n\t\t\t\t)\n\t\t\t\t.get({\n\t\t\t\t\tgrant_id: r.grantId,\n\t\t\t\t\taddress_id: r.addressId,\n\t\t\t\t\tcreated: r.grantCreatedAt,\n\t\t\t\t});\n\t\t\tif (revoked !== null) return false;\n\t\t\tthis.#insert(r);\n\t\t\treturn true;\n\t\t})();\n\t}\n\n\t#insert(r: TokenRecord): void {\n\t\tthis.#db\n\t\t\t.query(\n\t\t\t\t`INSERT OR REPLACE INTO tokens (hash, kind, grant_id, address_id, client_id,\n\t\t\t\tclient_name, scopes, grant_created_at, issued_at, expires_at, used_at, subject,\n\t\t\t\tclient_auth)\n\t\t\t\tVALUES ($hash, $kind, $grant_id, $address_id, $client_id, $client_name,\n\t\t\t\t$scopes, $grant_created_at, $issued_at, $expires_at, $used_at, $subject,\n\t\t\t\t$client_auth)`,\n\t\t\t)\n\t\t\t.run({\n\t\t\t\thash: r.hash,\n\t\t\t\tkind: r.kind,\n\t\t\t\tgrant_id: r.grantId,\n\t\t\t\taddress_id: r.addressId,\n\t\t\t\tclient_id: r.clientId,\n\t\t\t\tclient_name: r.clientName,\n\t\t\t\tscopes: JSON.stringify(r.scopes),\n\t\t\t\tgrant_created_at: r.grantCreatedAt,\n\t\t\t\tissued_at: r.issuedAt,\n\t\t\t\texpires_at: r.expiresAt,\n\t\t\t\tused_at: r.usedAt ?? null,\n\t\t\t\tsubject: r.subject ?? null,\n\t\t\t\tclient_auth: r.clientAuth ?? null,\n\t\t\t});\n\t}\n\n\tasync get(hash: string): Promise<TokenRecord | undefined> {\n\t\tconst row = this.#db\n\t\t\t.query(\"SELECT * FROM tokens WHERE hash = $hash\")\n\t\t\t.get({ hash }) as Row | null;\n\t\treturn row === null ? undefined : toRecord(row);\n\t}\n\n\tasync markUsed(\n\t\thash: string,\n\t\tnow: number,\n\t\tkeepUntil: number,\n\t): Promise<boolean> {\n\t\tconst result = this.#db\n\t\t\t.query(\n\t\t\t\t`UPDATE tokens SET used_at = $now, expires_at = min(expires_at, $keep_until)\n\t\t\t\tWHERE hash = $hash AND used_at IS NULL`,\n\t\t\t)\n\t\t\t.run({ hash, now, keep_until: keepUntil });\n\t\treturn result.changes === 1;\n\t}\n\n\tasync revoke(\n\t\tselector: RevokeSelector,\n\t\toptions: RevokeOptions,\n\t): Promise<number> {\n\t\treturn this.#db.transaction(() => {\n\t\t\tthis.#db\n\t\t\t\t.query(\n\t\t\t\t\t`INSERT INTO revocations (kind, id, at, keep_until) VALUES ($kind, $id, $at, $until)\n\t\t\t\t\tON CONFLICT (kind, id) DO UPDATE SET at = max(at, excluded.at),\n\t\t\t\t\tkeep_until = max(keep_until, excluded.keep_until)`,\n\t\t\t\t)\n\t\t\t\t.run({\n\t\t\t\t\tkind: \"grantId\" in selector ? \"grant\" : \"address\",\n\t\t\t\t\tid: \"grantId\" in selector ? selector.grantId : selector.addressId,\n\t\t\t\t\tat: options.now,\n\t\t\t\t\tuntil: options.keepUntil,\n\t\t\t\t});\n\t\t\treturn this.#delete(selector);\n\t\t})();\n\t}\n\n\t#delete(selector: RevokeSelector): number {\n\t\tconst result =\n\t\t\t\"grantId\" in selector\n\t\t\t\t? this.#db\n\t\t\t\t\t\t.query(\"DELETE FROM tokens WHERE grant_id = $id\")\n\t\t\t\t\t\t.run({ id: selector.grantId })\n\t\t\t\t: this.#db\n\t\t\t\t\t\t.query(\"DELETE FROM tokens WHERE address_id = $id\")\n\t\t\t\t\t\t.run({ id: selector.addressId });\n\t\treturn result.changes;\n\t}\n\n\tasync listGrants(addressId: string, now: number): Promise<GrantSummary[]> {\n\t\tconst rows = this.#db\n\t\t\t.query(\n\t\t\t\t`SELECT * FROM tokens WHERE address_id = $addressId AND expires_at > $now\n\t\t\t\tAND (kind = 'access' OR used_at IS NULL)`,\n\t\t\t)\n\t\t\t.all({ addressId, now }) as Row[];\n\t\treturn summarizeGrants(rows.map(toRecord), now);\n\t}\n\n\tasync deleteExpired(now: number): Promise<number> {\n\t\tthis.#db\n\t\t\t.query(\"DELETE FROM revocations WHERE keep_until <= $now\")\n\t\t\t.run({ now });\n\t\tthis.#db.query(\"DELETE FROM nonces WHERE expires_at <= $now\").run({ now });\n\t\treturn this.#db\n\t\t\t.query(\"DELETE FROM tokens WHERE expires_at <= $now\")\n\t\t\t.run({ now }).changes;\n\t}\n\n\t/** One statement: inserted, or an expired entry replaced; a live one stays. */\n\tasync claimNonce(\n\t\tkey: string,\n\t\texpiresAt: number,\n\t\tnow: number,\n\t): Promise<boolean> {\n\t\tconst result = this.#db\n\t\t\t.query(\n\t\t\t\t`INSERT INTO nonces (key, expires_at) VALUES ($key, $expires_at)\n\t\t\t\tON CONFLICT (key) DO UPDATE SET expires_at = excluded.expires_at\n\t\t\t\tWHERE nonces.expires_at <= $now`,\n\t\t\t)\n\t\t\t.run({ key, expires_at: expiresAt, now });\n\t\treturn result.changes === 1;\n\t}\n\n\tasync close(): Promise<void> {\n\t\tthis.#db.close();\n\t}\n}\n",
27
+ "import type { Server, ServerWebSocket } from \"bun\";\nimport { version } from \"../package.json\" with { type: \"json\" };\nimport { addressIdFromSecret, isAddressId, shortAddress } from \"./address.ts\";\nimport {\n\tlargestMessageBytes,\n\tMIN_MESSAGE_BYTES,\n\ttype RelayConfig,\n\tresolveConfig,\n\ttype Site,\n\ttype StoreKind,\n\tsiteFor,\n} from \"./config.ts\";\nimport {\n\ttype Exchange,\n\tRelayErrorCode,\n\ttype RpcId,\n\trpcErrorBody,\n} from \"./exchange.ts\";\nimport { APPLE_TOUCH_ICON, FAVICON_ICO, FAVICON_SVG } from \"./icons.ts\";\nimport { MISMATCH_TEXT, TabAssertionVerifier } from \"./identity.ts\";\nimport { ipPrefixKey } from \"./ip.ts\";\nimport { JwtError, type KeySource } from \"./jwt.ts\";\nimport { createLogger, errorName, type Logger, truncateIp } from \"./log.ts\";\nimport {\n\tisRetryable,\n\ttype NotOpenPhase,\n\tnotOpenAnswer,\n\ttype RelayInfo,\n} from \"./notopen.ts\";\nimport {\n\tAuthorizationServer,\n\ttype AuthorizationTestMode,\n} from \"./oauth/server.ts\";\nimport {\n\tCloseCode,\n\ttype GrantInfo,\n\tMCP_PROTOCOL_VERSION,\n\tPROTOCOL_VERSION_META_KEY,\n\ttype RelayMessage,\n\tSUBPROTOCOL,\n} from \"./protocol.ts\";\nimport { TabSession } from \"./session.ts\";\nimport { MemoryTokenStore } from \"./store/memory.ts\";\nimport { SqliteTokenStore } from \"./store/sqlite.ts\";\nimport type { TokenRecord, TokenStore } from \"./store/types.ts\";\nimport { hashToken } from \"./tokens.ts\";\n\nexport const VERSION: string = version;\n\nconst RELAY_INFO: RelayInfo = { name: \"tab-mcp\", version };\n\n/** Settles the race between an exchange and its page going away. */\nconst RETRY = Symbol(\"retry\");\n\n/** The one mismatched header Claude sends today (claude-code #93290, DEC-033). */\nconst CLAUDE_MISMATCHED_HEADER = \"2025-11-25\";\n\n/**\n * Listen streams one grant may hold on a page at once: they have no request\n * timer, so without a cap one AI app could take every concurrency slot.\n */\nconst MAX_LISTEN_STREAMS_PER_GRANT = 2;\n\n/**\n * Bun closes a WebSocket whose frame is over `maxPayloadLength` (1009). Frames\n * up to this many times the largest site limit get a `too_large` error instead\n * and the session stays; they are refused by length, before parsing.\n */\nconst FRAME_HEADROOM = 4;\n\n/**\n * Refusals of a tab assertion that say nothing about the user: the relay could\n * not check (signing keys or store unavailable, an unexpected error).\n */\nconst TRANSIENT_REFUSALS = new Set([\n\t\"jwks_unavailable\",\n\t\"store_unavailable\",\n\t\"relay_busy\",\n\t\"error\",\n]);\n\n/** The text for requests that cannot be checked against the page's user. */\nconst UNCHECKED_TEXT =\n\t\"The relay cannot check the page's user right now. Try again shortly.\";\n\n/** `Retry-After` (seconds) for answers that ask an AI app to try again soon. */\nconst RETRY_AFTER_S = \"5\";\n\n/** Request headers forwarded to the tab (protocol: tunnelled MCP requests). */\nconst FORWARDED_HEADERS = new Set([\n\t\"content-type\",\n\t\"accept\",\n\t\"mcp-method\",\n\t\"mcp-name\",\n\t\"traceparent\",\n\t\"tracestate\",\n]);\n\nexport interface CreateRelayOptions\n\textends Omit<Partial<RelayConfig>, \"store\"> {\n\t/**\n\t * The token store: a `TokenStore` instance (e.g. Prisma, Redis), or the name\n\t * of a built-in one (`\"sqlite\"`, the default, at `sqlitePath`; `\"memory\"`).\n\t */\n\tstore?: StoreKind | TokenStore;\n\t/** Logger; defaults to JSON lines on stdout at `logLevel`. */\n\tlogger?: Logger;\n}\n\n/**\n * The public URL in development without `publicUrl`: the loopback address the\n * relay listens on, so the addresses match the URL the page was given;\n * `localhost` for any other host (`0.0.0.0`, `::`, a name).\n */\nexport function developmentUrl(host: string, port: number): string {\n\tif (host === \"127.0.0.1\") return `http://127.0.0.1:${port}`;\n\tif (host === \"::1\" || host === \"[::1]\") return `http://[::1]:${port}`;\n\treturn `http://localhost:${port}`;\n}\n\nexport interface Relay {\n\t/** The external origin (issuer and address base). */\n\treadonly url: string;\n\t/** The port the server listens on. */\n\treadonly port: number;\n\treadonly config: RelayConfig;\n\t/** The MCP address a page with this secret gets. */\n\taddressFor(secret: string): string;\n\t/** Number of connected tab sessions. */\n\treadonly sessionCount: number;\n\t/** Close all sessions (bye `shutdown`, close 4004) and stop the server. */\n\tstop(): Promise<void>;\n}\n\n/** What `/healthz` says about the token store (DEC-099). */\ntype StoreHealth = \"ok\" | \"store_unavailable\" | \"store_timeout\";\n/** How long the health probe waits for the store. */\nconst HEALTH_STORE_TIMEOUT_MS = 1000;\n/** How long a probe's answer is reused. */\nconst HEALTH_REUSE_MS = 1000;\n/** While an earlier read hangs, how long before a new one is tried. */\nconst HEALTH_HUNG_RETRY_MS = 30_000;\n/** A store key no token ever has (a token's hash is of `tmcp_…`). */\nconst HEALTH_PROBE_HASH = hashToken(\"tab-mcp /healthz probe\");\n\n/**\n * The `a` parameter of a `/tab` query, read from the raw text: `undefined`\n * when absent, `false` when malformed or repeated. Only the literal id\n * counts, never a percent-encoded one, so the relay agrees with a load\n * balancer that matches the URL as sent (review of PR 38).\n */\nfunction rawAddressParam(search: string): string | undefined | false {\n\tlet found: string | undefined;\n\tfor (const pair of search.replace(/^\\?/, \"\").split(\"&\")) {\n\t\tif (pair === \"\") continue;\n\t\tconst eq = pair.indexOf(\"=\");\n\t\tconst rawName = eq < 0 ? pair : pair.slice(0, eq);\n\t\tlet name: string;\n\t\ttry {\n\t\t\tname = decodeURIComponent(rawName.replaceAll(\"+\", \" \"));\n\t\t} catch {\n\t\t\tcontinue; // not ours: other parameters are ignored\n\t\t}\n\t\tif (name !== \"a\") continue;\n\t\tconst value = eq < 0 ? \"\" : pair.slice(eq + 1);\n\t\tif (rawName !== \"a\" || found !== undefined || !isAddressId(value)) {\n\t\t\treturn false;\n\t\t}\n\t\tfound = value;\n\t}\n\treturn found;\n}\n\ninterface WsData {\n\tip: string;\n\torigin: string;\n\tsite: Site;\n\t/** The address id the URL named (`/tab?a=`, DEC-098). */\n\taddress?: string;\n\tsession?: TabSession;\n}\n\n/**\n * Test-only relaxations, never reachable from configuration or the package's\n * public API (`createTestRelay` is not exported from the package): CIMD\n * fetches from loopback test servers, shorter code lifetimes.\n */\nexport interface RelayTestHooks extends AuthorizationTestMode {\n\tpairingCodeTtlMs?: number;\n\t/** Keys for tab assertions instead of fetching the JWKS URL. */\n\tassertionKeys?: KeySource;\n\t/** A smaller relay-wide replay cache for tab assertions. */\n\tmaxSeenAssertions?: number;\n\t/** How long `/healthz` waits before a new read while one hangs. */\n\thealthRetryMs?: number;\n}\n\ninterface Body {\n\tvalue: Record<string, unknown>;\n\ttext: string;\n}\n\nfunction json(\n\tstatus: number,\n\tvalue: unknown,\n\theaders?: Record<string, string>,\n): Response {\n\tconst h = new Headers(headers);\n\th.set(\"content-type\", \"application/json\");\n\treturn new Response(JSON.stringify(value), { status, headers: h });\n}\n\nfunction rpcError(\n\tstatus: number,\n\trpcId: RpcId | undefined,\n\tcode: number,\n\tmessage: string,\n\theaders: Record<string, string>,\n\tdata?: unknown,\n): Response {\n\treturn new Response(rpcErrorBody(rpcId, code, message, data), {\n\t\tstatus,\n\t\theaders: { ...headers, \"content-type\": \"application/json\" },\n\t});\n}\n\nfunction rpcIdOf(body: Record<string, unknown>): RpcId | undefined {\n\tif (!(\"id\" in body)) return undefined;\n\tconst id = body[\"id\"];\n\treturn typeof id === \"string\" || typeof id === \"number\" ? id : null;\n}\n\n/** A loggable MCP method name (attacker-controlled input, so constrained). */\nfunction methodForLog(body: Record<string, unknown>): string {\n\tconst m = body[\"method\"];\n\treturn typeof m === \"string\" && /^[A-Za-z0-9_/.-]{1,64}$/.test(m) ? m : \"?\";\n}\n\nfunction metaVersion(body: Record<string, unknown>): unknown {\n\tconst params = body[\"params\"];\n\tif (typeof params !== \"object\" || params === null) return undefined;\n\tconst meta = (params as Record<string, unknown>)[\"_meta\"];\n\tif (typeof meta !== \"object\" || meta === null) return undefined;\n\treturn (meta as Record<string, unknown>)[PROTOCOL_VERSION_META_KEY];\n}\n\nconst INDEX_HTML = `<!doctype html>\n<html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>tab-mcp relay</title>\n<link rel=\"icon\" href=\"/favicon.ico\" sizes=\"32x32\"><link rel=\"icon\" href=\"/favicon.svg\" type=\"image/svg+xml\">\n<link rel=\"apple-touch-icon\" href=\"/apple-touch-icon.png\"></head>\n<body><h1>tab-mcp relay</h1>\n<p>This server relays AI apps' MCP requests to tools offered by web pages open in a browser tab.\nIt does not log request contents. Source and documentation:\n<a href=\"https://github.com/ractive/tab-mcp\">github.com/ractive/tab-mcp</a>.</p>\n</body></html>`;\n\nfunction icon(body: string | Uint8Array<ArrayBuffer>, type: string): Response {\n\treturn new Response(body, {\n\t\theaders: {\n\t\t\t\"content-type\": type,\n\t\t\t\"content-security-policy\": \"default-src 'none'\",\n\t\t\t\"x-content-type-options\": \"nosniff\",\n\t\t\t\"cache-control\": \"public, max-age=86400\",\n\t\t},\n\t});\n}\n\n/** Start a relay. Returns once the server listens. */\nexport function createRelay(options: CreateRelayOptions = {}): Relay {\n\treturn startRelay(options, undefined);\n}\n\n/**\n * A relay with test hooks. Only for this repository's tests: it refuses to\n * start outside development and logs a warning.\n */\nexport function createTestRelay(\n\toptions: CreateRelayOptions,\n\thooks: RelayTestHooks,\n): Relay {\n\tif (options.development !== true) {\n\t\tthrow new Error(\"test hooks need development: true\");\n\t}\n\treturn startRelay(options, hooks);\n}\n\nfunction startRelay(\n\toptions: CreateRelayOptions,\n\thooks: RelayTestHooks | undefined,\n): Relay {\n\tconst { store: storeOption, logger: givenLogger, ...partial } = options;\n\tconst tokenStore = typeof storeOption === \"object\" ? storeOption : undefined;\n\tconst config = resolveConfig(\n\t\ttypeof storeOption === \"string\"\n\t\t\t? { ...partial, store: storeOption }\n\t\t\t: partial,\n\t);\n\tconst logger = givenLogger ?? createLogger(config.logLevel);\n\tconst ownsStore = tokenStore === undefined;\n\tconst store: TokenStore =\n\t\ttokenStore ??\n\t\t(config.store === \"sqlite\"\n\t\t\t? new SqliteTokenStore(config.sqlitePath)\n\t\t\t: new MemoryTokenStore());\n\n\tconst sessions = new Map<string, TabSession>();\n\tconst allSessions = new Set<TabSession>();\n\tconst perIp = new Map<string, number>();\n\t/** Requests waiting for their page, per address (DEC-074). */\n\tconst waiters = new Map<string, Set<(s: TabSession | undefined) => void>>();\n\tlet waitingCount = 0;\n\t/**\n\t * Each address's last session: its app name, for \"open <name>\", and its\n\t * site's concurrency limit, for requests that wait for the page.\n\t */\n\tconst lastSeen = new Map<\n\t\tstring,\n\t\t{ appName: string | undefined; maxConcurrentRequests: number }\n\t>();\n\tlet claudeWorkarounds = 0;\n\tlet publicUrl = config.publicUrl ?? \"\";\n\t/** Tab assertions (identity binding, DEC-076), when configured. */\n\tconst assertions =\n\t\tconfig.oidc === undefined\n\t\t\t? undefined\n\t\t\t: new TabAssertionVerifier(\n\t\t\t\t\tconfig.oidc,\n\t\t\t\t\thooks?.assertionKeys,\n\t\t\t\t\thooks?.maxSeenAssertions,\n\t\t\t\t);\n\n\tconst corsHeaders: Record<string, string> = config.cors\n\t\t? {\n\t\t\t\t\"access-control-allow-origin\": \"*\",\n\t\t\t\t\"access-control-expose-headers\":\n\t\t\t\t\t\"www-authenticate, mcp-protocol-version\",\n\t\t\t}\n\t\t: {};\n\n\t/** Tombstones outlive every token issued before the revocation (DEC-063). */\n\tconst revokeOptions = () => {\n\t\tconst now = Date.now();\n\t\treturn { now, keepUntil: now + config.grantLifetimeMs };\n\t};\n\n\tfunction grantRemoved(\n\t\taddressId: string,\n\t\tgrantId: string,\n\t\treason: \"revoked\" | \"reuse_detected\" | \"expired\",\n\t): void {\n\t\tconst session = sessions.get(addressId);\n\t\tif (session?.ready)\n\t\t\tsession.send({ type: \"grant.removed\", grantId, reason });\n\t}\n\n\tconst host = {\n\t\tconfig,\n\t\tlogger,\n\t\tstore,\n\t\t...(hooks?.pairingCodeTtlMs === undefined\n\t\t\t? {}\n\t\t\t: { pairingCodeTtlMs: hooks.pairingCodeTtlMs }),\n\t\tpublicUrl: () => publicUrl,\n\t\tasync revokeGrant(session: TabSession, grantId: string): Promise<boolean> {\n\t\t\tconst addressId = session.addressId;\n\t\t\tif (addressId === undefined) return false;\n\t\t\tconst grants = await store.listGrants(addressId, Date.now());\n\t\t\tif (!grants.some((g) => g.grantId === grantId)) return false;\n\t\t\tawait store.revoke({ grantId }, revokeOptions());\n\t\t\tlogger.info(\"oauth.revoked\", {\n\t\t\t\taddress: shortAddress(addressId),\n\t\t\t\tby: \"tab\",\n\t\t\t});\n\t\t\tgrantRemoved(addressId, grantId, \"revoked\");\n\t\t\treturn true;\n\t\t},\n\t\tasync revokeAll(session: TabSession): Promise<void> {\n\t\t\tconst addressId = session.addressId;\n\t\t\tif (addressId === undefined) return;\n\t\t\tconst grants = await store.listGrants(addressId, Date.now());\n\t\t\tawait store.revoke({ addressId }, revokeOptions());\n\t\t\tlogger.info(\"oauth.revoked_all\", {\n\t\t\t\taddress: shortAddress(addressId),\n\t\t\t\tgrants: grants.length,\n\t\t\t});\n\t\t\tfor (const g of grants) grantRemoved(addressId, g.grantId, \"revoked\");\n\t\t},\n\t\t/**\n\t\t * Called one at a time per session (the session chains `identity` and\n\t\t * `identity.logout`), so a slow check never lands after a later sign-out.\n\t\t * Always answers the tab once: `identity.bound` or `identity_refused`.\n\t\t */\n\t\tasync bindIdentity(\n\t\t\tsession: TabSession,\n\t\t\tassertion: string,\n\t\t\tround?: number,\n\t\t): Promise<void> {\n\t\t\tconst addressId = session.addressId;\n\t\t\tif (addressId === undefined) return;\n\t\t\tconst echo = round === undefined ? {} : { round };\n\t\t\t// The page waits for this answer; a dropped one (backpressure) is\n\t\t\t// logged, and the page's timeout gives up on it (the round tells a late\n\t\t\t// answer from a new one).\n\t\t\tconst answer = (message: RelayMessage) => {\n\t\t\t\tif (!session.send(message)) {\n\t\t\t\t\tlogger.warn(\"identity.answer_dropped\", {\n\t\t\t\t\t\taddress: shortAddress(addressId),\n\t\t\t\t\t\ttype: message.type,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t};\n\t\t\tconst refuse = (reason: string, message: string) => {\n\t\t\t\t// Fail closed: a refused assertion also ends an earlier binding,\n\t\t\t\t// and the requests made under it. When the relay could not check\n\t\t\t\t// (keys or store unavailable), the AI app is told to try again,\n\t\t\t\t// not to sign in again, until an assertion is checked.\n\t\t\t\tconst transient = TRANSIENT_REFUSALS.has(reason);\n\t\t\t\tsession.identityUnchecked = transient;\n\t\t\t\tif (session.identity !== undefined) {\n\t\t\t\t\tsession.identity = undefined;\n\t\t\t\t\tif (transient) {\n\t\t\t\t\t\tfailPending(session, UNCHECKED_TEXT, true);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tfailPending(session, \"The page's user is no longer signed in.\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tlogger.info(\"identity.refused\", {\n\t\t\t\t\taddress: shortAddress(addressId),\n\t\t\t\t\treason,\n\t\t\t\t});\n\t\t\t\tanswer({ type: \"error\", code: \"identity_refused\", message, ...echo });\n\t\t\t};\n\t\t\tif (session.site.pairing !== \"oidc\" || assertions === undefined) {\n\t\t\t\trefuse(\"not_oidc\", \"this page's site does not use identity binding\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tlet key: string;\n\t\t\ttry {\n\t\t\t\tkey = await assertions.verify(\n\t\t\t\t\tassertion,\n\t\t\t\t\t`${publicUrl}/a/${addressId}/mcp`,\n\t\t\t\t);\n\t\t\t} catch (error) {\n\t\t\t\trefuse(\n\t\t\t\t\terror instanceof JwtError ? error.reason : \"error\",\n\t\t\t\t\t`the identity assertion was refused: ${error instanceof JwtError ? error.message : \"the relay could not check it\"}`,\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (!session.ready) return; // closed meanwhile\n\t\t\tconst changed = session.identity !== key;\n\t\t\tif (changed && session.identity !== undefined) {\n\t\t\t\t// Another person now: nothing more for the old one, before any I/O.\n\t\t\t\tsession.identity = undefined;\n\t\t\t\tfailPending(session, \"The page's user changed.\");\n\t\t\t}\n\t\t\t// Grants made for someone else (or with a code) end now (DEC-077).\n\t\t\tlet revoked = 0;\n\t\t\ttry {\n\t\t\t\tconst grants = await store.listGrants(addressId, Date.now());\n\t\t\t\tfor (const g of grants.filter((g) => g.subject !== key)) {\n\t\t\t\t\tawait store.revoke({ grantId: g.grantId }, revokeOptions());\n\t\t\t\t\tgrantRemoved(addressId, g.grantId, \"revoked\");\n\t\t\t\t\trevoked++;\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tlogger.error(\"store.error\", {\n\t\t\t\t\top: \"identity\",\n\t\t\t\t\terror: errorName(error),\n\t\t\t\t});\n\t\t\t\trefuse(\"store_unavailable\", \"the relay cannot check grants right now\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (!session.ready) return;\n\t\t\tsession.identity = key;\n\t\t\tsession.identityUnchecked = false;\n\t\t\tanswer({ type: \"identity.bound\", ...echo });\n\t\t\tlogger.info(\"identity.bound\", {\n\t\t\t\taddress: shortAddress(addressId),\n\t\t\t\tchanged,\n\t\t\t\trevoked,\n\t\t\t});\n\t\t\thost.ready(session); // requests waiting for a signed-in page go on\n\t\t},\n\t\tasync signOut(session: TabSession): Promise<void> {\n\t\t\tconst addressId = session.addressId;\n\t\t\tif (addressId === undefined) return;\n\t\t\tif (session.site.pairing !== \"oidc\") {\n\t\t\t\tsession.send({\n\t\t\t\t\ttype: \"error\",\n\t\t\t\t\tcode: \"identity_refused\",\n\t\t\t\t\tmessage: \"this page's site does not use identity binding\",\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// First the binding and the requests in flight: they end even if the\n\t\t\t// store fails below.\n\t\t\tsession.identity = undefined;\n\t\t\tsession.identityUnchecked = false;\n\t\t\tfailPending(session, \"The page's user signed out.\");\n\t\t\ttry {\n\t\t\t\tconst grants = await store.listGrants(addressId, Date.now());\n\t\t\t\tawait store.revoke({ addressId }, revokeOptions());\n\t\t\t\tlogger.info(\"identity.signed_out\", {\n\t\t\t\t\taddress: shortAddress(addressId),\n\t\t\t\t\tgrants: grants.length,\n\t\t\t\t});\n\t\t\t\tfor (const g of grants) grantRemoved(addressId, g.grantId, \"revoked\");\n\t\t\t} catch (error) {\n\t\t\t\t// The grants stay, but work only for a page bound to their person.\n\t\t\t\tlogger.error(\"store.error\", { op: \"signOut\", error: errorName(error) });\n\t\t\t}\n\t\t},\n\t\tclaimAddress(session: TabSession): TabSession | undefined {\n\t\t\tconst id = session.addressId;\n\t\t\tif (id === undefined) return undefined;\n\t\t\tconst previous = sessions.get(id);\n\t\t\tsessions.set(id, session);\n\t\t\treturn previous === session ? undefined : previous;\n\t\t},\n\t\treleased(session: TabSession): void {\n\t\t\tallSessions.delete(session);\n\t\t\tif (\n\t\t\t\tsession.addressId !== undefined &&\n\t\t\t\tsessions.get(session.addressId) === session\n\t\t\t) {\n\t\t\t\tsessions.delete(session.addressId);\n\t\t\t}\n\t\t\treleaseIp(session.ip);\n\t\t},\n\t\tready(session: TabSession): void {\n\t\t\tconst id = session.addressId;\n\t\t\tif (id === undefined) return;\n\t\t\tconst appName = session.appName ?? lastSeen.get(id)?.appName;\n\t\t\tlastSeen.delete(id);\n\t\t\tlastSeen.set(id, {\n\t\t\t\tappName,\n\t\t\t\tmaxConcurrentRequests: session.limits.maxConcurrentRequests,\n\t\t\t});\n\t\t\t// Bounded: forget the oldest addresses first.\n\t\t\tif (lastSeen.size > config.maxSessions * 2) {\n\t\t\t\tconst oldest = lastSeen.keys().next().value;\n\t\t\t\tif (oldest !== undefined) lastSeen.delete(oldest);\n\t\t\t}\n\t\t\tconst waiting = waiters.get(id);\n\t\t\tif (waiting !== undefined) for (const wake of [...waiting]) wake(session);\n\t\t},\n\t};\n\n\t/**\n\t * End a session's requests in flight with `-31009`; the page is told to stop\n\t * them. 401 with `WWW-Authenticate`, or, when the relay could not check\n\t * (`transient`), 503 with `Retry-After`, so AI apps keep their tokens.\n\t */\n\tfunction failPending(\n\t\tsession: TabSession,\n\t\tmessage: string,\n\t\ttransient = false,\n\t): void {\n\t\tconst addressId = session.addressId;\n\t\tconst headers: Record<string, string> = transient\n\t\t\t? { \"retry-after\": RETRY_AFTER_S }\n\t\t\t: addressId === undefined\n\t\t\t\t? {}\n\t\t\t\t: { \"www-authenticate\": wwwAuthenticate(addressId, true) };\n\t\tfor (const exchange of [...session.pending.values()]) {\n\t\t\texchange.fail(\n\t\t\t\ttransient ? 503 : 401,\n\t\t\t\tRelayErrorCode.signedOut,\n\t\t\t\tmessage,\n\t\t\t\ttrue,\n\t\t\t\theaders,\n\t\t\t);\n\t\t}\n\t}\n\n\tfunction readySession(\n\t\taddressId: string,\n\t\tusable: (s: TabSession) => boolean = () => true,\n\t): TabSession | undefined {\n\t\tconst s = sessions.get(addressId);\n\t\treturn s?.ready === true && usable(s) ? s : undefined;\n\t}\n\n\t/**\n\t * Whether a session can take a grant's requests: a grant with a subject, or\n\t * any grant on an `oidc` site, needs a page that proved who its user is\n\t * (DEC-077). Whether it is the same person is checked after the wait.\n\t */\n\tfunction usableFor(subject: string | undefined) {\n\t\treturn (s: TabSession) =>\n\t\t\t(subject === undefined && s.site.pairing === \"code\") ||\n\t\t\ts.identity !== undefined;\n\t}\n\n\t/**\n\t * The page's ready session, waiting up to `waitMs` for it (DEC-074).\n\t * `undefined` when it does not come in time or too many requests wait\n\t * already; `\"aborted\"` when the AI app gave up.\n\t */\n\tfunction waitForPage(\n\t\taddressId: string,\n\t\tsignal: AbortSignal,\n\t\twaitMs: number,\n\t\tusable: (s: TabSession) => boolean,\n\t): Promise<TabSession | undefined | \"aborted\"> {\n\t\tconst now = readySession(addressId, usable);\n\t\tif (now !== undefined) return Promise.resolve(now);\n\t\tif (waitMs === 0 || closing) {\n\t\t\treturn Promise.resolve(undefined);\n\t\t}\n\t\tconst set = waiters.get(addressId) ?? new Set();\n\t\t// A waiting request holds its body and a socket like a forwarded one: at\n\t\t// most maxConcurrentRequests per address (its site's, as far as known,\n\t\t// so none gets 429 when the page comes), maxSessions in all.\n\t\tconst limit =\n\t\t\tsessions.get(addressId)?.limits.maxConcurrentRequests ??\n\t\t\tlastSeen.get(addressId)?.maxConcurrentRequests ??\n\t\t\tconfig.maxConcurrentRequests;\n\t\tif (set.size >= limit || waitingCount >= config.maxSessions) {\n\t\t\treturn Promise.resolve(undefined);\n\t\t}\n\t\treturn new Promise((resolve) => {\n\t\t\tlet timer: Timer | undefined;\n\t\t\tlet settled = false;\n\t\t\tconst done = (result: TabSession | undefined | \"aborted\") => {\n\t\t\t\tif (settled) return;\n\t\t\t\tsettled = true;\n\t\t\t\tclearTimeout(timer);\n\t\t\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\t\t\tset.delete(wake);\n\t\t\t\twaitingCount--;\n\t\t\t\tif (set.size === 0 && waiters.get(addressId) === set) {\n\t\t\t\t\twaiters.delete(addressId);\n\t\t\t\t}\n\t\t\t\tresolve(result);\n\t\t\t};\n\t\t\tconst wake = (session: TabSession | undefined) => {\n\t\t\t\tif (session === undefined || usable(session)) done(session);\n\t\t\t};\n\t\t\tconst onAbort = () => done(\"aborted\");\n\t\t\tset.add(wake);\n\t\t\twaiters.set(addressId, set);\n\t\t\twaitingCount++;\n\t\t\ttimer = setTimeout(() => done(undefined), waitMs);\n\t\t\tsignal.addEventListener(\"abort\", onAbort, { once: true });\n\t\t\tif (signal.aborted) onAbort();\n\t\t});\n\t}\n\n\tfunction releaseIp(ip: string): void {\n\t\tconst n = (perIp.get(ip) ?? 1) - 1;\n\t\tif (n <= 0) perIp.delete(ip);\n\t\telse perIp.set(ip, n);\n\t}\n\n\tfunction clientIp(req: Request, server: Server<WsData>): string {\n\t\tif (config.trustProxy === \"x-real-ip\") {\n\t\t\tconst v = req.headers.get(\"x-real-ip\")?.trim();\n\t\t\tif (v) return v;\n\t\t} else if (config.trustProxy === \"x-forwarded-for\") {\n\t\t\tconst parts = (req.headers.get(\"x-forwarded-for\") ?? \"\")\n\t\t\t\t.split(\",\")\n\t\t\t\t.map((s) => s.trim())\n\t\t\t\t.filter((s) => s !== \"\");\n\t\t\tconst last = parts.at(-1);\n\t\t\tif (last !== undefined) return last;\n\t\t}\n\t\treturn server.requestIP(req)?.address ?? \"unknown\";\n\t}\n\n\tconst auth = new AuthorizationServer(\n\t\t{\n\t\t\tconfig,\n\t\t\tlogger,\n\t\t\tstore,\n\t\t\tpublicUrl: () => publicUrl,\n\t\t\tsession: (addressId) => {\n\t\t\t\tconst s = sessions.get(addressId);\n\t\t\t\treturn s?.ready === true ? s : undefined;\n\t\t\t},\n\t\t\tgrantAdded: (addressId: string, grant: GrantInfo) => {\n\t\t\t\tconst session = sessions.get(addressId);\n\t\t\t\tif (session?.ready) session.send({ type: \"grant.added\", grant });\n\t\t\t},\n\t\t\tgrantRemoved: (addressId, grantId, reason) =>\n\t\t\t\tgrantRemoved(\n\t\t\t\t\taddressId,\n\t\t\t\t\tgrantId,\n\t\t\t\t\treason as \"revoked\" | \"reuse_detected\" | \"expired\",\n\t\t\t\t),\n\t\t},\n\t\thooks,\n\t);\n\tif (hooks !== undefined) {\n\t\tlogger.warn(\"relay.test_hooks\", {\n\t\t\tmessage: \"test hooks are on: never use this relay outside tests\",\n\t\t});\n\t}\n\n\tfunction handleTab(\n\t\treq: Request,\n\t\tserver: Server<WsData>,\n\t): Response | undefined {\n\t\tconst origin = req.headers.get(\"origin\");\n\t\tconst site = siteFor(config, origin);\n\t\tif (site === undefined) {\n\t\t\tlogger.info(\"tab.refused\", { reason: \"origin\" });\n\t\t\treturn json(403, { error: \"origin_not_allowed\" });\n\t\t}\n\t\tconst offered = (req.headers.get(\"sec-websocket-protocol\") ?? \"\")\n\t\t\t.split(\",\")\n\t\t\t.map((s) => s.trim());\n\t\tif (!offered.includes(SUBPROTOCOL)) {\n\t\t\treturn json(426, {\n\t\t\t\terror: \"unsupported_protocol\",\n\t\t\t\tsupported: [SUBPROTOCOL],\n\t\t\t});\n\t\t}\n\t\t// The address id for a load balancer to route by (DEC-098). Optional\n\t\t// (libraries before 0.8 do not send it); checked against the secret\n\t\t// in hello. Never the secret itself: URLs end up in access logs.\n\t\tconst address = rawAddressParam(new URL(req.url).search);\n\t\tif (address === false) {\n\t\t\tlogger.info(\"tab.refused\", { reason: \"bad_address\" });\n\t\t\treturn json(400, { error: \"bad_address\" });\n\t\t}\n\t\tconst realIp = clientIp(req, server);\n\t\t// Counted per /64 for IPv6 (DEC-061): one subscriber owns a whole /64.\n\t\tconst ip = ipPrefixKey(realIp);\n\t\tif (allSessions.size >= config.maxSessions) {\n\t\t\tlogger.warn(\"tab.refused\", { reason: \"max_sessions\" });\n\t\t\treturn json(429, { error: \"too_many_sessions\" });\n\t\t}\n\t\tconst count = perIp.get(ip) ?? 0;\n\t\tif (count >= config.maxSessionsPerIp) {\n\t\t\tlogger.warn(\"tab.refused\", {\n\t\t\t\treason: \"max_sessions_per_ip\",\n\t\t\t\tip: truncateIp(realIp),\n\t\t\t});\n\t\t\treturn json(429, { error: \"too_many_sessions\" });\n\t\t}\n\t\tperIp.set(ip, count + 1);\n\t\tconst ok = server.upgrade(req, {\n\t\t\tdata: {\n\t\t\t\tip,\n\t\t\t\torigin: origin ?? \"\",\n\t\t\t\tsite,\n\t\t\t\t...(address === undefined ? {} : { address }),\n\t\t\t},\n\t\t\theaders: { \"Sec-WebSocket-Protocol\": SUBPROTOCOL },\n\t\t});\n\t\tif (ok) return undefined;\n\t\treleaseIp(ip);\n\t\treturn json(400, { error: \"websocket_upgrade_expected\" });\n\t}\n\n\tfunction wwwAuthenticate(id: string, presented: boolean): string {\n\t\tconst metadata = `${publicUrl}/.well-known/oauth-protected-resource/a/${id}/mcp`;\n\t\tconst error = presented ? ', error=\"invalid_token\"' : \"\";\n\t\treturn `Bearer resource_metadata=\"${metadata}\", scope=\"tools\"${error}`;\n\t}\n\n\tasync function authenticate(\n\t\treq: Request,\n\t\taddressId: string,\n\t): Promise<TokenRecord | \"missing\" | \"invalid\" | \"unavailable\"> {\n\t\tconst header = req.headers.get(\"authorization\");\n\t\tif (header === null) return \"missing\";\n\t\tconst match = /^Bearer ([A-Za-z0-9._~+/-]+=*)$/i.exec(header.trim());\n\t\tconst token = match?.[1];\n\t\tif (token === undefined) return \"invalid\";\n\t\tlet record: TokenRecord | undefined;\n\t\ttry {\n\t\t\trecord = await store.get(hashToken(token));\n\t\t} catch (error) {\n\t\t\tlogger.error(\"store.error\", { op: \"get\", error: errorName(error) });\n\t\t\treturn \"unavailable\";\n\t\t}\n\t\tif (\n\t\t\trecord === undefined ||\n\t\t\trecord.kind !== \"access\" ||\n\t\t\trecord.addressId !== addressId ||\n\t\t\trecord.expiresAt <= Date.now() ||\n\t\t\t!record.scopes.includes(\"tools\")\n\t\t) {\n\t\t\treturn \"invalid\";\n\t\t}\n\t\treturn record;\n\t}\n\n\tasync function readBody(\n\t\treq: Request,\n\t\tmaxBytes: number,\n\t): Promise<Body | Response> {\n\t\tconst length = Number(req.headers.get(\"content-length\") ?? \"0\");\n\t\tif (length > maxBytes) {\n\t\t\treturn rpcError(\n\t\t\t\t413,\n\t\t\t\tundefined,\n\t\t\t\t-32600,\n\t\t\t\t\"Request body too large.\",\n\t\t\t\tcorsHeaders,\n\t\t\t);\n\t\t}\n\t\tlet bytes: ArrayBuffer;\n\t\ttry {\n\t\t\t// Bun's idle timeout does not fire while a body is being read, so a\n\t\t\t// client that stalls mid-body would hold the socket; our own deadline\n\t\t\t// answers 408 and closes the connection.\n\t\t\tconst reading = req.arrayBuffer();\n\t\t\treading.catch(() => {}); // rejects once the socket is closed\n\t\t\tlet timer: Timer | undefined;\n\t\t\tconst deadline = new Promise<\"timeout\">((resolve) => {\n\t\t\t\ttimer = setTimeout(() => resolve(\"timeout\"), config.bodyTimeoutMs);\n\t\t\t});\n\t\t\tlet result: ArrayBuffer | \"timeout\";\n\t\t\ttry {\n\t\t\t\tresult = await Promise.race([reading, deadline]);\n\t\t\t} finally {\n\t\t\t\tclearTimeout(timer);\n\t\t\t}\n\t\t\tif (result === \"timeout\") {\n\t\t\t\treturn rpcError(\n\t\t\t\t\t408,\n\t\t\t\t\tundefined,\n\t\t\t\t\t-32600,\n\t\t\t\t\t\"The request body did not arrive in time.\",\n\t\t\t\t\t{ ...corsHeaders, connection: \"close\" },\n\t\t\t\t);\n\t\t\t}\n\t\t\tbytes = result;\n\t\t} catch {\n\t\t\treturn rpcError(\n\t\t\t\t400,\n\t\t\t\tundefined,\n\t\t\t\t-32700,\n\t\t\t\t\"Could not read the request body.\",\n\t\t\t\tcorsHeaders,\n\t\t\t);\n\t\t}\n\t\tif (bytes.byteLength > maxBytes) {\n\t\t\treturn rpcError(\n\t\t\t\t413,\n\t\t\t\tundefined,\n\t\t\t\t-32600,\n\t\t\t\t\"Request body too large.\",\n\t\t\t\tcorsHeaders,\n\t\t\t);\n\t\t}\n\t\tlet text: string;\n\t\tlet value: unknown;\n\t\ttry {\n\t\t\ttext = new TextDecoder(\"utf-8\", { fatal: true }).decode(bytes);\n\t\t\tvalue = JSON.parse(text);\n\t\t} catch {\n\t\t\treturn rpcError(400, undefined, -32700, \"Parse error.\", corsHeaders);\n\t\t}\n\t\tif (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n\t\t\treturn rpcError(\n\t\t\t\t400,\n\t\t\t\tundefined,\n\t\t\t\t-32600,\n\t\t\t\t\"The body must be one JSON-RPC message (no batches).\",\n\t\t\t\tcorsHeaders,\n\t\t\t);\n\t\t}\n\t\treturn { value: value as Record<string, unknown>, text };\n\t}\n\n\t/** DEC-033 / DEC-040: the protocol-version pre-check. */\n\tfunction checkVersion(\n\t\treq: Request,\n\t\tbody: Record<string, unknown>,\n\t\trpcId: RpcId | undefined,\n\t): Response | { forwardVersion: string } {\n\t\tconst header = req.headers.get(\"mcp-protocol-version\");\n\t\t// An initialize in a 2026-07-28 envelope is an ordinary request for a\n\t\t// method the tab does not have: forwarded, so the tab answers 404 and\n\t\t// -32601 like any removed method.\n\t\tif (\n\t\t\tbody[\"method\"] === \"initialize\" &&\n\t\t\t!(\n\t\t\t\theader === MCP_PROTOCOL_VERSION &&\n\t\t\t\tmetaVersion(body) === MCP_PROTOCOL_VERSION\n\t\t\t)\n\t\t) {\n\t\t\tconst params = body[\"params\"] as Record<string, unknown> | undefined;\n\t\t\tconst requested =\n\t\t\t\ttypeof params?.[\"protocolVersion\"] === \"string\"\n\t\t\t\t\t? params[\"protocolVersion\"]\n\t\t\t\t\t: (header ?? null);\n\t\t\treturn rpcError(\n\t\t\t\t400,\n\t\t\t\trpcId,\n\t\t\t\t-32022,\n\t\t\t\t`Unsupported protocol version: ${String(requested)}. This server supports only ${MCP_PROTOCOL_VERSION}.`,\n\t\t\t\tcorsHeaders,\n\t\t\t\t{ supported: [MCP_PROTOCOL_VERSION], requested },\n\t\t\t);\n\t\t}\n\t\tif (header === MCP_PROTOCOL_VERSION) return { forwardVersion: header };\n\t\tif (\n\t\t\theader === CLAUDE_MISMATCHED_HEADER &&\n\t\t\tmetaVersion(body) === MCP_PROTOCOL_VERSION\n\t\t) {\n\t\t\t// Logged once per process; the running count is in /healthz.\n\t\t\tif (++claudeWorkarounds === 1) {\n\t\t\t\tlogger.info(\"mcp.claude_header_workaround\", {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t\"accepting MCP-Protocol-Version 2025-11-25 with a 2026-07-28 body (claude-code #93290); counted in /healthz\",\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn { forwardVersion: MCP_PROTOCOL_VERSION };\n\t\t}\n\t\tif (header === null) {\n\t\t\treturn rpcError(\n\t\t\t\t400,\n\t\t\t\trpcId,\n\t\t\t\t-32020,\n\t\t\t\t\"Missing MCP-Protocol-Version header.\",\n\t\t\t\tcorsHeaders,\n\t\t\t);\n\t\t}\n\t\treturn rpcError(\n\t\t\t400,\n\t\t\trpcId,\n\t\t\t-32022,\n\t\t\t`Unsupported protocol version: ${header}. This server supports only ${MCP_PROTOCOL_VERSION}.`,\n\t\t\tcorsHeaders,\n\t\t\t{ supported: [MCP_PROTOCOL_VERSION], requested: header },\n\t\t);\n\t}\n\n\tasync function handleMcp(\n\t\treq: Request,\n\t\tserver: Server<WsData>,\n\t\taddressId: string,\n\t): Promise<Response> {\n\t\tconst started = Date.now();\n\t\tconst logDone = (\n\t\t\tstatus: number,\n\t\t\tmethod: string,\n\t\t\textra: Record<string, string | number> = {},\n\t\t) =>\n\t\t\tlogger.info(\"mcp.request\", {\n\t\t\t\taddress: shortAddress(addressId),\n\t\t\t\tmethod,\n\t\t\t\tstatus,\n\t\t\t\tdurationMs: Date.now() - started,\n\t\t\t\t...extra,\n\t\t\t});\n\n\t\t// The page's own limit when it is connected; else the instance's (the\n\t\t// request then ends with 401 or 503 anyway).\n\t\tconst body = await readBody(\n\t\t\treq,\n\t\t\tsessions.get(addressId)?.limits.maxMessageBytes ?? config.maxMessageBytes,\n\t\t);\n\t\tif (body instanceof Response) {\n\t\t\tlogDone(body.status, \"?\");\n\t\t\treturn body;\n\t\t}\n\t\tconst rpcId = rpcIdOf(body.value);\n\t\tconst method = methodForLog(body.value);\n\n\t\tconst version = checkVersion(req, body.value, rpcId);\n\t\tif (version instanceof Response) {\n\t\t\tlogDone(version.status, method);\n\t\t\treturn version;\n\t\t}\n\n\t\tconst auth = await authenticate(req, addressId);\n\t\tif (auth === \"unavailable\") {\n\t\t\t// Not the token's fault: a 401 would make clients drop valid tokens.\n\t\t\tlogDone(503, method, { reason: \"store_unavailable\" });\n\t\t\treturn rpcError(\n\t\t\t\t503,\n\t\t\t\trpcId,\n\t\t\t\tRelayErrorCode.storeUnavailable,\n\t\t\t\t\"The relay cannot check tokens right now. Try again shortly.\",\n\t\t\t\t{ ...corsHeaders, \"retry-after\": RETRY_AFTER_S },\n\t\t\t);\n\t\t}\n\t\tif (typeof auth === \"string\") {\n\t\t\tlogDone(401, method);\n\t\t\treturn json(\n\t\t\t\t401,\n\t\t\t\t{\n\t\t\t\t\terror: \"invalid_token\",\n\t\t\t\t\terror_description:\n\t\t\t\t\t\tauth === \"missing\"\n\t\t\t\t\t\t\t? \"Authorization required.\"\n\t\t\t\t\t\t\t: \"The access token is not valid.\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t...corsHeaders,\n\t\t\t\t\t\"www-authenticate\": wwwAuthenticate(addressId, auth === \"invalid\"),\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\n\t\tconst headers: Record<string, string> = {};\n\t\tfor (const [k, v] of req.headers) {\n\t\t\tconst key = k.toLowerCase();\n\t\t\tif (FORWARDED_HEADERS.has(key) || key.startsWith(\"mcp-param-\"))\n\t\t\t\theaders[key] = v;\n\t\t}\n\t\theaders[\"mcp-protocol-version\"] = version.forwardVersion;\n\n\t\tconst clientHostName = (() => {\n\t\t\ttry {\n\t\t\t\treturn new URL(auth.clientId).host;\n\t\t\t} catch {\n\t\t\t\treturn \"\";\n\t\t\t}\n\t\t})();\n\t\tconst isListen = body.value[\"method\"] === \"subscriptions/listen\";\n\t\t// `server/discover` does not wait: clients use it as the protocol-era\n\t\t// probe with a short timeout (Claude Code: 5 s, then it falls back to\n\t\t// the legacy handshake), and the stand-in answer has `ttlMs: 0`.\n\t\tconst waitMs =\n\t\t\tbody.value[\"method\"] === \"server/discover\" ||\n\t\t\t// Only small requests wait: a waiting request holds its body, and\n\t\t\t// tool calls are small (review of iteration 8).\n\t\t\tBuffer.byteLength(body.text) > MIN_MESSAGE_BYTES\n\t\t\t\t? 0\n\t\t\t\t: config.pageWaitMs;\n\t\tconst closed = () => {\n\t\t\tlogDone(499, method, { outcome: \"client_closed\" });\n\t\t\treturn new Response(null, { status: 499 });\n\t\t};\n\n\t\t// Only now, for an authenticated request, switch off Bun's idle timer:\n\t\t// our own timers (the page wait, the request timer) and keep-alives take\n\t\t// over (Bun research, finding 1). Body reading and every unauthenticated\n\t\t// path keep the idle timeout, so a stalled client cannot hold a socket.\n\t\t// It is switched on again on every exit, while the response is still in\n\t\t// progress: Bun ignores a timeout set once the response has finished.\n\t\tconst idle = (on: boolean) =>\n\t\t\tserver.timeout(req, on ? config.httpIdleTimeoutS : 0);\n\t\tconst usable = usableFor(auth.subject);\n\n\t\t/** The page's session to forward to, or the answer when there is none. */\n\t\tconst admit = async (): Promise<TabSession | Response> => {\n\t\t\tconst found = await waitForPage(addressId, req.signal, waitMs, usable);\n\t\t\t// The AI app may have gone while the body was read, the token checked\n\t\t\t// or the page awaited: then forward nothing (the abort event has\n\t\t\t// already fired and would not cancel the exchange, which would hold a\n\t\t\t// slot until the timeout).\n\t\t\tif (found === \"aborted\" || req.signal.aborted) return closed();\n\t\t\tif (found === undefined) {\n\t\t\t\treturn pageNotOpen(body.value, rpcId, addressId, logDone, method);\n\t\t\t}\n\t\t\tconst session = found;\n\t\t\t// Identity binding: only the person the grant was made for (DEC-077).\n\t\t\tif (\n\t\t\t\t(auth.subject !== undefined || session.site.pairing === \"oidc\") &&\n\t\t\t\tsession.identity !== auth.subject\n\t\t\t) {\n\t\t\t\tlogDone(401, method, { reason: \"identity_mismatch\" });\n\t\t\t\treturn json(\n\t\t\t\t\t401,\n\t\t\t\t\t{ error: \"invalid_token\", error_description: MISMATCH_TEXT },\n\t\t\t\t\t{\n\t\t\t\t\t\t...corsHeaders,\n\t\t\t\t\t\t\"www-authenticate\": `${wwwAuthenticate(addressId, true)}, error_description=\"${MISMATCH_TEXT}\"`,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t}\n\t\t\t// The body was read with the instance's limit if the page was not\n\t\t\t// open yet; its site may allow less.\n\t\t\tif (Buffer.byteLength(body.text) > session.limits.maxMessageBytes) {\n\t\t\t\tlogDone(413, method);\n\t\t\t\treturn rpcError(\n\t\t\t\t\t413,\n\t\t\t\t\trpcId,\n\t\t\t\t\t-32600,\n\t\t\t\t\t\"Request body too large.\",\n\t\t\t\t\tcorsHeaders,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (session.pending.size >= session.limits.maxConcurrentRequests) {\n\t\t\t\tlogDone(429, method);\n\t\t\t\treturn rpcError(\n\t\t\t\t\t429,\n\t\t\t\t\trpcId,\n\t\t\t\t\tRelayErrorCode.overloaded,\n\t\t\t\t\t\"Too many requests at once for this page.\",\n\t\t\t\t\tcorsHeaders,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (\n\t\t\t\tisListen &&\n\t\t\t\tsession.listenStreams(auth.grantId) >= MAX_LISTEN_STREAMS_PER_GRANT\n\t\t\t) {\n\t\t\t\tlogDone(429, method, { reason: \"listen_streams\" });\n\t\t\t\treturn rpcError(\n\t\t\t\t\t429,\n\t\t\t\t\trpcId,\n\t\t\t\t\tRelayErrorCode.overloaded,\n\t\t\t\t\t\"Too many listen streams at once for this AI app.\",\n\t\t\t\t\tcorsHeaders,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn session;\n\t\t};\n\n\t\tif (readySession(addressId, usable) === undefined && waitMs > 0) {\n\t\t\tidle(false);\n\t\t}\n\t\tlet retried = false;\n\t\tfor (;;) {\n\t\t\tconst admitted = await admit();\n\t\t\tif (admitted instanceof Response) {\n\t\t\t\tidle(true);\n\t\t\t\treturn admitted;\n\t\t\t}\n\t\t\tconst session = admitted;\n\t\t\tidle(false);\n\t\t\tlet retry: () => void = () => {};\n\t\t\tconst retrying = new Promise<typeof RETRY>((resolve) => {\n\t\t\t\tretry = () => resolve(RETRY);\n\t\t\t});\n\t\t\tconst mayRetry =\n\t\t\t\t!retried && waitMs > 0 && isRetryable(body.value[\"method\"]);\n\t\t\tconst exchange = session.forward({\n\t\t\t\theaders,\n\t\t\t\tbody: body.text,\n\t\t\t\trpcId,\n\t\t\t\tisListen,\n\t\t\t\tgrant: {\n\t\t\t\t\tgrantId: auth.grantId,\n\t\t\t\t\tclientId: auth.clientId,\n\t\t\t\t\tscopes: auth.scopes,\n\t\t\t\t},\n\t\t\t\textraHeaders: corsHeaders,\n\t\t\t\tonDone: (e: Exchange) => {\n\t\t\t\t\tif (e.outcome === \"page_gone_retry\") return; // logged once, at the end\n\t\t\t\t\tidle(true);\n\t\t\t\t\tlogDone(e.status, method, {\n\t\t\t\t\t\tsession: session.sessionId,\n\t\t\t\t\t\tclient: clientHostName,\n\t\t\t\t\t\toutcome: e.outcome,\n\t\t\t\t\t\t...(e.outcome === \"page_gone\" ? { reason: \"page_not_open\" } : {}),\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t\tpageGone: (waiting) => {\n\t\t\t\t\tif (waiting && mayRetry && !req.signal.aborted) {\n\t\t\t\t\t\tretry();\n\t\t\t\t\t\treturn { retry: true };\n\t\t\t\t\t}\n\t\t\t\t\tconst answer = notOpenAnswer(\n\t\t\t\t\t\tbody.value,\n\t\t\t\t\t\trpcId,\n\t\t\t\t\t\tsession.appName,\n\t\t\t\t\t\tclosing ? \"restarting_gone\" : \"gone\",\n\t\t\t\t\t\tRELAY_INFO,\n\t\t\t\t\t);\n\t\t\t\t\tif (answer !== undefined) return { retry: false, body: answer };\n\t\t\t\t\t// A notification: an HTTP error, which says \"restarting\" too.\n\t\t\t\t\treturn !closing\n\t\t\t\t\t\t? { retry: false }\n\t\t\t\t\t\t: {\n\t\t\t\t\t\t\t\tretry: false,\n\t\t\t\t\t\t\t\terror: {\n\t\t\t\t\t\t\t\t\tstatus: 503,\n\t\t\t\t\t\t\t\t\tcode: RelayErrorCode.restarting,\n\t\t\t\t\t\t\t\t\tmessage: \"The relay is restarting.\",\n\t\t\t\t\t\t\t\t\theaders: { \"retry-after\": RETRY_AFTER_S },\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t};\n\t\t\t\t},\n\t\t\t});\n\t\t\tconst onAbort = () => exchange.clientClosed();\n\t\t\treq.signal.addEventListener(\"abort\", onAbort, { once: true });\n\t\t\tconst result = await Promise.race([exchange.response, retrying]);\n\t\t\tif (result !== RETRY) return result;\n\t\t\treq.signal.removeEventListener(\"abort\", onAbort);\n\t\t\tretried = true;\n\t\t}\n\t}\n\n\t/**\n\t * DEC-074: HTTP 200 and a JSON-RPC message, not an HTTP error status. While\n\t * the relay stops, the answer says it restarts (iteration 11, finding 5).\n\t */\n\tfunction pageNotOpen(\n\t\tvalue: Record<string, unknown>,\n\t\trpcId: RpcId | undefined,\n\t\taddressId: string,\n\t\tlogDone: (\n\t\t\tstatus: number,\n\t\t\tmethod: string,\n\t\t\textra?: Record<string, string | number>,\n\t\t) => void,\n\t\tmethod: string,\n\t): Response {\n\t\t// Open but not signed in (identity binding) reads differently.\n\t\tconst open = sessions.get(addressId);\n\t\tif (\n\t\t\t!closing &&\n\t\t\topen?.ready === true &&\n\t\t\topen.identity === undefined &&\n\t\t\topen.identityUnchecked\n\t\t) {\n\t\t\t// The relay could not check the page's user: not the user's errand.\n\t\t\tlogDone(503, method, { reason: \"identity_unchecked\" });\n\t\t\treturn rpcError(503, rpcId, RelayErrorCode.signedOut, UNCHECKED_TEXT, {\n\t\t\t\t...corsHeaders,\n\t\t\t\t\"retry-after\": RETRY_AFTER_S,\n\t\t\t});\n\t\t}\n\t\tconst phase: NotOpenPhase = closing\n\t\t\t? \"restarting\"\n\t\t\t: open?.ready === true &&\n\t\t\t\t\topen.identity === undefined &&\n\t\t\t\t\topen.site.pairing === \"oidc\"\n\t\t\t\t? \"signed_out\"\n\t\t\t\t: \"not_open\";\n\t\tconst reason = phase === \"restarting\" ? \"restarting\" : \"page_not_open\";\n\t\tconst answer = notOpenAnswer(\n\t\t\tvalue,\n\t\t\trpcId,\n\t\t\topen?.appName ?? lastSeen.get(addressId)?.appName,\n\t\t\tphase,\n\t\t\tRELAY_INFO,\n\t\t);\n\t\tif (answer === undefined) {\n\t\t\t// A notification: the spec requires an HTTP error status.\n\t\t\tlogDone(503, method, { reason });\n\t\t\treturn phase === \"restarting\"\n\t\t\t\t? rpcError(\n\t\t\t\t\t\t503,\n\t\t\t\t\t\trpcId,\n\t\t\t\t\t\tRelayErrorCode.restarting,\n\t\t\t\t\t\t\"The relay is restarting.\",\n\t\t\t\t\t\t{ ...corsHeaders, \"retry-after\": RETRY_AFTER_S },\n\t\t\t\t\t)\n\t\t\t\t: rpcError(\n\t\t\t\t\t\t503,\n\t\t\t\t\t\trpcId,\n\t\t\t\t\t\tRelayErrorCode.pageNotOpen,\n\t\t\t\t\t\t\"The page is not open.\",\n\t\t\t\t\t\tcorsHeaders,\n\t\t\t\t\t);\n\t\t}\n\t\tlogDone(200, method, { reason });\n\t\treturn new Response(answer, {\n\t\t\tstatus: 200,\n\t\t\theaders: { ...corsHeaders, \"content-type\": \"application/json\" },\n\t\t});\n\t}\n\n\tfunction preflight(req: Request): Response {\n\t\tif (!config.cors)\n\t\t\treturn new Response(null, { status: 405, headers: { allow: \"POST\" } });\n\t\tconst requested = req.headers.get(\"access-control-request-headers\") ?? \"\";\n\t\treturn new Response(null, {\n\t\t\tstatus: 204,\n\t\t\theaders: {\n\t\t\t\t...corsHeaders,\n\t\t\t\t\"access-control-allow-methods\": \"POST, OPTIONS\",\n\t\t\t\t\"access-control-allow-headers\":\n\t\t\t\t\trequested !== \"\"\n\t\t\t\t\t\t? requested\n\t\t\t\t\t\t: \"authorization, content-type, accept, mcp-protocol-version, mcp-method, mcp-name\",\n\t\t\t\t\"access-control-max-age\": \"600\",\n\t\t\t},\n\t\t});\n\t}\n\n\tconst methodNotAllowed = () =>\n\t\tnew Response(null, {\n\t\t\tstatus: 405,\n\t\t\theaders: { ...corsHeaders, allow: \"POST\" },\n\t\t});\n\n\tconst postOnly = () =>\n\t\tnew Response(null, { status: 405, headers: { allow: \"POST, OPTIONS\" } });\n\n\t/**\n\t * Whether the token store answers (DEC-099): one cheap read of a hash that\n\t * never exists, with a deadline. One probe at a time, and its answer is\n\t * reused for a second, so a flood of `/healthz` does not reach the store.\n\t * While a read that missed its deadline still hangs, a new one starts only\n\t * every `healthRetryMs` (30 s): a store that lost one command recovers, and\n\t * a hung store holds at most one read per window.\n\t */\n\tconst healthRetryMs = hooks?.healthRetryMs ?? HEALTH_HUNG_RETRY_MS;\n\tlet probe: Promise<StoreHealth> | undefined;\n\tlet probed: { at: number; result: StoreHealth } | undefined;\n\tlet hungReads = 0;\n\tlet lastHungAt = 0;\n\tasync function runProbe(): Promise<StoreHealth> {\n\t\tlet timer: Timer | undefined;\n\t\tconst deadline = new Promise<\"store_timeout\">((resolve) => {\n\t\t\ttimer = setTimeout(\n\t\t\t\t() => resolve(\"store_timeout\"),\n\t\t\t\tHEALTH_STORE_TIMEOUT_MS,\n\t\t\t);\n\t\t});\n\t\t// Inside a promise chain, so a store that throws synchronously is a\n\t\t// failed probe like any other.\n\t\tconst read = Promise.resolve()\n\t\t\t.then(() => store.get(HEALTH_PROBE_HASH))\n\t\t\t.then(() => \"ok\" as const);\n\t\tread.catch(() => {}); // a late rejection after the deadline\n\t\ttry {\n\t\t\tconst result = await Promise.race([read, deadline]);\n\t\t\tif (result === \"store_timeout\") {\n\t\t\t\tlogger.warn(\"health.store\", { error: \"timeout\" });\n\t\t\t\thungReads++;\n\t\t\t\tlastHungAt = Date.now();\n\t\t\t\tconst settled = () => {\n\t\t\t\t\thungReads--;\n\t\t\t\t};\n\t\t\t\tread.then(settled, settled);\n\t\t\t}\n\t\t\treturn result;\n\t\t} catch (error) {\n\t\t\tlogger.warn(\"health.store\", { error: errorName(error) });\n\t\t\treturn \"store_unavailable\";\n\t\t} finally {\n\t\t\tclearTimeout(timer);\n\t\t}\n\t}\n\tfunction storeHealth(): Promise<StoreHealth> {\n\t\tif (probe !== undefined) return probe;\n\t\tconst now = Date.now();\n\t\tif (probed !== undefined && now - probed.at < HEALTH_REUSE_MS) {\n\t\t\treturn Promise.resolve(probed.result);\n\t\t}\n\t\tif (hungReads > 0 && now - lastHungAt < healthRetryMs) {\n\t\t\treturn Promise.resolve(\"store_timeout\");\n\t\t}\n\t\tconst current = runProbe().catch((): StoreHealth => \"store_unavailable\");\n\t\tprobe = current;\n\t\tvoid current.then((result) => {\n\t\t\tprobed = { at: Date.now(), result };\n\t\t\tif (probe === current) probe = undefined;\n\t\t});\n\t\treturn current;\n\t}\n\n\tconst largest = largestMessageBytes(config);\n\tconst ipOf = (req: Request, srv: Server<WsData>) =>\n\t\tipPrefixKey(clientIp(req, srv));\n\tconst server = Bun.serve({\n\t\tport: config.port,\n\t\thostname: config.host,\n\t\tdevelopment: false,\n\t\tmaxRequestBodySize: largest + 1024,\n\t\tidleTimeout: config.httpIdleTimeoutS,\n\t\troutes: {\n\t\t\t\"/healthz\": {\n\t\t\t\t// no-store: a CDN in front caches answers without cache headers\n\t\t\t\t// (bunny for 30 days), and a deploy check would read the old version.\n\t\t\t\tGET: async () => {\n\t\t\t\t\tconst store = await storeHealth();\n\t\t\t\t\tif (store !== \"ok\") {\n\t\t\t\t\t\t// A reason code only: never the store's error (DEC-099).\n\t\t\t\t\t\treturn json(\n\t\t\t\t\t\t\t503,\n\t\t\t\t\t\t\t{ status: \"unhealthy\", reason: store, version: VERSION },\n\t\t\t\t\t\t\t{ \"cache-control\": \"no-store\" },\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\treturn json(\n\t\t\t\t\t\t200,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tstatus: \"ok\",\n\t\t\t\t\t\t\tversion: VERSION,\n\t\t\t\t\t\t\tcounters: { claudeHeaderWorkaround: claudeWorkarounds },\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{ \"cache-control\": \"no-store\" },\n\t\t\t\t\t);\n\t\t\t\t},\n\t\t\t},\n\t\t\t\"/\": {\n\t\t\t\tGET: () =>\n\t\t\t\t\tnew Response(INDEX_HTML, {\n\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\t\"content-type\": \"text/html; charset=utf-8\",\n\t\t\t\t\t\t\t\"content-security-policy\":\n\t\t\t\t\t\t\t\t\"default-src 'none'; img-src 'self'; frame-ancestors 'none'\",\n\t\t\t\t\t\t\t\"x-content-type-options\": \"nosniff\",\n\t\t\t\t\t\t\t\"cache-control\": \"public, max-age=3600\",\n\t\t\t\t\t\t},\n\t\t\t\t\t}),\n\t\t\t},\n\t\t\t// The relay's icon, for the AI apps' connector lists and browser tabs.\n\t\t\t\"/favicon.ico\": { GET: () => icon(FAVICON_ICO, \"image/x-icon\") },\n\t\t\t\"/favicon.svg\": { GET: () => icon(FAVICON_SVG, \"image/svg+xml\") },\n\t\t\t\"/apple-touch-icon.png\": {\n\t\t\t\tGET: () => icon(APPLE_TOUCH_ICON, \"image/png\"),\n\t\t\t},\n\t\t\t\"/tab\": (req, srv) => handleTab(req, srv),\n\t\t\t\"/.well-known/oauth-authorization-server\": {\n\t\t\t\tGET: () => auth.metadata(),\n\t\t\t\tOPTIONS: () => auth.preflight(\"GET\"),\n\t\t\t},\n\t\t\t\"/.well-known/oauth-protected-resource/a/:id/mcp\": {\n\t\t\t\tGET: (req) => auth.protectedResource(req.params.id),\n\t\t\t\tOPTIONS: () => auth.preflight(\"GET\"),\n\t\t\t},\n\t\t\t\"/authorize\": {\n\t\t\t\tGET: (req, srv) => auth.authorize(req, ipOf(req, srv)),\n\t\t\t\tPOST: (req, srv) => auth.authorizeSubmit(req, ipOf(req, srv)),\n\t\t\t},\n\t\t\t\"/authorize/callback\": {\n\t\t\t\tGET: (req, srv) => auth.authorizeCallback(req, ipOf(req, srv)),\n\t\t\t},\n\t\t\t\"/authorize.css\": { GET: () => auth.stylesheet() },\n\t\t\t\"/token\": {\n\t\t\t\tPOST: (req, srv) => auth.token(req, ipOf(req, srv)),\n\t\t\t\tOPTIONS: () => auth.preflight(\"POST\"),\n\t\t\t\tGET: postOnly,\n\t\t\t},\n\t\t\t\"/revoke\": {\n\t\t\t\tPOST: (req, srv) => auth.revoke(req, ipOf(req, srv)),\n\t\t\t\tOPTIONS: () => auth.preflight(\"POST\"),\n\t\t\t\tGET: postOnly,\n\t\t\t},\n\t\t\t\"/a/:id/mcp\": {\n\t\t\t\tPOST: (req, srv) => {\n\t\t\t\t\tconst id = req.params.id;\n\t\t\t\t\tif (!isAddressId(id))\n\t\t\t\t\t\treturn new Response(\"Not found\", { status: 404 });\n\t\t\t\t\treturn handleMcp(req, srv, id);\n\t\t\t\t},\n\t\t\t\tOPTIONS: (req) => preflight(req),\n\t\t\t\tGET: methodNotAllowed,\n\t\t\t\tDELETE: methodNotAllowed,\n\t\t\t\tPUT: methodNotAllowed,\n\t\t\t\tPATCH: methodNotAllowed,\n\t\t\t},\n\t\t},\n\t\tfetch: () => new Response(\"Not found\", { status: 404 }),\n\t\twebsocket: {\n\t\t\tdata: {} as WsData,\n\t\t\tmaxPayloadLength: largest * FRAME_HEADROOM,\n\t\t\tbackpressureLimit: config.backpressureLimitBytes,\n\t\t\tcloseOnBackpressureLimit: false,\n\t\t\tidleTimeout: 120,\n\t\t\tsendPings: true,\n\t\t\tperMessageDeflate: false,\n\t\t\topen(ws: ServerWebSocket<WsData>) {\n\t\t\t\tconst session = new TabSession(\n\t\t\t\t\tws,\n\t\t\t\t\tws.data.ip,\n\t\t\t\t\tws.data.origin,\n\t\t\t\t\tws.data.site,\n\t\t\t\t\thost,\n\t\t\t\t\tws.data.address,\n\t\t\t\t);\n\t\t\t\tws.data.session = session;\n\t\t\t\tallSessions.add(session);\n\t\t\t},\n\t\t\tmessage(ws: ServerWebSocket<WsData>, message: string | Buffer) {\n\t\t\t\tws.data.session?.handleFrame(\n\t\t\t\t\ttypeof message === \"string\" ? message : new Uint8Array(message),\n\t\t\t\t);\n\t\t\t},\n\t\t\tclose(ws: ServerWebSocket<WsData>, code: number) {\n\t\t\t\tws.data.session?.closed(code);\n\t\t\t},\n\t\t},\n\t});\n\n\tlet stopping: Promise<void> | undefined;\n\t/** Set at once by `stop()`, before the sessions end (their requests read it). */\n\tlet closing = false;\n\tconst port = server.port ?? config.port;\n\tif (publicUrl === \"\") publicUrl = developmentUrl(config.host, port);\n\n\tconst sweep = setInterval(() => {\n\t\tauth.sweep();\n\t\tstore\n\t\t\t.deleteExpired(Date.now())\n\t\t\t.then((n) => {\n\t\t\t\tif (n > 0) logger.debug(\"store.swept\", { deleted: n });\n\t\t\t})\n\t\t\t.catch((error: unknown) =>\n\t\t\t\tlogger.error(\"store.error\", {\n\t\t\t\t\top: \"deleteExpired\",\n\t\t\t\t\terror: errorName(error),\n\t\t\t\t}),\n\t\t\t);\n\t}, config.sweepIntervalMs);\n\n\tconst siteCount =\n\t\tconfig.allowedOrigins === \"*\"\n\t\t\t? \"*\"\n\t\t\t: config.allowedOrigins.length + config.sites.length;\n\tif (config.allowedOrigins === \"*\") {\n\t\tlogger.warn(\"relay.open_relay\", {\n\t\t\tmessage:\n\t\t\t\t\"TAB_MCP_ALLOWED_ORIGINS=* lets any website open tab sessions on this relay\",\n\t\t});\n\t} else if (siteCount === 0) {\n\t\tlogger.warn(\"relay.no_origins\", {\n\t\t\tmessage:\n\t\t\t\t\"TAB_MCP_ALLOWED_ORIGINS and TAB_MCP_SITES are empty: no page can connect\",\n\t\t});\n\t}\n\tlogger.info(\"relay.start\", {\n\t\tversion: VERSION,\n\t\turl: publicUrl,\n\t\tport,\n\t\tstore: ownsStore ? config.store : \"custom\",\n\t\torigins: siteCount,\n\t});\n\n\treturn {\n\t\turl: publicUrl,\n\t\tport,\n\t\tconfig,\n\t\taddressFor: (secret) => `${publicUrl}/a/${addressIdFromSecret(secret)}/mcp`,\n\t\tget sessionCount() {\n\t\t\treturn allSessions.size;\n\t\t},\n\t\tstop() {\n\t\t\tclosing = true;\n\t\t\tstopping ??= (async () => {\n\t\t\t\tclearInterval(sweep);\n\t\t\t\tfor (const set of [...waiters.values()]) {\n\t\t\t\t\tfor (const wake of [...set]) wake(undefined);\n\t\t\t\t}\n\t\t\t\tfor (const session of [...allSessions]) {\n\t\t\t\t\tsession.end(\n\t\t\t\t\t\tCloseCode.shuttingDown,\n\t\t\t\t\t\t\"relay shutting down\",\n\t\t\t\t\t\t\"shutdown\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tawait Bun.sleep(20); // let close frames leave\n\t\t\t\tawait server.stop(true);\n\t\t\t\tif (ownsStore) await store.close?.();\n\t\t\t\tlogger.info(\"relay.stop\", {});\n\t\t\t})();\n\t\t\treturn stopping;\n\t\t},\n\t};\n}\n",
28
+ "import { afterEach, beforeEach, describe, expect, test } from \"bun:test\";\nimport type { TokenRecord, TokenStore } from \"./types.ts\";\n\n/**\n * The conformance suite every `TokenStore` must pass (DEC-041). Call it from a\n * `bun test` file with a factory that returns a fresh, empty store:\n *\n * ```ts\n * import { tokenStoreConformance } from \"@ractive-ch/tab-mcp/testing\";\n * tokenStoreConformance(\"RedisTokenStore\", async () => new RedisTokenStore(…));\n * ```\n */\nexport function tokenStoreConformance(\n\tname: string,\n\tfactory: () => TokenStore | Promise<TokenStore>,\n): void {\n\tdescribe(`TokenStore conformance: ${name}`, () => {\n\t\tlet store: TokenStore;\n\t\tconst now = 1_800_000_000_000;\n\t\tlet seq = 0;\n\n\t\tconst until = { now, keepUntil: now + 86_400_000 };\n\n\t\tconst record = (over: Partial<TokenRecord> = {}): TokenRecord => {\n\t\t\tseq++;\n\t\t\treturn {\n\t\t\t\thash: `hash-${seq}`,\n\t\t\t\tkind: \"access\",\n\t\t\t\tgrantId: \"g_1\",\n\t\t\t\taddressId: \"addr-a\",\n\t\t\t\tclientId: \"https://client.example/cimd\",\n\t\t\t\tclientName: \"Client\",\n\t\t\t\tscopes: [\"tools\"],\n\t\t\t\tgrantCreatedAt: now - 1000,\n\t\t\t\tissuedAt: now,\n\t\t\t\texpiresAt: now + 3_600_000,\n\t\t\t\t...over,\n\t\t\t};\n\t\t};\n\n\t\tbeforeEach(async () => {\n\t\t\tstore = await factory();\n\t\t});\n\n\t\tafterEach(async () => {\n\t\t\tawait store.close?.();\n\t\t});\n\n\t\ttest(\"put then get returns an equal record\", async () => {\n\t\t\tconst r = record({ scopes: [\"tools\", \"extra\"] });\n\t\t\texpect(await store.put(r)).toBe(true);\n\t\t\texpect(await store.get(r.hash)).toEqual(r);\n\t\t});\n\n\t\ttest(\"get of an unknown hash is undefined\", async () => {\n\t\t\texpect(await store.get(\"nope\")).toBeUndefined();\n\t\t});\n\n\t\ttest(\"put replaces the record with the same hash\", async () => {\n\t\t\tconst r = record();\n\t\t\tawait store.put(r);\n\t\t\tawait store.put({ ...r, clientName: \"Renamed\", expiresAt: now + 5 });\n\t\t\tconst got = await store.get(r.hash);\n\t\t\texpect(got?.clientName).toBe(\"Renamed\");\n\t\t\texpect(got?.expiresAt).toBe(now + 5);\n\t\t});\n\n\t\ttest(\"get returns expired and used records unchanged\", async () => {\n\t\t\tconst expired = record({ expiresAt: now - 1 });\n\t\t\tconst used = record({ kind: \"refresh\", usedAt: now - 10 });\n\t\t\tawait store.put(expired);\n\t\t\tawait store.put(used);\n\t\t\texpect(await store.get(expired.hash)).toEqual(expired);\n\t\t\texpect(await store.get(used.hash)).toEqual(used);\n\t\t});\n\n\t\ttest(\"a record without usedAt comes back without the key\", async () => {\n\t\t\tconst r = record();\n\t\t\tawait store.put(r);\n\t\t\tconst got = await store.get(r.hash);\n\t\t\texpect(got !== undefined && \"usedAt\" in got).toBe(false);\n\t\t});\n\n\t\ttest(\"the subject of an identity-bound grant round-trips (DEC-077)\", async () => {\n\t\t\tconst bound = record({ grantId: \"g_bound\", subject: \"key-of-alice\" });\n\t\t\tconst plain = record({ grantId: \"g_plain\" });\n\t\t\tawait store.put(bound);\n\t\t\tawait store.put(plain);\n\t\t\texpect(await store.get(bound.hash)).toEqual(bound);\n\t\t\tconst got = await store.get(plain.hash);\n\t\t\texpect(got !== undefined && \"subject\" in got).toBe(false);\n\t\t\tconst grants = await store.listGrants(\"addr-a\", now);\n\t\t\texpect(grants.find((g) => g.grantId === \"g_bound\")?.subject).toBe(\n\t\t\t\t\"key-of-alice\",\n\t\t\t);\n\t\t\tconst unbound = grants.find((g) => g.grantId === \"g_plain\");\n\t\t\texpect(unbound !== undefined && \"subject\" in unbound).toBe(false);\n\t\t});\n\n\t\ttest(\"the client authentication binding round-trips (DEC-106)\", async () => {\n\t\t\tconst bound = record({\n\t\t\t\tgrantId: \"g_key\",\n\t\t\t\tkind: \"refresh\",\n\t\t\t\tclientAuth: \"private_key_jwt\",\n\t\t\t});\n\t\t\tconst plain = record({ grantId: \"g_public\", kind: \"refresh\" });\n\t\t\tawait store.put(bound);\n\t\t\tawait store.put(plain);\n\t\t\texpect(await store.get(bound.hash)).toEqual(bound);\n\t\t\tconst got = await store.get(plain.hash);\n\t\t\texpect(got !== undefined && \"clientAuth\" in got).toBe(false);\n\t\t\texpect(await store.markUsed(bound.hash, now, now + 1000)).toBe(true);\n\t\t\texpect((await store.get(bound.hash))?.clientAuth).toBe(\"private_key_jwt\");\n\t\t});\n\n\t\ttest(\"claimNonce, if implemented: once until it expires, then again (DEC-106)\", async () => {\n\t\t\tif (store.claimNonce === undefined) return;\n\t\t\texpect(await store.claimNonce(\"n1\", now + 1000, now)).toBe(true);\n\t\t\texpect(await store.claimNonce(\"n1\", now + 5000, now + 1)).toBe(false);\n\t\t\texpect(await store.claimNonce(\"n2\", now + 1000, now)).toBe(true);\n\t\t\t// Expired, whether or not deleteExpired has run yet.\n\t\t\texpect(await store.claimNonce(\"n1\", now + 9000, now + 1000)).toBe(true);\n\t\t\texpect(await store.claimNonce(\"n1\", now + 9000, now + 2000)).toBe(false);\n\t\t\tawait store.deleteExpired(now + 9000);\n\t\t\texpect(await store.claimNonce(\"n1\", now + 20_000, now + 9000)).toBe(true);\n\t\t});\n\n\t\ttest(\"claimNonce, if implemented: of concurrent calls exactly one wins\", async () => {\n\t\t\tif (store.claimNonce === undefined) return;\n\t\t\tconst claim = store.claimNonce.bind(store);\n\t\t\tconst results = await Promise.all(\n\t\t\t\tArray.from({ length: 10 }, () => claim(\"race\", now + 1000, now)),\n\t\t\t);\n\t\t\texpect(results.filter(Boolean)).toHaveLength(1);\n\t\t});\n\n\t\ttest(\"markUsed succeeds once, then fails\", async () => {\n\t\t\tconst r = record({ kind: \"refresh\" });\n\t\t\tawait store.put(r);\n\t\t\texpect(await store.markUsed(r.hash, now + 1, r.expiresAt)).toBe(true);\n\t\t\texpect(await store.markUsed(r.hash, now + 2, r.expiresAt)).toBe(false);\n\t\t\texpect((await store.get(r.hash))?.usedAt).toBe(now + 1);\n\t\t});\n\n\t\ttest(\"markUsed cuts expiresAt to keepUntil, so deleteExpired drops the used token then\", async () => {\n\t\t\tconst r = record({ kind: \"refresh\", expiresAt: now + 30 * 86_400_000 });\n\t\t\tawait store.put(r);\n\t\t\texpect(await store.markUsed(r.hash, now, now + 86_400_000)).toBe(true);\n\t\t\tconst used = await store.get(r.hash);\n\t\t\texpect(used?.usedAt).toBe(now);\n\t\t\texpect(used?.expiresAt).toBe(now + 86_400_000);\n\t\t\tawait store.deleteExpired(now + 86_400_000 - 1);\n\t\t\texpect(await store.get(r.hash)).toBeDefined();\n\t\t\tawait store.deleteExpired(now + 86_400_000);\n\t\t\texpect(await store.get(r.hash)).toBeUndefined();\n\t\t});\n\n\t\ttest(\"markUsed never extends expiresAt, and a failed call changes nothing\", async () => {\n\t\t\tconst r = record({ kind: \"refresh\", expiresAt: now + 1000 });\n\t\t\tawait store.put(r);\n\t\t\texpect(await store.markUsed(r.hash, now, now + 86_400_000)).toBe(true);\n\t\t\texpect((await store.get(r.hash))?.expiresAt).toBe(now + 1000);\n\t\t\texpect(await store.markUsed(r.hash, now + 1, now + 10)).toBe(false);\n\t\t\tconst got = await store.get(r.hash);\n\t\t\texpect(got?.expiresAt).toBe(now + 1000);\n\t\t\texpect(got?.usedAt).toBe(now);\n\t\t});\n\n\t\ttest(\"markUsed of an unknown hash fails\", async () => {\n\t\t\texpect(await store.markUsed(\"nope\", now, now + 1000)).toBe(false);\n\t\t});\n\n\t\ttest(\"concurrent markUsed calls: exactly one wins\", async () => {\n\t\t\tconst r = record({ kind: \"refresh\" });\n\t\t\tawait store.put(r);\n\t\t\tconst results = await Promise.all(\n\t\t\t\tArray.from({ length: 10 }, (_, i) =>\n\t\t\t\t\tstore.markUsed(r.hash, now + i, r.expiresAt),\n\t\t\t\t),\n\t\t\t);\n\t\t\texpect(results.filter(Boolean)).toHaveLength(1);\n\t\t});\n\n\t\ttest(\"revoke by grant deletes only that grant's tokens\", async () => {\n\t\t\tconst a = record({ grantId: \"g_a\" });\n\t\t\tconst a2 = record({ grantId: \"g_a\", kind: \"refresh\" });\n\t\t\tconst b = record({ grantId: \"g_b\" });\n\t\t\tfor (const r of [a, a2, b]) await store.put(r);\n\t\t\texpect(await store.revoke({ grantId: \"g_a\" }, until)).toBe(2);\n\t\t\texpect(await store.get(a.hash)).toBeUndefined();\n\t\t\texpect(await store.get(a2.hash)).toBeUndefined();\n\t\t\texpect(await store.get(b.hash)).toEqual(b);\n\t\t});\n\n\t\ttest(\"revoke by address deletes only that address's tokens\", async () => {\n\t\t\tconst a = record({ addressId: \"addr-a\", grantId: \"g_a\" });\n\t\t\tconst b = record({ addressId: \"addr-b\", grantId: \"g_b\" });\n\t\t\tawait store.put(a);\n\t\t\tawait store.put(b);\n\t\t\texpect(await store.revoke({ addressId: \"addr-a\" }, until)).toBe(1);\n\t\t\texpect(await store.get(a.hash)).toBeUndefined();\n\t\t\texpect(await store.get(b.hash)).toEqual(b);\n\t\t});\n\n\t\ttest(\"revoke of nothing returns 0\", async () => {\n\t\t\texpect(await store.revoke({ grantId: \"none\" }, until)).toBe(0);\n\t\t});\n\n\t\ttest(\"a revoked grant gets no new tokens until its tombstone expires\", async () => {\n\t\t\tawait store.revoke({ grantId: \"g_dead\" }, until);\n\t\t\tconst late = record({ grantId: \"g_dead\", kind: \"refresh\" });\n\t\t\texpect(await store.put(late)).toBe(false);\n\t\t\texpect(await store.get(late.hash)).toBeUndefined();\n\t\t\texpect(await store.put(record({ grantId: \"g_alive\" }))).toBe(true);\n\t\t\tawait store.deleteExpired(until.keepUntil);\n\t\t\texpect(await store.put(record({ grantId: \"g_dead\" }))).toBe(true);\n\t\t});\n\n\t\ttest(\"a revoked address refuses grants created up to the revocation, not later ones\", async () => {\n\t\t\tawait store.revoke({ addressId: \"addr-a\" }, until);\n\t\t\texpect(\n\t\t\t\tawait store.put(record({ grantId: \"g_before\", grantCreatedAt: now })),\n\t\t\t).toBe(false);\n\t\t\texpect(\n\t\t\t\tawait store.put(\n\t\t\t\t\trecord({ grantId: \"g_after\", grantCreatedAt: now + 1 }),\n\t\t\t\t),\n\t\t\t).toBe(true);\n\t\t\texpect(\n\t\t\t\tawait store.put(\n\t\t\t\t\trecord({\n\t\t\t\t\t\tgrantId: \"g_elsewhere\",\n\t\t\t\t\t\taddressId: \"addr-b\",\n\t\t\t\t\t\tgrantCreatedAt: now,\n\t\t\t\t\t}),\n\t\t\t\t),\n\t\t\t).toBe(true);\n\t\t});\n\n\t\ttest(\"a put racing a revocation of its grant never survives it\", async () => {\n\t\t\tfor (let i = 0; i < 20; i++) {\n\t\t\t\tconst r = record({ grantId: `g_race_${i}`, kind: \"refresh\" });\n\t\t\t\tawait Promise.all([\n\t\t\t\t\tstore.put(r),\n\t\t\t\t\tstore.revoke({ grantId: r.grantId }, until),\n\t\t\t\t]);\n\t\t\t\texpect(await store.get(r.hash)).toBeUndefined();\n\t\t\t}\n\t\t});\n\n\t\ttest(\"deleteExpired removes records with expiresAt <= now only\", async () => {\n\t\t\tconst gone = record({ expiresAt: now - 1 });\n\t\t\tconst edge = record({ expiresAt: now });\n\t\t\tconst kept = record({ expiresAt: now + 1 });\n\t\t\tfor (const r of [gone, edge, kept]) await store.put(r);\n\t\t\texpect(await store.deleteExpired(now)).toBe(2);\n\t\t\texpect(await store.get(gone.hash)).toBeUndefined();\n\t\t\texpect(await store.get(edge.hash)).toBeUndefined();\n\t\t\texpect(await store.get(kept.hash)).toEqual(kept);\n\t\t});\n\n\t\ttest(\"listGrants groups live tokens of one address by grant\", async () => {\n\t\t\tawait store.put(\n\t\t\t\trecord({\n\t\t\t\t\tgrantId: \"g_new\",\n\t\t\t\t\tgrantCreatedAt: now - 10,\n\t\t\t\t\tclientName: \"New\",\n\t\t\t\t}),\n\t\t\t);\n\t\t\tawait store.put(\n\t\t\t\trecord({\n\t\t\t\t\tgrantId: \"g_old\",\n\t\t\t\t\tgrantCreatedAt: now - 500,\n\t\t\t\t\tclientName: \"Old\",\n\t\t\t\t\tscopes: [\"tools\"],\n\t\t\t\t}),\n\t\t\t);\n\t\t\tawait store.put(\n\t\t\t\trecord({\n\t\t\t\t\tgrantId: \"g_old\",\n\t\t\t\t\tkind: \"refresh\",\n\t\t\t\t\tgrantCreatedAt: now - 500,\n\t\t\t\t\tclientName: \"Old\",\n\t\t\t\t}),\n\t\t\t);\n\t\t\tawait store.put(record({ grantId: \"g_other\", addressId: \"addr-b\" }));\n\t\t\texpect(await store.listGrants(\"addr-a\", now)).toEqual([\n\t\t\t\t{\n\t\t\t\t\tgrantId: \"g_old\",\n\t\t\t\t\tclientId: \"https://client.example/cimd\",\n\t\t\t\t\tclientName: \"Old\",\n\t\t\t\t\tscopes: [\"tools\"],\n\t\t\t\t\tcreatedAt: now - 500,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tgrantId: \"g_new\",\n\t\t\t\t\tclientId: \"https://client.example/cimd\",\n\t\t\t\t\tclientName: \"New\",\n\t\t\t\t\tscopes: [\"tools\"],\n\t\t\t\t\tcreatedAt: now - 10,\n\t\t\t\t},\n\t\t\t]);\n\t\t});\n\n\t\ttest(\"listGrants leaves out grants without a usable token\", async () => {\n\t\t\tawait store.put(record({ grantId: \"g_expired\", expiresAt: now }));\n\t\t\tawait store.put(\n\t\t\t\trecord({ grantId: \"g_used\", kind: \"refresh\", usedAt: now - 1 }),\n\t\t\t);\n\t\t\tawait store.put(\n\t\t\t\trecord({ grantId: \"g_live\", kind: \"refresh\", expiresAt: now + 1 }),\n\t\t\t);\n\t\t\tconst grants = await store.listGrants(\"addr-a\", now);\n\t\t\texpect(grants.map((g) => g.grantId)).toEqual([\"g_live\"]);\n\t\t});\n\n\t\ttest(\"listGrants of an unknown address is empty\", async () => {\n\t\t\texpect(await store.listGrants(\"nobody\", now)).toEqual([]);\n\t\t});\n\n\t\ttest(\"changing a returned record does not change the store\", async () => {\n\t\t\tconst r = record();\n\t\t\tawait store.put(r);\n\t\t\tconst got = await store.get(r.hash);\n\t\t\tif (got === undefined) throw new Error(\"missing\");\n\t\t\tgot.scopes.push(\"mutated\");\n\t\t\tgot.expiresAt = 0;\n\t\t\texpect(await store.get(r.hash)).toEqual(r);\n\t\t});\n\t});\n}\n",
29
+ "/**\n * `@ractive-ch/tab-mcp/testing`: helpers for `bun test`. The `TokenStore`\n * conformance suite (DEC-041), and an in-process relay with a token helper\n * for end-to-end tests of a page's tools without pairing by hand.\n */\n\nimport { createLogger } from \"./log.ts\";\nimport { type CreateRelayOptions, createRelay, type Relay } from \"./relay.ts\";\nimport { MemoryTokenStore } from \"./store/memory.ts\";\nimport type { TokenStore } from \"./store/types.ts\";\nimport { issueAccessToken } from \"./tokens.ts\";\n\nexport { tokenStoreConformance } from \"./store/conformance.ts\";\n\n/** A relay started by `startTestRelay`. */\nexport interface TestRelay {\n\trelay: Relay;\n\t/** The relay's token store, for `tokenFor`. */\n\tstore: TokenStore;\n\t/** The relay's URL, for the page's `relay` option: `http://127.0.0.1:<port>`. */\n\turl: string;\n\tstop(): Promise<void>;\n}\n\n/**\n * Start a relay in this process on a free port of 127.0.0.1, in development\n * mode, open to every page origin, with a memory store and no log output\n * (unless `logger` or `logLevel` is given). `options` override any of that.\n * For tests only: it accepts any origin.\n */\nexport function startTestRelay(\n\toptions: Omit<CreateRelayOptions, \"store\"> & { store?: TokenStore } = {},\n): TestRelay {\n\tconst store = options.store ?? new MemoryTokenStore();\n\tconst relay = createRelay({\n\t\tdevelopment: true,\n\t\tport: 0,\n\t\thost: \"127.0.0.1\",\n\t\tallowedOrigins: \"*\",\n\t\t...(options.logger === undefined && options.logLevel === undefined\n\t\t\t? { logger: createLogger(\"error\", () => {}) }\n\t\t\t: {}),\n\t\t...options,\n\t\tstore,\n\t});\n\treturn { relay, store, url: relay.url, stop: () => relay.stop() };\n}\n\n/**\n * An access token for a page's MCP address, written straight into the relay's\n * token store, as if an AI app had paired. Use it as `Authorization: Bearer\n * <token>` with an MCP client. It lasts an hour unless `ttlMs` says otherwise.\n */\nexport async function tokenFor(\n\tstore: TokenStore,\n\taddress: string,\n\toptions: { clientId?: string; clientName?: string; ttlMs?: number } = {},\n): Promise<string> {\n\tconst addressId = /\\/a\\/([a-z2-7]{52})\\/mcp$/.exec(address)?.[1];\n\tif (addressId === undefined)\n\t\tthrow new TypeError(`not an MCP address: ${address}`);\n\tconst { token } = await issueAccessToken(\n\t\tstore,\n\t\t{\n\t\t\taddressId,\n\t\t\tclientId: options.clientId ?? \"https://client.example/cimd.json\",\n\t\t\tclientName: options.clientName ?? \"Test client\",\n\t\t},\n\t\t{ ttlMs: options.ttlMs ?? 3_600_000 },\n\t);\n\treturn token;\n}\n"
6
30
  ],
7
- "mappings": "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",
8
- "debugId": "6A29AC80FA53A78664756E2164756E21",
31
+ "mappings": "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",
32
+ "debugId": "160B2485561C7DBB64756E2164756E21",
9
33
  "names": []
10
34
  }