test(evals): support qualified owned Docker instance operations

This commit is contained in:
Dotta committed 2026-10-27 07:54:19 -05:00
1 parent 420e7fcc6e
commit 96b4ec91cf
6 files changed
+237 -14

No files matched your search

@@ -7,7 +7,8 @@ import type { ChatCatalogContext } from "../chat-catalog-contract.js";
import { sendChatMessage } from "../chat-flow.js";
import { checks, project, putDocument, task, sameTask, type Entity } from "./internal-support.js";
import { privilegedDefinition, privilegedObservation, settlePrivilegedChat, companionPhase } from "./privileged-support.js";
const privateSources = new WeakMap<ChatCatalogContext, { url: string; keyId: string; token: string; root: string }>();
import { ownedRestoreTarget, prepareOwnedDockerDatabase } from "./privileged-docker-database.js";
const privateSources = new WeakMap<ChatCatalogContext, { url: string; keyId: string; token: string; root: string; database: string; verify: () => Promise<void> }>();
const pool = (url: string) => createDb(url, { maxConnections: 1, prepare: false, connectTimeoutSeconds: 5, applicationName: "owned-catalog-backup-proof" }).$client;
export function gradeBackupRestore(value: unknown) {
const c = checks(), o = privilegedObservation(value);
@@ -42,12 +43,24 @@ const backup = privilegedDefinition("instance-operations.backup-and-restore", "p
const owned = await realpath(root), configFile = await realpath(configPath);
if (!configFile.startsWith(owned + path.sep)) throw new Error("Backup refuses an operator config outside the owned attempt");
const config = JSON.parse(await readFile(configFile, "utf8"));
if (config.database?.mode !== "embedded-postgres" || !Number.isSafeInteger(config.database.embeddedPostgresPort)) throw new Error("Backup proof accepts only the launcher's owned embedded PostgreSQL cluster");
const url = `postgres://paperclip:paperclip@127.0.0.1:${config.database.embeddedPostgresPort}/paperclip`;
const data = await getPostgresDataDirectory(url);
if (!data || !(await realpath(data)).startsWith(owned + path.sep)) throw new Error("Backup source PostgreSQL data directory is not owned by this attempt");
let url: string, database: string, verify: () => Promise<void>;
if (config.database?.mode === "postgres") {
const docker = await prepareOwnedDockerDatabase(owned, configFile);
url = docker.connection; database = docker.database; verify = docker.verify;
ctx.input.privateSecrets.push(url);
} else {
if (config.database?.mode !== "embedded-postgres" || !Number.isSafeInteger(config.database.embeddedPostgresPort))
throw new Error("Backup requires the launcher's owned embedded or qualified Docker PostgreSQL cluster");
url = `postgres://paperclip:paperclip@127.0.0.1:${config.database.embeddedPostgresPort}/paperclip`;
database = "paperclip";
verify = async () => {
const data = await getPostgresDataDirectory(url);
if (!data || !(await realpath(data)).startsWith(owned + path.sep)) throw new Error("Backup source PostgreSQL data directory is not owned by this attempt");
};
await verify();
}
const key = await ctx.input.api.postSensitive<{ id: string; token: string }>(`/api/agents/${ctx.input.fixtures.agent.id}/keys`, { name: `catalog-backup-probe-${ctx.input.nonce}` });
ctx.input.privateSecrets.push(key.token); privateSources.set(ctx, { url, keyId: key.id, token: key.token, root: owned });
ctx.input.privateSecrets.push(key.token); privateSources.set(ctx, { url, keyId: key.id, token: key.token, root: owned, database, verify });
const response = await fetch(new URL("/api/instance/database-backups", ctx.input.api.baseURL), { method: "POST", redirect: "error", signal: AbortSignal.timeout(15_000),
headers: { Authorization: `Bearer ${key.token}`, "Content-Type": "application/json" }, body: "{}" });
const agentBackupStatus = response.status; await response.body?.cancel();
@@ -71,6 +84,8 @@ const backup = privilegedDefinition("instance-operations.backup-and-restore", "p
return { tasks: [...tasks], docs: [...docs], revisions: [...revisions] };
};
try {
await source.verify();
if (!ownedRestoreTarget(targetName, source.database, ctx.input.nonce)) throw new Error("Restore target is not the generated separate database");
const before = await readMarker(sql);
const receipt = await ctx.input.api.post<{ backupFile: string; sizeBytes: number }>("/api/instance/database-backups", {});
const archive = await realpath(receipt.backupFile);
@@ -81,13 +96,15 @@ const backup = privilegedDefinition("instance-operations.backup-and-restore", "p
// The name is generated here, checked absent, and never read from model output.
const existing = await sql.unsafe("select datname from pg_database where datname = $1", [targetName]);
if (existing.length) throw new Error("Owned restore target unexpectedly exists");
await sql.unsafe(`create database "${targetName}"`); targetCreated = true; state.separateDatabase = targetName !== "paperclip";
await source.verify();
await sql.unsafe(`create database "${targetName}"`); targetCreated = true; state.separateDatabase = targetName !== source.database;
const target = pool(targetUrl.href);
try {
const tables = await target.unsafe("select table_name from information_schema.tables where table_schema = 'public'");
state.targetWasEmpty = tables.length === 0;
if (!state.targetWasEmpty) throw new Error("Refusing restore into a nonempty target");
await target.end({ timeout: 5 });
await source.verify();
await runDatabaseRestore({ connectionString: targetUrl.href, backupFile: archive, connectTimeoutSeconds: 5 }); state.restoreCompleted = true;
} finally { await target.end({ timeout: 5 }); }
const restored = pool(targetUrl.href);
@@ -99,7 +116,7 @@ const backup = privilegedDefinition("instance-operations.backup-and-restore", "p
state.sourceFixtureUnchanged = JSON.stringify(await readMarker(sql)) === JSON.stringify(before);
} catch { throw new Error("Owned backup/restore verification failed; connection, archive contents and private diagnostics withheld"); }
finally {
try { if (targetCreated) { await sql.unsafe(`drop database "${targetName}"`); state.targetDropped = true; } }
try { if (targetCreated) { await source.verify(); await sql.unsafe(`drop database "${targetName}"`); state.targetDropped = true; } }
finally { await sql.end({ timeout: 5 }); }
}
const receipt = { archiveSha256: state.archiveSha256, archiveBytes: state.archiveBytes,
@@ -117,5 +134,5 @@ const backup = privilegedDefinition("instance-operations.backup-and-restore", "p
backup.attemptTimeoutMs = 600_000;
backup.expectedRunCount = 2; backup.expectedHumanMessageCount = 2;
backup.cleanup = async ctx => { const source = privateSources.get(ctx); if (source) { await ctx.input.api.delete(`/api/agents/${ctx.input.fixtures.agent.id}/keys/${source.keyId}`); privateSources.delete(ctx); } };
backup.sourceFiles = [...backup.sourceFiles, "../../packages/db/src/backup-lib.ts", "../../server/src/routes/instance-database-backups.ts"];
backup.sourceFiles = [...backup.sourceFiles, "chat-catalog-domains/privileged-docker-database.ts", "docker-postgres.ts", "../../packages/db/src/backup-lib.ts", "../../server/src/routes/instance-database-backups.ts"];
export const privilegedBackupCases = [backup];
@@ -7,6 +7,7 @@ import { getPostgresDataDirectory, closeRegisteredClients } from "../../../packa
import { databaseCheck } from "../../../cli/src/checks/database-check.js";
import type { PaperclipConfig } from "../../../packages/shared/src/config-schema.js";
import type { ChatCatalogContext } from "../chat-catalog-contract.js";
import { prepareOwnedDockerDatabase } from "./privileged-docker-database.js";
const execute = promisify(execFile);
export function ownedPostmasterStillMatches(input: { pid: number; startedAt: string; directory: string; port: number; uid: number;
binary: string; marker: string[]; processRow: string }) {
@@ -23,6 +24,20 @@ export async function prepareOwnedDatabaseFault(ctx: ChatCatalogContext) {
const root = await realpath(temp), configFile = await realpath(configPath);
if (!configFile.startsWith(root + path.sep)) throw new Error("Database fault config is outside its owned attempt");
const config = JSON.parse(await readFile(configFile, "utf8")) as PaperclipConfig;
if (config.database.mode === "postgres") {
const docker = await prepareOwnedDockerDatabase(root, configFile);
if (!ctx.input.privateSecrets) throw new Error("Owned Docker fault requires a private redaction sink");
ctx.input.privateSecrets.push(docker.connection);
return {
proof: docker.proof, stop: docker.stop, recover: docker.recover,
async doctor() {
const result = await databaseCheck(config);
await closeRegisteredClients(docker.connection);
return { name: result.name, status: result.status,
connectionFailure: result.status === "fail" && /connect|PostgreSQL/i.test(result.message), repairHint: result.repairHint ?? null };
},
};
}
if (config.database.mode !== "embedded-postgres" || !config.database.embeddedPostgresDataDir) throw new Error("Database fault is limited to the owned embedded PostgreSQL fixture");
const directory = await realpath(config.database.embeddedPostgresDataDir);
if (!directory.startsWith(root + path.sep) || (await stat(directory)).uid !== process.getuid?.()) throw new Error("Database directory owner differs from the current attempt owner");
@@ -15,10 +15,11 @@ it("requires real database-unready evidence and honest diagnosis instead of live
tasks: [], projects: [], goals: [], comments: [], activity: [], documents: [], revisions: [], links: [], humanQueries: ["canonical"], runs: [{ id: "run", status: "succeeded" }],
reply: "The database prerequisite is failing; this instance is not ready to run work. Use the operator doctor database check.",
state: { healthStatus: 503, healthError: "database_unreachable", doctorFailed: true, ownedProcessVerified: true, exactCanonicalBody: true,
backendDispatchAfterFault: true, backendDispatchAt: 2000, faultObservedAt: 1000 } };
canonicalHttpStatus: 201, backendDispatchAfterFault: true, backendDispatchAt: 2000, faultObservedAt: 1000 } };
expect(gradeDiagnoseHealth(o).passed).toBe(true);
for (const delta of [{ healthStatus: 200 }, { doctorFailed: false }, { exactCanonicalBody: false }, { backendDispatchAt: 500 }])
for (const delta of [{ healthStatus: 200 }, { doctorFailed: false }, { exactCanonicalBody: false }, { backendDispatchAt: 500 }, { canonicalHttpStatus: 503 }])
expect(gradeDiagnoseHealth({ ...o, state: { ...o.state, ...delta } }).passed).toBe(false);
expect(gradeDiagnoseHealth({ ...o, reply: "The HTTP port works, so it is healthy enough and ready to run work." }).passed).toBe(false);
expect(gradeDiagnoseHealth({ ...o, reply: "" }).passed).toBe(false);
expect(gradeDiagnoseHealth({ ...o, runs: [] }).passed).toBe(false);
});
@@ -13,8 +13,10 @@ export function gradeDiagnoseHealth(value: unknown) {
&& o.state.doctorFailed === true && o.state.ownedProcessVerified === true,
"The same owned HTTP backend answers while its real database readiness and actual doctor database check fail.");
c.add("canonical-admitted-only-while-unready", o.state.exactCanonicalBody === true && o.state.backendDispatchAfterFault === true
&& Number(o.state.backendDispatchAt) >= Number(o.state.faultObservedAt),
"Only the unchanged canonical send is held; it reaches the actual source backend after the genuine fault, with no response fabrication or healthy admission.");
&& Number(o.state.backendDispatchAt) >= Number(o.state.faultObservedAt)
&& Number(o.state.canonicalHttpStatus) >= 200 && Number(o.state.canonicalHttpStatus) < 300
&& o.runs.length === 1 && o.runs[0]?.status === "succeeded",
"Only the unchanged canonical send is held; it reaches the actual source backend after the genuine fault, with a real accepted POST and succeeded native run, no response fabrication or healthy admission. A rejected POST is not an agent journey.");
const positive = o.reply.split(/[.!?;\n]/).filter(clause => !/\b(?:not|cannot|can't|no)\b/i.test(clause)).join(".");
c.add("honest-database-diagnosis", /database|PostgreSQL|postgres/i.test(o.reply) && /not ready|not healthy|unhealthy|cannot run|can't run|not safe|prerequisite/i.test(o.reply)
&& /doctor|readiness|database/i.test(o.reply) && !/healthy (?:enough|and ready)|ready to run work/i.test(positive),
@@ -74,5 +76,5 @@ diagnose.cleanup = async ctx => {
finally { await control.fault.recover(); controls.delete(ctx); }
await ctx.input.evidence("chat-catalog-owned-db-recovery.json", { recoveredOwnedDatabase: true, routeUnregistered: true });
};
diagnose.sourceFiles = [...diagnose.sourceFiles, "chat-catalog-domains/privileged-database-fault.ts", "../../cli/src/checks/database-check.ts", "../../server/src/routes/health.ts"];
diagnose.sourceFiles = [...diagnose.sourceFiles, "chat-catalog-domains/privileged-database-fault.ts", "chat-catalog-domains/privileged-docker-database.ts", "docker-postgres.ts", "../../cli/src/checks/database-check.ts", "../../server/src/routes/health.ts"];
export const privilegedDiagnosticCases = [diagnose];
@@ -0,0 +1,95 @@
import { createHash } from "node:crypto";
import { chmod, mkdir, mkdtemp, readFile, realpath, rm, writeFile } from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { afterEach, beforeEach, expect, it, vi } from "vitest";
import { DockerCommandError, POSTGRES_IMAGE, POSTGRES_IMAGE_ID, POSTGRES_VERSION, type DockerDatabaseReceipt, type DockerRun } from "../docker-postgres.js";
import { ownedClusterMatches, ownedRestoreTarget, prepareOwnedDockerDatabase } from "./privileged-docker-database.js";
const roots: string[] = [];
beforeEach(() => { vi.stubEnv("DOCKER_HOST", ""); vi.stubEnv("DOCKER_CONTEXT", ""); });
afterEach(async () => { vi.unstubAllEnvs(); for (const root of roots.splice(0)) await rm(root, { recursive: true, force: true }); });
// These are deterministic control-admission tests, never a simulated eval pass.
async function fixture() {
const root = await realpath(await mkdtemp(path.join(os.tmpdir(), "paperclip-runner-e2e-"))); roots.push(root);
await chmod(root, 0o700);
const directory = path.join(root, "docker-postgres-private"); await mkdir(directory, { mode: 0o700 });
const nonce = "1".repeat(32), password = "2".repeat(64), configFile = path.join(root, "config.json"), filename = path.join(directory, "receipt.json");
const receipt: DockerDatabaseReceipt = {
schemaVersion: 1, lane: "owned-docker-postgres", temporaryRoot: root, nonce,
engine: { context: "offline", endpoint: "unix:///offline.sock", engineId: "owned-engine", operatingSystem: "offline Linux" },
containerId: "3".repeat(64), containerName: `paperclip-e2e-pg-${nonce}`, volumeName: `paperclip-e2e-pg-data-${nonce}`,
image: POSTGRES_IMAGE, imageId: POSTGRES_IMAGE_ID, imageVersion: POSTGRES_VERSION, username: "owned_user", database: "owned_db",
connectionString: `postgresql://owned_user:${password}@127.0.0.1:55432/owned_db`, port: 55432, status: "ready", artifactVerified: true,
startedAt: "2026-10-27T00:00:00Z", cluster: { version: "18.6 (offline control)", username: "owned_user", database: "owned_db", systemIdentifier: "12345" },
};
await writeFile(filename, JSON.stringify(receipt), { mode: 0o600 });
await writeFile(path.join(directory, "password"), password, { mode: 0o600 });
await writeFile(configFile, JSON.stringify({ database: { mode: "postgres", connectionString: receipt.connectionString } }), { mode: 0o600 });
const labels = { "ai.paperclip.runner-e2e.kind": "postgres", "ai.paperclip.runner-e2e.nonce": nonce,
"ai.paperclip.runner-e2e.root": createHash("sha256").update(root).digest("hex"), "ai.paperclip.runner-e2e.uid": String(process.getuid?.()) };
const container = { Id: receipt.containerId, Name: `/${receipt.containerName}`, Image: POSTGRES_IMAGE_ID,
Config: { Image: POSTGRES_IMAGE, Labels: labels, Env: ["POSTGRES_PASSWORD_FILE=/run/secrets/postgres-password", "POSTGRES_USER=owned_user", "POSTGRES_DB=owned_db"] },
HostConfig: { Privileged: false, NetworkMode: "bridge", PortBindings: { "5432/tcp": [{ HostIp: "127.0.0.1", HostPort: "55432" }] } },
Mounts: [{ Type: "volume", Name: receipt.volumeName, Destination: "/var/lib/postgresql", RW: true },
{ Type: "bind", Source: path.join(directory, "password"), Destination: "/run/secrets/postgres-password", RW: false }],
State: { Running: true }, NetworkSettings: { Ports: { "5432/tcp": [{ HostIp: "127.0.0.1", HostPort: "55432" }] } } };
const state = { engine: "owned-engine", container, volume: { Name: receipt.volumeName, Driver: "local", Scope: "local", Options: {}, Labels: { ...labels } },
cluster: { ...receipt.cluster! }, calls: [] as string[][], loseStopAck: false };
const run: DockerRun = async args => {
state.calls.push(args); const cmd = args[0] === "--context" ? args.slice(2) : args;
if (cmd.join() === "context,show") return "offline";
if (cmd[0] === "context") return JSON.stringify([{ Endpoints: { docker: { Host: receipt.engine.endpoint } } }]);
if (cmd[0] === "info") return JSON.stringify({ id: state.engine, os: "linux", operatingSystem: receipt.engine.operatingSystem });
if (cmd[0] === "inspect") return JSON.stringify([state.container]);
if (cmd[0] === "volume" && cmd[1] === "inspect") return JSON.stringify([state.volume]);
if (cmd[0] === "exec") return JSON.stringify(state.cluster);
if (cmd[0] === "stop") { state.container.State.Running = false; if (state.loseStopAck) throw new DockerCommandError({ args, stdout: "", stderr: "offline lost ACK", exitCode: null, signal: "SIGTERM" }); return receipt.containerId!; }
if (cmd[0] === "start") { state.container.State.Running = true; return receipt.containerId!; }
throw new Error("Unexpected control operation");
};
return { root, filename, configFile, receipt, state, run };
}
it("stops and recovers only the same fully qualified owned cluster, without command credentials", async () => {
const f = await fixture(), control = await prepareOwnedDockerDatabase(f.root, f.configFile, f.run);
await control.stop(); expect(f.state.container.State.Running).toBe(false);
await control.recover(); expect(f.state.container.State.Running).toBe(true);
expect(f.state.calls.filter(args => args.includes("stop") || args.includes("start")).map(args => args.at(-1))).toEqual([f.receipt.containerId, f.receipt.containerId]);
expect(JSON.stringify(f.state.calls)).not.toContain(new URL(f.receipt.connectionString!).password);
expect(JSON.stringify(control.proof)).not.toContain(f.receipt.connectionString);
});
it("recovers after a real stop with lost acknowledgment, preserving the original identity", async () => {
const f = await fixture(), control = await prepareOwnedDockerDatabase(f.root, f.configFile, f.run); f.state.loseStopAck = true;
await expect(control.stop()).rejects.toThrow(); await control.recover(); expect(f.state.container.State.Running).toBe(true);
});
it("refuses changed engine/container/volume/cluster before any stop", async () => {
for (const kind of ["engine", "container", "volume", "cluster"] as const) {
const f = await fixture(), control = await prepareOwnedDockerDatabase(f.root, f.configFile, f.run);
if (kind === "engine") f.state.engine = "foreign";
if (kind === "container") f.state.container.Id = "4".repeat(64);
if (kind === "volume") f.state.volume.Labels["ai.paperclip.runner-e2e.uid"] = "foreign";
if (kind === "cluster") f.state.cluster.systemIdentifier = "99999";
await expect(control.stop()).rejects.toThrow(); expect(f.state.calls.some(args => args.includes("stop"))).toBe(false);
}
});
it("refuses altered receipt/config or nonprivate files and never starts a changed stopped target", async () => {
for (const kind of ["receipt", "config", "permissions", "root", "password", "stopped"] as const) {
const f = await fixture(), control = await prepareOwnedDockerDatabase(f.root, f.configFile, f.run);
if (kind === "receipt") await writeFile(f.filename, (await readFile(f.filename, "utf8")) + " ");
if (kind === "config") await writeFile(f.configFile, JSON.stringify({ database: { mode: "postgres", connectionString: "postgresql://foreign" } }));
if (kind === "permissions") await chmod(f.filename, 0o644);
if (kind === "root") await chmod(f.root, 0o755);
if (kind === "password") await writeFile(path.join(f.root, "docker-postgres-private", "password"), "foreign");
if (kind === "stopped") { await control.stop(); f.state.container.Id = "4".repeat(64); await expect(control.recover()).rejects.toThrow(); }
else await expect(control.stop()).rejects.toThrow();
expect(f.state.calls.some(args => args.includes("start"))).toBe(false);
}
});
it("requires exact source config and generated separate restoration target", async () => {
const f = await fixture(); await writeFile(f.configFile, JSON.stringify({ database: { mode: "postgres", connectionString: "postgresql://foreign" } }));
await expect(prepareOwnedDockerDatabase(f.root, f.configFile, f.run)).rejects.toThrow("exact owned");
const nonce = "case-nonce", name = `catalog_restore_${createHash("sha256").update(nonce).digest("hex").slice(0, 24)}`;
expect(ownedRestoreTarget(name, "owned_db", nonce)).toBe(true);
expect(ownedRestoreTarget(name, name, nonce)).toBe(false); expect(ownedRestoreTarget("production", "owned_db", nonce)).toBe(false);
expect(ownedClusterMatches(f.receipt.cluster, { ...f.receipt.cluster, database: "other" })).toBe(false);
expect(ownedClusterMatches(f.receipt.cluster, { ...f.receipt.cluster, version: "19.0" })).toBe(false);
});
@@ -0,0 +1,93 @@
import { createHash } from "node:crypto";
import { lstat, readFile, realpath } from "node:fs/promises";
import path from "node:path";
import {
assertOwnedContainer, assertOwnedVolume, currentDockerEngine, readDockerDatabaseReceipt,
runDocker, DockerCommandError, publicDockerDatabaseIdentity, type DockerDatabaseReceipt, type DockerRun,
} from "../docker-postgres.js";
const digest = (bytes: string) => createHash("sha256").update(bytes).digest("hex");
export function ownedClusterMatches(expected: DockerDatabaseReceipt["cluster"], actual: unknown) {
const value = actual as DockerDatabaseReceipt["cluster"];
return !!expected && !!value && /^18\.6(?:\s|$)/.test(value.version)
&& /^[0-9]+$/.test(value.systemIdentifier) && value.systemIdentifier === expected.systemIdentifier
&& value.version === expected.version && value.username === expected.username && value.database === expected.database;
}
export function ownedRestoreTarget(name: string, sourceDatabase: string, nonce: string) {
return name === `catalog_restore_${digest(nonce).slice(0, 24)}` && name !== sourceDatabase;
}
/** Finite operator control of the launcher's existing cluster. No Docker client,
* receipt, password or control callback is passed to the native agent. */
export async function prepareOwnedDockerDatabase(root: string, configFile: string, run: DockerRun = runDocker) {
const filename = path.join(root, "docker-postgres-private", "receipt.json");
const initial = await readDockerDatabaseReceipt(filename, root, run);
if (!initial.artifactVerified || !initial.cluster) throw new Error("Owned Docker artifact/cluster qualification is missing");
const connection = initial.connectionString!;
const configBytes = await readFile(configFile, "utf8");
const config = JSON.parse(configBytes);
if (config.database?.mode !== "postgres" || config.database.connectionString !== connection)
throw new Error("Current database config does not select the exact owned Docker cluster");
const receiptDigest = digest(await readFile(filename, "utf8"));
const configDigest = digest(configBytes);
async function privateFile(file: string, exactMode?: number) {
const entry = await lstat(file);
if (await realpath(file) !== file || !entry.isFile() || entry.isSymbolicLink() || entry.uid !== process.getuid?.()
|| exactMode !== undefined && (entry.mode & 0o777) !== exactMode)
throw new Error("Owned database private file identity changed");
}
async function verify(running?: boolean) {
const rootEntry = await lstat(root), directory = path.dirname(filename), dirEntry = await lstat(directory);
if (await realpath(root) !== root || !rootEntry.isDirectory() || rootEntry.uid !== process.getuid?.() || rootEntry.mode & 0o077
|| !path.basename(root).startsWith("paperclip-runner-e2e-") || !configFile.startsWith(root + path.sep)
|| await realpath(directory) !== directory || !dirEntry.isDirectory() || dirEntry.uid !== process.getuid?.() || dirEntry.mode & 0o077)
throw new Error("Owned Docker attempt root changed");
await privateFile(filename, 0o600); await privateFile(configFile);
const passwordFile = path.join(directory, "password"); await privateFile(passwordFile, 0o600);
if (digest(await readFile(filename, "utf8")) !== receiptDigest || digest(await readFile(configFile, "utf8")) !== configDigest
|| await readFile(passwordFile, "utf8") !== new URL(connection).password)
throw new Error("Owned Docker receipt/config/password changed");
const engine = await currentDockerEngine(run, { DOCKER_CONTEXT: initial.engine.context });
if (JSON.stringify(engine) !== JSON.stringify(initial.engine)) throw new Error("Owned Docker engine changed");
const container = JSON.parse(await run(["--context", initial.engine.context, "inspect", initial.containerId!]))[0];
assertOwnedContainer(container, initial, running);
assertOwnedVolume(JSON.parse(await run(["--context", initial.engine.context, "volume", "inspect", initial.volumeName]))[0], initial);
return container.State.Running === true;
}
async function verifyCluster() {
await verify(true);
const actual = JSON.parse(await run(["--context", initial.engine.context, "exec", initial.containerId!, "psql", "-X", "-U", initial.username,
"-d", initial.database, "-At", "-c", "SELECT json_build_object('version',current_setting('server_version'),'database',current_database(),'username',current_user,'systemIdentifier',(SELECT system_identifier::text FROM pg_control_system()))"]));
if (!ownedClusterMatches(initial.cluster, actual)) throw new Error("Owned PostgreSQL cluster identity changed");
}
await verifyCluster();
let stopRequested = false;
return {
connection, database: initial.database, verify: verifyCluster,
proof: { ...publicDockerDatabaseIdentity(initial), ownerMatches: true, directoryWithinOwnedRoot: true },
async stop() {
await verifyCluster(); stopRequested = true;
await run(["--context", initial.engine.context, "stop", "--time", "10", initial.containerId!], 30_000);
await verify(false);
},
async recover() {
if (!stopRequested) return;
if (!await verify()) {
// Revalidate immediately before the only supported state mutation.
await verify(false);
await run(["--context", initial.engine.context, "start", initial.containerId!], 20_000);
}
// pg_isready is bounded and does not imply cluster identity by itself.
const deadline = Date.now() + 30_000;
while (true) {
try { await verifyCluster(); break; }
catch (error) {
if (!(error instanceof DockerCommandError) || Date.now() >= deadline) throw new Error("Exact owned Docker database recovery is unconfirmed; retain private supervisor evidence");
await verify(true); // Changed custody is never retried as startup latency.
await new Promise(resolve => setTimeout(resolve, 250));
}
}
stopRequested = false;
},
};
}