enigma-memory 0.1.11 → 0.1.12

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 (55) hide show
  1. package/README.md +8 -0
  2. package/apps/cli/bin/enigma.mjs +257 -3
  3. package/deploy/SIMULATION.md +152 -0
  4. package/deploy/docker-compose.local-production-simulation.yml +237 -0
  5. package/deploy/docker-compose.production.example.yml +19 -0
  6. package/deploy/kms-mock.mjs +64 -0
  7. package/deploy/nginx.local-production-simulation.conf +33 -0
  8. package/deploy/siem-mock.mjs +50 -0
  9. package/docs/benchmark-attestation-network.md +488 -0
  10. package/docs/benchmark-reproducibility.md +19 -2
  11. package/docs/blockchain-only-mechanisms.md +388 -0
  12. package/docs/demo-proof-network.md +275 -0
  13. package/docs/developer-ecosystem.md +44 -1
  14. package/docs/developer-proof-quickstart.md +325 -0
  15. package/docs/enigma-memory-ready-conformance.md +376 -0
  16. package/docs/enterprise-proof-control-plane.md +365 -0
  17. package/docs/market-category-narrative.md +398 -0
  18. package/docs/memory-drive-health-model.md +649 -0
  19. package/docs/memory-drive-strategy.md +458 -0
  20. package/docs/memory-passport-standard.md +445 -0
  21. package/docs/novelty-invention-candidates.md +161 -0
  22. package/docs/privacy-ledger-model.md +229 -0
  23. package/docs/proof-network-build-notes.md +240 -0
  24. package/docs/proof-network-claim-boundaries.md +318 -0
  25. package/docs/proof-network-dashboard-spec.md +773 -0
  26. package/docs/proof-network-glossary.md +27 -0
  27. package/docs/proof-network-launch-plan.md +421 -0
  28. package/docs/proof-network-operator-protocol.md +432 -0
  29. package/docs/proof-network-roadmap.md +431 -0
  30. package/docs/proof-network-test-plan.md +216 -0
  31. package/docs/proof-network-threat-model.md +373 -0
  32. package/docs/proof-network.md +257 -0
  33. package/docs/sdk-api.md +130 -10
  34. package/docs/solana-devnet-acceptance.md +226 -0
  35. package/docs/solana-proof-rail.md +453 -0
  36. package/examples/proof-network-anchor.json +37 -0
  37. package/examples/proof-network-attestation.json +35 -0
  38. package/examples/proof-network-grant.json +27 -0
  39. package/examples/proof-network-packet.json +71 -0
  40. package/package.json +40 -4
  41. package/packages/mcp-server/src/index.js +1 -1
  42. package/packages/proof-network/src/index.js +570 -0
  43. package/scripts/build-hosted-api-key-lifecycle.mjs +1 -1
  44. package/scripts/build-hosted-customer-lifecycle.mjs +1 -1
  45. package/scripts/build-installer-assets.mjs +1 -1
  46. package/scripts/build-proof-network-packet.mjs +213 -0
  47. package/scripts/run-standard-memory-benchmarks.mjs +1 -1
  48. package/scripts/simulate-production-env.mjs +210 -0
  49. package/scripts/wait-for-backend-ready.mjs +101 -0
  50. package/specs/goal-completion-audit-v1.schema.json +1 -0
  51. package/specs/proof-network-anchor-batch-v1.schema.json +125 -0
  52. package/specs/proof-network-benchmark-attestation-v1.schema.json +103 -0
  53. package/specs/proof-network-capability-grant-v1.schema.json +132 -0
  54. package/specs/proof-network-packet-v1.schema.json +171 -0
  55. package/scripts/install-enigma-local.mjs +0 -270
package/README.md CHANGED
@@ -31,6 +31,14 @@ enigma setup --connect-installed --overwrite
31
31
 
32
32
  `--connect-installed` is the explicit client-config write path. It skips missing client configs instead of creating every default client config.
33
33
 
34
+ ## Enigma Proof Network
35
+
36
+ Enigma Proof Network is the public proof layer for AI memory: local tools can package privacy-preserving roots, refs, counts, signatures, scoped capability grants, revocations, and benchmark attestations without exposing raw memory, prompts, transcripts, completions, embeddings, tenant names, private keys, provider responses, or provider credentials.
37
+
38
+ The `enigma chain anchor|grant|revoke|attest|verify` commands are local planning and verification commands. They emit public-safe JSON with `transaction_submitted:false` and `raw_memory_on_chain:false`; they do not submit Solana transactions, deploy hosted SaaS, create accounts, or call external providers.
39
+
40
+ Start with the category narrative in [`docs/market-category-narrative.md`](docs/market-category-narrative.md), then read the technical overview in [`docs/proof-network.md`](docs/proof-network.md), use its [Solana role](docs/proof-network.md#solana-role) section for the Solana-ready anchoring boundary, and read [`docs/proof-network-faq.md`](docs/proof-network-faq.md) for claim boundaries.
41
+
34
42
  ## Install once, use everywhere
35
43
 
36
44
  Prerequisites:
@@ -30,6 +30,19 @@ import {
30
30
  createSettlementBatch,
31
31
  verifyServiceSettlementReceipt,
32
32
  } from '../../../packages/settlement/src/index.js';
33
+ import {
34
+ assertNoPrivateProofPayload,
35
+ createBenchmarkAttestation,
36
+ createCapabilityGrant,
37
+ createCapabilityRevocation,
38
+ createProofNetworkAnchorBatch,
39
+ sha256Json as proofNetworkSha256Json,
40
+ validateBenchmarkAttestation,
41
+ validateCapabilityGrant,
42
+ validateCapabilityRevocation,
43
+ validateProofNetworkAnchorBatch,
44
+ validateProofNetworkPacket,
45
+ } from '../../../packages/proof-network/src/index.js';
33
46
 
34
47
  const DEFAULT_BUNDLE = '.enigma/bundle.json';
35
48
  const DEFAULT_TEST_DRIVE_DIR = '.enigma/test-drive';
@@ -1723,7 +1736,7 @@ export async function testDriveCommand(flags, io) {
1723
1736
  out_dir: outDirInput,
1724
1737
  bundle: bundleInput,
1725
1738
  install_command: `npm install -g ${packageJson.name ?? 'enigma-memory'}`,
1726
- release_target: '0.1.11',
1739
+ release_target: '0.1.12',
1727
1740
  artifacts_written: !dryRun,
1728
1741
  client_configs_written: false,
1729
1742
  client_config_write_required: false,
@@ -2088,6 +2101,229 @@ function integerFlag(flags, names, label = names[0], fallback = undefined) {
2088
2101
  return number;
2089
2102
  }
2090
2103
 
2104
+ function flagValues(flags, names) {
2105
+ const values = [];
2106
+ for (const name of names) {
2107
+ const value = getFlag(flags, [name]);
2108
+ const entries = Array.isArray(value) ? value : [value];
2109
+ for (const entry of entries) {
2110
+ if (entry === undefined || entry === true || entry === '') continue;
2111
+ for (const item of String(entry).split(',')) {
2112
+ const trimmed = item.trim();
2113
+ if (trimmed) values.push(trimmed);
2114
+ }
2115
+ }
2116
+ }
2117
+ return values;
2118
+ }
2119
+
2120
+ function chainWriteOrPrint(flags, io, artifact, summary) {
2121
+ if (flags.has('out')) {
2122
+ const outPath = resolve(String(requireFlag(flags, ['out'])));
2123
+ return writeJson(outPath, artifact).then(() => {
2124
+ print({
2125
+ ok: true,
2126
+ path: publicPathDisplay(String(requireFlag(flags, ['out'])), 'proof-network-artifact'),
2127
+ transaction_submitted: false,
2128
+ raw_memory_on_chain: false,
2129
+ ...summary,
2130
+ }, io);
2131
+ return 0;
2132
+ });
2133
+ }
2134
+ print(artifact, io);
2135
+ return 0;
2136
+ }
2137
+
2138
+ async function sha256PublicFile(path) {
2139
+ const bytes = await readFile(path);
2140
+ try {
2141
+ assertNoPrivateProofPayload(JSON.parse(bytes.toString('utf8')));
2142
+ } catch (error) {
2143
+ if (error instanceof SyntaxError) return `sha256:${createHash('sha256').update(bytes).digest('hex')}`;
2144
+ throw new Error('Report file contains private proof payload markers.');
2145
+ }
2146
+ return `sha256:${createHash('sha256').update(bytes).digest('hex')}`;
2147
+ }
2148
+
2149
+ function scoreFlags(flags) {
2150
+ const scores = {};
2151
+ for (const value of flagValues(flags, ['score', 'scores', 'metric', 'metrics'])) {
2152
+ const eq = value.indexOf('=');
2153
+ if (eq <= 0) throw new Error('--score values must use name=value.');
2154
+ const key = value.slice(0, eq).trim();
2155
+ const raw = value.slice(eq + 1).trim();
2156
+ if (!key || !raw) throw new Error('--score values must use name=value.');
2157
+ const numeric = Number(raw);
2158
+ scores[key] = Number.isFinite(numeric) && raw !== '' ? numeric : raw;
2159
+ }
2160
+ return scores;
2161
+ }
2162
+
2163
+
2164
+ function assertChainArtifact(validate, artifact) {
2165
+ const result = chainValidationResult(validate, artifact);
2166
+ if (!result.ok) throw new Error(result.errors?.join('; ') || 'Invalid proof-network artifact.');
2167
+ return artifact;
2168
+ }
2169
+ function chainValidationResult(validate, artifact) {
2170
+ assertNoPrivateProofPayload(artifact);
2171
+ const result = validate(artifact);
2172
+ if (result === false) return { ok: false };
2173
+ if (result && typeof result === 'object') return { ok: result.ok !== false, ...result };
2174
+ return { ok: true };
2175
+ }
2176
+
2177
+ function chainArtifactValidator(artifact) {
2178
+ const schema = String(artifact?.schema ?? artifact?.type ?? artifact?.artifact_type ?? '');
2179
+ if (schema === 'enigma.proof_network.anchor_batch.v1') return [schema, validateProofNetworkAnchorBatch];
2180
+ if (schema === 'enigma.proof_network.capability_grant.v1') return [schema, validateCapabilityGrant];
2181
+ if (schema === 'enigma.proof_network.capability_revocation.v1') return [schema, validateCapabilityRevocation];
2182
+ if (schema === 'enigma.proof_network.benchmark_attestation.v1') return [schema, validateBenchmarkAttestation];
2183
+ if (schema === 'enigma.proof_network.packet.v1') return [schema, validateProofNetworkPacket];
2184
+ throw new Error(`Unsupported proof-network artifact schema: ${schema || 'missing'}.`);
2185
+ }
2186
+
2187
+ export async function chainAnchorCommand(flags, io) {
2188
+ const roots = flagValues(flags, ['root', 'roots', 'memory-root', 'memoryRoot', 'receipt-root', 'receiptRoot', 'context-root', 'contextRoot', 'memory-commitment-root', 'memoryCommitmentRoot']);
2189
+ if (roots.length === 0) throw new Error('Missing required --root.');
2190
+ const refs = flagValues(flags, ['ref', 'refs', 'public-ref', 'publicRef']);
2191
+ const publicChainRef = getFlag(flags, ['public-chain-ref', 'publicChainRef', 'chain-ref', 'chainRef'], 'solana:local-plan');
2192
+ const batch = createProofNetworkAnchorBatch({
2193
+ roots,
2194
+ root_count: roots.length,
2195
+ commitment_count: roots.length,
2196
+ refs,
2197
+ public_chain_ref: publicChainRef,
2198
+ authority_ref: getFlag(flags, ['authority', 'authority-ref', 'authorityRef']),
2199
+ batch_ref: getFlag(flags, ['batch-ref', 'batchRef']),
2200
+ created_at: getFlag(flags, ['created-at', 'createdAt']),
2201
+ transaction_submitted: false,
2202
+ raw_memory_on_chain: false,
2203
+ });
2204
+ assertChainArtifact(validateProofNetworkAnchorBatch, batch);
2205
+ return chainWriteOrPrint(flags, io, batch, {
2206
+ artifact_type: batch.schema,
2207
+ anchor_batch_id: batch.anchor_batch_id,
2208
+ anchor_batch_hash: batch.anchor_batch_hash ?? proofNetworkSha256Json(batch),
2209
+ });
2210
+ }
2211
+
2212
+ export async function chainGrantCommand(flags, io) {
2213
+ const resourceRefs = flagValues(flags, ['resource-root', 'resource-roots', 'resourceRoot', 'resourceRoots', 'resource-ref', 'resource-refs', 'resourceRef', 'resourceRefs', 'ref', 'refs']);
2214
+ const capability = requireFlag(flags, ['capability', 'capability-id', 'capabilityId'], 'capability');
2215
+ const scope = requireFlag(flags, ['scope', 'scope-ref', 'scopeRef', 'capability-scope', 'capabilityScope'], 'scope');
2216
+ const policyHash = getFlag(flags, ['policy-hash', 'policyHash'], proofNetworkSha256Json({ capability, scope, resource_refs: resourceRefs }));
2217
+ const grant = createCapabilityGrant({
2218
+ issuer_ref: getFlag(flags, ['issuer', 'issuer-ref', 'issuerRef'], 'issuer:local-cli'),
2219
+ subject_ref: requireFlag(flags, ['subject', 'subject-ref', 'subjectRef'], 'subject'),
2220
+ capability,
2221
+ scope,
2222
+ scopes: scope,
2223
+ capability_scope: scope,
2224
+ resource_roots: resourceRefs.length ? resourceRefs : [policyHash],
2225
+ policy_hash: policyHash,
2226
+ expires_at: requireFlag(flags, ['expires-at', 'expiresAt'], 'expires-at'),
2227
+ grant_ref: getFlag(flags, ['grant-ref', 'grantRef']),
2228
+ issued_at: getFlag(flags, ['issued-at', 'issuedAt', 'created-at', 'createdAt']),
2229
+ transaction_submitted: false,
2230
+ raw_memory_on_chain: false,
2231
+ });
2232
+ assertChainArtifact(validateCapabilityGrant, grant);
2233
+ return chainWriteOrPrint(flags, io, grant, {
2234
+ artifact_type: grant.schema,
2235
+ capability_grant_id: grant.capability_grant_id,
2236
+ capability_grant_hash: grant.capability_grant_hash ?? proofNetworkSha256Json(grant),
2237
+ });
2238
+ }
2239
+
2240
+ export async function chainRevokeCommand(flags, io) {
2241
+ const grantValue = getFlag(flags, ['grant']);
2242
+ let grantHash = getFlag(flags, ['grant-hash', 'grantHash']);
2243
+ let grantId = getFlag(flags, ['grant-id', 'grantId']);
2244
+ if (grantValue !== undefined && grantValue !== true && grantValue !== '') {
2245
+ const grantString = String(grantValue);
2246
+ if (grantString.startsWith('sha256:')) {
2247
+ grantHash = grantHash ?? grantString;
2248
+ } else {
2249
+ const grantArtifact = await readJson(resolve(grantString));
2250
+ const grantValidation = validateCapabilityGrant(grantArtifact);
2251
+ if (!grantValidation.ok) throw new Error(`Grant artifact is invalid: ${grantValidation.errors.join('; ')}`);
2252
+ grantHash = grantHash ?? grantArtifact.capability_grant_hash;
2253
+ grantId = grantId ?? grantArtifact.capability_grant_id;
2254
+ }
2255
+ }
2256
+ const nullifierValue = getFlag(flags, ['nullifier-root', 'nullifierRoot', 'nullifier-ref', 'nullifierRef', 'nullifier']);
2257
+ const revocation = createCapabilityRevocation({
2258
+ grant_id: grantId,
2259
+ grant_hash: grantHash ?? requireFlag(flags, ['grant-hash', 'grantHash'], 'grant-hash'),
2260
+ reason_ref: requireFlag(flags, ['reason', 'revocation-reason', 'revocationReason'], 'reason'),
2261
+ revocation_reason: getFlag(flags, ['reason', 'revocation-reason', 'revocationReason']),
2262
+ revocation_ref: getFlag(flags, ['revocation-ref', 'revocationRef']),
2263
+ nullifier_root: nullifierValue && String(nullifierValue).startsWith('sha256:') ? nullifierValue : undefined,
2264
+ nullifier_ref: nullifierValue && !String(nullifierValue).startsWith('sha256:') ? nullifierValue : undefined,
2265
+ revoked_at: getFlag(flags, ['revoked-at', 'revokedAt']),
2266
+ transaction_submitted: false,
2267
+ raw_memory_on_chain: false,
2268
+ });
2269
+ assertChainArtifact(validateCapabilityRevocation, revocation);
2270
+ return chainWriteOrPrint(flags, io, revocation, {
2271
+ artifact_type: revocation.schema,
2272
+ capability_revocation_id: revocation.capability_revocation_id,
2273
+ capability_revocation_hash: revocation.capability_revocation_hash ?? proofNetworkSha256Json(revocation),
2274
+ });
2275
+ }
2276
+
2277
+ export async function chainAttestCommand(flags, io) {
2278
+ const reportHash = getFlag(flags, ['report-hash', 'reportHash']);
2279
+ const reportFile = getFlag(flags, ['report-file', 'reportFile']);
2280
+ const resolvedReportHash = reportHash || (reportFile ? await sha256PublicFile(resolve(String(reportFile))) : undefined);
2281
+ if (!resolvedReportHash) throw new Error('Missing required --report-hash or --report-file.');
2282
+ const scores = scoreFlags(flags);
2283
+ const metricsHash = getFlag(flags, ['metrics-hash', 'metricsHash'], proofNetworkSha256Json({ scores }));
2284
+ const attestation = createBenchmarkAttestation({
2285
+ report_hash: resolvedReportHash,
2286
+ report_file_hash: resolvedReportHash,
2287
+ dataset_ref: requireFlag(flags, ['dataset-ref', 'datasetRef', 'dataset-manifest', 'datasetManifest'], 'dataset-ref'),
2288
+ runner_ref: requireFlag(flags, ['runner-ref', 'runnerRef'], 'runner-ref'),
2289
+ package_ref: requireFlag(flags, ['package-ref', 'packageRef'], 'package-ref'),
2290
+ metrics: scores,
2291
+ metrics_hash: metricsHash,
2292
+ attestation_ref: getFlag(flags, ['attestation-ref', 'attestationRef']),
2293
+ created_at: getFlag(flags, ['created-at', 'createdAt']),
2294
+ transaction_submitted: false,
2295
+ raw_memory_on_chain: false,
2296
+ });
2297
+ assertChainArtifact(validateBenchmarkAttestation, attestation);
2298
+ return chainWriteOrPrint(flags, io, attestation, {
2299
+ artifact_type: attestation.schema,
2300
+ benchmark_attestation_id: attestation.benchmark_attestation_id,
2301
+ benchmark_attestation_hash: attestation.benchmark_attestation_hash ?? proofNetworkSha256Json(attestation),
2302
+ });
2303
+ }
2304
+
2305
+ export async function chainVerifyCommand(flags, io, positionalFile = undefined) {
2306
+ const inPath = resolve(String(requireFileArg(flags, ['file', 'in'], positionalFile, 'file')));
2307
+ const artifact = await readJson(inPath);
2308
+ const [schema, validate] = chainArtifactValidator(artifact);
2309
+ let result;
2310
+ try {
2311
+ result = chainValidationResult(validate, artifact);
2312
+ } catch (error) {
2313
+ result = { ok: false, error: { code: 'PROOF_NETWORK_INVALID', message: error.message } };
2314
+ }
2315
+ print({
2316
+ ok: result.ok === true,
2317
+ artifact_type: schema,
2318
+ artifact_hash: proofNetworkSha256Json(artifact),
2319
+ transaction_submitted: false,
2320
+ raw_memory_on_chain: false,
2321
+ validation: result,
2322
+ }, io);
2323
+ return result.ok === true ? 0 : 1;
2324
+ }
2325
+
2326
+
2091
2327
  export async function meterEventCommand(flags, io) {
2092
2328
  const event = createUsageEvent({
2093
2329
  tenant_id: requireFlag(flags, ['tenant', 'tenant-id', 'tenantId'], 'tenant'),
@@ -2306,6 +2542,11 @@ function usage() {
2306
2542
  'settlement receipt',
2307
2543
  'settlement verify',
2308
2544
  'settlement batch',
2545
+ 'chain anchor',
2546
+ 'chain grant',
2547
+ 'chain revoke',
2548
+ 'chain attest',
2549
+ 'chain verify',
2309
2550
  ],
2310
2551
  connector_options: {
2311
2552
  '--bundle <path>': 'Absolute local Enigma vault bundle path rendered as ENIGMA_BUNDLE.',
@@ -2401,6 +2642,14 @@ function usage() {
2401
2642
  batch: 'enigma settlement batch --receipts <receipts.json> --batch-ref <ref> [--asset <asset>] [--out <file>]',
2402
2643
  boundary: 'Settlement artifacts contain commitment roots, capacity profiles, hashes, refs, prices, and claim boundaries only; no raw memory, prompts, provider responses, credentials, token ROI/profit, decentralization, or provider-invoice savings claim.',
2403
2644
  },
2645
+ chain: {
2646
+ anchor: 'enigma chain anchor --root <sha256:...> [--root <sha256:...>] [--ref <public-ref>] [--authority <public-authority-ref>] [--batch-ref <ref>] [--out <file>]',
2647
+ grant: 'enigma chain grant --subject <public-subject-ref> --capability <capability-id> --scope <scope-id> [--resource-ref <sha256:...>] [--policy-hash <sha256:...>] --expires-at <iso> [--grant-ref <public-ref>] [--out <file>]',
2648
+ revoke: 'enigma chain revoke --grant-hash <sha256:...> --reason <public-reason-code> [--revocation-ref <public-ref>] [--out <file>]',
2649
+ attest: 'enigma chain attest (--report-hash <sha256:...> | --report-file <report.json>) --dataset-ref <sha256:...> --runner-ref <public-runner-ref> --package-ref <public-package-ref> [--score name=value] [--out <file>]',
2650
+ verify: 'enigma chain verify --file <proof-artifact.json>',
2651
+ boundary: 'Proof Network chain commands are local planning commands only. They write public-safe hashes, roots, refs, counts, signatures, and booleans; they do not submit Solana transactions or put raw memory on chain.',
2652
+ },
2404
2653
  relay_gateway_options: {
2405
2654
  '--host <host>': 'Bind host. Defaults to 127.0.0.1.',
2406
2655
  '--port <port>': `Bind port. Defaults to ${DEFAULT_RELAY_PORT} for relay and ${DEFAULT_GATEWAY_PORT} for gateway.`,
@@ -2427,10 +2676,10 @@ export async function main(argv = process.argv.slice(2), io = { stdout: process.
2427
2676
  print(usage(), io);
2428
2677
  return 0;
2429
2678
  }
2430
- const twoPartCommands = ['boundary', 'mcp', 'mesh', 'enterprise', 'capsule', 'relay', 'gateway', 'connect', 'disconnect', 'import', 'native-host', 'meter', 'settlement', 'demo', 'passport'];
2679
+ const twoPartCommands = ['boundary', 'mcp', 'mesh', 'enterprise', 'capsule', 'relay', 'gateway', 'connect', 'disconnect', 'import', 'native-host', 'meter', 'settlement', 'chain', 'demo', 'passport'];
2431
2680
  const flags = parseArgs(twoPartCommands.includes(command) ? argv.slice(2) : argv.slice(1));
2432
2681
  const positionalFile = optionalPositional(argv[2]);
2433
- if ((flags.has('help') || argv.includes('-h')) && (command === 'setup' || command === 'test-drive' || command === 'search' || command === 'status' || (command === 'passport' && subcommand === 'status') || ((command === 'relay' || command === 'gateway') && (subcommand === 'serve' || subcommand === 'demo')) || (command === 'native-host' && (subcommand === 'manifest' || subcommand === 'install-plan')) || (command === 'demo' && subcommand === 'cross-model'))) {
2682
+ if ((command === 'chain' && (!subcommand || subcommand === '--help' || subcommand === '-h' || flags.has('help'))) || ((flags.has('help') || argv.includes('-h')) && (command === 'setup' || command === 'test-drive' || command === 'search' || command === 'status' || (command === 'passport' && subcommand === 'status') || ((command === 'relay' || command === 'gateway') && (subcommand === 'serve' || subcommand === 'demo')) || (command === 'native-host' && (subcommand === 'manifest' || subcommand === 'install-plan')) || (command === 'demo' && subcommand === 'cross-model')))) {
2434
2683
  print(usage(), io);
2435
2684
  return 0;
2436
2685
  }
@@ -2472,6 +2721,11 @@ export async function main(argv = process.argv.slice(2), io = { stdout: process.
2472
2721
  if (command === 'settlement' && subcommand === 'receipt') return await settlementReceiptCommand(flags, io);
2473
2722
  if (command === 'settlement' && subcommand === 'verify') return await settlementVerifyCommand(flags, io);
2474
2723
  if (command === 'settlement' && subcommand === 'batch') return await settlementBatchCommand(flags, io, positionalFile);
2724
+ if (command === 'chain' && subcommand === 'anchor') return await chainAnchorCommand(flags, io);
2725
+ if (command === 'chain' && subcommand === 'grant') return await chainGrantCommand(flags, io);
2726
+ if (command === 'chain' && subcommand === 'revoke') return await chainRevokeCommand(flags, io);
2727
+ if (command === 'chain' && subcommand === 'attest') return await chainAttestCommand(flags, io);
2728
+ if (command === 'chain' && subcommand === 'verify') return await chainVerifyCommand(flags, io, positionalFile);
2475
2729
  if (command === 'native-host' && subcommand === 'install-plan') return await nativeHostInstallPlanCommand(flags, io);
2476
2730
  if (command === 'mesh' && subcommand === 'demo') return await meshDemoCommand(flags, io);
2477
2731
  if (command === 'enterprise' && subcommand === 'demo') return await enterpriseDemoCommand(flags, io);
@@ -0,0 +1,152 @@
1
+ # Enigma Memory — Local Production Simulation
2
+
3
+ **CLAIM BOUNDARY:** This is a `local-simulation` environment. It uses
4
+ self-signed TLS, bind-mounted file "secrets", a mocked KMS, and a mocked SIEM.
5
+ It is **not** a production deployment and must not be used as go-live evidence.
6
+
7
+ ## Public-looking domain (local testing only)
8
+
9
+ The simulation is configured to answer on `sim.enigmamemory.com` so that the
10
+ hosted-backend live validator accepts the non-localhost HTTPS probe URLs. For
11
+ real go-live evidence the operator must point the chosen public domain at the
12
+ deployment's public IP address; the local simulation uses a hosts-file or local
13
+ DNS trick instead.
14
+
15
+ On the host running Docker, map the domain to the loopback interface:
16
+
17
+ ```text
18
+ 127.0.0.1 sim.enigmamemory.com relay.sim.enigmamemory.com gateway.sim.enigmamemory.com
19
+ ```
20
+
21
+ On Linux/macOS add that line to `/etc/hosts`; on Windows add it to
22
+ `C:\Windows\System32\drivers\etc\hosts`. Then the following commands work from
23
+ the host:
24
+
25
+ ```bash
26
+ curl -k https://sim.enigmamemory.com:8443/readyz
27
+ curl -k https://sim.enigmamemory.com:9443/readyz
28
+ ```
29
+
30
+ The tls-proxy service also declares `extra_hosts` entries for the same domain
31
+ names so that containers can resolve them locally when needed.
32
+
33
+ ## Quick start
34
+
35
+ 1. Generate secrets and a self-signed TLS certificate:
36
+
37
+ ```bash
38
+ node scripts/simulate-production-env.mjs
39
+ ```
40
+
41
+ Files are written to `deploy/secrets-simulation/` (gitignored).
42
+
43
+ 2. Start the simulation:
44
+
45
+ ```bash
46
+ docker compose -f deploy/docker-compose.local-production-simulation.yml up --build -d
47
+ ```
48
+
49
+ 3. Wait for the backend to become ready:
50
+
51
+ ```bash
52
+ node scripts/wait-for-backend-ready.mjs
53
+ ```
54
+
55
+ 4. Inspect readiness over HTTPS:
56
+
57
+ ```bash
58
+ curl -k https://localhost:8443/readyz
59
+ curl -k https://localhost:9443/readyz
60
+ curl -k https://sim.enigmamemory.com:8443/readyz
61
+ curl -k https://sim.enigmamemory.com:9443/readyz
62
+ ```
63
+
64
+ ## Stop
65
+
66
+ ```bash
67
+ docker compose -f deploy/docker-compose.local-production-simulation.yml down
68
+ ```
69
+
70
+ To also remove the Postgres volume and mock event data:
71
+
72
+ ```bash
73
+ docker compose -f deploy/docker-compose.local-production-simulation.yml down -v
74
+ ```
75
+
76
+ ## Ports and routes
77
+
78
+ | Service | Public port | Internal port | Health route | Notes |
79
+ |--------|-------------|---------------|--------------|-------|
80
+ | relay | `8443` (HTTPS, loopback) | `8787` | `/readyz`, `/livez` | TLS terminated by `tls-proxy`; also answers on `sim.enigmamemory.com` and `relay.sim.enigmamemory.com` |
81
+ | gateway | `9443` (HTTPS, loopback) | `8797` | `/readyz`, `/livez` | TLS terminated by `tls-proxy`; also answers on `sim.enigmamemory.com` and `gateway.sim.enigmamemory.com` |
82
+ | postgres | not exposed | `5432` | — | Used as the simulated durable store |
83
+ | kms-mock | not exposed | `3000` | `/healthz` | Serves a generated Ed25519 key ref |
84
+ | siem-mock | not exposed | `3000` | `/healthz` | Accepts `POST /events`, writes minimized metadata to `/data/siem-events.jsonl` |
85
+
86
+ ## Verify secret files
87
+
88
+ ```bash
89
+ node scripts/simulate-production-env.mjs --check
90
+ ```
91
+
92
+ This reports whether all required files in `deploy/secrets-simulation/` exist
93
+ and are non-empty.
94
+
95
+ ## Simulation-only behavior
96
+
97
+ - `ENIGMA_OPERATOR_ACCEPTANCE_DECISION` is set to `go` so the readiness
98
+ endpoints can turn green locally. This is **not** real operator acceptance.
99
+ - `ENIGMA_DISABLE_LOCAL_DEMO_FALLBACK` is `true`, but the "external" storage,
100
+ KMS, and SIEM are local mocks.
101
+ - The TLS certificate is self-signed; curl requires `-k`/`--insecure`.
102
+ - The certificate SAN list includes `localhost`, `127.0.0.1`,
103
+ `sim.enigmamemory.com`, `relay.sim.enigmamemory.com`,
104
+ `gateway.sim.enigmamemory.com`, and `*.sim.enigmamemory.com`.
105
+
106
+ ## Collect hosted backend live evidence
107
+
108
+ The simulation can be probed as if it were a public hosted deployment by
109
+ using the public-looking domain `sim.enigmamemory.com`. Because the domain
110
+ has no real DNS record, the collector resolves it to `127.0.0.1` locally and
111
+ accepts the self-signed certificate.
112
+
113
+ 1. Build a simulation operator acceptance packet:
114
+
115
+ ```bash
116
+ node scripts/build-operator-acceptance-packet.mjs \
117
+ --complete-fixture --decision go --packet-id sim-operator-acceptance \
118
+ --tenant enigma-sim --deployment-mode hosted --environment local-simulation \
119
+ --target-regions local --requested-go-live-date 2026-06-25 \
120
+ --evidence-repository https://github.com/enigma-memory/evidence/sim \
121
+ --packet-owner "Simulation Owner" \
122
+ --last-updated 2026-06-25T00:00:00.000Z \
123
+ --owners-json .enigma/sim-owner-overrides.json \
124
+ --evidence-refs .enigma/sim-evidence-overrides.json \
125
+ --out .enigma/sim-operator-acceptance.json --validate
126
+ ```
127
+
128
+ 2. Collect and validate live evidence:
129
+
130
+ ```bash
131
+ node .enigma/collect-sim-evidence.mjs \
132
+ --relay-url https://sim.enigmamemory.com:8443 \
133
+ --gateway-url https://sim.enigmamemory.com:9443 \
134
+ --refs-json .enigma/sim-hosted-refs.json \
135
+ --domain sim.enigmamemory.com --environment-id local-simulation \
136
+ --cloud-provider local --region local --owner enigma-sim \
137
+ --operator-decision go \
138
+ --operator-packet-ref .enigma/sim-operator-acceptance.json \
139
+ --operator-approved-at <iso8601> --operator-approved-by enigma-sim \
140
+ --out .enigma/hosted-backend-live-collection.json \
141
+ --evidence-out .enigma/hosted-backend-live-simulated.json
142
+
143
+ node scripts/validate-hosted-backend-live.mjs \
144
+ --evidence .enigma/hosted-backend-live-simulated.json
145
+ ```
146
+
147
+ The expected result is `status: accepted` with all four probes observed and no
148
+ blockers. The wrapper does not mutate DNS or deploy infrastructure and never
149
+ sends credentials.
150
+
151
+ Never commit `deploy/secrets-simulation/` or `*.pem` files. Both are
152
+ `.gitignore`d.