@drm3/sdk 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (228) hide show
  1. package/LICENSE +75 -0
  2. package/README.md +426 -0
  3. package/bin/drm3.js +255 -0
  4. package/dist/alerts-bell.d.ts +7 -0
  5. package/dist/alerts-bell.d.ts.map +1 -0
  6. package/dist/alerts-bell.js +118 -0
  7. package/dist/alerts-bell.js.map +1 -0
  8. package/dist/alerts-fetch.d.ts +41 -0
  9. package/dist/alerts-fetch.d.ts.map +1 -0
  10. package/dist/alerts-fetch.js +60 -0
  11. package/dist/alerts-fetch.js.map +1 -0
  12. package/dist/alerts-styles.d.ts +4 -0
  13. package/dist/alerts-styles.d.ts.map +1 -0
  14. package/dist/alerts-styles.js +49 -0
  15. package/dist/alerts-styles.js.map +1 -0
  16. package/dist/alerts.d.ts +3 -0
  17. package/dist/alerts.d.ts.map +1 -0
  18. package/dist/alerts.global.js +206 -0
  19. package/dist/alerts.global.text.js +2 -0
  20. package/dist/alerts.js +21 -0
  21. package/dist/alerts.js.map +1 -0
  22. package/dist/index.d.ts +64 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +117 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/keys-canonical.d.ts +7 -0
  27. package/dist/keys-canonical.d.ts.map +1 -0
  28. package/dist/keys-canonical.js +57 -0
  29. package/dist/keys-canonical.js.map +1 -0
  30. package/dist/keys-generate.d.ts +39 -0
  31. package/dist/keys-generate.d.ts.map +1 -0
  32. package/dist/keys-generate.js +39 -0
  33. package/dist/keys-generate.js.map +1 -0
  34. package/dist/keys-verify.d.ts +62 -0
  35. package/dist/keys-verify.d.ts.map +1 -0
  36. package/dist/keys-verify.js +68 -0
  37. package/dist/keys-verify.js.map +1 -0
  38. package/dist/keys.d.ts +4 -0
  39. package/dist/keys.d.ts.map +1 -0
  40. package/dist/keys.js +22 -0
  41. package/dist/keys.js.map +1 -0
  42. package/dist/meter/adapter.d.ts +35 -0
  43. package/dist/meter/adapter.d.ts.map +1 -0
  44. package/dist/meter/adapter.js +0 -0
  45. package/dist/meter/adapter.js.map +1 -0
  46. package/dist/meter/cascade.d.ts +56 -0
  47. package/dist/meter/cascade.d.ts.map +1 -0
  48. package/dist/meter/cascade.js +64 -0
  49. package/dist/meter/cascade.js.map +1 -0
  50. package/dist/meter/client.d.ts +78 -0
  51. package/dist/meter/client.d.ts.map +1 -0
  52. package/dist/meter/client.js +84 -0
  53. package/dist/meter/client.js.map +1 -0
  54. package/dist/meter/core.d.ts +37 -0
  55. package/dist/meter/core.d.ts.map +1 -0
  56. package/dist/meter/core.js +49 -0
  57. package/dist/meter/core.js.map +1 -0
  58. package/dist/meter/gate-client.d.ts +94 -0
  59. package/dist/meter/gate-client.d.ts.map +1 -0
  60. package/dist/meter/gate-client.js +77 -0
  61. package/dist/meter/gate-client.js.map +1 -0
  62. package/dist/meter/gate-decide.d.ts +61 -0
  63. package/dist/meter/gate-decide.d.ts.map +1 -0
  64. package/dist/meter/gate-decide.js +54 -0
  65. package/dist/meter/gate-decide.js.map +1 -0
  66. package/dist/meter/gate.d.ts +10 -0
  67. package/dist/meter/gate.d.ts.map +1 -0
  68. package/dist/meter/gate.js +10 -0
  69. package/dist/meter/gate.js.map +1 -0
  70. package/dist/meter/index.d.ts +28 -0
  71. package/dist/meter/index.d.ts.map +1 -0
  72. package/dist/meter/index.js +28 -0
  73. package/dist/meter/index.js.map +1 -0
  74. package/dist/meter/meters-buckets.d.ts +56 -0
  75. package/dist/meter/meters-buckets.d.ts.map +1 -0
  76. package/dist/meter/meters-buckets.js +111 -0
  77. package/dist/meter/meters-buckets.js.map +1 -0
  78. package/dist/meter/meters-charge.d.ts +13 -0
  79. package/dist/meter/meters-charge.d.ts.map +1 -0
  80. package/dist/meter/meters-charge.js +76 -0
  81. package/dist/meter/meters-charge.js.map +1 -0
  82. package/dist/meter/meters.d.ts +63 -0
  83. package/dist/meter/meters.d.ts.map +1 -0
  84. package/dist/meter/meters.js +44 -0
  85. package/dist/meter/meters.js.map +1 -0
  86. package/dist/meter/plans.d.ts +63 -0
  87. package/dist/meter/plans.d.ts.map +1 -0
  88. package/dist/meter/plans.js +114 -0
  89. package/dist/meter/plans.js.map +1 -0
  90. package/dist/meter/pricebook.d.ts +32 -0
  91. package/dist/meter/pricebook.d.ts.map +1 -0
  92. package/dist/meter/pricebook.js +38 -0
  93. package/dist/meter/pricebook.js.map +1 -0
  94. package/dist/meter/resolve.d.ts +103 -0
  95. package/dist/meter/resolve.d.ts.map +1 -0
  96. package/dist/meter/resolve.js +100 -0
  97. package/dist/meter/resolve.js.map +1 -0
  98. package/dist/meter/sink.d.ts +26 -0
  99. package/dist/meter/sink.d.ts.map +1 -0
  100. package/dist/meter/sink.js +9 -0
  101. package/dist/meter/sink.js.map +1 -0
  102. package/dist/meter/sql/d1.d.ts +32 -0
  103. package/dist/meter/sql/d1.d.ts.map +1 -0
  104. package/dist/meter/sql/d1.js +41 -0
  105. package/dist/meter/sql/d1.js.map +1 -0
  106. package/dist/meter/sql/driver.d.ts +34 -0
  107. package/dist/meter/sql/driver.d.ts.map +1 -0
  108. package/dist/meter/sql/driver.js +17 -0
  109. package/dist/meter/sql/driver.js.map +1 -0
  110. package/dist/meter/sql/postgres.d.ts +23 -0
  111. package/dist/meter/sql/postgres.d.ts.map +1 -0
  112. package/dist/meter/sql/postgres.js +36 -0
  113. package/dist/meter/sql/postgres.js.map +1 -0
  114. package/dist/meter/sql/schema.d.ts +13 -0
  115. package/dist/meter/sql/schema.d.ts.map +1 -0
  116. package/dist/meter/sql/schema.js +32 -0
  117. package/dist/meter/sql/schema.js.map +1 -0
  118. package/dist/meter/sql/sql-adapter.d.ts +36 -0
  119. package/dist/meter/sql/sql-adapter.d.ts.map +1 -0
  120. package/dist/meter/sql/sql-adapter.js +124 -0
  121. package/dist/meter/sql/sql-adapter.js.map +1 -0
  122. package/dist/meter/sql/statements.d.ts +30 -0
  123. package/dist/meter/sql/statements.d.ts.map +1 -0
  124. package/dist/meter/sql/statements.js +24 -0
  125. package/dist/meter/sql/statements.js.map +1 -0
  126. package/dist/meter/types.d.ts +48 -0
  127. package/dist/meter/types.d.ts.map +1 -0
  128. package/dist/meter/types.js +8 -0
  129. package/dist/meter/types.js.map +1 -0
  130. package/dist/meter/units.d.ts +33 -0
  131. package/dist/meter/units.d.ts.map +1 -0
  132. package/dist/meter/units.js +45 -0
  133. package/dist/meter/units.js.map +1 -0
  134. package/dist/meter/wallet-buckets.d.ts +63 -0
  135. package/dist/meter/wallet-buckets.d.ts.map +1 -0
  136. package/dist/meter/wallet-buckets.js +131 -0
  137. package/dist/meter/wallet-buckets.js.map +1 -0
  138. package/dist/meter/wallet-charge.d.ts +15 -0
  139. package/dist/meter/wallet-charge.d.ts.map +1 -0
  140. package/dist/meter/wallet-charge.js +64 -0
  141. package/dist/meter/wallet-charge.js.map +1 -0
  142. package/dist/meter/wallet.d.ts +68 -0
  143. package/dist/meter/wallet.d.ts.map +1 -0
  144. package/dist/meter/wallet.js +47 -0
  145. package/dist/meter/wallet.js.map +1 -0
  146. package/dist/receipts-api.d.ts +5 -0
  147. package/dist/receipts-api.d.ts.map +1 -0
  148. package/dist/receipts-api.js +117 -0
  149. package/dist/receipts-api.js.map +1 -0
  150. package/dist/receipts-chain.d.ts +3 -0
  151. package/dist/receipts-chain.d.ts.map +1 -0
  152. package/dist/receipts-chain.js +121 -0
  153. package/dist/receipts-chain.js.map +1 -0
  154. package/dist/receipts-modal.d.ts +14 -0
  155. package/dist/receipts-modal.d.ts.map +1 -0
  156. package/dist/receipts-modal.js +100 -0
  157. package/dist/receipts-modal.js.map +1 -0
  158. package/dist/receipts-registry.d.ts +4 -0
  159. package/dist/receipts-registry.d.ts.map +1 -0
  160. package/dist/receipts-registry.js +54 -0
  161. package/dist/receipts-registry.js.map +1 -0
  162. package/dist/receipts-render.d.ts +9 -0
  163. package/dist/receipts-render.d.ts.map +1 -0
  164. package/dist/receipts-render.js +83 -0
  165. package/dist/receipts-render.js.map +1 -0
  166. package/dist/receipts-types.d.ts +61 -0
  167. package/dist/receipts-types.d.ts.map +1 -0
  168. package/dist/receipts-types.js +12 -0
  169. package/dist/receipts-types.js.map +1 -0
  170. package/dist/receipts-ui.d.ts +10 -0
  171. package/dist/receipts-ui.d.ts.map +1 -0
  172. package/dist/receipts-ui.js +103 -0
  173. package/dist/receipts-ui.js.map +1 -0
  174. package/dist/receipts-verify.d.ts +8 -0
  175. package/dist/receipts-verify.d.ts.map +1 -0
  176. package/dist/receipts-verify.js +113 -0
  177. package/dist/receipts-verify.js.map +1 -0
  178. package/dist/receipts.d.ts +9 -0
  179. package/dist/receipts.d.ts.map +1 -0
  180. package/dist/receipts.global.js +587 -0
  181. package/dist/receipts.global.text.js +2 -0
  182. package/dist/receipts.js +28 -0
  183. package/dist/receipts.js.map +1 -0
  184. package/dist/sso/index.d.ts +4 -0
  185. package/dist/sso/index.d.ts.map +1 -0
  186. package/dist/sso/index.js +10 -0
  187. package/dist/sso/index.js.map +1 -0
  188. package/dist/sso/jwt.d.ts +29 -0
  189. package/dist/sso/jwt.d.ts.map +1 -0
  190. package/dist/sso/jwt.js +73 -0
  191. package/dist/sso/jwt.js.map +1 -0
  192. package/dist/sso/launch.d.ts +70 -0
  193. package/dist/sso/launch.d.ts.map +1 -0
  194. package/dist/sso/launch.js +75 -0
  195. package/dist/sso/launch.js.map +1 -0
  196. package/dist/sso/revocation.d.ts +23 -0
  197. package/dist/sso/revocation.d.ts.map +1 -0
  198. package/dist/sso/revocation.js +53 -0
  199. package/dist/sso/revocation.js.map +1 -0
  200. package/dist/switcher.d.ts +22 -0
  201. package/dist/switcher.d.ts.map +1 -0
  202. package/dist/switcher.js +174 -0
  203. package/dist/switcher.js.map +1 -0
  204. package/dist/x402/chain.d.ts +49 -0
  205. package/dist/x402/chain.d.ts.map +1 -0
  206. package/dist/x402/chain.js +33 -0
  207. package/dist/x402/chain.js.map +1 -0
  208. package/dist/x402/crypto.d.ts +25 -0
  209. package/dist/x402/crypto.d.ts.map +1 -0
  210. package/dist/x402/crypto.js +74 -0
  211. package/dist/x402/crypto.js.map +1 -0
  212. package/dist/x402/eip712.d.ts +20 -0
  213. package/dist/x402/eip712.d.ts.map +1 -0
  214. package/dist/x402/eip712.js +49 -0
  215. package/dist/x402/eip712.js.map +1 -0
  216. package/dist/x402/envelope.d.ts +45 -0
  217. package/dist/x402/envelope.d.ts.map +1 -0
  218. package/dist/x402/envelope.js +50 -0
  219. package/dist/x402/envelope.js.map +1 -0
  220. package/dist/x402/index.d.ts +41 -0
  221. package/dist/x402/index.d.ts.map +1 -0
  222. package/dist/x402/index.js +41 -0
  223. package/dist/x402/index.js.map +1 -0
  224. package/dist/x402/verify.d.ts +51 -0
  225. package/dist/x402/verify.d.ts.map +1 -0
  226. package/dist/x402/verify.js +110 -0
  227. package/dist/x402/verify.js.map +1 -0
  228. package/package.json +100 -0
@@ -0,0 +1,2 @@
1
+ // AUTO-GENERATED by scripts/wrap-text.mjs from dist/receipts.global.js - do not edit.
2
+ export default "\"use strict\";\nvar drm3Receipts = (() => {\n var __defProp = Object.defineProperty;\n var __getOwnPropDesc = Object.getOwnPropertyDescriptor;\n var __getOwnPropNames = Object.getOwnPropertyNames;\n var __hasOwnProp = Object.prototype.hasOwnProperty;\n var __export = (target, all) => {\n for (var name in all)\n __defProp(target, name, { get: all[name], enumerable: true });\n };\n var __copyProps = (to, from, except, desc) => {\n if (from && typeof from === \"object\" || typeof from === \"function\") {\n for (let key of __getOwnPropNames(from))\n if (!__hasOwnProp.call(to, key) && key !== except)\n __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });\n }\n return to;\n };\n var __toCommonJS = (mod) => __copyProps(__defProp({}, \"__esModule\", { value: true }), mod);\n\n // src/receipts.ts\n var receipts_exports = {};\n __export(receipts_exports, {\n ALERT: () => ALERT,\n CHECK: () => CHECK,\n DOWNLOAD: () => DOWNLOAD,\n FAILED: () => FAILED,\n HEX: () => HEX,\n LOCK: () => LOCK,\n RECEIPT: () => RECEIPT,\n SPIN: () => SPIN,\n STAMP: () => STAMP,\n UNVERIFIABLE_NO_ACCESS: () => UNVERIFIABLE_NO_ACCESS,\n VERIFIED: () => VERIFIED,\n appendDownload: () => appendDownload,\n buildModal: () => buildModal,\n canonicalize: () => canonicalize,\n countsLine: () => countsLine,\n esc: () => esc,\n hashHex: () => hashHex,\n injectStyles: () => injectStyles,\n isEd25519Supported: () => isEd25519Supported,\n legDetail: () => legDetail,\n loadRegistry: () => loadRegistry,\n monoColor: () => monoColor,\n mountVerifyButton: () => mountVerifyButton,\n openReceiptById: () => openReceiptById,\n openReceiptModal: () => openReceiptModal,\n prettyAction: () => prettyAction,\n resolveReceipt: () => resolveReceipt,\n sealVerdict: () => sealVerdict,\n sha256Hex: () => sha256Hex,\n shortHex: () => shortHex,\n shortKey: () => shortKey,\n verifyChainInto: () => verifyChainInto,\n verifyContentSig: () => verifyContentSig,\n verifyReceipt: () => verifyReceipt\n });\n\n // src/receipts-render.ts\n var esc = (s) => String(s).replace(/[&<>\"]/g, (c) => ({ \"&\": \"&amp;\", \"<\": \"&lt;\", \">\": \"&gt;\", '\"': \"&quot;\" })[c]);\n function shortHex(h) {\n const x = String(h || \"\").replace(/^(ed25519|sha256|sha1):/, \"\");\n return x ? `${x.slice(0, 10)}\\u2026${x.slice(-6)}` : \"-\";\n }\n function shortKey(k) {\n const h = String(k || \"\").replace(/^ed25519:/, \"\");\n return h ? `ed25519:${h.slice(0, 8)}\\u2026${h.slice(-6)}` : \"-\";\n }\n function prettyAction(a) {\n if (/fetch/i.test(a)) return \"Fetched & extracted\";\n if (/analy/i.test(a)) return \"AI analysis\";\n if (/scrape|extract/i.test(a)) return \"Content extracted\";\n if (/digest|compose/i.test(a)) return \"Composed\";\n if (/infer/i.test(a)) return \"Inference\";\n return String(a || \"receipt\").replace(/[._-]/g, \" \").replace(/\\b\\w/g, (c) => c.toUpperCase());\n }\n function monoColor(s) {\n let h = 0;\n for (let i = 0; i < s.length; i++) h = (h * 31 + s.charCodeAt(i)) % 360;\n return `hsl(${h} 52% 64%)`;\n }\n function countsLine(c) {\n return `<span class=\"drm3v-counts\">${c.verified} verified \\xB7 ${c.gated} not accessible \\xB7 ${c.failed} failed</span>`;\n }\n function fieldRow(label, value, mono = false) {\n return `<div class=\"drm3v-kv\"><span>${esc(label)}</span>${mono ? `<code>${esc(value)}</code>` : `<b>${esc(value)}</b>`}</div>`;\n }\n function legDetail(payload, contentRef) {\n const meta = payload?.metadata?._meta || {};\n const rows = [];\n const model = String(meta.model || \"\");\n const provider = String(meta.provider || \"\");\n if (model) rows.push(fieldRow(\"model\", provider ? `${model} \\xB7 ${provider}` : model));\n else if (meta.extractor) rows.push(fieldRow(\"extractor\", String(meta.extractor)));\n const ih = String(payload.input_hash || \"\");\n const oh = String(payload.output_hash || \"\");\n if (ih) rows.push(fieldRow(\"input\", shortHex(ih), true));\n if (oh) rows.push(fieldRow(\"output\", shortHex(oh), true));\n if (payload.timestamp) {\n const d = new Date(String(payload.timestamp));\n if (!Number.isNaN(d.getTime())) rows.push(fieldRow(\"signed\", d.toUTCString()));\n }\n const att = String(meta.attestation || \"\");\n const attHtml = att ? `<div class=\"drm3v-att\">${esc(att)}</div>` : \"\";\n const links = [];\n if (contentRef?.ourUrl) links.push(`<a class=\"drm3v-clink\" href=\"${esc(contentRef.ourUrl)}\" target=\"_blank\" rel=\"noopener\">read the copy \\u2197</a>`);\n if (contentRef?.origUrl)\n links.push(`<a class=\"drm3v-clink\" href=\"${esc(contentRef.origUrl)}\" target=\"_blank\" rel=\"noopener noreferrer\">original source \\u2197</a>`);\n const linksHtml = links.length ? `<div class=\"drm3v-clinks\">${links.join(\"\")}</div>` : \"\";\n if (!rows.length && !attHtml && !linksHtml) return \"\";\n return `<div class=\"drm3v-detail\">${rows.join(\"\")}${attHtml}${linksHtml}</div>`;\n }\n\n // src/receipts-types.ts\n var VERIFIED = \"verified\";\n var UNVERIFIABLE_NO_ACCESS = \"unverifiable_no_access\";\n var FAILED = \"failed\";\n\n // src/receipts-verify.ts\n function canonicalize(obj) {\n if (obj === null || obj === void 0) return JSON.stringify(obj);\n if (Array.isArray(obj)) return `[${obj.map(canonicalize).join(\",\")}]`;\n if (typeof obj === \"object\") {\n const keys = Object.keys(obj).map((k) => k.normalize(\"NFC\")).sort();\n return `{${keys.map((k) => `${JSON.stringify(k)}:${canonicalize(obj[k])}`).join(\",\")}}`;\n }\n if (typeof obj === \"string\") return JSON.stringify(obj.normalize(\"NFC\"));\n return JSON.stringify(obj);\n }\n function hexToBytes(hex) {\n const out = new Uint8Array(hex.length / 2);\n for (let i = 0; i < hex.length; i += 2) out[i / 2] = Number.parseInt(hex.substr(i, 2), 16);\n return out;\n }\n var SPEC11_FIELDS = [\"purpose\", \"caller\", \"inputs\", \"outputs\", \"content_sig\"];\n async function verifyReceipt(r) {\n const p = typeof r.receipt_json === \"string\" ? JSON.parse(r.receipt_json) : r.receipt_json;\n if (!p) return false;\n const sig = (r.signature || p.signature || \"\").replace(/^ed25519:/, \"\");\n const pub = (r.public_key || p.public_key || \"\").replace(/^ed25519:/, \"\");\n if (!sig || !pub) return false;\n const key = await crypto.subtle.importKey(\"raw\", hexToBytes(pub), { name: \"Ed25519\" }, false, [\"verify\"]);\n const sigBytes = hexToBytes(sig);\n const payloadFor = (ts) => {\n const base = {\n id: p.id,\n action: p.action,\n timestamp: ts,\n input_hash: p.input_hash,\n output_hash: p.output_hash,\n cost: p.cost ?? null,\n duration_ms: p.duration_ms ?? null,\n parent_id: p.parent_id ?? null,\n metadata: p.metadata ?? null,\n public_key: p.public_key\n };\n for (const k of SPEC11_FIELDS) {\n if (p[k] !== void 0 && p[k] !== null) base[k] = p[k];\n }\n return base;\n };\n const tryVerify = (ts) => crypto.subtle.verify(\"Ed25519\", key, sigBytes, new TextEncoder().encode(canonicalize(payloadFor(ts))));\n if (await tryVerify(p.timestamp)) return true;\n if (typeof p.timestamp === \"string\" && p.timestamp.endsWith(\"Z\")) {\n if (await tryVerify(`${p.timestamp.slice(0, -1)}+00:00`)) return true;\n }\n return false;\n }\n async function verifyContentSig(contentSig, publicKey, body) {\n const sig = String(contentSig || \"\").replace(/^ed25519:/, \"\");\n const pub = String(publicKey || \"\").replace(/^ed25519:/, \"\");\n if (!sig || !pub || !body || !crypto?.subtle) return null;\n try {\n const key = await crypto.subtle.importKey(\"raw\", hexToBytes(pub), { name: \"Ed25519\" }, false, [\"verify\"]);\n return await crypto.subtle.verify(\"Ed25519\", key, hexToBytes(sig), new TextEncoder().encode(body));\n } catch {\n return null;\n }\n }\n async function hashHex(algo, s) {\n const buf = await crypto.subtle.digest(algo, new TextEncoder().encode(s));\n return [...new Uint8Array(buf)].map((b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n }\n var sha256Hex = (s) => hashHex(\"SHA-256\", s);\n async function isEd25519Supported() {\n try {\n await crypto.subtle.generateKey({ name: \"Ed25519\" }, false, [\"sign\", \"verify\"]);\n return true;\n } catch {\n return false;\n }\n }\n\n // src/receipts-chain.ts\n function parentRefs(r, pj) {\n const out = [];\n const add = (v) => {\n if (typeof v === \"string\" && v) out.push(v);\n };\n add(r.parent_receipt_id);\n add(pj?.parent_id);\n for (const e of pj?.inputs || []) if (e && typeof e === \"object\") add(e.ref);\n return out;\n }\n function safeParse(s) {\n try {\n return JSON.parse(s);\n } catch {\n return null;\n }\n }\n async function verifyChainInto(chain, registry, addStep, settle, wait, contentRef) {\n const counts = { verified: 0, gated: 0, failed: 0 };\n const present = /* @__PURE__ */ new Set();\n for (const r of chain) {\n const pj = typeof r.receipt_json === \"string\" ? safeParse(r.receipt_json) : r.receipt_json;\n if (r.id) present.add(r.id);\n if (typeof pj?.id === \"string\") present.add(pj.id);\n }\n const referencedGated = /* @__PURE__ */ new Set();\n for (const r of chain) {\n const pj = typeof r.receipt_json === \"string\" ? safeParse(r.receipt_json) : r.receipt_json;\n for (const pid of parentRefs(r, pj)) if (!present.has(pid)) referencedGated.add(pid);\n }\n const ordered = [...chain].reverse();\n for (const r of ordered) {\n const payload = typeof r.receipt_json === \"string\" ? JSON.parse(r.receipt_json) : r.receipt_json;\n const action = r.action || payload?.action || r.receipt_type || \"receipt\";\n const pub = (r.public_key || payload?.public_key || \"\").toLowerCase();\n const signer = r.signer_path || payload?.metadata?._meta?.signer || \"\";\n const el = addStep(prettyAction(action), signer ? `signer ${signer} \\xB7 ${shortKey(pub)}` : shortKey(pub), legDetail(payload, contentRef));\n await wait(420);\n let ok = false;\n try {\n ok = await verifyReceipt(r);\n } catch {\n ok = false;\n }\n const rec = registry.get(pub);\n const known = registry.size === 0 || !!rec;\n const ts = payload?.timestamp || r.timestamp || \"\";\n let verdict = ok && known ? VERIFIED : FAILED;\n let note = \"\";\n if (ok && rec) {\n if (rec.status === \"compromised\" || rec.status === \"revoked\") {\n verdict = FAILED;\n note = ` \\xB7 \\u26A0 key ${rec.status}`;\n } else if (rec.status === \"superseded\" || rec.status === \"retired\") {\n const inWindow = (!rec.valid_from || !ts || ts >= rec.valid_from) && (!rec.valid_until || !ts || ts <= rec.valid_until);\n if (inWindow) {\n verdict = VERIFIED;\n note = ` \\xB7 key since rotated${rec.valid_until ? ` ${rec.valid_until.slice(0, 10)}` : \"\"} (verified)`;\n } else {\n verdict = FAILED;\n note = \" \\xB7 \\u26A0 signed outside the key's validity window\";\n }\n }\n } else if (ok && !known) {\n note = \" \\xB7 \\u26A0 key not found in registry\";\n }\n settle(el, verdict);\n if (verdict === VERIFIED) counts.verified++;\n else counts.failed++;\n if (note) {\n const meta = el.querySelector(\".drm3v-meta\");\n if (meta) meta.textContent += note;\n }\n await wait(180);\n }\n for (const pid of referencedGated) {\n const el = addStep(\n \"Upstream step (not accessible)\",\n `referenced ${shortHex(pid)} \\xB7 receipt not retrievable to you`,\n `<div class=\"drm3v-detail\"><div class=\"drm3v-clinks\"><a class=\"drm3v-clink\" href=\"#\" onclick=\"event.preventDefault();event.stopPropagation();window.drm3Receipts&&window.drm3Receipts.openReceiptById('${esc(pid)}')\">resolve this receipt</a></div></div>`\n );\n await wait(280);\n settle(el, UNVERIFIABLE_NO_ACCESS);\n counts.gated++;\n await wait(120);\n }\n return counts;\n }\n\n // src/receipts-ui.ts\n var CHECK = '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.4\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M20 6 9 17l-5-5\"/></svg>';\n var LOCK = '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.4\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"5\" y=\"11\" width=\"14\" height=\"9\" rx=\"2\"/><path d=\"M8 11V7a4 4 0 0 1 8 0v4\"/></svg>';\n var HEX = '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.7\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M12 2.5 20.5 7.2v9.6L12 21.5 3.5 16.8V7.2z\"/><path d=\"m8.7 12 2.2 2.2 4.4-4.4\"/></svg>';\n var STAMP = '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.7\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"9\"/><circle cx=\"12\" cy=\"12\" r=\"5.4\" stroke-dasharray=\"1.4 2.3\"/><path d=\"m9.5 12 1.8 1.8 3.6-3.7\"/></svg>';\n var ALERT = '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M12 9v4m0 4h.01M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0z\"/></svg>';\n var RECEIPT = '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M5 3v18l2-1.2L9 21l2-1.2L13 21l2-1.2L17 21l2-1.2V3l-2 1.2L15 3l-2 1.2L11 3 9 4.2 7 3z\"/><path d=\"M8 8.5h8M8 12h8M8 15.5h5\" stroke-width=\"1.4\"/></svg>';\n var SPIN = '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"3\" stroke-linecap=\"round\"><path d=\"M12 3a9 9 0 1 0 9 9\" opacity=\".95\"/></svg>';\n var DOWNLOAD = '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M12 3v12m0 0 4-4m-4 4-4-4M5 21h14\"/></svg>';\n function injectStyles() {\n if (typeof document === \"undefined\" || document.getElementById(\"drm3v-css\")) return;\n const s = document.createElement(\"style\");\n s.id = \"drm3v-css\";\n s.textContent = `\n .drm3v-trigger{display:inline-flex;align-items:center;gap:7px;margin:12px 0;padding:9px 15px;font:inherit;font-size:.82rem;font-weight:600;\n color:#0a0a10;background:linear-gradient(180deg,#7fe3b0,#46c98a);border:none;border-radius:999px;cursor:pointer;transition:transform .12s,filter .15s}\n .drm3v-trigger:hover{transform:translateY(-1px);filter:brightness(1.05)}\n .drm3v-trigger svg{width:15px;height:15px}\n .drm3v-ov{position:fixed;inset:0;z-index:99990;display:flex;align-items:center;justify-content:center;padding:20px;\n background:rgba(4,4,9,.78);backdrop-filter:blur(7px);-webkit-backdrop-filter:blur(7px);animation:drm3v-fade .18s ease}\n @keyframes drm3v-fade{from{opacity:0}to{opacity:1}}\n .drm3v-modal{width:min(600px,100%);max-height:90vh;overflow:auto;background:linear-gradient(180deg,#13131f,#0d0d16);\n border:1px solid #2a2a3f;border-radius:20px;box-shadow:0 30px 90px rgba(0,0,0,.66);padding:22px 22px 18px;font-family:ui-sans-serif,system-ui,sans-serif;color:#e8e8f2}\n .drm3v-modal::-webkit-scrollbar{width:10px}\n .drm3v-modal::-webkit-scrollbar-thumb{background:#2a2a3f;border-radius:8px;border:3px solid transparent;background-clip:padding-box}\n .drm3v-h{display:flex;align-items:center;justify-content:space-between;margin-bottom:4px}\n .drm3v-h h3{font-size:1.02rem;margin:0;color:#fff;display:flex;align-items:center;gap:9px}\n .drm3v-h h3 svg{width:18px;height:18px;color:#7fe3b0}\n .drm3v-x{background:none;border:none;color:#a8a8be;font-size:1.4rem;line-height:1;cursor:pointer}\n .drm3v-x:hover{color:#e8e8f2}\n .drm3v-sub{font-size:.79rem;color:#b6b6c8;margin-bottom:14px;line-height:1.5}\n .drm3v-rcpt{position:relative;overflow:hidden;border:1px solid #2c2c42;border-radius:16px;padding:15px 17px 14px;margin:4px 0 2px;\n background:radial-gradient(135% 150% at 0 0,rgba(70,201,138,.13),transparent 56%),#15151f}\n .drm3v-rcpt::after{content:\"\";position:absolute;left:0;top:0;bottom:0;width:3px;background:linear-gradient(180deg,#7fe3b0,#46c98a)}\n .drm3v-rcpt.part::after{background:linear-gradient(180deg,#f3d09a,#e3a24b)}\n .drm3v-rcpt.bad::after{background:linear-gradient(180deg,#ffb3c0,#e0556b)}\n .drm3v-rcpt.wait::after{background:linear-gradient(180deg,#3a3a52,#2a2a3d)}\n .drm3v-rtop{display:flex;align-items:center;gap:7px;font-size:.69rem;font-weight:700;letter-spacing:.11em;text-transform:uppercase;color:#9ff0c4}\n .drm3v-rtop .svc{color:#fff}\n .drm3v-rtop .dep{color:#a8a8be;font-weight:600}\n .drm3v-raction{margin-left:auto;font-family:ui-monospace,Menlo,monospace;font-size:.69rem;font-weight:600;color:#cfcfe0;\n background:rgba(141,141,163,.14);border:1px solid #2c2c42;padding:2px 9px;border-radius:999px;letter-spacing:0;text-transform:none}\n .drm3v-rsigner{display:flex;align-items:center;gap:9px;margin-top:12px}\n .drm3v-rsigner>svg{flex:none;width:20px;height:20px;color:#7fe3b0}\n .drm3v-rname{font-weight:700;font-size:.95rem;color:#fff;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n .drm3v-rstate{margin-left:auto;display:inline-flex;align-items:center;gap:5px;font-size:.71rem;font-weight:700;\n padding:4px 10px;border-radius:999px;border:1px solid;white-space:nowrap;flex:none}\n .drm3v-rstate svg{width:13px;height:13px}\n .drm3v-rstate.ok{color:#9ff0c4;background:rgba(70,201,138,.15);border-color:rgba(70,201,138,.45)}\n .drm3v-rstate.wait{color:#c2c2d4;background:rgba(141,141,163,.14);border-color:#34344a}\n .drm3v-rstate.part{color:#f3d09a;background:rgba(227,162,75,.15);border-color:rgba(227,162,75,.45)}\n .drm3v-rstate.bad{color:#ffb3c0;background:rgba(224,85,107,.15);border-color:rgba(224,85,107,.45)}\n .drm3v-rstate.spin svg{animation:drm3v-spin .7s linear infinite}\n .drm3v-rkey{font-family:ui-monospace,Menlo,monospace;font-size:.71rem;color:#a8a8be;margin-top:9px;word-break:break-all}\n .drm3v-rnote{font-size:.77rem;color:#bdbdd0;margin-top:8px;line-height:1.45}\n .drm3v-rnote b{color:#fff}\n .drm3v-rseal{position:absolute;right:-12px;bottom:-18px;width:94px;height:94px;color:#46c98a;opacity:.09;transform:rotate(-11deg);pointer-events:none}\n .drm3v-rcpt.part .drm3v-rseal{color:#e3a24b}\n .drm3v-rcpt.bad .drm3v-rseal{color:#e0556b}\n .drm3v-railhead{display:flex;align-items:center;gap:8px;font-size:.65rem;font-weight:700;text-transform:uppercase;letter-spacing:.09em;color:#a8a8be;margin:17px 0 4px}\n .drm3v-railhead .ct{color:#cfcfe0;font-family:ui-monospace,Menlo,monospace;letter-spacing:0;background:rgba(141,141,163,.14);border-radius:999px;padding:1px 8px}\n .drm3v-step{display:flex;gap:12px;padding:11px 0;border-top:1px solid #1d1d2c;opacity:.35;transform:translateY(4px);transition:opacity .4s,transform .4s}\n .drm3v-step.on{opacity:1;transform:none}\n .drm3v-dot{flex:none;width:22px;height:22px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:.7rem;\n background:#1d1d2c;color:#a8a8be;border:1px solid #2a2a3d}\n .drm3v-dot svg{width:13px;height:13px}\n .drm3v-dot.ok{background:#46c98a;color:#0a0a10;border-color:#46c98a}\n .drm3v-dot.warn{background:rgba(227,162,75,.18);color:#f3d09a;border-color:rgba(227,162,75,.5)}\n .drm3v-dot.bad{background:#e0556b;color:#fff;border-color:#e0556b}\n .drm3v-dot.spin{border-top-color:#7fe3b0;animation:drm3v-spin .7s linear infinite}\n @keyframes drm3v-spin{to{transform:rotate(360deg)}}\n .drm3v-body{flex:1;min-width:0}\n .drm3v-act{font-weight:600;font-size:.88rem;color:#fff}\n .drm3v-meta{font-size:.71rem;color:#a8a8be;margin-top:3px;word-break:break-all;font-family:ui-monospace,Menlo,monospace}\n .drm3v-detail{margin:8px 0 2px;padding:9px 11px;background:#0e0e16;border:1px solid #1d1d2c;border-radius:9px}\n .drm3v-kv{display:flex;gap:9px;font-size:.71rem;line-height:1.5;margin:2px 0}\n .drm3v-kv>span{flex:none;width:56px;color:#a8a8be;text-transform:uppercase;letter-spacing:.05em;font-size:.64rem;font-weight:700;padding-top:2px}\n .drm3v-kv>b{color:#e8e8f2;font-weight:600;min-width:0;word-break:break-word}\n .drm3v-kv>code{font-family:ui-monospace,Menlo,monospace;color:#9ff0c4;word-break:break-all;font-size:.7rem}\n .drm3v-att{margin-top:8px;font-size:.72rem;line-height:1.55;color:#a6a6bd;border-left:2px solid #2a2a3d;padding:1px 0 1px 9px}\n .drm3v-clinks{display:flex;flex-wrap:wrap;gap:8px;margin-top:9px}\n .drm3v-clink{font-size:.72rem;font-weight:600;color:#7fe3b0;text-decoration:none;border:1px solid rgba(70,201,138,.32);border-radius:999px;padding:3px 11px;transition:background .14s,border-color .14s;cursor:pointer}\n .drm3v-clink:hover{background:rgba(70,201,138,.13);border-color:rgba(70,201,138,.5)}\n .drm3v-dlbtn{display:inline-flex;align-items:center;gap:7px;margin-top:13px;padding:8px 14px;font:inherit;font-size:.78rem;font-weight:600;\n color:#cfcfe0;background:rgba(141,141,163,.12);border:1px solid #34344a;border-radius:9px;cursor:pointer;transition:border-color .14s,background .14s}\n .drm3v-dlbtn:hover{border-color:#7fe3b0;color:#9ff0c4;background:rgba(70,201,138,.1)}\n .drm3v-dlbtn svg{width:14px;height:14px}\n .drm3v-final{margin-top:16px;padding:16px;border-radius:14px;font-size:.86rem;line-height:1.5}\n .drm3v-final.ok{background:rgba(70,201,138,.10);border:1px solid rgba(70,201,138,.4);color:#cdeede}\n .drm3v-final.part{background:rgba(227,162,75,.10);border:1px solid rgba(227,162,75,.4);color:#ecd6ad}\n .drm3v-final.bad{background:rgba(224,85,107,.10);border:1px solid rgba(224,85,107,.4);color:#f3c0c9}\n .drm3v-final b{color:#fff}\n .drm3v-final svg{width:16px;height:16px;vertical-align:-3px;margin-right:4px}\n .drm3v-counts{display:block;margin-top:8px;font-size:.78rem;font-family:ui-monospace,Menlo,monospace;color:inherit;opacity:.92}`;\n document.head.appendChild(s);\n }\n\n // src/receipts-modal.ts\n function dlJSON(filename, obj) {\n const blob = new Blob([JSON.stringify(obj, null, 2)], { type: \"application/json\" });\n const url = URL.createObjectURL(blob);\n const a = document.createElement(\"a\");\n a.href = url;\n a.download = filename;\n a.click();\n setTimeout(() => URL.revokeObjectURL(url), 1e3);\n }\n function appendDownload(host, filename, obj) {\n if (!obj) return;\n const btn = document.createElement(\"button\");\n btn.type = \"button\";\n btn.className = \"drm3v-dlbtn\";\n btn.innerHTML = `${DOWNLOAD}<span>Download receipt JSON</span>`;\n btn.addEventListener(\"click\", () => dlJSON(filename, obj));\n host.appendChild(btn);\n }\n function buildModal(title, subtitle) {\n injectStyles();\n const ov = document.createElement(\"div\");\n ov.className = \"drm3v-ov\";\n ov.innerHTML = `<div class=\"drm3v-modal\" role=\"dialog\" aria-label=\"${esc(title)}\">\n <div class=\"drm3v-h\"><h3>${RECEIPT}${esc(title)}</h3><button class=\"drm3v-x\" aria-label=\"Close\">&times;</button></div>\n <div class=\"drm3v-sub\">${subtitle}</div>\n <div class=\"drm3v-steps\"></div>\n <div class=\"drm3v-final\"></div></div>`;\n document.body.appendChild(ov);\n const close = () => ov.remove();\n ov.addEventListener(\"click\", (e) => {\n if (e.target === ov) close();\n });\n ov.querySelector(\".drm3v-x\")?.addEventListener(\"click\", close);\n const onEsc = (e) => {\n if (e.key === \"Escape\") {\n close();\n document.removeEventListener(\"keydown\", onEsc);\n }\n };\n document.addEventListener(\"keydown\", onEsc);\n const steps = ov.querySelector(\".drm3v-steps\");\n const final = ov.querySelector(\".drm3v-final\");\n const addStep = (act, meta, detail) => {\n const el = document.createElement(\"div\");\n el.className = \"drm3v-step\";\n el.innerHTML = `<div class=\"drm3v-dot spin\"></div><div class=\"drm3v-body\"><div class=\"drm3v-act\">${esc(act)}</div><div class=\"drm3v-meta\">${esc(meta)}</div>${detail || \"\"}</div>`;\n steps.appendChild(el);\n requestAnimationFrame(() => el.classList.add(\"on\"));\n return el;\n };\n const settle = (el, verdict) => {\n const dot = el.querySelector(\".drm3v-dot\");\n if (verdict === VERIFIED) {\n dot.className = \"drm3v-dot ok\";\n dot.innerHTML = CHECK;\n } else if (verdict === UNVERIFIABLE_NO_ACCESS) {\n dot.className = \"drm3v-dot warn\";\n dot.innerHTML = LOCK;\n } else {\n dot.className = \"drm3v-dot bad\";\n dot.innerHTML = \"&times;\";\n }\n };\n const wait = (ms) => new Promise((r) => setTimeout(r, ms));\n return { ov, steps, final, addStep, settle, wait };\n }\n function sealVerdict(final, counts) {\n if (counts.verified + counts.gated + counts.failed === 0) {\n final.className = \"drm3v-final bad\";\n final.textContent = \"No receipts were found to verify.\";\n return;\n }\n if (counts.failed > 0) {\n final.className = \"drm3v-final bad\";\n final.innerHTML = `One or more signatures did not verify. That's exactly what provenance is for - it tells you when something doesn't add up.${countsLine(counts)}`;\n } else if (counts.gated > 0) {\n final.className = \"drm3v-final part\";\n final.innerHTML = `${LOCK} <b>Verified in part.</b> Every receipt you could retrieve carries a valid Ed25519 signature from a registered DRM3 signer, checked right here in your browser. ${counts.gated} upstream step${counts.gated === 1 ? \"\" : \"s\"} ${counts.gated === 1 ? \"is\" : \"are\"} referenced but not accessible to you - a normal access boundary, not a failure.${countsLine(counts)}`;\n } else {\n final.className = \"drm3v-final ok\";\n final.innerHTML = `${CHECK} <b>Verified.</b> Every receipt in this chain carries a valid Ed25519 signature from a registered DRM3 signer - checked right here in your browser.${countsLine(counts)}`;\n }\n }\n\n // src/receipts-registry.ts\n var DEFAULT_REGISTRY_URL = \"https://status.drm3.network/.well-known/drm3-keys.json\";\n var registryCache = /* @__PURE__ */ new Map();\n async function loadRegistry(url = DEFAULT_REGISTRY_URL) {\n const cached = registryCache.get(url);\n if (cached) return cached;\n const map = /* @__PURE__ */ new Map();\n try {\n const reg = await (await fetch(url)).json();\n for (const svc of reg.services || []) {\n for (const k of svc.current || [])\n if (k.key) map.set(k.key.toLowerCase(), { status: k.status || \"current\", valid_from: k.valid_from, valid_until: k.valid_until });\n for (const k of svc.history || [])\n if (k.key && !map.has(k.key.toLowerCase()))\n map.set(k.key.toLowerCase(), { status: k.status || \"superseded\", valid_from: k.valid_from, valid_until: k.valid_until });\n }\n for (const k of reg.keys || []) if (k.key && !map.has(k.key.toLowerCase())) map.set(k.key.toLowerCase(), { status: \"current\" });\n } catch {\n }\n registryCache.set(url, map);\n return map;\n }\n async function resolveReceipt(id, cfg = {}) {\n try {\n const base = cfg.resolverBase || \"\";\n const r = await fetch(`${base}/receipts/${encodeURIComponent(id)}`, cfg.headers ? { headers: cfg.headers } : void 0);\n if (!r.ok) return null;\n const j = await r.json();\n return j?.ok && j.receipt ? j : null;\n } catch {\n return null;\n }\n }\n\n // src/receipts-api.ts\n var activeCfg = {};\n async function openReceiptModal(chain, cfg = activeCfg) {\n if (typeof document === \"undefined\") return;\n registerGlobal(cfg);\n const { final, addStep, settle, wait } = buildModal(\n cfg.title || \"Provenance receipt\",\n cfg.subtitle || \"A signed, content-addressed receipt, verified <b>in your browser</b> against the public key registry. Each leg confirms only the one before it; anything it cannot open, it says so.\"\n );\n if (!await isEd25519Supported()) {\n final.className = \"drm3v-final bad\";\n final.innerHTML = \"This browser can't do Ed25519 in the page (try a recent Chrome, Safari, or Firefox). The receipts are still independently verifiable - see <b>status.drm3.network</b>.\";\n return;\n }\n if (!chain.length) {\n final.className = \"drm3v-final bad\";\n final.textContent = \"No receipts attached.\";\n return;\n }\n const registry = await loadRegistry(cfg.registryUrl);\n const counts = await verifyChainInto(chain, registry, addStep, settle, wait, cfg.contentRef);\n await wait(200);\n sealVerdict(final, counts);\n if (chain.length === 1) appendDownload(final, `receipt-${chain[0].id || \"drm3\"}.json`, chain[0].receipt_json || chain[0]);\n }\n async function openReceiptById(id, cfg = activeCfg) {\n if (typeof document === \"undefined\") return;\n registerGlobal(cfg);\n const { final, addStep, settle, wait } = buildModal(\n cfg.title || \"Provenance receipt\",\n cfg.subtitle || \"Resolved by its content-addressed id and verified <b>in your browser</b> against the public key registry.\"\n );\n if (!await isEd25519Supported()) {\n final.className = \"drm3v-final bad\";\n final.innerHTML = \"This browser can't do Ed25519 in the page. The receipt is still independently verifiable - see <b>status.drm3.network</b>.\";\n return;\n }\n const resolved = await resolveReceipt(id, cfg);\n if (!resolved?.receipt) {\n final.className = \"drm3v-final bad\";\n final.textContent = \"This receipt isn't retrievable here (it may live on another host, or require access).\";\n return;\n }\n const rc = resolved.receipt;\n const chain = [\n {\n id,\n receipt_json: rc,\n signature: rc.signature,\n public_key: rc.public_key,\n action: rc.action\n }\n ];\n const registry = await loadRegistry(cfg.registryUrl);\n const counts = await verifyChainInto(chain, registry, addStep, settle, wait, cfg.contentRef);\n await wait(200);\n sealVerdict(final, counts);\n appendDownload(final, `receipt-${id}.json`, rc);\n }\n function mountVerifyButton(target, source, cfg = {}) {\n if (!target || typeof document === \"undefined\") return;\n injectStyles();\n const btn = document.createElement(\"button\");\n btn.type = \"button\";\n btn.className = \"drm3v-trigger\";\n btn.innerHTML = `${CHECK}<span>Verify provenance in your browser</span>`;\n btn.addEventListener(\"click\", async () => {\n const resolved = typeof source === \"function\" ? await source() : source;\n if (typeof resolved === \"string\") return void openReceiptById(resolved, cfg);\n const chain = Array.isArray(resolved) ? resolved : [resolved];\n return void openReceiptModal(chain, cfg);\n });\n target.appendChild(btn);\n }\n function registerGlobal(cfg) {\n activeCfg = cfg;\n if (typeof window === \"undefined\") return;\n const w = window;\n if (!w.drm3Receipts) {\n try {\n w.drm3Receipts = { openReceiptById, openReceiptModal, mountVerifyButton };\n } catch {\n }\n }\n }\n return __toCommonJS(receipts_exports);\n})();\n";
@@ -0,0 +1,28 @@
1
+ // @drm3/sdk/receipts - the DRM3 in-browser receipt viewer, as a drop-in.
2
+ //
3
+ // import { openReceiptModal, openReceiptById, mountVerifyButton } from "@drm3/sdk/receipts";
4
+ // // you already hold a receipt (or chain): verify + render it, gorgeously
5
+ // openReceiptModal(chain, { registryUrl, resolverBase });
6
+ // // or resolve one by its content-addressed id (DRM3 resolver convention):
7
+ // openReceiptById("rcpt_…", { resolverBase: "https://your-app.example" });
8
+ //
9
+ // Framework-agnostic, zero deps. Injects its own scoped styles. Verifies every
10
+ // Ed25519 receipt LIVE with the Web Crypto API against the public key registry at
11
+ // status.drm3.network - no server round-trip for the crypto, no trust in the host.
12
+ // It's the same verifier that ships in Newsboy's article + brief receipts, lifted
13
+ // out so every DRM3 app can be a receipt viewer.
14
+ //
15
+ // This file is the BARREL: the full public API lives in the receipts-* siblings by
16
+ // concern (types · verify+canonicalization · registry+resolve · render · icons+CSS ·
17
+ // chain verifier · modal DOM · public API). esbuild builds THIS entry, so the barrel
18
+ // transitively pulls in everything. Verifier logic is a faithful port of
19
+ // drm3-provenance/bindings/browser/provenance-verify.js.
20
+ export * from "./receipts-api.js";
21
+ export * from "./receipts-chain.js";
22
+ export * from "./receipts-modal.js";
23
+ export * from "./receipts-registry.js";
24
+ export * from "./receipts-render.js";
25
+ export * from "./receipts-types.js";
26
+ export * from "./receipts-ui.js";
27
+ export * from "./receipts-verify.js";
28
+ //# sourceMappingURL=receipts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"receipts.js","sourceRoot":"","sources":["../src/receipts.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,EAAE;AACF,+FAA+F;AAC/F,6EAA6E;AAC7E,4DAA4D;AAC5D,8EAA8E;AAC9E,6EAA6E;AAC7E,EAAE;AACF,+EAA+E;AAC/E,kFAAkF;AAClF,mFAAmF;AACnF,kFAAkF;AAClF,iDAAiD;AACjD,EAAE;AACF,mFAAmF;AACnF,qFAAqF;AACrF,qFAAqF;AACrF,yEAAyE;AACzE,yDAAyD;AAEzD,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,wBAAwB,CAAC;AACvC,cAAc,sBAAsB,CAAC;AACrC,cAAc,qBAAqB,CAAC;AACpC,cAAc,kBAAkB,CAAC;AACjC,cAAc,sBAAsB,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { type SsoClaims, mintSsoToken, verifySsoToken, readSsoCookie, readCookie } from "./jwt.js";
2
+ export { type AppAccessResult, type LaunchClaims, verifyAppAccess, appKeyFor, mintLaunchToken, verifyLaunchToken } from "./launch.js";
3
+ export { type RevocationFeed, type RevocationGate, createRevocationGate, verifyActiveSsoToken } from "./revocation.js";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sso/index.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,KAAK,SAAS,EAAE,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAEnG,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,YAAY,EAAE,eAAe,EAAE,SAAS,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEtI,OAAO,EAAE,KAAK,cAAc,EAAE,KAAK,cAAc,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,10 @@
1
+ // Cross-app SSO - the offline HS256 verifier, the per-app launch protocol, and the revocation
2
+ // gate. This file is a thin barrel: the implementation lives in sibling modules by concern.
3
+ // ./jwt - HS256 mint/verify core, base64url helpers, SsoClaims + cookie reads
4
+ // ./launch - per-app key derivation + audience-bound launch tokens + app-entry gate
5
+ // ./revocation - cached revocation feed + verify-and-not-revoked
6
+ // The public API (this barrel's exports) is unchanged; it is the `@drm3/sdk/sso` subpath surface.
7
+ export { mintSsoToken, verifySsoToken, readSsoCookie, readCookie } from "./jwt.js";
8
+ export { verifyAppAccess, appKeyFor, mintLaunchToken, verifyLaunchToken } from "./launch.js";
9
+ export { createRevocationGate, verifyActiveSsoToken } from "./revocation.js";
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/sso/index.ts"],"names":[],"mappings":"AAAA,8FAA8F;AAC9F,4FAA4F;AAC5F,uFAAuF;AACvF,0FAA0F;AAC1F,mEAAmE;AACnE,kGAAkG;AAElG,OAAO,EAAkB,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAEnG,OAAO,EAA2C,eAAe,EAAE,SAAS,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEtI,OAAO,EAA4C,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,29 @@
1
+ export interface SsoClaims {
2
+ sub: string;
3
+ email?: string;
4
+ name?: string;
5
+ apps?: string[];
6
+ iat: number;
7
+ exp: number;
8
+ }
9
+ export declare const enc: TextEncoder;
10
+ export declare const dec: TextDecoder;
11
+ export declare function b64urlBytes(bytes: Uint8Array): string;
12
+ export declare function b64urlStr(s: string): string;
13
+ export declare function b64urlDecode(s: string): Uint8Array;
14
+ export declare function hmacKey(secret: string, usage: ("sign" | "verify")[]): Promise<CryptoKey>;
15
+ /** Mint a signed SSO token for the given identity. Default TTL 15 minutes. */
16
+ export declare function mintSsoToken(secret: string, claims: {
17
+ sub: string;
18
+ email?: string;
19
+ name?: string;
20
+ apps?: string[];
21
+ }, ttlSec?: number): Promise<string>;
22
+ /** Verify a token's signature + expiry. Returns the claims or null. Never throws. */
23
+ export declare function verifySsoToken(secret: string, token: string): Promise<SsoClaims | null>;
24
+ /** Read the drm3_sso cookie from a request's Cookie header. */
25
+ export declare function readSsoCookie(request: Request): string | null;
26
+ /** Read an arbitrary named cookie from a request's Cookie header. Used by apps to read their own
27
+ * host-scoped session cookie set after the launch handshake (see verifyLaunchToken). */
28
+ export declare function readCookie(request: Request, name: string): string | null;
29
+ //# sourceMappingURL=jwt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jwt.d.ts","sourceRoot":"","sources":["../../src/sso/jwt.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,SAAS;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;CACb;AAED,eAAO,MAAM,GAAG,aAAoB,CAAC;AACrC,eAAO,MAAM,GAAG,aAAoB,CAAC;AAErC,wBAAgB,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAIrD;AACD,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE3C;AACD,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,UAAU,CAOlD;AAED,wBAAsB,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,MAAM,GAAG,QAAQ,CAAC,EAAE,GAAG,OAAO,CAAC,SAAS,CAAC,CAE9F;AAED,8EAA8E;AAC9E,wBAAsB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,EAAE,MAAM,SAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAMzJ;AAED,qFAAqF;AACrF,wBAAsB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAoB7F;AAED,+DAA+D;AAC/D,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAI7D;AAED;yFACyF;AACzF,wBAAgB,UAAU,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAIxE"}
@@ -0,0 +1,73 @@
1
+ // Cross-app SSO token - a minimal HS256 JWT. The DRM3 account hub mints it from the
2
+ // signed-in session; every DRM3 app verifies it offline with the shared DRM3_SSO_SECRET.
3
+ // Offline verification is the whole point: it keeps identity off the hot path (no
4
+ // per-request network round-trip). Identity only, short TTL. Same wire format everywhere
5
+ // (Workers, Bun, Node) so any runtime can verify with stdlib crypto.
6
+ export const enc = new TextEncoder();
7
+ export const dec = new TextDecoder();
8
+ export function b64urlBytes(bytes) {
9
+ let s = "";
10
+ for (let i = 0; i < bytes.length; i++)
11
+ s += String.fromCharCode(bytes[i]);
12
+ return btoa(s).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
13
+ }
14
+ export function b64urlStr(s) {
15
+ return b64urlBytes(enc.encode(s));
16
+ }
17
+ export function b64urlDecode(s) {
18
+ let t = s.replace(/-/g, "+").replace(/_/g, "/");
19
+ while (t.length % 4)
20
+ t += "=";
21
+ const bin = atob(t);
22
+ const out = new Uint8Array(bin.length);
23
+ for (let i = 0; i < bin.length; i++)
24
+ out[i] = bin.charCodeAt(i);
25
+ return out;
26
+ }
27
+ export async function hmacKey(secret, usage) {
28
+ return crypto.subtle.importKey("raw", enc.encode(secret), { name: "HMAC", hash: "SHA-256" }, false, usage);
29
+ }
30
+ /** Mint a signed SSO token for the given identity. Default TTL 15 minutes. */
31
+ export async function mintSsoToken(secret, claims, ttlSec = 900) {
32
+ const now = Math.floor(Date.now() / 1000);
33
+ const payload = { ...claims, iat: now, exp: now + ttlSec };
34
+ const data = `${b64urlStr(JSON.stringify({ alg: "HS256", typ: "JWT" }))}.${b64urlStr(JSON.stringify(payload))}`;
35
+ const sig = new Uint8Array(await crypto.subtle.sign("HMAC", await hmacKey(secret, ["sign"]), enc.encode(data)));
36
+ return `${data}.${b64urlBytes(sig)}`;
37
+ }
38
+ /** Verify a token's signature + expiry. Returns the claims or null. Never throws. */
39
+ export async function verifySsoToken(secret, token) {
40
+ try {
41
+ const parts = token.split(".");
42
+ if (parts.length !== 3)
43
+ return null;
44
+ const data = `${parts[0]}.${parts[1]}`;
45
+ const ok = await crypto.subtle.verify("HMAC", await hmacKey(secret, ["verify"]),
46
+ // A Uint8Array is a valid BufferSource at runtime; the cast keeps the type-check happy
47
+ // across runtimes (Bun's lib narrows BufferSource away from Uint8Array<ArrayBufferLike>).
48
+ b64urlDecode(parts[2]), enc.encode(data));
49
+ if (!ok)
50
+ return null;
51
+ const claims = JSON.parse(dec.decode(b64urlDecode(parts[1])));
52
+ if (!claims.sub || !claims.exp || claims.exp < Math.floor(Date.now() / 1000))
53
+ return null;
54
+ return claims;
55
+ }
56
+ catch {
57
+ return null;
58
+ }
59
+ }
60
+ /** Read the drm3_sso cookie from a request's Cookie header. */
61
+ export function readSsoCookie(request) {
62
+ const raw = request.headers.get("Cookie") || "";
63
+ const m = raw.match(/(?:^|;\s*)drm3_sso=([^;]+)/);
64
+ return m ? decodeURIComponent(m[1]) : null;
65
+ }
66
+ /** Read an arbitrary named cookie from a request's Cookie header. Used by apps to read their own
67
+ * host-scoped session cookie set after the launch handshake (see verifyLaunchToken). */
68
+ export function readCookie(request, name) {
69
+ const raw = request.headers.get("Cookie") || "";
70
+ const m = raw.match(new RegExp(`(?:^|;\\s*)${name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}=([^;]+)`));
71
+ return m ? decodeURIComponent(m[1]) : null;
72
+ }
73
+ //# sourceMappingURL=jwt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jwt.js","sourceRoot":"","sources":["../../src/sso/jwt.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,70 @@
1
+ import { type SsoClaims } from "./jwt.js";
2
+ /**
3
+ * APP-ENTRY verification - the entitlement gate. Unlike verifySsoToken (identity only, for the
4
+ * account hub), this is what an APP calls to admit a user, and it is structurally impossible to
5
+ * use without asserting which app: `appId` is REQUIRED, and the call fails closed unless the
6
+ * token's SIGNED `apps` claim grants that exact app. network-site mints `apps` from app_access,
7
+ * so the claim is the cryptographic proof of entitlement (a user cannot forge it, an app cannot
8
+ * skip it). An app that has only this function for entry CANNOT let a user into a product they
9
+ * have not enabled - the protocol breaks closed if the access check is missing.
10
+ *
11
+ * Returns the claims on grant, or a typed denial so the caller can route correctly:
12
+ * { ok: true, claims } → admit
13
+ * { ok: false, reason: 'invalid' } → no/!bad token → send to sign-in
14
+ * { ok: false, reason: 'no_access', claims } → valid identity, app NOT enabled → send to the
15
+ * catalog to enable it (NOT back to sign-in - that loops)
16
+ */
17
+ export type AppAccessResult = {
18
+ ok: true;
19
+ claims: SsoClaims;
20
+ } | {
21
+ ok: false;
22
+ reason: "invalid";
23
+ } | {
24
+ ok: false;
25
+ reason: "no_access";
26
+ claims: SsoClaims;
27
+ };
28
+ export declare function verifyAppAccess(secret: string, token: string | null, appId: string): Promise<AppAccessResult>;
29
+ export interface LaunchClaims {
30
+ sub: string;
31
+ email?: string;
32
+ name?: string;
33
+ app: string;
34
+ jti: string;
35
+ iat: number;
36
+ exp: number;
37
+ }
38
+ /**
39
+ * Derive an app's per-app SSO key from the master secret. Deterministic, stable, async (WebCrypto).
40
+ * `appKey(appId) = hex(HMAC-SHA256(MASTER_SSO_SECRET, "drm3-sso-app:v1:" + appId))`.
41
+ * The MASTER secret lives ONLY on the hub (network-site). Each app is provisioned with the STRING
42
+ * this returns as its own DRM3_SSO_SECRET - nothing else. The hub re-derives the same value when it
43
+ * mints a launch token for that app, so signatures match; no app can derive another app's key.
44
+ */
45
+ export declare function appKeyFor(master: string, appId: string): Promise<string>;
46
+ /**
47
+ * Mint a short-lived, audience-bound, single-use launch token for one app. Called by the hub with
48
+ * the MASTER secret; it derives the destination app's key internally and signs with it. Default TTL
49
+ * 60s. Returns the token plus its `jti` + `exp` so the caller can record the jti (KV/D1, TTL=exp)
50
+ * to enforce single-use. The app verifies with verifyLaunchToken using its OWN derived key.
51
+ */
52
+ export declare function mintLaunchToken(master: string, claims: {
53
+ sub: string;
54
+ email?: string;
55
+ name?: string;
56
+ app: string;
57
+ }, ttlSec?: number): Promise<{
58
+ token: string;
59
+ jti: string;
60
+ exp: number;
61
+ }>;
62
+ /**
63
+ * Verify a launch token with THIS app's derived key. Returns the claims only if the signature is
64
+ * valid under `appKey`, the token is unexpired, and its `app` claim equals `appId` (audience check -
65
+ * a token minted for another app fails here even if its signature were valid, which it cannot be).
66
+ * Single-use (`jti`) is enforced by the hub's issued-jti store and the tight TTL; an app MAY also
67
+ * keep a short server-side seen-set of jtis for defense in depth. Never throws.
68
+ */
69
+ export declare function verifyLaunchToken(appKey: string, token: string, appId: string): Promise<LaunchClaims | null>;
70
+ //# sourceMappingURL=launch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"launch.d.ts","sourceRoot":"","sources":["../../src/sso/launch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAA2E,MAAM,UAAU,CAAC;AAEnH;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,eAAe,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,SAAS,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,SAAS,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,WAAW,CAAC;IAAC,MAAM,EAAE,SAAS,CAAA;CAAE,CAAC;AAEzJ,wBAAsB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAOnH;AAWD,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;;;GAMG;AACH,wBAAsB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAK9E;AAED;;;;;GAKG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,EACnE,MAAM,SAAK,GACV,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,CAStD;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAgBlH"}
@@ -0,0 +1,75 @@
1
+ import { b64urlBytes, b64urlDecode, b64urlStr, dec, enc, hmacKey, verifySsoToken } from "./jwt.js";
2
+ export async function verifyAppAccess(secret, token, appId) {
3
+ if (!appId)
4
+ return { ok: false, reason: "invalid" };
5
+ if (!token)
6
+ return { ok: false, reason: "invalid" };
7
+ const claims = await verifySsoToken(secret, token);
8
+ if (!claims)
9
+ return { ok: false, reason: "invalid" };
10
+ if (!Array.isArray(claims.apps) || !claims.apps.includes(appId))
11
+ return { ok: false, reason: "no_access", claims };
12
+ return { ok: true, claims };
13
+ }
14
+ /**
15
+ * Derive an app's per-app SSO key from the master secret. Deterministic, stable, async (WebCrypto).
16
+ * `appKey(appId) = hex(HMAC-SHA256(MASTER_SSO_SECRET, "drm3-sso-app:v1:" + appId))`.
17
+ * The MASTER secret lives ONLY on the hub (network-site). Each app is provisioned with the STRING
18
+ * this returns as its own DRM3_SSO_SECRET - nothing else. The hub re-derives the same value when it
19
+ * mints a launch token for that app, so signatures match; no app can derive another app's key.
20
+ */
21
+ export async function appKeyFor(master, appId) {
22
+ const sig = new Uint8Array(await crypto.subtle.sign("HMAC", await hmacKey(master, ["sign"]), enc.encode(`drm3-sso-app:v1:${appId}`)));
23
+ let hex = "";
24
+ for (let i = 0; i < sig.length; i++)
25
+ hex += sig[i].toString(16).padStart(2, "0");
26
+ return hex;
27
+ }
28
+ /**
29
+ * Mint a short-lived, audience-bound, single-use launch token for one app. Called by the hub with
30
+ * the MASTER secret; it derives the destination app's key internally and signs with it. Default TTL
31
+ * 60s. Returns the token plus its `jti` + `exp` so the caller can record the jti (KV/D1, TTL=exp)
32
+ * to enforce single-use. The app verifies with verifyLaunchToken using its OWN derived key.
33
+ */
34
+ export async function mintLaunchToken(master, claims, ttlSec = 60) {
35
+ const now = Math.floor(Date.now() / 1000);
36
+ const jti = crypto.randomUUID();
37
+ const exp = now + ttlSec;
38
+ const payload = { ...claims, jti, iat: now, exp };
39
+ const appKey = await appKeyFor(master, claims.app);
40
+ const data = `${b64urlStr(JSON.stringify({ alg: "HS256", typ: "JWT" }))}.${b64urlStr(JSON.stringify(payload))}`;
41
+ const sig = new Uint8Array(await crypto.subtle.sign("HMAC", await hmacKey(appKey, ["sign"]), enc.encode(data)));
42
+ return { token: `${data}.${b64urlBytes(sig)}`, jti, exp };
43
+ }
44
+ /**
45
+ * Verify a launch token with THIS app's derived key. Returns the claims only if the signature is
46
+ * valid under `appKey`, the token is unexpired, and its `app` claim equals `appId` (audience check -
47
+ * a token minted for another app fails here even if its signature were valid, which it cannot be).
48
+ * Single-use (`jti`) is enforced by the hub's issued-jti store and the tight TTL; an app MAY also
49
+ * keep a short server-side seen-set of jtis for defense in depth. Never throws.
50
+ */
51
+ export async function verifyLaunchToken(appKey, token, appId) {
52
+ try {
53
+ if (!appId)
54
+ return null;
55
+ const parts = token.split(".");
56
+ if (parts.length !== 3)
57
+ return null;
58
+ const data = `${parts[0]}.${parts[1]}`;
59
+ const valid = await crypto.subtle.verify("HMAC", await hmacKey(appKey, ["verify"]), b64urlDecode(parts[2]), enc.encode(data));
60
+ if (!valid)
61
+ return null;
62
+ const claims = JSON.parse(dec.decode(b64urlDecode(parts[1])));
63
+ if (!claims.sub || !claims.app || !claims.jti || !claims.exp)
64
+ return null;
65
+ if (claims.app !== appId)
66
+ return null;
67
+ if (claims.exp < Math.floor(Date.now() / 1000))
68
+ return null;
69
+ return claims;
70
+ }
71
+ catch {
72
+ return null;
73
+ }
74
+ }
75
+ //# sourceMappingURL=launch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"launch.js","sourceRoot":"","sources":["../../src/sso/launch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,WAAW,EAAE,YAAY,EAAE,SAAS,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAmBnH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,MAAc,EAAE,KAAoB,EAAE,KAAa;IACvF,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IACpD,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IACpD,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACnD,IAAI,CAAC,MAAM;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IACrD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC;IACnH,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;AAC9B,CAAC;AAqBD;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,MAAc,EAAE,KAAa;IAC3D,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,mBAAmB,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;IACtI,IAAI,GAAG,GAAG,EAAE,CAAC;IACb,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE;QAAE,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACjF,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,MAAc,EACd,MAAmE,EACnE,MAAM,GAAG,EAAE;IAEX,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;IAC1C,MAAM,GAAG,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;IAChC,MAAM,GAAG,GAAG,GAAG,GAAG,MAAM,CAAC;IACzB,MAAM,OAAO,GAAiB,EAAE,GAAG,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;IAChE,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;IACnD,MAAM,IAAI,GAAG,GAAG,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC,IAAI,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;IAChH,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAChH,OAAO,EAAE,KAAK,EAAE,GAAG,IAAI,IAAI,WAAW,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;AAC5D,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,MAAc,EAAE,KAAa,EAAE,KAAa;IAClF,IAAI,CAAC;QACH,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC;QACxB,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC/B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QACpC,MAAM,IAAI,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;QACvC,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,CAAiB,EAAE,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;QAC9I,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC;QACxB,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAiB,CAAC;QAC9E,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG;YAAE,OAAO,IAAI,CAAC;QAC1E,IAAI,MAAM,CAAC,GAAG,KAAK,KAAK;YAAE,OAAO,IAAI,CAAC;QACtC,IAAI,MAAM,CAAC,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC;QAC5D,OAAO,MAAM,CAAC;IAChB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -0,0 +1,23 @@
1
+ import { type SsoClaims } from "./jwt.js";
2
+ export interface RevocationFeed {
3
+ /** user.id (= sub) values currently revoked (banned). */
4
+ revoked: string[];
5
+ /** unix seconds the hub computed this list (informational). */
6
+ asOf: number;
7
+ }
8
+ export interface RevocationGate {
9
+ /** True iff `sub` is currently revoked. Refreshes the cached list when stale. */
10
+ isRevoked(sub: string): Promise<boolean>;
11
+ }
12
+ /** Build a cached gate over the hub's revocation feed (e.g. https://drm3.network/api/sso/revocations). */
13
+ export declare function createRevocationGate(opts: {
14
+ url: string;
15
+ ttlMs?: number;
16
+ timeoutMs?: number;
17
+ fetchImpl?: typeof fetch;
18
+ now?: () => number;
19
+ }): RevocationGate;
20
+ /** Verify a token AND confirm its subject isn't revoked. Returns claims or null. Never throws.
21
+ * Apps should call this instead of verifySsoToken once a gate is wired. */
22
+ export declare function verifyActiveSsoToken(secret: string, token: string, gate: RevocationGate): Promise<SsoClaims | null>;
23
+ //# sourceMappingURL=revocation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"revocation.d.ts","sourceRoot":"","sources":["../../src/sso/revocation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAkB,MAAM,UAAU,CAAC;AAW1D,MAAM,WAAW,cAAc;IAC7B,yDAAyD;IACzD,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,+DAA+D;IAC/D,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,cAAc;IAC7B,iFAAiF;IACjF,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC1C;AAED,0GAA0G;AAC1G,wBAAgB,oBAAoB,CAAC,IAAI,EAAE;IACzC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB,GAAG,cAAc,CAqCjB;AAED;4EAC4E;AAC5E,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAKzH"}
@@ -0,0 +1,53 @@
1
+ import { verifySsoToken } from "./jwt.js";
2
+ /** Build a cached gate over the hub's revocation feed (e.g. https://drm3.network/api/sso/revocations). */
3
+ export function createRevocationGate(opts) {
4
+ const ttlMs = opts.ttlMs ?? 60_000;
5
+ const timeoutMs = opts.timeoutMs ?? 3_000;
6
+ const doFetch = opts.fetchImpl ?? fetch;
7
+ const now = opts.now ?? Date.now;
8
+ let cache = null;
9
+ let fetchedAt = 0;
10
+ let inflight = null;
11
+ async function refresh() {
12
+ try {
13
+ const res = await doFetch(opts.url, { signal: AbortSignal.timeout(timeoutMs) });
14
+ if (!res.ok)
15
+ throw new Error(`revocation feed ${res.status}`);
16
+ const data = (await res.json());
17
+ cache = new Set(data.revoked || []);
18
+ }
19
+ catch {
20
+ // Fail-open: keep the last-known set (stale-while-error); if we never had one, use an
21
+ // empty set so nothing is revoked. Either way back off a full TTL before retrying.
22
+ if (cache === null)
23
+ cache = new Set();
24
+ }
25
+ finally {
26
+ fetchedAt = now();
27
+ }
28
+ }
29
+ return {
30
+ async isRevoked(sub) {
31
+ if (cache === null || now() - fetchedAt >= ttlMs) {
32
+ // Dedupe concurrent refreshes within one isolate.
33
+ if (!inflight)
34
+ inflight = refresh().finally(() => {
35
+ inflight = null;
36
+ });
37
+ await inflight;
38
+ }
39
+ return cache?.has(sub) ?? false;
40
+ },
41
+ };
42
+ }
43
+ /** Verify a token AND confirm its subject isn't revoked. Returns claims or null. Never throws.
44
+ * Apps should call this instead of verifySsoToken once a gate is wired. */
45
+ export async function verifyActiveSsoToken(secret, token, gate) {
46
+ const claims = await verifySsoToken(secret, token);
47
+ if (!claims)
48
+ return null;
49
+ if (await gate.isRevoked(claims.sub))
50
+ return null;
51
+ return claims;
52
+ }
53
+ //# sourceMappingURL=revocation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"revocation.js","sourceRoot":"","sources":["../../src/sso/revocation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,cAAc,EAAE,MAAM,UAAU,CAAC;AAuB1D,0GAA0G;AAC1G,MAAM,UAAU,oBAAoB,CAAC,IAMpC;IACC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,MAAM,CAAC;IACnC,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,KAAK,CAAC;IAC1C,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,IAAI,KAAK,CAAC;IACxC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC;IACjC,IAAI,KAAK,GAAuB,IAAI,CAAC;IACrC,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,IAAI,QAAQ,GAAyB,IAAI,CAAC;IAE1C,KAAK,UAAU,OAAO;QACpB,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;YAChF,IAAI,CAAC,GAAG,CAAC,EAAE;gBAAE,MAAM,IAAI,KAAK,CAAC,mBAAmB,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;YAC9D,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAmB,CAAC;YAClD,KAAK,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;QACtC,CAAC;QAAC,MAAM,CAAC;YACP,sFAAsF;YACtF,mFAAmF;YACnF,IAAI,KAAK,KAAK,IAAI;gBAAE,KAAK,GAAG,IAAI,GAAG,EAAE,CAAC;QACxC,CAAC;gBAAS,CAAC;YACT,SAAS,GAAG,GAAG,EAAE,CAAC;QACpB,CAAC;IACH,CAAC;IAED,OAAO;QACL,KAAK,CAAC,SAAS,CAAC,GAAW;YACzB,IAAI,KAAK,KAAK,IAAI,IAAI,GAAG,EAAE,GAAG,SAAS,IAAI,KAAK,EAAE,CAAC;gBACjD,kDAAkD;gBAClD,IAAI,CAAC,QAAQ;oBACX,QAAQ,GAAG,OAAO,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE;wBAChC,QAAQ,GAAG,IAAI,CAAC;oBAClB,CAAC,CAAC,CAAC;gBACL,MAAM,QAAQ,CAAC;YACjB,CAAC;YACD,OAAO,KAAK,EAAE,GAAG,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC;QAClC,CAAC;KACF,CAAC;AACJ,CAAC;AAED;4EAC4E;AAC5E,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,MAAc,EAAE,KAAa,EAAE,IAAoB;IAC5F,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACnD,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACzB,IAAI,MAAM,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAClD,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,22 @@
1
+ export interface SwitcherOptions {
2
+ /** id of the app this switcher lives in, to mark "you are here". */
3
+ currentId?: string;
4
+ /** Prefetch the catalog on mount so the first open is instant (default true). */
5
+ preload?: boolean;
6
+ /**
7
+ * Whether the current user is signed in to DRM3. The switcher is a signed-in-only
8
+ * widget (it exposes the user's app catalog + account link), so it renders NOTHING
9
+ * when the user is logged out. Pass `true`/`false` if your app already knows. If
10
+ * omitted, the SDK probes the account hub for an active session and shows the
11
+ * switcher only when one is present (fails closed: nothing is rendered on doubt).
12
+ */
13
+ authed?: boolean;
14
+ /** Optional `drm3_sso` token for the auth probe when the session cookie is not readable. */
15
+ ssoToken?: string;
16
+ }
17
+ /**
18
+ * Mount the DRM3 app-switcher into `target`. Idempotent per element; lazy-loads on first open.
19
+ * Signed-in-only: when the user is not authenticated, nothing is rendered (the target is left empty).
20
+ */
21
+ export declare function mountAppSwitcher(target: HTMLElement | null, opts?: SwitcherOptions): void;
22
+ //# sourceMappingURL=switcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"switcher.d.ts","sourceRoot":"","sources":["../src/switcher.ts"],"names":[],"mappings":"AA+FA,MAAM,WAAW,eAAe;IAC9B,oEAAoE;IACpE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iFAAiF;IACjF,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,4FAA4F;IAC5F,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,EAAE,IAAI,GAAE,eAAoB,GAAG,IAAI,CAkB7F"}