enigma-memory 0.1.0

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 (105) hide show
  1. package/LICENSE +55 -0
  2. package/README.md +379 -0
  3. package/apps/cli/bin/enigma.mjs +1117 -0
  4. package/apps/desktop/src/app.js +1327 -0
  5. package/apps/desktop/src/index.html +1373 -0
  6. package/apps/desktop/src/styles.css +386 -0
  7. package/apps/gateway/bin/enigma-gateway.mjs +52 -0
  8. package/apps/gateway/src/server.mjs +1403 -0
  9. package/apps/native-host/README.md +265 -0
  10. package/apps/native-host/bin/enigma-native-host.mjs +499 -0
  11. package/apps/native-host/manifests/com.enigma.native_host.chrome.json +9 -0
  12. package/apps/native-host/manifests/com.enigma.native_host.edge.json +9 -0
  13. package/apps/native-host/manifests/com.enigma.native_host.firefox.json +9 -0
  14. package/apps/relay/bin/enigma-relay.mjs +52 -0
  15. package/apps/relay/src/server.mjs +1571 -0
  16. package/apps/verifier/bin/enigma-verify.mjs +240 -0
  17. package/deploy/docker-compose.production.example.yml +150 -0
  18. package/deploy/kubernetes/enigma-backend.example.yaml +580 -0
  19. package/package.json +196 -0
  20. package/packages/adapters/PACKAGE_CONTRACT.md +47 -0
  21. package/packages/adapters/src/index.js +183 -0
  22. package/packages/boundary/src/index.js +468 -0
  23. package/packages/connectors/src/index.js +533 -0
  24. package/packages/core/src/index.js +766 -0
  25. package/packages/enterprise/src/index.js +505 -0
  26. package/packages/importers/src/index.js +1083 -0
  27. package/packages/mcp-server/bin/enigma-mcp.mjs +19 -0
  28. package/packages/mcp-server/src/index.js +1185 -0
  29. package/packages/mesh/src/index.js +531 -0
  30. package/packages/metering/PACKAGE_CONTRACT.md +20 -0
  31. package/packages/metering/src/index.js +246 -0
  32. package/packages/optimizer/src/index.js +707 -0
  33. package/packages/passport/src/index.js +651 -0
  34. package/packages/settlement/PACKAGE_CONTRACT.md +28 -0
  35. package/packages/settlement/src/index.js +369 -0
  36. package/packages/storage/PACKAGE_CONTRACT.md +23 -0
  37. package/packages/storage/src/index.js +392 -0
  38. package/packages/vault/src/index.js +904 -0
  39. package/scripts/build-ai-orchestration-plan.mjs +248 -0
  40. package/scripts/build-cloudflare-pages-release-packet.mjs +287 -0
  41. package/scripts/build-cloudflare-token-policy.mjs +239 -0
  42. package/scripts/build-cloudflare-token-request.mjs +314 -0
  43. package/scripts/build-demo-assets.mjs +263 -0
  44. package/scripts/build-edge-backend-workers.mjs +353 -0
  45. package/scripts/build-goal-completion-audit.mjs +398 -0
  46. package/scripts/build-hosted-probe-worker.mjs +191 -0
  47. package/scripts/build-operator-acceptance-packet.mjs +254 -0
  48. package/scripts/build-operator-evidence-starter.mjs +525 -0
  49. package/scripts/build-production-backend-env-kit.mjs +384 -0
  50. package/scripts/build-production-dependency-report.mjs +369 -0
  51. package/scripts/build-production-handoff-packet.mjs +475 -0
  52. package/scripts/build-production-readiness-manifest.mjs +209 -0
  53. package/scripts/build-production-status-board.mjs +297 -0
  54. package/scripts/build-production-storage-migration.mjs +86 -0
  55. package/scripts/build-production-workplan.mjs +371 -0
  56. package/scripts/build-review-packet.mjs +594 -0
  57. package/scripts/check.mjs +419 -0
  58. package/scripts/cloudflare-ops.mjs +1672 -0
  59. package/scripts/cloudflare-secret-env.mjs +128 -0
  60. package/scripts/collect-hosted-backend-live-evidence.mjs +263 -0
  61. package/scripts/infrastructure-readiness.mjs +944 -0
  62. package/scripts/install-enigma-local.mjs +270 -0
  63. package/scripts/memory-optimization-benchmark.mjs +242 -0
  64. package/scripts/monitor-live-endpoints.mjs +358 -0
  65. package/scripts/release-audit.mjs +5588 -0
  66. package/scripts/release-provenance.mjs +250 -0
  67. package/scripts/run-backend-readiness-smoke.mjs +220 -0
  68. package/scripts/validate-backup-restore-drill.mjs +166 -0
  69. package/scripts/validate-cloudflare-credentials.mjs +112 -0
  70. package/scripts/validate-cloudflare-worker-inspect.mjs +179 -0
  71. package/scripts/validate-domain-tls.mjs +248 -0
  72. package/scripts/validate-hosted-backend-live.mjs +319 -0
  73. package/scripts/validate-incident-drill.mjs +221 -0
  74. package/scripts/validate-kms-custody.mjs +235 -0
  75. package/scripts/validate-legal-compliance-approval.mjs +274 -0
  76. package/scripts/validate-monitoring-alerting.mjs +263 -0
  77. package/scripts/validate-network-access-policy.mjs +285 -0
  78. package/scripts/validate-operator-acceptance.mjs +298 -0
  79. package/scripts/validate-production-manifests.mjs +325 -0
  80. package/scripts/validate-public-site-security.mjs +252 -0
  81. package/scripts/validate-security-threat-model.mjs +285 -0
  82. package/scripts/validate-service-settlement.mjs +256 -0
  83. package/scripts/validate-support-sla.mjs +190 -0
  84. package/scripts/validate-tenant-policy-approval.mjs +228 -0
  85. package/scripts/validate-usage-metering.mjs +224 -0
  86. package/scripts/validate-whitepaper-claims.mjs +166 -0
  87. package/specs/boundary-manifest-v1.schema.json +35 -0
  88. package/specs/capsule-v1.schema.json +55 -0
  89. package/specs/claim-boundary-manifest-v1.schema.json +22 -0
  90. package/specs/connector-profile-v1.schema.json +62 -0
  91. package/specs/deletion-tombstone-v1.schema.json +26 -0
  92. package/specs/enterprise-policy-v1.schema.json +59 -0
  93. package/specs/gateway-request-v1.schema.json +32 -0
  94. package/specs/goal-completion-audit-v1.schema.json +70 -0
  95. package/specs/hosted-backend-live-evidence-v1.schema.json +147 -0
  96. package/specs/import-report-v1.schema.json +214 -0
  97. package/specs/infrastructure-readiness-manifest-v1.schema.json +130 -0
  98. package/specs/memory-event-v1.schema.json +42 -0
  99. package/specs/mesh-node-v1.schema.json +126 -0
  100. package/specs/passport-v1.schema.json +50 -0
  101. package/specs/production-handoff-packet-v1.schema.json +149 -0
  102. package/specs/receipt-v1.schema.json +61 -0
  103. package/specs/relay-record-v1.schema.json +43 -0
  104. package/specs/state-checkpoint-v1.schema.json +37 -0
  105. package/specs/trust-bundle-v1.schema.json +56 -0
@@ -0,0 +1,499 @@
1
+ #!/usr/bin/env node
2
+ import { realpathSync } from 'node:fs';
3
+ import { readFile, writeFile } from 'node:fs/promises';
4
+ import { isAbsolute, posix, win32 } from 'node:path';
5
+ import { pathToFileURL } from 'node:url';
6
+ import { canonicalize, receiptHash, sha256Hex } from '../../../packages/core/src/index.js';
7
+ import { compileContextPack } from '../../../packages/passport/src/index.js';
8
+ import { exportBundle, importBundle } from '../../../packages/vault/src/index.js';
9
+
10
+ export const NATIVE_HOST_NAME = 'com.enigma.native_host';
11
+ export const NATIVE_BROWSER_PROTOCOL = 'enigma.native.browser.v1';
12
+ export const MAX_NATIVE_MESSAGE_BYTES = 1024 * 1024;
13
+
14
+ const CONTEXT_REQUEST = 'enigma.browser.context.request';
15
+ const CONTEXT_RESPONSE = 'enigma.browser.context.response';
16
+ const INSERTION_RECORD = 'enigma.browser.insertion.record';
17
+ const INSERTION_RECORDED = 'enigma.browser.insertion.recorded';
18
+ const GENERIC_ERROR = 'enigma.native.error';
19
+ const SHA256_ROOT_RE = /^sha256:[a-f0-9]{64}$/;
20
+ const SUPPORTED_PROVIDER_IDS = new Set(['chatgpt', 'claude', 'kimi', 'perplexity']);
21
+ const SAFE_MESSAGE_ID_RE = /^[A-Za-z0-9._:-]{1,80}$/;
22
+ const SUPPORTED_MANIFEST_BROWSERS = new Set(['chrome', 'edge', 'firefox']);
23
+ const CHROMIUM_EXTENSION_ID_RE = /^[A-Za-z0-9.-]{1,128}$/;
24
+ const FIREFOX_EXTENSION_ID_RE = /^[A-Za-z0-9._@{}-]{1,128}$/;
25
+
26
+ export function encodeNativeMessage(message) {
27
+ const payload = Buffer.from(JSON.stringify(message), 'utf8');
28
+ if (payload.length > 0xffffffff) throw new RangeError('Native message is too large.');
29
+ const frame = Buffer.allocUnsafe(4 + payload.length);
30
+ frame.writeUInt32LE(payload.length, 0);
31
+ payload.copy(frame, 4);
32
+ return frame;
33
+ }
34
+
35
+ export function createNativeHostManifest({ browser, hostPath, extensionId } = {}) {
36
+ const normalizedBrowser = String(browser ?? '').toLowerCase();
37
+ if (!SUPPORTED_MANIFEST_BROWSERS.has(normalizedBrowser)) {
38
+ throw new Error('Native host manifest browser must be one of: chrome, edge, firefox.');
39
+ }
40
+ if (typeof hostPath !== 'string' || hostPath.length === 0 || !isNativeHostAbsolutePath(hostPath)) {
41
+ throw new Error('Native host manifest hostPath must be an absolute path.');
42
+ }
43
+ if (typeof extensionId !== 'string' || extensionId.length === 0) {
44
+ throw new Error('Native host manifest extensionId is required.');
45
+ }
46
+ if ((normalizedBrowser === 'chrome' || normalizedBrowser === 'edge') && !CHROMIUM_EXTENSION_ID_RE.test(extensionId)) {
47
+ throw new Error('Chrome and Edge native host manifests require an extension id with only letters, numbers, dots, or hyphens.');
48
+ }
49
+ if (normalizedBrowser === 'firefox' && !FIREFOX_EXTENSION_ID_RE.test(extensionId)) {
50
+ throw new Error('Firefox native host manifest extension id contains unsupported characters.');
51
+ }
52
+
53
+ const manifest = {
54
+ name: NATIVE_HOST_NAME,
55
+ description: 'Enigma local native messaging host for explicit browser context requests.',
56
+ path: hostPath,
57
+ type: 'stdio',
58
+ };
59
+ if (normalizedBrowser === 'firefox') {
60
+ manifest.allowed_extensions = [extensionId];
61
+ } else {
62
+ manifest.allowed_origins = [`chrome-extension://${extensionId}/`];
63
+ }
64
+ return manifest;
65
+ }
66
+
67
+ const SUPPORTED_INSTALL_OSES = new Set(['windows', 'macos', 'linux']);
68
+
69
+ export function createNativeHostInstallPlan({ browser, manifestPath, os, homeDir } = {}) {
70
+ const normalizedBrowser = String(browser ?? '').toLowerCase();
71
+ if (!SUPPORTED_MANIFEST_BROWSERS.has(normalizedBrowser)) {
72
+ throw new Error('Native host install plan browser must be one of: chrome, edge, firefox.');
73
+ }
74
+ const normalizedOs = String(os ?? '').toLowerCase();
75
+ if (!SUPPORTED_INSTALL_OSES.has(normalizedOs)) {
76
+ throw new Error('Native host install plan os must be one of: windows, macos, linux.');
77
+ }
78
+ if (typeof manifestPath !== 'string' || manifestPath.length === 0 || !isNativeHostAbsolutePath(manifestPath)) {
79
+ throw new Error('Native host install plan manifestPath must be an absolute path.');
80
+ }
81
+ if (typeof homeDir !== 'string' || homeDir.length === 0 || !isNativeHostAbsolutePath(homeDir)) {
82
+ throw new Error('Native host install plan homeDir must be an absolute path.');
83
+ }
84
+
85
+ const manifestDirectory = nativeHostManifestDirectory({ browser: normalizedBrowser, os: normalizedOs, homeDir });
86
+ const targetManifestPath = joinNativeHostPath(normalizedOs, manifestDirectory, `${NATIVE_HOST_NAME}.json`);
87
+ const registryCommandPreview = windowsRegistryCommandPreview(normalizedBrowser, normalizedOs, targetManifestPath);
88
+ return {
89
+ host_name: NATIVE_HOST_NAME,
90
+ browser: normalizedBrowser,
91
+ os: normalizedOs,
92
+ manifest_source: manifestPath,
93
+ target_manifest_paths: [targetManifestPath],
94
+ manual_steps: nativeHostInstallManualSteps({
95
+ browser: normalizedBrowser,
96
+ os: normalizedOs,
97
+ manifestPath,
98
+ targetManifestPath,
99
+ registryCommandPreview,
100
+ }),
101
+ registry_command_preview: registryCommandPreview,
102
+ firefox_manifest_directory: normalizedBrowser === 'firefox' ? manifestDirectory : null,
103
+ writes_performed: false,
104
+ };
105
+ }
106
+
107
+ function nativeHostManifestDirectory({ browser, os, homeDir }) {
108
+ if (os === 'windows') {
109
+ if (browser === 'firefox') return win32.join(homeDir, 'AppData', 'Roaming', 'Mozilla', 'NativeMessagingHosts');
110
+ if (browser === 'edge') return win32.join(homeDir, 'AppData', 'Local', 'Microsoft', 'Edge', 'User Data', 'NativeMessagingHosts');
111
+ return win32.join(homeDir, 'AppData', 'Local', 'Google', 'Chrome', 'User Data', 'NativeMessagingHosts');
112
+ }
113
+ if (os === 'macos') {
114
+ if (browser === 'firefox') return posix.join(homeDir, 'Library', 'Application Support', 'Mozilla', 'NativeMessagingHosts');
115
+ if (browser === 'edge') return posix.join(homeDir, 'Library', 'Application Support', 'Microsoft Edge', 'NativeMessagingHosts');
116
+ return posix.join(homeDir, 'Library', 'Application Support', 'Google', 'Chrome', 'NativeMessagingHosts');
117
+ }
118
+ if (browser === 'firefox') return posix.join(homeDir, '.mozilla', 'native-messaging-hosts');
119
+ if (browser === 'edge') return posix.join(homeDir, '.config', 'microsoft-edge', 'NativeMessagingHosts');
120
+ return posix.join(homeDir, '.config', 'google-chrome', 'NativeMessagingHosts');
121
+ }
122
+
123
+ function joinNativeHostPath(os, directory, fileName) {
124
+ return os === 'windows' ? win32.join(directory, fileName) : posix.join(directory, fileName);
125
+ }
126
+
127
+ function windowsRegistryCommandPreview(browser, os, targetManifestPath) {
128
+ if (os !== 'windows' || browser === 'firefox') return [];
129
+ const vendor = browser === 'edge' ? 'Microsoft\\Edge' : 'Google\\Chrome';
130
+ return [
131
+ `reg add "HKCU\\Software\\${vendor}\\NativeMessagingHosts\\${NATIVE_HOST_NAME}" /ve /t REG_SZ /d "${targetManifestPath}" /f`,
132
+ ];
133
+ }
134
+
135
+ function nativeHostInstallManualSteps({ browser, os, manifestPath, targetManifestPath, registryCommandPreview }) {
136
+ const steps = [
137
+ `Review the generated native messaging manifest at ${manifestPath}.`,
138
+ `Copy the manifest to ${targetManifestPath}.`,
139
+ ];
140
+ if (registryCommandPreview.length > 0) {
141
+ steps.push(`Review and run this registry command manually for ${browser}: ${registryCommandPreview[0]}`);
142
+ }
143
+ steps.push(`Restart ${browser} after the manifest is in place.`);
144
+ steps.push(`This planner is non-mutating on ${os}; it does not write files, registry keys, or browser profiles.`);
145
+ return steps;
146
+ }
147
+
148
+ function isNativeHostAbsolutePath(hostPath) {
149
+ return isAbsolute(hostPath) || win32.isAbsolute(hostPath);
150
+ }
151
+
152
+ export function decodeNativeMessage(frame, options = {}) {
153
+ const buffer = Buffer.isBuffer(frame) ? frame : Buffer.from(frame);
154
+ if (buffer.length < 4) throw new Error('Native message frame is missing a length prefix.');
155
+ const length = buffer.readUInt32LE(0);
156
+ const maxBytes = options.maxBytes ?? MAX_NATIVE_MESSAGE_BYTES;
157
+ if (length > maxBytes) throw new Error('Native message frame exceeds the configured maximum size.');
158
+ if (buffer.length !== 4 + length) throw new Error('Native message frame length does not match its payload.');
159
+ return JSON.parse(buffer.subarray(4).toString('utf8'));
160
+ }
161
+
162
+ export function decodeNativeMessageFrames(input, options = {}) {
163
+ let buffer = Buffer.isBuffer(input) ? input : Buffer.from(input);
164
+ const messages = [];
165
+ const maxBytes = options.maxBytes ?? MAX_NATIVE_MESSAGE_BYTES;
166
+ while (buffer.length >= 4) {
167
+ const length = buffer.readUInt32LE(0);
168
+ if (length > maxBytes) throw new Error('Native message frame exceeds the configured maximum size.');
169
+ if (buffer.length < 4 + length) break;
170
+ messages.push(JSON.parse(buffer.subarray(4, 4 + length).toString('utf8')));
171
+ buffer = buffer.subarray(4 + length);
172
+ }
173
+ return { messages, remaining: buffer };
174
+ }
175
+
176
+ export async function handleNativeBrowserMessage(message, options = {}) {
177
+ const id = safeMessageId(message?.id);
178
+ const responseType = responseTypeFor(message?.type);
179
+ if (!message || typeof message !== 'object' || Array.isArray(message)) {
180
+ return safeError({ id, type: responseType, code: 'ENIGMA_BAD_REQUEST', message: 'Native host received an invalid browser request.' });
181
+ }
182
+ if (message.protocol !== NATIVE_BROWSER_PROTOCOL) {
183
+ return safeError({ id, type: responseType, code: 'ENIGMA_BAD_PROTOCOL', message: 'Native host received an unsupported protocol.' });
184
+ }
185
+
186
+ try {
187
+ if (message.type === CONTEXT_REQUEST) return await handleContextRequest(message, options);
188
+ if (message.type === INSERTION_RECORD) return await handleInsertionRecord(message, options);
189
+ return safeError({ id, type: responseType, code: 'ENIGMA_UNSUPPORTED_REQUEST', message: 'Native host received an unsupported request type.' });
190
+ } catch {
191
+ return safeError({ id, type: responseType, code: 'ENIGMA_NATIVE_HOST_ERROR', message: 'Enigma native host failed without exposing local memory.' });
192
+ }
193
+ }
194
+
195
+ export function createNativeHostRuntime(options = {}) {
196
+ const stdin = options.stdin ?? process.stdin;
197
+ const stdout = options.stdout ?? process.stdout;
198
+ const stderr = options.stderr ?? process.stderr;
199
+ let pending = Buffer.alloc(0);
200
+ let queue = Promise.resolve();
201
+ let started = false;
202
+
203
+ const runtimeOptions = {
204
+ ...options,
205
+ argv: options.argv ?? process.argv.slice(2),
206
+ env: options.env ?? process.env,
207
+ };
208
+
209
+ async function handleChunk(chunk) {
210
+ pending = pending.length === 0 ? Buffer.from(chunk) : Buffer.concat([pending, Buffer.from(chunk)]);
211
+ let decoded;
212
+ try {
213
+ decoded = decodeNativeMessageFrames(pending, { maxBytes: options.maxBytes ?? MAX_NATIVE_MESSAGE_BYTES });
214
+ } catch {
215
+ pending = Buffer.alloc(0);
216
+ stdout.write(encodeNativeMessage(safeError({ type: GENERIC_ERROR, code: 'ENIGMA_BAD_FRAME', message: 'Native host received an invalid message frame.' })));
217
+ return;
218
+ }
219
+ pending = decoded.remaining;
220
+ for (const message of decoded.messages) {
221
+ const response = await handleNativeBrowserMessage(message, runtimeOptions);
222
+ stdout.write(encodeNativeMessage(response));
223
+ }
224
+ }
225
+
226
+ function onData(chunk) {
227
+ queue = queue.then(() => handleChunk(chunk)).catch(() => {
228
+ stdout.write(encodeNativeMessage(safeError({ type: GENERIC_ERROR, code: 'ENIGMA_NATIVE_HOST_ERROR', message: 'Enigma native host failed without exposing local memory.' })));
229
+ });
230
+ }
231
+
232
+ function onError() {
233
+ stderr.write('enigma-native-host stdin error\n');
234
+ }
235
+
236
+ return {
237
+ handleChunk,
238
+ start() {
239
+ if (started) return this;
240
+ started = true;
241
+ stdin.on('data', onData);
242
+ stdin.on('error', onError);
243
+ return this;
244
+ },
245
+ async stop() {
246
+ if (!started) return;
247
+ started = false;
248
+ stdin.off('data', onData);
249
+ stdin.off('error', onError);
250
+ await queue;
251
+ },
252
+ };
253
+ }
254
+
255
+ function nativeHostUsage() {
256
+ return {
257
+ manifest_generator: 'enigma native-host manifest --browser <chrome|edge|firefox> --host-path <absolute path> --extension-id <id> [--out <file>]',
258
+ install_planner: 'enigma native-host install-plan --browser <chrome|edge|firefox> --manifest <absolute path> [--os <windows|macos|linux>] [--home <absolute path>]',
259
+ usage: 'enigma-native-host [--bundle <path>]',
260
+ native_host: NATIVE_HOST_NAME,
261
+ protocol: NATIVE_BROWSER_PROTOCOL,
262
+ transport: 'Chrome/Edge/Firefox native messaging: 4-byte little-endian length-prefixed JSON over stdio.',
263
+ options: {
264
+ '--bundle <path>': 'Local Enigma bundle path. Defaults to ENIGMA_BUNDLE.',
265
+ '--help': 'Print this JSON help and exit without starting the native messaging loop.',
266
+ },
267
+ claim_boundaries: 'The native host reads local Enigma bundles and returns user-approved context plus receipt summaries. It does not prove provider deletion, model forgetting, or provider-side memory behavior.',
268
+ };
269
+ }
270
+
271
+ function isHelpArgv(argv) {
272
+ return Array.isArray(argv) && (argv.includes('--help') || argv.includes('-h'));
273
+ }
274
+
275
+ function printJson(value, stdout) {
276
+ stdout.write(`${JSON.stringify(value, null, 2)}\n`);
277
+ }
278
+
279
+ export async function main(argv = process.argv.slice(2), io = { stdin: process.stdin, stdout: process.stdout, stderr: process.stderr, env: process.env }) {
280
+ if (isHelpArgv(argv)) {
281
+ printJson(nativeHostUsage(), io.stdout);
282
+ return 0;
283
+ }
284
+ createNativeHostRuntime({ stdin: io.stdin, stdout: io.stdout, stderr: io.stderr, env: io.env, argv }).start();
285
+ return 0;
286
+ }
287
+
288
+ async function handleContextRequest(message, options) {
289
+ const id = safeMessageId(message.id);
290
+ const loaded = await loadVault(options);
291
+ if (!loaded.ok) return safeError({ id, type: CONTEXT_RESPONSE, ...loaded.error });
292
+
293
+ const activeAddresses = [...(loaded.vault.activeAddresses ?? [])];
294
+ if (activeAddresses.length === 0) {
295
+ return safeError({
296
+ id,
297
+ type: CONTEXT_RESPONSE,
298
+ code: 'ENIGMA_NO_ACTIVE_MEMORIES',
299
+ message: 'Add local Enigma memories before requesting browser context.',
300
+ action: 'Run enigma remember, then retry from the browser extension.',
301
+ });
302
+ }
303
+
304
+ const contextPack = compileContextPack({
305
+ vault: loaded.vault,
306
+ provider: safeProvider(message.provider),
307
+ purpose: 'browser_context_injection',
308
+ limit: options.limit ?? options.maxMemories ?? 12,
309
+ now: options.now,
310
+ });
311
+ const text = renderContextText(contextPack);
312
+ if (text.length === 0) {
313
+ return safeError({
314
+ id,
315
+ type: CONTEXT_RESPONSE,
316
+ code: 'ENIGMA_NO_CONTEXT_TEXT',
317
+ message: 'No insertable Enigma context is available.',
318
+ });
319
+ }
320
+
321
+ await persistVault(loaded, options);
322
+ return {
323
+ id,
324
+ protocol: NATIVE_BROWSER_PROTOCOL,
325
+ type: CONTEXT_RESPONSE,
326
+ ok: true,
327
+ context: {
328
+ text,
329
+ mime: 'text/plain',
330
+ charCount: text.length,
331
+ },
332
+ receipt: summarizeReceipt(contextPack.injection_receipts.at(-1) ?? contextPack.receipts.at(-1), contextPack.receipt_log_root),
333
+ };
334
+ }
335
+
336
+ async function handleInsertionRecord(message, options) {
337
+ const id = safeMessageId(message.id);
338
+ const loaded = await loadVault(options);
339
+ if (!loaded.ok) return safeError({ id, type: INSERTION_RECORDED, ...loaded.error });
340
+
341
+ const insertion = message.insertion && typeof message.insertion === 'object' && !Array.isArray(message.insertion) ? message.insertion : {};
342
+ const sourceReceipt = summarizeBrowserReceiptForMetadata(insertion.receipt);
343
+ const event = loaded.vault.__recordEvent({
344
+ operation: 'browser_insert_ack',
345
+ provider: safeProvider(message.provider),
346
+ purpose: 'browser_context_insertion_record',
347
+ now: options.now,
348
+ metadata: {
349
+ browser_receipt_commitment: sourceReceipt.commitmentDigest,
350
+ browser_receipt_id_commitment: sourceReceipt.idDigest,
351
+ inserted_char_count: safeCount(insertion.insertedCharCount),
352
+ insertion_mode: insertion.mode === 'replace-selection' ? 'replace-selection' : 'insert-at-cursor',
353
+ insertion_target_commitment: `sha256:${sha256Hex(String(insertion.target ?? 'prompt'))}`,
354
+ inserted_at_commitment: `sha256:${sha256Hex(String(insertion.insertedAt ?? ''))}`,
355
+ },
356
+ });
357
+ const roots = loaded.vault.__computeRoots();
358
+ await persistVault(loaded, options);
359
+ return {
360
+ id,
361
+ protocol: NATIVE_BROWSER_PROTOCOL,
362
+ type: INSERTION_RECORDED,
363
+ ok: true,
364
+ receipt: summarizeReceipt(event.receipt, roots.receipt_log_root),
365
+ };
366
+ }
367
+
368
+ async function loadVault(options) {
369
+ if (options.vault) return { ok: true, vault: options.vault, bundlePath: options.bundlePath, source: 'options.vault' };
370
+ if (options.bundle) {
371
+ try {
372
+ return { ok: true, ...importBundle({ bundle: options.bundle }), bundlePath: options.bundlePath, source: 'options.bundle' };
373
+ } catch {
374
+ return { ok: false, error: invalidBundleError() };
375
+ }
376
+ }
377
+
378
+ const bundlePath = bundlePathFromOptions(options);
379
+ if (!bundlePath) {
380
+ return {
381
+ ok: false,
382
+ error: {
383
+ code: 'ENIGMA_BUNDLE_REQUIRED',
384
+ message: 'Set ENIGMA_BUNDLE or pass --bundle to enigma-native-host.',
385
+ action: 'Create a local bundle with enigma init or enigma export, then set ENIGMA_BUNDLE.',
386
+ },
387
+ };
388
+ }
389
+
390
+ try {
391
+ const bundle = JSON.parse(await readFile(bundlePath, 'utf8'));
392
+ return { ok: true, ...importBundle({ bundle }), bundlePath, source: 'bundlePath' };
393
+ } catch {
394
+ return { ok: false, error: invalidBundleError() };
395
+ }
396
+ }
397
+
398
+ async function persistVault(loaded, options) {
399
+ if (options.persist === false || !loaded.bundlePath || !loaded.vault) return;
400
+ const bundle = exportBundle({ vault: loaded.vault, now: options.now });
401
+ await writeFile(loaded.bundlePath, `${JSON.stringify(bundle, null, 2)}\n`);
402
+ }
403
+
404
+ function invalidBundleError() {
405
+ return {
406
+ code: 'ENIGMA_BUNDLE_INVALID',
407
+ message: 'Enigma native host could not load the local bundle.',
408
+ action: 'Check that ENIGMA_BUNDLE points to an enigma.vault_bundle.v1 JSON file.',
409
+ };
410
+ }
411
+
412
+ function bundlePathFromOptions(options) {
413
+ if (typeof options.bundlePath === 'string' && options.bundlePath.length > 0) return options.bundlePath;
414
+ const argv = Array.isArray(options.argv) ? options.argv : [];
415
+ for (let index = 0; index < argv.length; index += 1) {
416
+ if (argv[index] === '--bundle' && typeof argv[index + 1] === 'string' && argv[index + 1].length > 0) return argv[index + 1];
417
+ if (argv[index]?.startsWith('--bundle=')) return argv[index].slice('--bundle='.length);
418
+ }
419
+ const env = options.env && typeof options.env === 'object' ? options.env : {};
420
+ return typeof env.ENIGMA_BUNDLE === 'string' && env.ENIGMA_BUNDLE.length > 0 ? env.ENIGMA_BUNDLE : undefined;
421
+ }
422
+
423
+ function renderContextText(contextPack) {
424
+ return contextPack.memories
425
+ .map((memory, index) => {
426
+ const content = typeof memory.content === 'string' ? memory.content.trim() : '';
427
+ return content.length > 0 ? `Enigma memory ${index + 1}: ${content}` : '';
428
+ })
429
+ .filter(Boolean)
430
+ .join('\n');
431
+ }
432
+
433
+ function summarizeReceipt(receipt, rootCommitment) {
434
+ const commitment = SHA256_ROOT_RE.test(rootCommitment ?? '') ? rootCommitment : receiptHash(receipt);
435
+ return {
436
+ id: String(receipt?.receipt_id ?? receipt?.id ?? receiptHash(receipt)),
437
+ commitment,
438
+ digestAlgorithm: 'sha256',
439
+ createdAt: String(receipt?.timestamp ?? receipt?.createdAt ?? new Date(0).toISOString()),
440
+ };
441
+ }
442
+
443
+ function summarizeBrowserReceiptForMetadata(receipt) {
444
+ const safeReceipt = receipt && typeof receipt === 'object' && !Array.isArray(receipt)
445
+ ? {
446
+ id: typeof receipt.id === 'string' ? receipt.id : '',
447
+ commitment: typeof (receipt.commitment ?? receipt.digest) === 'string' ? (receipt.commitment ?? receipt.digest) : '',
448
+ digestAlgorithm: typeof receipt.digestAlgorithm === 'string' ? receipt.digestAlgorithm : '',
449
+ createdAt: typeof receipt.createdAt === 'string' ? receipt.createdAt : '',
450
+ }
451
+ : {};
452
+ return {
453
+ idDigest: `sha256:${sha256Hex(String(safeReceipt.id ?? ''))}`,
454
+ commitmentDigest: `sha256:${sha256Hex(canonicalize(safeReceipt))}`,
455
+ };
456
+ }
457
+
458
+ function responseTypeFor(type) {
459
+ if (type === CONTEXT_REQUEST) return CONTEXT_RESPONSE;
460
+ if (type === INSERTION_RECORD) return INSERTION_RECORDED;
461
+ return GENERIC_ERROR;
462
+ }
463
+
464
+ function safeError({ id, type, code, message, action }) {
465
+ return {
466
+ id,
467
+ protocol: NATIVE_BROWSER_PROTOCOL,
468
+ type,
469
+ ok: false,
470
+ code,
471
+ message,
472
+ ...(action ? { action } : {}),
473
+ };
474
+ }
475
+
476
+ function safeMessageId(id) {
477
+ return typeof id === 'string' && SAFE_MESSAGE_ID_RE.test(id) ? id : undefined;
478
+ }
479
+
480
+ function safeProvider(provider) {
481
+ return typeof provider === 'string' && SUPPORTED_PROVIDER_IDS.has(provider) ? provider : 'browser';
482
+ }
483
+
484
+ function safeCount(value) {
485
+ return Number.isSafeInteger(value) && value >= 0 ? value : 0;
486
+ }
487
+ function isMainModule(metaUrl) {
488
+ if (!process.argv[1]) return false;
489
+ try {
490
+ return metaUrl === pathToFileURL(realpathSync(process.argv[1])).href;
491
+ } catch {
492
+ return metaUrl === pathToFileURL(process.argv[1]).href;
493
+ }
494
+ }
495
+
496
+
497
+ if (isMainModule(import.meta.url)) {
498
+ await main();
499
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "com.enigma.native_host",
3
+ "description": "Enigma local native messaging host for explicit browser context requests.",
4
+ "path": "/ABSOLUTE/PATH/TO/enigma-native-host",
5
+ "type": "stdio",
6
+ "allowed_origins": [
7
+ "chrome-extension://REPLACE_WITH_CHROME_EXTENSION_ID/"
8
+ ]
9
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "com.enigma.native_host",
3
+ "description": "Enigma local native messaging host for explicit browser context requests.",
4
+ "path": "/ABSOLUTE/PATH/TO/enigma-native-host",
5
+ "type": "stdio",
6
+ "allowed_origins": [
7
+ "chrome-extension://REPLACE_WITH_EDGE_EXTENSION_ID/"
8
+ ]
9
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "com.enigma.native_host",
3
+ "description": "Enigma local native messaging host for explicit browser context requests.",
4
+ "path": "/ABSOLUTE/PATH/TO/enigma-native-host",
5
+ "type": "stdio",
6
+ "allowed_extensions": [
7
+ "REPLACE_WITH_FIREFOX_EXTENSION_ID"
8
+ ]
9
+ }
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env node
2
+ import { realpathSync } from 'node:fs';
3
+ import { pathToFileURL } from 'node:url';
4
+ import { DEFAULT_RELAY_PORT, main as cliMain } from '../../cli/bin/enigma.mjs';
5
+
6
+ function relayUsage() {
7
+ return {
8
+ usage: 'enigma-relay [demo|serve] [options]',
9
+ default_command: 'serve',
10
+ commands: ['demo', 'serve'],
11
+ serve_options: {
12
+ '--host <host>': 'Bind host. Defaults to 127.0.0.1.',
13
+ '--port <port>': `Bind port. Defaults to ${DEFAULT_RELAY_PORT}.`,
14
+ '--state-file <path>': 'Load and persist local relay demo state as JSON.',
15
+ '--once': 'Start, report the listening address, persist state when configured, then close.',
16
+ },
17
+ claim_boundaries: 'Relay accepts opaque encrypted records only; do not send raw memory plaintext, prompts, transcripts, or conversation bodies.',
18
+ };
19
+ }
20
+
21
+ function print(value, io) {
22
+ io.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
23
+ }
24
+
25
+ function isHelp(argv) {
26
+ return argv.includes('--help') || argv.includes('-h');
27
+ }
28
+
29
+ function relayArgv(argv) {
30
+ return argv[0] === 'demo' || argv[0] === 'serve' ? ['relay', ...argv] : ['relay', 'serve', ...argv];
31
+ }
32
+
33
+ export async function main(argv = process.argv.slice(2), io = { stdout: process.stdout, stderr: process.stderr }) {
34
+ if (isHelp(argv)) {
35
+ print(relayUsage(), io);
36
+ return 0;
37
+ }
38
+ return cliMain(relayArgv(argv), io);
39
+ }
40
+ function isMainModule(metaUrl) {
41
+ if (!process.argv[1]) return false;
42
+ try {
43
+ return metaUrl === pathToFileURL(realpathSync(process.argv[1])).href;
44
+ } catch {
45
+ return metaUrl === pathToFileURL(process.argv[1]).href;
46
+ }
47
+ }
48
+
49
+
50
+ if (isMainModule(import.meta.url)) {
51
+ process.exitCode = await main();
52
+ }