@sema-agent/core
Version:
Stateless, task-oriented AI agent core
2,663 lines • 113 kB
JavaScript
import { closeSync, constants as fsConstants, copyFileSync, existsSync, fstatSync, fsyncSync, ftruncateSync, lstatSync, mkdirSync, openSync, readFileSync, readSync, readdirSync, readlinkSync, renameSync, rmSync, rmdirSync, statSync, unlinkSync, utimesSync, writeFileSync, writeSync } from "node:fs";
const { O_WRONLY, O_RDONLY, O_CREAT, O_NOFOLLOW, O_NONBLOCK, O_EXCL } = fsConstants;
import { homedir } from "node:os";
import { createHash, randomUUID } from "node:crypto";
import { basename, dirname, isAbsolute, join, resolve, sep } from "node:path";
import { MEMORY_ORIGIN_CAUSES } from "./types.js";
import { physicalPathOf, physicalPathOfExisting, priorCallerCasedPathOf } from "../physical-path.js";
import { thrownLabel, thrownText } from "../thrown-value.js";
const SCOPES_FILE = "scopes.json";
export const CURSORS_FILE = "cursors.json";
export const CONTROL_PLANE_DIR = ".engine";
export class ControlPlaneCorruptError extends Error {
facet;
constructor(message, opts) {
super(message, opts?.cause !== undefined ? { cause: opts.cause } : undefined);
this.name = "ControlPlaneCorruptError";
if (opts?.facet !== undefined)
this.facet = opts.facet;
}
}
function slugifyPath(absPath) {
return absPath.normalize("NFC").replace(/[^A-Za-z0-9]/g, "-");
}
export function deriveRepoKey(repoRoot) {
return slugifyPath(canonicalize(resolve(repoRoot)));
}
function deriveDirKey(dir) {
return slugifyPath(resolve(dir));
}
function subtreeHasFiles(dir) {
let entries;
try {
entries = readdirSync(dir, { withFileTypes: true });
}
catch {
return true;
}
for (const e of entries) {
if (!e.isDirectory())
return true;
if (subtreeHasFiles(join(dir, e.name)))
return true;
}
return false;
}
function pruneFilelessDirTree(dir) {
let entries;
try {
entries = readdirSync(dir, { withFileTypes: true });
}
catch {
return !existsSync(dir);
}
for (const e of entries) {
if (!e.isDirectory())
return false;
if (!pruneFilelessDirTree(join(dir, e.name)))
return false;
}
try {
rmdirSync(dir);
return true;
}
catch {
return false;
}
}
function tryAdoptByRename(fromDir, toDir) {
try {
renameSync(fromDir, toDir);
return { ok: true };
}
catch (err) {
return { ok: false, code: err.code, error: err };
}
}
function migrateKeyedPartition(m) {
const disclose = (code, message) => {
try {
const e = new Error(message);
e.code = code;
m.onIncident?.(e);
}
catch {
}
};
const splitText = `memory partition split detected for ${m.subject}: BOTH ${m.fromDir} and ${m.toDir} hold content. This session uses ${m.toDir}; the other dir is left untouched (never merged — CAS baselines would collide). If it holds the newer memory, move its rows by hand and remove it.`;
try {
if (m.fromDir === m.toDir || canonicalize(m.fromDir) === canonicalize(m.toDir))
return;
if (!existsSync(m.fromDir))
return;
if (existsSync(m.toDir) && subtreeHasFiles(m.toDir)) {
disclose("memory.partition_split", splitText);
return;
}
ensureDirExists(dirname(m.toDir));
let outcome = tryAdoptByRename(m.fromDir, m.toDir);
if (!outcome.ok && (outcome.code === "EEXIST" || outcome.code === "ENOTEMPTY") && !subtreeHasFiles(m.toDir) && pruneFilelessDirTree(m.toDir)) {
outcome = tryAdoptByRename(m.fromDir, m.toDir);
}
if (outcome.ok)
return;
const { code, error } = outcome;
if ((code === "ENOENT" || code === "EEXIST" || code === "ENOTEMPTY") && existsSync(m.toDir)) {
if (!existsSync(m.fromDir))
return;
disclose("memory.partition_split", splitText);
return;
}
disclose("memory.partition_adopt_failed", `could not adopt the memory partition ${m.fromDir} → ${m.toDir} (${code ?? thrownLabel(error)}): this session proceeds on ${m.toDir}; ` +
`${m.fromDir} still holds the prior memory — move it by hand.`);
}
catch {
}
}
export function adoptLegacyRepoDirs(configRoot, repoRoot, onIncident) {
const toDir = join(configRoot, deriveRepoKey(repoRoot));
for (const key of earlierRepoKeysOf(repoRoot)) {
migrateKeyedPartition({ fromDir: join(configRoot, key), toDir, subject: repoRoot, onIncident });
}
}
export function earlierRepoKeysOf(repoRoot) {
const today = deriveRepoKey(repoRoot);
const keys = [deriveDirKey(repoRoot)];
try {
keys.push(slugifyPath(priorCallerCasedPathOf(resolve(repoRoot))));
}
catch {
}
return [...new Set(keys)].filter((k) => k !== today);
}
export function adoptCanonicalKeyedControlDir(configRoot, dir, onIncident) {
migrateKeyedPartition({
fromDir: join(configRoot, deriveRepoKey(dir), CONTROL_PLANE_DIR),
toDir: join(configRoot, deriveDirKey(dir), CONTROL_PLANE_DIR),
subject: dir,
onIncident,
});
}
export function deriveRepoMemoryDir(configRoot, repoRoot) {
return join(configRoot, deriveRepoKey(repoRoot), "memory");
}
export function deriveProjectMemoryDir(configRoot, projectId) {
return join(configRoot, `proj-${projectId.toLowerCase()}`, "memory");
}
export function deriveProjectControlDir(configRoot, projectId) {
return join(configRoot, `proj-${projectId.toLowerCase()}`, CONTROL_PLANE_DIR);
}
export const PROJECT_ID_HINTS_FILE = "project-id-hints.json";
export function recordProjectIdHint(configRoot, repoRoot, projectId) {
try {
const path = join(configRoot, PROJECT_ID_HINTS_FILE);
let hints = {};
try {
const parsed = JSON.parse(readFileSync(path, "utf8"));
if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed))
hints = parsed;
}
catch {
}
const key = resolve(repoRoot);
if (hints[key] === projectId.toLowerCase())
return;
hints[key] = projectId.toLowerCase();
mkdirSync(configRoot, { recursive: true });
atomicWriteFileSync(path, `${JSON.stringify(hints, null, 2)}\n`);
}
catch {
}
}
export function lookupProjectIdHint(configRoot, repoRoot) {
try {
const parsed = JSON.parse(readFileSync(join(configRoot, PROJECT_ID_HINTS_FILE), "utf8"));
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed))
return undefined;
const v = parsed[resolve(repoRoot)];
return typeof v === "string" ? v : undefined;
}
catch {
return undefined;
}
}
export function deriveControlPlaneDir(configRoot, key) {
return join(configRoot, deriveDirKey(key), CONTROL_PLANE_DIR);
}
export function deriveRepoControlPlaneDir(configRoot, repoRoot) {
return join(configRoot, deriveRepoKey(repoRoot), CONTROL_PLANE_DIR);
}
export function effectiveControlRootOf(dir, opts = {}) {
const pinned = opts.pinnedBy?.controlPlaneRoot;
if (typeof pinned === "string" && pinned !== "")
return pinned;
if (opts.controlDir !== undefined)
return opts.controlDir;
const configRoot = resolveMemoryEngineRoot();
adoptCanonicalKeyedControlDir(configRoot, dir, opts.onIncident);
return deriveControlPlaneDir(configRoot, dir);
}
export function resolveMemoryEngineRoot(explicit) {
const raw = explicit ?? process.env.AGENT_DATA_DIR ?? join(homedir(), ".ai-agent");
return isAbsolute(raw) ? raw : resolve(raw);
}
export function scopeDirName(scope) {
if (/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(scope))
return scope;
const cleaned = scope.replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^[.-]+/, "") || "scope";
const hash = createHash("sha256").update(scope, "utf8").digest("hex").slice(0, 6);
return `${cleaned}-${hash}`;
}
const caseFoldByDirIdentity = new Map();
let caseFoldProbeSeq = 0;
function lstatOrAbsent(p) {
try {
return lstatSync(p);
}
catch (err) {
if (err.code === "ENOENT")
return undefined;
throw err;
}
}
function probeDirCaseFolds(dir) {
const name = `${CASEFOLD_PROBE_PREFIX}${process.pid}-${caseFoldProbeSeq++}`;
const writeLeg = (() => {
let fd;
try {
fd = openSync(join(dir, name), O_WRONLY | O_CREAT | O_EXCL, 0o600);
}
catch {
return undefined;
}
let identity;
try {
const st = fstatSync(fd);
identity = { dev: st.dev, ino: st.ino };
}
catch {
}
finally {
closeSync(fd);
}
try {
let swapped;
let orig;
try {
swapped = lstatOrAbsent(join(dir, name.toUpperCase()));
orig = swapped === undefined ? lstatOrAbsent(join(dir, name)) : undefined;
}
catch {
return undefined;
}
if (swapped !== undefined) {
return identity === undefined || (swapped.dev === identity.dev && swapped.ino === identity.ino) ? true : undefined;
}
if (orig === undefined || identity === undefined)
return undefined;
return orig.dev === identity.dev && orig.ino === identity.ino ? false : undefined;
}
finally {
try {
rmSync(join(dir, name), { force: true });
}
catch {
}
}
})();
if (writeLeg !== undefined)
return writeLeg;
try {
for (const entry of readdirSync(dir)) {
if (!/[A-Za-z]/.test(entry))
continue;
const swapped = entry.toLowerCase() !== entry ? entry.toLowerCase() : entry.toUpperCase();
try {
const orig = lstatOrAbsent(join(dir, entry));
if (orig === undefined)
continue;
const other = lstatOrAbsent(join(dir, swapped));
const again = lstatOrAbsent(join(dir, entry));
if (again === undefined || again.dev !== orig.dev || again.ino !== orig.ino)
continue;
if (other === undefined)
return false;
return other.dev === orig.dev && other.ino === orig.ino;
}
catch {
continue;
}
}
}
catch {
}
return undefined;
}
export function dirCaseFolds(dir) {
let key;
try {
ensureDirExists(dir);
const s = statSync(dir);
key = `${s.dev}:${s.ino}`;
}
catch {
}
if (key !== undefined) {
const cached = caseFoldByDirIdentity.get(key);
if (cached !== undefined)
return cached;
}
const folds = probeDirCaseFolds(dir);
if (folds === undefined)
return undefined;
try {
const after = statSync(dir);
if (key === undefined || key !== `${after.dev}:${after.ino}`)
return undefined;
}
catch {
return undefined;
}
caseFoldByDirIdentity.set(key, folds);
return folds;
}
export const SCOPE_DIR_LABEL_RE = /^[A-Za-z0-9_][A-Za-z0-9._-]*$/;
export const CASEFOLD_PROBE_PREFIX = ".casefold-probe-";
export const SCOPE_REGISTRY_SHAPES = Object.freeze(["absent", "v1", "v2", "migrating"]);
export const REGISTRY_SHAPE_DERIVES_PATHS = {
absent: true,
v1: false,
v2: true,
migrating: false,
};
function ownLabel(scopes, scope) {
return scopes !== undefined && Object.prototype.hasOwnProperty.call(scopes, scope) ? scopes[scope] : undefined;
}
function readScopesRecord(controlDir) {
const path = join(controlDir, SCOPES_FILE);
const journalPath = `${path}.journal`;
let raw;
let sourcePath = journalPath;
try {
raw = readFileSync(journalPath, "utf8");
}
catch (err) {
if (err.code !== "ENOENT") {
throw new ControlPlaneCorruptError(`scope registry journal unreadable (${err.code ?? "io error"}) — a committed next state may exist that cannot be proven: ${journalPath}`, { cause: err });
}
raw = undefined;
}
if (raw === undefined) {
sourcePath = path;
try {
raw = readFileSync(path, "utf8");
}
catch (err) {
if (err.code === "ENOENT")
return { shape: "absent", rec: { v: 2, scopes: {} } };
throw new ControlPlaneCorruptError(`scope registry unreadable (${err.code ?? "io error"}): ${path}`, { cause: err });
}
}
let parsed;
try {
parsed = JSON.parse(raw);
}
catch {
throw new ControlPlaneCorruptError(`scope registry is unparseable: ${sourcePath}`);
}
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
throw new ControlPlaneCorruptError(`scope registry has the wrong shape: ${path}`);
}
const rec = parsed;
const scopes = coerceScopeLabels(rec.scopes, path);
if (rec.v === undefined) {
if (rec.rootScope !== undefined && typeof rec.rootScope !== "string") {
throw new ControlPlaneCorruptError(`scope registry rootScope is not a string: ${path}`);
}
return { shape: "v1", ...(rec.rootScope !== undefined ? { rootScope: rec.rootScope } : {}), scopes };
}
if (rec.v !== 2) {
throw new ControlPlaneCorruptError(`scope registry version ${JSON.stringify(rec.v)} is not one this engine reads (expected 2): ${path}`);
}
for (const [k, label] of Object.entries(scopes)) {
if (!SCOPE_DIR_LABEL_RE.test(label)) {
throw new ControlPlaneCorruptError(`scope registry maps ${JSON.stringify(k)} to the label ${JSON.stringify(label)}, which is not a single non-hidden path segment — refusing (fail-closed): ${path}`);
}
}
if (rec.rootOwner !== undefined && typeof rec.rootOwner !== "string") {
throw new ControlPlaneCorruptError(`scope registry rootOwner is not a string: ${path}`);
}
const v2 = { v: 2, scopes, ...(rec.rootOwner !== undefined ? { rootOwner: rec.rootOwner } : {}) };
if (rec.migrating === undefined)
return { shape: "v2", rec: v2 };
const m = rec.migrating;
if (!m || typeof m !== "object" || Array.isArray(m) || typeof m.root !== "string" || typeof m.owner !== "string" || typeof m.label !== "string" || !SCOPE_DIR_LABEL_RE.test(m.label)) {
throw new ControlPlaneCorruptError(`scope registry carries a malformed migrating marker: ${path}`);
}
return { shape: "migrating", rec: { ...v2, migrating: { root: m.root, owner: m.owner, label: m.label } } };
}
function coerceScopeLabels(raw, path) {
if (raw === undefined)
return {};
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
throw new ControlPlaneCorruptError(`scope registry scopes map has the wrong shape: ${path}`);
}
const out = Object.create(null);
for (const [k, v] of Object.entries(raw)) {
if (typeof v !== "string")
throw new ControlPlaneCorruptError(`scope registry entry ${JSON.stringify(k)} is not a string: ${path}`);
out[k] = v;
}
return out;
}
function requireV2Registry(read, controlDir) {
if (REGISTRY_SHAPE_DERIVES_PATHS[read.shape])
return read.rec;
throw new ControlPlaneCorruptError(read.shape === "v1"
? `scope registry is in the v1 (pre-v2, root-owning) form at ${join(controlDir, SCOPES_FILE)} — scope homes cannot be derived until the layout adoption has run (a mount or a locked backend face runs it); refusing (fail-closed)`
: `scope registry carries an unfinished layout move (migrating marker) at ${join(controlDir, SCOPES_FILE)} — scope homes cannot be derived until the move completes (the next mount or locked backend face finishes it); refusing (fail-closed)`);
}
function commitScopesRecord(controlDir, lock, token, next) {
const file = join(controlDir, SCOPES_FILE);
const journal = `${file}.journal`;
const data = `${JSON.stringify(next, null, 2)}\n`;
const fence = () => assertSidecarLockOwnership(lock, token, "scope registry", "strict");
fence();
let current;
try {
current = readFileSync(file, "utf8");
}
catch {
current = undefined;
}
if (current === data)
return;
atomicWriteFileSync(journal, data);
fence();
atomicWriteFileSync(file, data);
fence();
rmSync(journal, { force: true });
}
function withScopesRegistry(controlDir, fn) {
ensureDirExists(controlDir);
const file = join(controlDir, SCOPES_FILE);
const journal = `${file}.journal`;
const lock = `${file}.lock`;
const token = acquireSidecarLock(lock, { onDeadline: "throw" });
try {
rollForwardStrictSidecar(file, journal, () => assertSidecarLockOwnership(lock, token, "scope registry", "strict"));
return fn({
read: () => readScopesRecord(controlDir),
commit: (next) => commitScopesRecord(controlDir, lock, token, next),
renew: () => {
const now = new Date();
utimesSync(lock, now, now);
assertSidecarLockOwnership(lock, token, "scope registry", "strict");
},
});
}
finally {
releaseSidecarLock(lock, token);
}
}
function assertNoLabelCollision(memoryDir, scopes, scope, label, caseFoldingFs) {
for (const [other, otherLabel] of Object.entries(scopes)) {
if (other === scope)
continue;
if (otherLabel === label) {
throw new ControlPlaneCorruptError(`scope directory collision: ${JSON.stringify(scope)} and ${JSON.stringify(other)} both map to ${JSON.stringify(label)} — refusing (fail-closed)`);
}
if (otherLabel.toLowerCase() === label.toLowerCase()) {
const folds = caseFoldingFs ?? dirCaseFolds(memoryDir);
if (folds !== false) {
throw new ControlPlaneCorruptError(`scope directory collision on a case-folding filesystem: ${JSON.stringify(scope)} → ${JSON.stringify(label)} and ${JSON.stringify(other)} → ${JSON.stringify(otherLabel)} are one physical directory under ${memoryDir}` +
`${folds === undefined ? " (volume case semantics could not be probed — refusing the fold-equal pair rather than risking a silent cross-scope merge)" : ""} — refusing (fail-closed)`);
}
}
}
}
function assertHomePhysicalForm(home) {
const st = lstatOrAbsent(home);
if (st === undefined)
return "absent";
if (st.isDirectory())
return "dir";
throw scopeHomeRefusal(st.isSymbolicLink() ? "symlink" : st.isFile() ? "file" : "other", home);
}
function ensureScopeHome(home) {
if (assertHomePhysicalForm(home) === "absent") {
try {
mkdirSync(home, { recursive: true });
}
catch (err) {
assertHomePhysicalForm(home);
throw err;
}
}
assertHomePhysicalForm(home);
}
export function registerScope(memoryDir, controlDir, scope, opts) {
return withScopesRegistry(controlDir, ({ read, commit }) => {
const rec = requireV2Registry(read(), controlDir);
const registered = ownLabel(rec.scopes, scope);
const label = registered ?? scopeDirName(scope);
assertNoLabelCollision(memoryDir, rec.scopes, scope, label, opts?.caseFoldingFs);
const home = join(memoryDir, label);
if (registered === undefined) {
ensureDirExists(memoryDir);
ensureScopeHome(home);
commit({ ...rec, scopes: { ...rec.scopes, [scope]: label } });
}
else {
assertHomePhysicalForm(home);
}
return home;
});
}
export function registeredScopes(controlDir) {
return requireV2Registry(readScopesRecord(controlDir), controlDir).scopes;
}
export function scopeDirFor(memoryDir, controlDir, scope) {
const rec = requireV2Registry(readScopesRecord(controlDir), controlDir);
return join(memoryDir, ownLabel(rec.scopes, scope) ?? scopeDirName(scope));
}
export function scopeRegistryShape(controlDir) {
return readScopesRecord(controlDir).shape;
}
export function v1MemoryPathKeyResolver(controlDir) {
const read = readScopesRecord(controlDir);
const owner = read.shape === "v1" ? read.rootScope : read.rec.rootOwner;
const scopes = read.shape === "v1" ? read.scopes : read.rec.scopes;
const byLabel = new Map();
for (const [scope, label] of Object.entries(scopes))
if (label !== "" && scope !== owner)
byLabel.set(label, scope);
return (relPath) => {
const norm = relPath.split(sep).join("/");
const cut = norm.indexOf("/");
if (cut > 0) {
const scope = byLabel.get(norm.slice(0, cut));
if (scope !== undefined)
return { scope, rel: norm.slice(cut + 1) };
}
return owner === undefined ? undefined : { scope: owner, rel: norm };
};
}
const UNOWNED_ROOT_CONTENT_NAMES_MAX = 8;
function discloseLayoutIncident(sink, code, message) {
if (sink === undefined)
return;
try {
const e = new Error(message);
e.code = code;
sink(e);
}
catch {
}
}
function sameEntry(a, b) {
const sa = lstatOrAbsent(a);
const sb = lstatOrAbsent(b);
return sa !== undefined && sb !== undefined && sa.dev === sb.dev && sa.ino === sb.ino;
}
function looseRootContent(root, labels, controlDir) {
let names;
try {
names = readdirSync(root);
}
catch (err) {
if (err.code === "ENOENT")
return { names: [], probes: [] };
throw new ControlPlaneCorruptError(`memory root ${root} could not be enumerated for the layout adoption (${err.code ?? "io error"}) — refusing (fail-closed)`, { cause: err });
}
const canonicalControl = canonicalize(controlDir);
const out = [];
const probes = [];
const foldedLabels = new Map();
for (const label of labels)
foldedLabels.set(label.toLowerCase(), label);
for (const name of names.sort()) {
if (labels.has(name))
continue;
if (name.startsWith(CASEFOLD_PROBE_PREFIX)) {
probes.push(name);
continue;
}
if (canonicalize(join(root, name)) === canonicalControl)
continue;
const label = foldedLabels.get(name.toLowerCase());
if (label !== undefined && sameEntry(join(root, name), join(root, label)))
continue;
out.push(name);
}
return { names: out, probes };
}
export function layoutNeedsAdoption(root, controlDir) {
const read = readScopesRecord(controlDir);
if (read.shape === "v1" || read.shape === "migrating")
return true;
const { names, probes } = looseRootContent(root, new Set(Object.values(read.rec.scopes)), controlDir);
return names.length > 0 || probes.length > 0;
}
export function adoptRootScopeLayout(rootPath, controlDir, opts = {}) {
const root = resolve(rootPath);
withScopesRegistry(controlDir, (ctx) => {
const renew = () => {
ctx.renew();
opts.renew?.();
};
const convertSidecars = () => {
renew();
opts.convertSidecars?.(controlDir);
};
const read = ctx.read();
let rec;
let converting = false;
if (read.shape === "v1") {
converting = true;
const scopes = Object.create(null);
for (const [scope, label] of Object.entries(read.scopes)) {
if (label === "")
continue;
if (!SCOPE_DIR_LABEL_RE.test(label)) {
throw new ControlPlaneCorruptError(`scope registry (v1 form) maps ${JSON.stringify(scope)} to the label ${JSON.stringify(label)}, which is not a single non-hidden path segment — refusing (fail-closed): ${join(controlDir, SCOPES_FILE)}`);
}
scopes[scope] = label;
}
rec = { v: 2, scopes, ...(read.rootScope !== undefined ? { rootOwner: read.rootScope } : {}) };
if (read.rootScope !== undefined && ownLabel(scopes, read.rootScope) === undefined) {
const label = scopeDirName(read.rootScope);
assertNoLabelCollision(root, scopes, read.rootScope, label, undefined);
const home = join(root, label);
if (assertHomePhysicalForm(home) === "dir" && readdirSync(home).length > 0) {
throw new ControlPlaneCorruptError(`memory root ${root} holds a non-empty directory ${home} named like the home of its root-owning scope ${JSON.stringify(read.rootScope)} — the layout adoption cannot tell a topic directory from a home; resolve by hand (fail-closed)`);
}
rec.scopes = { ...scopes, [read.rootScope]: label };
}
}
else if (read.shape === "migrating") {
rec = read.rec;
const m = read.rec.migrating;
const { migrating: _m, ...rest } = rec;
rec = { ...rest, scopes: { ...rec.scopes, [m.owner]: m.label }, rootOwner: m.owner };
moveLooseContent(m.root, controlDir, rec, m.owner, m.label, renew, opts.onIncident, true);
convertSidecars();
ctx.commit(rec);
if (canonicalize(m.root) === canonicalize(root))
return;
}
else {
rec = read.rec;
}
const labels = new Set(Object.values(rec.scopes));
const { names, probes } = looseRootContent(root, labels, controlDir);
if (names.length === 0) {
if (converting) {
convertSidecars();
ctx.commit(rec);
}
for (const p of probes)
rmSync(join(root, p), { force: true });
return;
}
const candidate = opts.ownerCandidate ?? undefined;
const owner = rec.rootOwner ?? candidate;
if (owner === undefined) {
if (converting) {
convertSidecars();
ctx.commit(rec);
}
discloseLayoutIncident(opts.onIncident, "memory.layout_unowned_root_content", `memory root ${root} holds ${names.length} loose entr${names.length === 1 ? "y" : "ies"} that belong to no recorded scope (${names.slice(0, UNOWNED_ROOT_CONTENT_NAMES_MAX).join(", ")}${names.length > UNOWNED_ROOT_CONTENT_NAMES_MAX ? ", …" : ""}) — left in place, not served; the next mount with a write scope adopts them into that scope's home`);
return;
}
if (isContainedIn(root, controlDir)) {
throw new ControlPlaneCorruptError(`memory control plane ${controlDir} lies inside the memory root ${root} — the layout adoption refuses to move engine metadata as scope content (fail-closed)`);
}
const label = ownLabel(rec.scopes, owner) ?? scopeDirName(owner);
assertNoLabelCollision(root, rec.scopes, owner, label, undefined);
const home = join(root, label);
renew();
const homeForm = assertHomePhysicalForm(home);
if (homeForm === "dir") {
const inHome = readdirSync(home);
if (inHome.length > 0) {
throw new ControlPlaneCorruptError(`memory root ${root} holds loose content for ${JSON.stringify(owner)} while its home ${home} is already a non-empty directory with no move in progress — the two cannot be merged automatically (a topic directory named like the home, a root re-occupied by an older engine, or a registry that lost this scope's row); resolve by hand (fail-closed)`);
}
}
for (const name of names) {
renew();
if (lstatOrAbsent(join(home, name)) !== undefined) {
throw new ControlPlaneCorruptError(`memory root ${root} and the home ${home} both hold ${JSON.stringify(name)} — the layout adoption refuses to overwrite either (fail-closed)`);
}
}
const next = { v: 2, scopes: { ...rec.scopes, [owner]: label }, rootOwner: owner, migrating: { root, owner, label } };
renew();
ctx.commit(next);
const { migrating: _marker, ...done } = next;
moveLooseContent(root, controlDir, done, owner, label, renew, opts.onIncident, false);
convertSidecars();
renew();
ctx.commit(done);
});
}
function moveLooseContent(root, controlDir, rec, owner, label, renew, sink, resumed) {
const labels = new Set(Object.values(rec.scopes));
const { names, probes } = looseRootContent(root, labels, controlDir);
const home = join(root, label);
if (names.length > 0)
ensureScopeHome(home);
let moved = 0;
for (const name of names) {
renew();
const src = join(root, name);
const dst = join(home, name);
if (sameEntry(src, home)) {
throw new ControlPlaneCorruptError(`memory root ${root}: ${JSON.stringify(name)} is the home ${home} itself under another spelling — the layout adoption refuses to move a directory into itself (fail-closed)`);
}
if (lstatOrAbsent(dst) !== undefined) {
throw new ControlPlaneCorruptError(`memory root ${root} and the home ${home} both hold ${JSON.stringify(name)} — the layout adoption refuses to overwrite either (fail-closed)`);
}
try {
renameSync(src, dst);
moved += 1;
}
catch (err) {
if (err.code === "ENOENT" && lstatOrAbsent(src) === undefined)
continue;
throw err;
}
}
for (const p of probes) {
renew();
rmSync(join(root, p), { force: true });
}
renew();
writeIndexRevs(controlDir, {});
discloseLayoutIncident(sink, "memory.layout_migrated", `memory layout adopted at ${root}: ${moved} loose entr${moved === 1 ? "y" : "ies"} moved into the home of ${JSON.stringify(owner)} (${label})${resumed ? " — an interrupted move was finished" : ""}`);
}
export const canonicalize = physicalPathOfExisting;
export function isContainedIn(root, child) {
const r = canonicalize(root);
const c = canonicalize(child);
return c === r || c.startsWith(`${r}${sep}`);
}
export const STORE_FORM_REFUSAL_CODES = Object.freeze(["memory.target_outside_store", "memory.scope_home_not_directory"]);
export const TARGET_OUTSIDE_STORE = "memory.target_outside_store";
export const SCOPE_HOME_NOT_DIRECTORY = "memory.scope_home_not_directory";
export const SCOPE_HOME_FORMS = Object.freeze(["symlink", "file", "other"]);
const SCOPE_HOME_FORM_PHRASE = {
symlink: "a symbolic link",
file: "a regular file",
other: "a special file",
};
function storeFormRefusal(code, sentence, detail) {
const e = new Error(`${code}: ${sentence}`);
e.code = code;
e.detail = detail;
return e;
}
export function storeActRefusal(act, path) {
return storeFormRefusal(TARGET_OUTSIDE_STORE, `${act} was refused — its directory resolves outside the store root; not performed`, { path });
}
export function storeRootMovedRefusal(path, anchor) {
return storeFormRefusal(TARGET_OUTSIDE_STORE, "the store root now resolves to a different directory than the one its control plane records — refused, nothing in the store was written; put the directory back, or, if the store was moved on purpose, call reanchorMemoryStoreRoot(detail.path, { controlDir: detail.controlDir }) and reopen", { path, controlDir: anchor.controlDir, recorded: anchor.recorded, now: anchor.now });
}
export function storeRootAbsentRefusal(path, anchor) {
return storeFormRefusal(TARGET_OUTSIDE_STORE, "the store root no longer exists where its control plane records it — refused, nothing in the store was written; put the directory back, or, if the store was moved on purpose, call reanchorMemoryStoreRoot with its new directory and { controlDir: detail.controlDir }; to start over in place, create the directory, then call reanchorMemoryStoreRoot(detail.path, { controlDir: detail.controlDir }) and reopen", { path, controlDir: anchor.controlDir, recorded: anchor.recorded, now: anchor.now });
}
export function scopeHomeRefusal(form, path) {
return storeFormRefusal(SCOPE_HOME_NOT_DIRECTORY, `a scope home must be a real directory, but this one is ${SCOPE_HOME_FORM_PHRASE[form]} — refused (fail-closed); put the real directory back`, { path, form });
}
export function landingPathOf(p) {
return landingPathWithin(resolve(p), 0);
}
const MAX_LANDING_LINK_HOPS = 40;
function landingPathWithin(normalized, hops) {
try {
return physicalPathOf(normalized);
}
catch (err) {
if (err.code !== "ENOENT")
throw err;
}
const tail = [];
for (let cur = normalized;;) {
const st = lstatOrAbsent(cur);
if (st?.isSymbolicLink() === true) {
if (hops >= MAX_LANDING_LINK_HOPS) {
const e = new Error(`ELOOP: too many symbolic links resolving ${normalized}`);
e.code = "ELOOP";
throw e;
}
return landingPathWithin(join(resolve(dirname(cur), readlinkSync(cur)), ...[...tail].reverse()), hops + 1);
}
if (st !== undefined)
return canonicalize(normalized);
const parent = dirname(cur);
if (parent === cur)
return normalized;
tail.push(basename(cur));
cur = parent;
}
}
export function landsOutsideStore(physicalRoot, target) {
const dir = landingPathOf(dirname(target));
return dir !== physicalRoot && !dir.startsWith(`${physicalRoot}${sep}`);
}
export function ensureDirExists(dir) {
if (!existsSync(dir))
mkdirSync(dir, { recursive: true, mode: 0o700 });
}
export const LEDGER_FILE = "revs.json";
export const ROOT_ANCHOR_FILE = "root-anchor.json";
const ANCHOR_BACKFILL_ARM_OPEN = true;
export function absentAnchorReading(controlPlaneHoldsStore, backfillArmOpen = ANCHOR_BACKFILL_ARM_OPEN) {
if (!controlPlaneHoldsStore)
return "record";
return backfillArmOpen ? "backfill" : "refuse";
}
function reanchorRemedy(dir, controlDir) {
return `once the store root is where it belongs, call reanchorMemoryStoreRoot(${JSON.stringify(dir)}, { controlDir: ${JSON.stringify(controlDir)} }) and reopen`;
}
function damagedAnchorError(controlDir, dir, why, cause) {
return new ControlPlaneCorruptError(`memory store root anchor ${join(controlDir, ROOT_ANCHOR_FILE)} is damaged (${why}) — the store root cannot be checked against it, so nothing in the store was opened; ${reanchorRemedy(dir, controlDir)}`, cause !== undefined ? { cause } : undefined);
}
function readStoreRootAnchor(controlDir, dir) {
let raw;
let fd;
try {
fd = openSync(join(controlDir, ROOT_ANCHOR_FILE), O_RDONLY | O_NOFOLLOW | O_NONBLOCK);
if (!fstatSync(fd).isFile())
throw damagedAnchorError(controlDir, dir, "not a regular file");
raw = readFileSync(fd, "utf8");
}
catch (err) {
if (err instanceof ControlPlaneCorruptError)
throw err;
const code = err.code;
if (code === "ENOENT" && fd === undefined)
return undefined;
throw damagedAnchorError(controlDir, dir, code === "ELOOP" ? "a symbolic link, not the record" : `unreadable: ${code ?? thrownLabel(err)}`, err);
}
finally {
if (fd !== undefined)
closeSync(fd);
}
let parsed;
try {
parsed = JSON.parse(raw);
}
catch (err) {
throw damagedAnchorError(controlDir, dir, "not JSON", err);
}
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed))
throw damagedAnchorError(controlDir, dir, "not an object");
const a = parsed;
if (a.v !== 1)
throw damagedAnchorError(controlDir, dir, `unknown version ${JSON.stringify(a.v) ?? typeof a.v}`);
if (typeof a.realpath !== "string" || !isAbsolute(a.realpath))
throw damagedAnchorError(controlDir, dir, "realpath is not an absolute path");
if (typeof a.spelling !== "string" || typeof a.recordedAt !== "number" || !Number.isFinite(a.recordedAt))
throw damagedAnchorError(controlDir, dir, "spelling / recordedAt out of shape");
return { v: 1, realpath: a.realpath, spelling: a.spelling, recordedAt: a.recordedAt };
}
function controlPlaneHoldsStore(controlDir) {
return lstatOrAbsent(join(controlDir, SCOPES_FILE)) !== undefined || lstatOrAbsent(join(controlDir, LEDGER_FILE)) !== undefined;
}
function judgeAgainstAnchor(dir, controlDir, anchor) {
const now = landingPathOf(dir);
if (now !== anchor.realpath)
throw storeRootMovedRefusal(dir, { controlDir, recorded: anchor.realpath, now });
if (lstatOrAbsent(now) === undefined && controlPlaneHoldsStore(controlDir))
throw storeRootAbsentRefusal(dir, { controlDir, recorded: anchor.realpath, now });
return now;
}
function writeStoreRootAnchor(controlDir, lock, token, anchor) {
assertSidecarLockOwnership(lock, token, "memory store root anchor", "strict");
atomicWriteFileSync(join(controlDir, ROOT_ANCHOR_FILE), `${JSON.stringify(anchor, null, 2)}\n`);
}
export function anchorStoreRoot(dir, controlDir, opts = {}) {
const anchor = readStoreRootAnchor(controlDir, dir);
if (anchor !== undefined)
return judgeAgainstAnchor(dir, controlDir, anchor);
ensureDirExists(controlDir);
const lock = join(controlDir, `${ROOT_ANCHOR_FILE}.lock`);
const token = acquireSidecarLock(lock, { onDeadline: "throw" });
try {
const raced = readStoreRootAnchor(controlDir, dir);
if (raced !== undefined)
return judgeAgainstAnchor(dir, controlDir, raced);
const reading = absentAnchorReading(controlPlaneHoldsStore(controlDir));
if (reading === "refuse")
throw damagedAnchorError(controlDir, dir, "absent, while the control plane holds a store");
ensureDirExists(dir);
const realpath = landingPathOf(dir);
writeStoreRootAnchor(controlDir, lock, token, { v: 1, realpath, spelling: dir, recordedAt: (opts.now ?? Date.now)() });
if (reading === "backfill") {
discloseLayoutIncident(opts.onIncident, "memory.root_anchor_backfilled", `memory store root anchor recorded for a store that had none: ${dir} resolves to ${realpath} (control plane ${controlDir}) — from now on the store opens only there; where the root resolved before this open cannot be told`);
}
return realpath;
}
finally {
releaseSidecarLock(lock, token);
}
}
export const STORE_JOURNAL_FILE = "journal.json";
export const STORE_TXN_LOCK_DIR = "txn.lock";
export const TXN_LOCK_STALE_MS = 30_000;
const TXN_LOCK_WAIT_MS = 15_000;
const TXN_LOCK_STEAL_GRACE_MS = 250;
export function tryStoreTxnLock(controlDir, opts) {
const lockDir = join(controlDir, STORE_TXN_LOCK_DIR);
const ownerPath = join(lockDir, "owner");
const staleMs = opts.staleMs ?? TXN_LOCK_STALE_MS;
const faultText = (err) => err.code ?? "io error";
const tryMkdir = () => {
try {
mkdirSync(controlDir, { recursive: true });
}
catch (err) {
throw new ControlPlaneCorruptError(`memory control-plane root could not be created (${faultText(err)}) at ${controlDir} — a lock-acquisition fault is not contention (fail-closed)`, { cause: err });
}
try {
mkdirSync(lockDir);
return true;
}
catch (err) {
if (err.code === "EEXIST")
return false;
throw new ControlPlaneCorruptError(`memory txn lock could not be created (${faultText(err)}) at ${lockDir} — a lock-acquisition fault is not contention (fail-closed)`, { cause: err });
}
};
let acquired = tryMkdir();
if (!acquired) {
let mtimeMs;
try {
mtimeMs = statSync(lockDir).mtimeMs;
opts.observed?.(mtimeMs);
}
catch (err) {
if (err.code !== "ENOENT") {
throw new ControlPlaneCorruptError(`memory txn lock state could not be probed (${faultText(err)}) at ${lockDir} — a probe failure is not contention (fail-closed)`, { cause: err });
}
let occupant;
try {
occupant = lstatSync(lockDir);
}
catch (lerr) {
if (lerr.code !== "ENOENT") {
throw new ControlPlaneCorruptError(`memory txn lock re-probe failed (${faultText(lerr)}) at ${lockDir} — a probe failure is not contention (fail-closed)`, { cause: lerr });
}
}
if (occupant !== undefined && !occupant.isDirectory()) {
throw new ControlPlaneCorruptError(`memory txn lock path is occupied by an invalid node (a dangling symlink?) at ${lockDir} — not contention and not a release race; remove the occupant (fail-closed)`, { cause: err });
}
mtimeMs = undefined;
}
if (mtimeMs !== undefined && opts.now() - mtimeMs > staleMs) {
let tokenPresent = false;
try {
statSync(ownerPath);
tokenPresent = true;
}
catch (err) {
if (err.code !== "ENOENT") {
throw new ControlPlaneCorruptError(`memory txn lock owner token could not be probed (${faultText(err)}) at ${ownerPath} — a probe failure is not a mid-acquisition (fail-closed)`, { cause: err });
}
}
if (tokenPresent) {
const tomb = join(controlDir, `${STORE_TXN_LOCK_DIR}.stale-${randomUUID()}`);
let stolen = false;
try {
renameSync(lockDir, tomb);
stolen = true;
}
catch (err) {
if (err.code !== "ENOENT") {
throw new ControlPlaneCorruptError(`memory txn lock stale-steal rename failed (${faultText(err)}) at ${lockDir} — a rename fault is not a lost race (fail-closed)`, { cause: err });
}
}
if (stolen) {
rmSync(tomb, { recursive: true, force: true });
acquired = tryMkdir();
}
}
}
if (!acquired)
return undefined;
}
const token = randomUUID();
try {
const fd = openSync(ownerPath, "w", 0o600);
try {
writeAllSync(fd, token);
fsyncSync(fd);
}
finally {
closeSync(fd);
}
}
catch (err) {
try {
rmSync(lockDir, { recursive: true, force: true });
}
catch {
}
throw new Error(`memory txn lock owner token could not be written at ${ownerPath}: ${thrownText(err)}`);
}
return {
token,
release: () => {
try {
if (readFileSync(ownerPath, "utf8") !== token)
return;
}
catch {
return;
}
try {
rmSync(lockDir, { recursive: true, force: true });
}
catch {
}
},
};
}
export function assertStoreTxnLockOwnership(controlDir, token) {
let held;
try {
held = readFileSync(join(controlDir, STORE_TXN_LOCK_DIR, "owner"), "utf8");
}
catch {
held = undefined;
}
if (held !== token) {
throw new ControlPlaneCorruptError("txn lock ownership lost before commit (holder stalled past the stale deadline and was stolen from?) — transaction aborted, nothing was committed", { facet: "lock-ownership" });
}
}
export async function acquireStoreTxnLock(controlDir, opts) {
const staleMs = opts.timings?.staleMs ?? TXN_LOCK_STALE_MS;
const waitMs = opts.timings?.waitMs ?? TXN_LOCK_WAIT_MS;
const stealGraceMs = opts.timings?.stealGraceMs ?? TXN_LOCK_STEAL_GRACE_MS;
let deadline = Date.now() + waitMs;
for (;;) {
const lock = tryStoreTxnLock(controlDir, { now: opts.now, staleMs, observed: (mtimeMs) => (deadline = Math.max(deadline, mtimeMs + staleMs + stealGraceMs)) });
if (lock !== undefined)
return lock;
if (Date.now() > deadline) {
throw new Error(`memory txn lock timeout (${waitMs}ms, extended to the lock's stale line) at ${join(controlDir, STORE_TXN_LOCK_DIR)} — another writer is wedged?`);
}
await new Promise((r) => setTimeout(r, 20 + Math.floor(Math.random() * 40)));
}
}
function reanchorDestination(dir) {
let to;
try {
to = physicalPathOf(resolve(dir));
}
catch (err) {
throw reanchorRefusal(`the store root ${JSON.stringify(dir)} does not resolve (${err.code ?? thrownLabel(err)}) — put the directory in place first`);
}
if (lstatOrAbsent(to)?.isDirectory() !== true)
throw reanchorRefusal(`the store root ${JSON.stringify(dir)} resolves to ${to}, which is not a directory`);
return to;
}
function reanchorRefusal(sentence) {
const e = new Error(`reanchorMemoryStoreRoot: ${sentence}`);
e.code = "config.memory_reanchor";
return e;
}
export async function reanchorMemoryStoreRoot(dir, opts = {}) {
const controlDir = effectiveControlRootOf(dir, { controlDir: opts.controlDir, onIncident: opts.onIncident });
const to = reanchorDestination(dir);
const lock = join(controlDir, `${ROOT_ANCHOR_FILE}.lock`);
if (lstatOrAbsent(join(controlDir, ROOT_ANCHOR_FILE)) === undefined && !controlPlaneHoldsStore(controlDir)) {
throw reanchorRefusal(`the control plane ${controlDir} holds no store and no root anchor — nothing to re-anchor; pass the controlDir this store's backend / engine is opened with`);
}
const txn = await acquireStoreTxnLock(controlDir, { now: Date.now });
let from;
try {
const token = acquireSidecarLock(lock, { onDeadline: "throw" });
try {
try {
from = readStoreRootAnchor(controlDir, dir)?.realpath;
}
catch {
from = undefined;
}
try {
assertStoreTxnLockOwnership(controlDir, txn.token);
}
catch {
throw reanchorRefusal(`the store's transaction lock was lost while re-anchoring (held past its stale line and taken by another writer) — nothing changed; retry (control plane ${controlDir})`);
}
if (lstatOrAbsent(join(controlDir, STORE_JOURNAL_FILE)) !== undefined) {
throw reanchorRefusal(`a committed transaction is still pending recovery at the recorded root — put the store back at ${from ?? "the root it was last opened at"}, open it once so the journal replays, then re-anchor (control plane ${controlDir})`);
}
let still;
try {
still = reanchorDestination(dir);
}
catch {
still = undefined;
}
if (still !== to)
throw reanchorRefusal(`the store root ${JSON.stringify(dir)} moved while re-anchoring (it resolved to ${to} when the call began) — nothing changed; retry`);
writeStoreRootAnchor(controlDir, lock, token, { v: 1, realpath: to, spelling: dir, recordedAt: Date.now() });
}
finally {
releaseSidecarLock(lock, token);
}
}
finally {
txn.release();
}
discloseLayoutIncident(opts.onIncident, "memory.root_reanchored", `memory store root re-anchored: ${dir} now opens at ${to}${from !== undefined ? ` (was ${from})` : ""} — control plane ${controlDir}`);
return from !== undefined ? { from, to, controlDir } : { to, controlDir };
}
export function permissionRemedyOf(err, memoryDir) {
const e = typeof err === "object" && err !== null ? err : undefined;
if (e === undefined || (e.code !== "EACCES" && e.code !== "EPERM") || typeof e.path !== "string" || !isContainedIn(memoryDir, e.path))
return undefined;
return `file permissions refused this memory write (the engine never changes permission bits) — if an engine from 7.30.x or earlier left read-only bits on this store, run \`chmod -R u+w ${shellQuoted(memoryDir)}\` once no such engine still runs on it (an older engine sets the bits again whenever it mounts a layer read-only)`;
}
function shellQuoted(s) {
return `'${s.replace(/'/g, "'\\''")}'`;
}
const remedied = new WeakSet();
export function withPermissionRemedy(err, memoryDir) {
const remedy = permissionRemedyOf(err, memoryDir);
if (remedy === undefined || !(err instanceof Error) || remedied.has(err))
return err;
err.message = `${err.message} — ${remedy}`;
remedied.add(err);
return err;
}
export const QUARANTINE_DIR = "quarantine";
const DELETED_TOMBSTONE = "---\ndeleted: true\n---\n";
export function writeFileNoFollow(path, content) {
const fd = openSync(path, O_WRONLY | O_CREAT | O_NOFOLLOW, 0o644);
try {
const links = fstatSync(fd).nlink;
if (links > 1)
throw Object.assign(new Error(`${path} has ${links} names (a hard link) — a write through one name changes the file under the others; not written`), { path });
ftruncateSync(fd, 0);
writeFileSync(fd, content, "utf8");
}
finally {
closeSync(fd);
}
}
export function writeFileNoReplace(path, content) {
let fd;
try {
fd = openSync(path, O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW, 0o644);
}
catch (err) {
if (err.code === "EEXIST")
return "kept";
throw err;
}
try {
writeFileSync(fd, content, "utf8");
}
finally {
closeSync(fd);
}
return "written";
}
function readForVerification(path, maxBytes) {
let fd;
try {
fd = openSync(path, O_RDONLY | O_NOFOLLOW | O_NONBLOCK);
}
catch {
return undefined;
}
try {
if (!fstatSync(fd).isFile())
return undefined;
const buf = Buffer.allocUnsafe(maxBytes);
let got = 0;
for (;;) {
const n = readSync(fd, buf, got, maxBytes - got, got);
if (n <= 0 || got + n >= maxBytes)
return buf.subarray(0, got + Math.max(0, n)).toString("utf8");
got += n;
}
}
catch {
return undefined;
}
finally {
closeSync(fd);
}
}
export function restoreContainedBaseline(path, committed, leftOnPlane) {
try {
if (leftOnPlane === undefined || readForVerification(path, Buffer.byteLength(leftOnPlane) + 1) !== leftOnPlane) {
return writeFileNoReplace(path, committed);
}
writeFileNoFollow(path, committed);
return "written";
}
catch {
return "failed";
}
}
let quarantineSeq = 0;
export function quarantineAndTombstone(path, content, quarantineDir, now) {
let captured;
let detail;
try {
ensureDirExists(quarantineDir);
const base = `${now()}-${basename(path)}`;
for (let attempt = 0; captured === undefined && attempt < 10; attempt++) {
const candidate = join(quarantineDir, attempt === 0 ? base : `${base}.${attempt}`);
try {
writeFileSync(candidate, content, { encoding: "utf8", flag: "wx" });
captured = candidate;
}
catch (err) {
if (!(err instanceof Error && "code" in err && err.code === "EEXIST"))
throw err;
}
}
if (captured === undefined) {
const last = join(quarantineDir, `${base}.${process.pid}.${quarantineSeq++}`);
writeFileSync(last, content, { encoding: "utf8", flag: "wx" });
captured = last;
}
}
catch (err) {
detail = `quarantine copy failed: ${thrownText(err)}`;
}
let left = content;
let removed = false;
try {
rmSync(path, { force: true });
left = undefined;
removed = true;
}
catch (rmErr) {
try {
writeFileNoFollow(path, DELETED_TOMBSTONE);
left = DELETED_TOMBSTONE;
removed = true;
detail = `${detail !== undefined ? `${detail}; ` : ""}delete failed (${thrownText(rmErr)}) — tombstoned in place`;
}
catch (clearErr) {
detail = `${detail !== undefined ? `${detail}; ` : ""}suspect file could not be deleted or cleared: ${thrownText(clearErr)}`;
}
}
return {
...(captured !== undefined ? { dest: captured } : {}),
removed,
...(left !== undefined ? { leftOnPlane: left } : {}),
...(detail !== undefined ? { detail } : {}),
};
}
export const ANNOUNCEMENTS_FILE = "announcements.json";
export const MEMORY_ANNOUNCEMENTS_MAX = 20;
export const SCAN_FUSE_FILE = "scan-fuse.json";
export const SCAN_FUSE_THRESHOLD = 3;
function sleepSync(ms) {
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
}
const LOCK_STALE_MS = 1_500;
const LOCK_WAIT_CAP_MS = 3_000;
const LOCK_SPIN_PAUSE_MS = 5;
const MAX_FAIL_OPEN_DEADLINE_ROUNDS = 3;
export function acquireSidecarLock(lockDir, opts = {}) {
const waitCapMs = opts.waitCapMs ?? LOCK_WAIT_CAP_MS;
const deadline = Date.now() + waitCapMs;
const mintToken = () => `${process.pid}-${Math.random().toString(36).slice(2, 10)}`;
let deadlineRounds = 0;
const stealOnce = () => {
const tomb = `${lockDir}.stale-${process.pid}-${Math.random().toString(36).slice(2, 8)}`;
try {
renameSync(lockDir, tomb);
}
catch {
return false;
}
rmSync(tomb, { recursive: true, force: true });
return true;
};
for (;;) {
try {
mkdirSync(lockDir);
const token = mintToken();
let fd;
try {
fd = openSync(join(lockDir, "owner"), "w");
writeAllSync(fd, token);
}
catch {
}
finally {
if (fd !== undefined) {
try {
closeSync(fd);
}
catch {
}
}
}
return token;
}
catch (err) {
if (err.code !== "EEXIST")
throw err;
}
try {
const age = Date.now() - statSync(lockDir).mtimeMs;
if (age > LOCK_STALE_MS)
stealOnce();
}
catch {
}
if (Date.now() > deadline) {
if (opts.onDeadline === "throw") {
throw new Error(`sidecar lock wait timed out (${waitCapMs}ms) at ${lockDir} — refusing to proceed without the lock (fail-closed sidecar)`);
}
deadlineRounds += 1;
if (deadlineRounds > MAX_FAIL_OPEN_DEADLINE_ROUNDS)
return mintToken();
stealOnce();
}
sleepSync(LOCK_SPIN_PAUSE_MS);
}
}
function rollForwardSidecar(file, journal) {
let raw;
try {
raw = readFileSync(journal, "utf8");
}
catch {
return;
}
try {
JSON.parse(raw);
atomicWriteFileSync(file, raw);
}
catch {
}
rmSync(journal, { force: true });
}
function lockedJournaledUpdate(controlDir, fileName, mutate) {
ensureDirExists(controlDir);
const file = join(controlDir, fileName);
const journal = `${file}.journal`;
const lock = `${file}.lock`;
const token = acquireSidecarLock(lock);
try {
rollForwardSidecar(file, journal);
let current;
try {
current = JSON.parse(readFileSync(file, "utf8"));
}
catch {
current = undefined;
}
const next = mutate(current);
if (next === undefined)
return;
const data = `${JSON.stringify(next, null, 2)}\n`;
atomicWriteFileSync(journal, data);
atomicWriteFileSync(file, data);
rmSync(journal, { force: true });
}
finally {
releaseSidecarLock(lock, token);
}
}
export function assertSidecarLockOwnership(lockDir, token, what, mode = "lenient") {
let held;
try {
held = readFileSync(join(lockDir, "owner"), "utf8");
}
catch (err) {
if (mode === "strict" && err.code !== undefined) {
throw new ControlPlaneCorruptError(`${what}: sidecar lock ownership UNPROVABLE at commit (owner file ${err.code === "ENOENT" ? "absent" : `unreadable: ${err.code}`}) — a fail-closed ledger must not commit on a fence it cannot prove; update aborted`, { cause: err, facet: "lock-ownership" });
}
return;
}
if (held !== token) {
throw new ControlPlaneCorruptError(`${what}: sidecar lock ownership lost before commit (holder stalled past ${LOCK_STALE_MS}ms and was stolen from) — update aborted`, { facet: "lock-ownership" });
}
}
function releaseSidecarLock(lockDir, token) {
try {
const held = readFileSync(join(lockDir, "owner"), "utf8");
if (held !== token)
return;
}
catch {
}
rmSync(lockDir, { recursive: true, force: true });
}
function readSidecarJson(controlDir, fileName) {
const file = join(controlDir, fileName);
for (const p of [`${file}.journal`, file]) {
try {
return JSON.parse(readFileSync(p, "utf8"));
}
catch {
}
}
return undefined;
}
function coerceAnnouncements(raw) {
const rec = { folded: 0, queue: [] };
if (!raw || typeof raw !== "object" || Array.isArray(raw))
return rec;
const r = raw;
if (typeof r.folded === "number" && Number.isFinite(r.folded) && r.folded > 0)
rec.folded = Math.floor(r.folded);
if (Array.isArray(r.queue)) {
for (const e of r.queue) {
const a = e;
if (a &&
typeof a === "object" &&
(a.kind === "gate" || a.kind === "external") &&
typeof a.at === "number" &&
Array.isArray(a.items) &&
a.items.every((i) => typeof i === "string")) {
rec.queue.push({
kind: a.kind,
at: a.at,
...(typeof a.sessionId === "string" ? { sessionId: a.sessionId } : {}),
...(typeof a.reportId === "string" ? { reportId: a.reportId } : {}),
items: a.items,
});
}
}
}
return rec;
}
export function enqueueMemoryAnnouncement(controlDir, ann, opts) {
const family = opts?.supersedesPrefix;
if (ann.items.length === 0 && family === undefined)
return;
lockedJournaledUpdate(controlDir, ANNOUNCEMENTS_FILE, (current) => {
const rec = coerceAnnouncements(current);
if (family !== undefined) {
const queued = rec.queue.flatMap((a) => a.items.filter((i) => i.startsWith(family)));
if (queued.length === ann.items.length && ann.items.every((i) => queued.includes(i)))
return undefined;
for (const a of rec.queue)
a.items = a.items.filter((i) => !i.startsWith(family));
rec.queue = rec.queue.filter((a) => a.items.length > 0);
if (ann.items.length === 0)
return rec;
}
rec.queue.push(ann);
if (rec.queue.length > MEMORY_ANNOUNCEMENTS_MAX) {
rec.folded += rec.queue.length - MEMORY_ANNOUNCEMENTS_MAX;
rec.queue = rec.queue.slice(-MEMORY_ANNOUNCEMENTS_MAX);
}
return rec;
});
}
export function drainMemoryAnnouncements(controlDir) {
const file = join(controlDir, ANNOUNCEMENTS_FILE);
if (!existsSync(file) && !existsSync(`${file}.journal`))
return { folded: 0, queue: [] };
let drained = { folded: 0, queue: [] };
lockedJournaledUpdate(controlDir, ANNOUNCEMENTS_FILE, (current) => {
drained = coerceAnnouncements(current);
if (drained.queue.length === 0 && drained.folded === 0)
return undefined;
return { folded: 0, queue: [] };
});
return drained;
}
export function peekMemoryAnnouncements(controlDir) {
return coerceAnnouncements(readSidecarJson(controlDir, ANNOUNCEMENTS_FILE));
}
export function scanFuseKey(scope, rel) {
return JSON.stringify([scope, rel.split(sep).join("/")]);
}
function coerceFuse(raw) {
if (!raw || typeof raw !== "object" || Array.isArray(raw))
return {};
const rec = raw;
if (rec.v !== 2 || !rec.counts || typeof rec.counts !== "object" || Array.isArray(rec.counts))
return {};
const out = {};
for (const [k, v] of Object.entries(rec.counts)) {
if (typeof v === "number" && Number.isFinite(v) && v > 0)
out[k] = Math.floor(v);
}
return out;
}
export function bumpScanFuse(controlDir, key) {
let prior = 0;
lockedJournaledUpdate(controlDir, SCAN_FUSE_FILE, (current) => {
const counts = coerceFuse(current);
prior = counts[key] ?? 0;
counts[key] = prior + 1;
return { v: 2, counts };
});
return prior;
}
const INDEX_REVS_FILE = "index-revs.json";
export function readIndexRevs(controlDir) {
const parsed = readSidecarJson(controlDir, INDEX_REVS_FILE);
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
return {};
const pairs = [];
for (const [k, v] of Object.entries(parsed)) {
if (typeof v === "string")
pairs.push([k, { rev: v }]);
else if (v !== null && typeof v === "object" && typeof v.rev === "string") {
const { rev, line } = v;
pairs.push([k, typeof line === "string" ? { rev, line } : { rev }]);
}
}
return Object.fromEntries(pairs);
}
export function normalizeIndexLine(line) {
return line.trim().replace(/\s+/g, " ");
}
export function indexLineIsStale(snapshot, entryRev, line) {
return snapshot !== undefined && snapshot.rev !== entryRev && snapshot.line !== undefined && normalizeIndexLine(snapshot.line) === normalizeIndexLine(line);
}
export function writeIndexRevs(controlDir, revs) {
try {
ensureDirExists(controlDir);
const recorded = Object.fromEntries(Object.entries(revs).map(([target, v]) => [target, { rev: v.rev, line: normalizeIndexLine(v.line) }]));
writeIfChanged(join(controlDir, INDEX_REVS_FILE), `${JSON.stringify(recorded, null, 2)}\n`);
}
catch {
}
}
export function scanFuseCount(controlDir, key) {
return coerceFuse(readSidecarJson(controlDir, SCAN_FUSE_FILE))[key] ?? 0;
}
export function sessionRecordBindsIncarnation(recorded, asking) {
return recorded === undefined || asking === undefined || recorded === asking;
}
function readIncarnationSeat(parsed) {
return isIncarnationSeat(parsed.incarnation) && parsed.incarnation !== undefined ? parsed.incarnation : undefined;
}
function isIncarnationSeat(raw) {
if (raw === undefined)
return true;
if (typeof raw !== "string" || raw === "")
return false;
try {
encodeURIComponent(raw);
return true;
}
catch {
return false;
}
}
export function usableSessionIncarnation(raw) {
return isIncarnationSeat(raw) ? raw : undefined;
}
function sessionRecordPath(controlDir, dir, sessionId, incarnation) {
const name = incarnation === undefined ? `${encodeURIComponent(sessionId)}.json` : `${encodeURIComponent(sessionId)}@${encodeURIComponent(incarnation)}.json`;
return join(controlDir, dir, name);
}
function standingOwnerAt(path) {
let raw;
try {
raw = readFileSync(path, "utf8");
}
catch (err) {
if (err.code !== "ENOENT")
return "unattributable";
try {
lstatSync(path);
}
catch {
return "unattributable";
}
return { owner: undefined };
}
try {
const parsed = JSON.parse(raw);
if (parsed !== null && typeof parsed === "object")
return { owner: readIncarnationSeat(parsed) };
}
catch {
}
return "unattributable";
}
function mintSessionRecordSlots(controlDir, dir, sessionId, incarnation, text) {
const idSlot = sessionRecordPath(controlDir, dir, sessionId);
try {
ensureDirExists(dirname(idSlot));
writeFileSync(idSlot, text, { encoding: "utf8", flag: "wx" });
return "created";
}
catch (err) {
if (!(err instanceof Error && "code" in err && err.code === "EEXIST"))
return "unpersisted";
}
const own = usableSessionIncarnation(incarnation);
const standing = standingOwnerAt(idSlot);
const vouches = standing !== "unattributable" && (standing.owner === undefined || standing.owner === own);
const unnamed = vouches || own === undefined ? undefined : standingOwnerAt(sessionRecordPath(controlDir, dir, sessionId, ""));
if (vouches || (unnamed !== undefined && unnamed !== "unattributable" && unnamed.owner === undefined))
return "existed";
try {
writeFileSync(sessionRecordPath(controlDir, dir, sessionId, own ?? ""), text, { encoding: "utf8", flag: "wx" });
return "created";
}
catch (err) {
if (err instanceof Error && "code" in err && err.code === "EEXIST")
return "existed";
return "unpersisted";
}
}
function bindOrSeparate(record, identity, asking, unknownSlot) {
if (sessionRecordBindsIncarnation(record.incarnation, asking))
return record;
const unnamed = unknownSlot();
if (unnamed !== undefined && sessionRecordBindsIncarnation(unnamed.incarnation, asking))
return unnamed;
if (record.incarnation !== undefined)
identity?.onSeparated?.(record.incarnation);
return undefined;
}
function sessionIdOfRecordName(name) {
if (!name.endsWith(".json"))
return undefined;
const stem = name.slice(0, -".json".length);
const at = stem.indexOf("@");
try {
return decodeURIComponent(at < 0 ? stem : stem.slice(0, at));
}
catch {
return undefined;
}
}
export const SESSION_POLLUTION_DIR = "session-pollution";
function pollutionPath(controlDir, sessionId, incarnation) {
return sessionRecordPath(controlDir, SESSION_POLLUTION_DIR, sessionId, incarnation);
}
export function markSessionPolluted(controlDir, sessionId, reason, now, cause, incarnation) {
const record = { at: now(), reason, ...(cause !== undefined ? { cause } : {}), ...(incarnation !== undefined ? { incarnation } : {}) };
return mintSessionRecordSlots(controlDir, SESSION_POLLUTION_DIR, sessionId, incarnation, `${JSON.stringify(record, null, 2)}\n`);
}
export function readSessionPollution(controlDir, sessionId, identity) {
const asking = usableSessionIncarnation(identity?.incarnation);
if (asking !== undefined) {
const own = readPollutionAt(pollutionPath(controlDir, sessionId, asking));
if (own !== undefined)
return own;
}
const unnamedSlot = () => readPollutionAt(pollutionPath(controlDir, sessionId, ""));
const standing = readPollutionAt(pollutionPath(controlDir, sessionId));
return standing === undefined ? unnamedSlot() : bindOrSeparate(standing, identity, asking, unnamedSlot);
}
function readPollutionAt(path) {
let raw;
try {
raw = readFileSync(path, "utf8");
}
catch {
try {
lstatSync(path);
}
catch {
return undefined;
}
return { at: 0, reason: "pollution marker present but unreadable (kept fail-closed)" };
}
try {
const parsed = JSON.parse(raw);
if (parsed !== null && typeof parsed === "object") {
const at = parsed.at;
const reason = parsed.reason;
const cause = parsed.cause;
const knownCause = typeof cause === "string" && MEMORY_ORIGIN_CAUSES.includes(cause) ? cause : undefined;
const incarnation = readIncarnationSeat(parsed);
if (typeof at === "number" && typeof reason === "string")
return { at, reason, ...(knownCause !== undefined ? { cause: knownCause } : {}), ...(incarnation !== undefined ? { incarnation } : {}) };
}
}
catch {
}
return { at: 0, reason: "pollution marker present but unreadable (kept fail-closed)" };
}
export function listSessionPollution(controlDir) {
const dir = join(controlDir, SESSION_POLLUTION_DIR);
let names;
try {
names = readdirSync(dir);
}
catch (err) {
if (err.code === "ENOENT")
return {};
throw new ControlPlaneCorruptError(`session pollution markers could not be enumerated (${err.code ?? "io error"}) at ${dir} — an enumeration failure is not an empty set (fail-closed)`, { cause: err });
}
const out = Object.create(null);
for (const name of names.sort()) {
const sessionId = sessionIdOfRecordName(name);
if (sessionId === undefined || Object.prototype.hasOwnProperty.call(out, sessionId))
continue;
const rec = readPollutionAt(join(dir, name));
if (rec !== undefined)
out[sessionId] = rec;
}
return out;
}
export function importSessionPollution(controlDir, sessionId, record) {
const path = pollutionPath(controlDir, sessionId);
ensureDirExists(dirname(path));
try {
writeFileSync(path, `${JSON.stringify({ at: record.at, reason: record.reason, ...(record.cause !== undefined ? { cause: record.cause } : {}) }, null, 2)}\n`, { encoding: "utf8", flag: "wx" });
return "written";
}
catch (err) {
if (!(err instanceof Error && "code" in err && err.code === "EEXIST"))
throw err;
}
const standing = readSessionPollution(controlDir, sessionId);
return standing !== undefined && standing.at === record.at && standing.reason === record.reason ? "already" : "divergent";
}
export const SESSION_CAPTURE_OPTOUT_DIR = "session-capture-optout";
function captureOptOutPath(controlDir, sessionId, incarnation) {
return sessionRecordPath(controlDir, SESSION_CAPTURE_OPTOUT_DIR, sessionId, incarnation);
}
export function markSessionCaptureOptOut(controlDir, sessionId, reason, now, incarnation) {
const record = { at: now(), reason, ...(incarnation !== undefined ? { incarnation } : {}) };
return mintSessionRecordSlots(controlDir, SESSION_CAPTURE_OPTOUT_DIR, sessionId, incarnation, `${JSON.stringify(record, null, 2)}\n`);
}
export function readSessionCaptureOptOut(controlDir, sessionId, identity) {
const asking = usableSessionIncarnation(identity?.incarnation);
if (asking !== undefined) {
const own = readCaptureOptOutAt(captureOptOutPath(controlDir, sessionId, asking));
if (own !== undefined)
return own;
}
const unnamedSlot = () => readCaptureOptOutAt(captureOptOutPath(controlDir, sessionId, ""));
const standing = readCaptureOptOutAt(captureOptOutPath(controlDir, sessionId));
return standing === undefined ? unnamedSlot() : bindOrSeparate(standing, identity, asking, unnamedSlot);
}
function readCaptureOptOutAt(path) {
let raw;
try {
raw = readFileSync(path, "utf8");
}
catch (err) {
if (err.code === "ENOENT") {
try {
lstatSync(path);
}
catch (lerr) {
if (lerr.code === "ENOENT")
return undefined;
throw new ControlPlaneCorruptError(`session capture opt-out record state is INDETERMINATE (${lerr.code ?? "io error"}) at ${path} — the store cannot establish whether a record exists; consumers must suppress on the fault axis (an outage is never reported as a user request)`, { cause: lerr });
}
return { at: 0, reason: "capture opt-out record present but unreadable (kept fail-closed)" };
}
try {
statSync(path);
}
catch {
throw new ControlPlaneCorruptError(`session capture opt-out record state is INDETERMINATE (${err.code ?? "io error"}) at ${path} — the store cannot establish whether a record exists; consumers must suppress on the fault axis (an outage is never reported as a user request)`, { cause: err });
}
return { at: 0, reason: "capture opt-out record present but unreadable (kept fail-closed)" };
}
try {
const parsed = JSON.parse(raw);
if (parsed !== null && typeof parsed === "object") {
const at = parsed.at;
const reason = parsed.reason;
const incarnation = readIncarnationSeat(parsed);
if (typeof at === "number" && typeof reason === "string")
return { at, reason, ...(incarnation !== undefined ? { incarnation } : {}) };
}
}
catch {
}
return { at: 0, reason: "capture opt-out record present but unreadable (kept fail-closed)" };
}
export function listSessionCaptureOptOut(controlDir) {
const dir = join(controlDir, SESSION_CAPTURE_OPTOUT_DIR);
let names;
try {
names = readdirSync(dir);
}
catch (err) {
if (err.code === "ENOENT")
return {};
throw new ControlPlaneCorruptError(`session capture opt-out records could not be enumerated (${err.code ?? "io error"}) at ${dir} — an enumeration failure is not an empty set (fail-closed)`, { cause: err });
}
const out = Object.create(null);
for (const name of names.sort()) {
const sessionId = sessionIdOfRecordName(name);
if (sessionId === undefined || Object.prototype.hasOwnProperty.call(out, sessionId))
continue;
const rec = readCaptureOptOutAt(join(dir, name));
if (rec !== undefined)
out[sessionId] = rec;
}
return out;
}
export function fileSessionCaptureRecordStore(controlDir) {
return {
mark: (sessionId, record) => markSessionCaptureOptOut(controlDir, sessionId, record.reason, () => record.at, record.incarnation),
read: (sessionId, identity) => readSessionCaptureOptOut(controlDir, sessionId, identity),
list: () => listSessionCaptureOptOut(controlDir),
};
}
export const USAGE_RETRIEVED_FILE = "usage-retrieved.json";
export const USAGE_RETRIEVED_MAX_IDS = 4096;
function coerceRetrievedAccount(raw) {
if (!raw || typeof raw !== "object" || Array.isArray(raw))
return Object.create(null);
const out = Object.create(null);
for (const [k, v] of Object.entries(raw)) {
const row = v;
if (row && typeof row === "object" && typeof row.count === "number" && Number.isFinite(row.count) && row.count > 0 && typeof row.lastAt === "number" && Number.isFinite(row.lastAt)) {
out[k] = { count: Math.floor(row.count), lastAt: row.lastAt };
}
}
return out;
}
export function recordRetrievedAccount(controlDir, ids, now) {
if (ids.length === 0)
return;
const at = now();
lockedJournaledUpdate(controlDir, USAGE_RETRIEVED_FILE, (current) => {
const rows = coerceRetrievedAccount(current);
for (const id of ids) {
const prior = rows[id];
rows[id] = { count: (prior?.count ?? 0) + 1, lastAt: at };
}
const keys = Object.keys(rows);
if (keys.length > USAGE_RETRIEVED_MAX_IDS) {
keys.sort((a, b) => rows[a].lastAt - rows[b].lastAt || (a < b ? -1 : 1));
for (const cold of keys.slice(0, keys.length - USAGE_RETRIEVED_MAX_IDS))
delete rows[cold];
}
return rows;
});
}
export function readRetrievedAccount(controlDir) {
return coerceRetrievedAccount(readSidecarJson(controlDir, USAGE_RETRIEVED_FILE));
}
export function clearScanFuse(controlDir, keys) {
const wanted = [...keys];
if (wanted.length === 0)
return;
const file = join(controlDir, SCAN_FUSE_FILE);
if (!existsSync(file) && !existsSync(`${file}.journal`))
return;
lockedJournaledUpdate(controlDir, SCAN_FUSE_FILE, (current) => {
const counts = coerceFuse(current);
let changed = false;
for (const k of wanted) {
if (counts[k] !== undefined) {
delete counts[k];
changed = true;
}
}
return changed ? { v: 2, counts } : undefined;
});
}
export const LINEAGE_FILE = "lineage.json";
export const CHALLENGES_FILE = "challenges.json";
export const LINEAGE_AUDIT_FILE = "lineage-audit.json";
export const LINEAGE_AUDIT_MAX_ROWS = 2048;
export const CHALLENGED_HISTORY_FILE = "usage-challenged-history.json";
function rollForwardStrictSidecar(file, journal, fence) {
let raw;
try {
raw = readFileSync(journal, "utf8");
}
catch {
return;
}
try {
JSON.parse(raw);
}
catch {
throw new ControlPlaneCorruptError(`control-plane journal is unparseable (fail-closed sidecar): ${journal}`);
}
fence?.();
atomicWriteFileSync(file, raw);
fence?.();
rmSync(journal, { force: true });
}
export function lockedStrictUpdate(controlDir, fileName, what, coerce, fn) {
ensureDirExists(controlDir);
const file = join(controlDir, fileName);
const journal = `${file}.journal`;
const lock = `${file}.lock`;
const token = acquireSidecarLock(lock, { onDeadline: "throw" });
try {
rollForwardStrictSidecar(file, journal, () => assertSidecarLockOwnership(lock, token, what, "strict"));
const current = coerce(readStrictSidecarRaw(file, what));
const { next, result } = fn(current);
if (next !== undefined) {
const data = `${JSON.stringify(next, null, 2)}\n`;
const fence = () => assertSidecarLockOwnership(lock, token, what, "strict");
fence();
atomicWriteFileSync(journal, data);
fence();
atomicWriteFileSync(file, data);
fence();
rmSync(journal, { force: true });
}
return result;
}
finally {
releaseSidecarLock(lock, token);
}
}
function readStrictSidecarRaw(file, what) {
let raw;
try {
raw = readFileSync(file, "utf8");
}
catch (err) {
if (err.code === "ENOENT")
return undefined;
throw new ControlPlaneCorruptError(`${what} unreadable (${err.code ?? "io error"}): ${file}`, { cause: err });
}
try {
return JSON.parse(raw);
}
catch {
throw new ControlPlaneCorruptError(`${what} is unparseable: ${file}`);
}
}
export function readStrictSidecar(controlDir, fileName, what) {
const file = join(controlDir, fileName);
const journal = `${file}.journal`;
let journalRaw;
try {
journalRaw = readFileSync(journal, "utf8");
}
catch (err) {
if (err.code !== "ENOENT") {
throw new ControlPlaneCorruptError(`control-plane journal unreadable ((${err.code ?? "io error"})) — a committed next state may exist that cannot be proven; repair the read fault rather than serving the pre-transaction state: ${journal}`, { cause: err });
}
journalRaw = undefined;
}
if (journalRaw !== undefined) {
try {
return JSON.parse(journalRaw);
}
catch {
throw new ControlPlaneCorruptError(`control-plane journal is unparseable (fail-closed sidecar): ${journal}`);
}
}
return readStrictSidecarRaw(file, what);
}
function coerceLineage(raw) {
if (raw === undefined) {
return { version: 1, committed: Object.create(null), pending: Object.create(null) };
}
if (!raw || typeof raw !== "object" || Array.isArray(raw))
throw new ControlPlaneCorruptError("memory lineage ledger has the wrong shape");
const r = raw;
if (r.version !== 1)
throw new ControlPlaneCorruptError(`memory lineage ledger has an unrecognized version (${String(r.version)}) — refusing (fail-closed)`);
const badShape = (part) => new ControlPlaneCorruptError(`memory lineage ledger ${part} has the wrong shape`);
if (!r.committed || typeof r.committed !== "object" || Array.isArray(r.committed))
throw badShape("committed set");
if (!r.pending || typeof r.pending !== "object" || Array.isArray(r.pending))
throw badShape("pending set");
for (const [entryId, sessions] of Object.entries(r.committed)) {
if (!sessions || typeof sessions !== "object" || Array.isArray(sessions))
throw badShape(`committed[${JSON.stringify(entryId)}]`);
for (const [sid, c] of Object.entries(sessions)) {
const row = c;
if (!row || typeof row !== "object" || typeof row.lastRev !== "string" || typeof row.lastAt !== "number") {
throw badShape(`committed[${JSON.stringify(entryId)}][${JSON.stringify(sid)}]`);
}
if (row.marked !== undefined && row.marked !== true)
throw badShape(`committed[${JSON.stringify(entryId)}][${JSON.stringify(sid)}] marked`);
if (!isIncarnationSeat(row.incarnation)) {
throw badShape(`committed[${JSON.stringify(entryId)}][${JSON.stringify(sid)}] incarnation`);
}
}
}
for (const [txnId, t] of Object.entries(r.pending)) {
const txn = t;
if (!txn || typeof txn !== "object" || typeof txn.sessionId !== "string" || typeof txn.at !== "number" || !Array.isArray(txn.rows)) {
throw badShape(`pending[${JSON.stringify(txnId)}]`);
}
if (txn.kind !== undefined && txn.kind !== "latch-only")
throw badShape(`pending[${JSON.stringify(txnId)}] kind`);
if (!isIncarnationSeat(txn.incarnation))
throw badShape(`pending[${JSON.stringify(txnId)}] incarnation`);
for (const row of txn.rows) {
const p = row;
if (!p || typeof p !== "object" || typeof p.entryId !== "string" || typeof p.rev !== "string")
throw badShape(`pending[${JSON.stringify(txnId)}] row`);
if (p.kind !== undefined && p.kind !== "latch-only")
throw badShape(`pending[${JSON.stringify(txnId)}] row kind`);
if (p.marked !== undefined && p.marked !== true)
throw badShape(`pending[${JSON.stringify(txnId)}] row marked`);
}
if (txn.credential !== undefined) {
const c = txn.credential;
if (!c || typeof c !== "object" || !Array.isArray(c.appliedIds) || c.appliedIds.some((x) => typeof x !== "string") || typeof c.at !== "number") {
throw badShape(`pending[${JSON.stringify(txnId)}] credential`);
}
}
}
const committed = Object.create(null);
for (const [entryId, sessions] of Object.entries(r.committed)) {
const s = Object.create(null);
for (const [sid, c] of Object.entries(sessions))
s[sid] = c;
committed[entryId] = s;
}
const pending = Object.create(null);
for (const [txnId, t] of Object.entries(r.pending))
pending[txnId] = t;
return { version: 1, committed, pending };
}
export function stageLineagePending(controlDir, txnId, sessionId, rows, now, incarnation) {
lockedStrictUpdate(controlDir, LINEAGE_FILE, "memory lineage ledger", coerceLineage, (rec) => {
rec.pending[txnId] = { sessionId, at: now(), rows: [...rows], ...(incarnation !== undefined ? { incarnation } : {}) };
return { next: rec, result: undefined };
});
}
export function discardLineagePending(controlDir, txnId) {
lockedStrictUpdate(controlDir, LINEAGE_FILE, "memory lineage ledger", coerceLineage, (rec) => {
if (rec.pending[txnId] === undefined)
return { result: undefined };
delete rec.pending[txnId];
return { next: rec, result: undefined };
});
}
export function recordLineageCredential(controlDir, txnId, appliedIds, now) {
lockedStrictUpdate(controlDir, LINEAGE_FILE, "memory lineage ledger", coerceLineage, (rec) => {
const txn = rec.pending[txnId];
if (txn === undefined)
throw new ControlPlaneCorruptError(`memory lineage ledger: credential for unknown pending transaction ${txnId}`);
txn.credential = { appliedIds: [...appliedIds], at: now() };
return { next: rec, result: undefined };
});
}
export function promoteLineagePending(controlDir, txnId) {
return lockedStrictUpdate(controlDir, LINEAGE_FILE, "memory lineage ledger", coerceLineage, (rec) => {
const txn = rec.pending[txnId];
if (txn === undefined)
return { result: [] };
if (txn.credential === undefined) {
throw new ControlPlaneCorruptError(`memory lineage ledger: refusing to promote uncredentialed pending transaction ${txnId} (the commit outcome is not durably known)`);
}
const applied = new Set(txn.credential.appliedIds);
const at = txn.credential.at;
const promoted = [];
for (const row of txn.rows) {
if (!applied.has(row.entryId))
continue;
const sessions = (rec.committed[row.entryId] ??= Object.create(null));
sessions[txn.sessionId] = { lastRev: row.rev, lastAt: at, ...(row.marked === true ? { marked: true } : {}), ...(txn.incarnation !== undefined ? { incarnation: txn.incarnation } : {}) };
promoted.push({ entryId: row.entryId, sessionId: txn.sessionId, rev: row.rev, ...(row.marked === true ? { marked: true } : {}) });
}
delete rec.pending[txnId];
return { next: rec, result: promoted };
});
}
export function adjudicateLineagePending(controlDir, txnId, action, now) {
return lockedStrictUpdate(controlDir, LINEAGE_FILE, "memory lineage ledger", coerceLineage, (rec) => {
const txn = rec.pending[txnId];
if (txn === undefined)
return { result: [] };
if (isLatchOnlyTxn(txn)) {
throw new ControlPlaneCorruptError(`memory lineage ledger: pending transaction ${txnId} is a bundle-import latch — it cannot be ${action === "promote" ? "promoted (its rows are not contributions)" : "discarded (its withhold guards not-yet-governed imported entries)"}; re-run the same bundle import to converge it (releaseImportLatch is receipt-gated)`);
}
if (action === "discard") {
delete rec.pending[txnId];
return { next: rec, result: [] };
}
const applied = txn.credential !== undefined ? new Set(txn.credential.appliedIds) : undefined;
const at = txn.credential?.at ?? now();
const promoted = [];
for (const row of txn.rows) {
if (applied !== undefined && !applied.has(row.entryId))
continue;
const sessions = (rec.committed[row.entryId] ??= Object.create(null));
sessions[txn.sessionId] = { lastRev: row.rev, lastAt: at, ...(row.marked === true ? { marked: true } : {}), ...(txn.incarnation !== undefined ? { incarnation: txn.incarnation } : {}) };
promoted.push({ entryId: row.entryId, sessionId: txn.sessionId, rev: row.rev, ...(row.marked === true ? { marked: true } : {}) });
}
delete rec.pending[txnId];
return { next: rec, result: promoted };
});
}
export function reconcileLineage(controlDir) {
return lockedStrictUpdate(controlDir, LINEAGE_FILE, "memory lineage ledger", coerceLineage, (rec) => {
const promoted = [];
const undecidable = [];
let changed = false;
for (const [txnId, txn] of Object.entries(rec.pending)) {
if (txn.credential === undefined) {
undecidable.push({ txnId, sessionId: txn.sessionId, entryIds: txn.rows.map((r) => r.entryId), ...(isLatchOnlyTxn(txn) ? { kind: "latch-only" } : {}) });
continue;
}
const applied = new Set(txn.credential.appliedIds);
for (const row of txn.rows) {
if (!applied.has(row.entryId))
continue;
const sessions = (rec.committed[row.entryId] ??= Object.create(null));
sessions[txn.sessionId] = { lastRev: row.rev, lastAt: txn.credential.at, ...(row.marked === true ? { marked: true } : {}), ...(txn.incarnation !== undefined ? { incarnation: txn.incarnation } : {}) };
promoted.push({ entryId: row.entryId, sessionId: txn.sessionId, rev: row.rev, ...(row.marked === true ? { marked: true } : {}) });
}
delete rec.pending[txnId];
changed = true;
}
return { ...(changed ? { next: rec } : {}), result: { promoted, undecidable } };
});
}
function isLatchOnlyTxn(txn) {
if (txn.kind === "latch-only")
return true;
return txn.rows.some((row) => row.kind === "latch-only");
}
export const IMPORT_RECEIPTS_DIR = "import-receipts";
export function importLatchTxnId(bundleHash) {
return `import:${bundleHash}`;
}
function importReceiptPath(controlDir, bundleHash) {
return join(controlDir, IMPORT_RECEIPTS_DIR, `${bundleHash}.json`);
}
export function stageImportLatch(controlDir, bundleHash, rows, now) {
const txnId = importLatchTxnId(bundleHash);
lockedStrictUpdate(controlDir, LINEAGE_FILE, "memory lineage ledger", coerceLineage, (rec) => {
const existing = rec.pending[txnId];
if (existing !== undefined && !isLatchOnlyTxn(existing)) {
throw new ControlPlaneCorruptError(`memory lineage ledger: pending transaction ${txnId} exists and is NOT an import latch — refusing to overwrite a real staged transaction`);
}
rec.pending[txnId] = {
sessionId: txnId,
at: now(),
kind: "latch-only",
rows: rows.map((r) => ({ entryId: r.entryId, rev: r.rev, kind: "latch-only" })),
};
return { next: rec, result: undefined };
});
return txnId;
}
export function releaseImportLatch(controlDir, txnId) {
const prefix = "import:";
if (!txnId.startsWith(prefix) || !/^[0-9a-f]{64}$/.test(txnId.slice(prefix.length))) {
throw new ControlPlaneCorruptError(`releaseImportLatch: ${JSON.stringify(txnId)} is not an import-latch transaction id (import:<64-hex bundleHash>)`);
}
const receipt = importReceiptPath(controlDir, txnId.slice(prefix.length));
let receiptPresent;
try {
readFileSync(receipt);
receiptPresent = true;
}
catch (err) {
if (err.code !== "ENOENT") {
throw new ControlPlaneCorruptError(`releaseImportLatch: the completion receipt at ${receipt} could not be read (${err.code ?? "io error"}) — a probe failure is not absence (fail-closed), and absence would refuse the release`, { cause: err });
}
receiptPresent = false;
}
if (!receiptPresent) {
throw new ControlPlaneCorruptError(`releaseImportLatch: no completion receipt at ${receipt} — the import's governance legs are not proven landed, the latch stays; re-run the same bundle import to converge`);
}
lockedStrictUpdate(controlDir, LINEAGE_FILE, "memory lineage ledger", coerceLineage, (rec) => {
const txn = rec.pending[txnId];
if (txn === undefined)
return { result: undefined };
if (!isLatchOnlyTxn(txn)) {
throw new ControlPlaneCorruptError(`releaseImportLatch: pending transaction ${txnId} is NOT an import latch — refusing (this channel never discards a real staged transaction)`);
}
delete rec.pending[txnId];
return { next: rec, result: undefined };
});
}
export function lineageLatchedIds(controlDir) {
const rec = coerceLineage(readStrictSidecar(controlDir, LINEAGE_FILE, "memory lineage ledger"));
const out = new Set();
for (const txn of Object.values(rec.pending))
for (const row of txn.rows)
out.add(row.entryId);
return out;
}
export function lineageContributionsOfSession(controlDir, sessionId, asking) {
const rec = coerceLineage(readStrictSidecar(controlDir, LINEAGE_FILE, "memory lineage ledger"));
const out = [];
for (const [entryId, sessions] of Object.entries(rec.committed)) {
const c = Object.prototype.hasOwnProperty.call(sessions, sessionId) ? sessions[sessionId] : undefined;
if (c === undefined || !sessionRecordBindsIncarnation(c.incarnation, asking))
continue;
out.push({ entryId, lastRev: c.lastRev, lastAt: c.lastAt, ...(c.marked === true ? { marked: true } : {}) });
}
return out;
}
export function lineageAccountOfEntry(controlDir, entryId) {
const rec = coerceLineage(readStrictSidecar(controlDir, LINEAGE_FILE, "memory lineage ledger"));
const sessions = Object.prototype.hasOwnProperty.call(rec.committed, entryId) ? rec.committed[entryId] : undefined;
const contributors = sessions === undefined ? [] : Object.entries(sessions).map(([sessionId, c]) => ({ sessionId, lastRev: c.lastRev, lastAt: c.lastAt }));
let pendingLatched = false;
for (const txn of Object.values(rec.pending)) {
if (txn.rows.some((r) => r.entryId === entryId)) {
pendingLatched = true;
break;
}
}
return { contributors, pendingLatched };
}
export function clearLineageForEntries(controlDir, entryIds) {
if (entryIds.length === 0)
return;
lockedStrictUpdate(controlDir, LINEAGE_FILE, "memory lineage ledger", coerceLineage, (rec) => {
let changed = false;
for (const id of entryIds) {
if (rec.committed[id] !== undefined) {
delete rec.committed[id];
changed = true;
}
}
return { ...(changed ? { next: rec } : {}), result: undefined };
});
}
export function captureAndClearLineageForEntries(controlDir, entryIds) {
if (entryIds.length === 0)
return new Map();
return lockedStrictUpdate(controlDir, LINEAGE_FILE, "memory lineage ledger", coerceLineage, (rec) => {
const want = new Set(entryIds);
const captured = new Map();
const add = (id, sessionId) => {
const set = captured.get(id) ?? new Set();
set.add(sessionId);
captured.set(id, set);
};
let changed = false;
for (const id of entryIds) {
if (!Object.prototype.hasOwnProperty.call(rec.committed, id))
continue;
for (const sessionId of Object.keys(rec.committed[id] ?? {}))
add(id, sessionId);
delete rec.committed[id];
changed = true;
}
for (const txn of Object.values(rec.pending)) {
const keep = [];
for (const row of txn.rows) {
if (!want.has(row.entryId)) {
keep.push(row);
continue;
}
changed = true;
if (row.kind !== "latch-only")
add(row.entryId, txn.sessionId);
}
txn.rows = keep;
}
const result = new Map();
for (const [id, sessions] of captured)
result.set(id, [...sessions].sort());
return { ...(changed ? { next: rec } : {}), result };
});
}
export function importLineageCommitted(controlDir, rows) {
if (rows.length === 0)
return [];
return lockedStrictUpdate(controlDir, LINEAGE_FILE, "memory lineage ledger", coerceLineage, (rec) => {
const divergence = [];
let changed = false;
for (const row of rows) {
const sessions = (rec.committed[row.entryId] ??= Object.create(null));
const standing = Object.prototype.hasOwnProperty.call(sessions, row.sessionId) ? sessions[row.sessionId] : undefined;
if (standing === undefined || row.lastAt > standing.lastAt) {
sessions[row.sessionId] = { lastRev: row.lastRev, lastAt: row.lastAt, ...(row.marked === true ? { marked: true } : {}) };
changed = true;
continue;
}
if (row.lastAt === standing.lastAt && row.lastRev !== standing.lastRev) {
divergence.push({ entryId: row.entryId, sessionId: row.sessionId, kept: "destination" });
continue;
}
if (row.lastAt === standing.lastAt && row.lastRev === standing.lastRev && row.marked === true && standing.marked !== true) {
sessions[row.sessionId] = { ...standing, marked: true };
changed = true;
continue;
}
}
return { ...(changed ? { next: rec } : {}), result: divergence };
});
}
export function readLineageRecord(controlDir) {
const rec = coerceLineage(readStrictSidecar(controlDir, LINEAGE_FILE, "memory lineage ledger"));
return { committed: rec.committed, pending: rec.pending };
}
export function appendLineageAudit(controlDir, rows) {
if (rows.length === 0)
return;
lockedJournaledUpdate(controlDir, LINEAGE_AUDIT_FILE, (current) => {
const list = Array.isArray(current) ? current.filter((r) => r && typeof r === "object") : [];
list.push(...rows);
return list.length > LINEAGE_AUDIT_MAX_ROWS ? list.slice(-LINEAGE_AUDIT_MAX_ROWS) : list;
});
}
function coerceChallenges(raw) {
if (raw === undefined)
return { version: 1, events: [] };
if (!raw || typeof raw !== "object" || Array.isArray(raw))
throw new ControlPlaneCorruptError("memory challenge ledger has the wrong shape");
const r = raw;
if (r.version !== 1)
throw new ControlPlaneCorruptError(`memory challenge ledger has an unrecognized version (${String(r.version)}) — refusing (fail-closed)`);
if (!Array.isArray(r.events))
throw new ControlPlaneCorruptError("memory challenge ledger events list has the wrong shape");
for (const e of r.events) {
const ev = e;
if (!ev ||
typeof ev !== "object" ||
typeof ev.eventId !== "string" ||
typeof ev.entryId !== "string" ||
(ev.kind !== "challenge" && ev.kind !== "resolve") ||
typeof ev.generation !== "number" ||
typeof ev.at !== "number" ||
typeof ev.reason !== "string") {
throw new ControlPlaneCorruptError("memory challenge ledger event has the wrong shape");
}
}
return raw;
}
export const CHALLENGE_LEDGER_MAX_EVENTS = 10_000;
export function appendChallengeEvents(controlDir, events, now) {
if (events.length === 0)
return { assignments: [], eventCount: undefined };
return lockedStrictUpdate(controlDir, CHALLENGES_FILE, "memory challenge ledger", coerceChallenges, (rec) => {
const byEventId = new Map(rec.events.map((e) => [e.eventId, e]));
const maxGen = new Map();
for (const e of rec.events) {
if (e.kind !== "challenge")
continue;
maxGen.set(e.entryId, Math.max(maxGen.get(e.entryId) ?? 0, e.generation));
}
const out = [];
let changed = false;
for (const req of events) {
const existing = byEventId.get(req.eventId);
if (existing !== undefined) {
out.push({ entryId: existing.entryId, eventId: existing.eventId, generation: existing.generation, at: existing.at, replayed: true });
continue;
}
const generation = (maxGen.get(req.entryId) ?? 0) + 1;
maxGen.set(req.entryId, generation);
const ev = {
eventId: req.eventId,
entryId: req.entryId,
kind: "challenge",
generation,
at: now(),
reason: req.reason,
grade: "S",
...(req.challengedRev !== undefined ? { challengedRev: req.challengedRev } : {}),
};
rec.events.push(ev);
byEventId.set(ev.eventId, ev);
out.push({ entryId: ev.entryId, eventId: ev.eventId, generation, at: ev.at, replayed: false });
changed = true;
}
return { ...(changed ? { next: rec } : {}), result: { assignments: out, eventCount: rec.events.length } };
});
}
export function resolveChallengeEvent(controlDir, entryId, generation, reason, now, requestId) {
return lockedStrictUpdate(controlDir, CHALLENGES_FILE, "memory challenge ledger", coerceChallenges, (rec) => {
const opened = rec.events.some((e) => e.kind === "challenge" && e.entryId === entryId && e.generation === generation);
if (!opened)
return { result: { resolved: false, eventCount: undefined } };
const alreadyResolved = rec.events.some((e) => e.kind === "resolve" && e.entryId === entryId && e.generation === generation);
if (alreadyResolved)
return { result: { resolved: true, eventCount: undefined } };
rec.events.push({
eventId: requestId !== undefined && requestId !== "" ? `${requestId}:${entryId}` : `resolve:${entryId}:${generation}`,
entryId,
kind: "resolve",
generation,
at: now(),
reason,
});
return { next: rec, result: { resolved: true, eventCount: rec.events.length } };
});
}
export function challengedEntryIds(controlDir) {
const rec = coerceChallenges(readStrictSidecar(controlDir, CHALLENGES_FILE, "memory challenge ledger"));
const resolved = new Set();
for (const e of rec.events)
if (e.kind === "resolve")
resolved.add(`${e.entryId}#${e.generation}`);
const out = new Map();
for (const e of rec.events) {
if (e.kind !== "challenge" || resolved.has(`${e.entryId}#${e.generation}`))
continue;
const prior = out.get(e.entryId);
if (prior === undefined || e.generation > prior.generation)
out.set(e.entryId, { generation: e.generation, at: e.at });
}
return out;
}
export function readChallengeEvents(controlDir) {
return coerceChallenges(readStrictSidecar(controlDir, CHALLENGES_FILE, "memory challenge ledger")).events;
}
export function isStrictControlPlaneLedgerCorrupt(controlDir, ledger) {
const fileName = ledger === "lineage" ? LINEAGE_FILE : CHALLENGES_FILE;
const what = ledger === "lineage" ? "memory lineage ledger" : "memory challenge ledger";
const coerce = ledger === "lineage" ? coerceLineage : coerceChallenges;
try {
coerce(readStrictSidecar(controlDir, fileName, what));
return false;
}
catch (err) {
if (err instanceof ControlPlaneCorruptError)
return true;
throw err;
}
}
export function rebuildStrictControlPlaneLedger(controlDir, ledger, now) {
const fileName = ledger === "lineage" ? LINEAGE_FILE : CHALLENGES_FILE;
const what = ledger === "lineage" ? "memory lineage ledger" : "memory challenge ledger";
const coerce = ledger === "lineage" ? coerceLineage : coerceChallenges;
const empty = ledger === "lineage" ? { version: 1, committed: {}, pending: {} } : { version: 1, events: [] };
ensureDirExists(controlDir);
const file = join(controlDir, fileName);
const journal = `${file}.journal`;
const lock = `${file}.lock`;
const token = acquireSidecarLock(lock, { onDeadline: "throw" });
try {
let healthy = false;
try {
coerce(readStrictSidecar(controlDir, fileName, what));
healthy = true;
}
catch (err) {
if (!(err instanceof ControlPlaneCorruptError))
throw err;
}
if (healthy) {
const e = new Error(`${what} reads cleanly — refusing to rebuild it. This call exists to recover a ledger whose corruption already refuses every read; resetting a healthy one would drop taint evidence that is still doing its job.`);
e.code = "memory.control_plane_not_corrupt";
throw e;
}
const at = now();
const quarantinedTo = [];
assertSidecarLockOwnership(lock, token, what, "strict");
for (const path of [file, journal]) {
if (!existsSync(path))
continue;
let dest = `${path}.corrupt-${at}`;
for (let n = 2;; n++) {
try {
copyFileSync(path, dest, fsConstants.COPYFILE_EXCL);
break;
}
catch (err) {
if (err.code !== "EEXIST")
throw err;
dest = `${path}.corrupt-${at}-${n}`;
}
}
quarantinedTo.push(dest);
}
assertSidecarLockOwnership(lock, token, what, "strict");
atomicWriteFileSync(file, `${JSON.stringify(empty, null, 2)}\n`);
rmSync(journal, { force: true });
return { ledger, quarantinedTo, at };
}
finally {
releaseSidecarLock(lock, token);
}
}
function coerceChallengedHistory(raw) {
if (!raw || typeof raw !== "object" || Array.isArray(raw))
return {};
const out = {};
for (const [k, v] of Object.entries(raw)) {
const row = v;
if (row &&
typeof row === "object" &&
typeof row.count === "number" &&
Number.isFinite(row.count) &&
row.count > 0 &&
typeof row.lastAt === "number" &&
(row.lastOp === "update" || row.lastOp === "tombstone")) {
out[k] = { count: Math.floor(row.count), lastAt: row.lastAt, lastOp: row.lastOp };
}
}
return out;
}
export function recordChallengedHistory(controlDir, rows, now) {
if (rows.length === 0)
return;
const at = now();
lockedJournaledUpdate(controlDir, CHALLENGED_HISTORY_FILE, (current) => {
const acc = coerceChallengedHistory(current);
for (const r of rows) {
const prior = acc[r.entryId];
acc[r.entryId] = { count: (prior?.count ?? 0) + 1, lastAt: at, lastOp: r.op };
}
return acc;
});
}
export function readChallengedHistory(controlDir) {
return coerceChallengedHistory(readSidecarJson(controlDir, CHALLENGED_HISTORY_FILE));
}
export const PROJECTION_DEBTS_FILE = "projection-debts.json";
function safeLedgerRel(rel) {
return typeof rel === "string" && rel.length > 0 && !rel.startsWith("/") && !rel.split(/[\\/]/).includes("..");
}
function malformedDebtRow(row) {
return (typeof row !== "object" ||
row === null ||
typeof row.entryId !== "string" ||
row.entryId.length === 0 ||
typeof row.rev !== "string" ||
row.rev.length === 0 ||
typeof row.at !== "number" ||
!Number.isFinite(row.at));
}
function coerceProjectionDebts(raw) {
if (raw === undefined)
return { version: 2, rows: [] };
const rec = raw;
if (typeof rec !== "object" || rec === null || (rec.version !== 1 && rec.version !== 2) || !Array.isArray(rec.rows)) {
throw new ControlPlaneCorruptError("memory projection-debt ledger has the wrong shape — refusing (fail-closed; an unreadable debt account must not read as 'no debts')");
}
const bad = () => {
throw new ControlPlaneCorruptError("memory projection-debt ledger: a row is malformed — refusing (fail-closed)");
};
const rows = rec.rows;
if (rec.version === 1) {
for (const r of rows) {
const row = r;
if (malformedDebtRow(row) || !safeLedgerRel(row.relPath))
bad();
}
return { version: 1, rows: rows };
}
for (const r of rows) {
const row = r;
if (malformedDebtRow(row) || typeof row.scope !== "string" || row.scope.length === 0 || !safeLedgerRel(row.rel))
bad();
}
const unresolved = rec.unresolved === undefined ? undefined : Array.isArray(rec.unresolved) ? rec.unresolved : bad();
for (const r of unresolved ?? []) {
const row = r;
if (malformedDebtRow(row) || !safeLedgerRel(row.relPath))
bad();
}
return { version: 2, rows: rows, ...(unresolved !== undefined ? { unresolved: unresolved } : {}) };
}
function convertProjectionDebts(read, controlDir) {
if (read.version === 2)
return { rec: read, converted: false };
const resolve = v1MemoryPathKeyResolver(controlDir);
const rows = [];
const unresolved = [];
for (const r of read.rows) {
const key = resolve(r.relPath);
if (key === undefined)
unresolved.push(r);
else
rows.push({ scope: key.scope, rel: key.rel, entryId: r.entryId, rev: r.rev, at: r.at });
}
return { rec: { version: 2, rows, ...(unresolved.length > 0 ? { unresolved } : {}) }, converted: true };
}
function debtSeatKey(scope, rel) {
return JSON.stringify([scope, rel]);
}
export function stageProjectionDebts(controlDir, rows, now) {
if (rows.length === 0)
return { refused: [] };
const at = now();
return lockedStrictUpdate(controlDir, PROJECTION_DEBTS_FILE, "memory projection-debt ledger", coerceProjectionDebts, (read) => {
const { rec } = convertProjectionDebts(read, controlDir);
const bySeat = new Map(rec.rows.map((r) => [debtSeatKey(r.scope, r.rel), r]));
const refused = [];
for (const r of rows) {
const seat = debtSeatKey(r.scope, r.rel);
const existing = bySeat.get(seat);
if (existing !== undefined && existing.entryId !== r.entryId && existing.entryId !== r.replaces) {
refused.push({ scope: r.scope, rel: r.rel, entryId: r.entryId });
continue;
}
bySeat.set(seat, { scope: r.scope, rel: r.rel, entryId: r.entryId, rev: r.rev, at });
}
return { next: { ...rec, version: 2, rows: [...bySeat.values()] }, result: { refused } };
});
}
export function settleProjectionDebts(controlDir, rows) {
if (rows.length === 0)
return;
const drop = new Set(rows.map((r) => JSON.stringify([r.entryId, r.scope, r.rel])));
lockedStrictUpdate(controlDir, PROJECTION_DEBTS_FILE, "memory projection-debt ledger", coerceProjectionDebts, (read) => {
const { rec, converted } = convertProjectionDebts(read, controlDir);
const kept = rec.rows.filter((r) => !drop.has(JSON.stringify([r.entryId, r.scope, r.rel])));
if (kept.length === rec.rows.length && !converted)
return { result: undefined };
return { next: { ...rec, version: 2, rows: kept }, result: undefined };
});
}
export function readProjectionDebts(controlDir) {
const read = coerceProjectionDebts(readStrictSidecar(controlDir, PROJECTION_DEBTS_FILE, "memory projection-debt ledger"));
if (read.version === 2)
return read.rows;
return lockedStrictUpdate(controlDir, PROJECTION_DEBTS_FILE, "memory projection-debt ledger", coerceProjectionDebts, (again) => {
const { rec, converted } = convertProjectionDebts(again, controlDir);
return converted ? { next: rec, result: rec.rows } : { result: rec.rows };
});
}
export function unresolvedProjectionDebts(controlDir) {
const read = coerceProjectionDebts(readStrictSidecar(controlDir, PROJECTION_DEBTS_FILE, "memory projection-debt ledger"));
if (read.version === 1)
return read.rows.map((r) => ({ relPath: r.relPath, entryId: r.entryId }));
return (read.unresolved ?? []).map((r) => ({ relPath: r.relPath, entryId: r.entryId }));
}
export function writeAllSync(fd, data) {
const buf = Buffer.from(data, "utf8");
let written = 0;
while (written < buf.length) {
const n = writeSync(fd, buf, written, buf.length - written);
if (n <= 0)
throw new Error(`writeAllSync: write made no progress (${written}/${buf.length} bytes) — refusing to leave a truncated file`);
written += n;
}
}
export function atomicWriteFileSync(path, data) {
ensureDirExists(dirname(path));
const tmp = join(dirname(path), `.tmp-${process.pid}-${Math.random().toString(36).slice(2, 10)}`);
const fd = openSync(tmp, "w", 0o600);
try {
writeAllSync(fd, data);
fsyncSync(fd);
}
finally {
closeSync(fd);
}
try {
renameSync(tmp, path);
}
catch (err) {
try {
unlinkSync(tmp);
}
catch {
}
throw err;
}
}
export function writeIfChanged(path, data) {
let current;
try {
current = readFileSync(path, "utf8");
}
catch {
current = undefined;
}
if (current === data)
return;
atomicWriteFileSync(path, data);
}