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 at boot, periodically re-encrypted every five minutes, and re- encrypted again at shutdown. The DB encryption key is sealed by b.vault at . 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?) #
{
{
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( ($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) #
{
{
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() #
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) #
{
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) #
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) #
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) #
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) #
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) #
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) #
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) #
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 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) #
{
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() #
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 , 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) #
{
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) #
{
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) #
{
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) #
{
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) #
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() #
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) #
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() #
Force the live tmpfs SQLite to be re-encrypted to 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() #
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() #
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) #
{
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) #
{
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() #
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() #
Diagnostic accessor — returns the absolute path of the live SQLite file. In encrypted mode this is a tmpfs path (e.g. /dev/shm/blamejs-); in plain mode it's .
var b = require("blamejs");
await b.db.init({ dataDir: "/tmp/data", atRest: "plain", schema: [] });
typeof b.db.getDbPath();
// → "string"
b.db.getDataResidency() #
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) #
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) #
{
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) #
{
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.