#!/usr/bin/env node const assert = require("assert"); const fs = require("fs"); const path = require("path"); const { assertFinalLedger, readPhase3Ledger, } = require("./phase3-ledger"); const repo = path.resolve(__dirname, ".."); const releaseRoot = path.resolve( process.env.DISASMER_PUBLIC_RELEASE_DIR || path.join(repo, "target/public-release-dryrun") ); const acceptanceRoot = path.join(repo, "target/acceptance"); const serviceEndpoint = "https://disasmer.michelpaulissen.com"; const serviceHost = "disasmer.michelpaulissen.com"; const serviceAddr = `${serviceHost}:443`; const forgejoHost = "git.michelpaulissen.com"; const inputs = { manifest: process.env.DISASMER_PUBLIC_RELEASE_MANIFEST || path.join(releaseRoot, "public-release-manifest.json"), forgejoRelease: process.env.DISASMER_PUBLIC_RELEASE_FORGEJO_REPORT || path.join(acceptanceRoot, "public-release-dryrun-forgejo-release.json"), deployment: process.env.DISASMER_PUBLIC_RELEASE_DEPLOYMENT_MANIFEST || path.join(releaseRoot, "deployment/stage/deployment-manifest.json"), service: process.env.DISASMER_PUBLIC_RELEASE_SERVICE_REPORT || path.join(acceptanceRoot, "public-release-dryrun-service.json"), hostedClientCompat: process.env.DISASMER_HOSTED_CLIENT_COMPAT_REPORT || path.join(acceptanceRoot, "hosted-client-compat.json"), coreCoordinatorCompat: process.env.DISASMER_CORE_COORDINATOR_COMPAT_REPORT || path.join(acceptanceRoot, "core-coordinator-compat.json"), e2e: process.env.DISASMER_PUBLIC_RELEASE_E2E_REPORT || path.join(acceptanceRoot, "public-release-dryrun-e2e.json"), }; assertFinalLedger(readPhase3Ledger(repo)); function readJson(name, file) { assert(fs.existsSync(file), `missing ${name} evidence: ${file}`); return JSON.parse(fs.readFileSync(file, "utf8")); } function assertIncludes(value, expected, message) { assert( typeof value === "string" && value.includes(expected), `${message}: expected ${JSON.stringify(value)} to include ${expected}` ); } function assetNames(manifest) { assert(Array.isArray(manifest.assets), "manifest assets must be an array"); return new Set(manifest.assets.map((asset) => asset.name)); } function uploadedAssetNames(report) { return new Set( [...(report.uploaded_assets || []), ...(report.reused_assets || [])].map( (asset) => asset.name ) ); } function assertEvidenceBooleans(report, fields) { for (const field of fields) { assert.strictEqual(report[field], true, `e2e report must set ${field}=true`); } } function compactToolVersions(...reports) { const versions = {}; for (const [name, report] of reports) { if (report && report.tool_versions) { versions[name] = report.tool_versions; } } return versions; } function assertDnsState(value, name) { assert( ["not-published", "published"].includes(value), `${name} has unexpected DNS publication state: ${value}` ); } function assertFilteredOut(manifest) { assert(Array.isArray(manifest.filtered_out), "manifest filtered_out must be an array"); for (const expected of [ "private/**", "experiments/**", ".git", "target", "root/*.md except README.md and SECURITY.md", "**/.disasmer/**", ]) { assert( manifest.filtered_out.includes(expected), `manifest filtered_out must include ${expected}` ); } const includeForgejoWorkflows = manifest.public_export && manifest.public_export.include_forgejo_workflows; if (!includeForgejoWorkflows) { assert( manifest.filtered_out.includes(".forgejo/**"), "manifest filtered_out must include .forgejo/** for the host-neutral export" ); } } function publicTreeAlreadyPushed(manifest) { return ( (manifest.public_tree_publish && manifest.public_tree_publish.pushed === true) || process.env.DISASMER_PUBLIC_TREE_ALREADY_PUSHED === "1" ); } function publicRepoRemote(manifest) { return ( manifest.public_repo_url || manifest.public_repo_remote || process.env.DISASMER_PUBLIC_REPO_REMOTE || process.env.DISASMER_PUBLIC_REPO_URL || "" ); } function commandOutput(command, args) { try { return require("child_process") .execFileSync(command, args, { cwd: repo, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], }) .trim(); } catch (_) { return null; } } function expectedSourceCommit() { return ( process.env.DISASMER_ACCEPTANCE_COMMIT || commandOutput("git", ["rev-parse", "HEAD"]) || "unknown" ); } const manifest = readJson("public release manifest", inputs.manifest); assert.strictEqual(manifest.kind, "disasmer-public-release-dryrun"); assert.strictEqual( manifest.source_tree_clean, true, "final release evidence cannot be produced from a dirty source tree" ); for (const binary of [ "disasmer", "disasmer-coordinator", "disasmer-node", "disasmer-debug-dap", ]) { const platformName = process.platform === "win32" ? `${binary}.exe` : binary; assert.match( manifest.binary_digests && manifest.binary_digests[platformName], /^sha256:[a-f0-9]{64}$/, `manifest omitted release binary digest for ${platformName}` ); } assert.strictEqual( manifest.source_commit, expectedSourceCommit(), "final public release evidence must be generated from the current acceptance commit" ); assert.strictEqual(manifest.default_hosted_coordinator_endpoint, serviceEndpoint); assert.strictEqual(manifest.forgejo_host, forgejoHost); assertDnsState(manifest.dns_publication_state, "manifest"); assert(manifest.resolver_override, "manifest must record resolver override"); assertFilteredOut(manifest); assert.strictEqual( publicTreeAlreadyPushed(manifest), true, "filtered public tree must be pushed to Forgejo" ); const resolvedPublicRepoRemote = publicRepoRemote(manifest); assertIncludes( resolvedPublicRepoRemote, forgejoHost, "manifest public repository must point at Forgejo" ); const manifestAssets = assetNames(manifest); for (const pattern of [ /^disasmer-public-source-/, /^disasmer-public-binaries-/, /^disasmer-public-evidence-/, /^disasmer-vscode-/, /^DISASMER_PUBLIC_DRYRUN_GETTING_STARTED-/, /^DISASMER_PUBLIC_DRYRUN_INVITE-/, /^SHA256SUMS$/, ]) { assert( [...manifestAssets].some((name) => pattern.test(name)), `manifest is missing asset matching ${pattern}` ); } const forgejoRelease = readJson("Forgejo Release report", inputs.forgejoRelease); assert.strictEqual( forgejoRelease.kind, "disasmer-public-release-dryrun-forgejo-release" ); assertIncludes(forgejoRelease.forgejo_url, forgejoHost, "Forgejo Release host"); assert.strictEqual(forgejoRelease.release_name, manifest.release_name); assert.strictEqual( forgejoRelease.default_hosted_coordinator_endpoint, manifest.default_hosted_coordinator_endpoint ); assert.strictEqual( forgejoRelease.public_tree_identity, manifest.public_tree_identity ); assert.strictEqual(forgejoRelease.source_commit, manifest.source_commit); const releaseAssets = uploadedAssetNames(forgejoRelease); for (const asset of manifestAssets) { assert(releaseAssets.has(asset), `Forgejo Release is missing asset ${asset}`); } const deployment = readJson("deployment manifest", inputs.deployment); assert.strictEqual(deployment.kind, "disasmer-public-release-dryrun-deployment"); assert.strictEqual(deployment.default_hosted_coordinator_endpoint, serviceEndpoint); assert.strictEqual(deployment.coordinator_implementation, "hosted-policy-coordinator"); assert.match( deployment.public_release_meaning || "", /public repository[\s\S]*public release assets[\s\S]*public client protocol/ ); assert.strictEqual(deployment.service_host, serviceHost); assert.strictEqual(deployment.service_addr, serviceAddr); assertDnsState(deployment.dns_publication_state, "deployment"); assert(deployment.resolver_override, "deployment manifest must record resolver override"); assert(deployment.deployed_service_commit, "deployment must record deployed service commit"); const service = readJson("live service smoke report", inputs.service); assert.strictEqual(service.kind, "disasmer-public-release-dryrun-service"); assert.strictEqual(service.source_commit, manifest.source_commit); assert.strictEqual(service.release_name, manifest.release_name); assert.strictEqual(service.endpoint, serviceEndpoint); assert.strictEqual(service.service_addr, serviceAddr); assert.strictEqual(service.coordinator_implementation, "hosted-policy-coordinator"); assertDnsState(service.dns_publication_state, "service smoke"); assert(service.resolver_override, "service smoke must record resolver override"); assert.strictEqual( service.public_tree_identity || manifest.public_tree_identity, manifest.public_tree_identity ); assert.strictEqual( service.deployed_service_commit || deployment.deployed_service_commit, deployment.deployed_service_commit ); assert(service.deployment_config_commit, "service smoke must record deployment config commit"); assert( service.deployment_system_generation, "service smoke must record the activated NixOS system generation" ); assert(service.service_unit, "service smoke must record the active service unit fragment"); for (const key of [ "ping", "login", "projects", "forged_unsigned_project", "session_scope", "server_generated_enrollment_grant", "node_credential", "signed_node_heartbeat", "signed_node_capabilities", "scoped_node_listing", "process_start", "debug_attach", "process_abort", ]) { assert(service.evidence && service.evidence[key], `service smoke missing ${key}`); } assert.strictEqual(service.evidence.forged_unsigned_project, "error"); assert.strictEqual(service.evidence.session_scope, "derived_from_server_identity"); if (service.acceptance_result) { assert.strictEqual(service.acceptance_result, "passed"); } const compat = readJson("hosted Client compatibility report", inputs.hostedClientCompat); assert.strictEqual(compat.kind, "disasmer-hosted-client-compatibility"); assert.strictEqual(compat.source_commit, manifest.source_commit); assert.strictEqual(compat.release_name, manifest.release_name); assert.strictEqual( compat.public_cli_attach.coordinator_response, "node_enrollment_exchanged" ); assert.strictEqual(compat.public_cli_attach.heartbeat_response, "node_heartbeat"); assert.strictEqual(compat.server_owned_browser_login.response, "oidc_browser_session"); assert(compat.server_owned_browser_login.coordinator_requests >= 2); assert.strictEqual( compat.server_owned_browser_login.provider_tokens_exposed_to_cli, false ); assert.strictEqual(compat.server_owned_browser_login.session_scope_derived, true); assert.strictEqual(compat.authority_denials.forged_unsigned_project, "error"); assert.strictEqual(compat.authority_denials.cross_tenant_task_events, "error"); assert.strictEqual(compat.session_lifecycle.logout_revoked, true); assert.strictEqual(compat.session_lifecycle.revoked_reuse, "error"); assert.strictEqual(compat.session_lifecycle.expired_reuse, "error"); const coreCoordinator = readJson( "Core coordinator compatibility report", inputs.coreCoordinatorCompat ); assert.strictEqual(coreCoordinator.kind, "disasmer-core-coordinator-compatibility"); assert.strictEqual(coreCoordinator.source_commit, manifest.source_commit); assert.strictEqual(coreCoordinator.release_name, manifest.release_name); assert.strictEqual( coreCoordinator.coordinator_implementation, "standalone-core-coordinator" ); assert.strictEqual(coreCoordinator.client_authority, "strict"); assert.strictEqual(coreCoordinator.authenticated_session, true); assert.strictEqual(coreCoordinator.forged_body_authority_denied, "error"); assert.strictEqual(coreCoordinator.wrong_session_denied, "error"); assert.strictEqual(coreCoordinator.self_hosted_cli.connected, "connected"); assert.strictEqual(coreCoordinator.self_hosted_cli.secret_read_from_stdin, true); assert.strictEqual(coreCoordinator.self_hosted_cli.secret_exposed_in_report, false); assert.strictEqual(coreCoordinator.self_hosted_cli.authenticated_status, "auth_status"); assert.strictEqual( coreCoordinator.self_hosted_admin.nonce_bound_proof_succeeded, "admin_status" ); assert.strictEqual(coreCoordinator.self_hosted_admin.replay_denied, "error"); if (process.platform !== "win32") { assert.strictEqual(coreCoordinator.self_hosted_cli.session_file_mode, "600"); } assert.strictEqual(coreCoordinator.task_placement, "task_placement"); assert.strictEqual(coreCoordinator.task_completion, "task_recorded"); assert(coreCoordinator.task_events >= 1, "Core coordinator compat must record task events"); assert.strictEqual(coreCoordinator.artifact_export_plan, "artifact_export_plan"); const e2e = readJson("public repository e2e report", inputs.e2e); assert.strictEqual(e2e.kind, "disasmer-public-release-dryrun-e2e"); assert.strictEqual(e2e.default_hosted_coordinator_endpoint, serviceEndpoint); assert.strictEqual(e2e.service_addr, serviceAddr); assert.strictEqual(e2e.launch_task_verified, true); assert.strictEqual(e2e.worker_assignment_poll_verified, true); assert.strictEqual(e2e.worker_assignment_poll_protocol, "poll_task_assignment"); assert.strictEqual(e2e.launch_task_response, "main_launched"); assert.strictEqual(e2e.worker_assignment_process, e2e.process); assert.strictEqual(e2e.core_coordinator_validated, true); assert.strictEqual( e2e.core_coordinator_implementation, "standalone-core-coordinator" ); assert.strictEqual(e2e.core_coordinator_client_authority, "strict"); assert.strictEqual(e2e.core_coordinator_authenticated_session, true); assert.strictEqual(e2e.core_coordinator_forged_body_authority_denied, "error"); assert.strictEqual(e2e.core_coordinator_wrong_session_denied, "error"); assert.strictEqual(e2e.core_coordinator_launch_task_response, "task_launched"); assert.strictEqual(e2e.core_coordinator_assignment_response, "task_assignment"); assert(e2e.core_coordinator_task_events >= 1, "e2e must record public coordinator task events"); assert.strictEqual(e2e.released_live_dap_verified, true); assert.strictEqual( e2e.released_live_dap_coordinator_implementation, "hosted-policy-coordinator" ); assert.strictEqual(e2e.released_live_dap_attached_to_client_process, true); assert.strictEqual(e2e.released_live_dap_authenticated_with_cli_session, true); assert(e2e.released_live_dap_task_events >= 1, "e2e must record released live-DAP task events"); assert( e2e.evidence && e2e.evidence.released_live_dap && e2e.evidence.released_live_dap.attached_to_existing_client_process === true, "e2e must prove released DAP attaches to the existing Client-authorized process" ); assert( e2e.evidence && e2e.evidence.released_live_dap && e2e.evidence.released_live_dap.variables_verified === true, "e2e must prove released live-DAP variables are inspectable" ); if (e2e.dns_publication_state) { assertDnsState(e2e.dns_publication_state, "public repository e2e"); } if (e2e.acceptance_result) { assert.strictEqual(e2e.acceptance_result, "passed"); } assertIncludes(e2e.public_repository_url, forgejoHost, "e2e public repo URL"); assert.strictEqual(e2e.release_name, manifest.release_name); assert.strictEqual(e2e.public_tree_identity, manifest.public_tree_identity); assert.strictEqual(e2e.source_commit, manifest.source_commit); assertEvidenceBooleans(e2e, [ "downloaded_release_assets", "verified_checksums", "clean_public_checkout", "public_repo_build_or_install", "default_operator_selected", "browser_or_cli_login", "attached_user_node", "core_coordinator_validated", "released_live_dap_verified", "ran_flagship_workflow", "vscode_debugger_verified", "logs_verified", "artifact_metadata_verified", "artifact_download_or_export_verified", ]); assert(Array.isArray(e2e.commands) && e2e.commands.length > 0); assert(Array.isArray(e2e.tool_versions) || typeof e2e.tool_versions === "object"); const finalReport = { kind: "disasmer-public-release-dryrun-final-evidence", release_name: manifest.release_name, source_commit: manifest.source_commit, public_tree_identity: manifest.public_tree_identity, deployed_service_commit: deployment.deployed_service_commit, default_hosted_coordinator_endpoint: serviceEndpoint, service_addr: serviceAddr, dns_publication_state: { manifest: manifest.dns_publication_state, deployment: deployment.dns_publication_state, service: service.dns_publication_state, public_repository_e2e: e2e.dns_publication_state || null, }, resolver_override: { manifest: manifest.resolver_override, deployment: deployment.resolver_override, service: service.resolver_override, public_repository_e2e: e2e.resolver_override || null, }, deployment_config: { repo: service.deployment_config_repo || null, commit: service.deployment_config_commit || null, system_generation: service.deployment_system_generation || null, service_unit: service.service_unit || null, }, coordinator_validation: { private_hosted_default_operator: { coordinator_implementation: service.coordinator_implementation, service_addr: service.service_addr, launch_task: service.evidence.launch_task, assignment_poll: service.evidence.assignment_poll, released_live_dap: e2e.evidence.released_live_dap, }, standalone_core_coordinator: { coordinator_implementation: coreCoordinator.coordinator_implementation, client_authority: coreCoordinator.client_authority, authenticated_session: coreCoordinator.authenticated_session, forged_body_authority_denied: coreCoordinator.forged_body_authority_denied, wrong_session_denied: coreCoordinator.wrong_session_denied, self_hosted_cli: coreCoordinator.self_hosted_cli, task_placement: coreCoordinator.task_placement, task_events: coreCoordinator.task_events, release_binary_e2e: { client_authority: e2e.core_coordinator_client_authority, authenticated_session: e2e.core_coordinator_authenticated_session, forged_body_authority_denied: e2e.core_coordinator_forged_body_authority_denied, wrong_session_denied: e2e.core_coordinator_wrong_session_denied, launch_task: e2e.core_coordinator_launch_task_response, assignment_poll: e2e.core_coordinator_assignment_response, task_events: e2e.core_coordinator_task_events, }, }, }, forgejo_host: forgejoHost, public_repository_url: resolvedPublicRepoRemote, forgejo_release: { owner: forgejoRelease.owner, repo: forgejoRelease.repo, release_id: forgejoRelease.release_id, asset_count: releaseAssets.size, }, tool_versions: compactToolVersions( ["manifest", manifest], ["service_smoke", service], ["public_repository_e2e", e2e] ), acceptance_results: { public_release_preparation: { commands: manifest.commands, result: "passed", }, deployment_bundle: { commands: deployment.commands, result: "passed", }, live_service_smoke: { command: service.acceptance_command || null, result: service.acceptance_result || "passed", evidence: service.evidence, }, core_coordinator_compatibility: { result: "passed", evidence: coreCoordinator, }, public_repository_e2e: { commands: e2e.commands, result: "passed", evidence: e2e.evidence, }, }, evidence_files: inputs, }; const output = path.join(acceptanceRoot, "public-release-dryrun-final.json"); fs.mkdirSync(path.dirname(output), { recursive: true }); fs.writeFileSync(output, `${JSON.stringify(finalReport, null, 2)}\n`); console.log(`Public release dry-run final evidence passed: ${output}`);