Files
stack/packages/bus/src/store.mjs
T
jason.woltjeandClaude Opus 5.5 38828a2cb3 feat(bus): the bus and the broker core (row 37, S2, rocko)
Rocko's round 2 candidate, approved by Darkwing (#1519 comment 26757).
build.patch 40d7e838, manifest 61519059, 24 files under packages/bus,
schema v3b (179ffe35, lead decision 60). Integration gate in a git
worktree of 942dca9e (S1 in the tree) plus the patch: bus 43/43 and
business 60/60 on Node 24 and 26, every package test and every
scripts/test-*.sh green, test-task 98/98 with the live-provider cases.
Rulings from lead decisions 62 and 63: the human proof is cooperative in
slice 1, and a self-raised cross-role decision routes to the human.
Single-use gated approvals follow in row 43.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 17:15:53 -05:00

176 lines
5.8 KiB
JavaScript

import { DatabaseSync } from 'node:sqlite';
import {
readFileSync,
mkdirSync,
lstatSync,
openSync,
closeSync,
writeFileSync,
fsyncSync,
unlinkSync,
} from 'node:fs';
import { join, isAbsolute } from 'node:path';
import { createHash } from 'node:crypto';
const ddl = readFileSync(new URL('../schema.sql', import.meta.url), 'utf8');
export const SCHEMA_SHA256 = '179ffe356d4ff19a49b5ebad39b6c6bfd7771deb8e1b7c55b5e746f039d69e65';
const timedTables = [
'events',
'role_claims',
'decisions',
'decision_events',
'messages',
'deliveries',
'task_snapshots',
];
const hex = (value) => createHash('sha256').update(value).digest('hex');
if (hex(ddl) !== SCHEMA_SHA256) throw Error('schema-source-mismatch');
export const schemaDigest = (db) =>
hex(
db
.prepare('SELECT type,name,sql FROM sqlite_master WHERE sql IS NOT NULL ORDER BY type,name')
.all()
.map((r) => `${r.type}|${r.name}|${r.sql}`)
.join('\n'),
);
const reference = new DatabaseSync(':memory:');
reference.exec(ddl);
const EXPECTED_DIGEST = schemaDigest(reference);
reference.close();
function exists(path) {
try {
return lstatSync(path);
} catch (e) {
if (e.code === 'ENOENT') return null;
throw e;
}
}
function safe(path, directory = false) {
const s = lstatSync(path);
if (
s.isSymbolicLink() ||
!(directory ? s.isDirectory() : s.isFile()) ||
s.uid !== process.getuid() ||
s.mode & 0o077
)
throw Error('unsafe-path');
return s;
}
// Internal trusted SQL surface. Only the broker owns a Store; never expose SQL over the socket.
export class Store {
#db;
#lock;
#lockStat;
#closed = false;
#inTransaction = false;
constructor(dataRoot) {
if (typeof dataRoot !== 'string' || !isAbsolute(dataRoot)) throw Error('unsafe-path');
const rootStat = lstatSync(dataRoot);
if (!rootStat.isDirectory() || rootStat.isSymbolicLink() || rootStat.uid !== process.getuid())
throw Error('unsafe-path');
this.directory = join(dataRoot, 'bus');
if (!exists(this.directory)) mkdirSync(this.directory, { mode: 0o700 });
safe(this.directory, true);
this.#lock = join(this.directory, 'writer.lock');
let fd;
try {
fd = openSync(this.#lock, 'wx', 0o600);
} catch (e) {
if (e.code === 'EEXIST') throw Error('writer-locked');
throw e;
}
this.#lockStat = lstatSync(this.#lock);
try {
writeFileSync(fd, JSON.stringify({ pid: process.pid, at: new Date().toISOString() }));
fsyncSync(fd);
closeSync(fd);
fd = undefined;
const dirfd = openSync(this.directory, 'r');
try {
fsyncSync(dirfd);
} finally {
closeSync(dirfd);
}
this.path = join(this.directory, 'bus.sqlite');
const fresh = !exists(this.path);
if (fresh) closeSync(openSync(this.path, 'wx', 0o600));
else safe(this.path);
for (const suffix of ['-wal', '-shm']) if (exists(this.path + suffix)) safe(this.path + suffix);
this.#db = new DatabaseSync(this.path, { timeout: 5000, defensive: true });
this.#db.exec('PRAGMA foreign_keys=ON; PRAGMA synchronous=FULL');
if (fresh) {
// journal_mode must be set outside a transaction.
this.#db.exec('PRAGMA journal_mode=WAL');
this.transaction(() => {
this.#db.exec(ddl.replace('PRAGMA journal_mode = WAL;', ''));
this.run('INSERT INTO meta(key,value) VALUES (?,?)', 'schema_version', '3b');
this.run('INSERT INTO meta(key,value) VALUES (?,?)', 'schema_digest', EXPECTED_DIGEST);
});
}
if (
schemaDigest(this.#db) !== EXPECTED_DIGEST ||
this.get("SELECT value FROM meta WHERE key='schema_version'")?.value !== '3b' ||
this.get("SELECT value FROM meta WHERE key='schema_digest'")?.value !== EXPECTED_DIGEST ||
this.get('PRAGMA journal_mode').journal_mode !== 'wal'
)
throw Error('schema-mismatch');
if (this.get('PRAGMA quick_check').quick_check !== 'ok' || this.all('PRAGMA foreign_key_check').length)
throw Error('integrity-failed');
this.#verifyTimes();
} catch (e) {
if (fd !== undefined) closeSync(fd);
this.close();
throw e;
}
}
get(sql, ...args) {
return this.#db.prepare(sql).get(...args);
}
all(sql, ...args) {
return this.#db.prepare(sql).all(...args);
}
run(sql, ...args) {
if (!this.#inTransaction) return this.transaction(() => this.run(sql, ...args));
return this.#db.prepare(sql).run(...args);
}
#verifyTimes() {
for (const table of timedTables) {
const badAt =
"length(at)<>24 OR at NOT GLOB '[0-9][0-9][0-9][0-9]-*' OR strftime('%Y-%m-%dT%H:%M:%fZ',at) IS NOT at";
const badRead =
table === 'task_snapshots'
? " OR (read_at IS NOT NULL AND (length(read_at)<>24 OR read_at NOT GLOB '[0-9][0-9][0-9][0-9]-*' OR strftime('%Y-%m-%dT%H:%M:%fZ',read_at) IS NOT read_at))"
: '';
if (this.get(`SELECT 1 FROM ${table} WHERE ${badAt}${badRead} LIMIT 1`))
throw Error('invalid-timestamp');
}
}
transaction(fn) {
if (typeof fn !== 'function' || fn.constructor.name === 'AsyncFunction')
throw Error('async-transaction-refused');
if (this.#inTransaction) throw Error('nested-transaction');
this.#db.exec('BEGIN IMMEDIATE');
this.#inTransaction = true;
try {
const result = fn();
if (result?.then) throw Error('async-transaction-refused');
this.#verifyTimes();
this.#db.exec('COMMIT');
return result;
} catch (e) {
this.#db.exec('ROLLBACK');
throw e;
} finally {
this.#inTransaction = false;
}
}
close() {
if (this.#closed) return;
this.#closed = true;
this.#db?.close();
const current = exists(this.#lock);
if (current?.ino === this.#lockStat?.ino && current?.dev === this.#lockStat?.dev) unlinkSync(this.#lock);
}
}