@blamejs/core 0.7.98 → 0.7.100
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 +4 -0
- package/lib/db.js +75 -0
- package/lib/network-tls.js +78 -0
- package/package.json +1 -1
- package/sbom.cyclonedx.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,10 @@ upgrading across more than a few patches at a time.
|
|
|
8
8
|
|
|
9
9
|
## v0.7.x
|
|
10
10
|
|
|
11
|
+
- **0.7.100** (2026-05-06) — `b.network.tls.expiryMonitor({ intervalMs, windowMs, onExpiring })` — periodic CA-trust-store expiry monitor. Runs `expiringSoon(windowMs)` on a schedule; emits `network.tls.ca.expiry_check` audit event on every check (with `expiring` count + `total` CA count), `network.tls.ca.expiring` audit event when any CA falls inside the window, and the matching `network.tls.ca.expiring` observability counter. Optional `onExpiring(rows)` operator hook fires on every check that surfaces expiring CAs so operators can wire pager / Slack alerts. Audit metadata captures the expiring CA labels + the earliest `validTo` timestamp so dashboards can compute "days until first expiry" without re-querying. Returns a handle with `.stop()` for graceful shutdown. Closes the v0.7.26 OCSP/CT batch's continuous-trust-monitoring follow-up.
|
|
12
|
+
|
|
13
|
+
- **0.7.99** (2026-05-06) — `b.db.integrityCheck()` + `b.db.integrityMonitor({ intervalMs, ... })` — periodic SQLite corruption detection. **`b.db.integrityCheck()`** runs `PRAGMA integrity_check` against the live database and returns `"ok"` on a healthy database, or an array of corruption description strings on damage. Operators wire this into `/healthz` handlers or one-off CLI checks. **`b.db.integrityMonitor({ intervalMs, onCorruption })`** runs the check on a schedule (24h default), emits `system.db.integrity_ok` / `system.db.integrity_corrupt` audit events, and fires the `db.integrity_check_ok` / `db.integrity_check_corrupt` observability counters. Optional `onCorruption(issues)` operator hook fires on every corrupt-result so operators can wire pager alerts. The previous boot-time-only integrity check (added in v0.7.79) continues to run unchanged at db.init; the monitor is for long-running deployments where filesystem-level corruption can develop after boot. Returns a handle with `.stop()` for graceful shutdown.
|
|
14
|
+
|
|
11
15
|
- **0.7.98** (2026-05-06) — `b.ntpCheck.monitor({ intervalMs, ... })` — periodic clock-drift monitor that runs `checkDrift` on a schedule and emits audit + observability events on threshold crossings. Returns a handle with `.stop()` for graceful shutdown. Audit emissions: `system.ntp.checked` (every check), `system.ntp.drift_warn` (drift exceeds warn threshold), `system.ntp.drift_fatal` (drift exceeds fatal threshold), `system.ntp.unreachable` (every server in the list failed to respond). Observability event: `ntp.drift_ms` gauge on every successful check, labeled with the responding server. Optional `onDrift(result)` operator hook fires on every warning/fatal check so operators can wire pager / Slack notifications. The previous boot-time-only `b.ntpCheck.bootCheck` continues to run unchanged at db.init; the monitor is for long-running deployments where clock-drift can develop after boot (container with no RTC sync, ntpd stopped after boot, etc.). Closes the v0.7.79 audit-batch slice for continuous time integrity.
|
|
12
16
|
|
|
13
17
|
- **0.7.97** (2026-05-06) — `b.compliance` lookup helpers. **`b.compliance.posturesByDomain(domain)`** returns every posture matching the named domain (`"privacy"` / `"health"` / `"payment"` / `"cybersecurity"` / `"financial-reporting"` / `"financial-resilience"` / `"product-cybersecurity"` / `"ai-governance"` / `"biometrics"` / `"audit-attestation"`). **`b.compliance.posturesByJurisdiction(jurisdiction)`** returns postures matching the ISO 3166 alpha-2 code, `EU`, or `international` — useful for multi-region deployments that resolve different posture configs per region. **`b.compliance.list()`** returns every posture as a `{ posture, name, citation, jurisdiction, domain }` row in canonical `KNOWN_POSTURES` order — admin UIs render the full set as a dropdown / table without iterating REGIME_MAP keys themselves. All three helpers are pure functions over the v0.7.94 REGIME_MAP and stay in sync with it as new postures land.
|
package/lib/db.js
CHANGED
|
@@ -59,6 +59,7 @@ var dbSchema = require("./db-schema");
|
|
|
59
59
|
var { defineClass } = require("./framework-error");
|
|
60
60
|
var { boot } = require("./log");
|
|
61
61
|
var lazyRequire = require("./lazy-require");
|
|
62
|
+
var observability = require("./observability");
|
|
62
63
|
var ntpCheck = lazyRequire(function () { return require("./ntp-check"); });
|
|
63
64
|
var safeAsync = require("./safe-async");
|
|
64
65
|
var safeEnv = require("./parsers/safe-env");
|
|
@@ -1213,6 +1214,80 @@ module.exports = {
|
|
|
1213
1214
|
// the snapshot source. Safe to call any time; no-op when no encPath
|
|
1214
1215
|
// (plain mode) or when the plaintext DB doesn't exist.
|
|
1215
1216
|
flushToDisk: encryptToDisk,
|
|
1217
|
+
// integrityCheck — runs PRAGMA integrity_check against the live db
|
|
1218
|
+
// and returns "ok" on success, an array of corruption lines
|
|
1219
|
+
// otherwise. Operators wire this into a periodic monitor or a
|
|
1220
|
+
// /healthz handler.
|
|
1221
|
+
integrityCheck: function () {
|
|
1222
|
+
_requireInit();
|
|
1223
|
+
var rows = database.prepare("PRAGMA integrity_check").all();
|
|
1224
|
+
if (rows.length === 1 && rows[0] && rows[0].integrity_check === "ok") return "ok";
|
|
1225
|
+
return rows.map(function (r) { return r && r.integrity_check; }).filter(Boolean);
|
|
1226
|
+
},
|
|
1227
|
+
// integrityMonitor — periodic PRAGMA integrity_check runner. Returns
|
|
1228
|
+
// a handle with .stop() for graceful shutdown. Audit emission on
|
|
1229
|
+
// every check; observability event on corruption.
|
|
1230
|
+
//
|
|
1231
|
+
// var mon = b.db.integrityMonitor({
|
|
1232
|
+
// intervalMs: C.TIME.hours(6),
|
|
1233
|
+
// onCorruption: function (issues) { /* operator hook — alerts */ },
|
|
1234
|
+
// });
|
|
1235
|
+
// ...
|
|
1236
|
+
// mon.stop();
|
|
1237
|
+
//
|
|
1238
|
+
// Audit emissions:
|
|
1239
|
+
// system.db.integrity_ok — every clean check
|
|
1240
|
+
// system.db.integrity_corrupt — corruption detected
|
|
1241
|
+
//
|
|
1242
|
+
// Observability event: db.integrity_check_ok counter on every clean
|
|
1243
|
+
// check, db.integrity_check_corrupt counter on corruption.
|
|
1244
|
+
integrityMonitor: function (opts) {
|
|
1245
|
+
_requireInit();
|
|
1246
|
+
opts = opts || {};
|
|
1247
|
+
var intervalMs = opts.intervalMs || C.TIME.hours(24);
|
|
1248
|
+
if (typeof intervalMs !== "number" || !isFinite(intervalMs) || intervalMs <= 0) {
|
|
1249
|
+
throw new TypeError("db.integrityMonitor: intervalMs must be a positive finite number");
|
|
1250
|
+
}
|
|
1251
|
+
var auditOn = opts.audit !== false;
|
|
1252
|
+
|
|
1253
|
+
function _tick() {
|
|
1254
|
+
var rows;
|
|
1255
|
+
try { rows = database.prepare("PRAGMA integrity_check").all(); }
|
|
1256
|
+
catch (_e) {
|
|
1257
|
+
try { observability.safeEvent("db.integrity_check_failed", 1, {}); }
|
|
1258
|
+
catch (_e2) { /* drop-silent */ }
|
|
1259
|
+
return;
|
|
1260
|
+
}
|
|
1261
|
+
var ok = rows.length === 1 && rows[0] && rows[0].integrity_check === "ok";
|
|
1262
|
+
if (ok) {
|
|
1263
|
+
try { observability.safeEvent("db.integrity_check_ok", 1, {}); }
|
|
1264
|
+
catch (_e) { /* drop-silent */ }
|
|
1265
|
+
if (auditOn) {
|
|
1266
|
+
try { audit.safeEmit({
|
|
1267
|
+
action: "system.db.integrity_ok", outcome: "ok", metadata: {},
|
|
1268
|
+
}); } catch (_e) { /* drop-silent */ }
|
|
1269
|
+
}
|
|
1270
|
+
return;
|
|
1271
|
+
}
|
|
1272
|
+
var issues = rows.map(function (r) { return r && r.integrity_check; }).filter(Boolean);
|
|
1273
|
+
try { observability.safeEvent("db.integrity_check_corrupt", 1, {}); }
|
|
1274
|
+
catch (_e) { /* drop-silent */ }
|
|
1275
|
+
if (auditOn) {
|
|
1276
|
+
try { audit.safeEmit({
|
|
1277
|
+
action: "system.db.integrity_corrupt", outcome: "fail",
|
|
1278
|
+
metadata: { issueCount: issues.length },
|
|
1279
|
+
}); } catch (_e) { /* drop-silent */ }
|
|
1280
|
+
}
|
|
1281
|
+
if (typeof opts.onCorruption === "function") {
|
|
1282
|
+
try { opts.onCorruption(issues); } catch (_e) { /* operator hook */ }
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
var handle = safeAsync.repeating(_tick, intervalMs, { name: "db-integrity-monitor" });
|
|
1287
|
+
return {
|
|
1288
|
+
stop: function () { if (handle) { handle.stop(); handle = null; } },
|
|
1289
|
+
};
|
|
1290
|
+
},
|
|
1216
1291
|
// purgeAuditChain — narrow-purpose DELETE for audit-tools.purge.
|
|
1217
1292
|
// Drops the BEFORE-DELETE append-only trigger inside a transaction,
|
|
1218
1293
|
// executes the deletion, then re-installs the trigger so the
|
package/lib/network-tls.js
CHANGED
|
@@ -9,6 +9,7 @@ var C = require("./constants");
|
|
|
9
9
|
var safeBuffer = require("./safe-buffer");
|
|
10
10
|
var validateOpts = require("./validate-opts");
|
|
11
11
|
var lazyRequire = require("./lazy-require");
|
|
12
|
+
var safeAsync = require("./safe-async");
|
|
12
13
|
var { defineClass } = require("./framework-error");
|
|
13
14
|
|
|
14
15
|
var TlsTrustError = defineClass("TlsTrustError", { alwaysPermanent: true });
|
|
@@ -248,6 +249,82 @@ function expiringSoon(windowMs) {
|
|
|
248
249
|
});
|
|
249
250
|
}
|
|
250
251
|
|
|
252
|
+
// expiryMonitor — periodic check that emits audit + observability
|
|
253
|
+
// events when any CA in the trust store falls inside the expiry
|
|
254
|
+
// window. Returns a handle with .stop() for graceful shutdown.
|
|
255
|
+
//
|
|
256
|
+
// var mon = b.network.tls.expiryMonitor({
|
|
257
|
+
// intervalMs: C.TIME.hours(6),
|
|
258
|
+
// windowMs: C.TIME.days(30),
|
|
259
|
+
// onExpiring: function (rows) { /* operator hook — alerts */ },
|
|
260
|
+
// });
|
|
261
|
+
// ...
|
|
262
|
+
// mon.stop();
|
|
263
|
+
//
|
|
264
|
+
// Audit emissions:
|
|
265
|
+
// network.tls.ca.expiry_check — every check, reports total + expiring count
|
|
266
|
+
// network.tls.ca.expiring — when expiringSoon(windowMs) > 0
|
|
267
|
+
//
|
|
268
|
+
// Observability event: network.tls.ca.expiring counter labeled with
|
|
269
|
+
// the count.
|
|
270
|
+
function expiryMonitor(opts) {
|
|
271
|
+
opts = opts || {};
|
|
272
|
+
var intervalMs = opts.intervalMs;
|
|
273
|
+
var windowMs = opts.windowMs;
|
|
274
|
+
var auditOn = opts.audit !== false;
|
|
275
|
+
if (typeof intervalMs !== "number" || !isFinite(intervalMs) || intervalMs <= 0) {
|
|
276
|
+
throw new TlsTrustError("tls/bad-interval",
|
|
277
|
+
"tls.expiryMonitor: intervalMs must be a positive finite number");
|
|
278
|
+
}
|
|
279
|
+
if (typeof windowMs !== "number" || !isFinite(windowMs) || windowMs <= 0) {
|
|
280
|
+
throw new TlsTrustError("tls/bad-window",
|
|
281
|
+
"tls.expiryMonitor: windowMs must be a positive finite number");
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function _tick() {
|
|
285
|
+
var rows;
|
|
286
|
+
try { rows = expiringSoon(windowMs); }
|
|
287
|
+
catch (_e) { return; }
|
|
288
|
+
if (auditOn) {
|
|
289
|
+
try {
|
|
290
|
+
audit().safeEmit({
|
|
291
|
+
action: "network.tls.ca.expiry_check",
|
|
292
|
+
outcome: rows.length > 0 ? "warn" : "ok",
|
|
293
|
+
metadata: { total: STATE.cas.length, expiring: rows.length, windowMs: windowMs },
|
|
294
|
+
});
|
|
295
|
+
} catch (_e) { /* drop-silent */ }
|
|
296
|
+
}
|
|
297
|
+
if (rows.length > 0) {
|
|
298
|
+
try { observability().safeEvent("network.tls.ca.expiring", rows.length, {}); }
|
|
299
|
+
catch (_e) { /* drop-silent */ }
|
|
300
|
+
if (auditOn) {
|
|
301
|
+
try {
|
|
302
|
+
audit().safeEmit({
|
|
303
|
+
action: "network.tls.ca.expiring",
|
|
304
|
+
outcome: "warn",
|
|
305
|
+
metadata: {
|
|
306
|
+
count: rows.length,
|
|
307
|
+
labels: rows.map(function (r) { return r.label; }),
|
|
308
|
+
earliestValidTo: rows.reduce(function (acc, r) {
|
|
309
|
+
var ms = r.validTo ? Date.parse(r.validTo) : Infinity;
|
|
310
|
+
return ms < acc ? ms : acc;
|
|
311
|
+
}, Infinity),
|
|
312
|
+
},
|
|
313
|
+
});
|
|
314
|
+
} catch (_e) { /* drop-silent */ }
|
|
315
|
+
}
|
|
316
|
+
if (typeof opts.onExpiring === "function") {
|
|
317
|
+
try { opts.onExpiring(rows); } catch (_e) { /* operator hook */ }
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
var handle = safeAsync.repeating(_tick, intervalMs, { name: "tls-expiry-monitor" });
|
|
323
|
+
return {
|
|
324
|
+
stop: function () { if (handle) { handle.stop(); handle = null; } },
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
|
|
251
328
|
function captureBaselineFingerprints() {
|
|
252
329
|
STATE.baselineFingerprints = STATE.cas.map(function (e) { return e.meta.fingerprint256; });
|
|
253
330
|
}
|
|
@@ -1557,6 +1634,7 @@ module.exports = {
|
|
|
1557
1634
|
clearAll: clearAll,
|
|
1558
1635
|
purgeExpired: purgeExpired,
|
|
1559
1636
|
expiringSoon: expiringSoon,
|
|
1637
|
+
expiryMonitor: expiryMonitor,
|
|
1560
1638
|
useSystemTrust: useSystemTrust,
|
|
1561
1639
|
isSystemTrustEnabled: isSystemTrustEnabled,
|
|
1562
1640
|
getTrustStore: getTrustStore,
|
package/package.json
CHANGED
package/sbom.cyclonedx.json
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
"$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json",
|
|
3
3
|
"bomFormat": "CycloneDX",
|
|
4
4
|
"specVersion": "1.5",
|
|
5
|
-
"serialNumber": "urn:uuid:
|
|
5
|
+
"serialNumber": "urn:uuid:41d841af-2890-4424-89ab-7b411b784c39",
|
|
6
6
|
"version": 1,
|
|
7
7
|
"metadata": {
|
|
8
|
-
"timestamp": "2026-05-06T09:
|
|
8
|
+
"timestamp": "2026-05-06T09:26:08.410Z",
|
|
9
9
|
"lifecycles": [
|
|
10
10
|
{
|
|
11
11
|
"phase": "build"
|
|
@@ -19,14 +19,14 @@
|
|
|
19
19
|
}
|
|
20
20
|
],
|
|
21
21
|
"component": {
|
|
22
|
-
"bom-ref": "@blamejs/core@0.7.
|
|
22
|
+
"bom-ref": "@blamejs/core@0.7.100",
|
|
23
23
|
"type": "library",
|
|
24
24
|
"name": "blamejs",
|
|
25
|
-
"version": "0.7.
|
|
25
|
+
"version": "0.7.100",
|
|
26
26
|
"scope": "required",
|
|
27
27
|
"author": "blamejs contributors",
|
|
28
28
|
"description": "The Node framework that owns its stack.",
|
|
29
|
-
"purl": "pkg:npm/%40blamejs/core@0.7.
|
|
29
|
+
"purl": "pkg:npm/%40blamejs/core@0.7.100",
|
|
30
30
|
"properties": [],
|
|
31
31
|
"externalReferences": [
|
|
32
32
|
{
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"components": [],
|
|
55
55
|
"dependencies": [
|
|
56
56
|
{
|
|
57
|
-
"ref": "@blamejs/core@0.7.
|
|
57
|
+
"ref": "@blamejs/core@0.7.100",
|
|
58
58
|
"dependsOn": []
|
|
59
59
|
}
|
|
60
60
|
]
|