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,101 @@
1
+ // Enigma GhostMesh Multi-Node Swarm Simulator.
2
+ // Simulates P2P routing convergence, multi-hop packet dispatching,
3
+ // and delivery success metrics across N autonomous agent nodes.
4
+ import {
5
+ MeshGossipNode,
6
+ InMemoryBus,
7
+ InMemoryTransport,
8
+ generateMeshIdentity,
9
+ } from '../../mesh/src/index.js';
10
+
11
+ export async function simulateSwarmNetwork(options = {}) {
12
+ const nodeCount = Math.max(2, Math.min(options.nodeCount || 5, 20));
13
+ const messageCount = Math.max(1, options.messageCount || 10);
14
+ const bus = new InMemoryBus();
15
+
16
+ const nodes = [];
17
+ const identities = [];
18
+ const receivedMessages = new Map(); // targetDestinationHex -> Array<plaintext>
19
+
20
+ // 1. Initialize nodes
21
+ for (let i = 0; i < nodeCount; i++) {
22
+ const identity = generateMeshIdentity();
23
+ const nodeId = `sim_node_${i + 1}`;
24
+ const transport = new InMemoryTransport(nodeId, bus);
25
+ const node = new MeshGossipNode({
26
+ nodeId,
27
+ identity,
28
+ transports: [transport],
29
+ });
30
+
31
+ const destHex = identity.destinationHex;
32
+ receivedMessages.set(destHex, []);
33
+ node.on('capsule', ({ plaintext }) => {
34
+ const list = receivedMessages.get(destHex);
35
+ if (list) list.push(plaintext);
36
+ });
37
+
38
+ await node.start();
39
+ nodes.push(node);
40
+ identities.push(identity);
41
+ }
42
+
43
+ // 2. Announce routes sequentially to establish topology
44
+ for (let i = 0; i < nodeCount; i++) {
45
+ await nodes[i].announce({ name: `Agent-${i + 1}`, index: i });
46
+ }
47
+ await new Promise(r => setTimeout(r, 60));
48
+
49
+ // 3. Dispatch simulated messages across random pairs
50
+ let deliveredCount = 0;
51
+ const deliveryLatenciesMs = [];
52
+
53
+ for (let m = 0; m < messageCount; m++) {
54
+ const senderIdx = m % nodeCount;
55
+ let targetIdx = (m + 1) % nodeCount;
56
+ if (senderIdx === targetIdx) targetIdx = (targetIdx + 1) % nodeCount;
57
+
58
+ const sender = nodes[senderIdx];
59
+ const target = identities[targetIdx];
60
+ const payload = `Swarm message #${m + 1} from Node ${senderIdx + 1} to Node ${targetIdx + 1}`;
61
+
62
+ const start = Date.now();
63
+ const res = await sender.sendEncryptedCapsule(
64
+ target.destinationHex,
65
+ target.encryptionPublicKey,
66
+ payload
67
+ );
68
+
69
+ // Wait briefly for in-memory delivery to process
70
+ await new Promise(r => setTimeout(r, 10));
71
+
72
+ const targetInbox = receivedMessages.get(target.destinationHex) || [];
73
+ const received = targetInbox.includes(payload);
74
+
75
+ if (res.routed && received) {
76
+ deliveredCount++;
77
+ deliveryLatenciesMs.push(Date.now() - start);
78
+ }
79
+ }
80
+
81
+ // 4. Teardown
82
+ for (const n of nodes) {
83
+ await n.stop();
84
+ }
85
+
86
+ const successRate = Math.round((deliveredCount / messageCount) * 100);
87
+ const meanLatency = deliveryLatenciesMs.length > 0
88
+ ? Math.round((deliveryLatenciesMs.reduce((a, b) => a + b, 0) / deliveryLatenciesMs.length) * 10) / 10
89
+ : 0;
90
+
91
+ return {
92
+ ok: true,
93
+ simulation: 'ghostmesh_swarm_simulation_v1',
94
+ nodeCount,
95
+ messageCount,
96
+ deliveredCount,
97
+ deliverySuccessRatePercent: successRate,
98
+ meanLatencyMs: meanLatency,
99
+ timestamp: Date.now(),
100
+ };
101
+ }
@@ -0,0 +1,114 @@
1
+ // Enigma Vault Bundle Inspector.
2
+ // Analyzes encrypted vault headers, KDF metadata, scope distribution,
3
+ // and Merkle tree roots with strict zero-leakage boundaries.
4
+ import fs from 'node:fs';
5
+ import path from 'node:path';
6
+ import crypto from 'node:crypto';
7
+ import { deriveKeysFromPassphrase, PBKDF2_KDF_SPEC } from '../../vault/src/index.js';
8
+
9
+ export async function inspectVaultBundle(bundlePath, options = {}) {
10
+ const resolved = path.resolve(bundlePath);
11
+ if (!fs.existsSync(resolved)) {
12
+ throw new Error(`Vault file not found at path: ${resolved}`);
13
+ }
14
+
15
+ const fileStats = fs.statSync(resolved);
16
+ const rawText = fs.readFileSync(resolved, 'utf8');
17
+
18
+ let bundle;
19
+ try {
20
+ bundle = JSON.parse(rawText);
21
+ } catch (err) {
22
+ throw new Error(`Corrupt JSON in vault file: ${err.message}`);
23
+ }
24
+
25
+ const format = bundle.format || 'unknown';
26
+ const version = bundle.version || 1;
27
+ const isEncrypted = typeof bundle.ciphertext === 'string' && typeof bundle.authTag === 'string';
28
+ const kdf = bundle.kdf || null;
29
+
30
+ const inspectionReport = {
31
+ ok: true,
32
+ file: {
33
+ path: resolved,
34
+ sizeBytes: fileStats.size,
35
+ updatedAt: fileStats.mtime.toISOString(),
36
+ },
37
+ envelope: {
38
+ format,
39
+ version,
40
+ isEncrypted,
41
+ count: bundle.count || 0,
42
+ kdf: kdf ? {
43
+ algorithm: kdf.algorithm,
44
+ iterations: kdf.iterations,
45
+ hash: kdf.hash,
46
+ saltPresent: Boolean(kdf.salt),
47
+ } : null,
48
+ ivLengthBytes: bundle.iv ? Buffer.from(bundle.iv, 'hex').length : 0,
49
+ authTagLengthBytes: bundle.authTag ? Buffer.from(bundle.authTag, 'hex').length : 0,
50
+ },
51
+ contents: null,
52
+ };
53
+
54
+ // If passphrase is provided, perform deep inspection
55
+ if (options.passphrase && isEncrypted) {
56
+ try {
57
+ if (!kdf || !kdf.salt) throw new Error('Missing KDF salt in bundle');
58
+ const saltBuf = /^[0-9a-fA-F]+$/.test(kdf.salt)
59
+ ? Buffer.from(kdf.salt, 'hex')
60
+ : Buffer.from(kdf.salt, 'base64');
61
+ const kdfSpec = {
62
+ algorithm: kdf.algorithm || 'scrypt',
63
+ n: kdf.n || 16384,
64
+ r: kdf.r || 8,
65
+ p: kdf.p || 1,
66
+ iterations: kdf.iterations || 600000,
67
+ hash: kdf.hash || 'sha256',
68
+ };
69
+ const vaultKey = deriveKeysFromPassphrase(options.passphrase, saltBuf, kdfSpec).vaultKey;
70
+
71
+ const iv = Buffer.from(bundle.iv, 'hex');
72
+ const authTag = Buffer.from(bundle.authTag, 'hex');
73
+ const ciphertext = Buffer.from(bundle.ciphertext, 'base64');
74
+
75
+ const decipher = crypto.createDecipheriv('aes-256-gcm', vaultKey, iv);
76
+ if (bundle.aad) {
77
+ decipher.setAAD(Buffer.isBuffer(bundle.aad) ? bundle.aad : Buffer.from(bundle.aad));
78
+ }
79
+ decipher.setAuthTag(authTag);
80
+ const decrypted = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
81
+ const data = JSON.parse(decrypted.toString('utf8'));
82
+
83
+ const scopes = {};
84
+ const memories = data.memories || [];
85
+ let oldest = Infinity;
86
+ let newest = 0;
87
+
88
+ for (const m of memories) {
89
+ const sc = m.scope || 'default';
90
+ scopes[sc] = (scopes[sc] || 0) + 1;
91
+ if (m.createdAt && m.createdAt < oldest) oldest = m.createdAt;
92
+ if (m.createdAt && m.createdAt > newest) newest = m.createdAt;
93
+ }
94
+
95
+ inspectionReport.contents = {
96
+ unlocked: true,
97
+ memoryCount: memories.length,
98
+ scopeDistribution: scopes,
99
+ timeline: {
100
+ oldestMemory: oldest !== Infinity ? new Date(oldest).toISOString() : null,
101
+ newestMemory: newest !== 0 ? new Date(newest).toISOString() : null,
102
+ },
103
+ hasVectorBundle: Boolean(data.vectorBundle),
104
+ };
105
+ } catch (err) {
106
+ inspectionReport.contents = {
107
+ unlocked: false,
108
+ error: `Decryption failed: ${err.message}`,
109
+ };
110
+ }
111
+ }
112
+
113
+ return inspectionReport;
114
+ }
@@ -0,0 +1,100 @@
1
+ // Enigma Vector Benchmark Suite.
2
+ // Benchmarks embedding generation throughput, semantic search latency,
3
+ // and Int8 quantization memory savings and cosine fidelity.
4
+ import { performance } from 'node:perf_hooks';
5
+ import {
6
+ computeLexicalEmbedding,
7
+ cosineSimilarity,
8
+ quantizeVector,
9
+ dequantizeVector,
10
+ EncryptedVectorStore,
11
+ } from '../../rag/src/index.js';
12
+
13
+ /**
14
+ * @param {Object} [options]
15
+ * @param {number} [options.itemCount]
16
+ * @param {number} [options.queryCount]
17
+ * @param {Object} [options.embeddingProvider]
18
+ * @param {EncryptedVectorStore} [options.vectorStore]
19
+ */
20
+ export async function runVectorBenchmark(options = {}) {
21
+ const itemCount = Math.max(10, options.itemCount || 100);
22
+ const queryCount = Math.max(5, options.queryCount || 50);
23
+
24
+ const sampleCorpus = [
25
+ 'Always enforce 0.5% maximum slippage on Jupiter DEX Solana swaps',
26
+ 'Verify on-chain zero-knowledge commitments before releasing private memory data',
27
+ 'Use Aztec Indexed Merkle Trees for constant-time non-membership nullifier proof verification',
28
+ 'GhostMesh peer communications require X25519 ECDH key exchange with AES-256-GCM AEAD',
29
+ 'Autonomous agents must fail closed when cryptographic custody is absent',
30
+ 'Token budget allocation for prompt context compilation should prioritize high importance memory atoms',
31
+ 'Sealed Sale fair exchange atomically reveals data encryption keys upon Solana claim settlement',
32
+ 'Poseidon hash sponge arithmetic over BN254 scalar field guarantees SNARK proof efficiency',
33
+ ];
34
+
35
+ // 1. Benchmark Embedding Throughput
36
+ const startEmbed = performance.now();
37
+ const embeddings = [];
38
+ for (let i = 0; i < itemCount; i++) {
39
+ const text = `${sampleCorpus[i % sampleCorpus.length]} (instance #${i})`;
40
+ embeddings.push(computeLexicalEmbedding(text));
41
+ }
42
+ const embedDurationMs = performance.now() - startEmbed;
43
+ const embedThroughput = Math.round((itemCount / (embedDurationMs / 1000)) * 10) / 10;
44
+
45
+ // 2. Benchmark Quantization Memory & Fidelity
46
+ let totalFidelity = 0;
47
+ for (let i = 0; i < Math.min(itemCount, 50); i++) {
48
+ const orig = embeddings[i];
49
+ const quantized = quantizeVector(orig);
50
+ const restored = dequantizeVector(quantized);
51
+ totalFidelity += cosineSimilarity(orig, restored);
52
+ }
53
+ const meanQuantizationFidelity = Math.round((totalFidelity / Math.min(itemCount, 50)) * 10000) / 10000;
54
+ const floatBytes = itemCount * 384 * 4;
55
+ const int8Bytes = itemCount * 384 * 1;
56
+ const memoryCompressionPercent = Math.round(((floatBytes - int8Bytes) / floatBytes) * 100);
57
+
58
+ // 3. Benchmark Vector Store Search Latency
59
+ const store = options.vectorStore || new EncryptedVectorStore({
60
+ quantize: true,
61
+ ...(options.embeddingProvider ? { embeddingProvider: options.embeddingProvider } : {}),
62
+ });
63
+ for (let i = 0; i < itemCount; i++) {
64
+ const text = `${sampleCorpus[i % sampleCorpus.length]} (instance #${i})`;
65
+ await store.add(`item_${i}`, text, { scope: 'benchmark' });
66
+ }
67
+
68
+ const startQuery = performance.now();
69
+ for (let q = 0; q < queryCount; q++) {
70
+ const query = sampleCorpus[q % sampleCorpus.length];
71
+ await store.search(query, { topK: 5 });
72
+ }
73
+ const queryDurationMs = performance.now() - startQuery;
74
+ const queryThroughput = Math.round((queryCount / (queryDurationMs / 1000)) * 10) / 10;
75
+ const meanLatencyMs = Math.round((queryDurationMs / queryCount) * 1000) / 1000;
76
+
77
+ return {
78
+ ok: true,
79
+ benchmark: 'enigma_vector_performance_v1',
80
+ itemCount,
81
+ queryCount,
82
+ embedding: {
83
+ totalDurationMs: Math.round(embedDurationMs * 100) / 100,
84
+ throughputPerSecond: embedThroughput,
85
+ },
86
+ quantization: {
87
+ dimension: 384,
88
+ float32Bytes: floatBytes,
89
+ int8Bytes: int8Bytes,
90
+ memoryReductionPercent: memoryCompressionPercent,
91
+ meanCosineFidelity: meanQuantizationFidelity,
92
+ },
93
+ retrieval: {
94
+ totalDurationMs: Math.round(queryDurationMs * 100) / 100,
95
+ queriesPerSecond: queryThroughput,
96
+ meanLatencyMs,
97
+ },
98
+ timestamp: Date.now(),
99
+ };
100
+ }
@@ -0,0 +1,341 @@
1
+ import { randomBytes, randomUUID, scrypt as scryptCallback, timingSafeEqual } from 'node:crypto';
2
+ import { mkdir, open, readFile, rename } from 'node:fs/promises';
3
+ import { dirname, resolve } from 'node:path';
4
+ import { promisify } from 'node:util';
5
+
6
+ export const ENIGMA_API_KEY_STORE_SCHEMA = 'enigma.api_key_store.v1';
7
+ export const ENIGMA_API_KEY_SCHEMA = 'enigma.api_key.v1';
8
+ const scrypt = promisify(scryptCallback);
9
+ const KEY_PATTERN = /^enigma_sk_(live|test)_([A-Za-z0-9_-]{12})_([A-Za-z0-9_-]{43})$/;
10
+
11
+ function clockIso(clock) {
12
+ return new Date(clock()).toISOString();
13
+ }
14
+
15
+ function boundedInteger(value, name, minimum, maximum, fallback) {
16
+ const resolved = value === undefined ? fallback : value;
17
+ if (!Number.isInteger(resolved) || resolved < minimum || resolved > maximum) {
18
+ throw new TypeError(`${name} must be an integer from ${minimum} through ${maximum}`);
19
+ }
20
+ return resolved;
21
+ }
22
+
23
+ function requiredString(value, name, maximum = 512) {
24
+ if (typeof value !== 'string' || !value.trim()) throw new TypeError(`${name} is required`);
25
+ const normalized = value.trim();
26
+ if (normalized.length > maximum) throw new TypeError(`${name} exceeds ${maximum} characters`);
27
+ return normalized;
28
+ }
29
+
30
+ function normalizeScopes(value, supportedScopes) {
31
+ const scopes = [...new Set((value || []).map((scope) => requiredString(scope, 'scope', 128)))].sort();
32
+ if (!scopes.length) throw new TypeError('at least one API key scope is required');
33
+ if (scopes.some((scope) => !supportedScopes.has(scope))) throw new TypeError('API key contains an unsupported scope');
34
+ return scopes;
35
+ }
36
+
37
+ function emptyState() {
38
+ return { schema: ENIGMA_API_KEY_STORE_SCHEMA, revision: 0, keys: {}, prefixIndex: {}, audit: [] };
39
+ }
40
+
41
+ function publicRecord(record) {
42
+ return {
43
+ schema: ENIGMA_API_KEY_SCHEMA,
44
+ id: record.id,
45
+ prefix: record.prefix,
46
+ environment: record.environment,
47
+ label: record.label,
48
+ ownerId: record.ownerId,
49
+ tenantId: record.tenantId,
50
+ scopes: [...record.scopes],
51
+ createdAt: record.createdAt,
52
+ expiresAt: record.expiresAt,
53
+ lastUsedAt: record.lastUsedAt,
54
+ revokedAt: record.revokedAt,
55
+ revokeReason: record.revokeReason,
56
+ rotatedFrom: record.rotatedFrom,
57
+ rotatedTo: record.rotatedTo,
58
+ status: record.revokedAt ? 'revoked' : Date.parse(record.expiresAt) <= Date.now() ? 'expired' : 'active',
59
+ };
60
+ }
61
+
62
+ function createStoreAdapter(load, save, { clock, auditLimit }) {
63
+ let queue = Promise.resolve();
64
+ const transaction = (operation) => {
65
+ const run = queue.then(async () => {
66
+ const state = await load();
67
+ const result = await operation(state);
68
+ state.revision += 1;
69
+ if (state.audit.length > auditLimit) state.audit.splice(0, state.audit.length - auditLimit);
70
+ await save(state);
71
+ return result;
72
+ });
73
+ queue = run.catch(() => undefined);
74
+ return run;
75
+ };
76
+ const view = async (operation) => {
77
+ await queue;
78
+ return operation(await load());
79
+ };
80
+ const appendAudit = (state, event) => state.audit.push({ eventId: randomUUID(), at: clockIso(clock), ...event });
81
+
82
+ return Object.freeze({
83
+ async insert(record, event) {
84
+ return transaction((state) => {
85
+ if (state.keys[record.id] || state.prefixIndex[record.prefix]) throw new Error('API key identifier collision');
86
+ state.keys[record.id] = structuredClone(record);
87
+ state.prefixIndex[record.prefix] = record.id;
88
+ appendAudit(state, event);
89
+ return structuredClone(record);
90
+ });
91
+ },
92
+ async rotate(previousId, nextRecord, event) {
93
+ return transaction((state) => {
94
+ const previous = state.keys[previousId];
95
+ if (!previous) return null;
96
+ if (previous.revokedAt) throw new Error('revoked API key cannot be rotated');
97
+ if (state.prefixIndex[nextRecord.prefix]) throw new Error('API key identifier collision');
98
+ previous.revokedAt = clockIso(clock);
99
+ previous.revokeReason = 'rotated';
100
+ previous.rotatedTo = nextRecord.id;
101
+ state.keys[nextRecord.id] = structuredClone(nextRecord);
102
+ state.prefixIndex[nextRecord.prefix] = nextRecord.id;
103
+ appendAudit(state, event);
104
+ return { previous: structuredClone(previous), next: structuredClone(nextRecord) };
105
+ });
106
+ },
107
+ async revoke(id, reason, event) {
108
+ return transaction((state) => {
109
+ const record = state.keys[id];
110
+ if (!record) return null;
111
+ if (!record.revokedAt) {
112
+ record.revokedAt = clockIso(clock);
113
+ record.revokeReason = reason;
114
+ appendAudit(state, event);
115
+ }
116
+ return structuredClone(record);
117
+ });
118
+ },
119
+ async getById(id) {
120
+ return view((state) => state.keys[id] ? structuredClone(state.keys[id]) : null);
121
+ },
122
+ async getByPrefix(prefix) {
123
+ return view((state) => {
124
+ const id = state.prefixIndex[prefix];
125
+ return id && state.keys[id] ? structuredClone(state.keys[id]) : null;
126
+ });
127
+ },
128
+ async list(ownerId, tenantId) {
129
+ return view((state) => Object.values(state.keys)
130
+ .filter((record) => record.ownerId === ownerId && record.tenantId === tenantId)
131
+ .map((record) => structuredClone(record))
132
+ .sort((a, b) => b.createdAt.localeCompare(a.createdAt)));
133
+ },
134
+ async markUsed(id, at) {
135
+ return transaction((state) => {
136
+ const record = state.keys[id];
137
+ if (!record || record.revokedAt || Date.parse(record.expiresAt) <= clock()) return false;
138
+ record.lastUsedAt = at;
139
+ return true;
140
+ });
141
+ },
142
+ async audit(ownerId, tenantId, limit = 100) {
143
+ return view((state) => state.audit
144
+ .filter((event) => event.ownerId === ownerId && event.tenantId === tenantId)
145
+ .slice(-limit)
146
+ .reverse()
147
+ .map((event) => structuredClone(event)));
148
+ },
149
+ });
150
+ }
151
+
152
+ export function createEphemeralApiKeyStore(options = {}) {
153
+ const clock = options.clock || Date.now;
154
+ const auditLimit = boundedInteger(options.auditLimit, 'auditLimit', 100, 100_000, 10_000);
155
+ let state = emptyState();
156
+ return createStoreAdapter(async () => structuredClone(state), async (next) => { state = structuredClone(next); }, { clock, auditLimit });
157
+ }
158
+
159
+ export function createFileApiKeyStore(options = {}) {
160
+ const path = resolve(String(options.path || '.enigma/api-keys.json'));
161
+ const clock = options.clock || Date.now;
162
+ const auditLimit = boundedInteger(options.auditLimit, 'auditLimit', 100, 100_000, 10_000);
163
+ async function load() {
164
+ try {
165
+ const state = JSON.parse(await readFile(path, 'utf8'));
166
+ if (state?.schema !== ENIGMA_API_KEY_STORE_SCHEMA) throw new Error('unsupported API key store schema');
167
+ return state;
168
+ } catch (error) {
169
+ if (error.code === 'ENOENT') return emptyState();
170
+ throw error;
171
+ }
172
+ }
173
+ async function save(state) {
174
+ await mkdir(dirname(path), { recursive: true, mode: 0o700 });
175
+ const temporary = `${path}.${process.pid}.${randomUUID()}.tmp`;
176
+ const handle = await open(temporary, 'wx', 0o600);
177
+ try {
178
+ await handle.writeFile(JSON.stringify(state));
179
+ await handle.sync();
180
+ } finally {
181
+ await handle.close();
182
+ }
183
+ await rename(temporary, path);
184
+ }
185
+ return createStoreAdapter(load, save, { clock, auditLimit });
186
+ }
187
+
188
+ async function deriveSecret(secret, salt) {
189
+ return Buffer.from(await scrypt(secret, Buffer.from(salt, 'base64url'), 32, { N: 16384, r: 8, p: 1, maxmem: 64 * 1024 * 1024 }));
190
+ }
191
+
192
+ function parseRawKey(value) {
193
+ const match = KEY_PATTERN.exec(String(value || ''));
194
+ return match ? { environment: match[1], prefix: match[2], secret: match[3] } : null;
195
+ }
196
+
197
+ function principalIdentity(input = {}) {
198
+ return {
199
+ ownerId: requiredString(input.ownerId || input.subject, 'ownerId'),
200
+ tenantId: requiredString(input.tenantId || input.tenant_id || 'default', 'tenantId'),
201
+ };
202
+ }
203
+
204
+ export function createApiKeyService(options = {}) {
205
+ const store = options.store;
206
+ if (!store || typeof store.insert !== 'function' || typeof store.getByPrefix !== 'function') throw new TypeError('API key store is required');
207
+ const clock = options.clock || Date.now;
208
+ const environment = options.environment === 'test' ? 'test' : 'live';
209
+ const supportedScopes = new Set(options.scopes || [
210
+ 'mcp:invoke', 'models:invoke', 'images:generate', 'video:generate', 'code:execute', 'browser:control', 'usage:read', 'credits:create',
211
+ 'memory:read', 'memory:write', 'memory:delete', 'vault:admin', 'swarm:read', 'swarm:write',
212
+ 'security:inspect', 'metering:write', 'settlement:read', 'settlement:write', 'settlement:operator', 'nullifier:prepare', 'payments:execute',
213
+ ]);
214
+ const defaultScopes = normalizeScopes(options.defaultScopes || ['mcp:invoke', 'models:invoke'], supportedScopes);
215
+ const maxActiveKeys = boundedInteger(options.maxActiveKeys, 'maxActiveKeys', 1, 100, 20);
216
+ const maximumTtlSeconds = boundedInteger(options.maximumTtlSeconds, 'maximumTtlSeconds', 300, 366 * 24 * 60 * 60, 90 * 24 * 60 * 60);
217
+ const defaultTtlSeconds = boundedInteger(options.defaultTtlSeconds, 'defaultTtlSeconds', 300, maximumTtlSeconds, 30 * 24 * 60 * 60);
218
+
219
+ async function createRecord(input, rotation = {}) {
220
+ const identity = principalIdentity(input);
221
+ const scopes = normalizeScopes(input.scopes || defaultScopes, supportedScopes);
222
+ const ttlSeconds = boundedInteger(input.ttlSeconds, 'ttlSeconds', 300, maximumTtlSeconds, defaultTtlSeconds);
223
+ const issuedAt = clock();
224
+ const prefix = randomBytes(9).toString('base64url');
225
+ const secret = randomBytes(32).toString('base64url');
226
+ const salt = randomBytes(16).toString('base64url');
227
+ const secretDigest = (await deriveSecret(secret, salt)).toString('base64url');
228
+ const record = {
229
+ id: randomUUID(),
230
+ prefix,
231
+ environment,
232
+ label: requiredString(input.label || 'API key', 'label', 120),
233
+ ownerId: identity.ownerId,
234
+ tenantId: identity.tenantId,
235
+ scopes,
236
+ salt,
237
+ secretDigest,
238
+ createdAt: new Date(issuedAt).toISOString(),
239
+ expiresAt: new Date(issuedAt + ttlSeconds * 1_000).toISOString(),
240
+ lastUsedAt: null,
241
+ revokedAt: null,
242
+ revokeReason: null,
243
+ rotatedFrom: rotation.rotatedFrom || null,
244
+ rotatedTo: null,
245
+ };
246
+ return { record, rawKey: `enigma_sk_${environment}_${prefix}_${secret}` };
247
+ }
248
+
249
+ function assertOwned(record, identity) {
250
+ if (!record || record.ownerId !== identity.ownerId || record.tenantId !== identity.tenantId) {
251
+ const error = new Error('API key not found');
252
+ error.code = 'not_found';
253
+ throw error;
254
+ }
255
+ }
256
+
257
+ return Object.freeze({
258
+ supportedScopes: Object.freeze([...supportedScopes].sort()),
259
+ async issue(input = {}) {
260
+ const identity = principalIdentity(input);
261
+ const active = (await store.list(identity.ownerId, identity.tenantId))
262
+ .filter((record) => !record.revokedAt && Date.parse(record.expiresAt) > clock());
263
+ if (active.length >= maxActiveKeys) throw new Error(`active API key limit of ${maxActiveKeys} reached`);
264
+ const { record, rawKey } = await createRecord(input);
265
+ await store.insert(record, {
266
+ action: 'issued', keyId: record.id, prefix: record.prefix, ownerId: record.ownerId, tenantId: record.tenantId,
267
+ actorId: requiredString(input.actorId || identity.ownerId, 'actorId'),
268
+ });
269
+ return { key: publicRecord(record), secret: rawKey, secretShownOnce: true };
270
+ },
271
+ async list(input = {}) {
272
+ const identity = principalIdentity(input);
273
+ return (await store.list(identity.ownerId, identity.tenantId)).map(publicRecord);
274
+ },
275
+ async rotate(id, input = {}) {
276
+ const identity = principalIdentity(input);
277
+ const previous = await store.getById(requiredString(id, 'key id'));
278
+ assertOwned(previous, identity);
279
+ if (previous.revokedAt || Date.parse(previous.expiresAt) <= clock()) throw new Error('only an active API key can be rotated');
280
+ const { record, rawKey } = await createRecord({
281
+ ...input,
282
+ ownerId: identity.ownerId,
283
+ tenantId: identity.tenantId,
284
+ label: input.label || previous.label,
285
+ scopes: input.scopes || previous.scopes,
286
+ }, { rotatedFrom: previous.id });
287
+ const rotated = await store.rotate(previous.id, record, {
288
+ action: 'rotated', keyId: record.id, previousKeyId: previous.id, prefix: record.prefix,
289
+ ownerId: identity.ownerId, tenantId: identity.tenantId,
290
+ actorId: requiredString(input.actorId || identity.ownerId, 'actorId'),
291
+ });
292
+ if (!rotated) throw new Error('API key not found');
293
+ return { key: publicRecord(rotated.next), previous: publicRecord(rotated.previous), secret: rawKey, secretShownOnce: true };
294
+ },
295
+ async revoke(id, input = {}) {
296
+ const identity = principalIdentity(input);
297
+ const existing = await store.getById(requiredString(id, 'key id'));
298
+ assertOwned(existing, identity);
299
+ const reason = requiredString(input.reason || 'revoked_by_owner', 'reason', 200);
300
+ const record = await store.revoke(existing.id, reason, {
301
+ action: 'revoked', keyId: existing.id, prefix: existing.prefix, reason,
302
+ ownerId: identity.ownerId, tenantId: identity.tenantId,
303
+ actorId: requiredString(input.actorId || identity.ownerId, 'actorId'),
304
+ });
305
+ return publicRecord(record);
306
+ },
307
+ async authenticate(value, input = {}) {
308
+ const parsed = parseRawKey(value);
309
+ if (!parsed || parsed.environment !== environment) return null;
310
+ const record = await store.getByPrefix(parsed.prefix);
311
+ if (!record || record.revokedAt || Date.parse(record.expiresAt) <= clock()) return null;
312
+ const actual = await deriveSecret(parsed.secret, record.salt);
313
+ const expected = Buffer.from(record.secretDigest, 'base64url');
314
+ if (actual.length !== expected.length || !timingSafeEqual(actual, expected)) return null;
315
+ const requiredScopes = [...new Set(input.requiredScopes || [])];
316
+ if (requiredScopes.some((scope) => !record.scopes.includes(scope))) return null;
317
+ if (input.tenantId && input.tenantId !== record.tenantId) return null;
318
+ const usedAt = clockIso(clock);
319
+ if (!await store.markUsed(record.id, usedAt)) return null;
320
+ return Object.freeze({
321
+ subject: record.ownerId,
322
+ ownerId: record.ownerId,
323
+ tenantId: record.tenantId,
324
+ keyId: record.id,
325
+ scopes: [...record.scopes],
326
+ authentication: 'api_key',
327
+ });
328
+ },
329
+ async audit(input = {}) {
330
+ const identity = principalIdentity(input);
331
+ const limit = boundedInteger(input.limit, 'limit', 1, 500, 100);
332
+ return store.audit(identity.ownerId, identity.tenantId, limit);
333
+ },
334
+ });
335
+ }
336
+
337
+ export function apiKeyFromAuthorization(value) {
338
+ if (!value || !String(value).startsWith('Bearer ')) return null;
339
+ const candidate = String(value).slice(7);
340
+ return KEY_PATTERN.test(candidate) ? candidate : null;
341
+ }