enigma-memory 0.1.17 → 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 +85 -27
- package/apps/cli/bin/enigma-desktop.mjs +140 -0
- package/apps/cli/bin/enigma-terminal.mjs +78 -0
- package/apps/cli/bin/enigma.mjs +4979 -3263
- 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/SIMULATION.md +14 -9
- package/deploy/docker-compose.local-production-simulation.yml +54 -12
- package/docs/benchmark-attestation-network.md +487 -487
- package/docs/benchmark-reproducibility.md +289 -289
- package/docs/blockchain-only-mechanisms.md +400 -400
- package/docs/browser-extension-install.md +8 -6
- package/docs/client-connectors.md +16 -12
- package/docs/demo-proof-network.md +275 -275
- package/docs/developer-ecosystem.md +15 -13
- package/docs/developer-proof-quickstart.md +325 -325
- package/docs/enigma-memory-ready-conformance.md +378 -376
- package/docs/install-anywhere.md +64 -30
- package/docs/installers-and-desktop.md +8 -7
- package/docs/memory-benchmarks.md +1 -1
- package/docs/memory-drive-health-model.md +690 -690
- package/docs/novelty-invention-candidates.md +161 -161
- package/docs/proof-network-build-notes.md +240 -240
- package/docs/proof-network-claim-boundaries.md +320 -318
- package/docs/proof-network.md +339 -339
- package/docs/sdk-api.md +324 -324
- package/docs/solana-proof-rail.md +453 -453
- 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 +410 -278
- 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 +249 -2
- 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 +2498 -1185
- 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 +436 -7
- 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 +1150 -125
- 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 +292 -274
- package/scripts/build-hosted-customer-lifecycle.mjs +493 -476
- package/scripts/build-hosted-probe-worker.mjs +19 -4
- package/scripts/build-installer-assets.mjs +409 -389
- 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 +4 -1
- package/scripts/build-proof-network-packet.mjs +213 -213
- package/scripts/check.mjs +25 -4
- package/scripts/collect-hosted-backend-live-evidence.mjs +49 -12
- package/scripts/install-enigma-local.mjs +18 -5
- package/scripts/release-audit.mjs +74 -115
- package/scripts/release-provenance.mjs +12 -2
- package/scripts/run-backend-readiness-smoke.mjs +112 -10
- package/scripts/run-standard-memory-benchmarks.mjs +1354 -1352
- package/scripts/scan-secrets.mjs +178 -0
- package/scripts/simulate-production-env.mjs +71 -10
- 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,178 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Enigma Memory — working-tree secret scanner.
|
|
3
|
+
// Scans for PEM private keys, bearer-token patterns, DSNs, AWS-style keys,
|
|
4
|
+
// and literal SECURITY.md placeholders. Approved test fixtures must carry an
|
|
5
|
+
// explicit approval comment on the same or previous non-empty line:
|
|
6
|
+
// // secret-scan:approved
|
|
7
|
+
// # secret-scan:approved
|
|
8
|
+
// This script exits non-zero when any unapproved detection remains.
|
|
9
|
+
|
|
10
|
+
import fs from 'node:fs'
|
|
11
|
+
import path from 'node:path'
|
|
12
|
+
import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
13
|
+
|
|
14
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
15
|
+
const APPROVAL_MARKERS = [
|
|
16
|
+
/secret-scan:(?:approved|ignore)/i,
|
|
17
|
+
/\bno[-_]?secret[-_]?scan\b/i,
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
const SKIP_DIRS = new Set([
|
|
21
|
+
'node_modules',
|
|
22
|
+
'.git',
|
|
23
|
+
'.enigma',
|
|
24
|
+
'.enigma-review-packet',
|
|
25
|
+
'.enigma-review-packet-debug',
|
|
26
|
+
'coverage',
|
|
27
|
+
'dist',
|
|
28
|
+
'build',
|
|
29
|
+
'.cache',
|
|
30
|
+
'tmp',
|
|
31
|
+
'.vscode',
|
|
32
|
+
'.idea',
|
|
33
|
+
'.next',
|
|
34
|
+
])
|
|
35
|
+
|
|
36
|
+
const SKIP_EXTENSIONS = new Set([
|
|
37
|
+
'.png',
|
|
38
|
+
'.jpg',
|
|
39
|
+
'.jpeg',
|
|
40
|
+
'.gif',
|
|
41
|
+
'.webp',
|
|
42
|
+
'.svg',
|
|
43
|
+
'.ico',
|
|
44
|
+
'.pdf',
|
|
45
|
+
'.zip',
|
|
46
|
+
'.tar',
|
|
47
|
+
'.gz',
|
|
48
|
+
'.tgz',
|
|
49
|
+
'.woff',
|
|
50
|
+
'.woff2',
|
|
51
|
+
'.ttf',
|
|
52
|
+
'.otf',
|
|
53
|
+
'.eot',
|
|
54
|
+
'.mp4',
|
|
55
|
+
'.webm',
|
|
56
|
+
'.mp3',
|
|
57
|
+
'.ogg',
|
|
58
|
+
])
|
|
59
|
+
|
|
60
|
+
// The security-placeholder detector matches its own pattern literal below.
|
|
61
|
+
// The preceding comment marks that line as an approved self-reference.
|
|
62
|
+
const DETECTORS = [
|
|
63
|
+
{
|
|
64
|
+
id: 'pem-private-key',
|
|
65
|
+
pattern: /-----BEGIN\s+(?:RSA\s+|EC\s+|DSA\s+|OPENSSH\s+|ENCRYPTED\s+)?PRIVATE\s+KEY-----/i,
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
id: 'aws-access-key-id',
|
|
69
|
+
pattern: /\b(?:AKIA|ASIA|AROA|AIDA)[0-9A-Z]{16}\b/,
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
id: 'aws-secret-access-key',
|
|
73
|
+
pattern: /\b(?:aws[_-]?secret[_-]?access[_-]?key|AWS[_-]?SECRET[_-]?ACCESS[_-]?KEY)\s*[=:]\s*["'][A-Za-z0-9/+=]{40}["']/i,
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
id: 'bearer-token',
|
|
77
|
+
// Skip placeholders like <token>, ${var}, and $ENV_VAR.
|
|
78
|
+
pattern: /(?:Authorization\s*:\s*Bearer\s+(?![<$\(])\S{8,}|\bbearer\s+[:=]\s*["']?[a-zA-Z0-9_\-]{16,}|\bapi[_-]?token\s*[:=]\s*["'][a-zA-Z0-9_\-]{16,}["'])/i,
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
id: 'dsn-with-password',
|
|
82
|
+
pattern: /\b(?:postgres|postgresql|mysql|mysql2|mongodb|redis|amqp|amqps):\/\/[^:]+:[^@\s]+@[^\/\s]+/i,
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
id: 'security-placeholder',
|
|
86
|
+
pattern: /REPLACE-WITH/i, // secret-scan:approved self-reference pattern
|
|
87
|
+
},
|
|
88
|
+
]
|
|
89
|
+
|
|
90
|
+
function hasApprovalComment(line) {
|
|
91
|
+
return APPROVAL_MARKERS.some((m) => m.test(line))
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function isApproved(lines, index) {
|
|
95
|
+
if (hasApprovalComment(lines[index])) return true
|
|
96
|
+
for (let i = index - 1; i >= 0; i -= 1) {
|
|
97
|
+
const trimmed = lines[i].trim()
|
|
98
|
+
if (trimmed === '') continue
|
|
99
|
+
return hasApprovalComment(trimmed)
|
|
100
|
+
}
|
|
101
|
+
return false
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function isSkippableFile(rel) {
|
|
105
|
+
const ext = path.extname(rel).toLowerCase()
|
|
106
|
+
if (SKIP_EXTENSIONS.has(ext)) return true
|
|
107
|
+
const base = path.basename(rel)
|
|
108
|
+
if (base === 'package-lock.json') return true
|
|
109
|
+
return false
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function* walk(root) {
|
|
113
|
+
const entries = fs.readdirSync(root, { withFileTypes: true })
|
|
114
|
+
for (const entry of entries) {
|
|
115
|
+
const full = path.join(root, entry.name)
|
|
116
|
+
if (entry.isDirectory()) {
|
|
117
|
+
if (SKIP_DIRS.has(entry.name)) continue
|
|
118
|
+
yield* walk(full)
|
|
119
|
+
} else if (entry.isFile()) {
|
|
120
|
+
yield full
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function scanForSecrets(root) {
|
|
126
|
+
const findings = []
|
|
127
|
+
const rootResolved = path.resolve(root)
|
|
128
|
+
for (const fullPath of walk(rootResolved)) {
|
|
129
|
+
const rel = path.relative(rootResolved, fullPath)
|
|
130
|
+
if (isSkippableFile(rel)) continue
|
|
131
|
+
let text
|
|
132
|
+
try {
|
|
133
|
+
text = fs.readFileSync(fullPath, 'utf8')
|
|
134
|
+
} catch {
|
|
135
|
+
continue
|
|
136
|
+
}
|
|
137
|
+
const lines = text.split(/\r?\n/)
|
|
138
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
139
|
+
const line = lines[i]
|
|
140
|
+
if (isApproved(lines, i)) continue
|
|
141
|
+
for (const detector of DETECTORS) {
|
|
142
|
+
if (detector.pattern.test(line)) {
|
|
143
|
+
findings.push({
|
|
144
|
+
file: rel,
|
|
145
|
+
line: i + 1,
|
|
146
|
+
type: detector.id,
|
|
147
|
+
})
|
|
148
|
+
break
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return findings
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function formatFindings(findings) {
|
|
157
|
+
return findings
|
|
158
|
+
.map((f) => `${f.file}:${f.line}: ${f.type}`)
|
|
159
|
+
.join('\n')
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function main() {
|
|
163
|
+
const rootArg = process.argv.includes('--root')
|
|
164
|
+
? process.argv[process.argv.indexOf('--root') + 1]
|
|
165
|
+
: path.resolve(path.dirname(__filename), '..')
|
|
166
|
+
const root = path.resolve(rootArg)
|
|
167
|
+
const findings = scanForSecrets(root)
|
|
168
|
+
if (findings.length > 0) {
|
|
169
|
+
process.stderr.write(`secret-scan: ${findings.length} unapproved detection(s)\n`)
|
|
170
|
+
process.stderr.write(formatFindings(findings) + '\n')
|
|
171
|
+
process.exit(1)
|
|
172
|
+
}
|
|
173
|
+
process.stdout.write('secret-scan ok\n')
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
177
|
+
main()
|
|
178
|
+
}
|
|
@@ -5,13 +5,15 @@
|
|
|
5
5
|
// are not production secrets, HSM custody, or real operator evidence.
|
|
6
6
|
|
|
7
7
|
import { spawnSync } from 'node:child_process'
|
|
8
|
-
import { generateKeyPairSync, randomUUID } from 'node:crypto'
|
|
8
|
+
import { createHash, generateKeyPairSync, randomUUID } from 'node:crypto'
|
|
9
9
|
import fs from 'node:fs'
|
|
10
|
+
import os from 'node:os'
|
|
10
11
|
import path from 'node:path'
|
|
11
12
|
import { fileURLToPath } from 'node:url'
|
|
12
13
|
|
|
13
14
|
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
|
|
14
|
-
const DEFAULT_SECRETS_DIR = path.join(
|
|
15
|
+
const DEFAULT_SECRETS_DIR = path.join(os.homedir(), '.enigma', 'simulation-secrets')
|
|
16
|
+
const DOCKER_READABLE_SECRET_MODE = 0o644
|
|
15
17
|
|
|
16
18
|
const REQUIRED_SECRET_FILES = [
|
|
17
19
|
'relay-signing-key',
|
|
@@ -27,6 +29,11 @@ const REQUIRED_SECRET_FILES = [
|
|
|
27
29
|
'tls.key',
|
|
28
30
|
]
|
|
29
31
|
|
|
32
|
+
const BEARER_FILES_FOR_DIGEST = {
|
|
33
|
+
'gateway-admin-auth-bearer': 'ENIGMA_GATEWAY_ADMIN_AUTH_BEARER_SHA256',
|
|
34
|
+
'gateway-data-plane-auth-bearer': 'ENIGMA_GATEWAY_DATA_PLANE_AUTH_BEARER_SHA256',
|
|
35
|
+
}
|
|
36
|
+
|
|
30
37
|
function parseArgs(argv) {
|
|
31
38
|
const flags = { check: false, secretsDir: DEFAULT_SECRETS_DIR }
|
|
32
39
|
for (let i = 0; i < argv.length; i += 1) {
|
|
@@ -41,8 +48,9 @@ function parseArgs(argv) {
|
|
|
41
48
|
process.stdout.write(
|
|
42
49
|
'Usage: node scripts/simulate-production-env.mjs [--check] [--secrets-dir <dir>]\n' +
|
|
43
50
|
'\n' +
|
|
44
|
-
'Generates local-simulation secret files and a self-signed TLS certificate
|
|
45
|
-
`
|
|
51
|
+
'Generates local-simulation secret files and a self-signed TLS certificate.\n' +
|
|
52
|
+
`By default files are written outside the repo to ${DEFAULT_SECRETS_DIR}.\n` +
|
|
53
|
+
'A path inside the repository is rejected to prevent accidental commits.\n' +
|
|
46
54
|
'Run with --check to verify required files exist and are non-empty.\n'
|
|
47
55
|
)
|
|
48
56
|
process.exit(0)
|
|
@@ -51,6 +59,22 @@ function parseArgs(argv) {
|
|
|
51
59
|
return flags
|
|
52
60
|
}
|
|
53
61
|
|
|
62
|
+
function isPathInside(child, parent) {
|
|
63
|
+
const c = path.resolve(child)
|
|
64
|
+
const p = path.resolve(parent)
|
|
65
|
+
const rel = path.relative(p, c)
|
|
66
|
+
return rel === '' || !rel.startsWith('..')
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function assertSecretsDirOutsideRepo(secretsDir) {
|
|
70
|
+
if (isPathInside(secretsDir, ROOT)) {
|
|
71
|
+
throw new Error(
|
|
72
|
+
`Refusing to use secrets directory inside the repository: ${secretsDir}\n` +
|
|
73
|
+
'Choose a path outside the repo (e.g. --secrets-dir ~/.enigma/simulation-secrets).'
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
54
78
|
function generateKmsKeyRef() {
|
|
55
79
|
const pair = generateKeyPairSync('ed25519', {
|
|
56
80
|
privateKeyEncoding: { type: 'pkcs8', format: 'pem' },
|
|
@@ -75,6 +99,7 @@ function generateKmsKeyRef() {
|
|
|
75
99
|
const SECRET_GENERATORS = {
|
|
76
100
|
'relay-signing-key': () => 'local-simulation-relay-signing-key-ref\n',
|
|
77
101
|
'gateway-signing-key': () => 'local-simulation-gateway-signing-key-ref\n',
|
|
102
|
+
// Simulation-only DSN. Never a real credential. secret-scan:approved
|
|
78
103
|
'external-storage-dsn': () => 'postgres://enigma:enigma@postgres:5432/enigma?sslmode=disable\n',
|
|
79
104
|
'kms-key-ref': generateKmsKeyRef,
|
|
80
105
|
'backup-target-uri': () => 'file:///tmp/enigma-backups\n',
|
|
@@ -85,7 +110,8 @@ const SECRET_GENERATORS = {
|
|
|
85
110
|
}
|
|
86
111
|
|
|
87
112
|
function ensureDir(dir) {
|
|
88
|
-
fs.mkdirSync(dir, { recursive: true })
|
|
113
|
+
fs.mkdirSync(dir, { recursive: true, mode: 0o700 })
|
|
114
|
+
if (process.platform !== 'win32') fs.chmodSync(dir, 0o700)
|
|
89
115
|
}
|
|
90
116
|
|
|
91
117
|
function isMissingOrEmpty(filePath) {
|
|
@@ -97,6 +123,28 @@ function isMissingOrEmpty(filePath) {
|
|
|
97
123
|
}
|
|
98
124
|
}
|
|
99
125
|
|
|
126
|
+
function sha256Hex(bufferOrString) {
|
|
127
|
+
return createHash('sha256').update(bufferOrString).digest('hex')
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function gatewayBearerSha256(filePath) {
|
|
131
|
+
const token = fs.readFileSync(filePath)
|
|
132
|
+
return `sha256:${sha256Hex(token)}`
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function writeSimulationEnv(secretsDir) {
|
|
136
|
+
const lines = [
|
|
137
|
+
'# Generated by scripts/simulate-production-env.mjs',
|
|
138
|
+
'# This file contains simulation-only digests, not production secrets.',
|
|
139
|
+
'# It is kept next to the other simulation secrets outside the repo.',
|
|
140
|
+
]
|
|
141
|
+
for (const [name, envVar] of Object.entries(BEARER_FILES_FOR_DIGEST)) {
|
|
142
|
+
const digest = gatewayBearerSha256(path.join(secretsDir, name))
|
|
143
|
+
lines.push(`${envVar}=${digest}`)
|
|
144
|
+
}
|
|
145
|
+
fs.writeFileSync(path.join(secretsDir, 'simulation.env'), lines.join('\n') + '\n', { mode: 0o600 })
|
|
146
|
+
}
|
|
147
|
+
|
|
100
148
|
function generateTlsCerts(secretsDir) {
|
|
101
149
|
const key = path.join(secretsDir, 'tls.key')
|
|
102
150
|
const cert = path.join(secretsDir, 'tls.crt')
|
|
@@ -149,18 +197,23 @@ function generateTlsCerts(secretsDir) {
|
|
|
149
197
|
|
|
150
198
|
function generateSecrets(secretsDir) {
|
|
151
199
|
ensureDir(secretsDir)
|
|
200
|
+
assertSecretsDirOutsideRepo(secretsDir)
|
|
152
201
|
for (const name of REQUIRED_SECRET_FILES) {
|
|
153
202
|
if (name === 'tls.crt' || name === 'tls.key') continue
|
|
154
203
|
const filePath = path.join(secretsDir, name)
|
|
155
204
|
if (isMissingOrEmpty(filePath)) {
|
|
156
|
-
fs.writeFileSync(filePath, SECRET_GENERATORS[name](), { mode:
|
|
205
|
+
fs.writeFileSync(filePath, SECRET_GENERATORS[name](), { mode: DOCKER_READABLE_SECRET_MODE })
|
|
157
206
|
}
|
|
207
|
+
// Compose file-backed secrets preserve host file bits. The parent directory
|
|
208
|
+
// remains owner-only while non-root simulation containers receive read access.
|
|
209
|
+
if (process.platform !== 'win32') fs.chmodSync(filePath, DOCKER_READABLE_SECRET_MODE)
|
|
158
210
|
}
|
|
159
211
|
const keyPath = path.join(secretsDir, 'tls.key')
|
|
160
212
|
const certPath = path.join(secretsDir, 'tls.crt')
|
|
161
213
|
if (isMissingOrEmpty(keyPath) || isMissingOrEmpty(certPath)) {
|
|
162
214
|
generateTlsCerts(secretsDir)
|
|
163
215
|
}
|
|
216
|
+
writeSimulationEnv(secretsDir)
|
|
164
217
|
}
|
|
165
218
|
|
|
166
219
|
function check(secretsDir) {
|
|
@@ -177,10 +230,17 @@ function check(secretsDir) {
|
|
|
177
230
|
)
|
|
178
231
|
}
|
|
179
232
|
|
|
180
|
-
function printStartInstructions() {
|
|
233
|
+
function printStartInstructions(secretsDir) {
|
|
234
|
+
process.stdout.write('\n')
|
|
235
|
+
process.stdout.write('Simulation secrets written outside the repo to:\n')
|
|
236
|
+
process.stdout.write(` ${secretsDir}\n`)
|
|
237
|
+
process.stdout.write('\n')
|
|
238
|
+
process.stdout.write('To start the local production simulation, set the secrets path and run:\n')
|
|
239
|
+
process.stdout.write(` export ENIGMA_SIM_SECRETS_DIR=${secretsDir}\n`)
|
|
240
|
+
process.stdout.write(` docker compose -f deploy/docker-compose.local-production-simulation.yml --env-file ${path.join(secretsDir, 'simulation.env')} up --build -d\n`)
|
|
181
241
|
process.stdout.write('\n')
|
|
182
|
-
process.stdout.write('To
|
|
183
|
-
process.stdout.write(
|
|
242
|
+
process.stdout.write('To tear down, use the same env file so the gateway bearer hashes resolve:\n')
|
|
243
|
+
process.stdout.write(` docker compose -f deploy/docker-compose.local-production-simulation.yml --env-file ${path.join(secretsDir, 'simulation.env')} down -v --remove-orphans\n`)
|
|
184
244
|
process.stdout.write('\n')
|
|
185
245
|
process.stdout.write('To make the public-looking domain resolve locally, add this line to /etc/hosts\n')
|
|
186
246
|
process.stdout.write('(or C:\\Windows\\System32\\drivers\\etc\\hosts on Windows):\n')
|
|
@@ -198,13 +258,14 @@ function printStartInstructions() {
|
|
|
198
258
|
|
|
199
259
|
function main() {
|
|
200
260
|
const flags = parseArgs(process.argv.slice(2))
|
|
261
|
+
assertSecretsDirOutsideRepo(flags.secretsDir)
|
|
201
262
|
if (flags.check) {
|
|
202
263
|
check(flags.secretsDir)
|
|
203
264
|
return
|
|
204
265
|
}
|
|
205
266
|
generateSecrets(flags.secretsDir)
|
|
206
267
|
check(flags.secretsDir)
|
|
207
|
-
printStartInstructions()
|
|
268
|
+
printStartInstructions(flags.secretsDir)
|
|
208
269
|
}
|
|
209
270
|
|
|
210
271
|
main()
|
|
@@ -5,6 +5,9 @@ import { fileURLToPath } from 'node:url';
|
|
|
5
5
|
|
|
6
6
|
export const HOSTED_BACKEND_LIVE_EVIDENCE_SCHEMA = 'enigma.hosted_backend_live_evidence.v1';
|
|
7
7
|
export const HOSTED_BACKEND_LIVE_RESULT_SCHEMA = 'enigma.hosted_backend_live_result.v1';
|
|
8
|
+
export const HOSTED_BACKEND_RUNTIME_CAPABILITY_SCHEMA = 'enigma.hosted_backend_runtime_capability.v1';
|
|
9
|
+
export const HOSTED_BACKEND_AUTHENTICATED_DATA_PLANE_SCHEMA = 'enigma.hosted_backend_authenticated_data_plane.v1';
|
|
10
|
+
export const PRIVATE_DATA_PLANE_RUNTIME_MARKER = 'enigma.private_data_plane.runtime.v1';
|
|
8
11
|
|
|
9
12
|
export const REQUIRED_REF_KEYS = Object.freeze([
|
|
10
13
|
'backend_host',
|
|
@@ -35,6 +38,11 @@ export const REQUIRED_REF_KEYS = Object.freeze([
|
|
|
35
38
|
]);
|
|
36
39
|
|
|
37
40
|
const REQUIRED_PROBES = Object.freeze(['relay_livez', 'relay_readyz', 'gateway_livez', 'gateway_readyz']);
|
|
41
|
+
const RUNTIME_SERVICES = Object.freeze(['relay', 'gateway']);
|
|
42
|
+
const DATA_PLANE_ROUTES = Object.freeze({
|
|
43
|
+
relay: Object.freeze(['/relay/records', '/pairing/challenge', '/pairing/complete']),
|
|
44
|
+
gateway: Object.freeze(['/policy', '/gateway/evaluate', '/gateway/decision', '/siem/export']),
|
|
45
|
+
});
|
|
38
46
|
const ACCEPTED_STATUSES = new Set(['observed', 'verified', 'go', 'accepted']);
|
|
39
47
|
const SECRET_VALUE_RE = /(?:Bearer\s+[A-Za-z0-9._~+/=-]{12,}|Basic\s+[A-Za-z0-9+/=-]{12,}|-----BEGIN [A-Z ]*PRIVATE KEY-----|https?:\/\/[^\s/@]+:[^\s/@]+@|raw memory|private prompt|full transcript|decrypted capsule|sk-[A-Za-z0-9_-]{16,}|AKIA[0-9A-Z]{16})/iu;
|
|
40
48
|
const FORBIDDEN_KEY_RE = /(?:password|passwd|pwd|token|api[_-]?key|private[_-]?key|secret|raw[_-]?memory|plaintext|plain[_-]?text|prompt|completion|transcript|embedding|provider[_-]?response|cookie|session)/iu;
|
|
@@ -151,6 +159,12 @@ function validateEnvironment(environment, blockers) {
|
|
|
151
159
|
for (const field of ['environment_id', 'cloud_provider', 'region', 'owner', 'status']) {
|
|
152
160
|
if (!nonEmptyString(environment[field])) blockers.push(blocker(`environment.${field} is required`, `environment.${field}`));
|
|
153
161
|
}
|
|
162
|
+
if (environment.local_simulation !== false) {
|
|
163
|
+
blockers.push(blocker('environment.local_simulation must be false for hosted production evidence', 'environment.local_simulation'));
|
|
164
|
+
}
|
|
165
|
+
if (nonEmptyString(environment.cloud_provider) && environment.cloud_provider.trim().toLowerCase() === 'local') {
|
|
166
|
+
blockers.push(blocker('environment.cloud_provider must identify a non-simulation production provider', 'environment.cloud_provider'));
|
|
167
|
+
}
|
|
154
168
|
if (!statusAccepted(environment.status)) blockers.push(blocker('environment.status must be observed/verified/go/accepted', 'environment.status'));
|
|
155
169
|
return { domain };
|
|
156
170
|
}
|
|
@@ -179,6 +193,9 @@ function validateProbe(probe, expectedName, domain, blockers) {
|
|
|
179
193
|
if (probe.body.hosted_probe_only === true || probe.body.pages_edge_probe_only === true) {
|
|
180
194
|
blockers.push(blocker(`${path}.body must not be an edge-probe-only payload`, `${path}.body`));
|
|
181
195
|
}
|
|
196
|
+
if (probe.body.hosted_backend_live === false || probe.body.private_data_plane_operational === false || probe.body.runtime_capability?.bootstrap_worker === true) {
|
|
197
|
+
blockers.push(blocker(`${path}.body declares the private data plane closed or bootstrap-only`, `${path}.body.runtime_capability`));
|
|
198
|
+
}
|
|
182
199
|
if (probe.body.service !== expectedService) blockers.push(blocker(`${path}.body.service must be ${expectedService}`, `${path}.body.service`));
|
|
183
200
|
if (probe.body.ok !== true) blockers.push(blocker(`${path}.body.ok must be true`, `${path}.body.ok`));
|
|
184
201
|
if (expectedName.endsWith('readyz')) {
|
|
@@ -203,6 +220,94 @@ function validateProbes(probes, domain, blockers) {
|
|
|
203
220
|
return { covered };
|
|
204
221
|
}
|
|
205
222
|
|
|
223
|
+
function validateRuntimeCapability(capability, blockers) {
|
|
224
|
+
if (!isPlainObject(capability)) {
|
|
225
|
+
blockers.push(blocker('runtime_capability object is required', 'runtime_capability'));
|
|
226
|
+
return { covered: 0 };
|
|
227
|
+
}
|
|
228
|
+
if (capability.schema !== HOSTED_BACKEND_RUNTIME_CAPABILITY_SCHEMA) blockers.push(blocker(`runtime_capability.schema must be ${HOSTED_BACKEND_RUNTIME_CAPABILITY_SCHEMA}`, 'runtime_capability.schema'));
|
|
229
|
+
if (capability.marker !== PRIVATE_DATA_PLANE_RUNTIME_MARKER) blockers.push(blocker(`runtime_capability.marker must be ${PRIVATE_DATA_PLANE_RUNTIME_MARKER}`, 'runtime_capability.marker'));
|
|
230
|
+
if (!isoLike(capability.observed_at)) blockers.push(blocker('runtime_capability.observed_at must be ISO time', 'runtime_capability.observed_at'));
|
|
231
|
+
if (!isPlainObject(capability.services)) {
|
|
232
|
+
blockers.push(blocker('runtime_capability.services object is required', 'runtime_capability.services'));
|
|
233
|
+
return { covered: 0 };
|
|
234
|
+
}
|
|
235
|
+
let covered = 0;
|
|
236
|
+
for (const serviceKey of RUNTIME_SERVICES) {
|
|
237
|
+
const path = `runtime_capability.services.${serviceKey}`;
|
|
238
|
+
const service = capability.services[serviceKey];
|
|
239
|
+
if (!isPlainObject(service)) {
|
|
240
|
+
blockers.push(blocker(`${path} object is required`, path));
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
const expectedService = serviceKey === 'relay' ? 'enigma-relay' : 'enigma-gateway';
|
|
244
|
+
if (service.service !== expectedService) blockers.push(blocker(`${path}.service must be ${expectedService}`, `${path}.service`));
|
|
245
|
+
if (service.private_data_plane_operational !== true) blockers.push(blocker(`${path}.private_data_plane_operational must be true`, `${path}.private_data_plane_operational`));
|
|
246
|
+
if (service.authentication_enforced !== true) blockers.push(blocker(`${path}.authentication_enforced must be true`, `${path}.authentication_enforced`));
|
|
247
|
+
if (service.bootstrap_worker !== false) blockers.push(blocker(`${path}.bootstrap_worker must be false`, `${path}.bootstrap_worker`));
|
|
248
|
+
if (!nonEmptyString(service.evidence_ref)) blockers.push(blocker(`${path}.evidence_ref is required`, `${path}.evidence_ref`));
|
|
249
|
+
covered += 1;
|
|
250
|
+
}
|
|
251
|
+
return { covered };
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function authenticatedDataPlaneUrl(value, serviceKey, domain, path, blockers) {
|
|
255
|
+
if (!nonEmptyString(value)) {
|
|
256
|
+
blockers.push(blocker(`${path} is required`, path));
|
|
257
|
+
return null;
|
|
258
|
+
}
|
|
259
|
+
try {
|
|
260
|
+
const url = new URL(value);
|
|
261
|
+
if (url.protocol !== 'https:') blockers.push(blocker(`${path} must use https`, path));
|
|
262
|
+
if (url.username || url.password) blockers.push(blocker(`${path} must not include credentials`, path));
|
|
263
|
+
if (url.search || url.hash) blockers.push(blocker(`${path} must not include query strings or fragments`, path));
|
|
264
|
+
if (isPrivateHost(url.hostname)) blockers.push(blocker(`${path} must not target localhost or private network host`, path));
|
|
265
|
+
const hostname = url.hostname.toLowerCase();
|
|
266
|
+
if (domain && hostname !== domain && !hostname.endsWith(`.${domain}`)) blockers.push(blocker(`${path} host must be the domain or a subdomain`, path));
|
|
267
|
+
if (!DATA_PLANE_ROUTES[serviceKey].includes(url.pathname)) blockers.push(blocker(`${path} must target a ${serviceKey} private data-plane route`, path));
|
|
268
|
+
return url;
|
|
269
|
+
} catch {
|
|
270
|
+
blockers.push(blocker(`${path} must be a valid URL`, path));
|
|
271
|
+
return null;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function validateAuthenticatedDataPlane(evidence, domain, blockers) {
|
|
276
|
+
if (!isPlainObject(evidence)) {
|
|
277
|
+
blockers.push(blocker('authenticated_data_plane object is required', 'authenticated_data_plane'));
|
|
278
|
+
return { covered: 0 };
|
|
279
|
+
}
|
|
280
|
+
if (evidence.schema !== HOSTED_BACKEND_AUTHENTICATED_DATA_PLANE_SCHEMA) blockers.push(blocker(`authenticated_data_plane.schema must be ${HOSTED_BACKEND_AUTHENTICATED_DATA_PLANE_SCHEMA}`, 'authenticated_data_plane.schema'));
|
|
281
|
+
if (!isoLike(evidence.observed_at)) blockers.push(blocker('authenticated_data_plane.observed_at must be ISO time', 'authenticated_data_plane.observed_at'));
|
|
282
|
+
if (!isPlainObject(evidence.proofs)) {
|
|
283
|
+
blockers.push(blocker('authenticated_data_plane.proofs object is required', 'authenticated_data_plane.proofs'));
|
|
284
|
+
return { covered: 0 };
|
|
285
|
+
}
|
|
286
|
+
let covered = 0;
|
|
287
|
+
for (const serviceKey of RUNTIME_SERVICES) {
|
|
288
|
+
const path = `authenticated_data_plane.proofs.${serviceKey}`;
|
|
289
|
+
const proof = evidence.proofs[serviceKey];
|
|
290
|
+
if (!isPlainObject(proof)) {
|
|
291
|
+
blockers.push(blocker(`${path} object is required`, path));
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
const expectedService = serviceKey === 'relay' ? 'enigma-relay' : 'enigma-gateway';
|
|
295
|
+
if (proof.service !== expectedService) blockers.push(blocker(`${path}.service must be ${expectedService}`, `${path}.service`));
|
|
296
|
+
authenticatedDataPlaneUrl(proof.url, serviceKey, domain, `${path}.url`, blockers);
|
|
297
|
+
if (!['GET', 'POST', 'PUT', 'DELETE'].includes(proof.method)) blockers.push(blocker(`${path}.method must be GET/POST/PUT/DELETE`, `${path}.method`));
|
|
298
|
+
if (!Number.isSafeInteger(proof.status_code) || proof.status_code < 200 || proof.status_code >= 300) blockers.push(blocker(`${path}.status_code must be a successful 2xx response`, `${path}.status_code`));
|
|
299
|
+
if (proof.authenticated !== true) blockers.push(blocker(`${path}.authenticated must be true`, `${path}.authenticated`));
|
|
300
|
+
if (proof.authorization_result !== 'allowed') blockers.push(blocker(`${path}.authorization_result must be allowed`, `${path}.authorization_result`));
|
|
301
|
+
if (!isoLike(proof.observed_at)) blockers.push(blocker(`${path}.observed_at must be ISO time`, `${path}.observed_at`));
|
|
302
|
+
for (const field of ['request_hash', 'response_hash']) {
|
|
303
|
+
if (!nonEmptyString(proof[field]) || !/^sha256:[a-f0-9]{64}$/i.test(proof[field])) blockers.push(blocker(`${path}.${field} must be sha256:<64 hex>`, `${path}.${field}`));
|
|
304
|
+
}
|
|
305
|
+
if (!nonEmptyString(proof.evidence_ref)) blockers.push(blocker(`${path}.evidence_ref is required`, `${path}.evidence_ref`));
|
|
306
|
+
covered += 1;
|
|
307
|
+
}
|
|
308
|
+
return { covered };
|
|
309
|
+
}
|
|
310
|
+
|
|
206
311
|
function validateOperator(operatorAcceptance, blockers) {
|
|
207
312
|
if (!isPlainObject(operatorAcceptance)) {
|
|
208
313
|
blockers.push(blocker('operator_acceptance object is required', 'operator_acceptance'));
|
|
@@ -238,6 +343,8 @@ export function validateHostedBackendLiveEvidence(evidence, options = {}) {
|
|
|
238
343
|
const env = validateEnvironment(evidence.environment, blockers);
|
|
239
344
|
const refs = validateRefs(evidence.refs, blockers);
|
|
240
345
|
const probes = validateProbes(evidence.probes, env.domain, blockers);
|
|
346
|
+
const runtimeCapability = validateRuntimeCapability(evidence.runtime_capability, blockers);
|
|
347
|
+
const authenticatedDataPlane = validateAuthenticatedDataPlane(evidence.authenticated_data_plane, env.domain, blockers);
|
|
241
348
|
validateOperator(evidence.operator_acceptance, blockers);
|
|
242
349
|
validateClaimBoundary(evidence.claim_boundary, blockers);
|
|
243
350
|
const result = {
|
|
@@ -253,11 +360,15 @@ export function validateHostedBackendLiveEvidence(evidence, options = {}) {
|
|
|
253
360
|
refs_missing: refs.missing,
|
|
254
361
|
required_probes: REQUIRED_PROBES.length,
|
|
255
362
|
probes_covered: probes.covered,
|
|
363
|
+
runtime_services_required: RUNTIME_SERVICES.length,
|
|
364
|
+
runtime_services_covered: runtimeCapability.covered,
|
|
365
|
+
authenticated_data_plane_proofs_required: RUNTIME_SERVICES.length,
|
|
366
|
+
authenticated_data_plane_proofs_covered: authenticatedDataPlane.covered,
|
|
256
367
|
operator_decision: evidence.operator_acceptance?.decision ?? null,
|
|
257
368
|
},
|
|
258
369
|
claim_boundary: [
|
|
259
370
|
'Hosted backend live validation checks supplied evidence only; it does not deploy infrastructure, mutate Cloudflare, create DNS records, or generate credentials.',
|
|
260
|
-
'A pass result requires public HTTPS /livez and /readyz
|
|
371
|
+
'A pass result requires public HTTPS /livez and /readyz probes, an explicit operational private-runtime capability marker, successful authenticated relay and gateway data-plane proofs, all required production refs, and operator acceptance go.',
|
|
261
372
|
'Credentials, bearer tokens, API tokens, private memory payloads, prompts, transcripts, provider responses, and personal contact data must remain outside hosted backend live evidence.',
|
|
262
373
|
],
|
|
263
374
|
};
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://schemas.enigma.ai/antibody-pack-v1.schema.json",
|
|
4
|
+
"title": "Enigma Antibody Pack v1",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": [
|
|
7
|
+
"schema",
|
|
8
|
+
"antibody_pack_id",
|
|
9
|
+
"created_at",
|
|
10
|
+
"version",
|
|
11
|
+
"policy_fingerprints",
|
|
12
|
+
"pack_root",
|
|
13
|
+
"signatures",
|
|
14
|
+
"public_payload_only",
|
|
15
|
+
"private_inputs_included"
|
|
16
|
+
],
|
|
17
|
+
"additionalProperties": false,
|
|
18
|
+
"properties": {
|
|
19
|
+
"schema": { "const": "enigma.antibody_pack.v1" },
|
|
20
|
+
"antibody_pack_id": { "$ref": "#/$defs/publicId" },
|
|
21
|
+
"created_at": { "type": "string", "format": "date-time" },
|
|
22
|
+
"version": { "type": "string", "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+(?:[-+][A-Za-z0-9.-]+)?$" },
|
|
23
|
+
"policy_fingerprints": {
|
|
24
|
+
"type": "array",
|
|
25
|
+
"minItems": 1,
|
|
26
|
+
"items": { "$ref": "#/$defs/policyFingerprint" }
|
|
27
|
+
},
|
|
28
|
+
"pack_root": { "$ref": "#/$defs/sha256Digest" },
|
|
29
|
+
"signatures": {
|
|
30
|
+
"type": "array",
|
|
31
|
+
"minItems": 1,
|
|
32
|
+
"items": { "$ref": "#/$defs/signatureRef" }
|
|
33
|
+
},
|
|
34
|
+
"public_payload_only": { "const": true },
|
|
35
|
+
"private_inputs_included": { "const": false }
|
|
36
|
+
},
|
|
37
|
+
"$defs": {
|
|
38
|
+
"sha256Digest": { "type": "string", "pattern": "^sha256:[a-f0-9]{64}$" },
|
|
39
|
+
"publicId": { "type": "string", "pattern": "^[a-z0-9][a-z0-9._:-]{7,127}$" },
|
|
40
|
+
"publicRef": { "type": "string", "pattern": "^ref:[A-Za-z0-9][A-Za-z0-9._~:@#?=&%+-]{0,255}$" },
|
|
41
|
+
"detectorId": { "type": "string", "pattern": "^detector:[A-Za-z0-9][A-Za-z0-9._~:@#?=&%+-]{0,191}$" },
|
|
42
|
+
"policyId": { "type": "string", "pattern": "^policy:[A-Za-z0-9][A-Za-z0-9._~:@#?=&%+-]{0,191}$" },
|
|
43
|
+
"signerRef": { "type": "string", "pattern": "^signer:[A-Za-z0-9][A-Za-z0-9._~:@#?=&%+-]{0,191}$" },
|
|
44
|
+
"signaturePublicRef": { "type": "string", "pattern": "^sigref:[A-Za-z0-9][A-Za-z0-9._~:@#?=&%+-]{0,191}$" },
|
|
45
|
+
"policyFingerprint": {
|
|
46
|
+
"type": "object",
|
|
47
|
+
"required": [
|
|
48
|
+
"policy_id",
|
|
49
|
+
"detector_id",
|
|
50
|
+
"detector_ref",
|
|
51
|
+
"policy_hash",
|
|
52
|
+
"risk_labels",
|
|
53
|
+
"threshold_commitment",
|
|
54
|
+
"private_inputs_included"
|
|
55
|
+
],
|
|
56
|
+
"additionalProperties": false,
|
|
57
|
+
"properties": {
|
|
58
|
+
"policy_id": { "$ref": "#/$defs/policyId" },
|
|
59
|
+
"detector_id": { "$ref": "#/$defs/detectorId" },
|
|
60
|
+
"detector_ref": { "$ref": "#/$defs/publicRef" },
|
|
61
|
+
"policy_hash": { "$ref": "#/$defs/sha256Digest" },
|
|
62
|
+
"risk_labels": {
|
|
63
|
+
"type": "array",
|
|
64
|
+
"items": { "$ref": "#/$defs/riskLabel" },
|
|
65
|
+
"uniqueItems": true
|
|
66
|
+
},
|
|
67
|
+
"threshold_commitment": { "$ref": "#/$defs/sha256Digest" },
|
|
68
|
+
"private_inputs_included": { "const": false }
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
"signatureRef": {
|
|
72
|
+
"type": "object",
|
|
73
|
+
"required": ["signer_ref", "signature_ref", "signature_hash", "alg"],
|
|
74
|
+
"additionalProperties": false,
|
|
75
|
+
"properties": {
|
|
76
|
+
"signer_ref": { "$ref": "#/$defs/signerRef" },
|
|
77
|
+
"signature_ref": { "$ref": "#/$defs/signaturePublicRef" },
|
|
78
|
+
"signature_hash": { "$ref": "#/$defs/sha256Digest" },
|
|
79
|
+
"alg": { "type": "string", "enum": ["Ed25519", "ECDSA-P256-SHA256"] }
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
"riskLabel": {
|
|
83
|
+
"type": "string",
|
|
84
|
+
"enum": [
|
|
85
|
+
"untrusted_origin",
|
|
86
|
+
"prompt_injection",
|
|
87
|
+
"data_exfiltration",
|
|
88
|
+
"secret_material",
|
|
89
|
+
"memory_poisoning",
|
|
90
|
+
"policy_violation",
|
|
91
|
+
"unknown"
|
|
92
|
+
]
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|