63 lines
2.6 KiB
TypeScript
63 lines
2.6 KiB
TypeScript
import * as fs from "node:fs";
|
|
import * as path from "node:path";
|
|
import { loadFactoryCapabilities, type FactoryCapabilityService } from "./factory-capabilities.js";
|
|
import type { CyberPreflightReceipt } from "./cyber-preflight.js";
|
|
|
|
export interface CyberReconcileFinding {
|
|
capability: string;
|
|
expected: string;
|
|
evidence: string;
|
|
status: "ok" | "missing";
|
|
}
|
|
|
|
export interface CyberReconcileReceipt {
|
|
created_at: string;
|
|
preflight: string;
|
|
capabilities: string;
|
|
findings: CyberReconcileFinding[];
|
|
decision: "allow_read_only" | "fail_closed";
|
|
deploy_allowed: false;
|
|
recommendation: string;
|
|
}
|
|
|
|
export function reconcileCyberPreflight(preflightFile: string, capabilitiesFile: string, now = new Date()): CyberReconcileReceipt {
|
|
const preflight = JSON.parse(fs.readFileSync(preflightFile, "utf-8")) as CyberPreflightReceipt;
|
|
const capabilities = loadFactoryCapabilities(capabilitiesFile);
|
|
const findings = capabilities.flatMap((service) => findingsForService(service, preflight));
|
|
const allOk = findings.every((finding) => finding.status === "ok") && preflight.decision === "allow_read_only";
|
|
|
|
return {
|
|
created_at: now.toISOString(),
|
|
preflight: preflightFile,
|
|
capabilities: capabilitiesFile,
|
|
findings,
|
|
decision: allOk ? "allow_read_only" : "fail_closed",
|
|
deploy_allowed: false,
|
|
recommendation: allOk ? "read-only posture verified; deploy still requires separate approval" : "hold position; restore telemetry routes or update capability registry",
|
|
};
|
|
}
|
|
|
|
export function writeCyberReconcileReceipt(file: string, receipt: CyberReconcileReceipt): void {
|
|
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
fs.writeFileSync(file, `${JSON.stringify(receipt, null, 2)}\n`);
|
|
}
|
|
|
|
function findingsForService(service: FactoryCapabilityService, preflight: CyberPreflightReceipt): CyberReconcileFinding[] {
|
|
return service.provides.map((capability) => {
|
|
const check = matchingCheck(capability, preflight);
|
|
return {
|
|
capability,
|
|
expected: service.url,
|
|
evidence: check ? `${check.name}: ${check.ok ? "ok" : `failed status=${check.status} ${check.stderr.trim()}`}` : "no matching preflight check",
|
|
status: check?.ok ? "ok" : "missing",
|
|
};
|
|
});
|
|
}
|
|
|
|
function matchingCheck(capability: string, preflight: CyberPreflightReceipt) {
|
|
if (capability === "deploy") return preflight.checks.find((check) => check.name === "deploy_route_health");
|
|
if (capability === "factory-status") return preflight.checks.find((check) => check.name === "git_proxy_factory_status");
|
|
if (capability === "discovery") return preflight.checks.find((check) => check.name === "git_proxy_health");
|
|
return undefined;
|
|
}
|