@suveren/gateway 0.7.4 → 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 (171) hide show
  1. package/dist/control-plane/index.mjs +248 -58
  2. package/dist/mcp-server/http.mjs +408 -65
  3. package/dist/ui/assets/{index-CkWaRck1.css → index-BYVb_0oG.css} +1 -1
  4. package/dist/ui/assets/index-CsrM6RQr.js +110 -0
  5. package/dist/ui/index.html +2 -2
  6. package/node_modules/@hap/core/dist/index.d.mts +258 -9
  7. package/node_modules/@hap/core/dist/index.d.ts +258 -9
  8. package/node_modules/@hap/core/dist/index.js +227 -5
  9. package/node_modules/@hap/core/dist/index.mjs +213 -4
  10. package/node_modules/@hap/core/package.json +3 -2
  11. package/node_modules/@hap/core/src/did-key.ts +126 -0
  12. package/node_modules/@hap/core/src/identity.ts +21 -5
  13. package/node_modules/@hap/core/src/index.ts +3 -0
  14. package/node_modules/@hap/core/src/mandate.ts +207 -0
  15. package/node_modules/@hap/core/src/receipt.ts +74 -0
  16. package/node_modules/@hap/core/src/types.ts +81 -5
  17. package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/lib/accept.js +54 -0
  18. package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/lib/charset.js +10 -34
  19. package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/lib/encoding.js +9 -30
  20. package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/lib/language.js +15 -36
  21. package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/lib/mediaType.js +16 -134
  22. package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/node_modules/content-type/LICENSE +22 -0
  23. package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/node_modules/content-type/README.md +71 -0
  24. package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/node_modules/content-type/dist/index.d.ts +46 -0
  25. package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/node_modules/content-type/dist/index.js +176 -0
  26. package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/node_modules/content-type/dist/index.js.map +1 -0
  27. package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/node_modules/content-type/package.json +52 -0
  28. package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/package.json +16 -10
  29. package/node_modules/@types/node/README.md +1 -1
  30. package/node_modules/@types/node/buffer.buffer.d.ts +5 -5
  31. package/node_modules/@types/node/crypto.d.ts +2 -2
  32. package/node_modules/@types/node/http.d.ts +16 -0
  33. package/node_modules/@types/node/http2.d.ts +10 -2
  34. package/node_modules/@types/node/package.json +2 -2
  35. package/node_modules/@types/node/process.d.ts +52 -0
  36. package/node_modules/@types/node/quic.d.ts +210 -28
  37. package/node_modules/@types/node/sqlite.d.ts +6 -2
  38. package/node_modules/@types/node/test.d.ts +36 -0
  39. package/node_modules/fast-uri/index.js +201 -48
  40. package/node_modules/fast-uri/lib/schemes.js +9 -4
  41. package/node_modules/fast-uri/lib/utils.js +388 -91
  42. package/node_modules/fast-uri/package.json +1 -1
  43. package/node_modules/fast-uri/test/component-safe-serialization.test.js +105 -0
  44. package/node_modules/fast-uri/test/equal.test.js +31 -3
  45. package/node_modules/fast-uri/test/fixtures/uri-js-parse.json +2 -0
  46. package/node_modules/fast-uri/test/ipv6-canonical.test.js +34 -0
  47. package/node_modules/fast-uri/test/ipv6-validation.test.js +90 -0
  48. package/node_modules/fast-uri/test/malformed-percent.test.js +77 -0
  49. package/node_modules/fast-uri/test/malformed-urn.test.js +61 -0
  50. package/node_modules/fast-uri/test/parse.test.js +7 -3
  51. package/node_modules/fast-uri/test/query-fragment-normalization.test.js +33 -0
  52. package/node_modules/fast-uri/test/reserved-path-normalization.test.js +109 -0
  53. package/node_modules/fast-uri/test/scheme-validation.test.js +124 -0
  54. package/node_modules/fast-uri/test/security-normalization.test.js +101 -0
  55. package/node_modules/fast-uri/test/security.test.js +75 -3
  56. package/node_modules/fast-uri/test/urn-full-input.test.js +29 -0
  57. package/node_modules/fast-uri/test/websocket-query-preservation.test.js +24 -0
  58. package/node_modules/hono/dist/cjs/client/client.js +25 -11
  59. package/node_modules/hono/dist/cjs/client/utils.js +4 -1
  60. package/node_modules/hono/dist/cjs/context.js +4 -0
  61. package/node_modules/hono/dist/cjs/helper/accepts/accepts.js +36 -2
  62. package/node_modules/hono/dist/cjs/helper/ssg/ssg.js +1 -1
  63. package/node_modules/hono/dist/cjs/helper/ssg/utils.js +30 -10
  64. package/node_modules/hono/dist/cjs/jsx/dom/render.js +2 -0
  65. package/node_modules/hono/dist/cjs/middleware/cache/index.js +1 -1
  66. package/node_modules/hono/dist/cjs/middleware/cors/index.js +1 -1
  67. package/node_modules/hono/dist/cjs/middleware/csrf/index.js +1 -1
  68. package/node_modules/hono/dist/cjs/middleware/etag/digest.js +1 -1
  69. package/node_modules/hono/dist/cjs/middleware/etag/index.js +3 -3
  70. package/node_modules/hono/dist/cjs/middleware/pretty-json/index.js +3 -1
  71. package/node_modules/hono/dist/cjs/request.js +8 -4
  72. package/node_modules/hono/dist/cjs/router/linear-router/router.js +7 -2
  73. package/node_modules/hono/dist/cjs/router/pattern-router/router.js +3 -9
  74. package/node_modules/hono/dist/cjs/router/reg-exp-router/node.js +10 -3
  75. package/node_modules/hono/dist/cjs/router/reg-exp-router/router.js +47 -83
  76. package/node_modules/hono/dist/cjs/router/reg-exp-router/trie.js +2 -1
  77. package/node_modules/hono/dist/cjs/router/trie-router/node.js +46 -69
  78. package/node_modules/hono/dist/cjs/router/trie-router/router.js +3 -11
  79. package/node_modules/hono/dist/cjs/router/utils.js +27 -0
  80. package/node_modules/hono/dist/cjs/utils/body.js +15 -3
  81. package/node_modules/hono/dist/cjs/utils/cookie.js +1 -1
  82. package/node_modules/hono/dist/cjs/utils/ipaddr.js +5 -3
  83. package/node_modules/hono/dist/cjs/utils/stream.js +7 -1
  84. package/node_modules/hono/dist/cjs/utils/url.js +9 -1
  85. package/node_modules/hono/dist/client/client.js +25 -11
  86. package/node_modules/hono/dist/client/utils.js +4 -1
  87. package/node_modules/hono/dist/context.js +4 -0
  88. package/node_modules/hono/dist/helper/accepts/accepts.js +36 -2
  89. package/node_modules/hono/dist/helper/ssg/ssg.js +1 -1
  90. package/node_modules/hono/dist/helper/ssg/utils.js +30 -10
  91. package/node_modules/hono/dist/jsx/dom/render.js +2 -0
  92. package/node_modules/hono/dist/middleware/cache/index.js +1 -1
  93. package/node_modules/hono/dist/middleware/cors/index.js +1 -1
  94. package/node_modules/hono/dist/middleware/csrf/index.js +1 -1
  95. package/node_modules/hono/dist/middleware/etag/digest.js +1 -1
  96. package/node_modules/hono/dist/middleware/etag/index.js +3 -3
  97. package/node_modules/hono/dist/middleware/pretty-json/index.js +3 -1
  98. package/node_modules/hono/dist/request.js +8 -4
  99. package/node_modules/hono/dist/router/linear-router/router.js +7 -2
  100. package/node_modules/hono/dist/router/pattern-router/router.js +3 -9
  101. package/node_modules/hono/dist/router/reg-exp-router/node.js +6 -2
  102. package/node_modules/hono/dist/router/reg-exp-router/router.js +53 -84
  103. package/node_modules/hono/dist/router/reg-exp-router/trie.js +2 -1
  104. package/node_modules/hono/dist/router/trie-router/node.js +46 -69
  105. package/node_modules/hono/dist/router/trie-router/router.js +3 -11
  106. package/node_modules/hono/dist/router/utils.js +5 -0
  107. package/node_modules/hono/dist/types/context.d.ts +4 -0
  108. package/node_modules/hono/dist/types/router/reg-exp-router/node.d.ts +3 -0
  109. package/node_modules/hono/dist/types/router/trie-router/node.d.ts +1 -2
  110. package/node_modules/hono/dist/types/router/trie-router/router.d.ts +0 -1
  111. package/node_modules/hono/dist/types/router/utils.d.ts +1 -0
  112. package/node_modules/hono/dist/types/utils/url.d.ts +4 -0
  113. package/node_modules/hono/dist/utils/body.js +15 -3
  114. package/node_modules/hono/dist/utils/cookie.js +1 -1
  115. package/node_modules/hono/dist/utils/ipaddr.js +5 -3
  116. package/node_modules/hono/dist/utils/stream.js +7 -1
  117. package/node_modules/hono/dist/utils/url.js +9 -1
  118. package/node_modules/hono/package.json +1 -1
  119. package/node_modules/is-plain-object/README.md +39 -95
  120. package/node_modules/is-plain-object/dist/is-plain-object.js +0 -2
  121. package/node_modules/is-plain-object/is-plain-object.d.ts +6 -1
  122. package/node_modules/is-plain-object/package.json +8 -34
  123. package/node_modules/jose/dist/types/jwe/compact/encrypt.d.ts +4 -3
  124. package/node_modules/jose/dist/types/jwe/flattened/encrypt.d.ts +4 -3
  125. package/node_modules/jose/dist/types/jwe/general/encrypt.d.ts +4 -3
  126. package/node_modules/jose/dist/types/jwks/remote.d.ts +4 -4
  127. package/node_modules/jose/dist/types/jws/general/verify.d.ts +3 -3
  128. package/node_modules/jose/dist/types/jwt/encrypt.d.ts +12 -17
  129. package/node_modules/jose/dist/types/jwt/sign.d.ts +8 -14
  130. package/node_modules/jose/dist/types/jwt/unsecured.d.ts +9 -15
  131. package/node_modules/jose/dist/types/types.d.ts +4 -2
  132. package/node_modules/jose/dist/webapi/jwe/compact/encrypt.js +30 -8
  133. package/node_modules/jose/dist/webapi/jwe/flattened/decrypt.js +8 -4
  134. package/node_modules/jose/dist/webapi/jwe/flattened/encrypt.js +6 -13
  135. package/node_modules/jose/dist/webapi/jwe/general/decrypt.js +19 -19
  136. package/node_modules/jose/dist/webapi/jwe/general/encrypt.js +32 -21
  137. package/node_modules/jose/dist/webapi/jwk/embedded.js +15 -1
  138. package/node_modules/jose/dist/webapi/jwk/thumbprint.js +11 -5
  139. package/node_modules/jose/dist/webapi/jwks/local.js +45 -53
  140. package/node_modules/jose/dist/webapi/jwks/remote.js +82 -103
  141. package/node_modules/jose/dist/webapi/jws/compact/sign.js +10 -9
  142. package/node_modules/jose/dist/webapi/jws/flattened/sign.js +2 -1
  143. package/node_modules/jose/dist/webapi/jws/flattened/verify.js +8 -7
  144. package/node_modules/jose/dist/webapi/jws/general/sign.js +5 -3
  145. package/node_modules/jose/dist/webapi/jws/general/verify.js +51 -21
  146. package/node_modules/jose/dist/webapi/jwt/decrypt.js +7 -9
  147. package/node_modules/jose/dist/webapi/jwt/encrypt.js +21 -50
  148. package/node_modules/jose/dist/webapi/jwt/sign.js +8 -41
  149. package/node_modules/jose/dist/webapi/jwt/unsecured.js +21 -42
  150. package/node_modules/jose/dist/webapi/key/generate_key_pair.js +6 -2
  151. package/node_modules/jose/dist/webapi/key/generate_secret.js +7 -6
  152. package/node_modules/jose/dist/webapi/key/import.js +16 -12
  153. package/node_modules/jose/dist/webapi/lib/asn1.js +7 -2
  154. package/node_modules/jose/dist/webapi/lib/content_encryption.js +7 -15
  155. package/node_modules/jose/dist/webapi/lib/deflate.js +8 -0
  156. package/node_modules/jose/dist/webapi/lib/helpers.js +1 -1
  157. package/node_modules/jose/dist/webapi/lib/jwe_decrypt.js +85 -28
  158. package/node_modules/jose/dist/webapi/lib/jwe_encrypt.js +45 -13
  159. package/node_modules/jose/dist/webapi/lib/jwk_metadata.js +20 -0
  160. package/node_modules/jose/dist/webapi/lib/jws_sign.js +47 -36
  161. package/node_modules/jose/dist/webapi/lib/jws_verify.js +77 -45
  162. package/node_modules/jose/dist/webapi/lib/jwt_claims_set.js +82 -47
  163. package/node_modules/jose/dist/webapi/lib/key.js +24 -9
  164. package/node_modules/jose/dist/webapi/lib/key_management.js +10 -4
  165. package/node_modules/jose/dist/webapi/lib/key_options.js +6 -0
  166. package/node_modules/jose/dist/webapi/lib/options.js +26 -0
  167. package/node_modules/jose/dist/webapi/lib/type_checks.js +11 -13
  168. package/node_modules/jose/package.json +1 -1
  169. package/package.json +2 -2
  170. package/dist/ui/assets/index-0Q2eNytX.js +0 -110
  171. package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/HISTORY.md +0 -114
@@ -55,6 +55,10 @@ var SPClient = class {
55
55
  sessionCookie = "";
56
56
  apiKey = "";
57
57
  receiptRetry;
58
+ /** The AS base URL this client talks to (for provenance records). */
59
+ get url() {
60
+ return this.baseUrl;
61
+ }
58
62
  setApiKey(key) {
59
63
  this.apiKey = key;
60
64
  }
@@ -825,6 +829,229 @@ var DenialLog = class {
825
829
  }
826
830
  };
827
831
 
832
+ // src/lib/receipt-archive.ts
833
+ import { mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync4, existsSync as existsSync4, unlinkSync as unlinkSync4 } from "fs";
834
+ import { homedir as homedir4 } from "os";
835
+ import { dirname as dirname4, join as join4 } from "path";
836
+ import { createCipheriv as createCipheriv4, createDecipheriv as createDecipheriv4, randomBytes as randomBytes4 } from "crypto";
837
+ var DEFAULT_DIR4 = process.env.SUVEREN_DATA_DIR ?? join4(homedir4(), ".suveren");
838
+ function stripPreviews(proposal) {
839
+ const args = proposal.toolArgs;
840
+ if (!args || typeof args !== "object") return proposal;
841
+ const { _imagePreview, ...rest } = args;
842
+ if (_imagePreview === void 0) return proposal;
843
+ return { ...proposal, toolArgs: rest };
844
+ }
845
+ var ReceiptArchive = class {
846
+ data = { version: 1, authorizations: {}, receipts: [] };
847
+ baseDir;
848
+ vaultKey = null;
849
+ constructor(filePath) {
850
+ if (filePath && filePath.endsWith(".json")) {
851
+ this.baseDir = dirname4(filePath);
852
+ } else {
853
+ this.baseDir = filePath ?? DEFAULT_DIR4;
854
+ }
855
+ this.loadPlaintext();
856
+ }
857
+ /**
858
+ * Set the vault key for encryption. Triggers migration from plaintext if needed.
859
+ */
860
+ setVaultKey(key) {
861
+ this.vaultKey = key;
862
+ if (existsSync4(this.encryptedFilePath)) {
863
+ this.loadEncrypted();
864
+ } else if (this.data.receipts.length > 0 || Object.keys(this.data.authorizations).length > 0) {
865
+ this.persistEncrypted();
866
+ if (existsSync4(this.plaintextFilePath)) {
867
+ try {
868
+ unlinkSync4(this.plaintextFilePath);
869
+ } catch {
870
+ }
871
+ }
872
+ }
873
+ }
874
+ /**
875
+ * True when encrypted evidence exists on disk that this process cannot read
876
+ * yet (no vault key). Callers MUST NOT present the archive as "empty" in
877
+ * this state — that would report a hollow success over unreadable evidence.
878
+ */
879
+ isLocked() {
880
+ return this.vaultKey === null && existsSync4(this.encryptedFilePath);
881
+ }
882
+ /**
883
+ * Archive a signed receipt (and the grant it was issued under).
884
+ * Idempotent on receipt id — a replayed receipt is recorded once.
885
+ */
886
+ record(entry, now = Math.floor(Date.now() / 1e3)) {
887
+ let changed = false;
888
+ const receiptId = typeof entry.receipt?.id === "string" ? entry.receipt.id : void 0;
889
+ const alreadyArchived = receiptId !== void 0 && this.data.receipts.some((r) => r.receipt.id === receiptId);
890
+ if (!alreadyArchived) {
891
+ this.data.receipts.push({
892
+ archivedAt: now,
893
+ authorizationId: entry.authorizationId,
894
+ asUrl: entry.asUrl,
895
+ asPublicKey: entry.asPublicKey,
896
+ receipt: entry.receipt,
897
+ ...entry.proposal ? { proposal: stripPreviews(entry.proposal) } : {},
898
+ ...entry.boundContent !== void 0 ? { boundContent: entry.boundContent } : {}
899
+ });
900
+ changed = true;
901
+ }
902
+ if (entry.authorization) {
903
+ const existing = this.data.authorizations[entry.authorizationId];
904
+ if (!existing) {
905
+ this.data.authorizations[entry.authorizationId] = {
906
+ authorizationId: entry.authorizationId,
907
+ profileId: entry.authorization.profileId,
908
+ boundsHash: entry.authorization.boundsHash,
909
+ contextHash: entry.authorization.contextHash,
910
+ bounds: entry.authorization.bounds,
911
+ context: entry.authorization.context,
912
+ intent: entry.authorization.intent,
913
+ attestations: [...entry.authorization.attestations],
914
+ archivedAt: now
915
+ };
916
+ changed = true;
917
+ } else {
918
+ for (const att of entry.authorization.attestations) {
919
+ const seen = existing.attestations.some(
920
+ (a) => a.domain === att.domain && a.blob === att.blob
921
+ );
922
+ if (!seen) {
923
+ existing.attestations.push(att);
924
+ changed = true;
925
+ }
926
+ }
927
+ if (!existing.bounds && entry.authorization.bounds) {
928
+ existing.bounds = entry.authorization.bounds;
929
+ changed = true;
930
+ }
931
+ if (!existing.context && entry.authorization.context) {
932
+ existing.context = entry.authorization.context;
933
+ changed = true;
934
+ }
935
+ if (!existing.intent && entry.authorization.intent) {
936
+ existing.intent = entry.authorization.intent;
937
+ changed = true;
938
+ }
939
+ }
940
+ }
941
+ if (changed) this.persist();
942
+ }
943
+ getReceipts() {
944
+ return [...this.data.receipts];
945
+ }
946
+ getAuthorizations() {
947
+ return Object.values(this.data.authorizations);
948
+ }
949
+ get size() {
950
+ return this.data.receipts.length;
951
+ }
952
+ // ─── Encryption helpers ─────────────────────────────────────────────────
953
+ encrypt(plaintext) {
954
+ if (!this.vaultKey) throw new Error("No vault key");
955
+ const iv = randomBytes4(12);
956
+ const cipher = createCipheriv4("aes-256-gcm", this.vaultKey, iv);
957
+ const encrypted = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
958
+ const tag = cipher.getAuthTag();
959
+ return {
960
+ iv: iv.toString("hex"),
961
+ ciphertext: encrypted.toString("hex"),
962
+ tag: tag.toString("hex")
963
+ };
964
+ }
965
+ decrypt(blob) {
966
+ if (!this.vaultKey) throw new Error("No vault key");
967
+ const decipher = createDecipheriv4(
968
+ "aes-256-gcm",
969
+ this.vaultKey,
970
+ Buffer.from(blob.iv, "hex")
971
+ );
972
+ decipher.setAuthTag(Buffer.from(blob.tag, "hex"));
973
+ const decrypted = Buffer.concat([
974
+ decipher.update(Buffer.from(blob.ciphertext, "hex")),
975
+ decipher.final()
976
+ ]);
977
+ return decrypted.toString("utf8");
978
+ }
979
+ // ─── File paths ─────────────────────────────────────────────────────────
980
+ get plaintextFilePath() {
981
+ return join4(this.baseDir, "receipt-archive.json");
982
+ }
983
+ get encryptedFilePath() {
984
+ return join4(this.baseDir, "receipt-archive.enc.json");
985
+ }
986
+ // ─── Load / Persist ─────────────────────────────────────────────────────
987
+ loadPlaintext() {
988
+ if (!existsSync4(this.plaintextFilePath)) {
989
+ mkdirSync4(this.baseDir, { recursive: true });
990
+ return;
991
+ }
992
+ try {
993
+ const raw = readFileSync4(this.plaintextFilePath, "utf-8");
994
+ const parsed = JSON.parse(raw);
995
+ this.data = {
996
+ version: 1,
997
+ authorizations: parsed.authorizations ?? {},
998
+ receipts: parsed.receipts ?? []
999
+ };
1000
+ } catch {
1001
+ console.error(`[ReceiptArchive] Could not parse ${this.plaintextFilePath} \u2014 preserving as .corrupt`);
1002
+ try {
1003
+ writeFileSync4(
1004
+ `${this.plaintextFilePath}.corrupt-${Date.now()}`,
1005
+ readFileSync4(this.plaintextFilePath)
1006
+ );
1007
+ } catch {
1008
+ }
1009
+ }
1010
+ }
1011
+ loadEncrypted() {
1012
+ if (!existsSync4(this.encryptedFilePath)) return;
1013
+ try {
1014
+ const raw = readFileSync4(this.encryptedFilePath, "utf-8");
1015
+ const data = JSON.parse(raw);
1016
+ const decrypted = this.decrypt(data.blob);
1017
+ const parsed = JSON.parse(decrypted);
1018
+ this.data = {
1019
+ version: 1,
1020
+ authorizations: parsed.authorizations ?? {},
1021
+ receipts: parsed.receipts ?? []
1022
+ };
1023
+ } catch (err) {
1024
+ console.error(`[ReceiptArchive] Could not decrypt ${this.encryptedFilePath} \u2014 preserving as .corrupt:`, err);
1025
+ try {
1026
+ writeFileSync4(
1027
+ `${this.encryptedFilePath}.corrupt-${Date.now()}`,
1028
+ readFileSync4(this.encryptedFilePath)
1029
+ );
1030
+ } catch {
1031
+ }
1032
+ }
1033
+ }
1034
+ persist() {
1035
+ if (this.vaultKey) {
1036
+ this.persistEncrypted();
1037
+ } else {
1038
+ this.persistPlaintext();
1039
+ }
1040
+ }
1041
+ persistPlaintext() {
1042
+ mkdirSync4(this.baseDir, { recursive: true, mode: 448 });
1043
+ writeFileSync4(this.plaintextFilePath, JSON.stringify(this.data, null, 2), { encoding: "utf-8", mode: 384 });
1044
+ }
1045
+ persistEncrypted() {
1046
+ const data = {
1047
+ version: 1,
1048
+ blob: this.encrypt(JSON.stringify(this.data))
1049
+ };
1050
+ mkdirSync4(this.baseDir, { recursive: true, mode: 448 });
1051
+ writeFileSync4(this.encryptedFilePath, JSON.stringify(data, null, 2), { encoding: "utf-8", mode: 384 });
1052
+ }
1053
+ };
1054
+
828
1055
  // src/lib/gatekeeper.ts
829
1056
  import { verify } from "@hap/core";
830
1057
  var MCPGatekeeper = class {
@@ -898,6 +1125,7 @@ var SharedState = class {
898
1125
  gateStore;
899
1126
  executionLog;
900
1127
  denialLog;
1128
+ receiptArchive;
901
1129
  gatekeeper;
902
1130
  constructor(spUrl2, gateStorePath) {
903
1131
  this.spClient = new SPClient(spUrl2);
@@ -905,8 +1133,50 @@ var SharedState = class {
905
1133
  this.gateStore = new GateStore(gateStorePath);
906
1134
  this.executionLog = new ExecutionLog(gateStorePath);
907
1135
  this.denialLog = new DenialLog(gateStorePath);
1136
+ this.receiptArchive = new ReceiptArchive(gateStorePath);
908
1137
  this.gatekeeper = new MCPGatekeeper(this.cache, this.executionLog);
909
1138
  }
1139
+ /**
1140
+ * Archive a signed receipt into the local receipt archive — the subject's
1141
+ * own durable copy of the evidence (see receipt-archive.ts).
1142
+ *
1143
+ * Best-effort by contract: the AS has already issued the receipt and holds
1144
+ * the authoritative copy, so an archive failure logs loudly but MUST NOT
1145
+ * block the execution it documents.
1146
+ */
1147
+ async archiveReceipt(receipt, opts) {
1148
+ try {
1149
+ let asPublicKey;
1150
+ try {
1151
+ asPublicKey = await this.cache.getPublicKey();
1152
+ } catch {
1153
+ }
1154
+ this.receiptArchive.record({
1155
+ receipt,
1156
+ authorizationId: opts.authorizationId,
1157
+ asUrl: this.spClient.url,
1158
+ asPublicKey,
1159
+ proposal: opts.proposal,
1160
+ boundContent: opts.boundContent,
1161
+ authorization: opts.profileId ? {
1162
+ profileId: opts.profileId,
1163
+ boundsHash: opts.boundsHash,
1164
+ contextHash: opts.contextHash ?? this.gateStore.get(opts.authorizationId)?.contextHash,
1165
+ bounds: opts.bounds,
1166
+ context: opts.context,
1167
+ // Fall back to the gate store — the review path's cached auth
1168
+ // carries no gate content.
1169
+ intent: opts.intent ?? this.gateStore.get(opts.authorizationId)?.gateContent?.intent,
1170
+ attestations: opts.attestations ?? []
1171
+ } : void 0
1172
+ });
1173
+ } catch (err) {
1174
+ console.error(
1175
+ "[Suveren MCP] Receipt archive write failed (execution proceeds \u2014 AS retains authoritative copy):",
1176
+ err
1177
+ );
1178
+ }
1179
+ }
910
1180
  setGateContent(path, authorizationId, profileId, content, opts) {
911
1181
  this.gateStore.set(authorizationId, {
912
1182
  authorizationId,
@@ -1012,17 +1282,17 @@ function formatConsumptionFull(entries) {
1012
1282
  }
1013
1283
 
1014
1284
  // 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");
1285
+ import { readFileSync as readFileSync5, existsSync as existsSync5 } from "fs";
1286
+ import { homedir as homedir5 } from "os";
1287
+ import { join as join5 } from "path";
1288
+ var DEFAULT_DIR5 = process.env.SUVEREN_DATA_DIR ?? join5(homedir5(), ".suveren");
1019
1289
  var BRIEF_MAX_CHARS = 16 * 1024;
1020
1290
  function readContextFile(dataDir) {
1021
- const dir = dataDir ?? DEFAULT_DIR4;
1022
- const filePath = join4(dir, "context.md");
1023
- if (!existsSync4(filePath)) return null;
1291
+ const dir = dataDir ?? DEFAULT_DIR5;
1292
+ const filePath = join5(dir, "context.md");
1293
+ if (!existsSync5(filePath)) return null;
1024
1294
  try {
1025
- const content = readFileSync4(filePath, "utf-8").trim();
1295
+ const content = readFileSync5(filePath, "utf-8").trim();
1026
1296
  return content || null;
1027
1297
  } catch {
1028
1298
  return null;
@@ -1192,7 +1462,7 @@ function appendVerificationFooter(tool, args, receiptId, subject) {
1192
1462
  // src/lib/content-binding.ts
1193
1463
  import {
1194
1464
  computeContentHash,
1195
- computeFieldsContentHash,
1465
+ selectBoundFields,
1196
1466
  isFieldBinding,
1197
1467
  bindingAppliesTo,
1198
1468
  getProfile as getProfile2
@@ -1212,14 +1482,18 @@ function computeContentBinding(profileId, tool, toolArgs, actionType) {
1212
1482
  }
1213
1483
  return void 0;
1214
1484
  }
1485
+ const bound = selectBoundFields(binding, toolArgs);
1215
1486
  return {
1216
- contentHash: computeFieldsContentHash(binding, toolArgs),
1217
- contentBinding: { version: binding.version, kind: binding.kind, fields: binding.fields }
1487
+ contentHash: computeContentHash(binding, bound),
1488
+ contentBinding: { version: binding.version, kind: binding.kind, fields: binding.fields },
1489
+ boundContent: bound
1218
1490
  };
1219
1491
  }
1220
1492
  let contentHash;
1493
+ let boundContent;
1221
1494
  if (binding.kind === "jcs") {
1222
1495
  contentHash = computeContentHash(binding, toolArgs);
1496
+ boundContent = toolArgs;
1223
1497
  } else {
1224
1498
  const declared = tool?.gating?.contentField;
1225
1499
  const field = declared ?? (tool ? detectContentField(tool) : null);
@@ -1227,10 +1501,12 @@ function computeContentBinding(profileId, tool, toolArgs, actionType) {
1227
1501
  const raw = typeof toolArgs[field] === "string" ? toolArgs[field] : "";
1228
1502
  if (raw === "") return void 0;
1229
1503
  contentHash = computeContentHash(binding, raw);
1504
+ boundContent = raw;
1230
1505
  }
1231
1506
  return {
1232
1507
  contentHash,
1233
- contentBinding: { version: binding.version, kind: binding.kind }
1508
+ contentBinding: { version: binding.version, kind: binding.kind },
1509
+ boundContent
1234
1510
  };
1235
1511
  }
1236
1512
  function attachReceiptId(tool, args, receiptId) {
@@ -1849,6 +2125,29 @@ function createGatedToolHandlerInner(tool, integrationManager2, state2) {
1849
2125
  }
1850
2126
  }
1851
2127
  }
2128
+ const declaredActionType = typeof execution.action_type === "string" && execution.action_type.length > 0 ? execution.action_type : void 0;
2129
+ if (!declaredActionType) {
2130
+ return {
2131
+ content: [{
2132
+ type: "text",
2133
+ 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.`
2134
+ }],
2135
+ isError: true
2136
+ };
2137
+ }
2138
+ {
2139
+ const profileDef = getProfile3(profile);
2140
+ const registry = profileDef?.boundsSchema?.actionTypes;
2141
+ if (Array.isArray(registry) && registry.length > 0 && !registry.includes(declaredActionType)) {
2142
+ return {
2143
+ content: [{
2144
+ type: "text",
2145
+ text: `Blocked by Gatekeeper: action_type "${declaredActionType}" is not in profile ${profile}'s actionTypes registry (${registry.join(", ")}). Fix the integration manifest.`
2146
+ }],
2147
+ isError: true
2148
+ };
2149
+ }
2150
+ }
1852
2151
  const auths = state2.getEnrichedAuthorizations();
1853
2152
  const matchingAuths = auths.filter(
1854
2153
  (a) => a.complete && profileMatches(a.profileId, profile)
@@ -1946,12 +2245,7 @@ Proposal ID: ${proposal.id}. Check status with check-pending-commitments(proposa
1946
2245
  }
1947
2246
  let receiptId;
1948
2247
  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
- }
2248
+ const actionType = declaredActionType;
1955
2249
  const binding = computeContentBinding(auth.profileId, tool, args, actionType);
1956
2250
  const { receipt } = await state2.spClient.postReceipt({
1957
2251
  authorizationId: authzId,
@@ -1966,6 +2260,19 @@ Proposal ID: ${proposal.id}. Check status with check-pending-commitments(proposa
1966
2260
  ...binding ?? {}
1967
2261
  });
1968
2262
  receiptId = typeof receipt?.id === "string" ? receipt.id : void 0;
2263
+ await state2.archiveReceipt(receipt, {
2264
+ authorizationId: authzId,
2265
+ profileId: auth.profileId,
2266
+ boundsHash: auth.boundsHash,
2267
+ contextHash: auth.contextHash,
2268
+ bounds: auth.bounds ?? auth.frame,
2269
+ context: auth.context,
2270
+ intent: auth.gateContent?.intent,
2271
+ attestations: auth.attestations,
2272
+ // Automatic path has no proposal, so without this the archive
2273
+ // would hold a hash of content it cannot reproduce.
2274
+ boundContent: binding?.boundContent
2275
+ });
1969
2276
  } catch (err) {
1970
2277
  if (err instanceof ContentBindingError) {
1971
2278
  return {
@@ -2434,6 +2741,19 @@ async function executeCommitted(proposal, state2, integrationManager2) {
2434
2741
  ...binding ?? {}
2435
2742
  });
2436
2743
  receiptId = typeof receipt?.id === "string" ? receipt.id : void 0;
2744
+ await state2.archiveReceipt(receipt, {
2745
+ authorizationId: proposal.authorizationId,
2746
+ profileId: proposal.profileId,
2747
+ boundsHash: cachedAuth?.boundsHash,
2748
+ contextHash: cachedAuth?.contextHash,
2749
+ bounds: cachedAuth?.bounds ?? cachedAuth?.frame,
2750
+ context: cachedAuth?.context,
2751
+ attestations: cachedAuth?.attestations,
2752
+ // What the human actually approved, kept next to the receipt that cites
2753
+ // it — otherwise `proposalId` is a pointer to a server we may not have.
2754
+ proposal,
2755
+ boundContent: binding?.boundContent
2756
+ });
2437
2757
  } catch (err) {
2438
2758
  if (err instanceof ContentBindingError2) {
2439
2759
  return {
@@ -2756,16 +3076,16 @@ function verifyGateContentHashes(content, auth) {
2756
3076
  }
2757
3077
 
2758
3078
  // 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");
3079
+ import { mkdirSync as mkdirSync5, readFileSync as readFileSync6, writeFileSync as writeFileSync5, existsSync as existsSync6 } from "fs";
3080
+ import { homedir as homedir6 } from "os";
3081
+ import { dirname as dirname5, join as join6 } from "path";
3082
+ var DEFAULT_DATA_DIR = process.env.SUVEREN_DATA_DIR ?? join6(homedir6(), ".suveren");
2763
3083
  var IntegrationRegistry = class {
2764
3084
  integrations = /* @__PURE__ */ new Map();
2765
3085
  filePath;
2766
3086
  constructor(dataDir) {
2767
3087
  const dir = dataDir ?? DEFAULT_DATA_DIR;
2768
- this.filePath = join5(dir, "integrations.json");
3088
+ this.filePath = join6(dir, "integrations.json");
2769
3089
  this.load();
2770
3090
  }
2771
3091
  // ─── CRUD ───────────────────────────────────────────────────────────────
@@ -2796,12 +3116,12 @@ var IntegrationRegistry = class {
2796
3116
  }
2797
3117
  // ─── Load / Save ────────────────────────────────────────────────────────
2798
3118
  load() {
2799
- if (!existsSync5(this.filePath)) {
2800
- mkdirSync4(dirname4(this.filePath), { recursive: true });
3119
+ if (!existsSync6(this.filePath)) {
3120
+ mkdirSync5(dirname5(this.filePath), { recursive: true });
2801
3121
  return;
2802
3122
  }
2803
3123
  try {
2804
- const raw = readFileSync5(this.filePath, "utf-8");
3124
+ const raw = readFileSync6(this.filePath, "utf-8");
2805
3125
  const data = JSON.parse(raw);
2806
3126
  const migrated = data.integrations.map((i) => {
2807
3127
  if ("toolGating" in i && !("profile" in i)) {
@@ -2830,33 +3150,33 @@ var IntegrationRegistry = class {
2830
3150
  version: 1,
2831
3151
  integrations: Array.from(this.integrations.values())
2832
3152
  };
2833
- mkdirSync4(dirname4(this.filePath), { recursive: true });
2834
- writeFileSync4(this.filePath, JSON.stringify(data, null, 2), { encoding: "utf-8" });
3153
+ mkdirSync5(dirname5(this.filePath), { recursive: true });
3154
+ writeFileSync5(this.filePath, JSON.stringify(data, null, 2), { encoding: "utf-8" });
2835
3155
  }
2836
3156
  };
2837
3157
 
2838
3158
  // 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";
3159
+ import { homedir as homedir7 } from "os";
3160
+ import { join as join7, delimiter } from "path";
3161
+ import { existsSync as existsSync7, writeFileSync as writeFileSync6, mkdirSync as mkdirSync6, readFileSync as readFileSync7, rmSync } from "fs";
2842
3162
  import { execFile } from "child_process";
2843
3163
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
2844
3164
  import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
2845
3165
  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");
3166
+ var DEFAULT_DATA_DIR2 = process.env.SUVEREN_DATA_DIR ?? join7(homedir7(), ".suveren");
3167
+ var INTEGRATIONS_DIR = process.env.SUVEREN_INTEGRATIONS_DIR ?? join7(DEFAULT_DATA_DIR2, "integrations");
3168
+ var INTEGRATIONS_BIN = join7(INTEGRATIONS_DIR, "node_modules", ".bin");
2849
3169
  function buildPath() {
2850
3170
  const base = process.env.PATH ?? "";
2851
3171
  return [INTEGRATIONS_BIN, base].join(delimiter);
2852
3172
  }
2853
3173
  function ensureIntegrationsDir() {
2854
- if (!existsSync6(INTEGRATIONS_DIR)) {
2855
- mkdirSync5(INTEGRATIONS_DIR, { recursive: true });
3174
+ if (!existsSync7(INTEGRATIONS_DIR)) {
3175
+ mkdirSync6(INTEGRATIONS_DIR, { recursive: true });
2856
3176
  }
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));
3177
+ const pkgPath = join7(INTEGRATIONS_DIR, "package.json");
3178
+ if (!existsSync7(pkgPath)) {
3179
+ writeFileSync6(pkgPath, JSON.stringify({ name: "suveren-integrations", version: "1.0.0", private: true }, null, 2));
2860
3180
  }
2861
3181
  }
2862
3182
  function readAgeOf(config) {
@@ -2922,19 +3242,19 @@ var IntegrationManager = class {
2922
3242
  * exist, and treat anything else as not-installed (and reinstallable).
2923
3243
  */
2924
3244
  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;
3245
+ const pkgDir = join7(INTEGRATIONS_DIR, "node_modules", ...npmPackage.split("/"));
3246
+ const pkgJson = join7(pkgDir, "package.json");
3247
+ if (!existsSync7(pkgDir) || !existsSync7(pkgJson)) return false;
2928
3248
  try {
2929
- const pkg = JSON.parse(readFileSync6(pkgJson, "utf8"));
3249
+ const pkg = JSON.parse(readFileSync7(pkgJson, "utf8"));
2930
3250
  const defaultBinName = (pkg.name ?? npmPackage).split("/").pop() ?? "";
2931
3251
  const binMap = typeof pkg.bin === "string" ? { [defaultBinName]: pkg.bin } : pkg.bin ?? {};
2932
3252
  const binEntries = Object.entries(binMap);
2933
3253
  for (const [binName, target] of binEntries) {
2934
- if (!existsSync6(join6(pkgDir, target))) return false;
3254
+ if (!existsSync7(join7(pkgDir, target))) return false;
2935
3255
  if (!this.binShimExists(binName)) return false;
2936
3256
  }
2937
- if (binEntries.length === 0 && pkg.main && !existsSync6(join6(pkgDir, pkg.main))) return false;
3257
+ if (binEntries.length === 0 && pkg.main && !existsSync7(join7(pkgDir, pkg.main))) return false;
2938
3258
  return true;
2939
3259
  } catch {
2940
3260
  return false;
@@ -2948,7 +3268,7 @@ var IntegrationManager = class {
2948
3268
  binShimExists(binName) {
2949
3269
  if (!binName) return false;
2950
3270
  const candidates = process.platform === "win32" ? [binName, `${binName}.cmd`, `${binName}.CMD`, `${binName}.ps1`] : [binName];
2951
- return candidates.some((c) => existsSync6(join6(INTEGRATIONS_BIN, c)));
3271
+ return candidates.some((c) => existsSync7(join7(INTEGRATIONS_BIN, c)));
2952
3272
  }
2953
3273
  /**
2954
3274
  * Install an npm package into the managed integrations directory if not
@@ -2965,8 +3285,8 @@ var IntegrationManager = class {
2965
3285
  async ensureInstalled(npmPackage) {
2966
3286
  ensureIntegrationsDir();
2967
3287
  if (this.isUsableInstall(npmPackage)) return;
2968
- const pkgDir = join6(INTEGRATIONS_DIR, "node_modules", ...npmPackage.split("/"));
2969
- if (existsSync6(pkgDir)) {
3288
+ const pkgDir = join7(INTEGRATIONS_DIR, "node_modules", ...npmPackage.split("/"));
3289
+ if (existsSync7(pkgDir)) {
2970
3290
  console.error(`[IntegrationManager] Removing unusable install of ${npmPackage}`);
2971
3291
  try {
2972
3292
  rmSync(pkgDir, { recursive: true, force: true });
@@ -3381,28 +3701,28 @@ var IntegrationManager = class {
3381
3701
  };
3382
3702
 
3383
3703
  // src/lib/profile-loader.ts
3384
- import { readFileSync as readFileSync7, existsSync as existsSync7 } from "fs";
3385
- import { join as join7, resolve } from "path";
3704
+ import { readFileSync as readFileSync8, existsSync as existsSync8 } from "fs";
3705
+ import { join as join8, resolve } from "path";
3386
3706
  import { registerProfile } from "@hap/core";
3387
3707
  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)) {
3708
+ const dir = resolve(profilesDir ?? process.env.SUVEREN_PROFILES_DIR ?? join8(import.meta.dirname ?? __dirname, "..", "..", "..", "..", "..", "hap-profiles"));
3709
+ const indexPath = join8(dir, "index.json");
3710
+ if (!existsSync8(indexPath)) {
3391
3711
  warnNoProfiles(`No index.json at ${indexPath}`);
3392
3712
  return 0;
3393
3713
  }
3394
3714
  let index;
3395
3715
  try {
3396
- index = JSON.parse(readFileSync7(indexPath, "utf-8"));
3716
+ index = JSON.parse(readFileSync8(indexPath, "utf-8"));
3397
3717
  } catch (err) {
3398
3718
  console.error(`[ProfileLoader] Failed to parse ${indexPath}:`, err);
3399
3719
  return 0;
3400
3720
  }
3401
3721
  let loaded = 0;
3402
3722
  for (const [profileId, relativePath] of Object.entries(index.profiles)) {
3403
- const profilePath = join7(dir, relativePath);
3723
+ const profilePath = join8(dir, relativePath);
3404
3724
  try {
3405
- const profile = JSON.parse(readFileSync7(profilePath, "utf-8"));
3725
+ const profile = JSON.parse(readFileSync8(profilePath, "utf-8"));
3406
3726
  registerProfile(profileId, profile);
3407
3727
  const shortName = profileId.split("/").pop()?.replace(/@.*$/, "");
3408
3728
  if (shortName && shortName !== profileId) {
@@ -3435,30 +3755,30 @@ function warnNoProfiles(reason) {
3435
3755
  }
3436
3756
 
3437
3757
  // 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";
3758
+ import { readFileSync as readFileSync9, existsSync as existsSync9 } from "fs";
3759
+ import { join as join9, resolve as resolve2 } from "path";
3440
3760
  var manifests = /* @__PURE__ */ new Map();
3441
3761
  function loadManifests(integrationsDir) {
3442
3762
  const dir = resolve2(
3443
- integrationsDir ?? process.env.SUVEREN_MANIFESTS_DIR ?? join8(import.meta.dirname ?? __dirname, "..", "..", "..", "..", "content", "integrations")
3763
+ integrationsDir ?? process.env.SUVEREN_MANIFESTS_DIR ?? join9(import.meta.dirname ?? __dirname, "..", "..", "..", "..", "content", "integrations")
3444
3764
  );
3445
- const indexPath = join8(dir, "index.json");
3446
- if (!existsSync8(indexPath)) {
3765
+ const indexPath = join9(dir, "index.json");
3766
+ if (!existsSync9(indexPath)) {
3447
3767
  console.error(`[ManifestLoader] No index.json found at ${indexPath}, skipping manifest loading`);
3448
3768
  return 0;
3449
3769
  }
3450
3770
  let index;
3451
3771
  try {
3452
- index = JSON.parse(readFileSync8(indexPath, "utf-8"));
3772
+ index = JSON.parse(readFileSync9(indexPath, "utf-8"));
3453
3773
  } catch (err) {
3454
3774
  console.error(`[ManifestLoader] Failed to parse ${indexPath}:`, err);
3455
3775
  return 0;
3456
3776
  }
3457
3777
  let loaded = 0;
3458
3778
  for (const [id, relativePath] of Object.entries(index.integrations)) {
3459
- const manifestPath = join8(dir, relativePath);
3779
+ const manifestPath = join9(dir, relativePath);
3460
3780
  try {
3461
- const manifest = JSON.parse(readFileSync8(manifestPath, "utf-8"));
3781
+ const manifest = JSON.parse(readFileSync9(manifestPath, "utf-8"));
3462
3782
  manifests.set(id, manifest);
3463
3783
  loaded++;
3464
3784
  } catch (err) {
@@ -3476,6 +3796,7 @@ function getAllManifests() {
3476
3796
  }
3477
3797
 
3478
3798
  // bin/http.ts
3799
+ import { decodeAttestationBlob as decodeAttestationBlob3 } from "@hap/core";
3479
3800
  var spUrl = process.env.SUVEREN_AS_URL ?? "https://www.suveren.ai";
3480
3801
  var port = parseInt(process.env.SUVEREN_MCP_PORT ?? "3430", 10);
3481
3802
  var state = new SharedState(spUrl);
@@ -3539,7 +3860,8 @@ app.post("/internal/configure", internalOnly, (req, res) => {
3539
3860
  const key = Buffer.from(vaultKeyHex, "hex");
3540
3861
  state.gateStore.setVaultKey(key);
3541
3862
  state.denialLog.setVaultKey(key);
3542
- console.error("[Suveren MCP] Vault key configured \u2014 gate store + denial log encryption active");
3863
+ state.receiptArchive.setVaultKey(key);
3864
+ console.error("[Suveren MCP] Vault key configured \u2014 gate store + denial log + receipt archive encryption active");
3543
3865
  }
3544
3866
  if (apiKey) {
3545
3867
  state.spClient.setApiKey(apiKey);
@@ -3836,6 +4158,27 @@ app.get("/internal/authorizations", internalOnly, (_req, res) => {
3836
4158
  app.get("/internal/manifests", internalOnly, (_req, res) => {
3837
4159
  res.json({ manifests: getAllManifests() });
3838
4160
  });
4161
+ app.get("/internal/evidence", internalOnly, (_req, res) => {
4162
+ if (state.receiptArchive.isLocked()) {
4163
+ res.status(503).json({ error: "Vault locked \u2014 the evidence archive is encrypted until sign-in." });
4164
+ return;
4165
+ }
4166
+ const authorizations = state.receiptArchive.getAuthorizations().map((a) => ({
4167
+ ...a,
4168
+ attestations: a.attestations.map((att) => {
4169
+ try {
4170
+ return { ...att, decoded: decodeAttestationBlob3(att.blob) };
4171
+ } catch {
4172
+ return att;
4173
+ }
4174
+ })
4175
+ }));
4176
+ res.json({
4177
+ receipts: state.receiptArchive.getReceipts(),
4178
+ authorizations,
4179
+ gates: state.gateStore.getAll()
4180
+ });
4181
+ });
3839
4182
  app.get("/internal/brief", internalOnly, (_req, res) => {
3840
4183
  try {
3841
4184
  const enriched = state.getEnrichedAuthorizations();