enigma-memory 0.1.18 → 0.1.22

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 (276) hide show
  1. package/README.md +76 -24
  2. package/apps/cli/bin/enigma-desktop.mjs +140 -0
  3. package/apps/cli/bin/enigma-terminal.mjs +78 -0
  4. package/apps/cli/bin/enigma.mjs +1923 -285
  5. package/apps/desktop/electron-main.cjs +217 -0
  6. package/apps/desktop/package.json +12 -0
  7. package/apps/desktop/src/app.js +264 -7
  8. package/apps/desktop/src/index.html +3514 -1373
  9. package/apps/desktop/src/launch-electron.mjs +51 -0
  10. package/apps/desktop/src/server.mjs +2914 -0
  11. package/apps/desktop/src/styles.css +2972 -260
  12. package/apps/desktop/src/zk-browser-prove.mjs +53 -0
  13. package/apps/desktop/src/zk-state.mjs +1789 -0
  14. package/apps/gateway/bin/enigma-gateway.mjs +102 -5
  15. package/apps/gateway/src/server.mjs +271 -8
  16. package/apps/ios/EnigmaCore/Package.swift +12 -0
  17. package/apps/ios/EnigmaCore/Sources/EnigmaCore/EnigmaAPIClient.swift +227 -0
  18. package/apps/ios/EnigmaCore/Sources/EnigmaCore/Models.swift +278 -0
  19. package/apps/ios/EnigmaCore/Sources/EnigmaCore/PKCE.swift +96 -0
  20. package/apps/ios/EnigmaCore/Sources/EnigmaCore/PrivacyMinimizer.swift +187 -0
  21. package/apps/ios/EnigmaCore/Sources/EnigmaCore/ToolModels.swift +129 -0
  22. package/apps/ios/EnigmaCore/Tests/EnigmaCoreTests/EnigmaCoreTests.swift +42 -0
  23. package/apps/ios/EnigmaIOS/Enigma/AppModel.swift +346 -0
  24. package/apps/ios/EnigmaIOS/Enigma/Assets.xcassets/AccentColor.colorset/Contents.json +12 -0
  25. package/apps/ios/EnigmaIOS/Enigma/Assets.xcassets/AppIcon.appiconset/Contents.json +11 -0
  26. package/apps/ios/EnigmaIOS/Enigma/Assets.xcassets/AppIcon.appiconset/EnigmaAppIcon.png +0 -0
  27. package/apps/ios/EnigmaIOS/Enigma/Assets.xcassets/Contents.json +3 -0
  28. package/apps/ios/EnigmaIOS/Enigma/Assets.xcassets/LaunchBackground.colorset/Contents.json +12 -0
  29. package/apps/ios/EnigmaIOS/Enigma/ChatView.swift +181 -0
  30. package/apps/ios/EnigmaIOS/Enigma/CouncilView.swift +78 -0
  31. package/apps/ios/EnigmaIOS/Enigma/CreateView.swift +152 -0
  32. package/apps/ios/EnigmaIOS/Enigma/EnigmaApp.swift +52 -0
  33. package/apps/ios/EnigmaIOS/Enigma/Info.plist +52 -0
  34. package/apps/ios/EnigmaIOS/Enigma/NaturalLanguagePrivacyTagger.swift +26 -0
  35. package/apps/ios/EnigmaIOS/Enigma/OAuthClient.swift +321 -0
  36. package/apps/ios/EnigmaIOS/Enigma/OnboardingView.swift +105 -0
  37. package/apps/ios/EnigmaIOS/Enigma/PrivateVaultView.swift +275 -0
  38. package/apps/ios/EnigmaIOS/Enigma/SecureStore.swift +76 -0
  39. package/apps/ios/EnigmaIOS/Enigma/SettingsView.swift +60 -0
  40. package/apps/ios/EnigmaIOS/Enigma/Theme.swift +80 -0
  41. package/apps/ios/EnigmaIOS/EnigmaIOS.xcodeproj/project.pbxproj +211 -0
  42. package/apps/ios/EnigmaIOS/EnigmaIOS.xcodeproj/xcshareddata/xcschemes/Enigma.xcscheme +23 -0
  43. package/apps/native-host/README.md +19 -8
  44. package/apps/native-host/bin/enigma-native-host.mjs +229 -13
  45. package/apps/relay/bin/enigma-relay.mjs +103 -5
  46. package/apps/relay/src/federation-runtime.mjs +618 -0
  47. package/apps/relay/src/server.mjs +310 -9
  48. package/apps/verifier/bin/enigma-verify.mjs +327 -11
  49. package/cortex-v3/circuits/build/intent_vk_bytes.json +35 -0
  50. package/cortex-v3/circuits/build/sale_vk_bytes.json +35 -0
  51. package/cortex-v3/circuits/build/vk_bytes.json +32 -0
  52. package/cortex-v3/proving-assets.json +64 -0
  53. package/cortex-v3/zk/BUILD-CONTRACT.md +87 -0
  54. package/cortex-v3/zk/action-transition-vk.json +119 -0
  55. package/cortex-v3/zk/alias-adversarial.test.mjs +220 -0
  56. package/cortex-v3/zk/groth16-verify-child.mjs +17 -0
  57. package/cortex-v3/zk/intent-witness.mjs +365 -0
  58. package/cortex-v3/zk/intent-witness.test.mjs +485 -0
  59. package/cortex-v3/zk/proving-assets.mjs +203 -0
  60. package/cortex-v3/zk/sale-witness.mjs +783 -0
  61. package/cortex-v3/zk/sale-witness.test.mjs +784 -0
  62. package/cortex-v3/zk/sealed-sale-release-vk.json +119 -0
  63. package/cortex-v3/zk/settlement-evidence.mjs +722 -0
  64. package/cortex-v3/zk/setup-intent.mjs +688 -0
  65. package/cortex-v3/zk/setup-sale.mjs +666 -0
  66. package/cortex-v3/zk/setup.mjs +594 -0
  67. package/cortex-v3/zk/witness.mjs +184 -0
  68. package/cortex-v3/zk/zk-codec.mjs +232 -0
  69. package/cortex-v3/zk/zk-codec.test.mjs +293 -0
  70. package/cortex-v3/zk/zk-settle.mjs +370 -0
  71. package/cortex-v3/zk/zk-tree.mjs +256 -0
  72. package/cortex-v3/zk/zk-tree.test.mjs +419 -0
  73. package/deploy/docker-compose.local-production-simulation.yml +36 -0
  74. package/docs/browser-extension-install.md +8 -6
  75. package/docs/client-connectors.md +15 -11
  76. package/docs/developer-ecosystem.md +15 -13
  77. package/docs/enigma-memory-ready-conformance.md +11 -9
  78. package/docs/install-anywhere.md +61 -28
  79. package/docs/installers-and-desktop.md +8 -7
  80. package/docs/novelty-invention-candidates.md +161 -161
  81. package/docs/proof-network-claim-boundaries.md +320 -318
  82. package/examples/01-quickstart-agent/index.mjs +49 -0
  83. package/examples/01_agent_memory_quickstart.mjs +57 -0
  84. package/examples/02-multi-agent-swarm/index.mjs +57 -0
  85. package/examples/02_cross_model_passport.mjs +64 -0
  86. package/examples/03-langchain-memory/index.mjs +41 -0
  87. package/examples/03_poseidon_commitment_verification.mjs +71 -0
  88. package/examples/04-python-trading-agent/trader.py +49 -0
  89. package/examples/README.md +27 -0
  90. package/examples/ci/github-actions.yml +7 -2
  91. package/package.json +142 -11
  92. package/packages/adapters/PACKAGE_CONTRACT.md +1 -1
  93. package/packages/connectors/src/index.js +196 -4
  94. package/packages/connectors/swarm-router.mjs +168 -0
  95. package/packages/core/src/index.js +248 -1
  96. package/packages/core/src/version.mjs +7 -0
  97. package/packages/dev-tools/package.json +19 -0
  98. package/packages/dev-tools/src/index.js +4 -0
  99. package/packages/dev-tools/src/memory-benchmark-suite.js +112 -0
  100. package/packages/dev-tools/src/swarm-simulator.js +101 -0
  101. package/packages/dev-tools/src/vault-inspector.js +114 -0
  102. package/packages/dev-tools/src/vector-benchmark.js +100 -0
  103. package/packages/developer-platform/src/access-credentials.js +341 -0
  104. package/packages/developer-platform/src/http.js +132 -0
  105. package/packages/developer-platform/src/index.js +4 -0
  106. package/packages/developer-platform/src/usage-http.js +60 -0
  107. package/packages/developer-platform/src/usage.js +295 -0
  108. package/packages/enclave-runtime/attestation.mjs +159 -0
  109. package/packages/enclave-runtime/index.mjs +47 -0
  110. package/packages/enclave-runtime/session-manager.mjs +253 -0
  111. package/packages/enclave-runtime/zeroization-proof.mjs +227 -0
  112. package/packages/enigma-reflex/package.json +14 -0
  113. package/packages/enigma-reflex/src/index.js +204 -0
  114. package/packages/enigma-reflex/training/generate-dataset.mjs +40 -0
  115. package/packages/enigma-reflex/training/requirements.txt +8 -0
  116. package/packages/enigma-reflex/training/train.py +314 -0
  117. package/packages/enigma-weave/LICENSE +22 -0
  118. package/packages/enigma-weave/UPSTREAM.json +21 -0
  119. package/packages/enigma-weave/package.json +14 -0
  120. package/packages/enigma-weave/src/index.js +286 -0
  121. package/packages/hosted-cloud/src/index.js +80 -5
  122. package/packages/importers/src/index.js +432 -0
  123. package/packages/inference-runtime/src/browser.js +401 -0
  124. package/packages/inference-runtime/src/chat.js +265 -0
  125. package/packages/inference-runtime/src/code.js +407 -0
  126. package/packages/inference-runtime/src/contracts.js +162 -0
  127. package/packages/inference-runtime/src/http.js +232 -0
  128. package/packages/inference-runtime/src/image.js +186 -0
  129. package/packages/inference-runtime/src/index.js +10 -0
  130. package/packages/inference-runtime/src/model-router.js +320 -0
  131. package/packages/inference-runtime/src/platform.js +125 -0
  132. package/packages/inference-runtime/src/privacy.js +400 -0
  133. package/packages/inference-runtime/src/video.js +253 -0
  134. package/packages/mcp-server/README.md +22 -6
  135. package/packages/mcp-server/bin/enigma-mcp.mjs +2 -1
  136. package/packages/mcp-server/src/index.js +1418 -105
  137. package/packages/mcp-server/src/oauth.js +561 -0
  138. package/packages/mcp-server/src/private-handoff.js +84 -0
  139. package/packages/mcp-server/src/remote-http.js +273 -0
  140. package/packages/mcp-server/src/remote-policy.js +72 -0
  141. package/packages/mcp-server/swarm-bridge.mjs +361 -0
  142. package/packages/mesh/index.d.ts +283 -0
  143. package/packages/mesh/package.json +23 -0
  144. package/packages/mesh/src/crypto.js +189 -0
  145. package/packages/mesh/src/federation-packets.js +353 -0
  146. package/packages/mesh/src/gossip.js +311 -0
  147. package/packages/mesh/src/index.js +6 -0
  148. package/packages/mesh/src/protocol.js +255 -0
  149. package/packages/mesh/src/router.js +279 -0
  150. package/packages/mesh/src/transport.js +306 -0
  151. package/packages/passport/src/index.js +426 -1
  152. package/packages/private-economy/src/credits-http.js +100 -0
  153. package/packages/private-economy/src/credits.js +447 -0
  154. package/packages/private-economy/src/index.js +5 -0
  155. package/packages/private-economy/src/payments-http.js +120 -0
  156. package/packages/private-economy/src/payments.js +509 -0
  157. package/packages/private-economy/src/x402.js +346 -0
  158. package/packages/proof-network/PACKAGE_CONTRACT.md +21 -0
  159. package/packages/rag/index.d.ts +182 -0
  160. package/packages/rag/models/Xenova/all-MiniLM-L6-v2/THIRD_PARTY_LICENSES.txt +207 -0
  161. package/packages/rag/models/Xenova/all-MiniLM-L6-v2/config.json +25 -0
  162. package/packages/rag/models/Xenova/all-MiniLM-L6-v2/onnx/model_quantized.onnx +0 -0
  163. package/packages/rag/models/Xenova/all-MiniLM-L6-v2/sha256-manifest.json +28 -0
  164. package/packages/rag/models/Xenova/all-MiniLM-L6-v2/tokenizer.json +30686 -0
  165. package/packages/rag/models/Xenova/all-MiniLM-L6-v2/tokenizer_config.json +15 -0
  166. package/packages/rag/package.json +27 -0
  167. package/packages/rag/src/blinded-search.js +109 -0
  168. package/packages/rag/src/bm25.js +169 -0
  169. package/packages/rag/src/embeddings.js +459 -0
  170. package/packages/rag/src/hybrid.js +76 -0
  171. package/packages/rag/src/index.js +38 -0
  172. package/packages/rag/src/reranker.js +61 -0
  173. package/packages/rag/src/research.js +107 -0
  174. package/packages/rag/src/vector-store.js +430 -0
  175. package/packages/rag/src/verify-model-artifacts.mjs +4 -0
  176. package/packages/sdk/index.d.ts +760 -0
  177. package/packages/sdk/package.json +33 -0
  178. package/packages/sdk/python/README.md +24 -0
  179. package/packages/sdk/python/enigma_sdk.py +250 -0
  180. package/packages/sdk/python/pyproject.toml +34 -0
  181. package/packages/sdk/python/requirements.txt +1 -0
  182. package/packages/sdk/python/setup.py +20 -0
  183. package/packages/sdk/src/federation/capability-grant.js +389 -0
  184. package/packages/sdk/src/federation/federation-router.js +360 -0
  185. package/packages/sdk/src/federation/ghostmesh-bridge.js +497 -0
  186. package/packages/sdk/src/federation/index.js +3 -0
  187. package/packages/sdk/src/index.js +1796 -0
  188. package/packages/sdk/src/intelligence/contradiction.js +337 -0
  189. package/packages/sdk/src/intelligence/decision-engine.js +155 -0
  190. package/packages/sdk/src/intelligence/index.js +4 -0
  191. package/packages/sdk/src/intelligence/ontology.js +122 -0
  192. package/packages/sdk/src/intelligence/temporal.js +123 -0
  193. package/packages/sdk/src/market-client.js +142 -0
  194. package/packages/sdk/src/mesh-client.js +110 -0
  195. package/packages/sdk/src/middleware/index.js +3 -0
  196. package/packages/sdk/src/middleware/langchain.js +159 -0
  197. package/packages/sdk/src/middleware/llamaindex.js +101 -0
  198. package/packages/sdk/src/middleware/vercel-ai.js +112 -0
  199. package/packages/sdk/src/rag-client.js +85 -0
  200. package/packages/sdk/src/swarm-orchestrator.js +260 -0
  201. package/packages/settlement/PACKAGE_CONTRACT.md +1 -1
  202. package/packages/snapcompact/THIRD_PARTY_LICENSES.txt +40 -0
  203. package/packages/snapcompact/assets/8x13-latin1.bdf +3837 -0
  204. package/packages/snapcompact/index.d.ts +284 -0
  205. package/packages/snapcompact/package.json +25 -0
  206. package/packages/snapcompact/src/index.js +716 -0
  207. package/packages/storage/PACKAGE_CONTRACT.md +1 -1
  208. package/packages/terminal-console/animations.mjs +240 -0
  209. package/packages/terminal-console/auto-anchor.mjs +220 -0
  210. package/packages/terminal-console/banner.mjs +91 -0
  211. package/packages/terminal-console/commands.mjs +459 -0
  212. package/packages/terminal-console/delegation.mjs +152 -0
  213. package/packages/terminal-console/index.mjs +5 -0
  214. package/packages/terminal-console/outbox.mjs +143 -0
  215. package/packages/terminal-console/phantom-bridge.mjs +637 -0
  216. package/packages/terminal-console/repl.mjs +136 -0
  217. package/packages/terminal-console/signer-store.mjs +130 -0
  218. package/packages/terminal-console/solana-rpc.mjs +214 -0
  219. package/packages/terminal-console/solana-transport.mjs +189 -0
  220. package/packages/terminal-tui/dashboard.mjs +214 -0
  221. package/packages/terminal-tui/index.mjs +28 -0
  222. package/packages/terminal-tui/merkle-tree-renderer.mjs +268 -0
  223. package/packages/terminal-tui/telemetry-hud.mjs +137 -0
  224. package/packages/vault/index.d.ts +449 -0
  225. package/packages/vault/package.json +27 -0
  226. package/packages/vault/src/e2ee.mjs +393 -0
  227. package/packages/vault/src/enclave.js +481 -0
  228. package/packages/vault/src/erasure.js +207 -0
  229. package/packages/vault/src/index.js +1018 -155
  230. package/packages/vault/src/persistence.js +307 -0
  231. package/packages/vault/src/poseidon.js +354 -0
  232. package/packages/vault/src/receipt.js +459 -0
  233. package/scripts/benchmark-optical-context.mjs +166 -0
  234. package/scripts/bootstrap-enigma.mjs +502 -0
  235. package/scripts/build-edge-backend-workers.mjs +20 -5
  236. package/scripts/build-goal-completion-audit.mjs +72 -25
  237. package/scripts/build-hosted-api-key-lifecycle.mjs +26 -8
  238. package/scripts/build-hosted-customer-lifecycle.mjs +20 -3
  239. package/scripts/build-hosted-probe-worker.mjs +19 -4
  240. package/scripts/build-installer-assets.mjs +41 -21
  241. package/scripts/build-operator-evidence-starter.mjs +59 -1
  242. package/scripts/build-production-backend-env-kit.mjs +2 -0
  243. package/scripts/build-production-unblocker.mjs +3 -0
  244. package/scripts/check.mjs +17 -3
  245. package/scripts/collect-hosted-backend-live-evidence.mjs +49 -12
  246. package/scripts/install-enigma-local.mjs +18 -5
  247. package/scripts/release-audit.mjs +65 -109
  248. package/scripts/release-provenance.mjs +6 -0
  249. package/scripts/run-backend-readiness-smoke.mjs +112 -10
  250. package/scripts/scan-secrets.mjs +1 -0
  251. package/scripts/simulate-production-env.mjs +7 -2
  252. package/scripts/validate-hosted-backend-live.mjs +112 -1
  253. package/specs/antibody-pack-v1.schema.json +95 -0
  254. package/specs/antigen-envelope-v1.schema.json +81 -0
  255. package/specs/boundary-manifest-v1.schema.json +35 -35
  256. package/specs/capsule-v1.schema.json +55 -55
  257. package/specs/claim-boundary-manifest-v1.schema.json +22 -22
  258. package/specs/claim-ledger-v1.schema.json +291 -0
  259. package/specs/context-passport-v1.schema.json +59 -0
  260. package/specs/deletion-tombstone-v1.schema.json +26 -26
  261. package/specs/evidence-packet-v1.schema.json +177 -0
  262. package/specs/hosted-backend-live-evidence-v1.schema.json +72 -3
  263. package/specs/immune-scan-report-v1.schema.json +112 -0
  264. package/specs/lifecycle-receipt-log-v1.schema.json +67 -0
  265. package/specs/memory-atom-v1.schema.json +59 -0
  266. package/specs/memory-event-v1.schema.json +42 -42
  267. package/specs/passport-v1.schema.json +50 -50
  268. package/specs/proof-of-non-use-v1.schema.json +65 -0
  269. package/specs/quarantine-record-v1.schema.json +126 -0
  270. package/specs/receipt-v1.schema.json +61 -61
  271. package/specs/state-checkpoint-v1.schema.json +37 -37
  272. package/specs/trust-bundle-v1.schema.json +56 -56
  273. package/specs/trust-card-v1.schema.json +119 -0
  274. package/docs/proof-network-launch-plan.md +0 -421
  275. package/packages/metering/PACKAGE_CONTRACT.md +0 -20
  276. package/scripts/build-ai-orchestration-plan.mjs +0 -248
@@ -0,0 +1,360 @@
1
+ // Enigma Cross-Agent Memory Federation Router.
2
+ // Mediates selective capability-based queries across sovereign agent memory clusters
3
+ // with cryptographic proof-of-possession, grantee identity binding, and nonce replay protection.
4
+ import {
5
+ verifyBlindedMemoryGrant,
6
+ createFederatedQueryRequest,
7
+ verifyFederatedQueryRequest,
8
+ getGrantSignablePayload,
9
+ FEDERATED_QUERY_REQUEST_SCHEMA,
10
+ } from './capability-grant.js';
11
+ import { normalizeMemoryType } from '../intelligence/ontology.js';
12
+
13
+ export const FEDERATION_ROUTER_STATE_SCHEMA = 'enigma.federation_router_state.v1';
14
+ export const MAX_ACTIVE_FEDERATION_GRANTS = 1_024;
15
+ export const MAX_REVOKED_FEDERATION_GRANTS = 4_096;
16
+ export const MAX_SEEN_FEDERATION_NONCES = 10_000;
17
+
18
+ export class EnigmaFederationRouter {
19
+ /**
20
+ * @param {Object} [options]
21
+ * @param {import('../index.js').Enigma} options.enigma - Sovereign agent instance
22
+ */
23
+ constructor(options = {}) {
24
+ if (!options.enigma) throw new Error('enigma SDK instance is required for federation router');
25
+ this.enigma = options.enigma;
26
+ /** @type {Map<string, { grant: Object, usedCount: number }>} */
27
+ this.activeGrants = new Map();
28
+ /** @type {Set<string>} */
29
+ this.revokedGrantIds = new Set();
30
+ /** @type {Map<string, number>} */
31
+ this.seenNonces = new Map();
32
+ this.onStateMutation = typeof options.onStateMutation === 'function'
33
+ ? options.onStateMutation
34
+ : null;
35
+ if (options.state !== undefined) this.importState(options.state);
36
+ }
37
+ setStateMutationHandler(handler) {
38
+ if (handler !== null && typeof handler !== 'function') {
39
+ throw new TypeError('federation state mutation handler must be a function or null');
40
+ }
41
+ this.onStateMutation = handler;
42
+ return this;
43
+ }
44
+
45
+ async persistStateMutation() {
46
+ if (this.onStateMutation) await this.onStateMutation(this.exportState());
47
+ }
48
+
49
+
50
+ /**
51
+ * Registers a valid BlindedMemoryGrant in the local router.
52
+ * @param {Object} grant
53
+ * @returns {{ ok: boolean, grant_id: string }}
54
+ */
55
+ registerGrant(grant) {
56
+ if (!grant || typeof grant !== 'object' || typeof grant.grant_id !== 'string') {
57
+ throw new Error('Cannot register invalid capability grant: grant object and grant_id are required');
58
+ }
59
+ if (this.revokedGrantIds.has(grant.grant_id)) {
60
+ throw new Error(`Cannot register revoked capability grant: ${grant.grant_id}`);
61
+ }
62
+
63
+ const verdict = verifyBlindedMemoryGrant(grant);
64
+ if (!verdict.ok) {
65
+ throw new Error(`Cannot register invalid capability grant: ${verdict.errors.join('; ')}`);
66
+ }
67
+ const myPubHex = this.enigma.mesh.identity?.publicKey?.toString('hex');
68
+ if (myPubHex && grant.granter_pubkey !== myPubHex && grant.grantee_pubkey !== myPubHex) {
69
+ throw new Error(`Cannot register capability grant ${grant.grant_id}: local agent is neither granter nor grantee`);
70
+ }
71
+ if (!this.activeGrants.has(grant.grant_id) && this.activeGrants.size >= MAX_ACTIVE_FEDERATION_GRANTS) {
72
+ throw new Error(`Cannot register capability grant: active grant capacity ${MAX_ACTIVE_FEDERATION_GRANTS} reached`);
73
+ }
74
+
75
+ const existing = this.activeGrants.get(grant.grant_id);
76
+ if (existing) {
77
+ const sameGrant = getGrantSignablePayload(existing.grant) === getGrantSignablePayload(grant) &&
78
+ existing.grant.signature === grant.signature;
79
+ if (!sameGrant) throw new Error(`Capability grant ID collision for ${grant.grant_id}`);
80
+ return { ok: true, grant_id: grant.grant_id };
81
+ }
82
+ this.activeGrants.set(grant.grant_id, { grant, usedCount: 0 });
83
+ return { ok: true, grant_id: grant.grant_id };
84
+ }
85
+
86
+ /**
87
+ * Revokes a capability grant, permanently denying subsequent queries.
88
+ * @param {string} grantId
89
+ * @returns {boolean}
90
+ */
91
+ revokeGrant(grantId) {
92
+ if (typeof grantId !== 'string' || grantId.length === 0) return false;
93
+ if (!this.revokedGrantIds.has(grantId) && this.revokedGrantIds.size >= MAX_REVOKED_FEDERATION_GRANTS) {
94
+ throw new Error(`Cannot revoke capability grant: revocation capacity ${MAX_REVOKED_FEDERATION_GRANTS} reached`);
95
+ }
96
+ this.revokedGrantIds.add(grantId);
97
+ return true;
98
+ }
99
+
100
+ /**
101
+ * Returns a JSON-safe snapshot of all authorization state needed to preserve
102
+ * replay protection, access counts, registered grants, and revocations.
103
+ */
104
+ exportState() {
105
+ return {
106
+ schema: FEDERATION_ROUTER_STATE_SCHEMA,
107
+ version: 1,
108
+ active_grants: Array.from(this.activeGrants.entries())
109
+ .sort(([a], [b]) => a.localeCompare(b))
110
+ .map(([, record]) => ({
111
+ grant: JSON.parse(JSON.stringify(record.grant)),
112
+ used_count: record.usedCount,
113
+ })),
114
+ revoked_grant_ids: Array.from(this.revokedGrantIds).sort(),
115
+ seen_nonces: Array.from(this.seenNonces.entries())
116
+ .sort(([a], [b]) => a.localeCompare(b))
117
+ .map(([key, seenAt]) => ({ key, seen_at: seenAt })),
118
+ };
119
+ }
120
+
121
+ /**
122
+ * Strictly restores a snapshot created by exportState(). Validation completes
123
+ * before any live authorization state is changed.
124
+ */
125
+ importState(state) {
126
+ if (!state || typeof state !== 'object' ||
127
+ state.schema !== FEDERATION_ROUTER_STATE_SCHEMA || state.version !== 1) {
128
+ throw new Error('Invalid federation router state schema or version');
129
+ }
130
+ if (!Array.isArray(state.active_grants) ||
131
+ !Array.isArray(state.revoked_grant_ids) ||
132
+ !Array.isArray(state.seen_nonces)) {
133
+ throw new Error('Federation router state arrays are required');
134
+ }
135
+ if (
136
+ state.active_grants.length > MAX_ACTIVE_FEDERATION_GRANTS
137
+ || state.revoked_grant_ids.length > MAX_REVOKED_FEDERATION_GRANTS
138
+ || state.seen_nonces.length > MAX_SEEN_FEDERATION_NONCES
139
+ ) {
140
+ throw new Error('Federation router state exceeds a safe authorization-state bound');
141
+ }
142
+
143
+ const nextActive = new Map();
144
+ const nextRevoked = new Set();
145
+ const nextNonces = new Map();
146
+ const myPubHex = this.enigma.mesh.identity?.publicKey?.toString('hex');
147
+
148
+ for (const entry of state.active_grants) {
149
+ if (!entry || typeof entry !== 'object' || !entry.grant ||
150
+ !Number.isSafeInteger(entry.used_count) || entry.used_count < 0) {
151
+ throw new Error('Invalid active grant state entry');
152
+ }
153
+ const verdict = verifyBlindedMemoryGrant(entry.grant, {
154
+ asOfTime: new Date(entry.grant.issued_at).getTime(),
155
+ });
156
+ if (!verdict.ok) {
157
+ throw new Error(`Invalid grant in federation router state: ${verdict.errors.join('; ')}`);
158
+ }
159
+ if (myPubHex && entry.grant.granter_pubkey !== myPubHex && entry.grant.grantee_pubkey !== myPubHex) {
160
+ throw new Error(`Federation router state grant ${entry.grant.grant_id} is not bound to the local agent`);
161
+ }
162
+ if (entry.grant.max_access_count > 0 && entry.used_count > entry.grant.max_access_count) {
163
+ throw new Error(`Federation router state usage exceeds grant ${entry.grant.grant_id} access limit`);
164
+ }
165
+ if (nextActive.has(entry.grant.grant_id)) {
166
+ throw new Error(`Duplicate active grant in federation router state: ${entry.grant.grant_id}`);
167
+ }
168
+ nextActive.set(entry.grant.grant_id, {
169
+ grant: JSON.parse(JSON.stringify(entry.grant)),
170
+ usedCount: entry.used_count,
171
+ });
172
+ }
173
+
174
+ for (const grantId of state.revoked_grant_ids) {
175
+ if (typeof grantId !== 'string' || grantId.length === 0 || nextRevoked.has(grantId)) {
176
+ throw new Error('Invalid or duplicate revoked grant ID in federation router state');
177
+ }
178
+ nextRevoked.add(grantId);
179
+ }
180
+
181
+ for (const entry of state.seen_nonces) {
182
+ if (!entry || typeof entry.key !== 'string' ||
183
+ !/^.+:[0-9a-f]{32}$/.test(entry.key) ||
184
+ !Number.isSafeInteger(entry.seen_at) || entry.seen_at < 0 ||
185
+ nextNonces.has(entry.key)) {
186
+ throw new Error('Invalid or duplicate nonce entry in federation router state');
187
+ }
188
+ nextNonces.set(entry.key, entry.seen_at);
189
+ }
190
+
191
+ this.activeGrants = nextActive;
192
+ this.revokedGrantIds = nextRevoked;
193
+ this.seenNonces = nextNonces;
194
+ return this;
195
+ }
196
+
197
+ /**
198
+ * Clean up expired seen nonces older than TTL.
199
+ * @param {number} [ttlMs=600000]
200
+ */
201
+ pruneSeenNonces(ttlMs = 600000) {
202
+ if (!Number.isSafeInteger(ttlMs) || ttlMs < 300000) {
203
+ throw new Error('Nonce replay TTL must be a safe integer of at least 300000ms');
204
+ }
205
+ const now = Date.now();
206
+ let pruned = 0;
207
+ for (const [key, ts] of this.seenNonces.entries()) {
208
+ if (now - ts > ttlMs) {
209
+ this.seenNonces.delete(key);
210
+ pruned++;
211
+ }
212
+ }
213
+ return pruned;
214
+ }
215
+
216
+ /**
217
+ * Executes a capability-gated federated memory query under a verified grant.
218
+ * Authenticates the requester via cryptographic proof-of-possession signature matching
219
+ * `grant.grantee_pubkey`, validates freshness/replay protection, and enforces ABAC ontology scoping.
220
+ *
221
+ * @param {Object} grantOrRequest - Signed BlindedMemoryGrant OR signed FederatedQueryRequest
222
+ * @param {string} [query] - Agent search prompt
223
+ * @param {Object} [options]
224
+ * @param {Buffer|string} [options.requesterSecretKey] - 32-byte private key of the grantee for inline signature
225
+ * @param {Buffer|string} [options.requesterPublicKey] - 32-byte public key of the grantee
226
+ * @param {import('../index.js').Enigma} [options.requesterAgent] - Grantee agent instance with mesh identity
227
+ * @param {Object} [options.authorization] - Explicit authorization envelope { requester_pubkey, nonce, timestamp, signature }
228
+ * @param {number} [options.topK=5]
229
+ * @returns {Promise<{ ok: boolean, grant_id: string, scope: string, items: Array<{ id: string, text: string, type: string, score: number }>, remainingAccess: number }>}
230
+ */
231
+ async queryFederatedCluster(grantOrRequest, query, options = {}) {
232
+ this.pruneSeenNonces();
233
+
234
+ let request;
235
+ let actualQuery = query;
236
+
237
+ if (grantOrRequest && grantOrRequest.schema === FEDERATED_QUERY_REQUEST_SCHEMA) {
238
+ request = grantOrRequest;
239
+ actualQuery = request.query;
240
+ } else {
241
+ const grant = grantOrRequest;
242
+ if (typeof actualQuery !== 'string' || actualQuery.trim().length === 0) {
243
+ throw new Error('Query string is required for federated cluster search');
244
+ }
245
+
246
+ let requesterSecretKey = options.requesterSecretKey;
247
+ let requesterPublicKey = options.requesterPublicKey;
248
+ if (!requesterSecretKey && options.requesterAgent?.mesh?.identity) {
249
+ requesterSecretKey = options.requesterAgent.mesh.identity.secretKey;
250
+ requesterPublicKey = options.requesterAgent.mesh.identity.publicKey;
251
+ }
252
+
253
+ if (requesterSecretKey) {
254
+ request = createFederatedQueryRequest({
255
+ grant,
256
+ query: actualQuery,
257
+ requesterSecretKey,
258
+ requesterPublicKey,
259
+ });
260
+ } else if (options.authorization) {
261
+ request = {
262
+ schema: FEDERATED_QUERY_REQUEST_SCHEMA,
263
+ version: 1,
264
+ grant,
265
+ grant_id: grant.grant_id,
266
+ query: actualQuery,
267
+ ...options.authorization,
268
+ };
269
+ } else {
270
+ throw new Error(
271
+ 'Grantee proof-of-possession required: Provide requesterAgent, requesterSecretKey, or a signed query authorization envelope matching grant.grantee_pubkey'
272
+ );
273
+ }
274
+ }
275
+
276
+ const reqVerdict = verifyFederatedQueryRequest(request);
277
+ if (!reqVerdict.ok) {
278
+ throw new Error(`Federated query authorization rejected: ${reqVerdict.errors.join('; ')}`);
279
+ }
280
+
281
+ const grant = request.grant;
282
+ const grantId = grant.grant_id;
283
+ if (this.revokedGrantIds.has(grantId)) {
284
+ throw new Error(`Capability grant ${grantId} has been revoked by granter`);
285
+ }
286
+
287
+ const myPubHex = this.enigma.mesh.identity?.publicKey?.toString('hex');
288
+ if (myPubHex && grant.granter_pubkey !== myPubHex) {
289
+ throw new Error(`Granter public key mismatch: grant issued by ${grant.granter_pubkey}, local agent is ${myPubHex}`);
290
+ }
291
+
292
+ const topK = options.topK === undefined ? 5 : options.topK;
293
+ if (!Number.isSafeInteger(topK) || topK < 1 || topK > 100) {
294
+ throw new Error('topK must be an integer between 1 and 100');
295
+ }
296
+
297
+ const nonceKey = `${grantId}:${request.nonce}`;
298
+ if (this.seenNonces.has(nonceKey)) {
299
+ throw new Error(`Replay attack detected: nonce ${request.nonce} has already been consumed for grant ${grantId}`);
300
+ }
301
+ if (!this.seenNonces.has(nonceKey) && this.seenNonces.size >= MAX_SEEN_FEDERATION_NONCES) {
302
+ throw new Error(`Federation replay cache capacity ${MAX_SEEN_FEDERATION_NONCES} reached`);
303
+ }
304
+
305
+ let record = this.activeGrants.get(grantId);
306
+ const createdRecord = !record;
307
+ if (record) {
308
+ const sameGrant = getGrantSignablePayload(record.grant) === getGrantSignablePayload(grant) &&
309
+ record.grant.signature === grant.signature;
310
+ if (!sameGrant) throw new Error(`Capability grant ID collision for ${grantId}`);
311
+ } else {
312
+ if (this.activeGrants.size >= MAX_ACTIVE_FEDERATION_GRANTS) {
313
+ throw new Error(`Active federation grant capacity ${MAX_ACTIVE_FEDERATION_GRANTS} reached`);
314
+ }
315
+ record = { grant, usedCount: 0 };
316
+ this.activeGrants.set(grantId, record);
317
+ }
318
+
319
+ if (grant.max_access_count > 0 && record.usedCount >= grant.max_access_count) {
320
+ if (createdRecord) this.activeGrants.delete(grantId);
321
+ throw new Error(`Access limit exceeded: grant ${grantId} has exhausted its ${grant.max_access_count} queries limit`);
322
+ }
323
+
324
+ // Consume and durably commit authorization before recall so concurrent
325
+ // requests cannot race limits and a successful response cannot outlive the
326
+ // persisted replay/count state.
327
+ this.seenNonces.set(nonceKey, Date.now());
328
+ record.usedCount += 1;
329
+ try {
330
+ await this.persistStateMutation();
331
+ } catch (error) {
332
+ this.seenNonces.delete(nonceKey);
333
+ if (createdRecord) this.activeGrants.delete(grantId);
334
+ else record.usedCount -= 1;
335
+ throw new Error(`Federation authorization state persistence failed: ${error.message}`);
336
+ }
337
+ const allowedTypes = grant.allowed_types.length > 0
338
+ ? new Set(grant.allowed_types.map(normalizeMemoryType))
339
+ : null;
340
+ const rawRecalls = await this.enigma.recall(actualQuery, {
341
+ scope: grant.scope,
342
+ topK,
343
+ });
344
+ const filteredItems = rawRecalls.filter((item) =>
345
+ item.scope === grant.scope && (!allowedTypes || allowedTypes.has(item.type))
346
+ );
347
+
348
+ const remainingAccess = grant.max_access_count > 0
349
+ ? Math.max(0, grant.max_access_count - record.usedCount)
350
+ : Infinity;
351
+
352
+ return {
353
+ ok: true,
354
+ grant_id: grantId,
355
+ scope: grant.scope,
356
+ items: filteredItems,
357
+ remainingAccess,
358
+ };
359
+ }
360
+ }