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.
- package/README.md +76 -24
- package/apps/cli/bin/enigma-desktop.mjs +140 -0
- package/apps/cli/bin/enigma-terminal.mjs +78 -0
- package/apps/cli/bin/enigma.mjs +1923 -285
- package/apps/desktop/electron-main.cjs +217 -0
- package/apps/desktop/package.json +12 -0
- package/apps/desktop/src/app.js +264 -7
- package/apps/desktop/src/index.html +3514 -1373
- package/apps/desktop/src/launch-electron.mjs +51 -0
- package/apps/desktop/src/server.mjs +2914 -0
- package/apps/desktop/src/styles.css +2972 -260
- package/apps/desktop/src/zk-browser-prove.mjs +53 -0
- package/apps/desktop/src/zk-state.mjs +1789 -0
- package/apps/gateway/bin/enigma-gateway.mjs +102 -5
- package/apps/gateway/src/server.mjs +271 -8
- package/apps/ios/EnigmaCore/Package.swift +12 -0
- package/apps/ios/EnigmaCore/Sources/EnigmaCore/EnigmaAPIClient.swift +227 -0
- package/apps/ios/EnigmaCore/Sources/EnigmaCore/Models.swift +278 -0
- package/apps/ios/EnigmaCore/Sources/EnigmaCore/PKCE.swift +96 -0
- package/apps/ios/EnigmaCore/Sources/EnigmaCore/PrivacyMinimizer.swift +187 -0
- package/apps/ios/EnigmaCore/Sources/EnigmaCore/ToolModels.swift +129 -0
- package/apps/ios/EnigmaCore/Tests/EnigmaCoreTests/EnigmaCoreTests.swift +42 -0
- package/apps/ios/EnigmaIOS/Enigma/AppModel.swift +346 -0
- package/apps/ios/EnigmaIOS/Enigma/Assets.xcassets/AccentColor.colorset/Contents.json +12 -0
- package/apps/ios/EnigmaIOS/Enigma/Assets.xcassets/AppIcon.appiconset/Contents.json +11 -0
- package/apps/ios/EnigmaIOS/Enigma/Assets.xcassets/AppIcon.appiconset/EnigmaAppIcon.png +0 -0
- package/apps/ios/EnigmaIOS/Enigma/Assets.xcassets/Contents.json +3 -0
- package/apps/ios/EnigmaIOS/Enigma/Assets.xcassets/LaunchBackground.colorset/Contents.json +12 -0
- package/apps/ios/EnigmaIOS/Enigma/ChatView.swift +181 -0
- package/apps/ios/EnigmaIOS/Enigma/CouncilView.swift +78 -0
- package/apps/ios/EnigmaIOS/Enigma/CreateView.swift +152 -0
- package/apps/ios/EnigmaIOS/Enigma/EnigmaApp.swift +52 -0
- package/apps/ios/EnigmaIOS/Enigma/Info.plist +52 -0
- package/apps/ios/EnigmaIOS/Enigma/NaturalLanguagePrivacyTagger.swift +26 -0
- package/apps/ios/EnigmaIOS/Enigma/OAuthClient.swift +321 -0
- package/apps/ios/EnigmaIOS/Enigma/OnboardingView.swift +105 -0
- package/apps/ios/EnigmaIOS/Enigma/PrivateVaultView.swift +275 -0
- package/apps/ios/EnigmaIOS/Enigma/SecureStore.swift +76 -0
- package/apps/ios/EnigmaIOS/Enigma/SettingsView.swift +60 -0
- package/apps/ios/EnigmaIOS/Enigma/Theme.swift +80 -0
- package/apps/ios/EnigmaIOS/EnigmaIOS.xcodeproj/project.pbxproj +211 -0
- package/apps/ios/EnigmaIOS/EnigmaIOS.xcodeproj/xcshareddata/xcschemes/Enigma.xcscheme +23 -0
- package/apps/native-host/README.md +19 -8
- package/apps/native-host/bin/enigma-native-host.mjs +229 -13
- package/apps/relay/bin/enigma-relay.mjs +103 -5
- package/apps/relay/src/federation-runtime.mjs +618 -0
- package/apps/relay/src/server.mjs +310 -9
- package/apps/verifier/bin/enigma-verify.mjs +327 -11
- package/cortex-v3/circuits/build/intent_vk_bytes.json +35 -0
- package/cortex-v3/circuits/build/sale_vk_bytes.json +35 -0
- package/cortex-v3/circuits/build/vk_bytes.json +32 -0
- package/cortex-v3/proving-assets.json +64 -0
- package/cortex-v3/zk/BUILD-CONTRACT.md +87 -0
- package/cortex-v3/zk/action-transition-vk.json +119 -0
- package/cortex-v3/zk/alias-adversarial.test.mjs +220 -0
- package/cortex-v3/zk/groth16-verify-child.mjs +17 -0
- package/cortex-v3/zk/intent-witness.mjs +365 -0
- package/cortex-v3/zk/intent-witness.test.mjs +485 -0
- package/cortex-v3/zk/proving-assets.mjs +203 -0
- package/cortex-v3/zk/sale-witness.mjs +783 -0
- package/cortex-v3/zk/sale-witness.test.mjs +784 -0
- package/cortex-v3/zk/sealed-sale-release-vk.json +119 -0
- package/cortex-v3/zk/settlement-evidence.mjs +722 -0
- package/cortex-v3/zk/setup-intent.mjs +688 -0
- package/cortex-v3/zk/setup-sale.mjs +666 -0
- package/cortex-v3/zk/setup.mjs +594 -0
- package/cortex-v3/zk/witness.mjs +184 -0
- package/cortex-v3/zk/zk-codec.mjs +232 -0
- package/cortex-v3/zk/zk-codec.test.mjs +293 -0
- package/cortex-v3/zk/zk-settle.mjs +370 -0
- package/cortex-v3/zk/zk-tree.mjs +256 -0
- package/cortex-v3/zk/zk-tree.test.mjs +419 -0
- package/deploy/docker-compose.local-production-simulation.yml +36 -0
- package/docs/browser-extension-install.md +8 -6
- package/docs/client-connectors.md +15 -11
- package/docs/developer-ecosystem.md +15 -13
- package/docs/enigma-memory-ready-conformance.md +11 -9
- package/docs/install-anywhere.md +61 -28
- package/docs/installers-and-desktop.md +8 -7
- package/docs/novelty-invention-candidates.md +161 -161
- package/docs/proof-network-claim-boundaries.md +320 -318
- package/examples/01-quickstart-agent/index.mjs +49 -0
- package/examples/01_agent_memory_quickstart.mjs +57 -0
- package/examples/02-multi-agent-swarm/index.mjs +57 -0
- package/examples/02_cross_model_passport.mjs +64 -0
- package/examples/03-langchain-memory/index.mjs +41 -0
- package/examples/03_poseidon_commitment_verification.mjs +71 -0
- package/examples/04-python-trading-agent/trader.py +49 -0
- package/examples/README.md +27 -0
- package/examples/ci/github-actions.yml +7 -2
- package/package.json +142 -11
- package/packages/adapters/PACKAGE_CONTRACT.md +1 -1
- package/packages/connectors/src/index.js +196 -4
- package/packages/connectors/swarm-router.mjs +168 -0
- package/packages/core/src/index.js +248 -1
- package/packages/core/src/version.mjs +7 -0
- package/packages/dev-tools/package.json +19 -0
- package/packages/dev-tools/src/index.js +4 -0
- package/packages/dev-tools/src/memory-benchmark-suite.js +112 -0
- package/packages/dev-tools/src/swarm-simulator.js +101 -0
- package/packages/dev-tools/src/vault-inspector.js +114 -0
- package/packages/dev-tools/src/vector-benchmark.js +100 -0
- package/packages/developer-platform/src/access-credentials.js +341 -0
- package/packages/developer-platform/src/http.js +132 -0
- package/packages/developer-platform/src/index.js +4 -0
- package/packages/developer-platform/src/usage-http.js +60 -0
- package/packages/developer-platform/src/usage.js +295 -0
- package/packages/enclave-runtime/attestation.mjs +159 -0
- package/packages/enclave-runtime/index.mjs +47 -0
- package/packages/enclave-runtime/session-manager.mjs +253 -0
- package/packages/enclave-runtime/zeroization-proof.mjs +227 -0
- package/packages/enigma-reflex/package.json +14 -0
- package/packages/enigma-reflex/src/index.js +204 -0
- package/packages/enigma-reflex/training/generate-dataset.mjs +40 -0
- package/packages/enigma-reflex/training/requirements.txt +8 -0
- package/packages/enigma-reflex/training/train.py +314 -0
- package/packages/enigma-weave/LICENSE +22 -0
- package/packages/enigma-weave/UPSTREAM.json +21 -0
- package/packages/enigma-weave/package.json +14 -0
- package/packages/enigma-weave/src/index.js +286 -0
- package/packages/hosted-cloud/src/index.js +80 -5
- package/packages/importers/src/index.js +432 -0
- package/packages/inference-runtime/src/browser.js +401 -0
- package/packages/inference-runtime/src/chat.js +265 -0
- package/packages/inference-runtime/src/code.js +407 -0
- package/packages/inference-runtime/src/contracts.js +162 -0
- package/packages/inference-runtime/src/http.js +232 -0
- package/packages/inference-runtime/src/image.js +186 -0
- package/packages/inference-runtime/src/index.js +10 -0
- package/packages/inference-runtime/src/model-router.js +320 -0
- package/packages/inference-runtime/src/platform.js +125 -0
- package/packages/inference-runtime/src/privacy.js +400 -0
- package/packages/inference-runtime/src/video.js +253 -0
- package/packages/mcp-server/README.md +22 -6
- package/packages/mcp-server/bin/enigma-mcp.mjs +2 -1
- package/packages/mcp-server/src/index.js +1418 -105
- package/packages/mcp-server/src/oauth.js +561 -0
- package/packages/mcp-server/src/private-handoff.js +84 -0
- package/packages/mcp-server/src/remote-http.js +273 -0
- package/packages/mcp-server/src/remote-policy.js +72 -0
- package/packages/mcp-server/swarm-bridge.mjs +361 -0
- package/packages/mesh/index.d.ts +283 -0
- package/packages/mesh/package.json +23 -0
- package/packages/mesh/src/crypto.js +189 -0
- package/packages/mesh/src/federation-packets.js +353 -0
- package/packages/mesh/src/gossip.js +311 -0
- package/packages/mesh/src/index.js +6 -0
- package/packages/mesh/src/protocol.js +255 -0
- package/packages/mesh/src/router.js +279 -0
- package/packages/mesh/src/transport.js +306 -0
- package/packages/passport/src/index.js +426 -1
- package/packages/private-economy/src/credits-http.js +100 -0
- package/packages/private-economy/src/credits.js +447 -0
- package/packages/private-economy/src/index.js +5 -0
- package/packages/private-economy/src/payments-http.js +120 -0
- package/packages/private-economy/src/payments.js +509 -0
- package/packages/private-economy/src/x402.js +346 -0
- package/packages/proof-network/PACKAGE_CONTRACT.md +21 -0
- package/packages/rag/index.d.ts +182 -0
- package/packages/rag/models/Xenova/all-MiniLM-L6-v2/THIRD_PARTY_LICENSES.txt +207 -0
- package/packages/rag/models/Xenova/all-MiniLM-L6-v2/config.json +25 -0
- package/packages/rag/models/Xenova/all-MiniLM-L6-v2/onnx/model_quantized.onnx +0 -0
- package/packages/rag/models/Xenova/all-MiniLM-L6-v2/sha256-manifest.json +28 -0
- package/packages/rag/models/Xenova/all-MiniLM-L6-v2/tokenizer.json +30686 -0
- package/packages/rag/models/Xenova/all-MiniLM-L6-v2/tokenizer_config.json +15 -0
- package/packages/rag/package.json +27 -0
- package/packages/rag/src/blinded-search.js +109 -0
- package/packages/rag/src/bm25.js +169 -0
- package/packages/rag/src/embeddings.js +459 -0
- package/packages/rag/src/hybrid.js +76 -0
- package/packages/rag/src/index.js +38 -0
- package/packages/rag/src/reranker.js +61 -0
- package/packages/rag/src/research.js +107 -0
- package/packages/rag/src/vector-store.js +430 -0
- package/packages/rag/src/verify-model-artifacts.mjs +4 -0
- package/packages/sdk/index.d.ts +760 -0
- package/packages/sdk/package.json +33 -0
- package/packages/sdk/python/README.md +24 -0
- package/packages/sdk/python/enigma_sdk.py +250 -0
- package/packages/sdk/python/pyproject.toml +34 -0
- package/packages/sdk/python/requirements.txt +1 -0
- package/packages/sdk/python/setup.py +20 -0
- package/packages/sdk/src/federation/capability-grant.js +389 -0
- package/packages/sdk/src/federation/federation-router.js +360 -0
- package/packages/sdk/src/federation/ghostmesh-bridge.js +497 -0
- package/packages/sdk/src/federation/index.js +3 -0
- package/packages/sdk/src/index.js +1796 -0
- package/packages/sdk/src/intelligence/contradiction.js +337 -0
- package/packages/sdk/src/intelligence/decision-engine.js +155 -0
- package/packages/sdk/src/intelligence/index.js +4 -0
- package/packages/sdk/src/intelligence/ontology.js +122 -0
- package/packages/sdk/src/intelligence/temporal.js +123 -0
- package/packages/sdk/src/market-client.js +142 -0
- package/packages/sdk/src/mesh-client.js +110 -0
- package/packages/sdk/src/middleware/index.js +3 -0
- package/packages/sdk/src/middleware/langchain.js +159 -0
- package/packages/sdk/src/middleware/llamaindex.js +101 -0
- package/packages/sdk/src/middleware/vercel-ai.js +112 -0
- package/packages/sdk/src/rag-client.js +85 -0
- package/packages/sdk/src/swarm-orchestrator.js +260 -0
- package/packages/settlement/PACKAGE_CONTRACT.md +1 -1
- package/packages/snapcompact/THIRD_PARTY_LICENSES.txt +40 -0
- package/packages/snapcompact/assets/8x13-latin1.bdf +3837 -0
- package/packages/snapcompact/index.d.ts +284 -0
- package/packages/snapcompact/package.json +25 -0
- package/packages/snapcompact/src/index.js +716 -0
- package/packages/storage/PACKAGE_CONTRACT.md +1 -1
- package/packages/terminal-console/animations.mjs +240 -0
- package/packages/terminal-console/auto-anchor.mjs +220 -0
- package/packages/terminal-console/banner.mjs +91 -0
- package/packages/terminal-console/commands.mjs +459 -0
- package/packages/terminal-console/delegation.mjs +152 -0
- package/packages/terminal-console/index.mjs +5 -0
- package/packages/terminal-console/outbox.mjs +143 -0
- package/packages/terminal-console/phantom-bridge.mjs +637 -0
- package/packages/terminal-console/repl.mjs +136 -0
- package/packages/terminal-console/signer-store.mjs +130 -0
- package/packages/terminal-console/solana-rpc.mjs +214 -0
- package/packages/terminal-console/solana-transport.mjs +189 -0
- package/packages/terminal-tui/dashboard.mjs +214 -0
- package/packages/terminal-tui/index.mjs +28 -0
- package/packages/terminal-tui/merkle-tree-renderer.mjs +268 -0
- package/packages/terminal-tui/telemetry-hud.mjs +137 -0
- package/packages/vault/index.d.ts +449 -0
- package/packages/vault/package.json +27 -0
- package/packages/vault/src/e2ee.mjs +393 -0
- package/packages/vault/src/enclave.js +481 -0
- package/packages/vault/src/erasure.js +207 -0
- package/packages/vault/src/index.js +1018 -155
- package/packages/vault/src/persistence.js +307 -0
- package/packages/vault/src/poseidon.js +354 -0
- package/packages/vault/src/receipt.js +459 -0
- package/scripts/benchmark-optical-context.mjs +166 -0
- package/scripts/bootstrap-enigma.mjs +502 -0
- package/scripts/build-edge-backend-workers.mjs +20 -5
- package/scripts/build-goal-completion-audit.mjs +72 -25
- package/scripts/build-hosted-api-key-lifecycle.mjs +26 -8
- package/scripts/build-hosted-customer-lifecycle.mjs +20 -3
- package/scripts/build-hosted-probe-worker.mjs +19 -4
- package/scripts/build-installer-assets.mjs +41 -21
- package/scripts/build-operator-evidence-starter.mjs +59 -1
- package/scripts/build-production-backend-env-kit.mjs +2 -0
- package/scripts/build-production-unblocker.mjs +3 -0
- package/scripts/check.mjs +17 -3
- package/scripts/collect-hosted-backend-live-evidence.mjs +49 -12
- package/scripts/install-enigma-local.mjs +18 -5
- package/scripts/release-audit.mjs +65 -109
- package/scripts/release-provenance.mjs +6 -0
- package/scripts/run-backend-readiness-smoke.mjs +112 -10
- package/scripts/scan-secrets.mjs +1 -0
- package/scripts/simulate-production-env.mjs +7 -2
- package/scripts/validate-hosted-backend-live.mjs +112 -1
- package/specs/antibody-pack-v1.schema.json +95 -0
- package/specs/antigen-envelope-v1.schema.json +81 -0
- package/specs/boundary-manifest-v1.schema.json +35 -35
- package/specs/capsule-v1.schema.json +55 -55
- package/specs/claim-boundary-manifest-v1.schema.json +22 -22
- package/specs/claim-ledger-v1.schema.json +291 -0
- package/specs/context-passport-v1.schema.json +59 -0
- package/specs/deletion-tombstone-v1.schema.json +26 -26
- package/specs/evidence-packet-v1.schema.json +177 -0
- package/specs/hosted-backend-live-evidence-v1.schema.json +72 -3
- package/specs/immune-scan-report-v1.schema.json +112 -0
- package/specs/lifecycle-receipt-log-v1.schema.json +67 -0
- package/specs/memory-atom-v1.schema.json +59 -0
- package/specs/memory-event-v1.schema.json +42 -42
- package/specs/passport-v1.schema.json +50 -50
- package/specs/proof-of-non-use-v1.schema.json +65 -0
- package/specs/quarantine-record-v1.schema.json +126 -0
- package/specs/receipt-v1.schema.json +61 -61
- package/specs/state-checkpoint-v1.schema.json +37 -37
- package/specs/trust-bundle-v1.schema.json +56 -56
- package/specs/trust-card-v1.schema.json +119 -0
- package/docs/proof-network-launch-plan.md +0 -421
- package/packages/metering/PACKAGE_CONTRACT.md +0 -20
- 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', '
|
|
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', '
|
|
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', '
|
|
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 =
|
|
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
|
|
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
|
-
|
|
1372
|
-
|
|
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.
|
|
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;
|