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,502 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawn } from 'node:child_process';
|
|
3
|
+
import { randomBytes } from 'node:crypto';
|
|
4
|
+
import { access, chmod, mkdir, mkdtemp, open, readFile, rm } from 'node:fs/promises';
|
|
5
|
+
import { homedir, tmpdir } from 'node:os';
|
|
6
|
+
import { basename, dirname, isAbsolute, join, resolve } from 'node:path';
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
8
|
+
|
|
9
|
+
import { validateNodeVersion } from './install-enigma-local.mjs';
|
|
10
|
+
|
|
11
|
+
export const BOOTSTRAP_SCHEMA = 'enigma.bootstrap.v1';
|
|
12
|
+
export const DEFAULT_LAUNCH = 'desktop';
|
|
13
|
+
|
|
14
|
+
const SCRIPT_PATH = fileURLToPath(import.meta.url);
|
|
15
|
+
const DEFAULT_PACKAGE_DIR = resolve(dirname(SCRIPT_PATH), '..');
|
|
16
|
+
const CONTROL_CHARACTER_RE = /[\0\n\r]/u;
|
|
17
|
+
|
|
18
|
+
function readRequiredValue(argv, index, flag) {
|
|
19
|
+
const value = argv[index + 1];
|
|
20
|
+
if (value === undefined || value.startsWith('--')) throw new Error(`Missing value for ${flag}.`);
|
|
21
|
+
return value;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function rejectUnsafeValue(value, label) {
|
|
25
|
+
const text = String(value ?? '');
|
|
26
|
+
if (text.length === 0) throw new Error(`${label} must not be empty.`);
|
|
27
|
+
if (CONTROL_CHARACTER_RE.test(text)) throw new Error(`${label} contains an invalid control character.`);
|
|
28
|
+
return text;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function usage() {
|
|
32
|
+
return `Install the complete Enigma harness, create an encrypted local vault, and open the desktop.\n\nUsage:\n npx --yes enigma-memory@latest\n enigma-memory [--desktop|--terminal|--no-launch] [--bundle <path>] [--passphrase-file <path>] [--prefix <path>] [--dry-run]\n\nDefaults:\n - Installs the exact package executing this bootstrap.\n - Creates ~/.enigma/passphrase with cryptographically random bytes when no vault exists.\n - Creates ~/.enigma/bundle.json with encrypted key custody.\n - Opens enigma-desktop with lexical embeddings, avoiding a first-run neural model download.\n\nExisting vaults are never overwritten. If a bundle exists without its passphrase file, bootstrap fails closed.\n`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function parseBootstrapArgs(argv = process.argv.slice(2)) {
|
|
36
|
+
const options = {
|
|
37
|
+
dryRun: false,
|
|
38
|
+
initialize: true,
|
|
39
|
+
launch: DEFAULT_LAUNCH,
|
|
40
|
+
};
|
|
41
|
+
let launchSelection;
|
|
42
|
+
|
|
43
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
44
|
+
const arg = argv[index];
|
|
45
|
+
if (arg === '--help' || arg === '-h') {
|
|
46
|
+
options.help = true;
|
|
47
|
+
} else if (arg === '--dry-run') {
|
|
48
|
+
options.dryRun = true;
|
|
49
|
+
} else if (arg === '--desktop') {
|
|
50
|
+
if (launchSelection && launchSelection !== 'desktop') throw new Error('Choose one launch mode.');
|
|
51
|
+
launchSelection = 'desktop';
|
|
52
|
+
options.launch = 'desktop';
|
|
53
|
+
} else if (arg === '--terminal') {
|
|
54
|
+
if (launchSelection && launchSelection !== 'terminal') throw new Error('Choose one launch mode.');
|
|
55
|
+
launchSelection = 'terminal';
|
|
56
|
+
options.launch = 'terminal';
|
|
57
|
+
} else if (arg === '--no-launch') {
|
|
58
|
+
if (launchSelection && launchSelection !== 'none') throw new Error('Choose one launch mode.');
|
|
59
|
+
launchSelection = 'none';
|
|
60
|
+
options.launch = 'none';
|
|
61
|
+
} else if (arg === '--no-init') {
|
|
62
|
+
options.initialize = false;
|
|
63
|
+
} else if (arg === '--bundle') {
|
|
64
|
+
options.bundlePath = readRequiredValue(argv, index, arg);
|
|
65
|
+
index += 1;
|
|
66
|
+
} else if (arg === '--passphrase-file') {
|
|
67
|
+
options.passphraseFile = readRequiredValue(argv, index, arg);
|
|
68
|
+
index += 1;
|
|
69
|
+
} else if (arg === '--prefix') {
|
|
70
|
+
options.prefix = readRequiredValue(argv, index, arg);
|
|
71
|
+
index += 1;
|
|
72
|
+
} else {
|
|
73
|
+
throw new Error(`Unknown bootstrap argument: ${arg}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return options;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function npmRuntime(runtime, platform) {
|
|
81
|
+
const nodeCommand = resolve(runtime.nodeCommand ?? process.execPath);
|
|
82
|
+
const npmCliPath = runtime.npmCliPath
|
|
83
|
+
?? process.env.npm_execpath
|
|
84
|
+
?? (platform === 'win32' ? join(dirname(nodeCommand), 'node_modules', 'npm', 'bin', 'npm-cli.js') : undefined);
|
|
85
|
+
return npmCliPath
|
|
86
|
+
? { command: nodeCommand, argsPrefix: [resolve(npmCliPath)] }
|
|
87
|
+
: { command: 'npm', argsPrefix: [] };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function resolveRequestedPath(value, fallback, cwd) {
|
|
91
|
+
const requested = rejectUnsafeValue(value ?? fallback, 'Bootstrap path');
|
|
92
|
+
return isAbsolute(requested) ? resolve(requested) : resolve(cwd, requested);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function buildBootstrapPlan(options = {}, runtime = {}) {
|
|
96
|
+
const platform = runtime.platform ?? process.platform;
|
|
97
|
+
const cwd = runtime.cwd ?? process.cwd();
|
|
98
|
+
const home = runtime.homeDir ?? homedir();
|
|
99
|
+
const packageDir = resolve(rejectUnsafeValue(runtime.packageDir ?? DEFAULT_PACKAGE_DIR, 'Package directory'));
|
|
100
|
+
const enigmaDir = resolve(runtime.enigmaDir ?? join(home, '.enigma'));
|
|
101
|
+
const bundlePath = resolveRequestedPath(options.bundlePath, join(enigmaDir, 'bundle.json'), cwd);
|
|
102
|
+
const passphraseFile = resolveRequestedPath(options.passphraseFile, join(enigmaDir, 'passphrase'), cwd);
|
|
103
|
+
const prefix = options.prefix === undefined ? undefined : resolveRequestedPath(options.prefix, options.prefix, cwd);
|
|
104
|
+
const archiveDir = runtime.archiveDir ?? '<temporary-pack-directory>';
|
|
105
|
+
const npm = npmRuntime(runtime, platform);
|
|
106
|
+
const npmArgs = (args) => [...npm.argsPrefix, ...args];
|
|
107
|
+
const installArgs = ['install', '--global', '<packed-tarball>'];
|
|
108
|
+
const globalRootArgs = ['root', '--global'];
|
|
109
|
+
if (prefix) {
|
|
110
|
+
installArgs.push('--prefix', prefix);
|
|
111
|
+
globalRootArgs.push('--prefix', prefix);
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
schema: BOOTSTRAP_SCHEMA,
|
|
115
|
+
platform,
|
|
116
|
+
nodeCommand: resolve(runtime.nodeCommand ?? process.execPath),
|
|
117
|
+
packageDir,
|
|
118
|
+
bundlePath,
|
|
119
|
+
passphraseFile,
|
|
120
|
+
prefix,
|
|
121
|
+
dryRun: options.dryRun === true,
|
|
122
|
+
initialize: options.initialize !== false,
|
|
123
|
+
launch: options.launch ?? DEFAULT_LAUNCH,
|
|
124
|
+
archiveDir,
|
|
125
|
+
commands: {
|
|
126
|
+
pack: { command: npm.command, args: npmArgs(['pack', packageDir, '--json', '--pack-destination', archiveDir]), cwd: packageDir },
|
|
127
|
+
install: { command: npm.command, args: npmArgs(installArgs), cwd: packageDir },
|
|
128
|
+
globalRoot: { command: npm.command, args: npmArgs(globalRootArgs), cwd: packageDir },
|
|
129
|
+
},
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export function installedHarnessCommands(plan, globalRoot) {
|
|
134
|
+
const root = rejectUnsafeValue(String(globalRoot ?? '').trim(), 'npm global root');
|
|
135
|
+
if (!isAbsolute(root)) throw new Error('npm global root must be absolute.');
|
|
136
|
+
const installedPackageDir = join(resolve(root), 'enigma-memory');
|
|
137
|
+
const entry = (path) => join(installedPackageDir, path);
|
|
138
|
+
return {
|
|
139
|
+
installedPackageDir,
|
|
140
|
+
version: {
|
|
141
|
+
command: plan.nodeCommand,
|
|
142
|
+
args: [entry('apps/cli/bin/enigma.mjs'), '--version'],
|
|
143
|
+
cwd: installedPackageDir,
|
|
144
|
+
},
|
|
145
|
+
setup: {
|
|
146
|
+
command: plan.nodeCommand,
|
|
147
|
+
args: [entry('apps/cli/bin/enigma.mjs'), 'setup', '--bundle', plan.bundlePath, '--out-dir', dirname(plan.bundlePath), '--passphrase-file', plan.passphraseFile],
|
|
148
|
+
cwd: installedPackageDir,
|
|
149
|
+
},
|
|
150
|
+
desktop: {
|
|
151
|
+
command: plan.nodeCommand,
|
|
152
|
+
args: [entry('apps/cli/bin/enigma-desktop.mjs'), '--bundle', plan.bundlePath, '--passphrase-file', plan.passphraseFile, '--embedding-model', 'lexical'],
|
|
153
|
+
cwd: installedPackageDir,
|
|
154
|
+
},
|
|
155
|
+
terminal: {
|
|
156
|
+
command: plan.nodeCommand,
|
|
157
|
+
args: [entry('apps/cli/bin/enigma-terminal.mjs'), '--bundle', plan.bundlePath],
|
|
158
|
+
cwd: installedPackageDir,
|
|
159
|
+
},
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async function fileExists(path, accessImpl = access) {
|
|
164
|
+
try {
|
|
165
|
+
await accessImpl(path);
|
|
166
|
+
return true;
|
|
167
|
+
} catch {
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export function windowsSidFromWhoami(stdout) {
|
|
173
|
+
const sid = String(stdout ?? '').match(/\bS-\d-(?:\d+-)+\d+\b/u)?.[0];
|
|
174
|
+
if (!sid) throw new Error('Unable to resolve the current Windows user SID.');
|
|
175
|
+
return sid;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
function windowsSystemTools(runtime = {}) {
|
|
180
|
+
const systemRoot = rejectUnsafeValue(
|
|
181
|
+
runtime.windowsSystemRoot ?? process.env.SystemRoot ?? process.env.WINDIR,
|
|
182
|
+
'Windows system root',
|
|
183
|
+
);
|
|
184
|
+
if (!isAbsolute(systemRoot)) throw new Error('Windows system root must be absolute.');
|
|
185
|
+
const system32 = join(systemRoot, 'System32');
|
|
186
|
+
return {
|
|
187
|
+
system32,
|
|
188
|
+
whoami: join(system32, 'whoami.exe'),
|
|
189
|
+
powershell: join(system32, 'WindowsPowerShell', 'v1.0', 'powershell.exe'),
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async function currentWindowsUserSid(runCommand, runtime) {
|
|
194
|
+
const tools = windowsSystemTools(runtime);
|
|
195
|
+
const whoami = await runCommand({
|
|
196
|
+
command: tools.whoami,
|
|
197
|
+
args: ['/user', '/fo', 'csv', '/nh'],
|
|
198
|
+
cwd: tools.system32,
|
|
199
|
+
}, { capture: true, step: 'resolve_windows_user' });
|
|
200
|
+
return windowsSidFromWhoami(whoami.stdout);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const WINDOWS_VERIFY_CURRENT_USER_ACL_SCRIPT = [
|
|
204
|
+
"$ErrorActionPreference = 'Stop'",
|
|
205
|
+
'$path = $env:ENIGMA_BOOTSTRAP_ACL_PATH',
|
|
206
|
+
'$expectedSid = [System.Security.Principal.SecurityIdentifier]::new($env:ENIGMA_BOOTSTRAP_ACL_SID)',
|
|
207
|
+
'$sections = [System.Security.AccessControl.AccessControlSections]::Access -bor [System.Security.AccessControl.AccessControlSections]::Owner',
|
|
208
|
+
'$acl = [System.IO.File]::GetAccessControl($path, $sections)',
|
|
209
|
+
'$owner = $acl.GetOwner([System.Security.Principal.SecurityIdentifier])',
|
|
210
|
+
'$rules = @($acl.GetAccessRules($true, $true, [System.Security.Principal.SecurityIdentifier]))',
|
|
211
|
+
'$valid = $owner.Value -eq $expectedSid.Value -and $acl.AreAccessRulesProtected -and $rules.Count -eq 1',
|
|
212
|
+
'if ($valid) { $rule = $rules[0]; $valid = -not $rule.IsInherited -and $rule.IdentityReference.Value -eq $expectedSid.Value -and $rule.AccessControlType -eq [System.Security.AccessControl.AccessControlType]::Allow -and $rule.FileSystemRights -eq [System.Security.AccessControl.FileSystemRights]::FullControl -and $rule.InheritanceFlags -eq [System.Security.AccessControl.InheritanceFlags]::None -and $rule.PropagationFlags -eq [System.Security.AccessControl.PropagationFlags]::None }',
|
|
213
|
+
"if (-not $valid) { throw 'Windows passphrase ACL is not current-user-only.' }",
|
|
214
|
+
'[Console]::Out.WriteLine($expectedSid.Value)',
|
|
215
|
+
].join('; ');
|
|
216
|
+
|
|
217
|
+
async function verifyWindowsPassphraseAclForSid(path, sid, runCommand, runtime) {
|
|
218
|
+
const tools = windowsSystemTools(runtime);
|
|
219
|
+
let verification;
|
|
220
|
+
try {
|
|
221
|
+
verification = await runCommand({
|
|
222
|
+
command: tools.powershell,
|
|
223
|
+
args: ['-NoLogo', '-NoProfile', '-NonInteractive', '-Command', WINDOWS_VERIFY_CURRENT_USER_ACL_SCRIPT],
|
|
224
|
+
cwd: tools.system32,
|
|
225
|
+
}, {
|
|
226
|
+
capture: true,
|
|
227
|
+
step: 'verify_passphrase_acl',
|
|
228
|
+
env: {
|
|
229
|
+
...process.env,
|
|
230
|
+
ENIGMA_BOOTSTRAP_ACL_PATH: path,
|
|
231
|
+
ENIGMA_BOOTSTRAP_ACL_SID: sid,
|
|
232
|
+
},
|
|
233
|
+
});
|
|
234
|
+
} catch (error) {
|
|
235
|
+
throw new Error(`Windows passphrase ACL is not current-user-only: ${String(error?.message ?? error)}`);
|
|
236
|
+
}
|
|
237
|
+
if (windowsSidFromWhoami(verification.stdout) !== sid) {
|
|
238
|
+
throw new Error('Windows passphrase ACL verification returned a different SID.');
|
|
239
|
+
}
|
|
240
|
+
return { verified: true, sid, ace_count: 1 };
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export async function verifyWindowsPassphraseAcl(path, runtime = {}) {
|
|
244
|
+
const runCommand = runtime.runAclCommand ?? defaultRunCommand;
|
|
245
|
+
const sid = await currentWindowsUserSid(runCommand, runtime);
|
|
246
|
+
return verifyWindowsPassphraseAclForSid(path, sid, runCommand, runtime);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const WINDOWS_CREATE_CURRENT_USER_PASSPHRASE_SCRIPT = [
|
|
250
|
+
"$ErrorActionPreference = 'Stop'",
|
|
251
|
+
'$path = $env:ENIGMA_BOOTSTRAP_ACL_PATH',
|
|
252
|
+
'$sid = [System.Security.Principal.WindowsIdentity]::GetCurrent().User',
|
|
253
|
+
'$acl = [System.Security.AccessControl.FileSecurity]::new()',
|
|
254
|
+
'$acl.SetOwner($sid)',
|
|
255
|
+
'$acl.SetAccessRuleProtection($true, $false)',
|
|
256
|
+
'$rule = [System.Security.AccessControl.FileSystemAccessRule]::new($sid, [System.Security.AccessControl.FileSystemRights]::FullControl, [System.Security.AccessControl.AccessControlType]::Allow)',
|
|
257
|
+
'$acl.SetAccessRule($rule)',
|
|
258
|
+
'$created = $false',
|
|
259
|
+
'try { $stream = [System.IO.FileStream]::new($path, [System.IO.FileMode]::CreateNew, [System.Security.AccessControl.FileSystemRights]::Write, [System.IO.FileShare]::None, 4096, [System.IO.FileOptions]::WriteThrough, $acl); $created = $true; try { [Console]::OpenStandardInput().CopyTo($stream); $stream.Flush($true) } finally { $stream.Dispose() }; [Console]::Out.WriteLine($sid.Value) } catch { if ($created) { [System.IO.File]::Delete($path) }; throw }',
|
|
260
|
+
].join('; ');
|
|
261
|
+
|
|
262
|
+
async function createWindowsPassphraseFile(path, secret, runtime = {}) {
|
|
263
|
+
const runCommand = runtime.runAclCommand ?? defaultRunCommand;
|
|
264
|
+
const tools = windowsSystemTools(runtime);
|
|
265
|
+
const creation = await runCommand({
|
|
266
|
+
command: tools.powershell,
|
|
267
|
+
args: ['-NoLogo', '-NoProfile', '-NonInteractive', '-Command', WINDOWS_CREATE_CURRENT_USER_PASSPHRASE_SCRIPT],
|
|
268
|
+
cwd: tools.system32,
|
|
269
|
+
}, {
|
|
270
|
+
capture: true,
|
|
271
|
+
input: Buffer.from(secret, 'utf8'),
|
|
272
|
+
step: 'create_secure_passphrase',
|
|
273
|
+
env: {
|
|
274
|
+
...process.env,
|
|
275
|
+
ENIGMA_BOOTSTRAP_ACL_PATH: path,
|
|
276
|
+
},
|
|
277
|
+
});
|
|
278
|
+
const sid = windowsSidFromWhoami(creation.stdout);
|
|
279
|
+
try {
|
|
280
|
+
return await verifyWindowsPassphraseAclForSid(path, sid, runCommand, runtime);
|
|
281
|
+
} catch (error) {
|
|
282
|
+
await (runtime.rmImpl ?? rm)(path, { force: true });
|
|
283
|
+
throw error;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export async function ensureBootstrapPassphrase(plan, runtime = {}) {
|
|
288
|
+
const accessImpl = runtime.accessImpl ?? access;
|
|
289
|
+
const bundleExists = await fileExists(plan.bundlePath, accessImpl);
|
|
290
|
+
const passphraseExists = await fileExists(plan.passphraseFile, accessImpl);
|
|
291
|
+
if (bundleExists && !passphraseExists) {
|
|
292
|
+
throw new Error('An Enigma bundle already exists but its passphrase file is missing. Refusing to generate a replacement credential.');
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const chmodImpl = runtime.chmodImpl ?? chmod;
|
|
296
|
+
if (passphraseExists) {
|
|
297
|
+
if (plan.platform === 'win32') {
|
|
298
|
+
try {
|
|
299
|
+
const acl = await verifyWindowsPassphraseAcl(plan.passphraseFile, runtime);
|
|
300
|
+
return { created: false, bundleExists, acl_verified: acl.verified };
|
|
301
|
+
} catch (error) {
|
|
302
|
+
throw new Error(`The existing Windows passphrase file is not current-user-only: ${String(error?.message ?? error)}`);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
await chmodImpl(plan.passphraseFile, 0o600);
|
|
306
|
+
return { created: false, bundleExists };
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const mkdirImpl = runtime.mkdirImpl ?? mkdir;
|
|
310
|
+
const randomBytesImpl = runtime.randomBytesImpl ?? randomBytes;
|
|
311
|
+
await mkdirImpl(dirname(plan.passphraseFile), { recursive: true });
|
|
312
|
+
const secret = `${randomBytesImpl(32).toString('base64')}\n`;
|
|
313
|
+
if (plan.platform === 'win32') {
|
|
314
|
+
try {
|
|
315
|
+
const acl = await createWindowsPassphraseFile(plan.passphraseFile, secret, runtime);
|
|
316
|
+
return { created: true, bundleExists, acl_verified: acl.verified };
|
|
317
|
+
} catch (error) {
|
|
318
|
+
throw new Error(`Unable to atomically create the generated Windows passphrase file: ${String(error?.message ?? error)}`);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const openImpl = runtime.openImpl ?? open;
|
|
323
|
+
const rmImpl = runtime.rmImpl ?? rm;
|
|
324
|
+
let passphraseHandle;
|
|
325
|
+
try {
|
|
326
|
+
passphraseHandle = await openImpl(plan.passphraseFile, 'wx', 0o600);
|
|
327
|
+
} catch (error) {
|
|
328
|
+
if (error?.code === 'EEXIST') {
|
|
329
|
+
throw new Error('The passphrase file appeared during bootstrap. Refusing to use an unverified credential.');
|
|
330
|
+
}
|
|
331
|
+
throw error;
|
|
332
|
+
}
|
|
333
|
+
try {
|
|
334
|
+
await chmodImpl(plan.passphraseFile, 0o600);
|
|
335
|
+
await passphraseHandle.writeFile(secret, { encoding: 'utf8' });
|
|
336
|
+
await passphraseHandle.sync();
|
|
337
|
+
await chmodImpl(plan.passphraseFile, 0o600);
|
|
338
|
+
await passphraseHandle.close();
|
|
339
|
+
passphraseHandle = undefined;
|
|
340
|
+
return { created: true, bundleExists };
|
|
341
|
+
} catch (error) {
|
|
342
|
+
if (passphraseHandle) await passphraseHandle.close().catch(() => {});
|
|
343
|
+
await rmImpl(plan.passphraseFile, { force: true });
|
|
344
|
+
throw error;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
export function installerFailureCode(stderr) {
|
|
349
|
+
const match = String(stderr ?? '').match(/npm\s+(?:error|ERR!)\s+code\s+([A-Z0-9_-]+)/iu);
|
|
350
|
+
return match?.[1]?.toUpperCase();
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
async function defaultRunCommand(spec, options = {}) {
|
|
354
|
+
const capture = options.capture === true;
|
|
355
|
+
const hasInput = options.input !== undefined;
|
|
356
|
+
const child = spawn(spec.command, spec.args, {
|
|
357
|
+
cwd: spec.cwd,
|
|
358
|
+
env: options.env ?? process.env,
|
|
359
|
+
shell: false,
|
|
360
|
+
windowsHide: true,
|
|
361
|
+
stdio: capture
|
|
362
|
+
? [hasInput ? 'pipe' : 'ignore', 'pipe', 'pipe']
|
|
363
|
+
: (hasInput ? ['pipe', 'inherit', 'inherit'] : 'inherit'),
|
|
364
|
+
});
|
|
365
|
+
let stdout = '';
|
|
366
|
+
let stderr = '';
|
|
367
|
+
if (capture) {
|
|
368
|
+
child.stdout.setEncoding('utf8');
|
|
369
|
+
child.stderr.setEncoding('utf8');
|
|
370
|
+
child.stdout.on('data', (chunk) => { stdout += chunk; });
|
|
371
|
+
child.stderr.on('data', (chunk) => { stderr += chunk; });
|
|
372
|
+
}
|
|
373
|
+
const exitPromise = new Promise((resolveExit, rejectExit) => {
|
|
374
|
+
child.once('error', rejectExit);
|
|
375
|
+
child.once('exit', (code) => resolveExit(code ?? 1));
|
|
376
|
+
});
|
|
377
|
+
if (hasInput) {
|
|
378
|
+
child.stdin.on('error', () => {});
|
|
379
|
+
child.stdin.end(options.input);
|
|
380
|
+
}
|
|
381
|
+
const exitCode = await exitPromise;
|
|
382
|
+
if (exitCode !== 0) {
|
|
383
|
+
const failureCode = installerFailureCode(stderr);
|
|
384
|
+
throw new Error(`Bootstrap command failed: ${options.step ?? 'unknown'}${failureCode ? ` (${failureCode})` : ''}.`);
|
|
385
|
+
}
|
|
386
|
+
return { exitCode, stdout, stderr };
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
export function packedTarballPath(stdout, archiveDir) {
|
|
390
|
+
let records;
|
|
391
|
+
try {
|
|
392
|
+
records = JSON.parse(String(stdout ?? ''));
|
|
393
|
+
} catch {
|
|
394
|
+
throw new Error('npm pack did not return valid JSON.');
|
|
395
|
+
}
|
|
396
|
+
const filename = records?.[0]?.filename;
|
|
397
|
+
if (typeof filename !== 'string' || basename(filename) !== filename || !filename.endsWith('.tgz')) {
|
|
398
|
+
throw new Error('npm pack returned an unsafe archive filename.');
|
|
399
|
+
}
|
|
400
|
+
return join(archiveDir, filename);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
function publicPlan(plan) {
|
|
404
|
+
return {
|
|
405
|
+
schema: plan.schema,
|
|
406
|
+
ok: true,
|
|
407
|
+
mode: plan.dryRun ? 'dry-run' : 'execute',
|
|
408
|
+
package_source: 'currently executing package',
|
|
409
|
+
install_scope: plan.prefix ? 'explicit_prefix' : 'global_npm_prefix',
|
|
410
|
+
initialize_vault: plan.initialize,
|
|
411
|
+
launch: plan.launch,
|
|
412
|
+
encrypted: plan.initialize,
|
|
413
|
+
generated_credential: plan.initialize ? 'secure local passphrase file when absent' : null,
|
|
414
|
+
neural_model_download_required: false,
|
|
415
|
+
commands: ['install harness', 'verify enigma command', ...(plan.initialize ? ['initialize encrypted vault'] : []), ...(plan.launch === 'none' ? [] : [`launch ${plan.launch}`])],
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
export async function runBootstrap(argv = process.argv.slice(2), runtime = {}) {
|
|
420
|
+
const options = Array.isArray(argv) ? parseBootstrapArgs(argv) : { ...argv };
|
|
421
|
+
if (options.help) return { help: usage() };
|
|
422
|
+
validateNodeVersion(runtime.nodeVersion ?? process.versions.node);
|
|
423
|
+
const mkdtempImpl = runtime.mkdtempImpl ?? mkdtemp;
|
|
424
|
+
const archiveDir = options.dryRun === true
|
|
425
|
+
? undefined
|
|
426
|
+
: await mkdtempImpl(join(runtime.tempDir ?? tmpdir(), 'enigma-bootstrap-'));
|
|
427
|
+
const plan = buildBootstrapPlan(options, { ...runtime, archiveDir });
|
|
428
|
+
if (plan.dryRun) return publicPlan(plan);
|
|
429
|
+
|
|
430
|
+
const runCommand = runtime.runCommand ?? defaultRunCommand;
|
|
431
|
+
let credential = { created: false, bundleExists: false };
|
|
432
|
+
if (plan.initialize) credential = await ensureBootstrapPassphrase(plan, runtime);
|
|
433
|
+
|
|
434
|
+
const rmImpl = runtime.rmImpl ?? rm;
|
|
435
|
+
try {
|
|
436
|
+
const packResult = await runCommand(plan.commands.pack, { capture: true, step: 'pack_harness' });
|
|
437
|
+
const tarball = packedTarballPath(packResult.stdout, plan.archiveDir);
|
|
438
|
+
const installSpec = {
|
|
439
|
+
...plan.commands.install,
|
|
440
|
+
args: plan.commands.install.args.map((arg) => arg === '<packed-tarball>' ? tarball : arg),
|
|
441
|
+
};
|
|
442
|
+
await runCommand(installSpec, { capture: true, step: 'install_harness' });
|
|
443
|
+
} finally {
|
|
444
|
+
await rmImpl(plan.archiveDir, { recursive: true, force: true });
|
|
445
|
+
}
|
|
446
|
+
const globalRootResult = await runCommand(plan.commands.globalRoot, { capture: true, step: 'resolve_global_root' });
|
|
447
|
+
const harnessCommands = installedHarnessCommands(plan, globalRootResult.stdout);
|
|
448
|
+
const versionResult = await runCommand(harnessCommands.version, { capture: true, step: 'verify_install' });
|
|
449
|
+
const readFileImpl = runtime.readFileImpl ?? readFile;
|
|
450
|
+
const packageJson = JSON.parse(await readFileImpl(join(plan.packageDir, 'package.json'), 'utf8'));
|
|
451
|
+
if (!String(versionResult.stdout ?? '').includes(String(packageJson.version))) {
|
|
452
|
+
throw new Error('Installed Enigma version could not be verified.');
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
if (plan.initialize && !credential.bundleExists) {
|
|
456
|
+
await runCommand(harnessCommands.setup, { capture: true, step: 'initialize_vault' });
|
|
457
|
+
if (!(await fileExists(plan.bundlePath, runtime.accessImpl ?? access))) throw new Error('Enigma setup completed without creating the bundle.');
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
const result = {
|
|
461
|
+
...publicPlan(plan),
|
|
462
|
+
mode: 'execute',
|
|
463
|
+
installed: true,
|
|
464
|
+
vault_initialized: plan.initialize,
|
|
465
|
+
passphrase_created: credential.created,
|
|
466
|
+
existing_bundle_reused: credential.bundleExists,
|
|
467
|
+
};
|
|
468
|
+
|
|
469
|
+
if (plan.launch === 'desktop') {
|
|
470
|
+
await runCommand(harnessCommands.desktop, { capture: false, step: 'launch_desktop' });
|
|
471
|
+
} else if (plan.launch === 'terminal') {
|
|
472
|
+
const passphrase = (await readFileImpl(plan.passphraseFile, 'utf8')).trim();
|
|
473
|
+
await runCommand(harnessCommands.terminal, {
|
|
474
|
+
capture: false,
|
|
475
|
+
step: 'launch_terminal',
|
|
476
|
+
env: { ...process.env, ENIGMA_PASSPHRASE: passphrase },
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
return result;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
function renderResult(result) {
|
|
483
|
+
if (result.help) return result.help;
|
|
484
|
+
if (result.mode === 'dry-run') return `${JSON.stringify(result, null, 2)}\n`;
|
|
485
|
+
return [
|
|
486
|
+
'Enigma harness installed.',
|
|
487
|
+
result.existing_bundle_reused ? 'Existing encrypted vault preserved.' : 'Encrypted local vault ready.',
|
|
488
|
+
result.launch === 'none' ? 'Run enigma-terminal or enigma-desktop to begin.' : `${result.launch === 'desktop' ? 'Desktop' : 'Terminal'} session closed. Run it again at any time.`,
|
|
489
|
+
].join('\n') + '\n';
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
async function main() {
|
|
493
|
+
try {
|
|
494
|
+
const result = await runBootstrap();
|
|
495
|
+
process.stdout.write(renderResult(result));
|
|
496
|
+
} catch (error) {
|
|
497
|
+
process.stderr.write(`Enigma bootstrap failed: ${String(error?.message ?? error)}\n`);
|
|
498
|
+
process.exitCode = 1;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
if (resolve(process.argv[1] ?? '') === SCRIPT_PATH) await main();
|
|
@@ -52,9 +52,9 @@ export const EDGE_BACKEND_REF_ENV_NAMES = Object.freeze({
|
|
|
52
52
|
gateway_deployment: Object.freeze(['ENIGMA_GATEWAY_DEPLOYMENT_REF']),
|
|
53
53
|
});
|
|
54
54
|
const CLAIM_BOUNDARY = Object.freeze([
|
|
55
|
-
'Edge backend Workers provide public HTTPS liveness/readiness
|
|
56
|
-
'
|
|
57
|
-
'
|
|
55
|
+
'Edge backend Workers provide public HTTPS liveness/readiness for urgent bootstrap only; hosted_backend_live is always false.',
|
|
56
|
+
'Bootstrap readiness covers operator-supplied refs and binding checks, but the Worker does not implement the relay/gateway runtime or prove production readiness.',
|
|
57
|
+
'The private data plane is closed and non-operational; raw memory, prompts, transcripts, provider responses, credentials, and private keys must not be returned by these Workers.',
|
|
58
58
|
]);
|
|
59
59
|
|
|
60
60
|
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/@]+@|sk-[A-Za-z0-9_-]{16,}|AKIA[0-9A-Z]{16})/iu;
|
|
@@ -98,6 +98,12 @@ const REQUIRED_REFS = ${requiredRefs};
|
|
|
98
98
|
const REF_ENV_NAMES = ${refEnvNames};
|
|
99
99
|
const PRIVATE_ROUTES = ${privateRoutes};
|
|
100
100
|
const CLAIM_BOUNDARY = ${boundary};
|
|
101
|
+
const RUNTIME_CAPABILITY = Object.freeze({
|
|
102
|
+
marker: 'enigma.private_data_plane.runtime.v1',
|
|
103
|
+
bootstrap_worker: true,
|
|
104
|
+
private_data_plane_operational: false,
|
|
105
|
+
authenticated_data_plane_supported: false
|
|
106
|
+
});
|
|
101
107
|
|
|
102
108
|
function json(body, status = 200) {
|
|
103
109
|
return new Response(JSON.stringify(body, null, 2), {
|
|
@@ -194,6 +200,9 @@ async function readiness(env) {
|
|
|
194
200
|
service_kind: SERVICE_KIND,
|
|
195
201
|
runtime: 'cloudflare-workers',
|
|
196
202
|
generated_at: GENERATED_AT,
|
|
203
|
+
hosted_backend_live: false,
|
|
204
|
+
private_data_plane_operational: false,
|
|
205
|
+
runtime_capability: RUNTIME_CAPABILITY,
|
|
197
206
|
status: ok ? 'ready' : 'blocked',
|
|
198
207
|
evidence_refs: refs,
|
|
199
208
|
evidence_ref_count: configured.length,
|
|
@@ -210,7 +219,7 @@ async function readiness(env) {
|
|
|
210
219
|
}
|
|
211
220
|
|
|
212
221
|
function error(code, message) {
|
|
213
|
-
return { ok: false, service: SERVICE_NAME, error: { code, message }, claim_boundary: CLAIM_BOUNDARY };
|
|
222
|
+
return { ok: false, service: SERVICE_NAME, hosted_backend_live: false, private_data_plane_operational: false, runtime_capability: RUNTIME_CAPABILITY, error: { code, message }, claim_boundary: CLAIM_BOUNDARY };
|
|
214
223
|
}
|
|
215
224
|
|
|
216
225
|
function plaintextLooking(value, seen = new WeakSet()) {
|
|
@@ -239,7 +248,7 @@ export default {
|
|
|
239
248
|
async fetch(request, env) {
|
|
240
249
|
const url = new URL(request.url);
|
|
241
250
|
if (request.method !== 'GET' && request.method !== 'HEAD' && request.method !== 'POST') return json(error('METHOD_NOT_ALLOWED', 'Only GET, HEAD, and POST are allowed.'), 405);
|
|
242
|
-
if (url.pathname === '/livez') return json({ ok: true, service: SERVICE_NAME, service_kind: SERVICE_KIND, runtime: 'cloudflare-workers', claim_boundary: CLAIM_BOUNDARY });
|
|
251
|
+
if (url.pathname === '/livez') return json({ ok: true, service: SERVICE_NAME, service_kind: SERVICE_KIND, runtime: 'cloudflare-workers', hosted_backend_live: false, private_data_plane_operational: false, runtime_capability: RUNTIME_CAPABILITY, claim_boundary: CLAIM_BOUNDARY });
|
|
243
252
|
if (url.pathname === '/readyz') {
|
|
244
253
|
const body = await readiness(env);
|
|
245
254
|
return json(body, body.ok ? 200 : 503);
|
|
@@ -264,6 +273,10 @@ export function validateEdgeBackendWorkerSource(source, configInput) {
|
|
|
264
273
|
if (!text.includes("url.pathname === '/readyz'")) blockers.push({ path: 'source.readyz', message: 'worker must route /readyz' });
|
|
265
274
|
if (!text.includes('PRIVATE_ROUTE_CLOSED')) blockers.push({ path: 'source.private_routes', message: 'worker must fail closed for private routes' });
|
|
266
275
|
if (text.includes('hosted_probe_only') || text.includes('pages_edge_probe_only')) blockers.push({ path: 'source.claim', message: 'edge backend worker must not masquerade as probe-only payload' });
|
|
276
|
+
if (!text.includes('hosted_backend_live: false')) blockers.push({ path: 'source.claim', message: 'bootstrap worker must explicitly declare hosted_backend_live false' });
|
|
277
|
+
if (!text.includes('private_data_plane_operational: false')) blockers.push({ path: 'source.capability', message: 'bootstrap worker must explicitly declare the private data plane non-operational' });
|
|
278
|
+
if (!text.includes('authenticated_data_plane_supported: false')) blockers.push({ path: 'source.capability', message: 'bootstrap worker must explicitly declare authenticated data-plane support unavailable' });
|
|
279
|
+
if (/hosted_backend_live\s*:\s*true/u.test(text)) blockers.push({ path: 'source.claim', message: 'bootstrap worker must never declare hosted_backend_live true' });
|
|
267
280
|
if (SECRET_VALUE_RE.test(text)) blockers.push({ path: 'source.secret', message: 'worker source contains secret-looking literal' });
|
|
268
281
|
for (const key of REQUIRED_REF_KEYS) if (!text.includes(key)) blockers.push({ path: `source.refs.${key}`, message: `worker source missing ${key}` });
|
|
269
282
|
return {
|
|
@@ -286,6 +299,8 @@ export function buildEdgeBackendWorkerBundle(options = {}) {
|
|
|
286
299
|
hostname: `${config.hostPrefix}.${domain}`,
|
|
287
300
|
default_routes: ['/livez', '/readyz'],
|
|
288
301
|
private_routes: [...config.privateRoutes],
|
|
302
|
+
hosted_backend_live: false,
|
|
303
|
+
private_data_plane_operational: false,
|
|
289
304
|
validation: validateEdgeBackendWorkerSource(source, config),
|
|
290
305
|
files: {
|
|
291
306
|
'worker.mjs': source,
|