#!/usr/bin/env node import { execFile } from "node:child_process"; import { createHash, randomBytes } from "node:crypto"; import { closeSync, constants as fsConstants, existsSync, fsyncSync, lstatSync, openSync, realpathSync, } from "node:fs"; import { access, chmod, lstat, mkdir, open, readFile, readdir, realpath, rename, rm, stat, symlink, writeFile, } from "node:fs/promises"; import { tmpdir } from "node:os"; import { basename, dirname, isAbsolute, join, relative, resolve, sep, } from "node:path"; import { fileURLToPath } from "node:url"; import { promisify } from "node:util"; const execFileAsync = promisify(execFile); const RUN_ID = /^p1-[0-9a-f]{32}$/; const HEX40 = /^[0-9a-f]{40}$/; const HEX64 = /^[0-9a-f]{64}$/; const ISO_UTC = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/; const SAFE_RELATIVE = /^(?!\/)(?!.*(?:^|\/)\.\.(?:\/|$))(?!.*\\)[A-Za-z0-9._/-]+$/; const COMMAND = /^[A-Za-z0-9._+-]+$/; const CHECK_IDS = [ "preflight", "clean_state", "ownership", "local_git_bootstrap", "http_validate_publish_pull_read_export", "same_revision_git_objects", "content_only_revision", "snapshot_and_docs", "runtime_render_determinism", "tht_config_check", "negative_schema_cases", "negative_context_case", "no_p1_scope_artifacts", "secret_scan", "cleanup_confinement", ]; const TOPOLOGY = [ "remote.git", "author", "installation/registry", "installation/data", "installation/runtime", "fixture-secrets", "fixtures/descriptors", "fixtures/requests", "requests", "responses", "exports/raw", "exports/extracted", "rendered", "logs", ]; const ZIP_FILES = ["manifest.json", "workspace.yaml", "contract.env.example", "README.md"]; const MAX_OUTPUT = 1024 * 1024; const modulePath = fileURLToPath(import.meta.url); const defaultRepositoryRoot = realpathSync(resolve(dirname(modulePath), "../..")); function nowIso() { return new Date().toISOString(); } function sha256(value) { return createHash("sha256").update(value).digest("hex"); } export function scalarSecretBytes(value) { if (typeof value !== "string" || value.length === 0 || /\s|\0/.test(value)) throw new Error("scalar fixture secret is invalid"); return Buffer.from(value); } function canonicalRoot(repositoryRoot) { return realpathSync(repositoryRoot); } export function canonicalIntegrationBase(repositoryRoot) { return join(canonicalRoot(repositoryRoot), ".artifacts", "p1-integration"); } export function validateRunRoot(repositoryRoot, runRoot, runId) { if (!RUN_ID.test(runId)) throw new Error("invalid owned run id"); const base = canonicalIntegrationBase(repositoryRoot); const lexical = resolve(runRoot); if (dirname(lexical) !== base || basename(lexical) !== runId) throw new Error("run root is not a direct integration child"); return lexical; } function validateNoSymlinkAncestors(repositoryRoot, target) { const repo = canonicalRoot(repositoryRoot); const rel = relative(repo, target); if (rel.startsWith("..") || isAbsolute(rel)) throw new Error("path leaves repository"); let cursor = repo; for (const part of rel.split(sep).filter(Boolean)) { cursor = join(cursor, part); if (!existsSync(cursor)) break; const entry = lstatSync(cursor); if (entry.isSymbolicLink()) throw new Error("owned path ancestor is a symlink"); } } async function atomicWrite(path, bytes, mode = 0o600) { await mkdir(dirname(path), { recursive: true }); const staging = join(dirname(path), `.${basename(path)}.${randomBytes(16).toString("hex")}.tmp`); let handle; try { handle = await open(staging, "wx", mode); await handle.writeFile(bytes); await handle.sync(); await handle.close(); handle = undefined; await rename(staging, path); const directory = openSync(dirname(path), fsConstants.O_RDONLY); try { fsyncSync(directory); } finally { closeSync(directory); } } catch (error) { if (handle) await handle.close().catch(() => {}); await rm(staging, { force: true }); throw error; } } function ownership(run, listener = run.listener) { return { schemaVersion: 1, runId: run.runId, runNonce: run.nonce, root: run.root, repositoryRoot: run.repositoryRoot, startedAt: run.startedAt, pid: run.pid, listener, resources: [run.root, { kind: "fastify", host: "127.0.0.1", requestedPort: 0, pid: run.pid }], }; } async function writeOwnership(run, listener = run.listener) { run.listener = listener; await atomicWrite(join(run.root, "ownership.json"), `${JSON.stringify(ownership(run), null, 2)}\n`); } export async function createOwnedRun({ repositoryRoot, runId, nonce, now, pid } = {}) { const repo = canonicalRoot(repositoryRoot); const base = canonicalIntegrationBase(repo); validateNoSymlinkAncestors(repo, base); await mkdir(join(repo, ".artifacts"), { mode: 0o700 }).catch((error) => { if (error.code !== "EEXIST") throw error; }); await mkdir(base, { mode: 0o700 }).catch((error) => { if (error.code !== "EEXIST") throw error; }); validateNoSymlinkAncestors(repo, base); const id = runId ?? `p1-${randomBytes(16).toString("hex")}`; const root = validateRunRoot(repo, join(base, id), id); const run = { repositoryRoot: repo, root, runId: id, nonce: nonce ?? randomBytes(32).toString("hex"), startedAt: now ?? nowIso(), pid: pid ?? process.pid, listener: { kind: "fastify", host: "127.0.0.1", requestedPort: 0, pid: pid ?? process.pid, state: "not_started" }, }; if (!HEX64.test(run.nonce) || !ISO_UTC.test(run.startedAt)) throw new Error("invalid ownership identity"); await mkdir(root, { mode: 0o700 }); await writeOwnership(run); return run; } function strictOwnership(value, run, expectedNonce) { if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("ownership is malformed"); const expected = ownership(run, value.listener); if (value.schemaVersion !== 1 || value.runId !== run.runId || value.runNonce !== expectedNonce || value.root !== run.root || value.repositoryRoot !== run.repositoryRoot || value.pid !== process.pid || !ISO_UTC.test(value.startedAt ?? "") || !value.listener || value.listener.kind !== "fastify" || value.listener.host !== "127.0.0.1" || value.listener.requestedPort !== 0 || value.listener.pid !== process.pid || JSON.stringify(value.resources) !== JSON.stringify(expected.resources)) throw new Error("ownership identity mismatch"); return value; } export async function readAndValidateOwnership({ repositoryRoot, runRoot, expectedNonce }) { const repo = canonicalRoot(repositoryRoot); const id = basename(resolve(runRoot)); const lexical = validateRunRoot(repo, runRoot, id); const rootEntry = await lstat(lexical); if (!rootEntry.isDirectory() || rootEntry.isSymbolicLink()) throw new Error("owned run root is not a directory"); if (await realpath(lexical) !== lexical) throw new Error("owned run root is not canonical"); const ownershipPath = join(lexical, "ownership.json"); const ownershipEntry = await lstat(ownershipPath); if (!ownershipEntry.isFile() || ownershipEntry.isSymbolicLink()) throw new Error("ownership file is unsafe"); let value; try { value = JSON.parse(await readFile(ownershipPath, "utf8")); } catch { throw new Error("ownership is malformed"); } return strictOwnership(value, { repositoryRoot: repo, root: lexical, runId: id, nonce: expectedNonce, startedAt: value.startedAt, pid: process.pid, listener: value.listener, }, expectedNonce); } export async function cleanupOwnedRun({ repositoryRoot, runRoot, expectedNonce }) { const value = await readAndValidateOwnership({ repositoryRoot, runRoot, expectedNonce }); const base = canonicalIntegrationBase(repositoryRoot); const tombstone = join(base, `.deleting-${value.runId}-${expectedNonce.slice(0, 16)}`); await rename(runRoot, tombstone); await rm(tombstone, { recursive: true }); } export async function finalizeOwnedRun({ run, success, keep }) { if (!success || keep) return false; await cleanupOwnedRun({ repositoryRoot: run.repositoryRoot, runRoot: run.root, expectedNonce: run.nonce }); return true; } export async function runCommand(options) { if (!options || typeof options !== "object" || Array.isArray(options)) throw new Error("command requires an options object"); const allowed = new Set(["executable", "argv", "cwd", "env", "timeoutMs", "stdin", "maxOutputBytes"]); for (const key of Object.keys(options)) if (!allowed.has(key)) throw new Error(`unsupported command option ${key}`); const { executable, argv, cwd, env, timeoutMs = 30_000, stdin, maxOutputBytes = MAX_OUTPUT } = options; if (typeof executable !== "string" || executable.length === 0 || /[;&|`$><\n\r]/.test(executable)) throw new Error("command executable is invalid"); if (!Array.isArray(argv) || argv.some((value) => typeof value !== "string")) throw new Error("command argv must be a string array"); if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 300_000) throw new Error("command timeout is invalid"); return await new Promise((resolvePromise, reject) => { const child = execFile(executable, argv, { cwd, env, timeout: timeoutMs, maxBuffer: maxOutputBytes, encoding: "utf8" }, (error, stdout, stderr) => { const code = error && typeof error.code === "number" ? error.code : error ? 1 : 0; const result = { code, stdout: stdout ?? "", stderr: stderr ?? "" }; if (error) Object.assign(error, { result }); error ? reject(error) : resolvePromise(result); }); if (stdin !== undefined) { child.stdin.end(stdin); } }); } async function git(argv, options = {}) { return await runCommand({ executable: "git", argv, ...options }); } async function tht(executable, argv, options = {}) { return await runCommand({ executable, argv, ...options }); } function safeArtifactPath(path) { if (typeof path !== "string" || !SAFE_RELATIVE.test(path) || path.startsWith(".") || path.includes("//")) throw new Error("unsafe artifact path"); return path; } async function fileArtifact(runRoot, path) { safeArtifactPath(path); return { path, sha256: sha256(await readFile(join(runRoot, path))) }; } function forbiddenKey(value) { if (!value || typeof value !== "object") return false; if (Array.isArray(value)) return value.some(forbiddenKey); for (const [key, nested] of Object.entries(value)) { if (/^(attempt|attempts|retry|retries)$/i.test(key) || forbiddenKey(nested)) return true; } return false; } export function deriveOverall(checks) { return checks.length > 0 && checks.every(({ status }) => status === "PASS") ? "PASS" : "FAIL"; } export function validateReport(report) { if (!report || report.schemaVersion !== 1 || !RUN_ID.test(report.runId ?? "") || !ISO_UTC.test(report.startedAt ?? "") || !ISO_UTC.test(report.finishedAt ?? "") || typeof report.command !== "string" || forbiddenKey(report) || !Array.isArray(report.checks) || report.checks.length === 0) throw new Error("report is invalid"); const ids = new Set(); for (const check of report.checks) { if (!check || !/^[a-z0-9_]+$/.test(check.id ?? "") || ids.has(check.id) || !["PASS", "FAIL"].includes(check.status) || !ISO_UTC.test(check.startedAt ?? "") || !ISO_UTC.test(check.finishedAt ?? "") || !Array.isArray(check.commands) || check.commands.some((name) => !COMMAND.test(name)) || !Array.isArray(check.artifacts) || check.artifacts.some(({ path, sha256 }) => { try { safeArtifactPath(path); } catch { return true; } return !HEX64.test(sha256 ?? ""); })) throw new Error("report check is invalid"); ids.add(check.id); } if (report.overall !== deriveOverall(report.checks)) throw new Error("report overall is not derived"); return report; } function renderReportMarkdown(report) { validateReport(report); const rows = report.checks.map((check) => `| ${check.id} | ${check.status} |`).join("\n"); return `# P1 automated integration\n\nRun: \`${report.runId}\`\n\n| Check | Status |\n|---|---|\n${rows}\n\nautomated integration: ${report.overall}\nmanual acceptance: PENDING\n`; } function containsAny(bytes, forbiddenValues) { return forbiddenValues.some((value) => value && bytes.includes(Buffer.from(value))); } async function walkFiles(root, current = root, out = []) { for (const entry of await readdir(current, { withFileTypes: true })) { const path = join(current, entry.name); const rel = relative(root, path).split(sep).join("/"); if (entry.isSymbolicLink()) continue; if (entry.isDirectory()) { if (rel === "fixture-secrets") continue; await walkFiles(root, path, out); } else if (entry.isFile()) out.push({ path, rel }); } return out; } async function gitObjectFindings(runRoot, forbiddenValues) { const findings = []; for (const directory of [join(runRoot, "remote.git"), join(runRoot, "author")]) { if (!existsSync(directory)) continue; const args = basename(directory) === "remote.git" ? ["--git-dir", directory] : ["-C", directory]; let objects; try { objects = (await git([...args, "rev-list", "--objects", "--all"])).stdout.trim().split("\n").filter(Boolean); } catch { continue; } for (const line of objects) { const oid = line.split(" ", 1)[0]; const type = (await git([...args, "cat-file", "-t", oid])).stdout.trim(); if (type !== "blob") continue; const bytes = Buffer.from((await git([...args, "cat-file", "blob", oid], { maxOutputBytes: 16 * 1024 * 1024 })).stdout); if (containsAny(bytes, forbiddenValues)) findings.push({ path: `git-object:${basename(directory)}:${oid}` }); } } return findings; } export async function scanSecrets({ runRoot, forbiddenValues, virtualFiles = [] }) { const values = forbiddenValues.filter((value) => typeof value === "string" && value.length >= 8); const findings = []; for (const file of await walkFiles(runRoot)) if (containsAny(await readFile(file.path), values)) findings.push({ path: file.rel }); for (const file of virtualFiles) if (containsAny(Buffer.from(file.bytes), values)) findings.push({ path: file.path }); findings.push(...await gitObjectFindings(runRoot, values)); return findings; } function sanitizeForEvidence(value, forbiddenValues = []) { if (typeof value === "string") { let safe = value; for (const forbidden of forbiddenValues) if (forbidden) safe = safe.split(forbidden).join("[REDACTED]"); return safe.length > 16_384 ? `${safe.slice(0, 16_384)}[TRUNCATED]` : safe; } if (Array.isArray(value)) return value.map((item) => sanitizeForEvidence(item, forbiddenValues)); if (value && typeof value === "object") return Object.fromEntries(Object.entries(value).map(([key, nested]) => [key, sanitizeForEvidence(nested, forbiddenValues)])); return value; } async function evidence(run, path, value, forbiddenValues = []) { const safe = sanitizeForEvidence(value, forbiddenValues); await atomicWrite(join(run.root, path), `${JSON.stringify(safe, null, 2)}\n`); return await fileArtifact(run.root, path); } export async function executeChecks({ checks, failAt, recorder } = {}) { const results = []; const ids = new Set(); for (const scenario of checks) { if (ids.has(scenario.id)) throw new Error("duplicate scenario id"); ids.add(scenario.id); const startedAt = nowIso(); let result; try { const output = await scenario.run(); if (scenario.id === failAt) throw new Error("injected acceptance failure"); result = { id: scenario.id, status: "PASS", startedAt, finishedAt: nowIso(), commands: output.commands ?? [], artifacts: output.artifacts ?? [] }; } catch (error) { result = { id: scenario.id, status: "FAIL", startedAt, finishedAt: nowIso(), commands: [], artifacts: [], error: "Acceptance scenario failed safely." }; } results.push(result); if (recorder) await recorder(result); if (result.status === "FAIL") break; } return results; } function baseWorkspace(id, evidenceSource) { return { workspace: { schema_version: 3, id, name: `P1 ${id}`, language: "en" }, dwh: { engine: "postgres", database: "postgres", schema: "public", supported_transports: ["postgres_direct"] }, semantic_index: { vector_store: { engine: "qdrant", collection: id, dimensions: 1024, distance: "cosine" }, embedding: { provider: "ollama_internal", model: "qwen3-embedding:0.6b", dimensions: 1024 }, }, llm_policy: { allowed: ["zai/glm-5.2"] }, evidence: { source: evidenceSource, policy: { max_chunk_chars: 4000, retain_published_generations: 3 } }, }; } function descriptors() { return [ baseWorkspace("p1-filesystem", { type: "filesystem", uri: "workspace-content/p1-filesystem/evidence", patterns: ["**/*.md"], max_bytes: 10485760 }), baseWorkspace("p1-http", { type: "http", uris: ["https://evidence.example.test/guide.md"], authentication: "signed_urls_file", connect_timeout_ms: 1250, read_timeout_ms: 30001, max_bytes: 12345, max_redirects: 2, allow_private_hosts: false, max_cache_bytes: 67890 }), baseWorkspace("p1-s3", { type: "s3", uri: "s3://p1-evidence/published/", endpoint_url: "https://s3.example.test/", region: "eu-west-1", credentials: "static_files", trusted_endpoint: true, allow_private_endpoint: false, allow_insecure_endpoint: false, max_bytes: 12345, max_objects: 33, max_pages: 4, page_size: 5 }), ]; } function namespace(id) { return id.toUpperCase().replaceAll("-", "_"); } async function createTopology(run) { for (const path of TOPOLOGY) await mkdir(join(run.root, path), { recursive: true, mode: path === "fixture-secrets" ? 0o700 : 0o755 }); } async function setupSecrets(ctx) { const secretDir = join(ctx.run.root, "fixture-secrets"); const values = { dwh: `DWH-${randomBytes(16).toString("hex")}`, signed: `SIGNED-${randomBytes(16).toString("hex")}`, access: `ACCESS-${randomBytes(16).toString("hex")}`, secret: `SECRET-${randomBytes(16).toString("hex")}`, session: `SESSION-${randomBytes(16).toString("hex")}`, rejected: `REJECTED-${randomBytes(16).toString("hex")}`, }; const paths = { dwh: join(secretDir, "dwh-password"), signed: join(secretDir, "evidence-signed-urls.json"), access: join(secretDir, "evidence-access"), secret: join(secretDir, "evidence-secret"), session: join(secretDir, "evidence-session"), }; await atomicWrite(paths.dwh, scalarSecretBytes(values.dwh)); await atomicWrite(paths.signed, JSON.stringify([`https://evidence.example.test/guide.md?token=${values.signed}`])); await atomicWrite(paths.access, scalarSecretBytes(values.access)); await atomicWrite(paths.secret, scalarSecretBytes(values.secret)); await atomicWrite(paths.session, scalarSecretBytes(values.session)); ctx.forbiddenValues = Object.values(values); ctx.secretValues = values; const env = {}; for (const workspace of ctx.descriptors) { const ns = namespace(workspace.workspace.id); const prefix = `THT_WS_${ns}`; Object.assign(env, { [`${prefix}_DWH_TRANSPORT`]: "postgres_direct", [`${prefix}_DWH_HOST`]: "dwh.invalid", [`${prefix}_DWH_PORT`]: "5432", [`${prefix}_DWH_USER`]: "reader", [`${prefix}_DWH_PASSWORD_FILE`]: paths.dwh, }); } Object.assign(env, { THT_WS_P1_HTTP_EVIDENCE_SIGNED_URLS_FILE: paths.signed, THT_WS_P1_S3_EVIDENCE_ACCESS_KEY_FILE: paths.access, THT_WS_P1_S3_EVIDENCE_SECRET_KEY_FILE: paths.secret, THT_WS_P1_S3_EVIDENCE_SESSION_TOKEN_FILE: paths.session, }); Object.assign(ctx.env, env); await atomicWrite(join(ctx.run.root, "installation", "bindings.env"), `${Object.entries(env).map(([key, value]) => `${key}=${value}`).join("\n")}\n`); await atomicWrite(join(ctx.run.root, "installation", "base.yaml"), "{}\n"); } async function initializeGit(ctx) { await git(["init", "--bare", "--initial-branch=main", join(ctx.run.root, "remote.git")], { cwd: ctx.run.root }); await git(["clone", join(ctx.run.root, "remote.git"), join(ctx.run.root, "author")], { cwd: ctx.run.root }); await git(["config", "user.name", "P1 Fixture Curator"], { cwd: join(ctx.run.root, "author") }); await git(["config", "user.email", "p1-curator@example.invalid"], { cwd: join(ctx.run.root, "author") }); const evidenceRoot = join(ctx.run.root, "author", "workspace-content", "p1-filesystem", "evidence"); await mkdir(join(evidenceRoot, "domain"), { recursive: true }); await writeFile(join(evidenceRoot, "guide.md"), "# P1 curated Evidence\n"); await writeFile(join(evidenceRoot, "domain", "table.md"), "# Curated table\n"); await git(["add", "workspace-content"], { cwd: join(ctx.run.root, "author") }); await git(["commit", "-m", "Bootstrap curated P1 content"], { cwd: join(ctx.run.root, "author") }); await git(["push", "origin", "main"], { cwd: join(ctx.run.root, "author") }); ctx.bootstrapCommit = (await git(["rev-parse", "HEAD"], { cwd: join(ctx.run.root, "author") })).stdout.trim(); } async function loadProductionBackend() { const [{ loadConfig }, { buildApp }, { WorkspaceRegistry }, { ThtRunner }] = await Promise.all([ import("../dist/config.js"), import("../dist/app.js"), import("../dist/workspaces/registry.js"), import("../dist/tht/tht-runner.js"), ]); return { loadConfig, buildApp, WorkspaceRegistry, ThtRunner }; } async function startBackend(ctx) { const { loadConfig, buildApp, WorkspaceRegistry, ThtRunner } = await loadProductionBackend(); const config = loadConfig(ctx.env); ctx.registryConfig = config.workspaceRegistry; ctx.registry = new WorkspaceRegistry(ctx.registryConfig); ctx.thtRunner = new ThtRunner({ thtBin: config.thtBin, harnessDir: config.harnessDir, configPath: join(ctx.run.root, "installation", "base.yaml"), dataRoot: config.dataRoot, runtimeSnapshotRoot: join(config.workspaceRegistry.root, "snapshots", "runtime"), secretRoots: config.workspaceRegistry.secretRoots, secretsFile: config.secretsFile, secretFiles: config.secretFiles, semanticRuntime: { internalQdrantUrl: config.internalQdrantUrl, internalEmbeddingUrl: config.internalEmbeddingUrl, internalEmbeddingModel: config.internalEmbeddingModel, internalEmbeddingDimensions: config.internalEmbeddingDimensions }, }); ctx.app = buildApp(config, { thtRunner: ctx.thtRunner, workspaceRegistry: ctx.registry }); const address = await ctx.app.listen({ host: "127.0.0.1", port: 0 }); const url = new URL(address); ctx.baseUrl = `http://127.0.0.1:${url.port}`; await writeOwnership(ctx.run, { kind: "fastify", host: "127.0.0.1", requestedPort: 0, actualPort: Number(url.port), pid: process.pid, state: "listening" }); } async function request(ctx, id, method, path, body, binary = false) { const requestSummary = { method, path, ...(body === undefined ? {} : { body: sanitizeForEvidence(body, ctx.forbiddenValues) }) }; await evidence(ctx.run, `requests/${id}.json`, requestSummary, ctx.forbiddenValues); const response = await fetch(`${ctx.baseUrl}${path}`, { method, headers: body === undefined ? {} : { "content-type": "application/json" }, ...(body === undefined ? {} : { body: JSON.stringify(body) }), signal: AbortSignal.timeout(15_000), }); if (binary) { const bytes = Buffer.from(await response.arrayBuffer()); await atomicWrite(join(ctx.run.root, `exports/raw/${id}.zip`), bytes); await evidence(ctx.run, `responses/${id}.json`, { status: response.status, contentType: response.headers.get("content-type"), bytes: bytes.length }); return { status: response.status, bytes }; } const text = await response.text(); let parsed; try { parsed = text ? JSON.parse(text) : null; } catch { parsed = { invalidJson: true }; } const safe = sanitizeForEvidence(parsed, ctx.forbiddenValues); await evidence(ctx.run, `responses/${id}.json`, { status: response.status, body: safe }, ctx.forbiddenValues); return { status: response.status, body: parsed }; } async function extractZip(ctx, id, bytes) { const yauzl = (await import("yauzl")).default; const output = join(ctx.run.root, "exports", "extracted", id); await mkdir(output, { recursive: true }); const files = await new Promise((resolvePromise, reject) => { yauzl.fromBuffer(bytes, { lazyEntries: true, strictFileNames: true, validateEntrySizes: true }, (error, zip) => { if (error || !zip) return reject(error ?? new Error("zip open failed")); const collected = new Map(); let total = 0; zip.on("error", reject); zip.on("end", () => resolvePromise(collected)); zip.on("entry", (entry) => { const type = (entry.externalFileAttributes >>> 16) & 0o170000; if (!ZIP_FILES.includes(entry.fileName) || entry.fileName.includes("\\") || entry.fileName.includes("..") || entry.fileName.startsWith("/") || entry.fileName.endsWith("/") || type === 0o120000 || collected.has(entry.fileName) || entry.uncompressedSize > 2_000_000) return reject(new Error("unsafe export entry")); zip.openReadStream(entry, (streamError, stream) => { if (streamError || !stream) return reject(streamError ?? new Error("zip stream failed")); const chunks = []; stream.on("data", (chunk) => { total += chunk.length; if (total > 8_000_000) reject(new Error("export too large")); else chunks.push(chunk); }); stream.on("end", () => { collected.set(entry.fileName, Buffer.concat(chunks)); zip.readEntry(); }); stream.on("error", reject); }); }); zip.readEntry(); }); }); assert(files.size === ZIP_FILES.length, "export file allowlist mismatch"); const manifest = JSON.parse(files.get("manifest.json").toString("utf8")); assert(manifest.schema_version === 1 && manifest.workspace_id === id, "export manifest identity mismatch"); for (const name of ZIP_FILES.slice(1)) assert(sha256(files.get(name)) === manifest.files[name], `export hash mismatch ${name}`); for (const [name, contents] of files) await atomicWrite(join(output, name), contents, 0o600); return manifest; } function assert(condition, message) { if (!condition) throw new Error(message); } async function snapshotDigest(path) { const files = await walkFiles(path); const result = {}; for (const file of files) result[file.rel] = sha256(await readFile(file.path)); return result; } async function setupContext(run, repositoryRoot, env) { const thtBin = realpathSync(env.THT_BIN ?? join(repositoryRoot, "harness", ".venv", "bin", "tht")); const harnessDir = realpathSync(join(repositoryRoot, "harness")); const ctx = { run, repositoryRoot, descriptors: descriptors(), forbiddenValues: [], env: { ...env, HOST: "127.0.0.1", PORT: "0", AUTH_MODE: "none", THT_BIN: thtBin, THT_HARNESS_DIR: harnessDir, THT_DATA_ROOT: join(run.root, "installation", "data"), SETTINGS_FILE: join(run.root, "installation", "data", "settings.json"), MAINTENANCE_STATE_FILE: join(run.root, "installation", "data", "maintenance.json"), THT_WORKSPACE_REGISTRY_ROOT: join(run.root, "installation", "registry"), THT_WORKSPACE_GIT_REMOTE: join(run.root, "remote.git"), THT_WORKSPACE_GIT_BRANCH: "main", THT_WORKSPACE_GIT_AUTHOR_NAME: "P1 API Publisher", THT_WORKSPACE_GIT_AUTHOR_EMAIL: "p1-api@example.invalid", THT_WORKSPACE_INSTALLATION_ID: "p1-acceptance", THT_WORKSPACE_SECRET_ROOTS: join(run.root, "fixture-secrets"), THT_HOME: join(run.root, "installation", "runtime", "tht-home"), } }; await createTopology(run); await setupSecrets(ctx); for (const [name, value] of Object.entries(ctx.env)) process.env[name] = value; return ctx; } function productionChecks(ctx) { const log = async (id, value) => ({ commands: [], artifacts: [await evidence(ctx.run, `logs/${id}.json`, value, ctx.forbiddenValues)] }); return [ { id: "preflight", run: async () => { const gitVersion = await git(["--version"]); await access(ctx.env.THT_BIN, fsConstants.X_OK); return await log("preflight", { git: gitVersion.stdout.trim(), node: process.version, thtExecutable: true }); } }, { id: "clean_state", run: async () => { assert(RUN_ID.test(ctx.run.runId), "run identity invalid"); return await log("clean_state", { exclusiveRoot: true, reused: false }); } }, { id: "ownership", run: async () => { await readAndValidateOwnership({ repositoryRoot: ctx.repositoryRoot, runRoot: ctx.run.root, expectedNonce: ctx.run.nonce }); return await log("ownership", { valid: true, listener: "not_started" }); } }, { id: "local_git_bootstrap", run: async () => { await initializeGit(ctx); for (const workspace of ctx.descriptors) await atomicWrite(join(ctx.run.root, "fixtures", "descriptors", `${workspace.workspace.id}.json`), `${JSON.stringify(workspace, null, 2)}\n`); assert(!existsSync(join(ctx.run.root, "author", "workspaces")), "fixture authored a descriptor"); return { commands: ["git"], artifacts: [await evidence(ctx.run, "logs/local_git_bootstrap.json", { bootstrapCommit: ctx.bootstrapCommit, descriptorEmpty: true })] }; } }, { id: "http_validate_publish_pull_read_export", run: async () => { await startBackend(ctx); const status = await request(ctx, "registry-status", "GET", "/workspace-registry/status"); assert(status.status === 200 && status.body.head === ctx.bootstrapCommit, "empty registry status failed"); let base = status.body.head; for (const workspace of ctx.descriptors) { const id = workspace.workspace.id; const validated = await request(ctx, `validate-${id}`, "POST", "/workspaces/validate", { workspace }); assert(validated.status === 200 && validated.body.workspace.workspace.id === id, `validation failed ${id}`); const published = await request(ctx, `publish-${id}`, "POST", "/workspaces/publish", { action: "create", workspace, baseCommit: base }); assert(published.status === 200 && HEX40.test(published.body.revision.commit), `publication failed ${id}`); base = published.body.revision.commit; } ctx.publicationHead = base; const pulled = await request(ctx, "registry-pull", "POST", "/workspace-registry/pull"); assert(pulled.status === 200 && pulled.body.head === base, "pull failed"); const listed = await request(ctx, "workspace-list", "GET", "/workspaces"); assert(listed.status === 200 && listed.body.length === 3, "list failed"); ctx.reads = {}; for (const workspace of ctx.descriptors) { const id = workspace.workspace.id; const read = await request(ctx, `read-${id}`, "GET", `/workspaces/${id}`); assert(read.status === 200, `read failed ${id}`); ctx.reads[id] = read.body; const exported = await request(ctx, `export-${id}`, "GET", `/workspaces/${id}/export`, undefined, true); assert(exported.status === 200, `export failed ${id}`); await extractZip(ctx, id, exported.bytes); } return await log("http_flow", { workspaceIds: Object.keys(ctx.reads), head: base, realListener: true, fetch: true }); } }, { id: "same_revision_git_objects", run: async () => { const read = await request(ctx, "read-filesystem-identity", "GET", "/workspaces/p1-filesystem"); const revision = read.body.revision; ctx.oldRevision = revision; ctx.oldSnapshotDigest = await snapshotDigest(dirname(revision.snapshotPath)); const checkoutHead = (await git(["rev-parse", "HEAD"], { cwd: join(ctx.run.root, "installation", "registry", "repo") })).stdout.trim(); const manifest = JSON.parse(await readFile(join(dirname(revision.snapshotPath), "snapshot.json"), "utf8")); const lease = ctx.thtRunner.acquireWorkspaceRuntime(revision.snapshotPath); let rendered; try { rendered = (await import("yaml")).parse(await readFile(lease.path, "utf8")); } finally { lease.release(); } const identities = [revision.commit, checkoutHead, manifest.head, rendered.runtime_identity.workspace_revision]; assert(new Set(identities).size === 1, "revision identities diverged"); const repo = join(ctx.run.root, "installation", "registry", "repo"); await git(["cat-file", "-e", `${revision.commit}:workspaces/p1-filesystem.yaml`], { cwd: repo }); await git(["cat-file", "-e", `${revision.commit}:workspace-content/p1-filesystem/evidence/guide.md`], { cwd: repo }); const type = (await git(["cat-file", "-t", `${revision.commit}:workspace-content/p1-filesystem/evidence`], { cwd: repo })).stdout.trim(); assert(type === "tree", "Evidence object is not a tree"); assert(!existsSync(join(dirname(revision.snapshotPath), "workspace-content")), "snapshot materialized workspace-content"); return { commands: ["git"], artifacts: [await evidence(ctx.run, "logs/git-object-proof.json", { commit: revision.commit, checkoutHead, manifestHead: manifest.head, runtimeRevision: rendered.runtime_identity.workspace_revision, evidenceType: type })] }; } }, { id: "content_only_revision", run: async () => { const author = join(ctx.run.root, "author"); await git(["fetch", "origin", "main"], { cwd: author }); await git(["reset", "--hard", "origin/main"], { cwd: author }); const descriptorBefore = (await git(["rev-parse", "HEAD:workspaces/p1-filesystem.yaml"], { cwd: author })).stdout.trim(); await writeFile(join(author, "workspace-content", "p1-filesystem", "evidence", "guide.md"), "# P1 curated Evidence v2\n"); await git(["add", "workspace-content/p1-filesystem/evidence/guide.md"], { cwd: author }); await git(["commit", "-m", "Update curated Evidence only"], { cwd: author }); await git(["push", "origin", "main"], { cwd: author }); ctx.contentCommit = (await git(["rev-parse", "HEAD"], { cwd: author })).stdout.trim(); const pulled = await request(ctx, "content-only-pull", "POST", "/workspace-registry/pull"); assert(pulled.status === 200 && pulled.body.head === ctx.contentCommit, "content pull failed"); const current = (await request(ctx, "read-filesystem-content", "GET", "/workspaces/p1-filesystem")).body.revision; const descriptorAfter = (await git(["rev-parse", "HEAD:workspaces/p1-filesystem.yaml"], { cwd: author })).stdout.trim(); assert(current.commit === ctx.contentCommit && current.blob === ctx.oldRevision.blob && descriptorAfter === descriptorBefore, "content revision identity failed"); assert(JSON.stringify(await snapshotDigest(dirname(ctx.oldRevision.snapshotPath))) === JSON.stringify(ctx.oldSnapshotDigest), "old snapshot changed"); ctx.currentRevision = current; return { commands: ["git"], artifacts: [await evidence(ctx.run, "logs/content-only-revision.json", { oldCommit: ctx.oldRevision.commit, newCommit: current.commit, descriptorBlob: current.blob, oldSnapshotImmutable: true })] }; } }, { id: "snapshot_and_docs", run: async () => { for (const id of ctx.descriptors.map((item) => item.workspace.id)) { const extracted = join(ctx.run.root, "exports", "extracted", id); for (const name of ZIP_FILES) assert((await lstat(join(extracted, name))).isFile(), `missing extracted ${name}`); const read = (await request(ctx, `read-${id}-snapshot`, "GET", `/workspaces/${id}`)).body; for (const suffix of [".yaml", ".env.example", ".md", "snapshot.json"]) { const file = suffix === "snapshot.json" ? join(dirname(read.revision.snapshotPath), suffix) : join(dirname(read.revision.snapshotPath), `${id}${suffix}`); assert(existsSync(file), `snapshot artifact absent ${file}`); } } return await log("snapshot_and_docs", { exactBundleFiles: ZIP_FILES, generatedDocs: true, immutableSnapshots: true }); } }, { id: "runtime_render_determinism", run: async () => { ctx.configChecks = []; const YAML = await import("yaml"); for (const id of ctx.descriptors.map((item) => item.workspace.id)) { const read = (await request(ctx, `read-${id}-runtime`, "GET", `/workspaces/${id}`)).body; const bytes = []; for (let n = 1; n <= 2; n += 1) { const lease = ctx.thtRunner.acquireWorkspaceRuntime(read.revision.snapshotPath); try { const contents = await readFile(lease.path); bytes.push(contents); await atomicWrite(join(ctx.run.root, "rendered", `${id}-${n}.yaml`), contents); const checked = await tht(ctx.env.THT_BIN, ["config", "check", "-c", lease.path], { cwd: ctx.env.THT_HARNESS_DIR, env: ctx.env, timeoutMs: 30_000 }); ctx.configChecks.push({ id, observation: n, code: checked.code }); } finally { lease.release(); } const runtimeDir = join(ctx.run.root, "installation", "registry", "snapshots", "runtime"); if (existsSync(runtimeDir)) assert((await readdir(runtimeDir)).length === 0, "runtime lease leaked"); } assert(bytes[0].equals(bytes[1]), `render nondeterministic ${id}`); const parsed = YAML.parse(bytes[0].toString("utf8")); assert(parsed.runtime_identity.workspace_id === id && parsed.runtime_identity.workspace_revision === read.revision.commit, "render identity mismatch"); } return { commands: ["tht"], artifacts: [await evidence(ctx.run, "logs/runtime-render.json", { deterministic: true, released: true, workspaces: ctx.descriptors.map((item) => item.workspace.id) })] }; } }, { id: "tht_config_check", run: async () => { assert(ctx.configChecks.length === 6 && ctx.configChecks.every(({ code }) => code === 0), "tht config checks incomplete"); return { commands: ["tht"], artifacts: [await evidence(ctx.run, "logs/tht-config-check.json", ctx.configChecks)] }; } }, { id: "negative_schema_cases", run: async () => { const baselineHead = (await git(["rev-parse", "HEAD"], { cwd: join(ctx.run.root, "installation", "registry", "repo") })).stdout.trim(); const base = structuredClone(ctx.descriptors[0]); const cases = [ ["absolute", (w) => { w.evidence.source.uri = "/tmp/evidence"; }, "evidence.source.uri"], ["traversal", (w) => { w.evidence.source.uri = "workspace-content/p1-filesystem/../evidence"; }, "evidence.source.uri"], ["backslash", (w) => { w.evidence.source.uri = "workspace-content\\p1-filesystem\\evidence"; }, "evidence.source.uri"], ["cross-workspace", (w) => { w.evidence.source.uri = "workspace-content/other/evidence"; }, "evidence.source.uri"], ["unsupported-source", (w) => { w.evidence.source.type = "ftp"; w.evidence.source.uri = "ftp://example.test/file"; }, "evidence.source.type"], ["credential-field", (w) => { w.evidence.source.password = ctx.secretValues.rejected; }, "evidence.source.password"], ["http-userinfo-query", (w) => { w.evidence.source = { type: "http", uris: [`https://user:${ctx.secretValues.rejected}@evidence.example.test/guide?x=${ctx.secretValues.rejected}`], authentication: "none" }; }, "evidence.source.uris"], ["malformed-policy", (w) => { w.evidence.policy.max_chunk_chars = 0; }, "evidence.policy.max_chunk_chars"], ]; const outcomes = []; for (const [id, mutate, field] of cases) { const workspace = structuredClone(base); mutate(workspace); await evidence(ctx.run, `fixtures/requests/negative-${id}.json`, { case: id, expectedField: field, rawCredentialPersisted: false }); const response = await request(ctx, `negative-${id}`, "POST", "/workspaces/validate", { workspace }); assert(response.status === 400 && response.body.code === "workspace_invalid", `negative accepted ${id}`); assert(JSON.stringify(response.body).includes(ctx.secretValues.rejected) === false, `negative leaked ${id}`); const currentHead = (await git(["rev-parse", "HEAD"], { cwd: join(ctx.run.root, "installation", "registry", "repo") })).stdout.trim(); assert(currentHead === baselineHead, `negative mutated head ${id}`); outcomes.push({ id, status: 400, code: "workspace_invalid", field }); } return { commands: ["git"], artifacts: [await evidence(ctx.run, "logs/negative-schema.json", outcomes, ctx.forbiddenValues)] }; } }, { id: "negative_context_case", run: async () => { const { WorkspaceRegistry } = await loadProductionBackend(); const author = join(ctx.run.root, "author"); await git(["checkout", "-b", "invalid-context", ctx.contentCommit], { cwd: author }); await git(["push", "-u", "origin", "invalid-context"], { cwd: author }); const isolatedRoot = join(ctx.run.root, "installation", "registry-context"); const registry = new WorkspaceRegistry({ ...ctx.registryConfig, root: isolatedRoot, branch: "invalid-context" }); await registry.bootstrap(); const before = await registry.read("p1-filesystem"); const missing = baseWorkspace("missing-context", { type: "filesystem", uri: "workspace-content/missing-context/evidence", patterns: ["**/*.md"], max_bytes: 100 }); await assertRejectsCode(() => registry.publish({ action: "create", workspace: missing, baseCommit: ctx.contentCommit }), "workspace_invalid"); await rm(join(author, "workspace-content", "p1-filesystem", "evidence"), { recursive: true }); await git(["add", "-A", "workspace-content/p1-filesystem/evidence"], { cwd: author }); await git(["commit", "-m", "Invalid contextual Evidence state"], { cwd: author }); await git(["push", "origin", "invalid-context"], { cwd: author }); await assertRejectsCode(() => registry.pull(), "workspace_invalid"); const after = await registry.read("p1-filesystem"); assert(after.revision.commit === before.revision.commit, "isolated active snapshot changed"); const primary = (await request(ctx, "primary-after-context", "GET", "/workspaces/p1-filesystem")).body; assert(primary.revision.commit === ctx.contentCommit, "primary state changed"); return { commands: ["git"], artifacts: [await evidence(ctx.run, "logs/negative-context.json", { missingCreateRejected: true, invalidPullRejected: true, lastValidCommit: after.revision.commit, primaryCommit: primary.revision.commit })] }; } }, { id: "no_p1_scope_artifacts", run: async () => { const forbidden = ["artifacts/evidence", "corpus/ACTIVE", "embeddings", "qdrant-records", "preprocessing-invocation"]; const present = (await walkFiles(ctx.run.root)).map(({ rel }) => rel).filter((path) => forbidden.some((part) => path.includes(part))); assert(present.length === 0, "P6 scope artifact created"); return await log("no-p1-scope-artifacts", { absent: forbidden }); } }, { id: "secret_scan", run: async () => { const findings = await scanSecrets({ runRoot: ctx.run.root, forbiddenValues: ctx.forbiddenValues }); assert(findings.length === 0, "secret canary found outside exclusion"); return await log("secret-scan", { scanned: true, excluded: "fixture-secrets", findings: [] }); } }, { id: "cleanup_confinement", run: async () => { const fakeRepo = join(ctx.run.root, "fixtures", "cleanup-repository"); await mkdir(join(fakeRepo, ".artifacts", "p1-integration"), { recursive: true }); const synthetic = await createOwnedRun({ repositoryRoot: fakeRepo }); const sibling = join(fakeRepo, ".artifacts", "p1-integration", `p1-${"e".repeat(32)}`); await mkdir(sibling); await writeFile(join(sibling, "sentinel"), "foreign"); await cleanupOwnedRun({ repositoryRoot: fakeRepo, runRoot: synthetic.root, expectedNonce: synthetic.nonce }); assert(await readFile(join(sibling, "sentinel"), "utf8") === "foreign", "cleanup removed sibling"); return await log("cleanup-confinement", { ownedRemoved: true, siblingPreserved: true }); } }, ]; } async function assertRejectsCode(fn, code) { try { await fn(); } catch (error) { if (error?.code === code) return; throw error; } throw new Error(`expected ${code}`); } export async function runIntegration({ repositoryRoot = defaultRepositoryRoot, keep = false, env = process.env, failAt = env.P1_ACCEPTANCE_FAIL_AT, checks } = {}) { const savedEnv = { ...process.env }; let run; let ctx; let results = []; let fatal; try { run = await createOwnedRun({ repositoryRoot }); if (checks === undefined) { ctx = await setupContext(run, repositoryRoot, env); checks = productionChecks(ctx); } results = await executeChecks({ checks, failAt }); } catch (error) { fatal = error; if (run && results.length === 0) results = [{ id: "preflight", status: "FAIL", startedAt: run.startedAt, finishedAt: nowIso(), commands: [], artifacts: [], error: "Acceptance setup failed safely." }]; } finally { if (ctx?.app) { await ctx.app.close().catch(() => {}); await writeOwnership(run, { ...run.listener, state: "closed" }).catch(() => {}); } for (const key of Object.keys(process.env)) if (!(key in savedEnv)) delete process.env[key]; Object.assign(process.env, savedEnv); } if (!run) throw fatal; const success = !fatal && results.length === checks.length && results.every(({ status }) => status === "PASS"); const report = { schemaVersion: 1, runId: run.runId, startedAt: run.startedAt, finishedAt: nowIso(), command: `p1-acceptance integration${keep ? " --keep" : ""}`, overall: success ? "PASS" : "FAIL", checks: results, }; validateReport(report); let jsonBytes = Buffer.from(`${JSON.stringify(report, null, 2)}\n`); let mdBytes = Buffer.from(renderReportMarkdown(report)); if (ctx?.forbiddenValues) { const findings = await scanSecrets({ runRoot: run.root, forbiddenValues: ctx.forbiddenValues, virtualFiles: [{ path: "report.json", bytes: jsonBytes }, { path: "report.md", bytes: mdBytes }] }); if (findings.length) { report.overall = "FAIL"; const secret = report.checks.find(({ id }) => id === "secret_scan"); if (secret) secret.status = "FAIL"; else report.checks.push({ id: "secret_scan", status: "FAIL", startedAt: nowIso(), finishedAt: nowIso(), commands: [], artifacts: [], error: "Secret scan found protected content." }); jsonBytes = Buffer.from(`${JSON.stringify(report, null, 2)}\n`); mdBytes = Buffer.from(renderReportMarkdown(report)); } } await atomicWrite(join(run.root, "report.json"), jsonBytes); await atomicWrite(join(run.root, "report.md"), mdBytes); const finalSuccess = report.overall === "PASS"; const removed = await finalizeOwnedRun({ run, success: finalSuccess, keep }); return { exitCode: finalSuccess ? 0 : 1, runRoot: run.root, retained: !removed, report }; } export async function main(argv = process.argv.slice(2), env = process.env) { if (argv.length < 1 || argv[0] !== "integration" || argv.length > 2 || (argv.length === 2 && argv[1] !== "--keep")) { console.error("usage: p1-acceptance integration [--keep]"); return 2; } try { const result = await runIntegration({ repositoryRoot: defaultRepositoryRoot, keep: argv.includes("--keep"), env }); if (result.retained) console.log(result.runRoot); return result.exitCode; } catch (error) { console.error("P1 acceptance failed before owning a reportable run."); return 1; } } if (resolve(process.argv[1] ?? "") === modulePath) process.exitCode = await main();