mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-04 19:28:46 +01:00
Qualify exact Docker Desktop private bind translation
This commit is contained in:
1 parent
c1d36080ac
commit
325af67e3d
3 files changed
+45
-8
No files matched your search
@@ -9,7 +9,7 @@ Updated: 2026-10-27. This is the single progress record for [the saved goal](202
|
||||
- Five partial definitions contain concrete executable supported-surface/context blocker probes: two access grants, subscription provenance, skill-use authority/context and genuine prior-day routine history. Three have unfinished required branches: ask-before-action, named ChatGPT setup and team installer failures. **207 authoring-resolved; 3 unfinished; 34 absent.** The native archive-fault transport is now fully authored and offline-qualified; it has not run against a live product instance. Only actual reserved execution can process a blocker; offline probes/calibrations do not establish processing. Incomplete conditions cannot pass.
|
||||
- **Four scenarios attempted; five actual measurements; zero first-attempt passes and zero final passes (0/244 for both rates).** Goal-tree is exhausted after two failures. Alignment and purpose each retain one failed initial attempt and one allowed correction/rerun remaining.
|
||||
- **No live launcher or provider process is running.** Admissions stopped after the authorized sign-in initial failed PostgreSQL bootstrap and finalization; its recovery state remains private with cleanup unconfirmed. The other four authorized initial cases remain unreserved. Final retries remain held for coherent B. No unchanged rerun, extra nudge, deadline extension, automatic retry or relabeled canonical health probe was used.
|
||||
- Current recovery: semaphore cleanup approval is still pending and no set has been removed. Read-only discovery verified an already running local Docker Linux/arm64 engine; an explicit owned PostgreSQL18.6 lane is being qualified, with no container/bootstrap started yet. AgentMail/GitHub read-only discovery is authorized/completed; designated self-email and repository choices remain pending.
|
||||
- Current recovery: semaphore cleanup approval is still pending and no set has been removed. Read-only discovery verified an already running local Docker Linux/arm64 engine; an explicit owned PostgreSQL18.6 lane is being qualified. Its one infrastructure-only health check at `772c29dad9` failed before container start because Docker Desktop represents the exact host password bind with `/host_mnt`; no Paperclip, native or model work began. The initial failed cleanup receipt is preserved; separate authorized cleanup verified exact container/volume absence and removed only its password. No second health check has run. AgentMail/GitHub read-only discovery is authorized/completed; designated self-email and repository choices remain pending.
|
||||
- Production baseline `8373d7867963674ff960d2345d0d28086f799bc7`; branch `codex/chat-catalog-evals`. PR #17794 merged via `2ee36a4`. No eval/core PR has been created. No merge is eligible.
|
||||
|
||||
## Measured evidence — all failures retained
|
||||
@@ -205,3 +205,9 @@ Twenty-six pure remaining-domain controls pass, including plausible reversed cou
|
||||
The coordinator paused five-computer authoring for a finite test-harness-only Docker PostgreSQL lane. Default embedded behavior remains unchanged; ambient URLs never select an external target. The optional lane pins the already installed official PostgreSQL18.6 Linux/arm64 image, local Unix context/engine, one per-attempt exact labeled container and named data volume, loopback publication, private mode0600 configuration and password-file handoff. No Docker pull, engine start, container start or database bootstrap has occurred during authoring. A separate infrastructure-only health entry point awaits manager review before its single actual onboarding/migration/API/isolation/cleanup check; it is not a canonical attempt.
|
||||
|
||||
Lost create acknowledgements are recorded before resource creation and recovered only through exact names/labels; unknown inspection/engine state preserves cleanup uncertainty. Docker selection never triggers Darwin shared-memory reaping, even if startup throws before returning a handle. Failed startup retains an explicit Docker identity or unconfirmed identity and private recovery receipt. Stable database lane/image/version enter case fingerprints; retained Docker proof must match the selected lane, while old embedded failures retain their original bytes/provenance. No passwords, URLs or per-attempt resource IDs enter the stable fingerprint. New source closures must be recomputed before future canonical admission.
|
||||
|
||||
## Docker infrastructure health and appended cleanup (2026-10-27)
|
||||
|
||||
The single infrastructure-only health command from immutable `772c29dad96b161e8f2a893df9d4ac1d3779d102` exited 1 during owned PostgreSQL startup. Actual official image/context/engine identities matched; the container remained `created`, never running. Every ownership predicate except literal password bind source matched. Docker Desktop exposes both actual bind records as `/host_mnt` plus the exact owned canonical host path. Original health and initial `cleanup-failed` receipts remain unchanged.
|
||||
|
||||
After explicit manager authorization, immediate full engine/context/ownership/private-file revalidation permitted removal of only full container ID `ec05ccb6b21e1e8724de792d314597ab10d27c22cfb6819d364eed67e75b976c` and its exact labeled local volume. Independent inspect calls proved both absent before the exact password file was removed. The append-only verification is `results/chat-catalog-campaign/docker-health-cleanup-772c29dad9.json`; diagnosis and original health receipt are linked there separately. This adds no canonical attempt or provider usage. The narrow correction admits only the exact Docker Desktop translation with an owned canonical regular0600 password, preserving all other guards; positive/negative offline controls cover wrong root, other engine OS, writable bind and symlink. A second infrastructure health requires separate authorization.
|
||||
@@ -1,4 +1,4 @@
|
||||
import { mkdtemp, mkdir, readFile, realpath, rm, writeFile } from "node:fs/promises";
|
||||
import { mkdtemp, mkdir, readFile, realpath, rm, writeFile, symlink } from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import os from "node:os";
|
||||
import { createHash } from "node:crypto";
|
||||
@@ -15,13 +15,13 @@ 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 }); });
|
||||
async function root() { const p = await realpath(await mkdtemp(path.join(os.tmpdir(), "paperclip-runner-e2e-"))); roots.push(p); return p; }
|
||||
function daemon() {
|
||||
const state = { calls: [] as string[][], engine: "engine-one", endpoint: "unix:///private/offline-docker.sock", container: null as any, volume: null as any, loseVolumeAck: false, loseContainerAck: false, inspectionError: false, volumeInspectionError: false, badImage: false, failStart: false };
|
||||
const state = { calls: [] as string[][], engine: "engine-one", operatingSystem: "Docker Desktop", endpoint: "unix:///private/offline-docker.sock", container: null as any, volume: null as any, loseVolumeAck: false, loseContainerAck: false, inspectionError: false, volumeInspectionError: false, badImage: false, failStart: false };
|
||||
const missing = (args: string[], name: string) => new DockerCommandError({ args, stdout: "[]", stderr: `Error: No such object: ${name}`, exitCode: 1, signal: null });
|
||||
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-context";
|
||||
if (cmd[0] === "context") return JSON.stringify([{ Endpoints: { docker: { Host: state.endpoint } } }]);
|
||||
if (cmd[0] === "info") return JSON.stringify({ id: state.engine, os: "linux" });
|
||||
if (cmd[0] === "info") return JSON.stringify({ id: state.engine, os: "linux", operatingSystem: state.operatingSystem });
|
||||
if (cmd[0] === "image") return JSON.stringify([{ Id: state.badImage ? "wrong-image" : POSTGRES_IMAGE_ID, Os: "linux", Architecture: "arm64", RepoDigests: [POSTGRES_IMAGE], Config: { Env: [`PG_VERSION=${POSTGRES_VERSION}`] } }]);
|
||||
if (cmd[0] === "volume") {
|
||||
if (cmd[1] === "create") { const labels: Record<string, string> = {}; for (let i = 0; i < cmd.length; i++) if (cmd[i] === "--label") { const [key, value] = cmd[i + 1]!.split("="); labels[key!] = value!; }
|
||||
@@ -166,3 +166,20 @@ it("fingerprints stable database lane/artifacts and rejects cross-lane retained
|
||||
expect(campaignDatabaseEvidenceEligible({ ...attempt, databaseLane: embedded }, temp)).toBe(false);
|
||||
expect(campaignDatabaseEvidenceEligible({ ...attempt, databaseLane: undefined }, temp)).toBe(false);
|
||||
});
|
||||
|
||||
it("allows only exact Docker Desktop host translation with an owned canonical private password", async () => {
|
||||
const temp = await root(), { state, run } = daemon(), owned = await startOwnedDockerDatabase({ temporaryRoot: temp, privateSecrets: [], run });
|
||||
const password = path.join(temp, "docker-postgres-private", "password");
|
||||
const mount = state.container.Mounts.find((m: any) => m.Destination === "/run/secrets/postgres-password");
|
||||
mount.Source = `/host_mnt${password}`;
|
||||
expect(() => assertOwnedContainer(state.container, owned.receipt, true)).not.toThrow();
|
||||
const wrongRoot = structuredClone(state.container); wrongRoot.Mounts.find((m: any) => m.Destination === "/run/secrets/postgres-password").Source = `/host_mnt${password}-foreign`;
|
||||
expect(() => assertOwnedContainer(wrongRoot, owned.receipt)).toThrow("ownership");
|
||||
expect(() => assertOwnedContainer(state.container, { ...owned.receipt, engine: { ...owned.receipt.engine, operatingSystem: "Debian GNU/Linux" } })).toThrow("ownership");
|
||||
const writable = structuredClone(state.container); writable.Mounts.find((m: any) => m.Destination === "/run/secrets/postgres-password").RW = true;
|
||||
expect(() => assertOwnedContainer(writable, owned.receipt)).toThrow("ownership");
|
||||
const contents = await readFile(password); await rm(password); const target = path.join(temp, "other-password"); await writeFile(target, contents, { mode: 0o600 }); await symlink(target, password);
|
||||
expect(() => assertOwnedContainer(state.container, owned.receipt)).toThrow("ownership");
|
||||
await rm(password); await writeFile(password, contents, { mode: 0o600 });
|
||||
await owned.close(); expect(state.container).toBeNull(); expect(state.volume).toBeNull();
|
||||
});
|
||||
@@ -1,3 +1,4 @@
|
||||
import { lstatSync, realpathSync } from "node:fs";
|
||||
import { execFile } from "node:child_process";
|
||||
import { promisify } from "node:util";
|
||||
import { createHash, randomBytes } from "node:crypto";
|
||||
@@ -13,7 +14,7 @@ const hash = (value: string) => createHash("sha256").update(value).digest("hex")
|
||||
const fullId = (value: unknown): value is string => typeof value === "string" && /^[a-f0-9]{64}$/.test(value);
|
||||
const uuidHex = (value: unknown): value is string => typeof value === "string" && /^[a-f0-9]{32}$/.test(value);
|
||||
const labelPrefix = "ai.paperclip.runner-e2e";
|
||||
export interface DockerEngineIdentity { context: string; endpoint: string; engineId: string }
|
||||
export interface DockerEngineIdentity { context: string; endpoint: string; engineId: string; operatingSystem: string }
|
||||
export interface DockerDatabaseReceipt {
|
||||
schemaVersion: 1; lane: "owned-docker-postgres"; temporaryRoot: string; nonce: string;
|
||||
engine: DockerEngineIdentity; containerId: string | null; containerName: string; volumeName: string;
|
||||
@@ -62,9 +63,10 @@ export async function currentDockerEngine(run: DockerRun = runDocker, env = proc
|
||||
const rows = JSON.parse(await run(["context", "inspect", context]));
|
||||
const endpoint = rows?.[0]?.Endpoints?.docker?.Host;
|
||||
if (rows.length !== 1 || typeof endpoint !== "string" || !endpoint.startsWith("unix:///") || endpoint.includes("\n") || env.DOCKER_HOST && env.DOCKER_HOST !== endpoint) throw new Error("Docker context must resolve to the exact local Unix socket");
|
||||
const info = JSON.parse(await run(["--context", context, "info", "--format", '{"id":{{json .ID}},"os":{{json .OSType}}}']));
|
||||
const info = JSON.parse(await run(["--context", context, "info", "--format", '{"id":{{json .ID}},"os":{{json .OSType}},"operatingSystem":{{json .OperatingSystem}}}']));
|
||||
if (info.os !== "linux" || typeof info.id !== "string" || !info.id.trim()) throw new Error("A running local Linux Docker engine is required");
|
||||
return { context, endpoint, engineId: info.id };
|
||||
if (typeof info.operatingSystem !== "string") throw new Error("Docker engine operating-system identity is missing");
|
||||
return { context, endpoint, engineId: info.id, operatingSystem: info.operatingSystem };
|
||||
}
|
||||
async function pinnedEngine(expected: DockerEngineIdentity, run: DockerRun) {
|
||||
const observed = await currentDockerEngine(run);
|
||||
@@ -75,12 +77,24 @@ function hasLabels(actual: Record<string, string> | undefined, receipt: DockerDa
|
||||
export function assertOwnedVolume(value: any, receipt: DockerDatabaseReceipt) {
|
||||
if (!value || value.Name !== receipt.volumeName || value.Driver !== "local" || value.Scope !== "local" || Object.keys(value.Options ?? {}).length || !hasLabels(value.Labels, receipt)) throw new Error("Docker volume ownership/configuration mismatch");
|
||||
}
|
||||
function exactPasswordBindSource(source: unknown, receipt: DockerDatabaseReceipt) {
|
||||
const expected = path.join(receipt.temporaryRoot, "docker-postgres-private", "password");
|
||||
if (source === expected) return true;
|
||||
// Docker Desktop exposes its exact host bind through this Linux VM prefix.
|
||||
// Never accept arbitrary suffixes, another engine, another root or a symlink.
|
||||
if (receipt.engine.operatingSystem !== "Docker Desktop" || source !== `/host_mnt${expected}`) return false;
|
||||
try {
|
||||
const entry = lstatSync(expected);
|
||||
return entry.isFile() && !entry.isSymbolicLink() && entry.uid === process.getuid?.()
|
||||
&& (entry.mode & 0o777) === 0o600 && realpathSync(expected) === expected;
|
||||
} catch { return false; }
|
||||
}
|
||||
export function assertOwnedContainer(value: any, receipt: DockerDatabaseReceipt, running?: boolean) {
|
||||
const mounts = value?.Mounts, data = mounts?.find((m: any) => m.Destination === "/var/lib/postgresql"), password = mounts?.find((m: any) => m.Destination === "/run/secrets/postgres-password");
|
||||
const ports = value?.HostConfig?.PortBindings?.["5432/tcp"], config = value?.Config;
|
||||
if (!fullId(receipt.containerId) || value?.Id !== receipt.containerId || value.Name !== `/${receipt.containerName}` || value.Image !== POSTGRES_IMAGE_ID || config?.Image !== POSTGRES_IMAGE || !hasLabels(config.Labels, receipt)
|
||||
|| value.HostConfig.Privileged !== false || !["default", "bridge"].includes(value.HostConfig.NetworkMode) || !Array.isArray(mounts) || mounts.length !== 2
|
||||
|| data?.Type !== "volume" || data.Name !== receipt.volumeName || data.RW !== true || password?.Type !== "bind" || password.Source !== path.join(receipt.temporaryRoot, "docker-postgres-private", "password") || password.RW !== false
|
||||
|| data?.Type !== "volume" || data.Name !== receipt.volumeName || data.RW !== true || password?.Type !== "bind" || !exactPasswordBindSource(password.Source, receipt) || password.RW !== false
|
||||
|| !Array.isArray(ports) || ports.length !== 1 || ports[0].HostIp !== "127.0.0.1" || Object.keys(value.HostConfig.PortBindings).join() !== "5432/tcp"
|
||||
|| !config.Env.includes("POSTGRES_PASSWORD_FILE=/run/secrets/postgres-password") || !config.Env.includes(`POSTGRES_USER=${receipt.username}`) || !config.Env.includes(`POSTGRES_DB=${receipt.database}`) || config.Env.some((v: string) => /^POSTGRES_PASSWORD=/.test(v))
|
||||
|| running !== undefined && value.State?.Running !== running) throw new Error("Docker container ownership/isolation mismatch");
|
||||
|
||||
Reference in new issue
Block a user