@suveren/gateway 0.7.4 → 0.8.0

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 (264) hide show
  1. package/content/integrations/mollie.json +103 -28
  2. package/dist/control-plane/index.mjs +248 -58
  3. package/dist/mcp-server/http.mjs +577 -124
  4. package/dist/ui/assets/index-BFnSVa9N.js +110 -0
  5. package/dist/ui/assets/{index-CkWaRck1.css → index-BYVb_0oG.css} +1 -1
  6. package/dist/ui/index.html +2 -2
  7. package/node_modules/@hap/core/dist/index.d.mts +285 -13
  8. package/node_modules/@hap/core/dist/index.d.ts +285 -13
  9. package/node_modules/@hap/core/dist/index.js +281 -13
  10. package/node_modules/@hap/core/dist/index.mjs +266 -12
  11. package/node_modules/@hap/core/package.json +4 -2
  12. package/node_modules/@hap/core/src/did-key.ts +126 -0
  13. package/node_modules/@hap/core/src/frame.ts +115 -14
  14. package/node_modules/@hap/core/src/gatekeeper.ts +26 -0
  15. package/node_modules/@hap/core/src/identity.ts +21 -5
  16. package/node_modules/@hap/core/src/index.ts +3 -0
  17. package/node_modules/@hap/core/src/mandate.ts +207 -0
  18. package/node_modules/@hap/core/src/receipt.ts +74 -0
  19. package/node_modules/@hap/core/src/types.ts +87 -5
  20. package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/lib/accept.js +54 -0
  21. package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/lib/charset.js +10 -34
  22. package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/lib/encoding.js +9 -30
  23. package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/lib/language.js +15 -36
  24. package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/lib/mediaType.js +16 -134
  25. package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/node_modules/content-type/LICENSE +22 -0
  26. package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/node_modules/content-type/README.md +71 -0
  27. package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/node_modules/content-type/dist/index.d.ts +46 -0
  28. package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/node_modules/content-type/dist/index.js +176 -0
  29. package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/node_modules/content-type/dist/index.js.map +1 -0
  30. package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/node_modules/content-type/package.json +52 -0
  31. package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/package.json +16 -10
  32. package/node_modules/@types/node/README.md +1 -1
  33. package/node_modules/@types/node/buffer.buffer.d.ts +10 -5
  34. package/node_modules/@types/node/crypto.d.ts +2 -4
  35. package/node_modules/@types/node/dgram.d.ts +119 -4
  36. package/node_modules/@types/node/ffi.d.ts +1 -1
  37. package/node_modules/@types/node/fs/promises.d.ts +61 -65
  38. package/node_modules/@types/node/fs.d.ts +86 -76
  39. package/node_modules/@types/node/http.d.ts +17 -1
  40. package/node_modules/@types/node/http2.d.ts +12 -4
  41. package/node_modules/@types/node/index.d.ts +1 -0
  42. package/node_modules/@types/node/inspector.d.ts +2 -1
  43. package/node_modules/@types/node/net.d.ts +108 -16
  44. package/node_modules/@types/node/package.json +2 -2
  45. package/node_modules/@types/node/process.d.ts +52 -0
  46. package/node_modules/@types/node/quic.d.ts +246 -38
  47. package/node_modules/@types/node/sqlite.d.ts +6 -2
  48. package/node_modules/@types/node/stream/iter.d.ts +4 -4
  49. package/node_modules/@types/node/test.d.ts +36 -0
  50. package/node_modules/@types/node/tls.d.ts +24 -0
  51. package/node_modules/@types/node/ts5.6/buffer.buffer.d.ts +5 -0
  52. package/node_modules/@types/node/ts5.6/index.d.ts +1 -0
  53. package/node_modules/@types/node/ts5.7/index.d.ts +1 -0
  54. package/node_modules/@types/node/tty.d.ts +4 -3
  55. package/node_modules/@types/node/vfs.d.ts +210 -0
  56. package/node_modules/@types/node/vm.d.ts +1 -1
  57. package/node_modules/express-rate-limit/dist/index.cjs +6 -3
  58. package/node_modules/express-rate-limit/dist/index.d.cts +11 -0
  59. package/node_modules/express-rate-limit/dist/index.d.mts +11 -0
  60. package/node_modules/express-rate-limit/dist/index.d.ts +11 -0
  61. package/node_modules/express-rate-limit/dist/index.mjs +6 -3
  62. package/node_modules/express-rate-limit/{license.md → license} +7 -6
  63. package/node_modules/express-rate-limit/package.json +5 -5
  64. package/node_modules/express-rate-limit/readme.md +1 -0
  65. package/node_modules/fast-uri/index.js +215 -47
  66. package/node_modules/fast-uri/lib/schemes.js +9 -4
  67. package/node_modules/fast-uri/lib/utils.js +396 -92
  68. package/node_modules/fast-uri/package.json +1 -1
  69. package/node_modules/fast-uri/test/component-safe-serialization.test.js +163 -0
  70. package/node_modules/fast-uri/test/equal.test.js +31 -3
  71. package/node_modules/fast-uri/test/fixtures/uri-js-parse.json +2 -0
  72. package/node_modules/fast-uri/test/ipv6-canonical.test.js +34 -0
  73. package/node_modules/fast-uri/test/ipv6-validation.test.js +124 -0
  74. package/node_modules/fast-uri/test/malformed-percent.test.js +77 -0
  75. package/node_modules/fast-uri/test/malformed-urn.test.js +61 -0
  76. package/node_modules/fast-uri/test/parse.test.js +7 -3
  77. package/node_modules/fast-uri/test/query-fragment-normalization.test.js +33 -0
  78. package/node_modules/fast-uri/test/reserved-path-normalization.test.js +109 -0
  79. package/node_modules/fast-uri/test/scheme-validation.test.js +124 -0
  80. package/node_modules/fast-uri/test/security-normalization.test.js +101 -0
  81. package/node_modules/fast-uri/test/security.test.js +75 -3
  82. package/node_modules/fast-uri/test/urn-full-input.test.js +29 -0
  83. package/node_modules/fast-uri/test/websocket-query-preservation.test.js +24 -0
  84. package/node_modules/hono/dist/cjs/client/client.js +25 -11
  85. package/node_modules/hono/dist/cjs/client/utils.js +4 -1
  86. package/node_modules/hono/dist/cjs/context.js +4 -0
  87. package/node_modules/hono/dist/cjs/helper/accepts/accepts.js +36 -2
  88. package/node_modules/hono/dist/cjs/helper/ssg/ssg.js +1 -1
  89. package/node_modules/hono/dist/cjs/helper/ssg/utils.js +30 -10
  90. package/node_modules/hono/dist/cjs/jsx/dom/render.js +2 -0
  91. package/node_modules/hono/dist/cjs/middleware/cache/index.js +1 -1
  92. package/node_modules/hono/dist/cjs/middleware/cors/index.js +1 -1
  93. package/node_modules/hono/dist/cjs/middleware/csrf/index.js +1 -1
  94. package/node_modules/hono/dist/cjs/middleware/etag/digest.js +1 -1
  95. package/node_modules/hono/dist/cjs/middleware/etag/index.js +3 -3
  96. package/node_modules/hono/dist/cjs/middleware/pretty-json/index.js +3 -1
  97. package/node_modules/hono/dist/cjs/request.js +8 -4
  98. package/node_modules/hono/dist/cjs/router/linear-router/router.js +7 -2
  99. package/node_modules/hono/dist/cjs/router/pattern-router/router.js +3 -9
  100. package/node_modules/hono/dist/cjs/router/reg-exp-router/node.js +10 -3
  101. package/node_modules/hono/dist/cjs/router/reg-exp-router/router.js +47 -83
  102. package/node_modules/hono/dist/cjs/router/reg-exp-router/trie.js +2 -1
  103. package/node_modules/hono/dist/cjs/router/trie-router/node.js +46 -69
  104. package/node_modules/hono/dist/cjs/router/trie-router/router.js +3 -11
  105. package/node_modules/hono/dist/cjs/router/utils.js +27 -0
  106. package/node_modules/hono/dist/cjs/utils/body.js +15 -3
  107. package/node_modules/hono/dist/cjs/utils/cookie.js +1 -1
  108. package/node_modules/hono/dist/cjs/utils/ipaddr.js +5 -3
  109. package/node_modules/hono/dist/cjs/utils/stream.js +7 -1
  110. package/node_modules/hono/dist/cjs/utils/url.js +9 -1
  111. package/node_modules/hono/dist/client/client.js +25 -11
  112. package/node_modules/hono/dist/client/utils.js +4 -1
  113. package/node_modules/hono/dist/context.js +4 -0
  114. package/node_modules/hono/dist/helper/accepts/accepts.js +36 -2
  115. package/node_modules/hono/dist/helper/ssg/ssg.js +1 -1
  116. package/node_modules/hono/dist/helper/ssg/utils.js +30 -10
  117. package/node_modules/hono/dist/jsx/dom/render.js +2 -0
  118. package/node_modules/hono/dist/middleware/cache/index.js +1 -1
  119. package/node_modules/hono/dist/middleware/cors/index.js +1 -1
  120. package/node_modules/hono/dist/middleware/csrf/index.js +1 -1
  121. package/node_modules/hono/dist/middleware/etag/digest.js +1 -1
  122. package/node_modules/hono/dist/middleware/etag/index.js +3 -3
  123. package/node_modules/hono/dist/middleware/pretty-json/index.js +3 -1
  124. package/node_modules/hono/dist/request.js +8 -4
  125. package/node_modules/hono/dist/router/linear-router/router.js +7 -2
  126. package/node_modules/hono/dist/router/pattern-router/router.js +3 -9
  127. package/node_modules/hono/dist/router/reg-exp-router/node.js +6 -2
  128. package/node_modules/hono/dist/router/reg-exp-router/router.js +53 -84
  129. package/node_modules/hono/dist/router/reg-exp-router/trie.js +2 -1
  130. package/node_modules/hono/dist/router/trie-router/node.js +46 -69
  131. package/node_modules/hono/dist/router/trie-router/router.js +3 -11
  132. package/node_modules/hono/dist/router/utils.js +5 -0
  133. package/node_modules/hono/dist/types/context.d.ts +4 -0
  134. package/node_modules/hono/dist/types/router/reg-exp-router/node.d.ts +3 -0
  135. package/node_modules/hono/dist/types/router/trie-router/node.d.ts +1 -2
  136. package/node_modules/hono/dist/types/router/trie-router/router.d.ts +0 -1
  137. package/node_modules/hono/dist/types/router/utils.d.ts +1 -0
  138. package/node_modules/hono/dist/types/utils/url.d.ts +4 -0
  139. package/node_modules/hono/dist/utils/body.js +15 -3
  140. package/node_modules/hono/dist/utils/cookie.js +1 -1
  141. package/node_modules/hono/dist/utils/ipaddr.js +5 -3
  142. package/node_modules/hono/dist/utils/stream.js +7 -1
  143. package/node_modules/hono/dist/utils/url.js +9 -1
  144. package/node_modules/hono/package.json +1 -1
  145. package/node_modules/ip-address/README.md +92 -82
  146. package/node_modules/ip-address/dist/common.d.ts +20 -0
  147. package/node_modules/ip-address/dist/common.js +36 -0
  148. package/node_modules/ip-address/dist/common.js.map +1 -1
  149. package/node_modules/ip-address/dist/ipv4.d.ts +50 -0
  150. package/node_modules/ip-address/dist/ipv4.js +75 -0
  151. package/node_modules/ip-address/dist/ipv4.js.map +1 -1
  152. package/node_modules/ip-address/dist/ipv6.d.ts +79 -10
  153. package/node_modules/ip-address/dist/ipv6.js +120 -30
  154. package/node_modules/ip-address/dist/ipv6.js.map +1 -1
  155. package/node_modules/ip-address/dist/v4/constants.d.ts +12 -0
  156. package/node_modules/ip-address/dist/v4/constants.js +40 -1
  157. package/node_modules/ip-address/dist/v4/constants.js.map +1 -1
  158. package/node_modules/ip-address/dist/v6/constants.d.ts +14 -0
  159. package/node_modules/ip-address/dist/v6/constants.js +47 -1
  160. package/node_modules/ip-address/dist/v6/constants.js.map +1 -1
  161. package/node_modules/ip-address/package.json +1 -1
  162. package/node_modules/is-plain-object/README.md +39 -95
  163. package/node_modules/is-plain-object/dist/is-plain-object.js +0 -2
  164. package/node_modules/is-plain-object/is-plain-object.d.ts +6 -1
  165. package/node_modules/is-plain-object/package.json +8 -34
  166. package/node_modules/jose/dist/types/jwe/compact/decrypt.d.ts +7 -16
  167. package/node_modules/jose/dist/types/jwe/compact/encrypt.d.ts +10 -20
  168. package/node_modules/jose/dist/types/jwe/flattened/decrypt.d.ts +7 -16
  169. package/node_modules/jose/dist/types/jwe/flattened/encrypt.d.ts +16 -38
  170. package/node_modules/jose/dist/types/jwe/general/decrypt.d.ts +7 -16
  171. package/node_modules/jose/dist/types/jwe/general/encrypt.d.ts +19 -38
  172. package/node_modules/jose/dist/types/jwk/embedded.d.ts +3 -8
  173. package/node_modules/jose/dist/types/jwk/thumbprint.d.ts +5 -5
  174. package/node_modules/jose/dist/types/jwks/local.d.ts +6 -10
  175. package/node_modules/jose/dist/types/jwks/remote.d.ts +17 -28
  176. package/node_modules/jose/dist/types/jws/compact/sign.d.ts +5 -10
  177. package/node_modules/jose/dist/types/jws/compact/verify.d.ts +9 -18
  178. package/node_modules/jose/dist/types/jws/flattened/sign.d.ts +7 -16
  179. package/node_modules/jose/dist/types/jws/flattened/verify.d.ts +9 -18
  180. package/node_modules/jose/dist/types/jws/general/sign.d.ts +11 -21
  181. package/node_modules/jose/dist/types/jws/general/verify.d.ts +12 -21
  182. package/node_modules/jose/dist/types/jwt/decrypt.d.ts +10 -20
  183. package/node_modules/jose/dist/types/jwt/encrypt.d.ts +17 -29
  184. package/node_modules/jose/dist/types/jwt/sign.d.ts +12 -19
  185. package/node_modules/jose/dist/types/jwt/unsecured.d.ts +17 -28
  186. package/node_modules/jose/dist/types/jwt/verify.d.ts +9 -19
  187. package/node_modules/jose/dist/types/key/export.d.ts +4 -4
  188. package/node_modules/jose/dist/types/key/generate_key_pair.d.ts +6 -8
  189. package/node_modules/jose/dist/types/key/generate_secret.d.ts +4 -11
  190. package/node_modules/jose/dist/types/key/import.d.ts +10 -17
  191. package/node_modules/jose/dist/types/types.d.ts +48 -86
  192. package/node_modules/jose/dist/types/util/base64url.d.ts +2 -2
  193. package/node_modules/jose/dist/types/util/decode_jwt.d.ts +3 -10
  194. package/node_modules/jose/dist/types/util/decode_protected_header.d.ts +4 -4
  195. package/node_modules/jose/dist/types/util/errors.d.ts +26 -53
  196. package/node_modules/jose/dist/webapi/index.js +70 -32
  197. package/node_modules/jose/dist/webapi/jwe/compact/decrypt.js +7 -8
  198. package/node_modules/jose/dist/webapi/jwe/compact/encrypt.js +42 -26
  199. package/node_modules/jose/dist/webapi/jwe/flattened/decrypt.js +14 -10
  200. package/node_modules/jose/dist/webapi/jwe/flattened/encrypt.js +53 -71
  201. package/node_modules/jose/dist/webapi/jwe/general/decrypt.js +38 -49
  202. package/node_modules/jose/dist/webapi/jwe/general/encrypt.js +121 -158
  203. package/node_modules/jose/dist/webapi/jwk/embedded.js +30 -18
  204. package/node_modules/jose/dist/webapi/jwk/thumbprint.js +54 -64
  205. package/node_modules/jose/dist/webapi/jwks/local.js +49 -74
  206. package/node_modules/jose/dist/webapi/jwks/remote.js +91 -160
  207. package/node_modules/jose/dist/webapi/jws/compact/sign.js +21 -20
  208. package/node_modules/jose/dist/webapi/jws/compact/verify.js +7 -8
  209. package/node_modules/jose/dist/webapi/jws/flattened/sign.js +29 -30
  210. package/node_modules/jose/dist/webapi/jws/flattened/verify.js +21 -23
  211. package/node_modules/jose/dist/webapi/jws/general/sign.js +61 -70
  212. package/node_modules/jose/dist/webapi/jws/general/verify.js +47 -41
  213. package/node_modules/jose/dist/webapi/jwt/decrypt.js +13 -22
  214. package/node_modules/jose/dist/webapi/jwt/encrypt.js +58 -100
  215. package/node_modules/jose/dist/webapi/jwt/sign.js +18 -50
  216. package/node_modules/jose/dist/webapi/jwt/unsecured.js +34 -63
  217. package/node_modules/jose/dist/webapi/jwt/verify.js +12 -14
  218. package/node_modules/jose/dist/webapi/key/export.js +32 -37
  219. package/node_modules/jose/dist/webapi/key/generate_key_pair.js +40 -44
  220. package/node_modules/jose/dist/webapi/key/generate_secret.js +35 -39
  221. package/node_modules/jose/dist/webapi/key/import.js +52 -52
  222. package/node_modules/jose/dist/webapi/lib/asn1.js +130 -164
  223. package/node_modules/jose/dist/webapi/lib/base64.js +18 -20
  224. package/node_modules/jose/dist/webapi/lib/buffer_utils.js +34 -38
  225. package/node_modules/jose/dist/webapi/lib/content_encryption.js +79 -123
  226. package/node_modules/jose/dist/webapi/lib/crypto_key.js +24 -25
  227. package/node_modules/jose/dist/webapi/lib/deflate.js +46 -40
  228. package/node_modules/jose/dist/webapi/lib/helpers.js +40 -37
  229. package/node_modules/jose/dist/webapi/lib/invalid_key_input.js +10 -24
  230. package/node_modules/jose/dist/webapi/lib/is_key_like.js +17 -15
  231. package/node_modules/jose/dist/webapi/lib/jwe_algorithms.js +106 -93
  232. package/node_modules/jose/dist/webapi/lib/jwe_decrypt.js +210 -176
  233. package/node_modules/jose/dist/webapi/lib/jwe_encrypt.js +105 -107
  234. package/node_modules/jose/dist/webapi/lib/jwk_metadata.js +19 -0
  235. package/node_modules/jose/dist/webapi/lib/jwk_to_key.js +9 -16
  236. package/node_modules/jose/dist/webapi/lib/jws_algorithms.js +62 -61
  237. package/node_modules/jose/dist/webapi/lib/jws_sign.js +58 -72
  238. package/node_modules/jose/dist/webapi/lib/jws_verify.js +90 -103
  239. package/node_modules/jose/dist/webapi/lib/jwt_claims_set.js +134 -185
  240. package/node_modules/jose/dist/webapi/lib/key.js +84 -121
  241. package/node_modules/jose/dist/webapi/lib/key_algorithm.js +13 -8
  242. package/node_modules/jose/dist/webapi/lib/key_descriptor.js +8 -6
  243. package/node_modules/jose/dist/webapi/lib/key_management.js +200 -225
  244. package/node_modules/jose/dist/webapi/lib/key_options.js +8 -0
  245. package/node_modules/jose/dist/webapi/lib/options.js +57 -44
  246. package/node_modules/jose/dist/webapi/lib/signing.js +18 -22
  247. package/node_modules/jose/dist/webapi/lib/type_checks.js +28 -33
  248. package/node_modules/jose/dist/webapi/util/base64url.js +26 -35
  249. package/node_modules/jose/dist/webapi/util/decode_jwt.js +32 -31
  250. package/node_modules/jose/dist/webapi/util/decode_protected_header.js +18 -23
  251. package/node_modules/jose/dist/webapi/util/errors.js +94 -84
  252. package/node_modules/jose/package.json +17 -81
  253. package/package.json +2 -2
  254. package/profiles/calendar/0.5.profile.json +190 -0
  255. package/profiles/charge/0.5.profile.json +210 -0
  256. package/profiles/customers/0.7.profile.json +157 -0
  257. package/profiles/deploy/0.9.profile.json +176 -0
  258. package/profiles/email/0.6.profile.json +208 -0
  259. package/profiles/index.json +9 -1
  260. package/profiles/publish/0.5.profile.json +186 -0
  261. package/profiles/purchase/0.5.profile.json +231 -0
  262. package/profiles/records/0.5.profile.json +117 -0
  263. package/dist/ui/assets/index-0Q2eNytX.js +0 -110
  264. package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/HISTORY.md +0 -114
@@ -33,6 +33,85 @@ async function notifyControlPlane(type) {
33
33
  }
34
34
  }
35
35
 
36
+ // src/lib/client-version.ts
37
+ import { createRequire } from "module";
38
+ import { existsSync, readFileSync } from "fs";
39
+ import { join } from "path";
40
+ var UNKNOWN = "unknown";
41
+ function readJson(path) {
42
+ try {
43
+ return JSON.parse(readFileSync(path, "utf8"));
44
+ } catch {
45
+ return null;
46
+ }
47
+ }
48
+ function detectGatewayVersion() {
49
+ const override = process.env.SUVEREN_GATEWAY_VERSION;
50
+ if (override) return override;
51
+ const start = import.meta.dirname ?? process.cwd();
52
+ let dir = start;
53
+ for (let up = 0; up < 6; up++) {
54
+ const pkg = readJson(join(dir, "package.json"));
55
+ const name = pkg?.name;
56
+ const version = pkg?.version;
57
+ if (typeof version === "string" && (name === "@suveren/gateway" || name === "suveren-gateway")) {
58
+ return version;
59
+ }
60
+ const parent = join(dir, "..");
61
+ if (parent === dir) break;
62
+ dir = parent;
63
+ }
64
+ return process.env.HAP_BUILD_SHA ?? UNKNOWN;
65
+ }
66
+ function findPackageVersion(entry, wantName) {
67
+ let dir = join(entry, "..");
68
+ for (let up = 0; up < 6; up++) {
69
+ const candidate = join(dir, "package.json");
70
+ if (existsSync(candidate)) {
71
+ const pkg = readJson(candidate);
72
+ if (typeof pkg?.version === "string" && (pkg.name === wantName || pkg.name === "@hap/core")) {
73
+ return pkg.name === wantName ? pkg.version : null;
74
+ }
75
+ }
76
+ const parent = join(dir, "..");
77
+ if (parent === dir) break;
78
+ dir = parent;
79
+ }
80
+ return null;
81
+ }
82
+ function detectHapCoreVersion() {
83
+ const CORE = "@humanagencyp/hap-core";
84
+ let req;
85
+ try {
86
+ req = createRequire(import.meta.url);
87
+ } catch {
88
+ return UNKNOWN;
89
+ }
90
+ try {
91
+ const v = findPackageVersion(req.resolve(CORE), CORE);
92
+ if (v) return v;
93
+ } catch {
94
+ }
95
+ try {
96
+ const wrapperEntry = req.resolve("@hap/core");
97
+ const v = findPackageVersion(wrapperEntry, CORE);
98
+ if (v) return v;
99
+ const wrapperReq = createRequire(wrapperEntry);
100
+ const v2 = findPackageVersion(wrapperReq.resolve(CORE), CORE);
101
+ if (v2) return v2;
102
+ } catch {
103
+ }
104
+ return UNKNOWN;
105
+ }
106
+ var GATEWAY_VERSION = detectGatewayVersion();
107
+ var HAP_CORE_VERSION = detectHapCoreVersion();
108
+ function clientVersionHeaders() {
109
+ return {
110
+ "x-suveren-gateway-version": GATEWAY_VERSION,
111
+ "x-hap-core-version": HAP_CORE_VERSION
112
+ };
113
+ }
114
+
36
115
  // src/lib/sp-client.ts
37
116
  var SPReceiptError = class extends Error {
38
117
  constructor(message, statusCode, body) {
@@ -55,6 +134,10 @@ var SPClient = class {
55
134
  sessionCookie = "";
56
135
  apiKey = "";
57
136
  receiptRetry;
137
+ /** The AS base URL this client talks to (for provenance records). */
138
+ get url() {
139
+ return this.baseUrl;
140
+ }
58
141
  setApiKey(key) {
59
142
  this.apiKey = key;
60
143
  }
@@ -78,6 +161,11 @@ var SPClient = class {
78
161
  const url = `${this.baseUrl}${path}`;
79
162
  const headers = {
80
163
  "Content-Type": "application/json",
164
+ // Identify the caller so the AS can answer "what is deployed against
165
+ // me?" and name a version in an upgrade instruction. Advisory only —
166
+ // never used for authorization. Sent on every call, not just receipts,
167
+ // so the picture is not skewed toward one endpoint.
168
+ ...clientVersionHeaders(),
81
169
  ...init?.headers
82
170
  };
83
171
  if (this.sessionCookie) {
@@ -374,11 +462,11 @@ var AttestationCache = class {
374
462
  };
375
463
 
376
464
  // src/lib/gate-store.ts
377
- import { mkdirSync, readFileSync, writeFileSync, existsSync, unlinkSync } from "fs";
465
+ import { mkdirSync, readFileSync as readFileSync2, writeFileSync, existsSync as existsSync2, unlinkSync } from "fs";
378
466
  import { homedir } from "os";
379
- import { dirname, join } from "path";
467
+ import { dirname, join as join2 } from "path";
380
468
  import { createCipheriv, createDecipheriv, randomBytes } from "crypto";
381
- var DEFAULT_DIR = process.env.SUVEREN_DATA_DIR ?? join(homedir(), ".suveren");
469
+ var DEFAULT_DIR = process.env.SUVEREN_DATA_DIR ?? join2(homedir(), ".suveren");
382
470
  var GateStore = class {
383
471
  entries = /* @__PURE__ */ new Map();
384
472
  baseDir;
@@ -396,11 +484,11 @@ var GateStore = class {
396
484
  */
397
485
  setVaultKey(key) {
398
486
  this.vaultKey = key;
399
- if (existsSync(this.encryptedFilePath)) {
487
+ if (existsSync2(this.encryptedFilePath)) {
400
488
  this.loadEncrypted();
401
489
  } else if (this.entries.size > 0) {
402
490
  this.persistEncrypted();
403
- if (existsSync(this.plaintextFilePath)) {
491
+ if (existsSync2(this.plaintextFilePath)) {
404
492
  try {
405
493
  unlinkSync(this.plaintextFilePath);
406
494
  } catch {
@@ -451,19 +539,19 @@ var GateStore = class {
451
539
  }
452
540
  // ─── File paths ─────────────────────────────────────────────────────────
453
541
  get plaintextFilePath() {
454
- return join(this.baseDir, "gates.json");
542
+ return join2(this.baseDir, "gates.json");
455
543
  }
456
544
  get encryptedFilePath() {
457
- return join(this.baseDir, "gates.enc.json");
545
+ return join2(this.baseDir, "gates.enc.json");
458
546
  }
459
547
  // ─── Load / Persist ─────────────────────────────────────────────────────
460
548
  loadPlaintext() {
461
- if (!existsSync(this.plaintextFilePath)) {
549
+ if (!existsSync2(this.plaintextFilePath)) {
462
550
  mkdirSync(this.baseDir, { recursive: true });
463
551
  return;
464
552
  }
465
553
  try {
466
- const raw = readFileSync(this.plaintextFilePath, "utf-8");
554
+ const raw = readFileSync2(this.plaintextFilePath, "utf-8");
467
555
  const data = JSON.parse(raw);
468
556
  this.entries = new Map(Object.entries(data.entries));
469
557
  } catch {
@@ -472,9 +560,9 @@ var GateStore = class {
472
560
  }
473
561
  }
474
562
  loadEncrypted() {
475
- if (!existsSync(this.encryptedFilePath)) return;
563
+ if (!existsSync2(this.encryptedFilePath)) return;
476
564
  try {
477
- const raw = readFileSync(this.encryptedFilePath, "utf-8");
565
+ const raw = readFileSync2(this.encryptedFilePath, "utf-8");
478
566
  const data = JSON.parse(raw);
479
567
  this.entries = /* @__PURE__ */ new Map();
480
568
  for (const [key, blob] of Object.entries(data.entries)) {
@@ -515,11 +603,11 @@ var GateStore = class {
515
603
  };
516
604
 
517
605
  // src/lib/execution-log.ts
518
- import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2, existsSync as existsSync2, unlinkSync as unlinkSync2 } from "fs";
606
+ import { mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2, existsSync as existsSync3, unlinkSync as unlinkSync2 } from "fs";
519
607
  import { homedir as homedir2 } from "os";
520
- import { dirname as dirname2, join as join2 } from "path";
608
+ import { dirname as dirname2, join as join3 } from "path";
521
609
  import { createCipheriv as createCipheriv2, createDecipheriv as createDecipheriv2, randomBytes as randomBytes2 } from "crypto";
522
- var DEFAULT_DIR2 = process.env.SUVEREN_DATA_DIR ?? join2(homedir2(), ".suveren");
610
+ var DEFAULT_DIR2 = process.env.SUVEREN_DATA_DIR ?? join3(homedir2(), ".suveren");
523
611
  var MAX_AGE_SECONDS = 31 * 24 * 60 * 60;
524
612
  var ExecutionLog = class {
525
613
  entries = [];
@@ -538,11 +626,11 @@ var ExecutionLog = class {
538
626
  */
539
627
  setVaultKey(key) {
540
628
  this.vaultKey = key;
541
- if (existsSync2(this.encryptedFilePath)) {
629
+ if (existsSync3(this.encryptedFilePath)) {
542
630
  this.loadEncrypted();
543
631
  } else if (this.entries.length > 0) {
544
632
  this.persistEncrypted();
545
- if (existsSync2(this.plaintextFilePath)) {
633
+ if (existsSync3(this.plaintextFilePath)) {
546
634
  try {
547
635
  unlinkSync2(this.plaintextFilePath);
548
636
  } catch {
@@ -637,19 +725,19 @@ var ExecutionLog = class {
637
725
  }
638
726
  // ─── File paths ─────────────────────────────────────────────────────────
639
727
  get plaintextFilePath() {
640
- return join2(this.baseDir, "execution-log.json");
728
+ return join3(this.baseDir, "execution-log.json");
641
729
  }
642
730
  get encryptedFilePath() {
643
- return join2(this.baseDir, "execution-log.enc.json");
731
+ return join3(this.baseDir, "execution-log.enc.json");
644
732
  }
645
733
  // ─── Load / Persist ─────────────────────────────────────────────────────
646
734
  loadPlaintext() {
647
- if (!existsSync2(this.plaintextFilePath)) {
735
+ if (!existsSync3(this.plaintextFilePath)) {
648
736
  mkdirSync2(this.baseDir, { recursive: true });
649
737
  return;
650
738
  }
651
739
  try {
652
- const raw = readFileSync2(this.plaintextFilePath, "utf-8");
740
+ const raw = readFileSync3(this.plaintextFilePath, "utf-8");
653
741
  const data = JSON.parse(raw);
654
742
  this.entries = data.entries;
655
743
  this.prune();
@@ -659,9 +747,9 @@ var ExecutionLog = class {
659
747
  }
660
748
  }
661
749
  loadEncrypted() {
662
- if (!existsSync2(this.encryptedFilePath)) return;
750
+ if (!existsSync3(this.encryptedFilePath)) return;
663
751
  try {
664
- const raw = readFileSync2(this.encryptedFilePath, "utf-8");
752
+ const raw = readFileSync3(this.encryptedFilePath, "utf-8");
665
753
  const data = JSON.parse(raw);
666
754
  const decrypted = this.decrypt(data.blob);
667
755
  const logFile = JSON.parse(decrypted);
@@ -708,11 +796,11 @@ function windowCutoff(window, now) {
708
796
  }
709
797
 
710
798
  // src/lib/denial-log.ts
711
- import { mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3, existsSync as existsSync3, unlinkSync as unlinkSync3 } from "fs";
799
+ import { mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync4, unlinkSync as unlinkSync3 } from "fs";
712
800
  import { homedir as homedir3 } from "os";
713
- import { dirname as dirname3, join as join3 } from "path";
801
+ import { dirname as dirname3, join as join4 } from "path";
714
802
  import { createCipheriv as createCipheriv3, createDecipheriv as createDecipheriv3, randomBytes as randomBytes3 } from "crypto";
715
- var DEFAULT_DIR3 = process.env.SUVEREN_DATA_DIR ?? join3(homedir3(), ".suveren");
803
+ var DEFAULT_DIR3 = process.env.SUVEREN_DATA_DIR ?? join4(homedir3(), ".suveren");
716
804
  var MAX_RECORDS = 200;
717
805
  var MAX_AGE_MS = 30 * 24 * 60 * 60 * 1e3;
718
806
  var DenialLog = class {
@@ -725,11 +813,11 @@ var DenialLog = class {
725
813
  }
726
814
  setVaultKey(key) {
727
815
  this.vaultKey = key;
728
- if (existsSync3(this.encryptedFilePath)) {
816
+ if (existsSync4(this.encryptedFilePath)) {
729
817
  this.loadEncrypted();
730
818
  } else if (this.records.length > 0) {
731
819
  this.persistEncrypted();
732
- if (existsSync3(this.plaintextFilePath)) {
820
+ if (existsSync4(this.plaintextFilePath)) {
733
821
  try {
734
822
  unlinkSync3(this.plaintextFilePath);
735
823
  } catch {
@@ -779,18 +867,18 @@ var DenialLog = class {
779
867
  return Buffer.concat([decipher.update(Buffer.from(blob.ciphertext, "hex")), decipher.final()]).toString("utf8");
780
868
  }
781
869
  get plaintextFilePath() {
782
- return join3(this.baseDir, "denials.json");
870
+ return join4(this.baseDir, "denials.json");
783
871
  }
784
872
  get encryptedFilePath() {
785
- return join3(this.baseDir, "denials.enc.json");
873
+ return join4(this.baseDir, "denials.enc.json");
786
874
  }
787
875
  loadPlaintext() {
788
- if (!existsSync3(this.plaintextFilePath)) {
876
+ if (!existsSync4(this.plaintextFilePath)) {
789
877
  mkdirSync3(this.baseDir, { recursive: true });
790
878
  return;
791
879
  }
792
880
  try {
793
- const data = JSON.parse(readFileSync3(this.plaintextFilePath, "utf-8"));
881
+ const data = JSON.parse(readFileSync4(this.plaintextFilePath, "utf-8"));
794
882
  this.records = data.records ?? [];
795
883
  } catch {
796
884
  console.error(`[DenialLog] Could not parse ${this.plaintextFilePath}, starting fresh`);
@@ -798,9 +886,9 @@ var DenialLog = class {
798
886
  }
799
887
  }
800
888
  loadEncrypted() {
801
- if (!existsSync3(this.encryptedFilePath)) return;
889
+ if (!existsSync4(this.encryptedFilePath)) return;
802
890
  try {
803
- const data = JSON.parse(readFileSync3(this.encryptedFilePath, "utf-8"));
891
+ const data = JSON.parse(readFileSync4(this.encryptedFilePath, "utf-8"));
804
892
  const logFile = JSON.parse(this.decrypt(data.blob));
805
893
  this.records = logFile.records ?? [];
806
894
  } catch (err) {
@@ -825,12 +913,242 @@ var DenialLog = class {
825
913
  }
826
914
  };
827
915
 
916
+ // src/lib/receipt-archive.ts
917
+ import { mkdirSync as mkdirSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync4, existsSync as existsSync5, unlinkSync as unlinkSync4 } from "fs";
918
+ import { homedir as homedir4 } from "os";
919
+ import { dirname as dirname4, join as join5 } from "path";
920
+ import { createCipheriv as createCipheriv4, createDecipheriv as createDecipheriv4, randomBytes as randomBytes4 } from "crypto";
921
+ var DEFAULT_DIR4 = process.env.SUVEREN_DATA_DIR ?? join5(homedir4(), ".suveren");
922
+ function stripPreviews(proposal) {
923
+ const args = proposal.toolArgs;
924
+ if (!args || typeof args !== "object") return proposal;
925
+ const { _imagePreview, ...rest } = args;
926
+ if (_imagePreview === void 0) return proposal;
927
+ return { ...proposal, toolArgs: rest };
928
+ }
929
+ var ReceiptArchive = class {
930
+ data = { version: 1, authorizations: {}, receipts: [] };
931
+ baseDir;
932
+ vaultKey = null;
933
+ constructor(filePath) {
934
+ if (filePath && filePath.endsWith(".json")) {
935
+ this.baseDir = dirname4(filePath);
936
+ } else {
937
+ this.baseDir = filePath ?? DEFAULT_DIR4;
938
+ }
939
+ this.loadPlaintext();
940
+ }
941
+ /**
942
+ * Set the vault key for encryption. Triggers migration from plaintext if needed.
943
+ */
944
+ setVaultKey(key) {
945
+ this.vaultKey = key;
946
+ if (existsSync5(this.encryptedFilePath)) {
947
+ this.loadEncrypted();
948
+ } else if (this.data.receipts.length > 0 || Object.keys(this.data.authorizations).length > 0) {
949
+ this.persistEncrypted();
950
+ if (existsSync5(this.plaintextFilePath)) {
951
+ try {
952
+ unlinkSync4(this.plaintextFilePath);
953
+ } catch {
954
+ }
955
+ }
956
+ }
957
+ }
958
+ /**
959
+ * True when encrypted evidence exists on disk that this process cannot read
960
+ * yet (no vault key). Callers MUST NOT present the archive as "empty" in
961
+ * this state — that would report a hollow success over unreadable evidence.
962
+ */
963
+ isLocked() {
964
+ return this.vaultKey === null && existsSync5(this.encryptedFilePath);
965
+ }
966
+ /**
967
+ * Archive a signed receipt (and the grant it was issued under).
968
+ * Idempotent on receipt id — a replayed receipt is recorded once.
969
+ */
970
+ record(entry, now = Math.floor(Date.now() / 1e3)) {
971
+ let changed = false;
972
+ const receiptId = typeof entry.receipt?.id === "string" ? entry.receipt.id : void 0;
973
+ const alreadyArchived = receiptId !== void 0 && this.data.receipts.some((r) => r.receipt.id === receiptId);
974
+ if (!alreadyArchived) {
975
+ this.data.receipts.push({
976
+ archivedAt: now,
977
+ authorizationId: entry.authorizationId,
978
+ asUrl: entry.asUrl,
979
+ asPublicKey: entry.asPublicKey,
980
+ receipt: entry.receipt,
981
+ ...entry.proposal ? { proposal: stripPreviews(entry.proposal) } : {},
982
+ ...entry.boundContent !== void 0 ? { boundContent: entry.boundContent } : {}
983
+ });
984
+ changed = true;
985
+ }
986
+ if (entry.authorization) {
987
+ const existing = this.data.authorizations[entry.authorizationId];
988
+ if (!existing) {
989
+ this.data.authorizations[entry.authorizationId] = {
990
+ authorizationId: entry.authorizationId,
991
+ profileId: entry.authorization.profileId,
992
+ boundsHash: entry.authorization.boundsHash,
993
+ contextHash: entry.authorization.contextHash,
994
+ bounds: entry.authorization.bounds,
995
+ context: entry.authorization.context,
996
+ intent: entry.authorization.intent,
997
+ attestations: [...entry.authorization.attestations],
998
+ archivedAt: now
999
+ };
1000
+ changed = true;
1001
+ } else {
1002
+ for (const att of entry.authorization.attestations) {
1003
+ const seen = existing.attestations.some(
1004
+ (a) => a.domain === att.domain && a.blob === att.blob
1005
+ );
1006
+ if (!seen) {
1007
+ existing.attestations.push(att);
1008
+ changed = true;
1009
+ }
1010
+ }
1011
+ if (!existing.bounds && entry.authorization.bounds) {
1012
+ existing.bounds = entry.authorization.bounds;
1013
+ changed = true;
1014
+ }
1015
+ if (!existing.context && entry.authorization.context) {
1016
+ existing.context = entry.authorization.context;
1017
+ changed = true;
1018
+ }
1019
+ if (!existing.intent && entry.authorization.intent) {
1020
+ existing.intent = entry.authorization.intent;
1021
+ changed = true;
1022
+ }
1023
+ }
1024
+ }
1025
+ if (changed) this.persist();
1026
+ }
1027
+ getReceipts() {
1028
+ return [...this.data.receipts];
1029
+ }
1030
+ getAuthorizations() {
1031
+ return Object.values(this.data.authorizations);
1032
+ }
1033
+ get size() {
1034
+ return this.data.receipts.length;
1035
+ }
1036
+ // ─── Encryption helpers ─────────────────────────────────────────────────
1037
+ encrypt(plaintext) {
1038
+ if (!this.vaultKey) throw new Error("No vault key");
1039
+ const iv = randomBytes4(12);
1040
+ const cipher = createCipheriv4("aes-256-gcm", this.vaultKey, iv);
1041
+ const encrypted = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
1042
+ const tag = cipher.getAuthTag();
1043
+ return {
1044
+ iv: iv.toString("hex"),
1045
+ ciphertext: encrypted.toString("hex"),
1046
+ tag: tag.toString("hex")
1047
+ };
1048
+ }
1049
+ decrypt(blob) {
1050
+ if (!this.vaultKey) throw new Error("No vault key");
1051
+ const decipher = createDecipheriv4(
1052
+ "aes-256-gcm",
1053
+ this.vaultKey,
1054
+ Buffer.from(blob.iv, "hex")
1055
+ );
1056
+ decipher.setAuthTag(Buffer.from(blob.tag, "hex"));
1057
+ const decrypted = Buffer.concat([
1058
+ decipher.update(Buffer.from(blob.ciphertext, "hex")),
1059
+ decipher.final()
1060
+ ]);
1061
+ return decrypted.toString("utf8");
1062
+ }
1063
+ // ─── File paths ─────────────────────────────────────────────────────────
1064
+ get plaintextFilePath() {
1065
+ return join5(this.baseDir, "receipt-archive.json");
1066
+ }
1067
+ get encryptedFilePath() {
1068
+ return join5(this.baseDir, "receipt-archive.enc.json");
1069
+ }
1070
+ // ─── Load / Persist ─────────────────────────────────────────────────────
1071
+ loadPlaintext() {
1072
+ if (!existsSync5(this.plaintextFilePath)) {
1073
+ mkdirSync4(this.baseDir, { recursive: true });
1074
+ return;
1075
+ }
1076
+ try {
1077
+ const raw = readFileSync5(this.plaintextFilePath, "utf-8");
1078
+ const parsed = JSON.parse(raw);
1079
+ this.data = {
1080
+ version: 1,
1081
+ authorizations: parsed.authorizations ?? {},
1082
+ receipts: parsed.receipts ?? []
1083
+ };
1084
+ } catch {
1085
+ console.error(`[ReceiptArchive] Could not parse ${this.plaintextFilePath} \u2014 preserving as .corrupt`);
1086
+ try {
1087
+ writeFileSync4(
1088
+ `${this.plaintextFilePath}.corrupt-${Date.now()}`,
1089
+ readFileSync5(this.plaintextFilePath)
1090
+ );
1091
+ } catch {
1092
+ }
1093
+ }
1094
+ }
1095
+ loadEncrypted() {
1096
+ if (!existsSync5(this.encryptedFilePath)) return;
1097
+ try {
1098
+ const raw = readFileSync5(this.encryptedFilePath, "utf-8");
1099
+ const data = JSON.parse(raw);
1100
+ const decrypted = this.decrypt(data.blob);
1101
+ const parsed = JSON.parse(decrypted);
1102
+ this.data = {
1103
+ version: 1,
1104
+ authorizations: parsed.authorizations ?? {},
1105
+ receipts: parsed.receipts ?? []
1106
+ };
1107
+ } catch (err) {
1108
+ console.error(`[ReceiptArchive] Could not decrypt ${this.encryptedFilePath} \u2014 preserving as .corrupt:`, err);
1109
+ try {
1110
+ writeFileSync4(
1111
+ `${this.encryptedFilePath}.corrupt-${Date.now()}`,
1112
+ readFileSync5(this.encryptedFilePath)
1113
+ );
1114
+ } catch {
1115
+ }
1116
+ }
1117
+ }
1118
+ persist() {
1119
+ if (this.vaultKey) {
1120
+ this.persistEncrypted();
1121
+ } else {
1122
+ this.persistPlaintext();
1123
+ }
1124
+ }
1125
+ persistPlaintext() {
1126
+ mkdirSync4(this.baseDir, { recursive: true, mode: 448 });
1127
+ writeFileSync4(this.plaintextFilePath, JSON.stringify(this.data, null, 2), { encoding: "utf-8", mode: 384 });
1128
+ }
1129
+ persistEncrypted() {
1130
+ const data = {
1131
+ version: 1,
1132
+ blob: this.encrypt(JSON.stringify(this.data))
1133
+ };
1134
+ mkdirSync4(this.baseDir, { recursive: true, mode: 448 });
1135
+ writeFileSync4(this.encryptedFilePath, JSON.stringify(data, null, 2), { encoding: "utf-8", mode: 384 });
1136
+ }
1137
+ };
1138
+
828
1139
  // src/lib/gatekeeper.ts
829
1140
  import { verify } from "@hap/core";
830
1141
  var MCPGatekeeper = class {
831
- constructor(cache, executionLog) {
1142
+ /**
1143
+ * No ExecutionLog dependency by construction. The local record is
1144
+ * display-only (protocol.md → "Executor Gating, Context vs Bounds,
1145
+ * Display-Only Logs"), so the Gatekeeper must not be able to read it: with
1146
+ * no log in scope, a local cumulative check cannot be reintroduced by
1147
+ * accident. `SharedState.executionLog` stays — it feeds the UI's consumption
1148
+ * display and nothing else.
1149
+ */
1150
+ constructor(cache) {
832
1151
  this.cache = cache;
833
- this.executionLog = executionLog;
834
1152
  }
835
1153
  /**
836
1154
  * Verify an execution request against a cached authorization.
@@ -877,16 +1195,15 @@ var MCPGatekeeper = class {
877
1195
  attestations: auth.attestations.map((a) => a.blob),
878
1196
  execution,
879
1197
  context: resolvedContext,
880
- // Scopes the local cumulative check to the same (profileId, path) key the
881
- // ExecutionLog records under (see tool-proxy's record call). It cannot go
882
- // inside `frame`: that is validated against the profile's boundsSchema,
883
- // which declares no `path` field in any shipped profile, so an extra key
884
- // there fails verification with "Unknown field". Omitting it entirely
885
- // the previous behaviour made every cumulative lookup return zero, so
886
- // the local gate never fired and the AS was doing all the enforcing.
1198
+ // Identifies the grant whose bounds are being checked. hap-core reads it
1199
+ // only to scope a cumulative lookup, which no longer happens here (see
1200
+ // below); it is kept because it belongs to the request, not to the
1201
+ // dropped check. It cannot go inside `frame`: that is validated against
1202
+ // the profile's boundsSchema, which declares no `path` field in any
1203
+ // shipped profile, so an extra key there fails with "Unknown field".
887
1204
  path: auth.path
888
1205
  };
889
- const result = await verify(request, publicKeyHex, void 0, this.executionLog);
1206
+ const result = await verify(request, publicKeyHex);
890
1207
  return { result, authorization: auth };
891
1208
  }
892
1209
  };
@@ -898,6 +1215,7 @@ var SharedState = class {
898
1215
  gateStore;
899
1216
  executionLog;
900
1217
  denialLog;
1218
+ receiptArchive;
901
1219
  gatekeeper;
902
1220
  constructor(spUrl2, gateStorePath) {
903
1221
  this.spClient = new SPClient(spUrl2);
@@ -905,7 +1223,49 @@ var SharedState = class {
905
1223
  this.gateStore = new GateStore(gateStorePath);
906
1224
  this.executionLog = new ExecutionLog(gateStorePath);
907
1225
  this.denialLog = new DenialLog(gateStorePath);
908
- this.gatekeeper = new MCPGatekeeper(this.cache, this.executionLog);
1226
+ this.receiptArchive = new ReceiptArchive(gateStorePath);
1227
+ this.gatekeeper = new MCPGatekeeper(this.cache);
1228
+ }
1229
+ /**
1230
+ * Archive a signed receipt into the local receipt archive — the subject's
1231
+ * own durable copy of the evidence (see receipt-archive.ts).
1232
+ *
1233
+ * Best-effort by contract: the AS has already issued the receipt and holds
1234
+ * the authoritative copy, so an archive failure logs loudly but MUST NOT
1235
+ * block the execution it documents.
1236
+ */
1237
+ async archiveReceipt(receipt, opts) {
1238
+ try {
1239
+ let asPublicKey;
1240
+ try {
1241
+ asPublicKey = await this.cache.getPublicKey();
1242
+ } catch {
1243
+ }
1244
+ this.receiptArchive.record({
1245
+ receipt,
1246
+ authorizationId: opts.authorizationId,
1247
+ asUrl: this.spClient.url,
1248
+ asPublicKey,
1249
+ proposal: opts.proposal,
1250
+ boundContent: opts.boundContent,
1251
+ authorization: opts.profileId ? {
1252
+ profileId: opts.profileId,
1253
+ boundsHash: opts.boundsHash,
1254
+ contextHash: opts.contextHash ?? this.gateStore.get(opts.authorizationId)?.contextHash,
1255
+ bounds: opts.bounds,
1256
+ context: opts.context,
1257
+ // Fall back to the gate store — the review path's cached auth
1258
+ // carries no gate content.
1259
+ intent: opts.intent ?? this.gateStore.get(opts.authorizationId)?.gateContent?.intent,
1260
+ attestations: opts.attestations ?? []
1261
+ } : void 0
1262
+ });
1263
+ } catch (err) {
1264
+ console.error(
1265
+ "[Suveren MCP] Receipt archive write failed (execution proceeds \u2014 AS retains authoritative copy):",
1266
+ err
1267
+ );
1268
+ }
909
1269
  }
910
1270
  setGateContent(path, authorizationId, profileId, content, opts) {
911
1271
  this.gateStore.set(authorizationId, {
@@ -1012,17 +1372,17 @@ function formatConsumptionFull(entries) {
1012
1372
  }
1013
1373
 
1014
1374
  // src/lib/context-loader.ts
1015
- import { readFileSync as readFileSync4, existsSync as existsSync4 } from "fs";
1016
- import { homedir as homedir4 } from "os";
1017
- import { join as join4 } from "path";
1018
- var DEFAULT_DIR4 = process.env.SUVEREN_DATA_DIR ?? join4(homedir4(), ".suveren");
1375
+ import { readFileSync as readFileSync6, existsSync as existsSync6 } from "fs";
1376
+ import { homedir as homedir5 } from "os";
1377
+ import { join as join6 } from "path";
1378
+ var DEFAULT_DIR5 = process.env.SUVEREN_DATA_DIR ?? join6(homedir5(), ".suveren");
1019
1379
  var BRIEF_MAX_CHARS = 16 * 1024;
1020
1380
  function readContextFile(dataDir) {
1021
- const dir = dataDir ?? DEFAULT_DIR4;
1022
- const filePath = join4(dir, "context.md");
1023
- if (!existsSync4(filePath)) return null;
1381
+ const dir = dataDir ?? DEFAULT_DIR5;
1382
+ const filePath = join6(dir, "context.md");
1383
+ if (!existsSync6(filePath)) return null;
1024
1384
  try {
1025
- const content = readFileSync4(filePath, "utf-8").trim();
1385
+ const content = readFileSync6(filePath, "utf-8").trim();
1026
1386
  return content || null;
1027
1387
  } catch {
1028
1388
  return null;
@@ -1192,7 +1552,7 @@ function appendVerificationFooter(tool, args, receiptId, subject) {
1192
1552
  // src/lib/content-binding.ts
1193
1553
  import {
1194
1554
  computeContentHash,
1195
- computeFieldsContentHash,
1555
+ selectBoundFields,
1196
1556
  isFieldBinding,
1197
1557
  bindingAppliesTo,
1198
1558
  getProfile as getProfile2
@@ -1212,14 +1572,18 @@ function computeContentBinding(profileId, tool, toolArgs, actionType) {
1212
1572
  }
1213
1573
  return void 0;
1214
1574
  }
1575
+ const bound = selectBoundFields(binding, toolArgs);
1215
1576
  return {
1216
- contentHash: computeFieldsContentHash(binding, toolArgs),
1217
- contentBinding: { version: binding.version, kind: binding.kind, fields: binding.fields }
1577
+ contentHash: computeContentHash(binding, bound),
1578
+ contentBinding: { version: binding.version, kind: binding.kind, fields: binding.fields },
1579
+ boundContent: bound
1218
1580
  };
1219
1581
  }
1220
1582
  let contentHash;
1583
+ let boundContent;
1221
1584
  if (binding.kind === "jcs") {
1222
1585
  contentHash = computeContentHash(binding, toolArgs);
1586
+ boundContent = toolArgs;
1223
1587
  } else {
1224
1588
  const declared = tool?.gating?.contentField;
1225
1589
  const field = declared ?? (tool ? detectContentField(tool) : null);
@@ -1227,10 +1591,12 @@ function computeContentBinding(profileId, tool, toolArgs, actionType) {
1227
1591
  const raw = typeof toolArgs[field] === "string" ? toolArgs[field] : "";
1228
1592
  if (raw === "") return void 0;
1229
1593
  contentHash = computeContentHash(binding, raw);
1594
+ boundContent = raw;
1230
1595
  }
1231
1596
  return {
1232
1597
  contentHash,
1233
- contentBinding: { version: binding.version, kind: binding.kind }
1598
+ contentBinding: { version: binding.version, kind: binding.kind },
1599
+ boundContent
1234
1600
  };
1235
1601
  }
1236
1602
  function attachReceiptId(tool, args, receiptId) {
@@ -1657,10 +2023,11 @@ function createGatedToolHandlerInner(tool, integrationManager2, state2) {
1657
2023
  return inner(args);
1658
2024
  };
1659
2025
  if (category === "disabled") {
2026
+ const reason = tool.gating.disabledReason;
1660
2027
  return async () => ({
1661
2028
  content: [{
1662
2029
  type: "text",
1663
- text: `Tool "${tool.namespacedName}" is disabled by the integration manifest and cannot be used.`
2030
+ text: reason ? `Tool "${tool.namespacedName}" is refused: ${reason}. A tool that no manifest entry describes cannot be gated \u2014 there is nothing declaring which action type it performs or how its arguments map to the bounds. Add an entry for "${tool.originalName}" under this integration's toolGating.overrides (with "category": "read" for a read-only tool) before it can be used.` : `Tool "${tool.namespacedName}" is disabled by the integration manifest and cannot be used.`
1664
2031
  }],
1665
2032
  isError: true
1666
2033
  });
@@ -1849,6 +2216,29 @@ function createGatedToolHandlerInner(tool, integrationManager2, state2) {
1849
2216
  }
1850
2217
  }
1851
2218
  }
2219
+ const declaredActionType = typeof execution.action_type === "string" && execution.action_type.length > 0 ? execution.action_type : void 0;
2220
+ if (!declaredActionType) {
2221
+ return {
2222
+ content: [{
2223
+ type: "text",
2224
+ text: `Blocked by Gatekeeper: tool ${tool.namespacedName} declares no action_type in its manifest's staticExecution. A write cannot be bounds-checked without its action type \u2014 fix the integration manifest.`
2225
+ }],
2226
+ isError: true
2227
+ };
2228
+ }
2229
+ {
2230
+ const profileDef = getProfile3(profile);
2231
+ const registry = profileDef?.boundsSchema?.actionTypes;
2232
+ if (Array.isArray(registry) && registry.length > 0 && !registry.includes(declaredActionType)) {
2233
+ return {
2234
+ content: [{
2235
+ type: "text",
2236
+ text: `Blocked by Gatekeeper: action_type "${declaredActionType}" is not in profile ${profile}'s actionTypes registry (${registry.join(", ")}). Fix the integration manifest.`
2237
+ }],
2238
+ isError: true
2239
+ };
2240
+ }
2241
+ }
1852
2242
  const auths = state2.getEnrichedAuthorizations();
1853
2243
  const matchingAuths = auths.filter(
1854
2244
  (a) => a.complete && profileMatches(a.profileId, profile)
@@ -1946,12 +2336,7 @@ Proposal ID: ${proposal.id}. Check status with check-pending-commitments(proposa
1946
2336
  }
1947
2337
  let receiptId;
1948
2338
  try {
1949
- const actionType = typeof execution.action_type === "string" ? execution.action_type : void 0;
1950
- if (!actionType) {
1951
- console.error(
1952
- `[Suveren MCP] Warning: tool ${tool.namespacedName} has no action_type in staticExecution. Bounds check may be skipped. Fix the integration manifest.`
1953
- );
1954
- }
2339
+ const actionType = declaredActionType;
1955
2340
  const binding = computeContentBinding(auth.profileId, tool, args, actionType);
1956
2341
  const { receipt } = await state2.spClient.postReceipt({
1957
2342
  authorizationId: authzId,
@@ -1963,9 +2348,27 @@ Proposal ID: ${proposal.id}. Check status with check-pending-commitments(proposa
1963
2348
  executionContext: { ...execution },
1964
2349
  amount: typeof execution.amount === "number" ? execution.amount : void 0,
1965
2350
  idempotencyKey: randomUUID(),
1966
- ...binding ?? {}
2351
+ // Privacy: send the hash and how to reproduce it — never the
2352
+ // preimage. `binding` also carries `boundContent`, the plaintext
2353
+ // that was hashed (an email's to/cc/subject/body); it stays on
2354
+ // this machine for the local archive, so it must be picked out
2355
+ // field by field rather than spread.
2356
+ ...binding ? { contentHash: binding.contentHash, contentBinding: binding.contentBinding } : {}
1967
2357
  });
1968
2358
  receiptId = typeof receipt?.id === "string" ? receipt.id : void 0;
2359
+ await state2.archiveReceipt(receipt, {
2360
+ authorizationId: authzId,
2361
+ profileId: auth.profileId,
2362
+ boundsHash: auth.boundsHash,
2363
+ contextHash: auth.contextHash,
2364
+ bounds: auth.bounds ?? auth.frame,
2365
+ context: auth.context,
2366
+ intent: auth.gateContent?.intent,
2367
+ attestations: auth.attestations,
2368
+ // Automatic path has no proposal, so without this the archive
2369
+ // would hold a hash of content it cannot reproduce.
2370
+ boundContent: binding?.boundContent
2371
+ });
1969
2372
  } catch (err) {
1970
2373
  if (err instanceof ContentBindingError) {
1971
2374
  return {
@@ -2100,7 +2503,7 @@ function buildCapabilityMap(profileId, toolGating, integrationManager2) {
2100
2503
  const shortName = shortProfileName2(profileId);
2101
2504
  const gated = [];
2102
2505
  const readOnly = [];
2103
- const defaultGated = [];
2506
+ const undescribed = [];
2104
2507
  for (const tool of allTools) {
2105
2508
  if (!tool.gating || !tool.gating.profile) {
2106
2509
  continue;
@@ -2122,12 +2525,7 @@ function buildCapabilityMap(profileId, toolGating, integrationManager2) {
2122
2525
  const actionType = override.staticExecution?.action_type ?? "unknown";
2123
2526
  gated.push(` - ${tool.originalName}: ${actionType}${mappingDesc ? `, ${mappingDesc}` : ""}`);
2124
2527
  } else {
2125
- const defaultAction = toolGating.default.staticExecution?.action_type;
2126
- if (defaultAction === "read" && Object.keys(toolGating.default.executionMapping ?? {}).length === 0) {
2127
- defaultGated.push(tool.originalName);
2128
- } else {
2129
- gated.push(` - ${tool.originalName}: ${defaultAction ?? "default"} (default gating)`);
2130
- }
2528
+ undescribed.push(tool.originalName);
2131
2529
  }
2132
2530
  }
2133
2531
  const lines = [];
@@ -2139,10 +2537,12 @@ function buildCapabilityMap(profileId, toolGating, integrationManager2) {
2139
2537
  if (readOnly.length > 0) {
2140
2538
  lines.push(` Read-only (no authorization needed): ${readOnly.join(", ")}`);
2141
2539
  }
2142
- if (defaultGated.length > 0) {
2143
- lines.push(` Default-gated (action_type: ${toolGating.default.staticExecution?.action_type ?? "read"}): ${defaultGated.join(", ")}`);
2540
+ if (undescribed.length > 0) {
2541
+ lines.push(
2542
+ ` Refused (no manifest entry \u2014 do not call): ${undescribed.join(", ")}`
2543
+ );
2144
2544
  }
2145
- if (gated.length === 0 && readOnly.length === 0 && defaultGated.length === 0) {
2545
+ if (gated.length === 0 && readOnly.length === 0 && undescribed.length === 0) {
2146
2546
  lines.push(" No tools discovered for this profile.");
2147
2547
  }
2148
2548
  return lines.join("\n");
@@ -2431,9 +2831,25 @@ async function executeCommitted(proposal, state2, integrationManager2) {
2431
2831
  // SPClient therefore treats proposalId as retry-safe on its own; a
2432
2832
  // PROPOSAL_ALREADY_EXECUTED answer is still a definitive rejection
2433
2833
  // (never retried) and means a *different* caller consumed the proposal.
2434
- ...binding ?? {}
2834
+ // Privacy: hash + how to reproduce it only. `binding.boundContent` is
2835
+ // the plaintext preimage (what the human approved) and never leaves
2836
+ // this machine — see the archive call below.
2837
+ ...binding ? { contentHash: binding.contentHash, contentBinding: binding.contentBinding } : {}
2435
2838
  });
2436
2839
  receiptId = typeof receipt?.id === "string" ? receipt.id : void 0;
2840
+ await state2.archiveReceipt(receipt, {
2841
+ authorizationId: proposal.authorizationId,
2842
+ profileId: proposal.profileId,
2843
+ boundsHash: cachedAuth?.boundsHash,
2844
+ contextHash: cachedAuth?.contextHash,
2845
+ bounds: cachedAuth?.bounds ?? cachedAuth?.frame,
2846
+ context: cachedAuth?.context,
2847
+ attestations: cachedAuth?.attestations,
2848
+ // What the human actually approved, kept next to the receipt that cites
2849
+ // it — otherwise `proposalId` is a pointer to a server we may not have.
2850
+ proposal,
2851
+ boundContent: binding?.boundContent
2852
+ });
2437
2853
  } catch (err) {
2438
2854
  if (err instanceof ContentBindingError2) {
2439
2855
  return {
@@ -2756,16 +3172,16 @@ function verifyGateContentHashes(content, auth) {
2756
3172
  }
2757
3173
 
2758
3174
  // src/lib/integration-registry.ts
2759
- import { mkdirSync as mkdirSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync4, existsSync as existsSync5 } from "fs";
2760
- import { homedir as homedir5 } from "os";
2761
- import { dirname as dirname4, join as join5 } from "path";
2762
- var DEFAULT_DATA_DIR = process.env.SUVEREN_DATA_DIR ?? join5(homedir5(), ".suveren");
3175
+ import { mkdirSync as mkdirSync5, readFileSync as readFileSync7, writeFileSync as writeFileSync5, existsSync as existsSync7 } from "fs";
3176
+ import { homedir as homedir6 } from "os";
3177
+ import { dirname as dirname5, join as join7 } from "path";
3178
+ var DEFAULT_DATA_DIR = process.env.SUVEREN_DATA_DIR ?? join7(homedir6(), ".suveren");
2763
3179
  var IntegrationRegistry = class {
2764
3180
  integrations = /* @__PURE__ */ new Map();
2765
3181
  filePath;
2766
3182
  constructor(dataDir) {
2767
3183
  const dir = dataDir ?? DEFAULT_DATA_DIR;
2768
- this.filePath = join5(dir, "integrations.json");
3184
+ this.filePath = join7(dir, "integrations.json");
2769
3185
  this.load();
2770
3186
  }
2771
3187
  // ─── CRUD ───────────────────────────────────────────────────────────────
@@ -2796,12 +3212,12 @@ var IntegrationRegistry = class {
2796
3212
  }
2797
3213
  // ─── Load / Save ────────────────────────────────────────────────────────
2798
3214
  load() {
2799
- if (!existsSync5(this.filePath)) {
2800
- mkdirSync4(dirname4(this.filePath), { recursive: true });
3215
+ if (!existsSync7(this.filePath)) {
3216
+ mkdirSync5(dirname5(this.filePath), { recursive: true });
2801
3217
  return;
2802
3218
  }
2803
3219
  try {
2804
- const raw = readFileSync5(this.filePath, "utf-8");
3220
+ const raw = readFileSync7(this.filePath, "utf-8");
2805
3221
  const data = JSON.parse(raw);
2806
3222
  const migrated = data.integrations.map((i) => {
2807
3223
  if ("toolGating" in i && !("profile" in i)) {
@@ -2830,33 +3246,39 @@ var IntegrationRegistry = class {
2830
3246
  version: 1,
2831
3247
  integrations: Array.from(this.integrations.values())
2832
3248
  };
2833
- mkdirSync4(dirname4(this.filePath), { recursive: true });
2834
- writeFileSync4(this.filePath, JSON.stringify(data, null, 2), { encoding: "utf-8" });
3249
+ mkdirSync5(dirname5(this.filePath), { recursive: true });
3250
+ writeFileSync5(this.filePath, JSON.stringify(data, null, 2), { encoding: "utf-8" });
2835
3251
  }
2836
3252
  };
2837
3253
 
2838
3254
  // src/lib/integration-manager.ts
2839
- import { homedir as homedir6 } from "os";
2840
- import { join as join6, delimiter } from "path";
2841
- import { existsSync as existsSync6, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, readFileSync as readFileSync6, rmSync } from "fs";
3255
+ import { homedir as homedir7 } from "os";
3256
+ import { join as join8, delimiter } from "path";
3257
+ import { existsSync as existsSync8, writeFileSync as writeFileSync6, mkdirSync as mkdirSync6, readFileSync as readFileSync8, rmSync } from "fs";
2842
3258
  import { execFile } from "child_process";
2843
3259
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
2844
3260
  import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
2845
3261
  import { getProfile as getProfile5 } from "@hap/core";
2846
- var DEFAULT_DATA_DIR2 = process.env.SUVEREN_DATA_DIR ?? join6(homedir6(), ".suveren");
2847
- var INTEGRATIONS_DIR = process.env.SUVEREN_INTEGRATIONS_DIR ?? join6(DEFAULT_DATA_DIR2, "integrations");
2848
- var INTEGRATIONS_BIN = join6(INTEGRATIONS_DIR, "node_modules", ".bin");
3262
+ var DEFAULT_DATA_DIR2 = process.env.SUVEREN_DATA_DIR ?? join8(homedir7(), ".suveren");
3263
+ var INTEGRATIONS_DIR = process.env.SUVEREN_INTEGRATIONS_DIR ?? join8(DEFAULT_DATA_DIR2, "integrations");
3264
+ var installLock = Promise.resolve();
3265
+ function withInstallLock(task) {
3266
+ const run = installLock.then(task, task);
3267
+ installLock = run.then(() => void 0, () => void 0);
3268
+ return run;
3269
+ }
3270
+ var INTEGRATIONS_BIN = join8(INTEGRATIONS_DIR, "node_modules", ".bin");
2849
3271
  function buildPath() {
2850
3272
  const base = process.env.PATH ?? "";
2851
3273
  return [INTEGRATIONS_BIN, base].join(delimiter);
2852
3274
  }
2853
3275
  function ensureIntegrationsDir() {
2854
- if (!existsSync6(INTEGRATIONS_DIR)) {
2855
- mkdirSync5(INTEGRATIONS_DIR, { recursive: true });
3276
+ if (!existsSync8(INTEGRATIONS_DIR)) {
3277
+ mkdirSync6(INTEGRATIONS_DIR, { recursive: true });
2856
3278
  }
2857
- const pkgPath = join6(INTEGRATIONS_DIR, "package.json");
2858
- if (!existsSync6(pkgPath)) {
2859
- writeFileSync5(pkgPath, JSON.stringify({ name: "suveren-integrations", version: "1.0.0", private: true }, null, 2));
3279
+ const pkgPath = join8(INTEGRATIONS_DIR, "package.json");
3280
+ if (!existsSync8(pkgPath)) {
3281
+ writeFileSync6(pkgPath, JSON.stringify({ name: "suveren-integrations", version: "1.0.0", private: true }, null, 2));
2860
3282
  }
2861
3283
  }
2862
3284
  function readAgeOf(config) {
@@ -2922,19 +3344,19 @@ var IntegrationManager = class {
2922
3344
  * exist, and treat anything else as not-installed (and reinstallable).
2923
3345
  */
2924
3346
  isUsableInstall(npmPackage) {
2925
- const pkgDir = join6(INTEGRATIONS_DIR, "node_modules", ...npmPackage.split("/"));
2926
- const pkgJson = join6(pkgDir, "package.json");
2927
- if (!existsSync6(pkgDir) || !existsSync6(pkgJson)) return false;
3347
+ const pkgDir = join8(INTEGRATIONS_DIR, "node_modules", ...npmPackage.split("/"));
3348
+ const pkgJson = join8(pkgDir, "package.json");
3349
+ if (!existsSync8(pkgDir) || !existsSync8(pkgJson)) return false;
2928
3350
  try {
2929
- const pkg = JSON.parse(readFileSync6(pkgJson, "utf8"));
3351
+ const pkg = JSON.parse(readFileSync8(pkgJson, "utf8"));
2930
3352
  const defaultBinName = (pkg.name ?? npmPackage).split("/").pop() ?? "";
2931
3353
  const binMap = typeof pkg.bin === "string" ? { [defaultBinName]: pkg.bin } : pkg.bin ?? {};
2932
3354
  const binEntries = Object.entries(binMap);
2933
3355
  for (const [binName, target] of binEntries) {
2934
- if (!existsSync6(join6(pkgDir, target))) return false;
3356
+ if (!existsSync8(join8(pkgDir, target))) return false;
2935
3357
  if (!this.binShimExists(binName)) return false;
2936
3358
  }
2937
- if (binEntries.length === 0 && pkg.main && !existsSync6(join6(pkgDir, pkg.main))) return false;
3359
+ if (binEntries.length === 0 && pkg.main && !existsSync8(join8(pkgDir, pkg.main))) return false;
2938
3360
  return true;
2939
3361
  } catch {
2940
3362
  return false;
@@ -2948,7 +3370,7 @@ var IntegrationManager = class {
2948
3370
  binShimExists(binName) {
2949
3371
  if (!binName) return false;
2950
3372
  const candidates = process.platform === "win32" ? [binName, `${binName}.cmd`, `${binName}.CMD`, `${binName}.ps1`] : [binName];
2951
- return candidates.some((c) => existsSync6(join6(INTEGRATIONS_BIN, c)));
3373
+ return candidates.some((c) => existsSync8(join8(INTEGRATIONS_BIN, c)));
2952
3374
  }
2953
3375
  /**
2954
3376
  * Install an npm package into the managed integrations directory if not
@@ -2965,8 +3387,15 @@ var IntegrationManager = class {
2965
3387
  async ensureInstalled(npmPackage) {
2966
3388
  ensureIntegrationsDir();
2967
3389
  if (this.isUsableInstall(npmPackage)) return;
2968
- const pkgDir = join6(INTEGRATIONS_DIR, "node_modules", ...npmPackage.split("/"));
2969
- if (existsSync6(pkgDir)) {
3390
+ return withInstallLock(async () => {
3391
+ if (this.isUsableInstall(npmPackage)) return;
3392
+ await this.installNow(npmPackage);
3393
+ });
3394
+ }
3395
+ /** The actual install. Callers MUST hold the install lock. */
3396
+ async installNow(npmPackage) {
3397
+ const pkgDir = join8(INTEGRATIONS_DIR, "node_modules", ...npmPackage.split("/"));
3398
+ if (existsSync8(pkgDir)) {
2970
3399
  console.error(`[IntegrationManager] Removing unusable install of ${npmPackage}`);
2971
3400
  try {
2972
3401
  rmSync(pkgDir, { recursive: true, force: true });
@@ -3338,8 +3767,9 @@ var IntegrationManager = class {
3338
3767
  }
3339
3768
  return {
3340
3769
  profile: profileId,
3341
- executionMapping: profileGating.default.executionMapping,
3342
- staticExecution: profileGating.default.staticExecution
3770
+ executionMapping: {},
3771
+ category: "disabled",
3772
+ disabledReason: "not described in manifest"
3343
3773
  };
3344
3774
  }
3345
3775
  /**
@@ -3381,28 +3811,28 @@ var IntegrationManager = class {
3381
3811
  };
3382
3812
 
3383
3813
  // src/lib/profile-loader.ts
3384
- import { readFileSync as readFileSync7, existsSync as existsSync7 } from "fs";
3385
- import { join as join7, resolve } from "path";
3814
+ import { readFileSync as readFileSync9, existsSync as existsSync9 } from "fs";
3815
+ import { join as join9, resolve } from "path";
3386
3816
  import { registerProfile } from "@hap/core";
3387
3817
  function loadProfiles(profilesDir) {
3388
- const dir = resolve(profilesDir ?? process.env.SUVEREN_PROFILES_DIR ?? join7(import.meta.dirname ?? __dirname, "..", "..", "..", "..", "..", "hap-profiles"));
3389
- const indexPath = join7(dir, "index.json");
3390
- if (!existsSync7(indexPath)) {
3818
+ const dir = resolve(profilesDir ?? process.env.SUVEREN_PROFILES_DIR ?? join9(import.meta.dirname ?? __dirname, "..", "..", "..", "..", "..", "hap-profiles"));
3819
+ const indexPath = join9(dir, "index.json");
3820
+ if (!existsSync9(indexPath)) {
3391
3821
  warnNoProfiles(`No index.json at ${indexPath}`);
3392
3822
  return 0;
3393
3823
  }
3394
3824
  let index;
3395
3825
  try {
3396
- index = JSON.parse(readFileSync7(indexPath, "utf-8"));
3826
+ index = JSON.parse(readFileSync9(indexPath, "utf-8"));
3397
3827
  } catch (err) {
3398
3828
  console.error(`[ProfileLoader] Failed to parse ${indexPath}:`, err);
3399
3829
  return 0;
3400
3830
  }
3401
3831
  let loaded = 0;
3402
3832
  for (const [profileId, relativePath] of Object.entries(index.profiles)) {
3403
- const profilePath = join7(dir, relativePath);
3833
+ const profilePath = join9(dir, relativePath);
3404
3834
  try {
3405
- const profile = JSON.parse(readFileSync7(profilePath, "utf-8"));
3835
+ const profile = JSON.parse(readFileSync9(profilePath, "utf-8"));
3406
3836
  registerProfile(profileId, profile);
3407
3837
  const shortName = profileId.split("/").pop()?.replace(/@.*$/, "");
3408
3838
  if (shortName && shortName !== profileId) {
@@ -3435,30 +3865,30 @@ function warnNoProfiles(reason) {
3435
3865
  }
3436
3866
 
3437
3867
  // src/lib/manifest-loader.ts
3438
- import { readFileSync as readFileSync8, existsSync as existsSync8 } from "fs";
3439
- import { join as join8, resolve as resolve2 } from "path";
3868
+ import { readFileSync as readFileSync10, existsSync as existsSync10 } from "fs";
3869
+ import { join as join10, resolve as resolve2 } from "path";
3440
3870
  var manifests = /* @__PURE__ */ new Map();
3441
3871
  function loadManifests(integrationsDir) {
3442
3872
  const dir = resolve2(
3443
- integrationsDir ?? process.env.SUVEREN_MANIFESTS_DIR ?? join8(import.meta.dirname ?? __dirname, "..", "..", "..", "..", "content", "integrations")
3873
+ integrationsDir ?? process.env.SUVEREN_MANIFESTS_DIR ?? join10(import.meta.dirname ?? __dirname, "..", "..", "..", "..", "content", "integrations")
3444
3874
  );
3445
- const indexPath = join8(dir, "index.json");
3446
- if (!existsSync8(indexPath)) {
3875
+ const indexPath = join10(dir, "index.json");
3876
+ if (!existsSync10(indexPath)) {
3447
3877
  console.error(`[ManifestLoader] No index.json found at ${indexPath}, skipping manifest loading`);
3448
3878
  return 0;
3449
3879
  }
3450
3880
  let index;
3451
3881
  try {
3452
- index = JSON.parse(readFileSync8(indexPath, "utf-8"));
3882
+ index = JSON.parse(readFileSync10(indexPath, "utf-8"));
3453
3883
  } catch (err) {
3454
3884
  console.error(`[ManifestLoader] Failed to parse ${indexPath}:`, err);
3455
3885
  return 0;
3456
3886
  }
3457
3887
  let loaded = 0;
3458
3888
  for (const [id, relativePath] of Object.entries(index.integrations)) {
3459
- const manifestPath = join8(dir, relativePath);
3889
+ const manifestPath = join10(dir, relativePath);
3460
3890
  try {
3461
- const manifest = JSON.parse(readFileSync8(manifestPath, "utf-8"));
3891
+ const manifest = JSON.parse(readFileSync10(manifestPath, "utf-8"));
3462
3892
  manifests.set(id, manifest);
3463
3893
  loaded++;
3464
3894
  } catch (err) {
@@ -3476,6 +3906,7 @@ function getAllManifests() {
3476
3906
  }
3477
3907
 
3478
3908
  // bin/http.ts
3909
+ import { decodeAttestationBlob as decodeAttestationBlob3 } from "@hap/core";
3479
3910
  var spUrl = process.env.SUVEREN_AS_URL ?? "https://www.suveren.ai";
3480
3911
  var port = parseInt(process.env.SUVEREN_MCP_PORT ?? "3430", 10);
3481
3912
  var state = new SharedState(spUrl);
@@ -3539,7 +3970,8 @@ app.post("/internal/configure", internalOnly, (req, res) => {
3539
3970
  const key = Buffer.from(vaultKeyHex, "hex");
3540
3971
  state.gateStore.setVaultKey(key);
3541
3972
  state.denialLog.setVaultKey(key);
3542
- console.error("[Suveren MCP] Vault key configured \u2014 gate store + denial log encryption active");
3973
+ state.receiptArchive.setVaultKey(key);
3974
+ console.error("[Suveren MCP] Vault key configured \u2014 gate store + denial log + receipt archive encryption active");
3543
3975
  }
3544
3976
  if (apiKey) {
3545
3977
  state.spClient.setApiKey(apiKey);
@@ -3836,6 +4268,27 @@ app.get("/internal/authorizations", internalOnly, (_req, res) => {
3836
4268
  app.get("/internal/manifests", internalOnly, (_req, res) => {
3837
4269
  res.json({ manifests: getAllManifests() });
3838
4270
  });
4271
+ app.get("/internal/evidence", internalOnly, (_req, res) => {
4272
+ if (state.receiptArchive.isLocked()) {
4273
+ res.status(503).json({ error: "Vault locked \u2014 the evidence archive is encrypted until sign-in." });
4274
+ return;
4275
+ }
4276
+ const authorizations = state.receiptArchive.getAuthorizations().map((a) => ({
4277
+ ...a,
4278
+ attestations: a.attestations.map((att) => {
4279
+ try {
4280
+ return { ...att, decoded: decodeAttestationBlob3(att.blob) };
4281
+ } catch {
4282
+ return att;
4283
+ }
4284
+ })
4285
+ }));
4286
+ res.json({
4287
+ receipts: state.receiptArchive.getReceipts(),
4288
+ authorizations,
4289
+ gates: state.gateStore.getAll()
4290
+ });
4291
+ });
3839
4292
  app.get("/internal/brief", internalOnly, (_req, res) => {
3840
4293
  try {
3841
4294
  const enriched = state.getEnrichedAuthorizations();