enigma-memory 0.1.16 → 0.1.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +23 -13
  2. package/apps/cli/bin/enigma.mjs +3341 -3151
  3. package/deploy/SIMULATION.md +14 -9
  4. package/deploy/docker-compose.local-production-simulation.yml +18 -12
  5. package/docs/benchmark-attestation-network.md +487 -487
  6. package/docs/benchmark-reproducibility.md +289 -240
  7. package/docs/blockchain-only-mechanisms.md +400 -388
  8. package/docs/client-connectors.md +10 -6
  9. package/docs/demo-proof-network.md +275 -275
  10. package/docs/developer-ecosystem.md +7 -5
  11. package/docs/developer-proof-quickstart.md +325 -325
  12. package/docs/enigma-memory-ready-conformance.md +376 -376
  13. package/docs/hosted-cloud-product.md +29 -0
  14. package/docs/install-anywhere.md +16 -12
  15. package/docs/memory-benchmarks.md +16 -1
  16. package/docs/memory-drive-health-model.md +690 -649
  17. package/docs/proof-network-build-notes.md +240 -240
  18. package/docs/proof-network.md +339 -282
  19. package/docs/sdk-api.md +324 -324
  20. package/docs/solana-proof-rail.md +453 -453
  21. package/package.json +279 -278
  22. package/packages/core/src/index.js +1 -1
  23. package/packages/hosted-cloud/src/index.js +470 -2
  24. package/packages/mcp-server/src/index.js +1185 -1185
  25. package/packages/passport/src/index.js +740 -6
  26. package/packages/proof-network/src/index.js +139 -0
  27. package/packages/vault/src/index.js +187 -25
  28. package/scripts/build-benchmark-proof-release.mjs +15 -3
  29. package/scripts/build-hosted-api-key-lifecycle.mjs +274 -274
  30. package/scripts/build-hosted-customer-lifecycle.mjs +476 -456
  31. package/scripts/build-installer-assets.mjs +389 -389
  32. package/scripts/build-production-unblocker.mjs +409 -409
  33. package/scripts/build-proof-network-packet.mjs +213 -213
  34. package/scripts/check.mjs +8 -1
  35. package/scripts/release-audit.mjs +9 -6
  36. package/scripts/release-provenance.mjs +6 -2
  37. package/scripts/run-standard-memory-benchmarks.mjs +1354 -1194
  38. package/scripts/scan-secrets.mjs +177 -0
  39. package/scripts/simulate-production-env.mjs +64 -8
@@ -0,0 +1,177 @@
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
+ ])
34
+
35
+ const SKIP_EXTENSIONS = new Set([
36
+ '.png',
37
+ '.jpg',
38
+ '.jpeg',
39
+ '.gif',
40
+ '.webp',
41
+ '.svg',
42
+ '.ico',
43
+ '.pdf',
44
+ '.zip',
45
+ '.tar',
46
+ '.gz',
47
+ '.tgz',
48
+ '.woff',
49
+ '.woff2',
50
+ '.ttf',
51
+ '.otf',
52
+ '.eot',
53
+ '.mp4',
54
+ '.webm',
55
+ '.mp3',
56
+ '.ogg',
57
+ ])
58
+
59
+ // The security-placeholder detector matches its own pattern literal below.
60
+ // The preceding comment marks that line as an approved self-reference.
61
+ const DETECTORS = [
62
+ {
63
+ id: 'pem-private-key',
64
+ pattern: /-----BEGIN\s+(?:RSA\s+|EC\s+|DSA\s+|OPENSSH\s+|ENCRYPTED\s+)?PRIVATE\s+KEY-----/i,
65
+ },
66
+ {
67
+ id: 'aws-access-key-id',
68
+ pattern: /\b(?:AKIA|ASIA|AROA|AIDA)[0-9A-Z]{16}\b/,
69
+ },
70
+ {
71
+ id: 'aws-secret-access-key',
72
+ pattern: /\b(?:aws[_-]?secret[_-]?access[_-]?key|AWS[_-]?SECRET[_-]?ACCESS[_-]?KEY)\s*[=:]\s*["'][A-Za-z0-9/+=]{40}["']/i,
73
+ },
74
+ {
75
+ id: 'bearer-token',
76
+ // Skip placeholders like <token>, ${var}, and $ENV_VAR.
77
+ 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,
78
+ },
79
+ {
80
+ id: 'dsn-with-password',
81
+ pattern: /\b(?:postgres|postgresql|mysql|mysql2|mongodb|redis|amqp|amqps):\/\/[^:]+:[^@\s]+@[^\/\s]+/i,
82
+ },
83
+ {
84
+ id: 'security-placeholder',
85
+ pattern: /REPLACE-WITH/i, // secret-scan:approved self-reference pattern
86
+ },
87
+ ]
88
+
89
+ function hasApprovalComment(line) {
90
+ return APPROVAL_MARKERS.some((m) => m.test(line))
91
+ }
92
+
93
+ function isApproved(lines, index) {
94
+ if (hasApprovalComment(lines[index])) return true
95
+ for (let i = index - 1; i >= 0; i -= 1) {
96
+ const trimmed = lines[i].trim()
97
+ if (trimmed === '') continue
98
+ return hasApprovalComment(trimmed)
99
+ }
100
+ return false
101
+ }
102
+
103
+ function isSkippableFile(rel) {
104
+ const ext = path.extname(rel).toLowerCase()
105
+ if (SKIP_EXTENSIONS.has(ext)) return true
106
+ const base = path.basename(rel)
107
+ if (base === 'package-lock.json') return true
108
+ return false
109
+ }
110
+
111
+ function* walk(root) {
112
+ const entries = fs.readdirSync(root, { withFileTypes: true })
113
+ for (const entry of entries) {
114
+ const full = path.join(root, entry.name)
115
+ if (entry.isDirectory()) {
116
+ if (SKIP_DIRS.has(entry.name)) continue
117
+ yield* walk(full)
118
+ } else if (entry.isFile()) {
119
+ yield full
120
+ }
121
+ }
122
+ }
123
+
124
+ export function scanForSecrets(root) {
125
+ const findings = []
126
+ const rootResolved = path.resolve(root)
127
+ for (const fullPath of walk(rootResolved)) {
128
+ const rel = path.relative(rootResolved, fullPath)
129
+ if (isSkippableFile(rel)) continue
130
+ let text
131
+ try {
132
+ text = fs.readFileSync(fullPath, 'utf8')
133
+ } catch {
134
+ continue
135
+ }
136
+ const lines = text.split(/\r?\n/)
137
+ for (let i = 0; i < lines.length; i += 1) {
138
+ const line = lines[i]
139
+ if (isApproved(lines, i)) continue
140
+ for (const detector of DETECTORS) {
141
+ if (detector.pattern.test(line)) {
142
+ findings.push({
143
+ file: rel,
144
+ line: i + 1,
145
+ type: detector.id,
146
+ })
147
+ break
148
+ }
149
+ }
150
+ }
151
+ }
152
+ return findings
153
+ }
154
+
155
+ function formatFindings(findings) {
156
+ return findings
157
+ .map((f) => `${f.file}:${f.line}: ${f.type}`)
158
+ .join('\n')
159
+ }
160
+
161
+ function main() {
162
+ const rootArg = process.argv.includes('--root')
163
+ ? process.argv[process.argv.indexOf('--root') + 1]
164
+ : path.resolve(path.dirname(__filename), '..')
165
+ const root = path.resolve(rootArg)
166
+ const findings = scanForSecrets(root)
167
+ if (findings.length > 0) {
168
+ process.stderr.write(`secret-scan: ${findings.length} unapproved detection(s)\n`)
169
+ process.stderr.write(formatFindings(findings) + '\n')
170
+ process.exit(1)
171
+ }
172
+ process.stdout.write('secret-scan ok\n')
173
+ }
174
+
175
+ if (import.meta.url === pathToFileURL(process.argv[1]).href) {
176
+ main()
177
+ }
@@ -5,13 +5,14 @@
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(ROOT, 'deploy', 'secrets-simulation')
15
+ const DEFAULT_SECRETS_DIR = path.join(os.homedir(), '.enigma', 'simulation-secrets')
15
16
 
16
17
  const REQUIRED_SECRET_FILES = [
17
18
  'relay-signing-key',
@@ -27,6 +28,11 @@ const REQUIRED_SECRET_FILES = [
27
28
  'tls.key',
28
29
  ]
29
30
 
31
+ const BEARER_FILES_FOR_DIGEST = {
32
+ 'gateway-admin-auth-bearer': 'ENIGMA_GATEWAY_ADMIN_AUTH_BEARER_SHA256',
33
+ 'gateway-data-plane-auth-bearer': 'ENIGMA_GATEWAY_DATA_PLANE_AUTH_BEARER_SHA256',
34
+ }
35
+
30
36
  function parseArgs(argv) {
31
37
  const flags = { check: false, secretsDir: DEFAULT_SECRETS_DIR }
32
38
  for (let i = 0; i < argv.length; i += 1) {
@@ -41,8 +47,9 @@ function parseArgs(argv) {
41
47
  process.stdout.write(
42
48
  'Usage: node scripts/simulate-production-env.mjs [--check] [--secrets-dir <dir>]\n' +
43
49
  '\n' +
44
- 'Generates local-simulation secret files and a self-signed TLS certificate\n' +
45
- `under ${DEFAULT_SECRETS_DIR} (customizable with --secrets-dir).\n` +
50
+ 'Generates local-simulation secret files and a self-signed TLS certificate.\n' +
51
+ `By default files are written outside the repo to ${DEFAULT_SECRETS_DIR}.\n` +
52
+ 'A path inside the repository is rejected to prevent accidental commits.\n' +
46
53
  'Run with --check to verify required files exist and are non-empty.\n'
47
54
  )
48
55
  process.exit(0)
@@ -51,6 +58,22 @@ function parseArgs(argv) {
51
58
  return flags
52
59
  }
53
60
 
61
+ function isPathInside(child, parent) {
62
+ const c = path.resolve(child)
63
+ const p = path.resolve(parent)
64
+ const rel = path.relative(p, c)
65
+ return rel === '' || !rel.startsWith('..')
66
+ }
67
+
68
+ function assertSecretsDirOutsideRepo(secretsDir) {
69
+ if (isPathInside(secretsDir, ROOT)) {
70
+ throw new Error(
71
+ `Refusing to use secrets directory inside the repository: ${secretsDir}\n` +
72
+ 'Choose a path outside the repo (e.g. --secrets-dir ~/.enigma/simulation-secrets).'
73
+ )
74
+ }
75
+ }
76
+
54
77
  function generateKmsKeyRef() {
55
78
  const pair = generateKeyPairSync('ed25519', {
56
79
  privateKeyEncoding: { type: 'pkcs8', format: 'pem' },
@@ -75,6 +98,7 @@ function generateKmsKeyRef() {
75
98
  const SECRET_GENERATORS = {
76
99
  'relay-signing-key': () => 'local-simulation-relay-signing-key-ref\n',
77
100
  'gateway-signing-key': () => 'local-simulation-gateway-signing-key-ref\n',
101
+ // Simulation-only DSN. Never a real credential. secret-scan:approved
78
102
  'external-storage-dsn': () => 'postgres://enigma:enigma@postgres:5432/enigma?sslmode=disable\n',
79
103
  'kms-key-ref': generateKmsKeyRef,
80
104
  'backup-target-uri': () => 'file:///tmp/enigma-backups\n',
@@ -97,6 +121,28 @@ function isMissingOrEmpty(filePath) {
97
121
  }
98
122
  }
99
123
 
124
+ function sha256Hex(bufferOrString) {
125
+ return createHash('sha256').update(bufferOrString).digest('hex')
126
+ }
127
+
128
+ function gatewayBearerSha256(filePath) {
129
+ const token = fs.readFileSync(filePath)
130
+ return `sha256:${sha256Hex(token)}`
131
+ }
132
+
133
+ function writeSimulationEnv(secretsDir) {
134
+ const lines = [
135
+ '# Generated by scripts/simulate-production-env.mjs',
136
+ '# This file contains simulation-only digests, not production secrets.',
137
+ '# It is kept next to the other simulation secrets outside the repo.',
138
+ ]
139
+ for (const [name, envVar] of Object.entries(BEARER_FILES_FOR_DIGEST)) {
140
+ const digest = gatewayBearerSha256(path.join(secretsDir, name))
141
+ lines.push(`${envVar}=${digest}`)
142
+ }
143
+ fs.writeFileSync(path.join(secretsDir, 'simulation.env'), lines.join('\n') + '\n', { mode: 0o600 })
144
+ }
145
+
100
146
  function generateTlsCerts(secretsDir) {
101
147
  const key = path.join(secretsDir, 'tls.key')
102
148
  const cert = path.join(secretsDir, 'tls.crt')
@@ -149,6 +195,7 @@ function generateTlsCerts(secretsDir) {
149
195
 
150
196
  function generateSecrets(secretsDir) {
151
197
  ensureDir(secretsDir)
198
+ assertSecretsDirOutsideRepo(secretsDir)
152
199
  for (const name of REQUIRED_SECRET_FILES) {
153
200
  if (name === 'tls.crt' || name === 'tls.key') continue
154
201
  const filePath = path.join(secretsDir, name)
@@ -161,6 +208,7 @@ function generateSecrets(secretsDir) {
161
208
  if (isMissingOrEmpty(keyPath) || isMissingOrEmpty(certPath)) {
162
209
  generateTlsCerts(secretsDir)
163
210
  }
211
+ writeSimulationEnv(secretsDir)
164
212
  }
165
213
 
166
214
  function check(secretsDir) {
@@ -177,10 +225,17 @@ function check(secretsDir) {
177
225
  )
178
226
  }
179
227
 
180
- function printStartInstructions() {
228
+ function printStartInstructions(secretsDir) {
229
+ process.stdout.write('\n')
230
+ process.stdout.write('Simulation secrets written outside the repo to:\n')
231
+ process.stdout.write(` ${secretsDir}\n`)
232
+ process.stdout.write('\n')
233
+ process.stdout.write('To start the local production simulation, set the secrets path and run:\n')
234
+ process.stdout.write(` export ENIGMA_SIM_SECRETS_DIR=${secretsDir}\n`)
235
+ 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
236
  process.stdout.write('\n')
182
- process.stdout.write('To start the local production simulation, run:\n')
183
- process.stdout.write(' docker compose -f deploy/docker-compose.local-production-simulation.yml up --build -d\n')
237
+ process.stdout.write('To tear down, use the same env file so the gateway bearer hashes resolve:\n')
238
+ 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
239
  process.stdout.write('\n')
185
240
  process.stdout.write('To make the public-looking domain resolve locally, add this line to /etc/hosts\n')
186
241
  process.stdout.write('(or C:\\Windows\\System32\\drivers\\etc\\hosts on Windows):\n')
@@ -198,13 +253,14 @@ function printStartInstructions() {
198
253
 
199
254
  function main() {
200
255
  const flags = parseArgs(process.argv.slice(2))
256
+ assertSecretsDirOutsideRepo(flags.secretsDir)
201
257
  if (flags.check) {
202
258
  check(flags.secretsDir)
203
259
  return
204
260
  }
205
261
  generateSecrets(flags.secretsDir)
206
262
  check(flags.secretsDir)
207
- printStartInstructions()
263
+ printStartInstructions(flags.secretsDir)
208
264
  }
209
265
 
210
266
  main()