mumpix 1.0.18 → 1.0.19

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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.0.19 - 2026-04-13
4
+
5
+ ### Internal & Sync
6
+ - Synchronized latest temporal engine and operator scripts from core module into the package tree.
7
+ - Configured embedded GGUF agent context window ceilings (8K) to prevent VRAM memory allocation conflicts with local LLMs (e.g., `mumpix-mlc-llm`).
8
+
9
+ ## 1.0.18 - 2026-03-30
10
+ - General performance and sync updates
11
+
12
+ ## 1.0.17 - 2026-03-24
13
+ - Core temporal sync patches
14
+
3
15
  ## 1.0.11 - 2026-03-03
4
16
 
5
17
  ## 1.0.12 - 2026-03-03
package/README.md CHANGED
@@ -95,6 +95,69 @@ await db.stats();
95
95
  await db.close();
96
96
  ```
97
97
 
98
+ ### Temporal API
99
+
100
+ `mumpix` also exposes a deterministic temporal layer built on top of WAL-backed records.
101
+
102
+ This is meant to answer timeline questions by computing over normalized events, not by asking a model to guess from raw text.
103
+
104
+ ```js
105
+ const { Mumpix } = require('mumpix');
106
+
107
+ const db = await Mumpix.open('./agent.mumpix', { consistency: 'eventual' });
108
+
109
+ await db.remember({
110
+ content: 'I set up the smart thermostat on March 1st.',
111
+ workspace: 'home',
112
+ repo: 'infra-home',
113
+ source: 'ops-log',
114
+ ts: Date.parse('2026-03-03T09:10:00Z'),
115
+ });
116
+
117
+ await db.remember({
118
+ content: 'I set up the mesh network system on March 3rd.',
119
+ workspace: 'home',
120
+ repo: 'infra-home',
121
+ source: 'ops-log',
122
+ ts: Date.parse('2026-03-03T09:00:00Z'),
123
+ });
124
+
125
+ const events = await db.events();
126
+ const index = await db.eventIndex();
127
+ const result = await db.temporalQuery(
128
+ 'Which device did I set up first, the smart thermostat or the mesh network system?'
129
+ );
130
+
131
+ console.log(events.length);
132
+ console.log(Array.from(index.byWorkspace.keys()));
133
+ console.log(result.value); // smart thermostat
134
+ ```
135
+
136
+ Available temporal package APIs:
137
+
138
+ - `db.events({ query?, topK? })`
139
+ - `db.eventIndex({ query?, topK? })`
140
+ - `db.temporalQuery(query, { topK?, now? })`
141
+
142
+ Low-level exports are also available for advanced use:
143
+
144
+ ```js
145
+ const { temporal, temporalEngine, temporalIndexes } = require('mumpix');
146
+ ```
147
+
148
+ These expose:
149
+
150
+ - deterministic event operators
151
+ - event materialization
152
+ - event indexing by title, type, workspace, repo, and source
153
+
154
+ Temporal records support scoped metadata on write:
155
+
156
+ - `workspace`
157
+ - `repo`
158
+ - `source`
159
+ - `ts`
160
+
98
161
  ### Consistency modes
99
162
 
100
163
  | Mode | Behavior | Use case |
@@ -159,6 +222,7 @@ Examples:
159
222
  ```bash
160
223
  node examples/langchain-adapter.js
161
224
  node examples/llamaindex-adapter.js
225
+ node examples/temporal-memory.js
162
226
  ```
163
227
 
164
228
  ## CLI
package/bin/mumpix.js CHANGED
File without changes
@@ -0,0 +1,80 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * examples/temporal-memory.js
5
+ *
6
+ * Run:
7
+ * node examples/temporal-memory.js
8
+ */
9
+
10
+ const fs = require('fs');
11
+ const os = require('os');
12
+ const path = require('path');
13
+ const { Mumpix } = require('../src/index');
14
+
15
+ async function main() {
16
+ const dbPath = path.join(os.tmpdir(), `mumpix-temporal-${process.pid}.mumpix`);
17
+ const db = await Mumpix.open(dbPath, { consistency: 'eventual' });
18
+
19
+ try {
20
+ await db.rememberAll([
21
+ {
22
+ content: 'I set up the mesh network system on March 3rd.',
23
+ workspace: 'home',
24
+ repo: 'infra-home',
25
+ source: 'ops-log',
26
+ ts: Date.parse('2026-03-03T09:00:00Z'),
27
+ },
28
+ {
29
+ content: 'I set up the smart thermostat on March 1st.',
30
+ workspace: 'home',
31
+ repo: 'infra-home',
32
+ source: 'ops-log',
33
+ ts: Date.parse('2026-03-03T09:10:00Z'),
34
+ },
35
+ {
36
+ content: 'I deployed the schema migration on January 10th.',
37
+ workspace: 'client-b',
38
+ repo: 'billing-api',
39
+ source: 'deploy-log',
40
+ ts: Date.parse('2026-01-10T09:00:00Z'),
41
+ },
42
+ {
43
+ content: 'I noticed the payment queue was not functioning correctly on January 12th.',
44
+ workspace: 'client-b',
45
+ repo: 'billing-api',
46
+ source: 'incident-log',
47
+ ts: Date.parse('2026-01-12T09:00:00Z'),
48
+ },
49
+ ]);
50
+
51
+ const events = await db.events();
52
+ console.log('events:', events.map((event) => ({
53
+ title: event.title,
54
+ type: event.event_type,
55
+ date: event.event_date,
56
+ workspace: event.workspace,
57
+ repo: event.repo,
58
+ source: event.source,
59
+ })));
60
+
61
+ const index = await db.eventIndex();
62
+ console.log('workspaces indexed:', Array.from(index.byWorkspace.keys()));
63
+
64
+ const firstSetup = await db.temporalQuery('Which device did I set up first, the smart thermostat or the mesh network system?');
65
+ console.log('first setup:', firstSetup.value);
66
+
67
+ const daysBetween = await db.temporalQuery('How many days had passed between "schema migration" and "payment queue"?');
68
+ console.log('days between deploy and issue:', daysBetween.value);
69
+ } finally {
70
+ await db.close();
71
+ for (const suffix of ['', '.wal', '.lock']) {
72
+ try { fs.unlinkSync(dbPath + suffix); } catch (_) {}
73
+ }
74
+ }
75
+ }
76
+
77
+ main().catch((error) => {
78
+ console.error(error);
79
+ process.exit(1);
80
+ });
package/package.json CHANGED
@@ -1,17 +1,18 @@
1
1
  {
2
2
  "name": "mumpix",
3
- "version": "1.0.18",
3
+ "version": "1.0.19",
4
4
  "description": "MumpixDB reasoning ledger and structured memory engine for AI systems",
5
5
  "main": "src/index.js",
6
6
  "bin": {
7
7
  "mumpix": "bin/mumpix.js"
8
8
  },
9
9
  "scripts": {
10
+ "build": "node ./scripts/build-package.cjs",
10
11
  "postinstall": "node ./scripts/postinstall-auth.js",
11
12
  "verify:claims": "node ./scripts/verify-claims.cjs",
12
13
  "release:publish-and-deprecate": "node ./scripts/publish-and-deprecate.cjs",
13
14
  "release:clean": "bash ./scripts/release-clean.sh",
14
- "release:pack": "npm run release:clean && npm pack --cache /tmp/mumpix-npm-cache"
15
+ "release:pack": "npm run build && npm run release:clean && node ./scripts/obfuscate.cjs && npm pack --cache /tmp/mumpix-npm-cache ; node ./scripts/restore.cjs"
15
16
  },
16
17
  "keywords": [
17
18
  "ai",
@@ -55,5 +56,8 @@
55
56
  "CHANGELOG.md",
56
57
  "README.md",
57
58
  "LICENSE"
58
- ]
59
+ ],
60
+ "devDependencies": {
61
+ "javascript-obfuscator": "^5.4.1"
62
+ }
59
63
  }
@@ -18,6 +18,8 @@ const { MumpixAudit } = require('./audit');
18
18
  const { recall, recallMany } = require('./recall');
19
19
  const { LicenseManager } = require('./license');
20
20
  const { getStoredLicenseKey } = require('./auth');
21
+ const { answerTemporalQuery, materializeEvents, materializeEventIndex, extractEventsFromRecord } = require('../temporal/engine');
22
+ const { appendEventIndex } = require('../temporal/indexes');
21
23
 
22
24
  const VALID_MODES = ['eventual', 'strict', 'verified'];
23
25
 
@@ -34,6 +36,7 @@ class MumpixDB {
34
36
  this._opts = opts;
35
37
  this._closed = false;
36
38
  this._license = license;
39
+ this._temporalCache = null;
37
40
  }
38
41
 
39
42
  // ─────────────────────────────────────────
@@ -63,16 +66,17 @@ class MumpixDB {
63
66
  const store = new MumpixStore(filePath);
64
67
  let auditLog = null;
65
68
  try {
66
- store.open({ consistency: requestedConsistency });
69
+ await store.open({ consistency: requestedConsistency });
67
70
 
68
71
  // License initialization & check.
69
72
  // Allow dependency injection for local verification and integration tests.
70
73
  const resolvedLicenseKey = opts.licenseKey || getStoredLicenseKey() || null;
71
- const license = opts.licenseManager instanceof LicenseManager
72
- ? opts.licenseManager
73
- : new LicenseManager(resolvedLicenseKey);
74
+ const license = new LicenseManager(resolvedLicenseKey);
74
75
  license.setFileContext(store.header && store.header.fileId ? store.header.fileId : null);
75
- await license.init();
76
+ const licenseReady = await license.init();
77
+ if (resolvedLicenseKey && !licenseReady) {
78
+ console.warn(`[DEBUG] License rejected: ${license.validationError}`);
79
+ }
76
80
 
77
81
  // Capability-gated mode check with graceful downgrade policy:
78
82
  // if a paid mode (strict/verified) is requested with expired/invalid license,
@@ -84,6 +88,7 @@ class MumpixDB {
84
88
  license.assertActive(requestedConsistency);
85
89
  } catch (modeErr) {
86
90
  const msg = String(modeErr && modeErr.message ? modeErr.message : modeErr).toLowerCase();
91
+ console.warn(`[DEBUG] Mode check failed: ${msg}`);
87
92
  const downgradeable =
88
93
  msg.includes('expired') ||
89
94
  msg.includes('invalid for mode') ||
@@ -97,9 +102,10 @@ class MumpixDB {
97
102
  }
98
103
 
99
104
  if (effectiveConsistency !== requestedConsistency) {
105
+ console.warn(`[DEBUG] Downgrading consistency to ${effectiveConsistency}`);
100
106
  // Re-open with downgraded mode after releasing current FD/lock first.
101
107
  store.close();
102
- store.open({ consistency: effectiveConsistency });
108
+ await store.open({ consistency: effectiveConsistency });
103
109
  }
104
110
 
105
111
  if (effectiveConsistency === 'verified') {
@@ -134,16 +140,31 @@ class MumpixDB {
134
140
  * @param {string} content
135
141
  * @returns {{ id: number, written: boolean, consistency: string }}
136
142
  */
137
- async remember(content) {
143
+ async remember(content, opts = {}) {
138
144
  this._assertOpen();
139
- if (typeof content !== 'string' || !content.trim()) {
140
- throw new TypeError('remember() requires a non-empty string');
145
+ let text = content;
146
+ let meta = opts;
147
+
148
+ if (content && typeof content === 'object' && !Array.isArray(content)) {
149
+ text = content.content;
150
+ meta = {
151
+ ...opts,
152
+ workspace: content.workspace ?? opts.workspace,
153
+ repo: content.repo ?? opts.repo,
154
+ source: content.source ?? opts.source,
155
+ ts: content.ts ?? opts.ts,
156
+ };
157
+ }
158
+
159
+ if (typeof text !== 'string' || !text.trim()) {
160
+ throw new TypeError('remember() requires a non-empty string or { content } object');
141
161
  }
142
162
 
143
163
  // License Check for Record Limit
144
164
  this._license.checkLimit('records', this._store.records.length);
145
165
 
146
- const record = this._store.write(content);
166
+ const record = this._store.write(text, meta);
167
+ this._appendTemporalRecord(record);
147
168
 
148
169
  if (this._audit) {
149
170
  this._audit.logWrite(record);
@@ -234,6 +255,104 @@ class MumpixDB {
234
255
  }));
235
256
  }
236
257
 
258
+ /**
259
+ * Materialize normalized temporal events from WAL-backed records.
260
+ *
261
+ * @param {object} [opts]
262
+ * @param {string} [opts.query] Optional query to shortlist candidate records first
263
+ * @param {number} [opts.topK=50]
264
+ * @returns {Array<object>}
265
+ */
266
+ async events(opts = {}) {
267
+ this._assertOpen();
268
+ const records = this._store.all();
269
+ if (!records.length) return [];
270
+
271
+ if (!opts.query) {
272
+ return this._getTemporalCache().events;
273
+ }
274
+
275
+ if (opts.query && typeof opts.query === 'string' && opts.query.trim()) {
276
+ const topK = Number.isFinite(opts.topK) ? Number(opts.topK) : 50;
277
+ const shortlisted = await recallMany(opts.query, records, {
278
+ embedFn: this._opts.embedFn,
279
+ k: topK,
280
+ });
281
+ return materializeEvents(shortlisted);
282
+ }
283
+
284
+ return this._getTemporalCache().events;
285
+ }
286
+
287
+ /**
288
+ * Return derived event indexes built from WAL-backed records.
289
+ *
290
+ * @param {object} [opts]
291
+ * @param {string} [opts.query]
292
+ * @param {number} [opts.topK=50]
293
+ * @returns {object}
294
+ */
295
+ async eventIndex(opts = {}) {
296
+ this._assertOpen();
297
+ const records = this._store.all();
298
+ if (!records.length) {
299
+ return materializeEventIndex([]);
300
+ }
301
+
302
+ if (!opts.query) {
303
+ return this._getTemporalCache();
304
+ }
305
+
306
+ const topK = Number.isFinite(opts.topK) ? Number(opts.topK) : 50;
307
+ const shortlisted = await recallMany(opts.query, records, {
308
+ embedFn: this._opts.embedFn,
309
+ k: topK,
310
+ });
311
+ return materializeEventIndex(shortlisted);
312
+ }
313
+
314
+ /**
315
+ * Run a deterministic temporal query over WAL-backed records.
316
+ *
317
+ * @param {string} query
318
+ * @param {object} [opts]
319
+ * @param {number} [opts.topK=50]
320
+ * @param {Date|string|number} [opts.now]
321
+ * @returns {object}
322
+ */
323
+ async temporalQuery(query, opts = {}) {
324
+ this._assertOpen();
325
+ if (typeof query !== 'string' || !query.trim()) {
326
+ throw new TypeError('temporalQuery() requires a non-empty string');
327
+ }
328
+
329
+ const records = this._store.all();
330
+ if (!records.length) {
331
+ return {
332
+ ok: false,
333
+ kind: null,
334
+ value: null,
335
+ evidence: [],
336
+ warnings: ['No records available.'],
337
+ parsed_query: null,
338
+ events: [],
339
+ };
340
+ }
341
+
342
+ const topK = Number.isFinite(opts.topK) ? Number(opts.topK) : 50;
343
+ const shortlisted = await recallMany(query, records, {
344
+ embedFn: this._opts.embedFn,
345
+ k: topK,
346
+ });
347
+
348
+ const now =
349
+ opts.now instanceof Date ? opts.now :
350
+ (typeof opts.now === 'string' || typeof opts.now === 'number') ? new Date(opts.now) :
351
+ new Date();
352
+
353
+ return answerTemporalQuery(query, shortlisted, { now });
354
+ }
355
+
237
356
  /**
238
357
  * Delete all memories.
239
358
  *
@@ -242,6 +361,7 @@ class MumpixDB {
242
361
  async clear() {
243
362
  this._assertOpen();
244
363
  const count = this._store.clear();
364
+ this._invalidateTemporalCache();
245
365
 
246
366
  if (this._audit) {
247
367
  this._audit.logClear(count);
@@ -366,6 +486,34 @@ class MumpixDB {
366
486
  if (this._closed) throw new Error('Database is closed. Call Mumpix.open() again.');
367
487
  this._license.assertActive(this._store.header.consistency);
368
488
  }
489
+
490
+ _invalidateTemporalCache() {
491
+ this._temporalCache = null;
492
+ }
493
+
494
+ _temporalSignature() {
495
+ const records = this._store.records || [];
496
+ const last = records.length ? records[records.length - 1] : null;
497
+ return `${records.length}:${last ? last.id : 0}:${last ? last.ts : 0}`;
498
+ }
499
+
500
+ _getTemporalCache() {
501
+ const signature = this._temporalSignature();
502
+ if (this._temporalCache && this._temporalCache.signature === signature) {
503
+ return this._temporalCache.index;
504
+ }
505
+
506
+ const index = materializeEventIndex(this._store.all());
507
+ this._temporalCache = { signature, index };
508
+ return index;
509
+ }
510
+
511
+ _appendTemporalRecord(record) {
512
+ if (!this._temporalCache) return;
513
+ const events = extractEventsFromRecord(record);
514
+ appendEventIndex(this._temporalCache.index, events);
515
+ this._temporalCache.signature = this._temporalSignature();
516
+ }
369
517
  }
370
518
 
371
519
  module.exports = { MumpixDB };
@@ -7,7 +7,7 @@ const https = require('https');
7
7
  const path = require('path');
8
8
 
9
9
  // Default embedded public key (official issuer).
10
- const DEFAULT_MASTER_PUBLIC_KEY_B64 = `szA4EoME83k0zoSQnzMWSC5BOEnr1VazfZlyP4JKqufZHIjlc0TalnK4gi4Jsn0PMHAJlzEOp/0PvUiW8L0RN3rj0pKEkuwDY5c1Q5C2u1Dh7VfcyiCzj+JusviaANuJQxoEdi6yeBUXJO7tZpwpTrekF5hrc5V9ccY0U3gAofg+4prZz0bA7aaLxK+/WIYXtO+PR7VOAavMzR536FAdJtfcgz08L1hu7RUiE38hQnAH2yRuBkmcGesw4k0XSx3HZXjVwaVxZsOZpL3DihS2EwbluDIzrb3Tz5nf7a2ak/FRFcTS+LLjM4ywxuq6toKT/laAOt/0AEVp67nFGVm8OU4blpmgO3gA0APpglrVMGFcxrBQyYLqMsgwxmb6dQMydILriHQ9vN+jR8jD3qs5f14FwsZkkMtBgBMNRFybQvrXOxvPF36rknnkeDuE0W+9sSpzOdzM8/UZKInzh+v0yOYPIW1y9oiJb2RtoIz0kJkg0IwR8JOIZ7Svfa62ifT/CFhIsBr6zIVdK7mlxmDqiD3MnlLvfqhyPfdgIbCRLmClXI6Jjpt4d7shzBHvOxCpEGwYw4hVCGTnVhPne8UWmgqU3BkSVBQsHU/7s6jNH/tvZjfkOgWoz81RrnrE8xMGRh5yn/F/bEa0igbyJ/4XlmAPRMkQ+CCIo/NbBlYtKvnMHTwatj3B4Z06hTFl0rnuGbQ9u9JXp/1x7bVztMdSM/T65VW8H/zcvZ7aZTbEMzcTUqMdo9IwOWVfyb6lwg5FSCANKzmyT//CJQtpacS9TjPZ0m4AOFyAwhj57fxY7xMPSYQIyTrpMRkoNACJ9QoSSAnyJ0u1aBTMbiFr8IgjgZm0XL2giz8yD5bi/kS+85qPGut+Hvx66VztumAQUoLpJYJSgVXO4ZYMNY0DraTE48l3Y2RG/KmerSGbjAjZjXPJlKxF94M8Y4Su18iy0BVpS7Ax+8nEzDPVfTqPUoSJctv/YLY6bdMqhi9/rTIrE/3Htm5YwHfZP+BV59JLFflT7iud4nBuRZ5rF7ONGMDELg/sglBOHqkv7bDaSoxgcD2SA0L3sptnW57ohUHwmFWuSE8zYkoUsT0sCwvClbmpcgPUxisSY3duAEC3Q+l+NibYhXsavUuHZ9h/O2oMUUvIxtmcF1t17c1f6QwhYpZlJYuYZ/wWTGWqD5MADslUUHCvla5hoZRhWOsDdQspP8O1Sa2Cpzh8zWuKAr2YaSNsUQi/orwzfWFZ56ss9JAH9d+W1Sqc0qOxeQgMoSF3UmHalDQ+xrRl/b1fcN5ZBNmDaM1o0JHGNl7gB7q0Va7NXH2KD1k21f7POPIAdTXlqquFS+MOdy+atPnhncht3bVzSRcerelIk/YGxKCJbsXqFB38s2tR7u4D56B6LiIl/1x4UmP9MmgvJDPMDwrpD9ExgCIyoordPiixYuOeq5LVG6VanIrrVgRlrGmW2lbVX5PMZfZsfhCPW9/JrvrqBPVtnqAjcappUGG4yJyHYFIXeE6Z1nd5DKdBHXEl6zACV6SwxJJ6qgapyOrXCnTmXqkmtI0ebJWGRJ+NHtgl29FF4LXV7HmNgrYrSFdgc6tv/ZBycEFPmsc/HK2Omo+dQKmVI/6w95ctKOsTQmim+pqz5QcZYE8LnCm+U/8iosivWaJ2s6jB+YXQ6Y4+IF9vSPonyqOViw97Moe9uq7qkSM+sfm2pL/oyGTsluOUftREbtIWFK4f4L9EEIWp9em/dAP6mQ==`;
10
+ const DEFAULT_MASTER_PUBLIC_KEY_B64 = `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`;
11
11
 
12
12
  function activeMasterPublicKeyB64() {
13
13
  return String(process.env.MUMPIX_LICENSE_PUBLIC_KEY_B64 || DEFAULT_MASTER_PUBLIC_KEY_B64).trim();
@@ -36,10 +36,6 @@ class LicenseManager {
36
36
  : 'free';
37
37
  }
38
38
 
39
- // Capability matrix for Option B model.
40
- // free/community: eventual only
41
- // developer/teams: eventual + strict
42
- // compliance/enterprise: eventual + strict + verified
43
39
  _capsForTier(tier) {
44
40
  const t = this._normalizeTier(tier);
45
41
  if (t === 'monthly') return { modes: ['eventual', 'strict'] };
@@ -62,13 +58,10 @@ class LicenseManager {
62
58
 
63
59
  _maxDaysForTier(tier) {
64
60
  const t = this._normalizeTier(tier);
65
- // Monthly subscriptions default to 45-day lease windows for offline/air-gapped tolerance.
66
61
  if (t === 'monthly') return envInt('MUMPIX_LICENSE_MAX_DAYS_MONTHLY', 45);
67
62
  if (t === 'standard') return envInt('MUMPIX_LICENSE_MAX_DAYS_STANDARD', 45);
68
63
  if (t === 'developer') return envInt('MUMPIX_LICENSE_MAX_DAYS_DEVELOPER', 365);
69
64
  if (t === 'teams') return envInt('MUMPIX_LICENSE_MAX_DAYS_TEAMS', 365);
70
- // Enterprise / government / compliance can be long-lived for air-gapped installs.
71
- // Defaults allow up to 100 years, and operators can tighten/expand via env.
72
65
  if (t === 'enterprise') return envInt('MUMPIX_LICENSE_MAX_DAYS_ENTERPRISE', 36500);
73
66
  if (t === 'government') return envInt('MUMPIX_LICENSE_MAX_DAYS_GOVERNMENT', 36500);
74
67
  if (t === 'compliance' || t === 'verified' || t === 'pro') {
@@ -160,8 +153,8 @@ class LicenseManager {
160
153
  globalThis.crypto = require('node:crypto').webcrypto;
161
154
  }
162
155
  const mlDsaPath = 'file://' + path.resolve(__dirname, 'ml-dsa.mjs');
163
- const { ml_dsa44 } = await import(mlDsaPath);
164
- this._ml_dsa = ml_dsa44;
156
+ const { ml_dsa65 } = await import(mlDsaPath);
157
+ this._ml_dsa = ml_dsa65;
165
158
  return await this.validate(this.key);
166
159
  } catch (e) {
167
160
  console.error('Mumpix Licensing Initialization Failed:', e.message);
@@ -177,12 +170,18 @@ class LicenseManager {
177
170
  const payloadRaw = Buffer.from(payloadB64, 'base64').toString();
178
171
  const payload = JSON.parse(payloadRaw);
179
172
 
180
- // Verify Quantum-Safe Signature (ML-DSA-44)
181
173
  const msg = Buffer.from(payloadB64, 'utf-8');
182
174
  const sig = Buffer.from(signatureB64, 'base64');
183
- const pub = Buffer.from(activeMasterPublicKeyB64(), 'base64');
175
+ const pubB64 = activeMasterPublicKeyB64();
176
+ const pub = Buffer.from(pubB64, 'base64');
177
+
178
+ console.log('[CRYPTO DEBUG] Payload Length:', msg.length);
179
+ console.log('[CRYPTO DEBUG] Sig Length:', sig.length);
180
+ console.log('[CRYPTO DEBUG] Pub Length:', pub.length);
181
+ console.log('[CRYPTO DEBUG] Pub Start (B64):', pubB64.substring(0, 20));
184
182
 
185
183
  const isValid = this._ml_dsa.verify(sig, msg, pub);
184
+ console.log('[CRYPTO DEBUG] isValid:', isValid);
186
185
  if (!isValid) throw new Error('Quantum signature verification failed');
187
186
 
188
187
  this._validateLeaseWindow(payload);
@@ -191,8 +190,6 @@ class LicenseManager {
191
190
  throw new Error('License key has expired');
192
191
  }
193
192
 
194
- // Optional binding for newly issued licenses.
195
- // Backward-compatible: if fid missing from payload, accept legacy license.
196
193
  if (payload.fid && this.fileId && String(payload.fid) !== String(this.fileId)) {
197
194
  throw new Error('License key is bound to a different file');
198
195
  }
@@ -214,7 +211,6 @@ class LicenseManager {
214
211
  }
215
212
 
216
213
  checkLimit(type, currentCount) {
217
- // Option B: no record count gate. Gating is by capabilities/mode.
218
214
  if (type === 'records') return true;
219
215
 
220
216
  if (type === 'mode') {
@@ -222,15 +218,15 @@ class LicenseManager {
222
218
  const caps = this._capsForTier(this.tier);
223
219
  if (!caps.modes.includes(requested)) {
224
220
  if (this.key && !this.verified && this.validationError) {
225
- throw new Error(`Mumpix license is invalid for mode "${requested}": ${this.validationError}`);
221
+ throw new Error(`Mumpix license is invalid for mode ${requested}: ${this.validationError}`);
226
222
  }
227
223
  if (requested === 'strict') {
228
- throw new Error('MumpixDB "strict" mode requires Developer tier or higher. Upgrade at mumpixdb.com.');
224
+ throw new Error('MumpixDB strict mode requires Developer tier or higher. Upgrade at mumpixdb.com.');
229
225
  }
230
226
  if (requested === 'verified') {
231
- throw new Error('MumpixDB "verified" mode requires Compliance tier. Upgrade at mumpixdb.com.');
227
+ throw new Error('MumpixDB verified mode requires Compliance tier. Upgrade at mumpixdb.com.');
232
228
  }
233
- throw new Error(`MumpixDB mode "${requested}" is not available on your current tier.`);
229
+ throw new Error(`MumpixDB mode ${requested} is not available on your current tier.`);
234
230
  }
235
231
  return true;
236
232
  }
@@ -242,7 +238,6 @@ class LicenseManager {
242
238
  this.checkLimit('mode', mode);
243
239
  const now = Date.now();
244
240
  this._enforceClockMonotonic(now);
245
- // Expired licenses degrade to eventual mode only. Strict/verified remain blocked until renewal.
246
241
  if (this.expiry && now > Number(this.expiry) && mode !== 'eventual') {
247
242
  throw new Error('Mumpix license expired. Renew license to continue using this mode.');
248
243
  }
@@ -269,4 +264,4 @@ class LicenseManager {
269
264
  }
270
265
  }
271
266
 
272
- module.exports = { LicenseManager };
267
+ module.exports = { LicenseManager };