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
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
// Enigma Cross-Agent Memory Federation Router.
|
|
2
|
+
// Mediates selective capability-based queries across sovereign agent memory clusters
|
|
3
|
+
// with cryptographic proof-of-possession, grantee identity binding, and nonce replay protection.
|
|
4
|
+
import {
|
|
5
|
+
verifyBlindedMemoryGrant,
|
|
6
|
+
createFederatedQueryRequest,
|
|
7
|
+
verifyFederatedQueryRequest,
|
|
8
|
+
getGrantSignablePayload,
|
|
9
|
+
FEDERATED_QUERY_REQUEST_SCHEMA,
|
|
10
|
+
} from './capability-grant.js';
|
|
11
|
+
import { normalizeMemoryType } from '../intelligence/ontology.js';
|
|
12
|
+
|
|
13
|
+
export const FEDERATION_ROUTER_STATE_SCHEMA = 'enigma.federation_router_state.v1';
|
|
14
|
+
export const MAX_ACTIVE_FEDERATION_GRANTS = 1_024;
|
|
15
|
+
export const MAX_REVOKED_FEDERATION_GRANTS = 4_096;
|
|
16
|
+
export const MAX_SEEN_FEDERATION_NONCES = 10_000;
|
|
17
|
+
|
|
18
|
+
export class EnigmaFederationRouter {
|
|
19
|
+
/**
|
|
20
|
+
* @param {Object} [options]
|
|
21
|
+
* @param {import('../index.js').Enigma} options.enigma - Sovereign agent instance
|
|
22
|
+
*/
|
|
23
|
+
constructor(options = {}) {
|
|
24
|
+
if (!options.enigma) throw new Error('enigma SDK instance is required for federation router');
|
|
25
|
+
this.enigma = options.enigma;
|
|
26
|
+
/** @type {Map<string, { grant: Object, usedCount: number }>} */
|
|
27
|
+
this.activeGrants = new Map();
|
|
28
|
+
/** @type {Set<string>} */
|
|
29
|
+
this.revokedGrantIds = new Set();
|
|
30
|
+
/** @type {Map<string, number>} */
|
|
31
|
+
this.seenNonces = new Map();
|
|
32
|
+
this.onStateMutation = typeof options.onStateMutation === 'function'
|
|
33
|
+
? options.onStateMutation
|
|
34
|
+
: null;
|
|
35
|
+
if (options.state !== undefined) this.importState(options.state);
|
|
36
|
+
}
|
|
37
|
+
setStateMutationHandler(handler) {
|
|
38
|
+
if (handler !== null && typeof handler !== 'function') {
|
|
39
|
+
throw new TypeError('federation state mutation handler must be a function or null');
|
|
40
|
+
}
|
|
41
|
+
this.onStateMutation = handler;
|
|
42
|
+
return this;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async persistStateMutation() {
|
|
46
|
+
if (this.onStateMutation) await this.onStateMutation(this.exportState());
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Registers a valid BlindedMemoryGrant in the local router.
|
|
52
|
+
* @param {Object} grant
|
|
53
|
+
* @returns {{ ok: boolean, grant_id: string }}
|
|
54
|
+
*/
|
|
55
|
+
registerGrant(grant) {
|
|
56
|
+
if (!grant || typeof grant !== 'object' || typeof grant.grant_id !== 'string') {
|
|
57
|
+
throw new Error('Cannot register invalid capability grant: grant object and grant_id are required');
|
|
58
|
+
}
|
|
59
|
+
if (this.revokedGrantIds.has(grant.grant_id)) {
|
|
60
|
+
throw new Error(`Cannot register revoked capability grant: ${grant.grant_id}`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const verdict = verifyBlindedMemoryGrant(grant);
|
|
64
|
+
if (!verdict.ok) {
|
|
65
|
+
throw new Error(`Cannot register invalid capability grant: ${verdict.errors.join('; ')}`);
|
|
66
|
+
}
|
|
67
|
+
const myPubHex = this.enigma.mesh.identity?.publicKey?.toString('hex');
|
|
68
|
+
if (myPubHex && grant.granter_pubkey !== myPubHex && grant.grantee_pubkey !== myPubHex) {
|
|
69
|
+
throw new Error(`Cannot register capability grant ${grant.grant_id}: local agent is neither granter nor grantee`);
|
|
70
|
+
}
|
|
71
|
+
if (!this.activeGrants.has(grant.grant_id) && this.activeGrants.size >= MAX_ACTIVE_FEDERATION_GRANTS) {
|
|
72
|
+
throw new Error(`Cannot register capability grant: active grant capacity ${MAX_ACTIVE_FEDERATION_GRANTS} reached`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const existing = this.activeGrants.get(grant.grant_id);
|
|
76
|
+
if (existing) {
|
|
77
|
+
const sameGrant = getGrantSignablePayload(existing.grant) === getGrantSignablePayload(grant) &&
|
|
78
|
+
existing.grant.signature === grant.signature;
|
|
79
|
+
if (!sameGrant) throw new Error(`Capability grant ID collision for ${grant.grant_id}`);
|
|
80
|
+
return { ok: true, grant_id: grant.grant_id };
|
|
81
|
+
}
|
|
82
|
+
this.activeGrants.set(grant.grant_id, { grant, usedCount: 0 });
|
|
83
|
+
return { ok: true, grant_id: grant.grant_id };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Revokes a capability grant, permanently denying subsequent queries.
|
|
88
|
+
* @param {string} grantId
|
|
89
|
+
* @returns {boolean}
|
|
90
|
+
*/
|
|
91
|
+
revokeGrant(grantId) {
|
|
92
|
+
if (typeof grantId !== 'string' || grantId.length === 0) return false;
|
|
93
|
+
if (!this.revokedGrantIds.has(grantId) && this.revokedGrantIds.size >= MAX_REVOKED_FEDERATION_GRANTS) {
|
|
94
|
+
throw new Error(`Cannot revoke capability grant: revocation capacity ${MAX_REVOKED_FEDERATION_GRANTS} reached`);
|
|
95
|
+
}
|
|
96
|
+
this.revokedGrantIds.add(grantId);
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Returns a JSON-safe snapshot of all authorization state needed to preserve
|
|
102
|
+
* replay protection, access counts, registered grants, and revocations.
|
|
103
|
+
*/
|
|
104
|
+
exportState() {
|
|
105
|
+
return {
|
|
106
|
+
schema: FEDERATION_ROUTER_STATE_SCHEMA,
|
|
107
|
+
version: 1,
|
|
108
|
+
active_grants: Array.from(this.activeGrants.entries())
|
|
109
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
110
|
+
.map(([, record]) => ({
|
|
111
|
+
grant: JSON.parse(JSON.stringify(record.grant)),
|
|
112
|
+
used_count: record.usedCount,
|
|
113
|
+
})),
|
|
114
|
+
revoked_grant_ids: Array.from(this.revokedGrantIds).sort(),
|
|
115
|
+
seen_nonces: Array.from(this.seenNonces.entries())
|
|
116
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
117
|
+
.map(([key, seenAt]) => ({ key, seen_at: seenAt })),
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Strictly restores a snapshot created by exportState(). Validation completes
|
|
123
|
+
* before any live authorization state is changed.
|
|
124
|
+
*/
|
|
125
|
+
importState(state) {
|
|
126
|
+
if (!state || typeof state !== 'object' ||
|
|
127
|
+
state.schema !== FEDERATION_ROUTER_STATE_SCHEMA || state.version !== 1) {
|
|
128
|
+
throw new Error('Invalid federation router state schema or version');
|
|
129
|
+
}
|
|
130
|
+
if (!Array.isArray(state.active_grants) ||
|
|
131
|
+
!Array.isArray(state.revoked_grant_ids) ||
|
|
132
|
+
!Array.isArray(state.seen_nonces)) {
|
|
133
|
+
throw new Error('Federation router state arrays are required');
|
|
134
|
+
}
|
|
135
|
+
if (
|
|
136
|
+
state.active_grants.length > MAX_ACTIVE_FEDERATION_GRANTS
|
|
137
|
+
|| state.revoked_grant_ids.length > MAX_REVOKED_FEDERATION_GRANTS
|
|
138
|
+
|| state.seen_nonces.length > MAX_SEEN_FEDERATION_NONCES
|
|
139
|
+
) {
|
|
140
|
+
throw new Error('Federation router state exceeds a safe authorization-state bound');
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const nextActive = new Map();
|
|
144
|
+
const nextRevoked = new Set();
|
|
145
|
+
const nextNonces = new Map();
|
|
146
|
+
const myPubHex = this.enigma.mesh.identity?.publicKey?.toString('hex');
|
|
147
|
+
|
|
148
|
+
for (const entry of state.active_grants) {
|
|
149
|
+
if (!entry || typeof entry !== 'object' || !entry.grant ||
|
|
150
|
+
!Number.isSafeInteger(entry.used_count) || entry.used_count < 0) {
|
|
151
|
+
throw new Error('Invalid active grant state entry');
|
|
152
|
+
}
|
|
153
|
+
const verdict = verifyBlindedMemoryGrant(entry.grant, {
|
|
154
|
+
asOfTime: new Date(entry.grant.issued_at).getTime(),
|
|
155
|
+
});
|
|
156
|
+
if (!verdict.ok) {
|
|
157
|
+
throw new Error(`Invalid grant in federation router state: ${verdict.errors.join('; ')}`);
|
|
158
|
+
}
|
|
159
|
+
if (myPubHex && entry.grant.granter_pubkey !== myPubHex && entry.grant.grantee_pubkey !== myPubHex) {
|
|
160
|
+
throw new Error(`Federation router state grant ${entry.grant.grant_id} is not bound to the local agent`);
|
|
161
|
+
}
|
|
162
|
+
if (entry.grant.max_access_count > 0 && entry.used_count > entry.grant.max_access_count) {
|
|
163
|
+
throw new Error(`Federation router state usage exceeds grant ${entry.grant.grant_id} access limit`);
|
|
164
|
+
}
|
|
165
|
+
if (nextActive.has(entry.grant.grant_id)) {
|
|
166
|
+
throw new Error(`Duplicate active grant in federation router state: ${entry.grant.grant_id}`);
|
|
167
|
+
}
|
|
168
|
+
nextActive.set(entry.grant.grant_id, {
|
|
169
|
+
grant: JSON.parse(JSON.stringify(entry.grant)),
|
|
170
|
+
usedCount: entry.used_count,
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
for (const grantId of state.revoked_grant_ids) {
|
|
175
|
+
if (typeof grantId !== 'string' || grantId.length === 0 || nextRevoked.has(grantId)) {
|
|
176
|
+
throw new Error('Invalid or duplicate revoked grant ID in federation router state');
|
|
177
|
+
}
|
|
178
|
+
nextRevoked.add(grantId);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
for (const entry of state.seen_nonces) {
|
|
182
|
+
if (!entry || typeof entry.key !== 'string' ||
|
|
183
|
+
!/^.+:[0-9a-f]{32}$/.test(entry.key) ||
|
|
184
|
+
!Number.isSafeInteger(entry.seen_at) || entry.seen_at < 0 ||
|
|
185
|
+
nextNonces.has(entry.key)) {
|
|
186
|
+
throw new Error('Invalid or duplicate nonce entry in federation router state');
|
|
187
|
+
}
|
|
188
|
+
nextNonces.set(entry.key, entry.seen_at);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
this.activeGrants = nextActive;
|
|
192
|
+
this.revokedGrantIds = nextRevoked;
|
|
193
|
+
this.seenNonces = nextNonces;
|
|
194
|
+
return this;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Clean up expired seen nonces older than TTL.
|
|
199
|
+
* @param {number} [ttlMs=600000]
|
|
200
|
+
*/
|
|
201
|
+
pruneSeenNonces(ttlMs = 600000) {
|
|
202
|
+
if (!Number.isSafeInteger(ttlMs) || ttlMs < 300000) {
|
|
203
|
+
throw new Error('Nonce replay TTL must be a safe integer of at least 300000ms');
|
|
204
|
+
}
|
|
205
|
+
const now = Date.now();
|
|
206
|
+
let pruned = 0;
|
|
207
|
+
for (const [key, ts] of this.seenNonces.entries()) {
|
|
208
|
+
if (now - ts > ttlMs) {
|
|
209
|
+
this.seenNonces.delete(key);
|
|
210
|
+
pruned++;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return pruned;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Executes a capability-gated federated memory query under a verified grant.
|
|
218
|
+
* Authenticates the requester via cryptographic proof-of-possession signature matching
|
|
219
|
+
* `grant.grantee_pubkey`, validates freshness/replay protection, and enforces ABAC ontology scoping.
|
|
220
|
+
*
|
|
221
|
+
* @param {Object} grantOrRequest - Signed BlindedMemoryGrant OR signed FederatedQueryRequest
|
|
222
|
+
* @param {string} [query] - Agent search prompt
|
|
223
|
+
* @param {Object} [options]
|
|
224
|
+
* @param {Buffer|string} [options.requesterSecretKey] - 32-byte private key of the grantee for inline signature
|
|
225
|
+
* @param {Buffer|string} [options.requesterPublicKey] - 32-byte public key of the grantee
|
|
226
|
+
* @param {import('../index.js').Enigma} [options.requesterAgent] - Grantee agent instance with mesh identity
|
|
227
|
+
* @param {Object} [options.authorization] - Explicit authorization envelope { requester_pubkey, nonce, timestamp, signature }
|
|
228
|
+
* @param {number} [options.topK=5]
|
|
229
|
+
* @returns {Promise<{ ok: boolean, grant_id: string, scope: string, items: Array<{ id: string, text: string, type: string, score: number }>, remainingAccess: number }>}
|
|
230
|
+
*/
|
|
231
|
+
async queryFederatedCluster(grantOrRequest, query, options = {}) {
|
|
232
|
+
this.pruneSeenNonces();
|
|
233
|
+
|
|
234
|
+
let request;
|
|
235
|
+
let actualQuery = query;
|
|
236
|
+
|
|
237
|
+
if (grantOrRequest && grantOrRequest.schema === FEDERATED_QUERY_REQUEST_SCHEMA) {
|
|
238
|
+
request = grantOrRequest;
|
|
239
|
+
actualQuery = request.query;
|
|
240
|
+
} else {
|
|
241
|
+
const grant = grantOrRequest;
|
|
242
|
+
if (typeof actualQuery !== 'string' || actualQuery.trim().length === 0) {
|
|
243
|
+
throw new Error('Query string is required for federated cluster search');
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
let requesterSecretKey = options.requesterSecretKey;
|
|
247
|
+
let requesterPublicKey = options.requesterPublicKey;
|
|
248
|
+
if (!requesterSecretKey && options.requesterAgent?.mesh?.identity) {
|
|
249
|
+
requesterSecretKey = options.requesterAgent.mesh.identity.secretKey;
|
|
250
|
+
requesterPublicKey = options.requesterAgent.mesh.identity.publicKey;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (requesterSecretKey) {
|
|
254
|
+
request = createFederatedQueryRequest({
|
|
255
|
+
grant,
|
|
256
|
+
query: actualQuery,
|
|
257
|
+
requesterSecretKey,
|
|
258
|
+
requesterPublicKey,
|
|
259
|
+
});
|
|
260
|
+
} else if (options.authorization) {
|
|
261
|
+
request = {
|
|
262
|
+
schema: FEDERATED_QUERY_REQUEST_SCHEMA,
|
|
263
|
+
version: 1,
|
|
264
|
+
grant,
|
|
265
|
+
grant_id: grant.grant_id,
|
|
266
|
+
query: actualQuery,
|
|
267
|
+
...options.authorization,
|
|
268
|
+
};
|
|
269
|
+
} else {
|
|
270
|
+
throw new Error(
|
|
271
|
+
'Grantee proof-of-possession required: Provide requesterAgent, requesterSecretKey, or a signed query authorization envelope matching grant.grantee_pubkey'
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const reqVerdict = verifyFederatedQueryRequest(request);
|
|
277
|
+
if (!reqVerdict.ok) {
|
|
278
|
+
throw new Error(`Federated query authorization rejected: ${reqVerdict.errors.join('; ')}`);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const grant = request.grant;
|
|
282
|
+
const grantId = grant.grant_id;
|
|
283
|
+
if (this.revokedGrantIds.has(grantId)) {
|
|
284
|
+
throw new Error(`Capability grant ${grantId} has been revoked by granter`);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const myPubHex = this.enigma.mesh.identity?.publicKey?.toString('hex');
|
|
288
|
+
if (myPubHex && grant.granter_pubkey !== myPubHex) {
|
|
289
|
+
throw new Error(`Granter public key mismatch: grant issued by ${grant.granter_pubkey}, local agent is ${myPubHex}`);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const topK = options.topK === undefined ? 5 : options.topK;
|
|
293
|
+
if (!Number.isSafeInteger(topK) || topK < 1 || topK > 100) {
|
|
294
|
+
throw new Error('topK must be an integer between 1 and 100');
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const nonceKey = `${grantId}:${request.nonce}`;
|
|
298
|
+
if (this.seenNonces.has(nonceKey)) {
|
|
299
|
+
throw new Error(`Replay attack detected: nonce ${request.nonce} has already been consumed for grant ${grantId}`);
|
|
300
|
+
}
|
|
301
|
+
if (!this.seenNonces.has(nonceKey) && this.seenNonces.size >= MAX_SEEN_FEDERATION_NONCES) {
|
|
302
|
+
throw new Error(`Federation replay cache capacity ${MAX_SEEN_FEDERATION_NONCES} reached`);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
let record = this.activeGrants.get(grantId);
|
|
306
|
+
const createdRecord = !record;
|
|
307
|
+
if (record) {
|
|
308
|
+
const sameGrant = getGrantSignablePayload(record.grant) === getGrantSignablePayload(grant) &&
|
|
309
|
+
record.grant.signature === grant.signature;
|
|
310
|
+
if (!sameGrant) throw new Error(`Capability grant ID collision for ${grantId}`);
|
|
311
|
+
} else {
|
|
312
|
+
if (this.activeGrants.size >= MAX_ACTIVE_FEDERATION_GRANTS) {
|
|
313
|
+
throw new Error(`Active federation grant capacity ${MAX_ACTIVE_FEDERATION_GRANTS} reached`);
|
|
314
|
+
}
|
|
315
|
+
record = { grant, usedCount: 0 };
|
|
316
|
+
this.activeGrants.set(grantId, record);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
if (grant.max_access_count > 0 && record.usedCount >= grant.max_access_count) {
|
|
320
|
+
if (createdRecord) this.activeGrants.delete(grantId);
|
|
321
|
+
throw new Error(`Access limit exceeded: grant ${grantId} has exhausted its ${grant.max_access_count} queries limit`);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Consume and durably commit authorization before recall so concurrent
|
|
325
|
+
// requests cannot race limits and a successful response cannot outlive the
|
|
326
|
+
// persisted replay/count state.
|
|
327
|
+
this.seenNonces.set(nonceKey, Date.now());
|
|
328
|
+
record.usedCount += 1;
|
|
329
|
+
try {
|
|
330
|
+
await this.persistStateMutation();
|
|
331
|
+
} catch (error) {
|
|
332
|
+
this.seenNonces.delete(nonceKey);
|
|
333
|
+
if (createdRecord) this.activeGrants.delete(grantId);
|
|
334
|
+
else record.usedCount -= 1;
|
|
335
|
+
throw new Error(`Federation authorization state persistence failed: ${error.message}`);
|
|
336
|
+
}
|
|
337
|
+
const allowedTypes = grant.allowed_types.length > 0
|
|
338
|
+
? new Set(grant.allowed_types.map(normalizeMemoryType))
|
|
339
|
+
: null;
|
|
340
|
+
const rawRecalls = await this.enigma.recall(actualQuery, {
|
|
341
|
+
scope: grant.scope,
|
|
342
|
+
topK,
|
|
343
|
+
});
|
|
344
|
+
const filteredItems = rawRecalls.filter((item) =>
|
|
345
|
+
item.scope === grant.scope && (!allowedTypes || allowedTypes.has(item.type))
|
|
346
|
+
);
|
|
347
|
+
|
|
348
|
+
const remainingAccess = grant.max_access_count > 0
|
|
349
|
+
? Math.max(0, grant.max_access_count - record.usedCount)
|
|
350
|
+
: Infinity;
|
|
351
|
+
|
|
352
|
+
return {
|
|
353
|
+
ok: true,
|
|
354
|
+
grant_id: grantId,
|
|
355
|
+
scope: grant.scope,
|
|
356
|
+
items: filteredItems,
|
|
357
|
+
remainingAccess,
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
}
|