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
@@ -23,6 +23,18 @@ import {
23
23
  normalizeDependencyEvidence,
24
24
  readinessEvidenceRefs,
25
25
  } from '../../../packages/adapters/src/index.js';
26
+ import { attachFederationServeMode } from './federation-runtime.mjs';
27
+ export {
28
+ AuthenticatedWebSocketFederationTransport,
29
+ FEDERATION_SERVE_PATH,
30
+ FEDERATION_STORAGE_ADAPTER_SCHEMA,
31
+ FederationServeRuntime,
32
+ FederationServeTransport,
33
+ MemoryFederationStorageAdapter,
34
+ attachFederationServeMode,
35
+ createMemoryFederationStorageAdapter,
36
+ validateFederationStorageAdapter,
37
+ } from './federation-runtime.mjs';
26
38
 
27
39
  const SERVICE_SCHEMA = 'enigma.relay_service.v1';
28
40
  const PAIRING_CHALLENGE_SCHEMA = 'enigma.pairing_challenge.v1';
@@ -30,6 +42,7 @@ const PAIRING_COMPLETION_SCHEMA = 'enigma.pairing_completion.v1';
30
42
  const CLIENT_AUTH_SCHEMA = 'enigma.relay_client_authorization.v1';
31
43
  const RELAY_STATE_SCHEMA = 'enigma.relay_state.v1';
32
44
  const RELAY_STATE_VERSION = 1;
45
+ export const RELAY_STATE_ADAPTER_SCHEMA = 'enigma.relay_state_adapter.v1';
33
46
  const DEFAULT_NOW = '1970-01-01T00:00:00.000Z';
34
47
  const DEFAULT_CHALLENGE_TTL_MS = 5 * 60 * 1000;
35
48
  const MAX_JSON_BODY_BYTES = 64 * 1024;
@@ -182,6 +195,7 @@ const RELAY_STATE_KEYS = new Set([
182
195
  'relay_store',
183
196
  'relay_records',
184
197
  'witness_log',
198
+ 'pending_pairing_challenges',
185
199
  'completed_pairings',
186
200
  'authorization'
187
201
  ]);
@@ -218,6 +232,16 @@ const RELAY_STATE_PAIRING_KEYS = new Set([
218
232
  'issued_at',
219
233
  'signature'
220
234
  ]);
235
+ const RELAY_STATE_CHALLENGE_KEYS = new Set([
236
+ 'schema',
237
+ 'challenge_id',
238
+ 'relay_node_id',
239
+ 'client_public_key',
240
+ 'client_public_key_hash',
241
+ 'nonce',
242
+ 'issued_at',
243
+ 'expires_at'
244
+ ]);
221
245
  const SIGNATURE_KEYS = new Set(['alg', 'key_id', 'value']);
222
246
 
223
247
  function isPlainRecord(value) {
@@ -554,7 +578,7 @@ function relayProductionReadinessFromOptions(options) {
554
578
  options.storage,
555
579
  options.storage_ref,
556
580
  options.storageRef,
557
- envValue('ENIGMA_EXTERNAL_STORAGE_DSN', 'ENIGMA_EXTERNAL_STORAGE_DSN_FILE', 'ENIGMA_EXTERNAL_STORAGE_REF', 'ENIGMA_RELAY_STORAGE_REF', 'ENIGMA_DURABLE_STORAGE_REF')
581
+ envValue('ENIGMA_EXTERNAL_STORAGE_DSN', 'ENIGMA_EXTERNAL_STORAGE_REF', 'ENIGMA_RELAY_STORAGE_REF', 'ENIGMA_DURABLE_STORAGE_REF')
558
582
  ),
559
583
  kms_or_secret_custody: firstDefined(
560
584
  options.kms_or_secret_custody,
@@ -578,7 +602,7 @@ function relayProductionReadinessFromOptions(options) {
578
602
  options.auditSink,
579
603
  options.log_sink,
580
604
  options.logSink,
581
- envValue('ENIGMA_SIEM_EXPORT_ENDPOINT', 'ENIGMA_SIEM_EXPORT_ENDPOINT_FILE', 'ENIGMA_SIEM_REF', 'ENIGMA_AUDIT_SINK_REF', 'ENIGMA_LOG_SINK_REF')
605
+ envValue('ENIGMA_SIEM_EXPORT_ENDPOINT', 'ENIGMA_SIEM_REF', 'ENIGMA_AUDIT_SINK_REF', 'ENIGMA_LOG_SINK_REF')
582
606
  ),
583
607
  backup_restore: firstDefined(
584
608
  options.backup_restore,
@@ -588,7 +612,7 @@ function relayProductionReadinessFromOptions(options) {
588
612
  options.backupTarget,
589
613
  options.restore_target,
590
614
  options.restoreTarget,
591
- envValue('ENIGMA_BACKUP_TARGET_URI', 'ENIGMA_BACKUP_TARGET_URI_FILE', 'ENIGMA_BACKUP_TARGET_REF', 'ENIGMA_RESTORE_TARGET_REF')
615
+ envValue('ENIGMA_BACKUP_TARGET_URI', 'ENIGMA_BACKUP_TARGET_REF', 'ENIGMA_RESTORE_TARGET_REF')
592
616
  ),
593
617
  monitoring: firstDefined(
594
618
  options.monitoring,
@@ -839,6 +863,22 @@ function relayReadiness(state) {
839
863
  : [
840
864
  relayReadinessCheck('non_production_local_mode', localMode, 'relay readiness is only green for non-production local mode')
841
865
  ];
866
+ const federationStatus = state.federationRuntime?.status();
867
+ if (federationStatus) {
868
+ checks.push(...federationStatus.checks.map((check) => relayReadinessCheck(
869
+ check.check,
870
+ check.ok,
871
+ `${check.check.replaceAll('_', ' ')} is not ready`
872
+ )));
873
+ }
874
+ const persistenceStatus = state.relayStatePersistence?.status();
875
+ if (persistenceStatus) {
876
+ checks.push(...persistenceStatus.checks.map((check) => relayReadinessCheck(
877
+ check.check,
878
+ check.ok,
879
+ `${check.check.replaceAll('_', ' ')} is not ready`
880
+ )));
881
+ }
842
882
  const missingChecks = checks.filter((check) => check.ok !== true).map((check) => check.check);
843
883
  const ready = checks.every((check) => check.ok === true);
844
884
  return {
@@ -1046,7 +1086,8 @@ function serviceHealth(state) {
1046
1086
  authorization: {
1047
1087
  mode: state.allowUnauthenticated === true ? 'unauthenticated_local_demo' : 'paired_client_signature_required',
1048
1088
  endpoints: ['/relay/push', '/relay/pull', '/witness/checkpoint']
1049
- }
1089
+ },
1090
+ federation: state.federationRuntime?.status() ?? { enabled: false, ready: false }
1050
1091
  };
1051
1092
  }
1052
1093
 
@@ -1201,6 +1242,25 @@ function hydrateRelayStoreState(storeState, recordsState, node) {
1201
1242
  }
1202
1243
  return relayStore;
1203
1244
  }
1245
+ function validatePendingPairingChallenge(challengeState, node) {
1246
+ const challenge = assertPlainRecord(cloneJson(challengeState, 'pending pairing challenge state'), 'pending pairing challenge state');
1247
+ assertOnlyKeys(challenge, RELAY_STATE_CHALLENGE_KEYS, 'pending pairing challenge state');
1248
+ if (challenge.schema !== PAIRING_CHALLENGE_SCHEMA) throw new Error('pending pairing challenge schema mismatch');
1249
+ if (challenge.relay_node_id !== node.node_id) throw new Error('pending pairing challenge relay_node_id mismatch');
1250
+ assertString(challenge.client_public_key, 'pending pairing challenge client_public_key');
1251
+ if (challenge.client_public_key_hash !== `sha256:${sha256Hex(challenge.client_public_key)}`) {
1252
+ throw new Error('pending pairing challenge client_public_key_hash mismatch');
1253
+ }
1254
+ assertString(challenge.nonce, 'pending pairing challenge nonce');
1255
+ assertIsoDate(challenge.issued_at, 'pending pairing challenge issued_at');
1256
+ assertIsoDate(challenge.expires_at, 'pending pairing challenge expires_at');
1257
+ const expectedChallengeId = `pch_${sha256Hex(canonicalize({
1258
+ clientPublicKey: challenge.client_public_key,
1259
+ nonce: challenge.nonce
1260
+ })).slice(0, 32)}`;
1261
+ if (challenge.challenge_id !== expectedChallengeId) throw new Error('pending pairing challenge challenge_id mismatch');
1262
+ return Object.freeze({ challenge: Object.freeze(challenge), publicKey: challenge.client_public_key });
1263
+ }
1204
1264
 
1205
1265
  function authorizationFromSnapshot(authorization) {
1206
1266
  const record = assertPlainRecord(authorization, 'relay authorization state');
@@ -1221,6 +1281,9 @@ export function serializeRelayState(state) {
1221
1281
  const records = [...state.relayStore.records.values()].map((record) => cloneJson(record, 'relay record state'));
1222
1282
  const witnessLog = state.witnessLog.map((checkpoint) => cloneJson(checkpoint, 'witness checkpoint state'));
1223
1283
  const completedPairings = [...state.pairings.values()].map((pairing) => cloneJson(pairing, 'completed pairing state'));
1284
+ const pendingPairingChallenges = state.persistPendingPairingChallenges === true
1285
+ ? [...state.pairingChallenges.values()].map((pending) => cloneJson(pending.challenge, 'pending pairing challenge state'))
1286
+ : null;
1224
1287
  const snapshot = {
1225
1288
  schema: RELAY_STATE_SCHEMA,
1226
1289
  version: RELAY_STATE_VERSION,
@@ -1237,6 +1300,7 @@ export function serializeRelayState(state) {
1237
1300
  },
1238
1301
  relay_records: records,
1239
1302
  witness_log: witnessLog,
1303
+ ...(pendingPairingChallenges ? { pending_pairing_challenges: pendingPairingChallenges } : {}),
1240
1304
  completed_pairings: completedPairings,
1241
1305
  authorization: {
1242
1306
  mode: relayAuthorizationMode(state),
@@ -1260,6 +1324,14 @@ export function hydrateRelayState(snapshot, options = {}) {
1260
1324
  const node = hydrateNodeState(record.node);
1261
1325
  const relayStore = hydrateRelayStoreState(record.relay_store, record.relay_records, node);
1262
1326
  const witnessLog = validateWitnessLog(record.witness_log, node);
1327
+ const pendingChallengeStates = record.pending_pairing_challenges ?? [];
1328
+ if (!Array.isArray(pendingChallengeStates)) throw new TypeError('pending_pairing_challenges must be an array');
1329
+ const pairingChallenges = new Map();
1330
+ for (const challengeState of pendingChallengeStates) {
1331
+ const pending = validatePendingPairingChallenge(challengeState, node);
1332
+ if (pairingChallenges.has(pending.challenge.challenge_id)) throw new Error('duplicate pending pairing challenge_id in state');
1333
+ pairingChallenges.set(pending.challenge.challenge_id, pending);
1334
+ }
1263
1335
  if (!Array.isArray(record.completed_pairings)) throw new TypeError('completed_pairings must be an array');
1264
1336
  const pairings = new Map();
1265
1337
  for (const pairingState of record.completed_pairings) {
@@ -1269,6 +1341,7 @@ export function hydrateRelayState(snapshot, options = {}) {
1269
1341
  }
1270
1342
 
1271
1343
  const state = createRelayState({
1344
+ ...options,
1272
1345
  node,
1273
1346
  relayStore,
1274
1347
  witnessLog,
@@ -1277,7 +1350,7 @@ export function hydrateRelayState(snapshot, options = {}) {
1277
1350
  allowUnauthenticated: authorization.allow_unauthenticated
1278
1351
  });
1279
1352
  state.pairings = pairings;
1280
- state.pairingChallenges = new Map();
1353
+ state.pairingChallenges = pairingChallenges;
1281
1354
  return state;
1282
1355
  }
1283
1356
 
@@ -1360,19 +1433,246 @@ export function createRelayState(options = {}) {
1360
1433
  value: relayProductionReadinessFromOptions(options),
1361
1434
  writable: true,
1362
1435
  configurable: true
1436
+ },
1437
+ persistPendingPairingChallenges: {
1438
+ value: options.persistPendingPairingChallenges === true || options.persist_pending_pairing_challenges === true,
1439
+ writable: false,
1440
+ configurable: true
1363
1441
  }
1364
1442
  });
1365
1443
  return state;
1366
1444
  }
1367
1445
 
1446
+ function relayFederationOptions(options, state) {
1447
+ const configured = options.federation ?? options.federationServe;
1448
+ if (!configured) return null;
1449
+ const readiness = state.production_readiness ?? {};
1450
+ return {
1451
+ ...configured,
1452
+ mode: configured.mode ?? state.mode,
1453
+ authRef: configured.authRef ?? readiness.runtime_auth,
1454
+ kmsRef: configured.kmsRef ?? readiness.kms_or_secret_custody,
1455
+ };
1456
+ }
1457
+
1458
+ function relayReplicaCount(options) {
1459
+ const value = firstDefined(
1460
+ options.replicaCount,
1461
+ options.replica_count,
1462
+ envValue('ENIGMA_RELAY_REPLICA_COUNT', 'ENIGMA_REPLICA_COUNT')
1463
+ );
1464
+ if (value === undefined) return 1;
1465
+ const count = Number(value);
1466
+ if (!Number.isSafeInteger(count) || count < 1) throw new TypeError('relay replica count must be a positive safe integer');
1467
+ return count;
1468
+ }
1469
+
1470
+ export function validateRelayStateAdapter(adapter, { production = false, replicaCount = 1 } = {}) {
1471
+ if (!isPlainRecord(adapter)) throw new TypeError('relay state adapter is required');
1472
+ if (adapter.schema !== RELAY_STATE_ADAPTER_SCHEMA) {
1473
+ throw new TypeError(`relay state adapter schema must be ${RELAY_STATE_ADAPTER_SCHEMA}`);
1474
+ }
1475
+ if (!nonEmptyString(adapter.ref)) throw new TypeError('relay state adapter ref is required');
1476
+ if (typeof adapter.isReady !== 'function' || typeof adapter.transaction !== 'function') {
1477
+ throw new TypeError('relay state adapter must implement isReady() and transaction(operation, options)');
1478
+ }
1479
+ if (production && (adapter.durable !== true || adapter.encryptedAtRest !== true)) {
1480
+ throw new TypeError('production relay state adapter must be durable and encrypted at rest');
1481
+ }
1482
+ if (replicaCount > 1 && adapter.supportsMultiReplica !== true) {
1483
+ throw new TypeError('multiple relay replicas require an atomic multi-replica relay state adapter');
1484
+ }
1485
+ return adapter;
1486
+ }
1487
+
1488
+ function createBufferedRelayResponse() {
1489
+ const response = {
1490
+ statusCode: 200,
1491
+ headers: {},
1492
+ body: '',
1493
+ writeHead(statusCode, headers = {}) {
1494
+ response.statusCode = statusCode;
1495
+ response.headers = { ...headers };
1496
+ return response;
1497
+ },
1498
+ end(body = '') {
1499
+ response.body = body === undefined ? '' : Buffer.isBuffer(body) ? Buffer.from(body) : String(body);
1500
+ response.ended = true;
1501
+ return response;
1502
+ }
1503
+ };
1504
+ return response;
1505
+ }
1506
+
1507
+ function flushBufferedRelayResponse(buffered, response) {
1508
+ if (!buffered?.ended || !Number.isInteger(buffered.statusCode)) {
1509
+ throw new Error('relay state transaction completed without an HTTP response');
1510
+ }
1511
+ response.writeHead(buffered.statusCode, buffered.headers);
1512
+ response.end(buffered.body);
1513
+ }
1514
+
1515
+ class RelayStatePersistenceCoordinator {
1516
+ constructor(adapter, initialState, options, replicaCount) {
1517
+ this.adapter = adapter;
1518
+ Object.defineProperty(initialState, 'persistPendingPairingChallenges', {
1519
+ value: true,
1520
+ writable: false,
1521
+ configurable: true
1522
+ });
1523
+ this.options = { ...options, persistPendingPairingChallenges: true };
1524
+ this.initialSnapshot = serializeRelayState(initialState);
1525
+ this.replicaCount = replicaCount;
1526
+ this.ready = false;
1527
+ this.lastError = null;
1528
+ this.state = initialState;
1529
+ }
1530
+
1531
+ async refresh() {
1532
+ try {
1533
+ this.ready = await this.adapter.isReady() === true;
1534
+ if (!this.ready) throw new Error('relay state adapter is not ready');
1535
+ this.lastError = null;
1536
+ } catch (error) {
1537
+ this.ready = false;
1538
+ this.lastError = error instanceof Error ? error.message : 'relay state adapter failed';
1539
+ }
1540
+ return this.ready;
1541
+ }
1542
+
1543
+ status() {
1544
+ return {
1545
+ ready: this.ready,
1546
+ ref: this.adapter.ref,
1547
+ durable: this.adapter.durable === true,
1548
+ encrypted_at_rest: this.adapter.encryptedAtRest === true,
1549
+ supports_multi_replica: this.adapter.supportsMultiReplica === true,
1550
+ replica_count: this.replicaCount,
1551
+ last_error: this.lastError,
1552
+ checks: [
1553
+ { check: 'relay_state_adapter', ok: this.ready === true },
1554
+ { check: 'relay_state_durable', ok: this.adapter.durable === true },
1555
+ { check: 'relay_state_encrypted_at_rest', ok: this.adapter.encryptedAtRest === true },
1556
+ { check: 'relay_state_replica_safety', ok: this.replicaCount === 1 || this.adapter.supportsMultiReplica === true }
1557
+ ]
1558
+ };
1559
+ }
1560
+
1561
+ decorate(state, federationRuntime) {
1562
+ Object.defineProperties(state, {
1563
+ relayStatePersistence: {
1564
+ value: this,
1565
+ writable: false,
1566
+ configurable: true
1567
+ },
1568
+ ...(federationRuntime ? {
1569
+ federationRuntime: {
1570
+ value: federationRuntime,
1571
+ writable: false,
1572
+ configurable: true
1573
+ }
1574
+ } : {})
1575
+ });
1576
+ return state;
1577
+ }
1578
+
1579
+ async handle(request, response, federationRuntime) {
1580
+ const path = new URL(request.url ?? '/', 'http://enigma.local').pathname;
1581
+ if (path === '/livez') {
1582
+ await handleRelayRequest(this.decorate(this.state, federationRuntime), request, response);
1583
+ return;
1584
+ }
1585
+ if (!await this.refresh()) {
1586
+ jsonResponse(response, 503, fail('relay durable state dependency is unavailable'));
1587
+ return;
1588
+ }
1589
+ try {
1590
+ const value = await this.adapter.transaction(async (storedSnapshot) => {
1591
+ const state = storedSnapshot
1592
+ ? hydrateRelayState(storedSnapshot, this.options)
1593
+ : hydrateRelayState(this.initialSnapshot, this.options);
1594
+ this.decorate(state, federationRuntime);
1595
+ const buffered = createBufferedRelayResponse();
1596
+ await handleRelayRequest(state, request, buffered);
1597
+ return {
1598
+ snapshot: serializeRelayState(state),
1599
+ value: { state, response: buffered }
1600
+ };
1601
+ }, { initialSnapshot: this.initialSnapshot });
1602
+ if (!value?.state || !value?.response) throw new Error('relay state adapter transaction returned an invalid result');
1603
+ this.state = value.state;
1604
+ flushBufferedRelayResponse(value.response, response);
1605
+ } catch (error) {
1606
+ this.ready = false;
1607
+ this.lastError = error instanceof Error ? error.message : 'relay state transaction failed';
1608
+ jsonResponse(response, 503, fail('relay durable state transaction failed'));
1609
+ }
1610
+ }
1611
+ }
1612
+
1368
1613
  export function createRelayServer(options = {}) {
1369
- const state = options.state ?? createRelayState(options);
1614
+ const initialState = options.state ?? createRelayState(options);
1615
+ const production = isProductionLikeState(initialState);
1616
+ const replicaCount = relayReplicaCount(options);
1617
+ const configuredAdapter = options.relayStateAdapter ?? options.relay_state_adapter;
1618
+ if (production && !configuredAdapter) {
1619
+ throw new TypeError('production relay serve mode requires a durable relay state adapter');
1620
+ }
1621
+ const stateAdapter = configuredAdapter
1622
+ ? validateRelayStateAdapter(configuredAdapter, { production, replicaCount })
1623
+ : null;
1624
+ let persistence = null;
1625
+ let federationRuntime = null;
1626
+ const localStateFile = !production && !stateAdapter && typeof options.stateFile === 'string' && options.stateFile
1627
+ ? options.stateFile
1628
+ : null;
1629
+ const saveLocalState = typeof options.saveRelayState === 'function'
1630
+ ? options.saveRelayState
1631
+ : saveRelayStateToFile;
1632
+ let localSaveQueue = Promise.resolve();
1370
1633
  const server = createServer((req, res) => {
1371
- handleRelayRequest(state, req, res).catch((error) => {
1372
- jsonResponse(res, 500, fail(error instanceof Error ? error.message : 'relay request failed'));
1634
+ let durableTask;
1635
+ if (persistence) {
1636
+ durableTask = persistence.handle(req, res, federationRuntime);
1637
+ } else if (localStateFile) {
1638
+ const buffered = createBufferedRelayResponse();
1639
+ durableTask = handleRelayRequest(initialState, req, buffered).then(async () => {
1640
+ localSaveQueue = localSaveQueue.then(() => saveLocalState(initialState, localStateFile));
1641
+ await localSaveQueue;
1642
+ flushBufferedRelayResponse(buffered, res);
1643
+ });
1644
+ } else {
1645
+ durableTask = handleRelayRequest(initialState, req, res);
1646
+ }
1647
+ durableTask.catch((error) => {
1648
+ server.emit('statePersistenceError', error);
1649
+ if (!res.headersSent) jsonResponse(res, 500, fail(error instanceof Error ? error.message : 'relay request failed'));
1650
+ });
1651
+ });
1652
+ const federationOptions = relayFederationOptions(options, initialState);
1653
+ if (production && !federationOptions) {
1654
+ throw new TypeError('production relay serve mode requires federation bridge configuration');
1655
+ }
1656
+ if (federationOptions) {
1657
+ federationRuntime = attachFederationServeMode(server, federationOptions);
1658
+ Object.defineProperty(initialState, 'federationRuntime', {
1659
+ value: federationRuntime,
1660
+ writable: false,
1661
+ configurable: true,
1373
1662
  });
1663
+ server.federationRuntime = federationRuntime;
1664
+ }
1665
+ if (stateAdapter) {
1666
+ persistence = new RelayStatePersistenceCoordinator(stateAdapter, initialState, options, replicaCount);
1667
+ persistence.decorate(initialState, federationRuntime);
1668
+ server.relayStatePersistence = persistence;
1669
+ server.once('listening', () => void persistence.refresh());
1670
+ }
1671
+ Object.defineProperty(server, 'relayState', {
1672
+ configurable: true,
1673
+ get: () => persistence?.state ?? initialState,
1374
1674
  });
1375
- server.relayState = state;
1675
+ server.waitForLocalPersistence = () => localSaveQueue;
1376
1676
  return server;
1377
1677
  }
1378
1678
 
@@ -1396,6 +1696,7 @@ export async function handleRelayRequest(state, req, res) {
1396
1696
  }
1397
1697
 
1398
1698
  if (method === 'GET' && url.pathname === '/readyz') {
1699
+ if (state.federationRuntime) await state.federationRuntime.refreshDependencies();
1399
1700
  const readiness = relayReadiness(state);
1400
1701
  jsonResponse(res, readiness.statusCode, readiness.body);
1401
1702
  return;