Db

Database core — SQLite (node:sqlite) wrapped in encrypted-at-rest storage, sealed-column field-level crypto, append-only audit-chain integration, declarative schema reconcile, and run-once migrations. Default at-rest posture is encrypted: the live .db lives in tmpfs (/dev/shm), is decrypted from /db.enc at boot, periodically re-encrypted every five minutes, and re- encrypted again at shutdown. The DB encryption key is sealed by b.vault at /db.key.enc. Operators who want a plain on-disk SQLite file pass atRest: "plain" and accept a boot warning — sealed columns still protect PII, but schema and row counts are visible to a forensic disk image.

Beyond the storage shell, the module owns the framework's data contract: audit_log / consent_log / audit_checkpoints and the _blamejs_* reserved tables are provisioned before any operator schema reconciles, append-only triggers refuse UPDATE/DELETE on the chain tables, and boot refuses to continue on chain breakage, checkpoint signature failure, audit-log rollback, or PRAGMA integrity_check corruption. WORM declarations (declareWorm) and dual-control gates (declareRequireDualControl) layer SEC 17a-4(f) / FINRA 4511 / 21 CFR Part 11 §11.10(c) record-preservation invariants on operator tables.

The query surface is db.from(table) (chainable), db.prepare (LRU-cached node:sqlite Statement), db.stream (object-mode Readable for million-row exports with auto-unseal), and db.transaction (BEGIN/COMMIT/ROLLBACK around a callback). Postgres-only declarative migrations (declareView / declareRowPolicy) emit migration-shape objects consumed by b.externalDb.migrate.

b.db.collection(name, opts?) #

stable0.8.58
{
  {
    overflow?:     string,                       // JSON-text column for unknown fields (off when absent)
    jsonColumns?:  string[],                     // auto-stringify on write, auto-parse on read
    sealedFields?: { [plain: string]: string },  // plain column → hash column; registers via b.cryptoField
    columns?:      string[],                     // explicit column whitelist (defaults to PRAGMA introspection)
  }
}

Returns a Mongo-style adapter for the named table. Each method dispatches to b.db.from(name) under the hood; sealed-column semantics, derived-hash translation, and audit emission carry through unchanged.

Pass opts to enable schemaless-document features:

- overflow: "data" — unknown insert/update fields fold into the named JSON-text column. find / findOne parse that column and merge its keys back onto the row. WHERE on an unknown field rewrites to JSON_EXTRACT(, '$.field') ($eq / $ne / $in only — range / LIKE require a real column with an index). - jsonColumns: ["roles", "metadata"] — listed columns are JSON.stringify'd on write and parsed via b.safeJson on read. - sealedFields: { email: "emailHash" } — co-locates a sealed- column / derived-hash declaration with the collection. The plaintext field is registered as sealed; the hash column is registered as a derivedHashes[hashCol] = { from: plain } mapping in b.cryptoField. Subsequent where({ email: "x" }) calls automatically rewrite to where({ emailHash: }) via the existing query-builder rewrite path. - columns: ["_id", "email", ...] — explicit column whitelist. If omitted, the framework introspects via PRAGMA table_info once at first use and caches.

var b = require("@blamejs/core");
await b.db.init({ dataDir: "/tmp/data", schema: [{
  name: "users",
  columns: {
    _id:       "TEXT PRIMARY KEY",
    email:     "TEXT",
    emailHash: "TEXT",
    roles:     "TEXT",
    data:      "TEXT",
  },
}] });
var users = b.db.collection("users", {
  overflow:     "data",
  jsonColumns:  ["roles"],
  sealedFields: { email: "emailHash" },
});
users.insert({ _id: "u1", email: "alice@x.com", roles: ["admin"], dept: "eng", joined: "2026-01-01" });
//   → roles is JSON-stringified; dept + joined fold into data; email seals + emailHash derives
users.findOne({ email: "alice@x.com" });
//   → { _id: "u1", email: "alice@x.com", roles: ["admin"], dept: "eng", joined: "2026-01-01" }
users.find({ dept: "eng" });
//   → JSON_EXTRACT(data, '$.dept') = 'eng'

b.db.fileLifecycle(opts) #

stable0.8.62
{
  {
    dataDir:           string,                   // operator's data dir (used as AAD)
    tmpDir?:           string,                   // tmpfs path; default /dev/shm on Linux
    allowDiskFallback?: boolean,                 // permit os.tmpdir() fallback (warns)
    encryptedDbPath?:  string,                   // default /db.enc
    encryptedDbName?:  string,                   // basename under dataDir (default "db.enc")
    dbKeyPath?:        string,                   // default /db.key.enc
    vault:             ,       // for sealing the DB key
    label?:            string,                   // AAD label (default "default")
    flushIntervalMs?:  number,                   // default 5 minutes
  }
}

Returns an encrypted-DB-file lifecycle handle. Methods:

- decryptToTmp() — decrypt the encrypted DB file to a fresh tmpfs path and return the path. Idempotent: subsequent calls return the existing path. - dbPath — the resolved plaintext-tmpfs path (set after decryptToTmp() runs). - startFlushTimer(db, opts?) — start a periodic flush timer against the operator's SQLite handle. Returns a stop function. - flushNow(db) — force a single re-encrypt flush (WAL checkpoint + write encPath atomically). Used by backup paths. - snapshot(db) — return the encrypted Buffer (same envelope as flushNow writes), without touching the disk encPath. - flushAndCleanup(db, opts) — shutdown sequence: flushNow, close the handle, optionally remove the plaintext file + WAL/SHM sidecars.

var lc = b.db.fileLifecycle({ dataDir: "/var/lib/app", vault: b.vault });
var dbPath = lc.decryptToTmp();
var db = new (require("node:sqlite").DatabaseSync)(dbPath);
var stop = lc.startFlushTimer(db);
// ... operator runs the app ...
process.on("exit", function () { lc.flushAndCleanup(db, { removePlaintext: true }); });

b.db.snapshot() #

stable0.8.58

In-memory encrypted snapshot — same envelope shape that flushToDisk writes, just held in memory. Operators capturing a backup mid-flight (b.backup wrapping a hot DB) get a Buffer they can stream onward to object storage without touching the on-disk encPath. Forces a WAL checkpoint first so the snapshot reflects committed state, not pre-WAL pages.

Under atRest: 'plain' returns the raw plaintext SQLite file as a Buffer (no envelope), since there's no encryption key to apply — operators wanting an encrypted snapshot under plain mode wrap with their own b.crypto.encryptPacked at the call site.

var b = require("@blamejs/core");
var snap = b.db.snapshot();
await b.objectStore.put("backups/" + Date.now() + ".enc", snap);

b.db.init(opts) #

stable0.1.0
{
  dataDir:                 string,            // required — where db.enc + db.key.enc live
  schema:                  Array,             // required — [{ name, columns, indexes, sealedFields, derivedHashes, foreignKeys, primaryKey, subjectField, personalDataCategories }, ...]
  atRest:                  "encrypted"|"plain", // default "encrypted"
  tmpDir:                  string,            // override the encrypted-mode tmpfs path (default /dev/shm or BLAMEJS_TMPDIR)
  allowNonTmpfsTmpDir:     boolean,           // default false — encrypted mode THROWS when tmpDir is not a recognized tmpfs mount (plaintext-on-disk leak); pass true to downgrade to a warning when the mount is verified in-memory out-of-band
  migrationDir:            string,            // optional — path to ./migrations/ (run-once each)
  streamLimit:             number,            // default 1_000_000 — db.stream row ceiling
  columnGate:              "reject"|"warn"|"off", // default "reject" — refuse queries on columns not declared in the table schema
  skipBootIntegrityCheck:  boolean,           // default false — skip PRAGMA integrity_check
  skipIntegrityCheck:      boolean,           // default false — alias
  auditSigning:            { mode, algorithm }, // default { mode: "wrapped" }
  ntpServers:              string[],          // override NTP server list
  ntpTimeoutMs:            number,            // override NTP timeout
  dataResidency:           object,            // operator's region declaration
}

Boot the database. Provisions the framework-baked tables (audit_log / consent_log / audit_checkpoints / _blamejs_*), reconciles the operator schema, installs append- only triggers on chain tables, runs any pending file-based migrations, verifies the audit + consent chains end-to-end, verifies every audit checkpoint signature, runs PRAGMA integrity_check, performs a rollback-detection check against audit.tip, and runs a best-effort SNTP boot drift check. Refuses to boot on any chain breakage, signature mismatch, or rollback — compliance posture demands fail-closed at the earliest signal.

var b = require("blamejs");
await b.db.init({
  dataDir: "/var/lib/myapp",
  atRest:  "encrypted",
  schema: [
    {
      name: "orders",
      columns: {
        _id:        "TEXT PRIMARY KEY",
        customerId: "TEXT NOT NULL",
        totalCents: "INTEGER NOT NULL",
        note:       "TEXT",
        createdAt:  "INTEGER NOT NULL",
      },
      indexes:       ["customerId"],
      sealedFields:  ["note"],
      derivedHashes: { customerIdHash: { from: "customerId" } },
      subjectField:  "customerId",
    },
  ],
});

b.db.from(tableName) #

stable0.1.0

Open a chainable Query against a registered table. Sealed columns auto-encrypt on insert/update and auto-decrypt on read; derived- hash columns auto-populate from their source field on insert. Identifier safety, parameter binding, row-policy gates, and audit-emission are wired into the chain so operator code never concatenates SQL by hand.

var b = require("blamejs");
await b.db.init({ dataDir: "/tmp/data", schema: [
  { name: "orders",
    columns: { _id: "TEXT PRIMARY KEY", customerId: "TEXT NOT NULL", totalCents: "INTEGER NOT NULL" },
    sealedFields: ["customerId"] },
] });

b.db.from("orders").insert({
  _id: b.uuid.v7(), customerId: "cust_123", totalCents: 4999,
});

var rows = b.db.from("orders").where({ customerId: "cust_123" }).all();
rows.length;
// → 1

b.db.getDeclaredColumns(tableName) #

stable0.14.7

Returns the declared column names for a table as an array, or null when the table has no registered schema metadata (a cross- or attached-schema table — the column-membership gate is a no-op for those). The declared set includes _id and any derived-hash columns, so sealed-field queries (which rewrite to the hash column) and _id lookups pass the gate. Backs the db.init({ columnGate }) gate that refuses queries ordering / selecting / filtering on an undeclared column before the identifier interpolates into SQL.

b.db.getDeclaredColumns("orders");
// → ["_id", "customerId", "total", "createdAt"]

b.db.prepare(sql) #

stable0.1.0

Raw-escape-hatch wrapper around node:sqlite's Statement preparation, with an LRU cache keyed by SQL string (cap 256 distinct shapes). Reuse of the same SQL returns the cached Statement so a hot path doesn't churn file descriptors. Use b.db.from(table) for the typical chainable surface; prepare is for the rare cases where the chainable Query doesn't cover the shape.

var b = require("blamejs");
await b.db.init({ dataDir: "/tmp/data", schema: [
  { name: "orders",
    columns: { _id: "TEXT PRIMARY KEY", totalCents: "INTEGER NOT NULL" } },
] });

var stmt = b.db.prepare("SELECT SUM(totalCents) AS total FROM orders");
var row = stmt.get();
typeof row.total;
// → "object"

b.db.stream(sql) #

stable0.4.0

Object-mode Readable that yields rows as node:sqlite's iterate() produces them. Unlike .all(), the engine never materializes the full result set, so audit exports, backup table dumps, and million-row reports finish without OOM pressure. Variadic: positional parameter bindings come after sql; an optional final plain-object argument carries opts.table (enables sealed-column auto-unseal) and opts.streamLimit (per-call row ceiling override). Default ceiling is the module-level streamLimit (1_000_000); the stream destroys with a db/stream-limit-exceeded error past the cap rather than accumulating unboundedly.

var b = require("blamejs");
await b.db.init({ dataDir: "/tmp/data", schema: [
  { name: "events",
    columns: { _id: "TEXT PRIMARY KEY", payload: "TEXT" },
    sealedFields: ["payload"] },
] });

var count = 0;
var s = b.db.stream("SELECT * FROM events", { table: "events" });
await new Promise(function (resolve, reject) {
  s.on("data", function (_row) { count += 1; });
  s.on("end",   resolve);
  s.on("error", reject);
});
count >= 0;
// → true

b.db.transaction(fn) #

stable0.1.0

Run fn(db) inside a BEGIN ... COMMIT block; any throw inside fn triggers ROLLBACK and re-propagates the error. Returns the value fn returned. Transactions compose with the chainable Query surface and with audit-chain emissions inside the body — the audit row's chain hash is computed from the value at COMMIT time, so a rolled-back transaction never leaves a phantom row in audit_log.

var b = require("blamejs");
await b.db.init({ dataDir: "/tmp/data", schema: [
  { name: "ledger",
    columns: { _id: "TEXT PRIMARY KEY", balanceCents: "INTEGER NOT NULL" } },
] });

b.db.from("ledger").insert({ _id: "acct_1", balanceCents: 100 });
b.db.from("ledger").insert({ _id: "acct_2", balanceCents: 0 });

b.db.transaction(function (db) {
  db.from("ledger").where({ _id: "acct_1" }).update({ balanceCents: 50 });
  db.from("ledger").where({ _id: "acct_2" }).update({ balanceCents: 50 });
});

b.db.from("ledger").where({ _id: "acct_2" }).first().balanceCents;
// → 50

b.db.hashFor(table, field, value) #

stable0.1.0

Look up the deterministic SHA3 hash a sealed-source field maps to via the table's registered derivedHashes. Used to query a sealed column without unsealing every row — operator code passes the cleartext, the framework hashes it through the same namespaced derivation, and a WHERE = ? lookup returns the matching rows. Returns null when the field has no derived-hash declaration on the table.

var b = require("blamejs");
await b.db.init({ dataDir: "/tmp/data", schema: [
  { name: "users",
    columns: { _id: "TEXT PRIMARY KEY", email: "TEXT", emailHash: "TEXT" },
    sealedFields:  ["email"],
    derivedHashes: { emailHash: { from: "email" } } },
] });

b.db.from("users").insert({ _id: "u1", email: "alice@example.com" });

var h = b.db.hashFor("users", "email", "alice@example.com");
typeof h;
// → "string"

b.db.hashCandidatesFor(table, field, value) #

stable0.15.1

Dual-read sibling of hashFor. Returns { field, values } where values holds the active derived-hash digest AND — across the v0.15.0 keyed-MAC default flip — the legacy salted-sha3 digest a row written before the flip carries. A WHERE IN (...) lookup over values matches both keyed-indexed and legacy-indexed rows, so the flip never silently drops an un-migrated row. Returns null when the field has no derived-hash declaration on the table.

var c = b.db.hashCandidatesFor("users", "email", "alice@example.com");
b.db.from("users").whereIn(c.field, c.values).all();
// → rows matching either the keyed-MAC or the legacy digest

b.db.exportCsv(opts) #

stable0.7.0
{
  table:           string,      // required — registered table name
  columns:         string[],    // optional column projection (default: all)
  where:           object,      // optional Query.where(...) filter
  bom:             boolean,     // default false; emit U+FEFF prefix
  format:          "rfc4180",   // default "rfc4180" (only supported value)
  timestampFields: string[],    // ms-int columns to cast to ISO-8601
  signWith:        object,      // signer with sign / getPublicKey / getAlgorithm / getPublicKeyFingerprint
}

RFC 4180 strict CSV export of a single registered table, with sealed-column auto-unseal (rides the chainable Query), optional WHERE filter, optional column projection, optional UTF-8 BOM, ISO-8601 cast for declared timestamp fields, SHA3-512 manifest of the byte stream, and an optional detached signature via any b.auditSign-shaped signer. Refuses unknown table names, refuses arbitrary column strings (every column must belong to the table), and emits a db.export.csv audit row.

var b = require("blamejs");
await b.db.init({ dataDir: "/tmp/data", schema: [
  { name: "orders",
    columns: { _id: "TEXT PRIMARY KEY", totalCents: "INTEGER NOT NULL", createdAt: "INTEGER NOT NULL" } },
] });
b.db.from("orders").insert({ _id: "o1", totalCents: 4999, createdAt: Date.now() });

var out = b.db.exportCsv({
  table:           "orders",
  columns:         ["_id", "totalCents", "createdAt"],
  bom:             true,
  timestampFields: ["createdAt"],
});
typeof out.sha3_512;
// → "string"
out.rowCount >= 1;
// → true

b.db.close() #

stable0.1.0

Idempotent shutdown. Stops the periodic encrypt timer, fires a best-effort final audit checkpoint when the local node is the cluster leader, re-encrypts the live tmpfs database back to /db.enc, closes the SQLite handle (releasing the file lock on Windows), then unlinks the plaintext sidecar files in tmpnodeFs. Safe to call multiple times — no-ops after the first successful close.

var b = require("blamejs");
await b.db.init({ dataDir: "/tmp/data", schema: [] });
b.db.close();
b.db.close();
// → undefined

b.db.declareWorm(args) #

stable0.8.021-cfr-11
{
  tables:  string[],  // required — non-empty array of operator table names
  posture: string,    // optional — posture label recorded on each row
}

Install row-level WORM (write-once-read-many) triggers on operator-named business-record tables. Per SEC Rule 17a-4(f), FINRA Rule 4511, and 21 CFR Part 11 §11.10(c). UPDATE and DELETE are refused at the SQLite-trigger level, independent of the application's discipline. Each declared table is registered in _blamejs_worm_tables; under sec-17a-4 / finra-4511 / fda-21cfr11 postures the boot-time assertion refuses to start if the registry is empty. Cluster mode (external-db) refuses the call — operators install WORM via b.externalDb.migrate instead.

var b = require("blamejs");
await b.db.init({ dataDir: "/tmp/data", schema: [
  { name: "trade_blotter",
    columns: { _id: "TEXT PRIMARY KEY", symbol: "TEXT NOT NULL", qty: "INTEGER NOT NULL" } },
] });

var declared = b.db.declareWorm({
  tables:  ["trade_blotter"],
  posture: "sec-17a-4",
});
declared.tables;
// → ["trade_blotter"]

b.db.declareRequireDualControl(args) #

stable0.8.0
{
  tables:  string[],  // required — non-empty array of table names
  m:       number,    // default 2 — minimum approvals
  n:       number,    // default max(2, m) — total approver pool
  posture: string,    // optional — posture label recorded with the gate
}

Gate destructive operations (b.db.eraseHard, retention sweeps, audit purges) on operator-named tables behind an m-of-n dual- control grant. Each declared table is registered in _blamejs_dual_control_gates with its quorum tuple (m, n); the gate consult on eraseHard refuses execution unless the caller passes opts.dualControlGrant returned by b.dualControl.consume().

var b = require("blamejs");
await b.db.init({ dataDir: "/tmp/data", schema: [
  { name: "patient_records",
    columns: { _id: "TEXT PRIMARY KEY", chartJson: "TEXT" } },
] });

var gate = b.db.declareRequireDualControl({
  tables:  ["patient_records"],
  m:       2,
  n:       3,
  posture: "hipaa",
});
gate.m;
// → 2

b.db.eraseHard(tableName, rowId, opts) #

stable0.8.0gdprhipaa
{
  reason:            string,   // required — non-empty rationale recorded in audit
  subjectId:         string,   // optional — consults legal-hold registry
  dualControlGrant:  object,   // required when the table is gated; from b.dualControl.consume()
}

Crypto-erase one row plus a REINDEX on the table so freed B-tree pages can't reconstruct the deleted row's index entries. Closes the F-RTBF B-tree-residual class on a per-row basis. Consults the legal-hold registry (refuses on subjectId held) and the dual- control gate registry (refuses unless opts.dualControlGrant is a consumed grant); emits a db.erase_hard audit row on success or a db.erase_hard.denied audit row on either gate refusal.

var b = require("blamejs");
await b.db.init({ dataDir: "/tmp/data", schema: [
  { name: "stale_pii",
    columns: { _id: "TEXT PRIMARY KEY", ssn: "TEXT" },
    sealedFields: ["ssn"] },
] });
b.db.from("stale_pii").insert({ _id: "row1", ssn: "123-45-6789" });

var result = b.db.eraseHard("stale_pii", "row1", {
  reason: "subject erasure under GDPR Art 17",
});
result.rowsDeleted;
// → 1

b.db.vacuumAfterErase(opts) #

stable0.8.0gdprhipaa
{
  mode:  "incremental"|"full",  // default "incremental"
  pages: number,                // incremental only; default 1000
}

Run after a large-scale erase (b.subject.erase batch, b.retention sweep) so SQLite's freed pages don't linger with sealed-column ciphertext that a forensic disk image could recover. incremental mode runs PRAGMA incremental_vacuum(N) (default 1000 pages) — fast, doesn't rewrite the whole file. full mode runs VACUUM — rewrites every page; the database is locked for the duration.

var b = require("blamejs");
await b.db.init({ dataDir: "/tmp/data", schema: [] });
b.db.vacuumAfterErase({ mode: "incremental", pages: 500 });
// → undefined

b.db.applyPosture(posture) #

stable0.8.0

Record the active compliance posture for the database subsystem. Called by b.compliance.set(p) during posture cascade so the downstream cryptoField.eraseRow path can consult getActivePosture() and auto-vacuum under postures whose defaults set requireVacuumAfterErase: true. Returns null for empty input; otherwise { posture, dbInitialized }.

var b = require("blamejs");
var result = b.db.applyPosture("hipaa");
result.posture;
// → "hipaa"

b.db.getActivePosture() #

stable0.8.0

Read the posture last installed via applyPosture. Used by downstream subsystems (cryptoField.eraseRow, retention sweeps) to branch on posture-driven defaults. Returns null before any posture has been set.

var b = require("blamejs");
b.db.applyPosture("pci-dss");
b.db.getActivePosture();
// → "pci-dss"

b.db.runSql(sql) #

stable0.1.0

Execute a raw SQL string with no result-set return — DDL (CREATE TABLE / DROP TABLE / ALTER / etc.), DML where the caller doesn't need rows back, and BEGIN / COMMIT / ROLLBACK outside of transaction(). Slow-query observability buckets fire on every call. DDL statements emit a db.ddl.executed audit row with the leading keyword extracted so a forensic review can reconstruct schema evolution from the audit chain alone.

var b = require("blamejs");
await b.db.init({ dataDir: "/tmp/data", schema: [] });
b.db.runSql("CREATE TABLE IF NOT EXISTS scratch (id INTEGER PRIMARY KEY)");
// → undefined

b.db.flushToDisk() #

stable0.4.0

Force the live tmpfs SQLite to be re-encrypted to /db.enc immediately. The framework already does this every five minutes and at clean shutdown; operators running a backup workflow call flushToDisk() first so the snapshot source reflects the most recent committed state. No-op in atRest: "plain" mode (no db.enc exists).

var b = require("blamejs");
await b.db.init({ dataDir: "/tmp/data", atRest: "encrypted", schema: [] });
b.db.flushToDisk();
// → undefined

b.db.getStreamLimit() #

stable0.7.67

Read the module-level streamLimit ceiling (default 1_000_000). Per-call opts.streamLimit on db.stream overrides this; db.init({ streamLimit }) raises or lowers it for the process.

var b = require("blamejs");
await b.db.init({ dataDir: "/tmp/data", schema: [] });
b.db.getStreamLimit() > 0;
// → true

b.db.integrityCheck() #

stable0.8.0

Run PRAGMA integrity_check on the live database. Returns the string "ok" on a clean check or an array of corruption descriptions otherwise. Operators wire this into a /healthz handler or a periodic monitor.

var b = require("blamejs");
await b.db.init({ dataDir: "/tmp/data", schema: [] });
b.db.integrityCheck();
// → "ok"

b.db.integrityMonitor(opts) #

stable0.8.0
{
  intervalMs:   number,        // default C.TIME.hours(24)
  audit:        boolean,       // default true; emit audit rows on every check
  onCorruption: Function,      // (issues) => void; fires on corruption
}

Periodic PRAGMA integrity_check runner. Returns a handle with .stop() for graceful shutdown. Emits system.db.integrity_ok / system.db.integrity_corrupt audit rows and matching observability counters on every check. Operators pass onCorruption to receive the issues array on detection (alerts, page outs, kill-switches).

var b = require("blamejs");
await b.db.init({ dataDir: "/tmp/data", schema: [] });
var mon = b.db.integrityMonitor({
  intervalMs:   60000,
  onCorruption: function (_issues) { },
});
mon.stop();

b.db.purgeAuditChain(args) #

stable0.8.0
{
  lastPurgedCounter: number,   // required — non-negative; rows at or below this counter are deleted
}

Narrow-purpose DELETE against audit_log + audit_checkpoints for use by audit-tools.purge. Drops the BEFORE-DELETE append- only triggers inside a transaction, executes the deletion against rows with monotonicCounter <= lastPurgedCounter, then re- installs the triggers so the append-only invariant resumes. Cluster mode delegates to cluster-storage (no triggers in external-db). The caller is responsible for verifying purge legitimacy via audit-tools.verifyBundle before invoking.

var b = require("blamejs");
await b.db.init({ dataDir: "/tmp/data", schema: [] });
var result = await b.db.purgeAuditChain({ lastPurgedCounter: 0 });
typeof result.rowsDeleted;
// → "number"

b.db.getMode() #

stable0.1.0

Diagnostic accessor — returns the active at-rest posture ("encrypted" or "plain") chosen at init time.

var b = require("blamejs");
await b.db.init({ dataDir: "/tmp/data", atRest: "plain", schema: [] });
b.db.getMode();
// → "plain"

b.db.getDbPath() #

stable0.1.0

Diagnostic accessor — returns the absolute path of the live SQLite file. In encrypted mode this is a tmpfs path (e.g. /dev/shm/blamejs-.db); in plain mode it's /blamejs.db.

var b = require("blamejs");
await b.db.init({ dataDir: "/tmp/data", atRest: "plain", schema: [] });
typeof b.db.getDbPath();
// → "string"

b.db.getDataResidency() #

stable0.7.0

Read the operator's declared data-residency configuration (passed via db.init({ dataResidency })). Storage / mail / log destinations consult this to refuse cross-region writes.

var b = require("blamejs");
await b.db.init({
  dataDir:       "/tmp/data",
  dataResidency: { region: "eu-west-1" },
  schema:        [],
});
b.db.getDataResidency().region;
// → "eu-west-1"

b.db.getTableMetadata(nameOrOpts) #

stable0.7.0

Reflective metadata for one or every registered table — primary- key columns, foreign keys, sealed-field list, derived-hash declarations, subject mapping, personal-data categories. Returns a deep-copied snapshot; mutations don't affect framework state. Two-arg form supports format dispatch: getTableMetadata({ table, format: "json-schema-2020-12" }) emits a JSON Schema 2020-12 document with sealed columns annotated x-blamejs-sealed: true and derived-hash columns annotated x-blamejs-derived-from: "".

var b = require("blamejs");
await b.db.init({ dataDir: "/tmp/data", schema: [
  { name: "users",
    columns: { _id: "TEXT PRIMARY KEY", email: "TEXT" },
    sealedFields: ["email"] },
] });

var meta = b.db.getTableMetadata("users");
meta.sealedFields;
// → ["email"]

var schema = b.db.getTableMetadata({
  table:  "users",
  format: "json-schema-2020-12",
});
schema.properties.email["x-blamejs-sealed"];
// → true

b.db.declareView(opts) #

stable0.8.0
{
  name:    string,    // required — view identifier
  select:  string,    // required — view body
  grants:  object,    // optional — { role: ["SELECT", ...] }
  schema:  string,    // optional — schema-qualified namespace
}

Declarative CREATE VIEW + GRANT migration spec for a Postgres-backed b.externalDb deployment. Returns a migration- shape object consumed by b.externalDb.migrate. Postgres-only; fail-fast at apply time on other dialects.

var b = require("blamejs");
var spec = b.db.declareView({
  name:   "active_users",
  select: "SELECT id, email FROM users WHERE deleted_at IS NULL",
  grants: { app_reader: ["SELECT"] },
});
spec.kind;
// → "view"

b.db.declareRowPolicy(opts) #

stable0.8.0
{
  table:    string,    // required — target table
  name:     string,    // required — policy identifier
  command:  string,    // optional — "SELECT" | "INSERT" | "UPDATE" | "DELETE" | "ALL"
  using:    string,    // optional — USING expression
  withCheck:string,    // optional — WITH CHECK expression
  roles:    string[],  // optional — TO role list
}

Declarative Postgres ROW LEVEL SECURITY migration spec. Pairs with b.externalDb.transaction({ sessionGucs }) for the per- request SET LOCAL plumbing that scopes the policy. Returns a migration-shape object consumed by b.externalDb.migrate. Postgres-only; fail-fast on other dialects.

var b = require("blamejs");
var spec = b.db.declareRowPolicy({
  table:   "orders",
  name:    "tenant_isolation",
  command: "ALL",
  using:   "tenant_id = current_setting('app.tenant_id')::uuid",
  roles:   ["app_user"],
});
spec.kind;
// → "row-policy"

Last updated 2026-08-08T16:39:49.652Z by seeder.