@suveren/gateway 0.7.3 → 0.7.6

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 (206) hide show
  1. package/content/integrations/gmail.json +3 -6
  2. package/dist/control-plane/index.mjs +294 -62
  3. package/dist/mcp-server/http.mjs +467 -73
  4. package/dist/ui/assets/{index-CkWaRck1.css → index-BYVb_0oG.css} +1 -1
  5. package/dist/ui/assets/index-CsrM6RQr.js +110 -0
  6. package/dist/ui/index.html +2 -2
  7. package/node_modules/@hap/core/dist/index.d.mts +258 -9
  8. package/node_modules/@hap/core/dist/index.d.ts +258 -9
  9. package/node_modules/@hap/core/dist/index.js +227 -5
  10. package/node_modules/@hap/core/dist/index.mjs +213 -4
  11. package/node_modules/@hap/core/package.json +3 -2
  12. package/node_modules/@hap/core/src/did-key.ts +126 -0
  13. package/node_modules/@hap/core/src/identity.ts +21 -5
  14. package/node_modules/@hap/core/src/index.ts +3 -0
  15. package/node_modules/@hap/core/src/mandate.ts +207 -0
  16. package/node_modules/@hap/core/src/receipt.ts +74 -0
  17. package/node_modules/@hap/core/src/types.ts +81 -5
  18. package/node_modules/@hono/node-server/dist/index.cjs +151 -9
  19. package/node_modules/@hono/node-server/dist/index.mjs +151 -9
  20. package/node_modules/@hono/node-server/package.json +1 -1
  21. package/node_modules/@modelcontextprotocol/sdk/node_modules/body-parser/node_modules/content-type/README.md +2 -0
  22. package/node_modules/@modelcontextprotocol/sdk/node_modules/body-parser/node_modules/content-type/dist/index.d.ts +20 -0
  23. package/node_modules/@modelcontextprotocol/sdk/node_modules/body-parser/node_modules/content-type/dist/index.js +20 -14
  24. package/node_modules/@modelcontextprotocol/sdk/node_modules/body-parser/node_modules/content-type/dist/index.js.map +1 -1
  25. package/node_modules/@modelcontextprotocol/sdk/node_modules/body-parser/node_modules/content-type/package.json +1 -1
  26. package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/lib/accept.js +54 -0
  27. package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/lib/charset.js +10 -34
  28. package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/lib/encoding.js +9 -30
  29. package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/lib/language.js +15 -36
  30. package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/lib/mediaType.js +16 -134
  31. package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/node_modules/content-type/LICENSE +22 -0
  32. package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/node_modules/content-type/README.md +71 -0
  33. package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/node_modules/content-type/dist/index.d.ts +46 -0
  34. package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/node_modules/content-type/dist/index.js +176 -0
  35. package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/node_modules/content-type/dist/index.js.map +1 -0
  36. package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/node_modules/content-type/package.json +52 -0
  37. package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/package.json +16 -10
  38. package/node_modules/@modelcontextprotocol/sdk/node_modules/type-is/node_modules/content-type/README.md +2 -0
  39. package/node_modules/@modelcontextprotocol/sdk/node_modules/type-is/node_modules/content-type/dist/index.d.ts +20 -0
  40. package/node_modules/@modelcontextprotocol/sdk/node_modules/type-is/node_modules/content-type/dist/index.js +20 -14
  41. package/node_modules/@modelcontextprotocol/sdk/node_modules/type-is/node_modules/content-type/dist/index.js.map +1 -1
  42. package/node_modules/@modelcontextprotocol/sdk/node_modules/type-is/node_modules/content-type/package.json +1 -1
  43. package/node_modules/@types/node/README.md +1 -1
  44. package/node_modules/@types/node/buffer.buffer.d.ts +5 -5
  45. package/node_modules/@types/node/crypto.d.ts +2 -2
  46. package/node_modules/@types/node/http.d.ts +16 -0
  47. package/node_modules/@types/node/http2.d.ts +10 -2
  48. package/node_modules/@types/node/package.json +2 -2
  49. package/node_modules/@types/node/process.d.ts +52 -0
  50. package/node_modules/@types/node/quic.d.ts +210 -28
  51. package/node_modules/@types/node/sqlite.d.ts +6 -2
  52. package/node_modules/@types/node/test.d.ts +36 -0
  53. package/node_modules/eventsource-parser/dist/index.cjs +2 -2
  54. package/node_modules/eventsource-parser/dist/index.cjs.map +1 -1
  55. package/node_modules/eventsource-parser/dist/index.js +2 -2
  56. package/node_modules/eventsource-parser/dist/index.js.map +1 -1
  57. package/node_modules/eventsource-parser/package.json +1 -1
  58. package/node_modules/eventsource-parser/src/parse.ts +4 -2
  59. package/node_modules/fast-uri/index.js +201 -48
  60. package/node_modules/fast-uri/lib/schemes.js +9 -4
  61. package/node_modules/fast-uri/lib/utils.js +388 -91
  62. package/node_modules/fast-uri/package.json +1 -1
  63. package/node_modules/fast-uri/test/component-safe-serialization.test.js +105 -0
  64. package/node_modules/fast-uri/test/equal.test.js +31 -3
  65. package/node_modules/fast-uri/test/fixtures/uri-js-parse.json +2 -0
  66. package/node_modules/fast-uri/test/ipv6-canonical.test.js +34 -0
  67. package/node_modules/fast-uri/test/ipv6-validation.test.js +90 -0
  68. package/node_modules/fast-uri/test/malformed-percent.test.js +77 -0
  69. package/node_modules/fast-uri/test/malformed-urn.test.js +61 -0
  70. package/node_modules/fast-uri/test/parse.test.js +7 -3
  71. package/node_modules/fast-uri/test/query-fragment-normalization.test.js +33 -0
  72. package/node_modules/fast-uri/test/reserved-path-normalization.test.js +109 -0
  73. package/node_modules/fast-uri/test/scheme-validation.test.js +124 -0
  74. package/node_modules/fast-uri/test/security-normalization.test.js +101 -0
  75. package/node_modules/fast-uri/test/security.test.js +75 -3
  76. package/node_modules/fast-uri/test/urn-full-input.test.js +29 -0
  77. package/node_modules/fast-uri/test/websocket-query-preservation.test.js +24 -0
  78. package/node_modules/hono/dist/cjs/client/client.js +27 -13
  79. package/node_modules/hono/dist/cjs/client/utils.js +4 -1
  80. package/node_modules/hono/dist/cjs/context.js +4 -0
  81. package/node_modules/hono/dist/cjs/helper/accepts/accepts.js +36 -2
  82. package/node_modules/hono/dist/cjs/helper/ssg/ssg.js +1 -1
  83. package/node_modules/hono/dist/cjs/helper/ssg/utils.js +30 -10
  84. package/node_modules/hono/dist/cjs/jsx/dom/render.js +2 -0
  85. package/node_modules/hono/dist/cjs/jsx/intrinsic-element/components.js +1 -1
  86. package/node_modules/hono/dist/cjs/middleware/cache/index.js +1 -1
  87. package/node_modules/hono/dist/cjs/middleware/cors/index.js +16 -13
  88. package/node_modules/hono/dist/cjs/middleware/csrf/index.js +1 -1
  89. package/node_modules/hono/dist/cjs/middleware/etag/digest.js +47 -1
  90. package/node_modules/hono/dist/cjs/middleware/etag/index.js +3 -3
  91. package/node_modules/hono/dist/cjs/middleware/pretty-json/index.js +3 -1
  92. package/node_modules/hono/dist/cjs/middleware/secure-headers/secure-headers.js +6 -3
  93. package/node_modules/hono/dist/cjs/request.js +8 -4
  94. package/node_modules/hono/dist/cjs/router/linear-router/router.js +7 -2
  95. package/node_modules/hono/dist/cjs/router/pattern-router/router.js +3 -9
  96. package/node_modules/hono/dist/cjs/router/reg-exp-router/node.js +10 -3
  97. package/node_modules/hono/dist/cjs/router/reg-exp-router/router.js +47 -83
  98. package/node_modules/hono/dist/cjs/router/reg-exp-router/trie.js +2 -1
  99. package/node_modules/hono/dist/cjs/router/trie-router/node.js +46 -69
  100. package/node_modules/hono/dist/cjs/router/trie-router/router.js +3 -11
  101. package/node_modules/hono/dist/cjs/router/utils.js +27 -0
  102. package/node_modules/hono/dist/cjs/utils/body.js +15 -3
  103. package/node_modules/hono/dist/cjs/utils/cookie.js +1 -1
  104. package/node_modules/hono/dist/cjs/utils/ipaddr.js +5 -3
  105. package/node_modules/hono/dist/cjs/utils/stream.js +7 -1
  106. package/node_modules/hono/dist/cjs/utils/url.js +11 -3
  107. package/node_modules/hono/dist/client/client.js +27 -13
  108. package/node_modules/hono/dist/client/utils.js +4 -1
  109. package/node_modules/hono/dist/context.js +4 -0
  110. package/node_modules/hono/dist/helper/accepts/accepts.js +36 -2
  111. package/node_modules/hono/dist/helper/ssg/ssg.js +1 -1
  112. package/node_modules/hono/dist/helper/ssg/utils.js +30 -10
  113. package/node_modules/hono/dist/jsx/dom/render.js +2 -0
  114. package/node_modules/hono/dist/jsx/intrinsic-element/components.js +1 -1
  115. package/node_modules/hono/dist/middleware/cache/index.js +1 -1
  116. package/node_modules/hono/dist/middleware/cors/index.js +16 -13
  117. package/node_modules/hono/dist/middleware/csrf/index.js +1 -1
  118. package/node_modules/hono/dist/middleware/etag/digest.js +47 -1
  119. package/node_modules/hono/dist/middleware/etag/index.js +3 -3
  120. package/node_modules/hono/dist/middleware/pretty-json/index.js +3 -1
  121. package/node_modules/hono/dist/middleware/secure-headers/secure-headers.js +6 -3
  122. package/node_modules/hono/dist/request.js +8 -4
  123. package/node_modules/hono/dist/router/linear-router/router.js +7 -2
  124. package/node_modules/hono/dist/router/pattern-router/router.js +3 -9
  125. package/node_modules/hono/dist/router/reg-exp-router/node.js +6 -2
  126. package/node_modules/hono/dist/router/reg-exp-router/router.js +53 -84
  127. package/node_modules/hono/dist/router/reg-exp-router/trie.js +2 -1
  128. package/node_modules/hono/dist/router/trie-router/node.js +46 -69
  129. package/node_modules/hono/dist/router/trie-router/router.js +3 -11
  130. package/node_modules/hono/dist/router/utils.js +5 -0
  131. package/node_modules/hono/dist/types/context.d.ts +4 -0
  132. package/node_modules/hono/dist/types/middleware/secure-headers/permissions-policy.d.ts +3 -3
  133. package/node_modules/hono/dist/types/router/reg-exp-router/node.d.ts +3 -0
  134. package/node_modules/hono/dist/types/router/trie-router/node.d.ts +1 -2
  135. package/node_modules/hono/dist/types/router/trie-router/router.d.ts +0 -1
  136. package/node_modules/hono/dist/types/router/utils.d.ts +1 -0
  137. package/node_modules/hono/dist/types/utils/url.d.ts +4 -0
  138. package/node_modules/hono/dist/utils/body.js +15 -3
  139. package/node_modules/hono/dist/utils/cookie.js +1 -1
  140. package/node_modules/hono/dist/utils/ipaddr.js +5 -3
  141. package/node_modules/hono/dist/utils/stream.js +7 -1
  142. package/node_modules/hono/dist/utils/url.js +11 -3
  143. package/node_modules/hono/package.json +1 -1
  144. package/node_modules/ip-address/README.md +134 -134
  145. package/node_modules/ip-address/dist/address-error.d.ts +11 -0
  146. package/node_modules/ip-address/dist/address-error.js.map +1 -1
  147. package/node_modules/ip-address/dist/ipv4.d.ts +22 -6
  148. package/node_modules/ip-address/dist/ipv4.js +22 -6
  149. package/node_modules/ip-address/dist/ipv4.js.map +1 -1
  150. package/node_modules/ip-address/dist/ipv6.d.ts +10 -3
  151. package/node_modules/ip-address/dist/ipv6.js +26 -17
  152. package/node_modules/ip-address/dist/ipv6.js.map +1 -1
  153. package/node_modules/ip-address/package.json +1 -1
  154. package/node_modules/is-plain-object/README.md +39 -95
  155. package/node_modules/is-plain-object/dist/is-plain-object.js +0 -2
  156. package/node_modules/is-plain-object/is-plain-object.d.ts +6 -1
  157. package/node_modules/is-plain-object/package.json +8 -34
  158. package/node_modules/jose/dist/types/jwe/compact/encrypt.d.ts +4 -3
  159. package/node_modules/jose/dist/types/jwe/flattened/encrypt.d.ts +4 -3
  160. package/node_modules/jose/dist/types/jwe/general/encrypt.d.ts +4 -3
  161. package/node_modules/jose/dist/types/jwks/remote.d.ts +4 -4
  162. package/node_modules/jose/dist/types/jws/general/verify.d.ts +3 -3
  163. package/node_modules/jose/dist/types/jwt/encrypt.d.ts +12 -17
  164. package/node_modules/jose/dist/types/jwt/sign.d.ts +8 -14
  165. package/node_modules/jose/dist/types/jwt/unsecured.d.ts +9 -15
  166. package/node_modules/jose/dist/types/types.d.ts +4 -2
  167. package/node_modules/jose/dist/webapi/jwe/compact/encrypt.js +30 -8
  168. package/node_modules/jose/dist/webapi/jwe/flattened/decrypt.js +8 -4
  169. package/node_modules/jose/dist/webapi/jwe/flattened/encrypt.js +6 -13
  170. package/node_modules/jose/dist/webapi/jwe/general/decrypt.js +19 -19
  171. package/node_modules/jose/dist/webapi/jwe/general/encrypt.js +32 -21
  172. package/node_modules/jose/dist/webapi/jwk/embedded.js +15 -1
  173. package/node_modules/jose/dist/webapi/jwk/thumbprint.js +11 -5
  174. package/node_modules/jose/dist/webapi/jwks/local.js +45 -53
  175. package/node_modules/jose/dist/webapi/jwks/remote.js +82 -103
  176. package/node_modules/jose/dist/webapi/jws/compact/sign.js +10 -9
  177. package/node_modules/jose/dist/webapi/jws/flattened/sign.js +2 -1
  178. package/node_modules/jose/dist/webapi/jws/flattened/verify.js +8 -7
  179. package/node_modules/jose/dist/webapi/jws/general/sign.js +5 -3
  180. package/node_modules/jose/dist/webapi/jws/general/verify.js +51 -21
  181. package/node_modules/jose/dist/webapi/jwt/decrypt.js +7 -9
  182. package/node_modules/jose/dist/webapi/jwt/encrypt.js +21 -50
  183. package/node_modules/jose/dist/webapi/jwt/sign.js +8 -41
  184. package/node_modules/jose/dist/webapi/jwt/unsecured.js +21 -42
  185. package/node_modules/jose/dist/webapi/key/generate_key_pair.js +6 -2
  186. package/node_modules/jose/dist/webapi/key/generate_secret.js +7 -6
  187. package/node_modules/jose/dist/webapi/key/import.js +16 -12
  188. package/node_modules/jose/dist/webapi/lib/asn1.js +7 -2
  189. package/node_modules/jose/dist/webapi/lib/content_encryption.js +7 -15
  190. package/node_modules/jose/dist/webapi/lib/deflate.js +8 -0
  191. package/node_modules/jose/dist/webapi/lib/helpers.js +1 -1
  192. package/node_modules/jose/dist/webapi/lib/jwe_decrypt.js +85 -28
  193. package/node_modules/jose/dist/webapi/lib/jwe_encrypt.js +45 -13
  194. package/node_modules/jose/dist/webapi/lib/jwk_metadata.js +20 -0
  195. package/node_modules/jose/dist/webapi/lib/jws_sign.js +47 -36
  196. package/node_modules/jose/dist/webapi/lib/jws_verify.js +77 -45
  197. package/node_modules/jose/dist/webapi/lib/jwt_claims_set.js +82 -47
  198. package/node_modules/jose/dist/webapi/lib/key.js +24 -9
  199. package/node_modules/jose/dist/webapi/lib/key_management.js +10 -4
  200. package/node_modules/jose/dist/webapi/lib/key_options.js +6 -0
  201. package/node_modules/jose/dist/webapi/lib/options.js +26 -0
  202. package/node_modules/jose/dist/webapi/lib/type_checks.js +11 -13
  203. package/node_modules/jose/package.json +1 -1
  204. package/package.json +2 -2
  205. package/dist/ui/assets/index-DdJeSfV9.js +0 -110
  206. package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/HISTORY.md +0 -114
@@ -216,8 +216,7 @@
216
216
  "bounds": {
217
217
  "recipient_max": "0",
218
218
  "send_daily_max": "0",
219
- "read_max_age_days": "30",
220
- "read_daily_max": "100"
219
+ "read_max_age_days": "30"
221
220
  },
222
221
  "context": {
223
222
  "allowed_recipients": "",
@@ -227,8 +226,7 @@
227
226
  "ttl": 2592000,
228
227
  "tags": [
229
228
  "read-only",
230
- "30 days",
231
- "100 reads/day"
229
+ "30 days"
232
230
  ]
233
231
  },
234
232
  {
@@ -239,8 +237,7 @@
239
237
  "bounds": {
240
238
  "recipient_max": "5",
241
239
  "send_daily_max": "20",
242
- "read_max_age_days": "90",
243
- "read_daily_max": "200"
240
+ "read_max_age_days": "90"
244
241
  },
245
242
  "context": {
246
243
  "allowed_recipients": "",
@@ -419,6 +419,13 @@ async function getEnrichedAuthorizations() {
419
419
  if (!res.ok) throw new Error("Failed to fetch authorizations");
420
420
  return res.json();
421
421
  }
422
+ async function getLocalEvidence() {
423
+ const res = await fetch(`${MCP_BASE}/internal/evidence`, {
424
+ headers: internalHeaders()
425
+ });
426
+ if (!res.ok) throw new Error(`Failed to fetch local evidence: ${res.status}`);
427
+ return res.json();
428
+ }
422
429
  async function getMcpHealth() {
423
430
  const res = await fetch(`${MCP_BASE}/health`);
424
431
  if (!res.ok) throw new Error("MCP server unreachable");
@@ -865,6 +872,7 @@ function createVaultRouter(vault2) {
865
872
  import { Router as Router3 } from "express";
866
873
 
867
874
  // src/lib/ai-client.ts
875
+ var CHAT_TOKEN_BUDGET = 4e3;
868
876
  var PROVIDER_PRESETS = {
869
877
  ollama: {
870
878
  provider: "ollama",
@@ -892,6 +900,30 @@ var PROVIDER_PRESETS = {
892
900
  model: "meta-llama/Llama-3-8b-chat-hf"
893
901
  }
894
902
  };
903
+ function explainEmptyReply(choice, usage, budget) {
904
+ const reasoningTokens = usage?.completion_tokens_details?.reasoning_tokens ?? 0;
905
+ const finish = choice?.finish_reason;
906
+ const hadReasoning = reasoningTokens > 0 || Boolean(choice?.message?.reasoning_content || choice?.message?.reasoning);
907
+ if (finish === "length" && hadReasoning) {
908
+ return `The model is a reasoning model and spent its whole ${budget}-token budget` + (reasoningTokens ? ` (${reasoningTokens} tokens)` : "") + " thinking, leaving nothing for the answer. Raise the token limit or choose a non-reasoning model in Settings.";
909
+ }
910
+ if (finish === "length") {
911
+ return `The model hit the ${budget}-token limit before writing any answer.`;
912
+ }
913
+ if (hadReasoning) {
914
+ return "The model returned only internal reasoning and no answer \u2014 it may not be compatible with this endpoint. Try a different model in Settings.";
915
+ }
916
+ if (finish === "content_filter") {
917
+ return "The provider's content filter blocked the response.";
918
+ }
919
+ if (choice?.message?.tool_calls?.length) {
920
+ return "The model tried to call a tool instead of answering. Choose a model without forced tool use.";
921
+ }
922
+ if (!choice) {
923
+ return "The provider returned no choices. Check the model name in Settings.";
924
+ }
925
+ return `The model returned an empty answer${finish ? ` (finish_reason: ${finish})` : ""}.`;
926
+ }
895
927
  var SYSTEM_PROMPTS = {
896
928
  intent: `You are a reviewing assistant helping a human articulate their intent for an AI agent authorization.
897
929
 
@@ -1062,13 +1094,20 @@ ${request.currentText}
1062
1094
  model: config.model,
1063
1095
  messages,
1064
1096
  stream: false,
1065
- options: { temperature: 0.3, num_predict: 600 }
1097
+ options: { temperature: 0.3, num_predict: CHAT_TOKEN_BUDGET }
1066
1098
  }),
1067
1099
  signal: AbortSignal.timeout(6e4)
1068
1100
  });
1069
1101
  if (!response.ok) throw new Error(`Ollama: ${response.status}`);
1070
1102
  const data = await response.json();
1071
- reply = data.message?.content?.trim() || "No response generated.";
1103
+ const content = data.message?.content?.trim();
1104
+ if (!content) {
1105
+ return {
1106
+ success: false,
1107
+ error: data.message?.thinking ? "The model returned only internal reasoning and no answer. Try a non-thinking model." : `The model returned an empty answer. Check that "${config.model}" is pulled and running in Ollama.`
1108
+ };
1109
+ }
1110
+ reply = content;
1072
1111
  } else {
1073
1112
  const headers = { "Content-Type": "application/json" };
1074
1113
  if (config.apiKey) headers["Authorization"] = `Bearer ${config.apiKey}`;
@@ -1079,7 +1118,10 @@ ${request.currentText}
1079
1118
  model: config.model,
1080
1119
  messages,
1081
1120
  temperature: 0.3,
1082
- max_tokens: 600
1121
+ // Reasoning models charge their thinking against this same budget,
1122
+ // so a small number here produces an empty answer rather than a
1123
+ // short one. 4000 leaves room to think AND reply.
1124
+ max_tokens: CHAT_TOKEN_BUDGET
1083
1125
  }),
1084
1126
  signal: AbortSignal.timeout(6e4)
1085
1127
  });
@@ -1088,7 +1130,14 @@ ${request.currentText}
1088
1130
  throw new Error(`AI provider: ${response.status} - ${errorText}`);
1089
1131
  }
1090
1132
  const data = await response.json();
1091
- reply = data.choices?.[0]?.message?.content?.trim() || "No response generated.";
1133
+ const content = data.choices?.[0]?.message?.content?.trim();
1134
+ if (!content) {
1135
+ return {
1136
+ success: false,
1137
+ error: explainEmptyReply(data.choices?.[0], data.usage, CHAT_TOKEN_BUDGET)
1138
+ };
1139
+ }
1140
+ reply = content;
1092
1141
  }
1093
1142
  return { success: true, reply };
1094
1143
  } catch (error) {
@@ -1893,49 +1942,11 @@ function createEncryptIntentRouter() {
1893
1942
  return router;
1894
1943
  }
1895
1944
 
1896
- // src/routes/decrypt-intent.ts
1897
- import { Router as Router8 } from "express";
1898
- function createDecryptIntentRouter(vault2) {
1899
- const router = Router8();
1900
- router.post("/", async (req, res) => {
1901
- const { intentCiphertext, encryptedKey, approverId } = req.body;
1902
- if (typeof intentCiphertext !== "string" || intentCiphertext.length === 0) {
1903
- res.status(400).json({ error: "Missing or invalid field: intentCiphertext (base64 string)" });
1904
- return;
1905
- }
1906
- if (typeof encryptedKey !== "object" || encryptedKey === null || typeof encryptedKey.ct !== "string" || typeof encryptedKey.enc !== "string") {
1907
- res.status(400).json({ error: "Missing or invalid field: encryptedKey ({ ct: string; enc: string })" });
1908
- return;
1909
- }
1910
- if (typeof approverId !== "string" || approverId.length === 0) {
1911
- res.status(400).json({ error: "Missing or invalid field: approverId (string)" });
1912
- return;
1913
- }
1914
- try {
1915
- const kp = await loadOrGenerateKeyPair(vault2);
1916
- const { ct, enc } = encryptedKey;
1917
- const encrypted = {
1918
- intentCiphertext: new Uint8Array(Buffer.from(intentCiphertext, "base64")),
1919
- encryptedKeys: {
1920
- [approverId]: {
1921
- ct: new Uint8Array(Buffer.from(ct, "base64")),
1922
- enc: new Uint8Array(Buffer.from(enc, "base64"))
1923
- }
1924
- }
1925
- };
1926
- const intent = await decryptIntent(encrypted, approverId, kp.privateKey);
1927
- res.json({ intent });
1928
- } catch (err) {
1929
- console.error("[Control Plane] decrypt-intent error:", err);
1930
- const msg = err instanceof Error ? err.message : "Decryption failed";
1931
- res.status(500).json({ error: msg });
1932
- }
1933
- });
1934
- return router;
1935
- }
1945
+ // src/routes/evidence-export.ts
1946
+ import { Router as Router9 } from "express";
1936
1947
 
1937
1948
  // src/routes/approved-intents.ts
1938
- import { Router as Router9 } from "express";
1949
+ import { Router as Router8 } from "express";
1939
1950
  import { readFileSync as readFileSync2, writeFileSync as writeFileSync3, existsSync as existsSync4, mkdirSync as mkdirSync3 } from "fs";
1940
1951
  import { homedir as homedir3 } from "os";
1941
1952
  import { join as join3 } from "path";
@@ -1955,19 +1966,22 @@ function writeFile(data) {
1955
1966
  mkdirSync3(SUVEREN_DATA_DIR, { recursive: true });
1956
1967
  writeFileSync3(FILE_PATH, JSON.stringify(data, null, 2), "utf-8");
1957
1968
  }
1969
+ function readApprovedIntents(vault2) {
1970
+ const file = readFile();
1971
+ const result = {};
1972
+ for (const [authorityId, blob] of Object.entries(file.entries)) {
1973
+ try {
1974
+ result[authorityId] = vault2.decrypt(blob);
1975
+ } catch {
1976
+ }
1977
+ }
1978
+ return result;
1979
+ }
1958
1980
  function createApprovedIntentsRouter(vault2) {
1959
- const router = Router9();
1981
+ const router = Router8();
1960
1982
  router.get("/", (req, res) => {
1961
1983
  try {
1962
- const file = readFile();
1963
- const result = {};
1964
- for (const [authorityId, blob] of Object.entries(file.entries)) {
1965
- try {
1966
- result[authorityId] = vault2.decrypt(blob);
1967
- } catch {
1968
- }
1969
- }
1970
- res.json({ intents: result });
1984
+ res.json({ intents: readApprovedIntents(vault2) });
1971
1985
  } catch (err) {
1972
1986
  console.error("[Control Plane] approved-intents GET error:", err);
1973
1987
  res.status(500).json({ error: "Failed to read approved intents" });
@@ -1997,6 +2011,222 @@ function createApprovedIntentsRouter(vault2) {
1997
2011
  return router;
1998
2012
  }
1999
2013
 
2014
+ // src/routes/evidence-export.ts
2015
+ var AS_FETCH_TIMEOUT_MS = 2e4;
2016
+ function createEvidenceExportRouter(spUrl, vault2) {
2017
+ const router = Router9();
2018
+ const getSpCookie = () => vault2.getSpCookie();
2019
+ router.get("/", async (_req, res) => {
2020
+ let local = null;
2021
+ let localError;
2022
+ try {
2023
+ local = await getLocalEvidence();
2024
+ } catch (err) {
2025
+ localError = err instanceof Error ? err.message : String(err);
2026
+ }
2027
+ let authorityServer = null;
2028
+ let asError;
2029
+ const cookie = getSpCookie();
2030
+ if (!cookie) {
2031
+ asError = "No Authority Server session \u2014 sign in to include the full account history.";
2032
+ } else {
2033
+ const controller = new AbortController();
2034
+ const timeout = setTimeout(() => controller.abort(), AS_FETCH_TIMEOUT_MS);
2035
+ try {
2036
+ const asRes = await fetch(`${spUrl}/api/receipts/export`, {
2037
+ headers: { Cookie: cookie },
2038
+ signal: controller.signal
2039
+ });
2040
+ if (asRes.ok) {
2041
+ authorityServer = await asRes.json();
2042
+ } else {
2043
+ asError = `Authority Server export failed: ${asRes.status}`;
2044
+ }
2045
+ } catch (err) {
2046
+ asError = `Authority Server unreachable: ${err instanceof Error ? err.message : String(err)}`;
2047
+ } finally {
2048
+ clearTimeout(timeout);
2049
+ }
2050
+ }
2051
+ if (!local && !authorityServer) {
2052
+ res.status(502).json({
2053
+ error: "No evidence source available.",
2054
+ localError,
2055
+ asError
2056
+ });
2057
+ return;
2058
+ }
2059
+ let approvedIntents = {};
2060
+ try {
2061
+ approvedIntents = readApprovedIntents(vault2);
2062
+ } catch {
2063
+ }
2064
+ const exportedAt = Math.floor(Date.now() / 1e3);
2065
+ const date = new Date(exportedAt * 1e3).toISOString().slice(0, 10);
2066
+ res.setHeader("Content-Disposition", `attachment; filename="suveren-evidence-${date}.json"`).setHeader("Cache-Control", "no-store").json({
2067
+ format: "suveren-evidence-bundle/1",
2068
+ exportedAt,
2069
+ sources: {
2070
+ local: local !== null,
2071
+ authorityServer: authorityServer !== null,
2072
+ ...localError ? { localError } : {},
2073
+ ...asError ? { asError } : {}
2074
+ },
2075
+ local,
2076
+ approvedIntents,
2077
+ authorityServer
2078
+ });
2079
+ });
2080
+ return router;
2081
+ }
2082
+
2083
+ // src/routes/evidence.ts
2084
+ import { Router as Router10 } from "express";
2085
+ import { verifyReceiptSignature } from "@hap/core";
2086
+ async function verifyAll(receipts) {
2087
+ const out = {};
2088
+ for (const raw of receipts) {
2089
+ const e = raw;
2090
+ const id = typeof e.receipt?.id === "string" ? e.receipt.id : void 0;
2091
+ if (!id) continue;
2092
+ if (!e.asPublicKey) {
2093
+ out[id] = "unverifiable";
2094
+ continue;
2095
+ }
2096
+ try {
2097
+ await verifyReceiptSignature(e.receipt, e.asPublicKey);
2098
+ out[id] = "valid";
2099
+ } catch {
2100
+ out[id] = "invalid";
2101
+ }
2102
+ }
2103
+ return out;
2104
+ }
2105
+ function mergeAuthorizations(evidence) {
2106
+ const out = {};
2107
+ for (const raw of evidence.authorizations ?? []) {
2108
+ const a = raw;
2109
+ const id = a.authorizationId;
2110
+ if (!id) continue;
2111
+ out[id] = {
2112
+ authorizationId: id,
2113
+ profileId: a.profileId,
2114
+ boundsHash: a.boundsHash,
2115
+ contextHash: a.contextHash,
2116
+ bounds: a.bounds,
2117
+ context: a.context,
2118
+ intent: a.intent,
2119
+ archived: true
2120
+ };
2121
+ }
2122
+ for (const raw of evidence.gates ?? []) {
2123
+ const g = raw;
2124
+ const id = g.authorizationId;
2125
+ if (!id) continue;
2126
+ const gateContent = g.gateContent ?? {};
2127
+ const existing = out[id];
2128
+ if (!existing) {
2129
+ out[id] = {
2130
+ authorizationId: id,
2131
+ profileId: g.profileId,
2132
+ boundsHash: g.boundsHash,
2133
+ contextHash: g.contextHash,
2134
+ context: g.context,
2135
+ intent: gateContent.intent,
2136
+ archived: false
2137
+ };
2138
+ continue;
2139
+ }
2140
+ existing.intent ??= gateContent.intent;
2141
+ existing.context ??= g.context;
2142
+ existing.contextHash ??= g.contextHash;
2143
+ }
2144
+ return out;
2145
+ }
2146
+ function createEvidenceRouter() {
2147
+ const router = Router10();
2148
+ router.get("/authorizations", async (_req, res) => {
2149
+ try {
2150
+ const evidence = await getLocalEvidence();
2151
+ res.json({
2152
+ authorizations: mergeAuthorizations(evidence),
2153
+ // Verified here, on this machine, so the card's badge reports a check
2154
+ // that actually ran rather than the issuer vouching for itself.
2155
+ signatures: await verifyAll(evidence.receipts ?? [])
2156
+ });
2157
+ } catch (err) {
2158
+ res.status(503).json({
2159
+ error: "Local evidence unavailable",
2160
+ detail: err instanceof Error ? err.message : String(err)
2161
+ });
2162
+ }
2163
+ });
2164
+ router.get("/receipt/:id", async (req, res) => {
2165
+ const id = String(req.params.id);
2166
+ try {
2167
+ const evidence = await getLocalEvidence();
2168
+ const entry = (evidence.receipts ?? []).find(
2169
+ (r) => r.receipt?.id === id
2170
+ );
2171
+ if (!entry) {
2172
+ res.status(404).json({ error: "No local copy of this receipt" });
2173
+ return;
2174
+ }
2175
+ const authorizationId = entry.authorizationId;
2176
+ const authorization = authorizationId ? mergeAuthorizations(evidence)[authorizationId] ?? null : null;
2177
+ const signature = (await verifyAll([entry]))[id] ?? "unverifiable";
2178
+ res.json({ entry, authorization, signature });
2179
+ } catch (err) {
2180
+ res.status(503).json({
2181
+ error: "Local evidence unavailable",
2182
+ detail: err instanceof Error ? err.message : String(err)
2183
+ });
2184
+ }
2185
+ });
2186
+ return router;
2187
+ }
2188
+
2189
+ // src/routes/decrypt-intent.ts
2190
+ import { Router as Router11 } from "express";
2191
+ function createDecryptIntentRouter(vault2) {
2192
+ const router = Router11();
2193
+ router.post("/", async (req, res) => {
2194
+ const { intentCiphertext, encryptedKey, approverId } = req.body;
2195
+ if (typeof intentCiphertext !== "string" || intentCiphertext.length === 0) {
2196
+ res.status(400).json({ error: "Missing or invalid field: intentCiphertext (base64 string)" });
2197
+ return;
2198
+ }
2199
+ if (typeof encryptedKey !== "object" || encryptedKey === null || typeof encryptedKey.ct !== "string" || typeof encryptedKey.enc !== "string") {
2200
+ res.status(400).json({ error: "Missing or invalid field: encryptedKey ({ ct: string; enc: string })" });
2201
+ return;
2202
+ }
2203
+ if (typeof approverId !== "string" || approverId.length === 0) {
2204
+ res.status(400).json({ error: "Missing or invalid field: approverId (string)" });
2205
+ return;
2206
+ }
2207
+ try {
2208
+ const kp = await loadOrGenerateKeyPair(vault2);
2209
+ const { ct, enc } = encryptedKey;
2210
+ const encrypted = {
2211
+ intentCiphertext: new Uint8Array(Buffer.from(intentCiphertext, "base64")),
2212
+ encryptedKeys: {
2213
+ [approverId]: {
2214
+ ct: new Uint8Array(Buffer.from(ct, "base64")),
2215
+ enc: new Uint8Array(Buffer.from(enc, "base64"))
2216
+ }
2217
+ }
2218
+ };
2219
+ const intent = await decryptIntent(encrypted, approverId, kp.privateKey);
2220
+ res.json({ intent });
2221
+ } catch (err) {
2222
+ console.error("[Control Plane] decrypt-intent error:", err);
2223
+ const msg = err instanceof Error ? err.message : "Decryption failed";
2224
+ res.status(500).json({ error: msg });
2225
+ }
2226
+ });
2227
+ return router;
2228
+ }
2229
+
2000
2230
  // src/lib/update-checker.ts
2001
2231
  import { execSync } from "child_process";
2002
2232
  import { dirname as dirname3 } from "path";
@@ -2133,7 +2363,7 @@ data: null
2133
2363
  }
2134
2364
 
2135
2365
  // src/routes/gateway-settings.ts
2136
- import { Router as Router10 } from "express";
2366
+ import { Router as Router12 } from "express";
2137
2367
 
2138
2368
  // src/lib/gateway-settings.ts
2139
2369
  import { readFileSync as readFileSync3, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4 } from "fs";
@@ -2169,7 +2399,7 @@ function writeSettings(patch, dataDir) {
2169
2399
 
2170
2400
  // src/routes/gateway-settings.ts
2171
2401
  function createGatewaySettingsRouter() {
2172
- const router = Router10();
2402
+ const router = Router12();
2173
2403
  router.get("/", (_req, res) => {
2174
2404
  res.json(readSettings());
2175
2405
  });
@@ -2192,7 +2422,7 @@ function createGatewaySettingsRouter() {
2192
2422
  }
2193
2423
 
2194
2424
  // src/routes/internal-events.ts
2195
- import { Router as Router11 } from "express";
2425
+ import { Router as Router13 } from "express";
2196
2426
  import { timingSafeEqual as timingSafeEqual2 } from "crypto";
2197
2427
  var ALLOWED = ["proposal-added", "action-approval-needed"];
2198
2428
  function secretMatches(provided, expected) {
@@ -2204,7 +2434,7 @@ function secretMatches(provided, expected) {
2204
2434
  return timingSafeEqual2(a, b);
2205
2435
  }
2206
2436
  function createInternalEventsRouter(getSecret) {
2207
- const router = Router11();
2437
+ const router = Router13();
2208
2438
  router.post("/event", (req, res) => {
2209
2439
  if (!secretMatches(req.headers["x-internal-secret"], getSecret())) {
2210
2440
  res.status(401).json({ error: "Unauthorized" });
@@ -2586,6 +2816,8 @@ app.use("/mcp", jsonParser, authGuard, createMCPRouter());
2586
2816
  app.use("/autostart", jsonParser, authGuard, createAutostartRouter());
2587
2817
  app.use("/api/gateway-settings", jsonParser, authGuard, createGatewaySettingsRouter());
2588
2818
  app.use("/api/encrypt-intent", jsonParser, authGuard, createEncryptIntentRouter());
2819
+ app.use("/api/evidence-export", authGuard, createEvidenceExportRouter(SP_URL2, vault));
2820
+ app.use("/api/evidence", authGuard, createEvidenceRouter());
2589
2821
  app.use("/api/decrypt-intent", jsonParser, authGuard, createDecryptIntentRouter(vault));
2590
2822
  app.use("/api/approved-intents", jsonParser, authGuard, createApprovedIntentsRouter(vault));
2591
2823
  app.put("/integrations/:id/read-policy", jsonParser, authGuard, async (req, res) => {
@@ -2808,10 +3040,10 @@ app.use(
2808
3040
  const url = req.url ?? "";
2809
3041
  if (method !== "POST" && method !== "DELETE") return;
2810
3042
  const path = url.split("?")[0];
2811
- if (method === "POST" && path === "/api/as/attest") {
2812
- eventBus.emit("attestation-changed");
2813
- } else if (method === "POST" && /^\/api\/attestations\/[^/]+\/revoke$/.test(path)) {
3043
+ if (method === "POST" && path === "/api/as/attest" || method === "POST" && /^\/api\/attestations\/[^/]+\/revoke$/.test(path) || method === "POST" && /^\/api\/authorizations\/[^/]+\/delete$/.test(path)) {
2814
3044
  eventBus.emit("attestation-changed");
3045
+ void resyncGates().catch(() => {
3046
+ });
2815
3047
  } else if (method === "POST" && path === "/api/proposals") {
2816
3048
  eventBus.emit("proposal-added");
2817
3049
  } else if (method === "POST" && /^\/api\/proposals\/[^/]+\/resolve$/.test(path)) {