clusterflux-public/scripts/cli-happy-path-live-smoke.js
Disasmer release dry run 2bef715211 Public dry run dryrun-20b59dc46b72
Source commit: 20b59dc46b72103c2f8a516c692b5cc3d54fab19

Public tree identity: sha256:aaa5ac7b58b2a0d23c6b11e5f76324bf3839ca1f62653a83deb736932adcfbd9
2026-07-14 10:08:15 +02:00

2116 lines
65 KiB
JavaScript

#!/usr/bin/env node
const assert = require("assert");
const cp = require("child_process");
const crypto = require("crypto");
const fs = require("fs");
const https = require("https");
const os = require("os");
const path = require("path");
const { DapClient } = require("./dap-client");
const { agentIdentity, signedAgentWorkflowRequest } = require("./agent-signing");
const { coordinatorWireRequest } = require("./coordinator-wire");
const {
nodeIdentity,
nodeIdentityFromPrivateKey,
signedNodeHeartbeat,
signedNodeRequest,
} = require("./node-signing");
const { configurePodmanTestEnvironment } = require("./podman-test-env");
const repo = path.resolve(__dirname, "..");
configurePodmanTestEnvironment(repo);
if (
!process.env.DISASMER_PODMAN_NIX_SHELL &&
cp.spawnSync("podman", ["--version"], { stdio: "ignore" }).status !== 0 &&
cp.spawnSync("nix", ["--version"], { stdio: "ignore" }).status === 0
) {
cp.execFileSync(
"nix",
["shell", "nixpkgs#podman", "--command", "node", __filename],
{
cwd: repo,
env: { ...process.env, DISASMER_PODMAN_NIX_SHELL: "1" },
stdio: "inherit",
}
);
process.exit(0);
}
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 manifestPath = path.join(releaseRoot, "public-release-manifest.json");
const reportPath = path.join(acceptanceRoot, "cli-happy-path-live.json");
const serviceEndpoint = "https://disasmer.michelpaulissen.com";
const serviceAddr =
process.env.DISASMER_PUBLIC_RELEASE_DRYRUN_SERVICE_ADDR ||
"disasmer.michelpaulissen.com:443";
const browserOpenCommand =
process.env.DISASMER_PUBLIC_RELEASE_DRYRUN_BROWSER_OPEN_COMMAND;
const enabled = process.env.DISASMER_CLI_HAPPY_PATH_LIVE === "1";
const strictFullRelease = process.env.DISASMER_STRICT_FULL_RELEASE === "1";
const strictVpsRestart = process.env.DISASMER_STRICT_VPS_RESTART === "1";
const strictVpsHost = process.env.DISASMER_STRICT_VPS_HOST;
const strictVpsIdentity = process.env.DISASMER_STRICT_VPS_IDENTITY;
const strictServiceUnit =
process.env.DISASMER_STRICT_SERVICE_UNIT ||
"disasmer-public-release-dryrun.service";
const strictSoakSeconds = Number(
process.env.DISASMER_STRICT_SOAK_SECONDS || "300"
);
const commands = [];
function requireEnabled() {
if (!enabled) {
throw new Error(
"DISASMER_CLI_HAPPY_PATH_LIVE=1 is required because this runs the live CLI happy path"
);
}
if (!browserOpenCommand) {
throw new Error(
"DISASMER_PUBLIC_RELEASE_DRYRUN_BROWSER_OPEN_COMMAND is required to complete the server-owned hosted browser login"
);
}
if (
strictFullRelease &&
!process.env.DISASMER_SECOND_TENANT_SESSION_FILE &&
!process.env.DISASMER_SECOND_TENANT_EVIDENCE_FILE
) {
throw new Error(
"DISASMER_STRICT_FULL_RELEASE=1 requires DISASMER_SECOND_TENANT_SESSION_FILE or DISASMER_SECOND_TENANT_EVIDENCE_FILE for the isolation proof"
);
}
if (strictFullRelease && !process.env.DISASMER_EXPIRED_USER_SESSION_FILE) {
throw new Error(
"DISASMER_STRICT_FULL_RELEASE=1 requires DISASMER_EXPIRED_USER_SESSION_FILE"
);
}
if (strictFullRelease && !strictVpsRestart) {
throw new Error(
"DISASMER_STRICT_FULL_RELEASE=1 requires DISASMER_STRICT_VPS_RESTART=1"
);
}
if (strictVpsRestart && (!strictVpsHost || !strictVpsIdentity)) {
throw new Error(
"strict VPS restart requires DISASMER_STRICT_VPS_HOST and DISASMER_STRICT_VPS_IDENTITY"
);
}
if (
!Number.isInteger(strictSoakSeconds) ||
strictSoakSeconds < 120 ||
strictSoakSeconds > 1800
) {
throw new Error("DISASMER_STRICT_SOAK_SECONDS must be an integer from 120 to 1800");
}
}
function readJson(file) {
return JSON.parse(fs.readFileSync(file, "utf8"));
}
function ensureDir(dir) {
fs.mkdirSync(dir, { recursive: true });
}
function sha256(bytes) {
return `sha256:${crypto.createHash("sha256").update(bytes).digest("hex")}`;
}
function nonInteractiveGitEnv(extra = {}) {
return {
...process.env,
GIT_TERMINAL_PROMPT: "0",
GIT_ASKPASS: process.env.GIT_ASKPASS || "/bin/false",
SSH_ASKPASS: process.env.SSH_ASKPASS || "/bin/false",
GIT_SSH_COMMAND:
process.env.GIT_SSH_COMMAND ||
"ssh -o BatchMode=yes -o NumberOfPasswordPrompts=0",
...extra,
};
}
function commandEnv(command, env) {
if (command !== "git") return env;
return nonInteractiveGitEnv(env);
}
function recordCommand(command, args) {
const redacted = [];
let hideNext = false;
for (const argument of args) {
if (hideNext) {
redacted.push("<redacted>");
hideNext = false;
} else {
redacted.push(argument);
hideNext = ["--enrollment-grant", "--admin-token"].includes(argument);
}
}
commands.push([command, ...redacted].join(" "));
}
function run(command, args, options = {}) {
recordCommand(command, args);
return cp.execFileSync(command, args, {
cwd: repo,
encoding: "utf8",
stdio: ["ignore", "pipe", "pipe"],
timeout: 15 * 60 * 1000,
...options,
env: commandEnv(command, options.env),
});
}
function parseJsonOutput(output) {
const trimmed = output.trim();
if (!trimmed) throw new Error("expected JSON output, got empty stdout");
try {
return JSON.parse(trimmed);
} catch (_) {
// Commands may print progress before their final JSON report.
}
const lines = trimmed.split(/\r?\n/).filter(Boolean);
for (let index = lines.length - 1; index >= 0; index -= 1) {
try {
return JSON.parse(lines.slice(index).join("\n"));
} catch (_) {
// Keep scanning for the trailing JSON value.
}
}
throw new Error(`could not parse JSON output:\n${trimmed}`);
}
function runJson(command, args, options = {}) {
return parseJsonOutput(run(command, args, options));
}
function authenticatedRequest(sessionSecret, request) {
return {
type: "authenticated",
session_secret: sessionSecret,
request,
};
}
function sendHostedControl(payload) {
const body = Buffer.from(
JSON.stringify(coordinatorWireRequest(payload, "strict-live"))
);
const url = new URL("/api/v1/control", serviceEndpoint);
return new Promise((resolve, reject) => {
const request = https.request(
url,
{
method: "POST",
headers: {
"content-type": "application/json",
"content-length": body.length,
},
timeout: 30_000,
},
(response) => {
const chunks = [];
response.on("data", (chunk) => chunks.push(chunk));
response.on("end", () => {
const responseBody = Buffer.concat(chunks).toString("utf8");
if (response.statusCode < 200 || response.statusCode >= 300) {
reject(
new Error(
`hosted control HTTP ${response.statusCode}: ${responseBody}`
)
);
return;
}
try {
resolve(JSON.parse(responseBody));
} catch (error) {
reject(
new Error(`hosted control returned invalid JSON: ${error.message}`)
);
}
});
}
);
request.on("timeout", () =>
request.destroy(new Error("hosted control request timed out"))
);
request.on("error", reject);
request.end(body);
});
}
function assertDenied(response, pattern, label) {
assert.strictEqual(response.type, "error", `${label}: ${JSON.stringify(response)}`);
assert.match(response.message, pattern, `${label}: ${JSON.stringify(response)}`);
return {
denied: true,
category: response.error?.category || null,
message: response.message,
};
}
function readNodeCredential(projectDir, node) {
const directory = path.join(projectDir, ".disasmer", "nodes");
for (const file of fs.readdirSync(directory)) {
if (!file.endsWith(".json")) continue;
const absolute = path.join(directory, file);
const credential = readJson(absolute);
if (credential.node === node) return { absolute, credential };
}
throw new Error(`persisted credential for ${node} was not found`);
}
function directoryStats(root) {
if (!fs.existsSync(root)) return { files: 0, bytes: 0 };
let files = 0;
let bytes = 0;
const visit = (directory) => {
for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
const absolute = path.join(directory, entry.name);
if (entry.isDirectory()) visit(absolute);
else if (entry.isFile()) {
files += 1;
bytes += fs.statSync(absolute).size;
}
}
};
visit(root);
return { files, bytes };
}
function sshArgs(...remoteArgs) {
assert(strictVpsHost && strictVpsIdentity);
return [
"-i",
path.resolve(strictVpsIdentity.replace(/^~(?=\/)/, os.homedir())),
"-o",
"BatchMode=yes",
strictVpsHost,
...remoteArgs,
];
}
function strictInfrastructureSample(projectDir) {
const memory = Object.fromEntries(
run(
"ssh",
sshArgs(
"systemctl",
"show",
strictServiceUnit,
"--property=MemoryCurrent",
"--property=MemoryPeak"
)
)
.trim()
.split(/\r?\n/)
.map((line) => line.split("="))
);
const serviceDisk = Number(
run(
"ssh",
sshArgs("du", "-sb", "/var/lib/disasmer-public-release-dryrun")
)
.trim()
.split(/\s+/)[0]
);
const database = run(
"ssh",
sshArgs(
"runuser",
"-u",
"postgres",
"--",
"psql",
"-d",
"disasmer",
"-AtF,"
),
{
input:
"SELECT pg_database_size('disasmer'), (SELECT count(*) FROM disasmer_tenants), (SELECT count(*) FROM disasmer_projects), (SELECT count(*) FROM disasmer_node_identities), (SELECT count(*) FROM disasmer_cli_sessions);\n",
stdio: ["pipe", "pipe", "pipe"],
}
)
.trim()
.split(",")
.map(Number);
assert(Number.isFinite(Number(memory.MemoryCurrent)));
assert(Number.isFinite(Number(memory.MemoryPeak)));
assert.strictEqual(database.length, 5);
return {
sampled_at_epoch_ms: Date.now(),
service_memory_current_bytes: Number(memory.MemoryCurrent),
service_memory_peak_bytes: Number(memory.MemoryPeak),
service_state_disk_bytes: serviceDisk,
postgres_database_bytes: database[0],
durable_rows: {
tenants: database[1],
projects: database[2],
node_identities: database[3],
cli_sessions: database[4],
},
local_node_state: directoryStats(path.join(projectDir, ".disasmer")),
};
}
function executable(root, name) {
return path.join(root, "bin", process.platform === "win32" ? `${name}.exe` : name);
}
function binaryArchive(manifest) {
const platform = `${os.platform()}-${os.arch()}`;
const asset = manifest.assets.find(
(candidate) =>
candidate.name.startsWith("disasmer-public-binaries-") &&
candidate.name.includes(platform)
);
assert(asset, `missing released binary archive for ${platform}`);
assert(fs.existsSync(asset.file), `missing released binary archive ${asset.file}`);
return asset.file;
}
function publicRepoUrl(manifest) {
const url =
process.env.DISASMER_PUBLIC_REPO_URL ||
process.env.DISASMER_PUBLIC_REPO_REMOTE ||
manifest.public_repo_url ||
manifest.public_repo_remote;
assert(url, "public repository URL is required");
assert(url.includes("git.michelpaulissen.com"));
return url;
}
function stagePublicCheckout(manifest, checkout) {
const candidateTree = process.env.DISASMER_LIVE_PUBLIC_TREE;
if (candidateTree) {
const source = path.resolve(candidateTree);
assert.strictEqual(
source,
path.resolve(manifest.public_tree),
"explicit live candidate tree must be the tree recorded by the release manifest"
);
fs.cpSync(source, checkout, { recursive: true });
return {
kind: "local_filtered_release_candidate",
path: source,
published: false,
};
}
run("git", ["clone", "--depth", "1", publicRepoUrl(manifest), checkout]);
return {
kind: "published_forgejo_checkout",
url: publicRepoUrl(manifest),
published: true,
};
}
function delay(milliseconds) {
return new Promise((resolve) => setTimeout(resolve, milliseconds));
}
async function waitForCli(label, action, predicate, timeoutMs = 10 * 60 * 1000) {
const deadline = Date.now() + timeoutMs;
let lastValue;
let lastError;
while (Date.now() < deadline) {
try {
lastValue = action();
if (predicate(lastValue)) return lastValue;
lastError = undefined;
} catch (error) {
lastError = error;
}
await delay(250);
}
throw new Error(
`${label} timed out; last value=${JSON.stringify(lastValue)}${
lastError ? `; last error=${lastError.message}` : ""
}`
);
}
function spawnJsonLines(command, args, options) {
recordCommand(command, args);
const child = cp.spawn(command, args, {
...options,
stdio: ["ignore", "pipe", "pipe"],
});
let stdout = "";
let stderr = "";
const values = [];
const waiters = [];
function deliver(value) {
values.push(value);
for (let index = waiters.length - 1; index >= 0; index -= 1) {
const waiter = waiters[index];
if (waiter.predicate(value)) {
waiters.splice(index, 1);
clearTimeout(waiter.timer);
waiter.resolve(value);
}
}
}
child.stdout.on("data", (chunk) => {
stdout += chunk.toString();
while (stdout.includes("\n")) {
const newline = stdout.indexOf("\n");
const line = stdout.slice(0, newline).trim();
stdout = stdout.slice(newline + 1);
if (!line) continue;
try {
deliver(JSON.parse(line));
} catch (_) {
// Non-JSON progress stays out of the evidence report.
}
}
});
child.stderr.on("data", (chunk) => {
stderr += chunk.toString();
});
function waitFor(predicate, label, timeoutMs = 120000) {
const existing = values.find(predicate);
if (existing) return Promise.resolve(existing);
return new Promise((resolve, reject) => {
const waiter = { predicate, resolve, reject, timer: undefined };
waiter.timer = setTimeout(() => {
const index = waiters.indexOf(waiter);
if (index >= 0) waiters.splice(index, 1);
reject(new Error(`${label} timed out\n${stderr}`));
}, timeoutMs);
waiters.push(waiter);
});
}
child.once("exit", (code) => {
for (const waiter of waiters.splice(0)) {
clearTimeout(waiter.timer);
waiter.reject(new Error(`${waiter.label || "child"} exited with ${code}\n${stderr}`));
}
});
return { child, values, waitFor, stderr: () => stderr };
}
async function stopChild(child) {
if (!child || child.exitCode !== null) return;
child.kill("SIGTERM");
const exited = new Promise((resolve) => child.once("exit", resolve));
const forced = delay(5000).then(() => {
if (child.exitCode === null) child.kill("SIGKILL");
});
await Promise.race([exited, forced]);
if (child.exitCode === null) await exited;
}
function rawTaskEvents(report) {
return report?.events?.response?.events || [];
}
function completedFlagship(events) {
const completedDefinitions = new Set(
events
.filter((event) => event.terminal_state === "completed")
.map((event) => event.task_definition)
);
return (
completedDefinitions.has("prepare_source") &&
completedDefinitions.has("compile_linux") &&
completedDefinitions.has("package_release") &&
events.some(
(event) =>
event.executor === "coordinator_main" && event.terminal_state === "completed"
)
);
}
function contentAddressedArtifactId(event, logicalName) {
const digest = event?.artifact_digest;
if (typeof digest !== "string" || !/^sha256:[0-9a-f]{64}$/.test(digest)) {
return null;
}
const artifactId = `${logicalName}-${digest.slice("sha256:".length)}`;
return event.artifact_path?.endsWith(`/vfs/artifacts/${artifactId}`)
? artifactId
: null;
}
async function prepareLiveNodeCredentialSecurity({
disasmer,
projectDir,
scope,
tenant,
project,
suffix,
}) {
const node = `security-node-${suffix}`;
const grant = runJson(disasmer, ["node", "enroll", ...scope], {
cwd: projectDir,
});
const attach = runJson(
disasmer,
[
"node",
"attach",
"--coordinator",
serviceEndpoint,
"--tenant",
tenant,
"--project-id",
project,
"--node",
node,
"--enrollment-grant",
grant.enrollment_grant.grant,
"--json",
],
{ cwd: projectDir }
);
assert.strictEqual(attach.boundary.used_enrollment_exchange, true);
const stored = readNodeCredential(projectDir, node);
const identity = nodeIdentityFromPrivateKey(stored.credential.private_key);
assert.strictEqual(identity.publicKey, stored.credential.public_key);
const heartbeat = {
type: "node_heartbeat",
node,
node_signature: signedNodeHeartbeat(node, identity, {
nonce: `strict-valid-${suffix}`,
}),
};
const valid = await sendHostedControl(heartbeat);
assert.strictEqual(valid.type, "node_heartbeat");
const replay = assertDenied(
await sendHostedControl(heartbeat),
/nonce.*already.*used|replay/i,
"replayed node credential"
);
const expired = assertDenied(
await sendHostedControl({
type: "node_heartbeat",
node,
node_signature: signedNodeHeartbeat(node, identity, {
nonce: `strict-expired-${suffix}`,
issuedAtEpochSeconds: Math.floor(Date.now() / 1000) - 3600,
}),
}),
/expired|clock skew/i,
"expired node credential"
);
const forgedIdentity = nodeIdentity("strict-forged-node", node);
const forged = assertDenied(
await sendHostedControl({
type: "node_heartbeat",
node,
node_signature: signedNodeHeartbeat(node, forgedIdentity, {
nonce: `strict-forged-${suffix}`,
}),
}),
/signature|public key/i,
"forged node credential"
);
const originalCapabilityBody = {
type: "report_node_capabilities",
tenant,
project,
node,
capabilities: {
os: "Linux",
arch: process.arch === "x64" ? "x86_64" : process.arch,
capabilities: [],
environment_backends: [],
source_providers: [],
},
cached_environment_digests: [],
dependency_cache_digests: [],
source_snapshots: [],
artifact_locations: [],
direct_connectivity: false,
online: true,
};
const modifiedEnvelope = signedNodeRequest(
node,
identity,
"report_node_capabilities",
originalCapabilityBody,
{ nonce: `strict-modified-${suffix}` }
);
modifiedEnvelope.request.online = false;
const bodyModified = assertDenied(
await sendHostedControl(modifiedEnvelope),
/signature/i,
"body-modified node credential"
);
return {
node,
identity,
credential_path: stored.absolute,
credential_digest: sha256(fs.readFileSync(stored.absolute)),
capability_body: originalCapabilityBody,
evidence: {
valid_request: valid.type,
replay,
expired,
forged,
body_modified: bodyModified,
},
};
}
async function runLiveAgentCredentialSecurity({
disasmer,
projectDir,
scope,
sessionSecret,
tenant,
project,
suffix,
}) {
const agent = `security-agent-${suffix}`;
const identity = agentIdentity("strict-live-agent", agent);
const added = runJson(
disasmer,
["key", "add", ...scope, "--agent", agent, "--public-key", identity.publicKey],
{ cwd: projectDir }
);
assert.strictEqual(added.command, "key add");
const processId = `vp-agent-security-${suffix}`;
const unsigned = {
type: "start_process",
tenant,
project,
actor_agent: agent,
process: processId,
restart: false,
};
const validRequest = signedAgentWorkflowRequest(identity, unsigned, {
nonce: `strict-agent-valid-${suffix}`,
});
const valid = await sendHostedControl(validRequest);
assert.strictEqual(valid.type, "process_started", JSON.stringify(valid));
const replay = assertDenied(
await sendHostedControl(validRequest),
/nonce.*already.*used|replay/i,
"replayed agent credential"
);
const expired = assertDenied(
await sendHostedControl(
signedAgentWorkflowRequest(
identity,
{ ...unsigned, process: `${processId}-expired` },
{
nonce: `strict-agent-expired-${suffix}`,
issuedAtEpochSeconds: Math.floor(Date.now() / 1000) - 3600,
}
)
),
/expired|clock skew/i,
"expired agent credential"
);
const modifiedRequest = signedAgentWorkflowRequest(
identity,
{ ...unsigned, process: `${processId}-modified` },
{ nonce: `strict-agent-modified-${suffix}` }
);
modifiedRequest.restart = true;
const bodyModified = assertDenied(
await sendHostedControl(modifiedRequest),
/signature/i,
"body-modified agent credential"
);
const forgedIdentity = agentIdentity("strict-live-forged-agent", agent);
const forged = assertDenied(
await sendHostedControl(
signedAgentWorkflowRequest(
forgedIdentity,
{ ...unsigned, process: `${processId}-forged` },
{
nonce: `strict-agent-forged-${suffix}`,
publicKeyFingerprint: identity.publicKeyFingerprint,
}
)
),
/signature|public key/i,
"forged agent credential"
);
const aborted = await sendHostedControl(
authenticatedRequest(sessionSecret, {
type: "abort_process",
process: processId,
})
);
assert.strictEqual(aborted.type, "process_aborted", JSON.stringify(aborted));
const revoked = runJson(
disasmer,
["key", "revoke", ...scope, "--agent", agent, "--yes"],
{ cwd: projectDir }
);
assert.strictEqual(revoked.command, "key revoke");
const revokedUse = assertDenied(
await sendHostedControl(
signedAgentWorkflowRequest(
identity,
{ ...unsigned, process: `${processId}-revoked` },
{ nonce: `strict-agent-revoked-${suffix}` }
)
),
/revoked/i,
"revoked agent credential"
);
return {
agent,
valid_request: valid.type,
replay,
expired,
forged,
body_modified: bodyModified,
revoked: revokedUse,
};
}
async function runSecondTenantIsolation({
firstTenant,
firstProject,
firstProcess,
firstNode,
firstArtifact,
manifest,
}) {
const file = process.env.DISASMER_SECOND_TENANT_SESSION_FILE;
const evidenceFile = process.env.DISASMER_SECOND_TENANT_EVIDENCE_FILE;
if (!file && evidenceFile) {
const evidenceBytes = fs.readFileSync(path.resolve(evidenceFile));
const previous = JSON.parse(evidenceBytes);
const isolation = previous.tenant_isolation;
assert.strictEqual(isolation?.executed, true);
assert.notStrictEqual(isolation.second_tenant, firstTenant);
assert.strictEqual(isolation.project?.denied, true);
assert.strictEqual(isolation.process_hidden, true);
assert.strictEqual(isolation.node_hidden, true);
assert.strictEqual(isolation.tasks_and_logs?.denied, true);
assert.strictEqual(isolation.debug?.denied, true);
assert.strictEqual(isolation.debug?.audited, true);
assert.strictEqual(isolation.debug?.charged_debug_read_bytes, 0);
assert.strictEqual(isolation.artifact_and_download?.denied, true);
assert.strictEqual(isolation.process_control?.denied, true);
const previousDeployment = previous.release_binding?.deployment;
const currentDeployment = deploymentProvenance();
assert.strictEqual(
previousDeployment?.hosted_service_sha256,
currentDeployment.hosted_service_sha256,
"reused isolation evidence must target the exact deployed hosted binary"
);
assert.deepStrictEqual(
previous.release_binding?.binary_digests,
manifest.binary_digests,
"reused isolation evidence must target the exact public binaries"
);
return {
...isolation,
reused_from_immutable_evidence: {
report_sha256: sha256(evidenceBytes),
source_commit: previous.source_commit,
hosted_service_sha256: currentDeployment.hosted_service_sha256,
public_binary_digests: manifest.binary_digests,
},
};
}
if (!file) return { executed: false, reason: "second tenant session not supplied" };
const second = readJson(path.resolve(file));
assert.strictEqual(second.coordinator, serviceEndpoint);
assert(second.session_secret, "second tenant session omitted its session secret");
assert.notStrictEqual(
second.tenant,
firstTenant,
"isolation proof requires a genuinely distinct hosted tenant"
);
const call = (request) =>
sendHostedControl(authenticatedRequest(second.session_secret, request));
const project = assertDenied(
await call({ type: "select_project", project: firstProject }),
/outside|not visible|tenant|project/i,
"cross-tenant project selection"
);
const processes = await call({ type: "list_processes" });
assert.strictEqual(
processes.type,
"process_statuses",
JSON.stringify(processes)
);
assert(!JSON.stringify(processes).includes(firstProcess));
const nodes = await call({ type: "list_node_descriptors" });
assert.strictEqual(nodes.type, "node_descriptors", JSON.stringify(nodes));
assert(!JSON.stringify(nodes).includes(firstNode));
const tasksAndLogs = assertDenied(
await call({ type: "list_task_events", process: firstProcess }),
/outside|scope|tenant|project|not active|requires an active|unknown/i,
"cross-tenant task and log listing"
);
const debugResponse = await call({
type: "debug_attach",
process: firstProcess,
});
assert.strictEqual(debugResponse.type, "debug_attach", JSON.stringify(debugResponse));
assert.strictEqual(debugResponse.authorization?.allowed, false);
assert.strictEqual(debugResponse.audit_event?.allowed, false);
assert.match(
debugResponse.authorization?.reason || "",
/outside|scope|permission|tenant|project|not active|unknown/i
);
assert.strictEqual(debugResponse.charged_debug_read_bytes, 0);
const debug = {
denied: true,
reason: debugResponse.authorization.reason,
audited: true,
charged_debug_read_bytes: debugResponse.charged_debug_read_bytes,
};
const artifact = assertDenied(
await call({
type: "create_artifact_download_link",
artifact: firstArtifact,
max_bytes: 1024,
ttl_seconds: 60,
}),
/outside|scope|tenant|project|not found|unavailable/i,
"cross-tenant artifact download"
);
const control = assertDenied(
await call({ type: "abort_process", process: firstProcess }),
/outside|scope|tenant|project|not active|requires an active|unknown/i,
"cross-tenant process control"
);
return {
executed: true,
second_tenant: second.tenant,
project,
process_hidden: true,
node_hidden: true,
tasks_and_logs: tasksAndLogs,
debug,
artifact_and_download: artifact,
process_control: control,
};
}
async function runLiveSoak({
disasmer,
projectDir,
scope,
securityNode,
}) {
if (!strictVpsRestart) {
return { executed: false, reason: "strict VPS measurement access not supplied" };
}
const runReport = runJson(
disasmer,
["run", "build", "--project", ".", "--json"],
{ cwd: projectDir }
);
assert.strictEqual(runReport.status, "main_launched");
const processId = runReport.process;
const parked = await waitForCli(
"soak coordinator main to park without a capable node",
() =>
runJson(
disasmer,
["process", "status", ...scope, "--process", processId],
{ cwd: projectDir }
),
(status) =>
status.live_process?.main_state === "running" &&
status.live_process?.main_wait_state === "waiting_for_node",
120000
);
const startedAt = Date.now();
const deadline = startedAt + strictSoakSeconds * 1000;
const samples = [];
let sampleIndex = 0;
while (true) {
const capabilities = await sendHostedControl(
signedNodeRequest(
securityNode.node,
securityNode.identity,
"report_node_capabilities",
securityNode.capability_body,
{ nonce: `strict-soak-capabilities-${sampleIndex}-${Date.now()}` }
)
);
assert.strictEqual(
capabilities.type,
"node_capabilities_recorded",
JSON.stringify(capabilities)
);
const poll = await sendHostedControl(
signedNodeRequest(
securityNode.node,
securityNode.identity,
"poll_task_assignment",
{
type: "poll_task_assignment",
tenant: securityNode.capability_body.tenant,
project: securityNode.capability_body.project,
node: securityNode.node,
},
{ nonce: `strict-soak-poll-${sampleIndex}-${Date.now()}` }
)
);
assert.strictEqual(poll.type, "task_assignment", JSON.stringify(poll));
assert.strictEqual(poll.assignment, null);
const status = runJson(
disasmer,
["process", "status", ...scope, "--process", processId],
{ cwd: projectDir }
);
assert.strictEqual(status.live_process.main_wait_state, "waiting_for_node");
samples.push(strictInfrastructureSample(projectDir));
sampleIndex += 1;
if (Date.now() >= deadline) break;
await delay(Math.min(30_000, deadline - Date.now()));
}
const currentMemory = samples.map(
(sample) => sample.service_memory_current_bytes
);
const serviceDisk = samples.map((sample) => sample.service_state_disk_bytes);
const databaseDisk = samples.map((sample) => sample.postgres_database_bytes);
const localDisk = samples.map((sample) => sample.local_node_state.bytes);
const spread = (values) => Math.max(...values) - Math.min(...values);
assert(spread(currentMemory) <= 128 * 1024 * 1024);
assert(spread(serviceDisk) <= 16 * 1024 * 1024);
assert(spread(databaseDisk) <= 32 * 1024 * 1024);
assert(spread(localDisk) <= 16 * 1024 * 1024);
assert.deepStrictEqual(
samples.at(-1).durable_rows,
samples[0].durable_rows,
"durable object counts grew during the parked-process soak"
);
const aborted = runJson(
disasmer,
["process", "abort", ...scope, "--process", processId, "--yes"],
{ cwd: projectDir }
);
assert.strictEqual(aborted.abort_request.process_slot_released, true);
return {
executed: true,
process: processId,
duration_seconds: Math.floor((Date.now() - startedAt) / 1000),
sample_interval_seconds: 30,
sample_count: samples.length,
parked_main: {
main_state: parked.live_process.main_state,
main_wait_state: parked.live_process.main_wait_state,
},
signed_node_poll_cycles: samples.length,
memory_spread_bytes: spread(currentMemory),
service_disk_spread_bytes: spread(serviceDisk),
postgres_disk_spread_bytes: spread(databaseDisk),
local_node_state_spread_bytes: spread(localDisk),
samples,
};
}
async function revokeSecurityNode({
disasmer,
projectDir,
scope,
securityNode,
}) {
const revoked = runJson(
disasmer,
["node", "revoke", ...scope, "--node", securityNode.node, "--yes"],
{ cwd: projectDir }
);
assert.strictEqual(revoked.command, "node revoke");
const denied = assertDenied(
await sendHostedControl({
type: "node_heartbeat",
node: securityNode.node,
node_signature: signedNodeHeartbeat(
securityNode.node,
securityNode.identity,
{ nonce: `strict-node-revoked-${Date.now()}` }
),
}),
/revoked|unknown node|not recognized|not enrolled/i,
"revoked node credential"
);
return { node: securityNode.node, denied };
}
async function runLiveQuotaPreallocation({ sessionSecret, suffix }) {
let denial;
let deniedProcess;
let acceptedStarts = 0;
for (let index = 0; index < 96; index += 1) {
const processId = `vp-quota-${suffix}-${index}`;
const started = await sendHostedControl(
authenticatedRequest(sessionSecret, {
type: "start_process",
process: processId,
restart: false,
})
);
if (started.type === "error") {
assert.match(started.message, /quota|spawn|limit|community tier/i);
denial = started;
deniedProcess = processId;
break;
}
assert.strictEqual(started.type, "process_started", JSON.stringify(started));
acceptedStarts += 1;
const aborted = await sendHostedControl(
authenticatedRequest(sessionSecret, {
type: "abort_process",
process: processId,
})
);
assert.strictEqual(aborted.type, "process_aborted", JSON.stringify(aborted));
}
assert(denial, "live community-tier spawn quota was not reached in 96 attempts");
const processes = await sendHostedControl(
authenticatedRequest(sessionSecret, { type: "list_processes" })
);
assert.strictEqual(
processes.type,
"process_statuses",
JSON.stringify(processes)
);
assert(
!JSON.stringify(processes).includes(deniedProcess),
"quota-denied process was allocated before denial"
);
return {
accepted_starts_before_denial: acceptedStarts,
denied_process: deniedProcess,
denial: {
type: denial.type,
category: denial.error?.category || null,
message: denial.message,
},
denied_process_allocated: false,
};
}
function deploymentProvenance() {
if (!strictVpsRestart) return null;
const systemGeneration = run(
"ssh",
sshArgs("readlink", "-f", "/run/current-system")
).trim();
const binarySha256 = run(
"ssh",
sshArgs(
"sha256sum",
"/opt/disasmer-public-release-dryrun/bin/disasmer-hosted-service"
)
)
.trim()
.split(/\s+/)[0];
const fragment = run(
"ssh",
sshArgs("systemctl", "show", strictServiceUnit, "--property=FragmentPath", "--value")
).trim();
const serviceUnitSha256 = run(
"ssh",
sshArgs("sha256sum", fragment)
)
.trim()
.split(/\s+/)[0];
return {
system_generation: systemGeneration,
hosted_service_sha256: `sha256:${binarySha256}`,
service_unit: fragment,
service_unit_sha256: `sha256:${serviceUnitSha256}`,
};
}
function configurationProvenance() {
const configRepo = path.resolve(
process.env.DISASMER_DEPLOYMENT_CONFIG_REPO ||
path.join(repo, "..", "michelpaulissen.com")
);
return {
repository: configRepo,
revision: run("git", ["rev-parse", "HEAD"], { cwd: configRepo }).trim(),
clean:
run("git", ["status", "--short"], { cwd: configRepo }).trim() === "",
};
}
async function restartHostedServiceAndResume({
disasmer,
disasmerNode,
projectDir,
scope,
workerArgs,
workerNode,
credentialPath,
credentialDigest,
}) {
if (!strictVpsRestart) {
return {
executed: false,
reason: "strict VPS restart access not supplied",
worker: null,
};
}
const before = deploymentProvenance();
run("ssh", sshArgs("systemctl", "restart", strictServiceUnit));
const deadline = Date.now() + 120000;
let ping;
let lastError;
while (Date.now() < deadline) {
try {
ping = await sendHostedControl({ type: "ping" });
if (ping.type === "pong") break;
} catch (error) {
lastError = error;
}
await delay(500);
}
assert.strictEqual(
ping?.type,
"pong",
`hosted service did not recover after restart: ${lastError?.message || "no pong"}`
);
const afterFirstRestart = deploymentProvenance();
assert.strictEqual(afterFirstRestart.system_generation, before.system_generation);
assert.strictEqual(
afterFirstRestart.hosted_service_sha256,
before.hosted_service_sha256
);
const projects = runJson(disasmer, ["project", "list", ...scope], {
cwd: projectDir,
});
assert(projects.project_count >= 1, "CLI session/project did not survive restart");
const ephemeralRun = runJson(
disasmer,
["run", "build", "--project", ".", "--json"],
{ cwd: projectDir }
);
assert.strictEqual(ephemeralRun.status, "main_launched");
const ephemeralProcess = ephemeralRun.process;
await waitForCli(
"pre-restart ephemeral main to park",
() =>
runJson(
disasmer,
["process", "status", ...scope, "--process", ephemeralProcess],
{ cwd: projectDir }
),
(status) => status.live_process?.main_wait_state === "waiting_for_node",
120000
);
run("ssh", sshArgs("systemctl", "restart", strictServiceUnit));
const secondDeadline = Date.now() + 120000;
ping = undefined;
lastError = undefined;
while (Date.now() < secondDeadline) {
try {
ping = await sendHostedControl({ type: "ping" });
if (ping.type === "pong") break;
} catch (error) {
lastError = error;
}
await delay(500);
}
assert.strictEqual(
ping?.type,
"pong",
`hosted service did not recover after ephemeral-state restart: ${
lastError?.message || "no pong"
}`
);
const after = deploymentProvenance();
assert.strictEqual(after.system_generation, before.system_generation);
assert.strictEqual(after.hosted_service_sha256, before.hosted_service_sha256);
const oldStatus = runJson(
disasmer,
["process", "status", ...scope, "--process", ephemeralProcess],
{ cwd: projectDir }
);
assert.strictEqual(oldStatus.state, "not_active");
assert.strictEqual(sha256(fs.readFileSync(credentialPath)), credentialDigest);
const worker = spawnJsonLines(disasmerNode, workerArgs, {
cwd: projectDir,
env: { ...process.env },
});
const ready = await worker.waitFor(
(value) => value.node_status === "ready",
"persisted worker ready after hosted service restart"
);
assert.strictEqual(ready.node, workerNode);
assert.strictEqual(sha256(fs.readFileSync(credentialPath)), credentialDigest);
const restartedRun = runJson(
disasmer,
["run", "restart", "--project", ".", "--json"],
{ cwd: projectDir }
);
assert.strictEqual(restartedRun.status, "main_launched");
const restartedProcess = restartedRun.process;
const completed = await waitForCli(
"new process completion after hosted service restart",
() =>
runJson(
disasmer,
["task", "list", ...scope, "--process", restartedProcess],
{ cwd: projectDir }
),
(tasks) =>
rawTaskEvents(tasks).some(
(event) =>
event.task_definition === "task_add_one" &&
event.terminal_state === "completed" &&
JSON.stringify(event.result) === JSON.stringify({ SmallJson: 42 })
),
5 * 60 * 1000
);
const completedEvent = rawTaskEvents(completed).find(
(event) => event.task_definition === "task_add_one"
);
const aborted = runJson(
disasmer,
["process", "abort", ...scope, "--process", restartedProcess, "--yes"],
{ cwd: projectDir }
);
assert.strictEqual(aborted.abort_request.process_slot_released, true);
return {
executed: true,
account_project_session_preserved: true,
node_identity_preserved: true,
old_process_terminated_honestly: true,
terminated_ephemeral_process: ephemeralProcess,
new_process: restartedProcess,
new_process_result: completedEvent.result,
before,
after_first_restart: afterFirstRestart,
after,
worker,
};
}
async function finishUserCredentialSecurity({
disasmer,
projectDir,
scope,
sessionSecret,
}) {
const forged = assertDenied(
await sendHostedControl(
authenticatedRequest(
`forged-session-${crypto.randomBytes(24).toString("hex")}`,
{ type: "list_projects" }
)
),
/not recognized|invalid|authentication|session/i,
"forged user session"
);
let expired = null;
const expiredFile = process.env.DISASMER_EXPIRED_USER_SESSION_FILE;
if (expiredFile) {
const oldSession = readJson(path.resolve(expiredFile));
assert(oldSession.session_secret, "expired session evidence omitted its secret");
expired = assertDenied(
await sendHostedControl(
authenticatedRequest(oldSession.session_secret, { type: "list_projects" })
),
/expired/i,
"expired user session"
);
} else if (strictFullRelease) {
throw new Error(
"strict full release requires DISASMER_EXPIRED_USER_SESSION_FILE for a genuinely expired hosted session"
);
}
const logout = runJson(disasmer, ["logout", ...scope, "--yes"], {
cwd: projectDir,
});
assert.strictEqual(logout.server_session_revocation.revoked, true);
const revoked = assertDenied(
await sendHostedControl(
authenticatedRequest(sessionSecret, { type: "list_projects" })
),
/revoked|not recognized/i,
"revoked user session"
);
return { forged, expired, revoked };
}
async function dapVariables(client, variablesReference) {
const request = client.send("variables", { variablesReference });
return (await client.response(request, "variables")).body.variables;
}
async function runLiveDapEditRestart({
disasmerDap,
disasmer,
projectDir,
scope,
worker,
}) {
const sourcePath = fs.realpathSync(path.join(projectDir, "src/build.rs"));
const originalSource = fs.readFileSync(sourcePath, "utf8");
const sourceLines = originalSource.split(/\r?\n/);
const lineFor = (needle) => {
const line = sourceLines.findIndex((candidate) => candidate.includes(needle)) + 1;
assert(line > 0, `flagship source omitted ${needle}`);
return line;
};
const mainLine = lineFor("pub async fn restart_main()");
const taskLine = lineFor("fn task_add_one(");
const client = new DapClient({
cwd: projectDir,
command: disasmerDap,
args: [],
env: { ...process.env },
});
let sourceRestored = false;
try {
const initialize = client.send("initialize", {
adapterID: "disasmer",
linesStartAt1: true,
columnsStartAt1: true,
});
await client.response(initialize, "initialize");
const launch = client.send("launch", {
entry: "restart",
project: projectDir,
runtimeBackend: "live-services",
coordinatorEndpoint: serviceEndpoint,
});
await client.response(launch, "launch");
await client.waitFor(
(message) => message.type === "event" && message.event === "initialized"
);
const setBreakpoints = client.send("setBreakpoints", {
source: { path: sourcePath },
breakpoints: [{ line: mainLine }, { line: taskLine }],
});
const breakpointResponse = await client.response(
setBreakpoints,
"setBreakpoints"
);
assert.deepStrictEqual(
breakpointResponse.body.breakpoints.map((breakpoint) => breakpoint.verified),
[true, true]
);
const configurationDone = client.send("configurationDone");
await client.response(configurationDone, "configurationDone");
const mainStop = await client.waitFor(
(message) =>
message.type === "event" &&
message.event === "stopped" &&
message.body.reason === "breakpoint"
);
assert.strictEqual(mainStop.body.allThreadsStopped, true);
const mainContinue = client.send("continue", { threadId: mainStop.body.threadId });
await client.response(mainContinue, "continue");
const taskStop = await client.waitFor(
(message) =>
message.seq > mainStop.seq &&
message.type === "event" &&
message.event === "stopped" &&
message.body.reason === "breakpoint"
);
assert.strictEqual(taskStop.body.allThreadsStopped, true);
assert.notStrictEqual(taskStop.body.threadId, mainStop.body.threadId);
const stackTrace = client.send("stackTrace", {
threadId: taskStop.body.threadId,
startFrame: 0,
levels: 1,
});
const taskFrames = (await client.response(stackTrace, "stackTrace")).body
.stackFrames;
assert.strictEqual(taskFrames[0].line, taskLine);
const scopesRequest = client.send("scopes", { frameId: taskFrames[0].id });
const scopes = (await client.response(scopesRequest, "scopes")).body.scopes;
const argsScope = scopes.find(
(candidate) => candidate.name === "Task Args and Handles"
);
const runtimeScope = scopes.find(
(candidate) => candidate.name === "Disasmer Runtime"
);
assert(argsScope && runtimeScope);
const taskArgs = await dapVariables(client, argsScope.variablesReference);
assert(
taskArgs.some(
(variable) =>
variable.name === "arg_0" && String(variable.value).includes("41")
)
);
const runtime = await dapVariables(client, runtimeScope.variablesReference);
assert(
runtime.some(
(variable) =>
variable.name === "runtime_backend" && variable.value === "LiveServices"
)
);
assert(
runtime.some(
(variable) => variable.name === "state" && variable.value === "Frozen"
)
);
const dapProcess = runtime.find(
(variable) => variable.name === "virtual_process_id"
)?.value;
assert.match(dapProcess || "", /^vp-[0-9a-f]{12}$/);
const taskContinue = client.send("continue", { threadId: taskStop.body.threadId });
await client.response(taskContinue, "continue");
await client.waitFor(
(message) =>
message.seq > taskStop.seq &&
message.type === "event" &&
message.event === "terminated",
5 * 60 * 1000
);
const beforeEdit = runJson(
disasmer,
["task", "list", ...scope, "--process", dapProcess],
{ cwd: projectDir }
);
const originalTaskEvent = rawTaskEvents(beforeEdit).find(
(event) =>
event.task_definition === "task_add_one" &&
event.terminal_state === "completed"
);
assert(originalTaskEvent, "DAP restart entry omitted its original child event");
assert.deepStrictEqual(originalTaskEvent.result, { SmallJson: 42 });
const editedSource = originalSource.replace(
" input + 1\n",
" input + 2 // strict hosted compatible restart probe\n"
);
assert.notStrictEqual(editedSource, originalSource);
fs.writeFileSync(sourcePath, editedSource);
const restartFrame = client.send("restartFrame", {
frameId: taskFrames[0].id,
});
const restartResponse = await client.response(restartFrame, "restartFrame");
const restartedStop = await client.waitFor(
(message) =>
message.seq > restartResponse.seq &&
message.type === "event" &&
message.event === "stopped" &&
message.body.reason === "breakpoint"
);
assert.strictEqual(restartedStop.body.allThreadsStopped, true);
const restartedContinue = client.send("continue", {
threadId: restartedStop.body.threadId,
});
await client.response(restartedContinue, "continue");
const restartedNodeReport = await worker.waitFor(
(value) =>
value.virtual_thread !== originalTaskEvent.task &&
value.task_assignment_response?.task_spec?.task_definition ===
"task_add_one",
"edited task to execute on the live restarted node",
5 * 60 * 1000
);
assert.strictEqual(
restartedNodeReport.node_status,
"completed",
`edited task restart failed on the live node: ${JSON.stringify(restartedNodeReport)}`
);
assert.match(restartedNodeReport.stdout_tail, /"SmallJson":43/);
const restartedTask = restartedNodeReport.virtual_thread;
const afterEdit = await waitForCli(
"edited live task event",
() =>
runJson(disasmer, ["task", "list", ...scope, "--process", dapProcess], {
cwd: projectDir,
}),
(tasks) =>
rawTaskEvents(tasks).some(
(event) =>
event.task === restartedTask &&
event.terminal_state === "completed" &&
JSON.stringify(event.result) === JSON.stringify({ SmallJson: 43 })
),
5 * 60 * 1000
);
const restartedEvent = rawTaskEvents(afterEdit).find(
(event) => event.task === restartedTask
);
fs.writeFileSync(sourcePath, originalSource);
sourceRestored = true;
return {
process: dapProcess,
main_breakpoint_line: mainLine,
task_breakpoint_line: taskLine,
main_all_threads_stopped: mainStop.body.allThreadsStopped,
task_all_threads_stopped: taskStop.body.allThreadsStopped,
original_task_instance: originalTaskEvent.task,
original_result: originalTaskEvent.result,
restarted_task_instance: restartedTask,
restarted_result: restartedEvent.result,
compatible_source_edit: "task_add_one: input + 1 -> input + 2",
original_arguments_preserved: true,
clean_vfs_boundary_used: true,
};
} finally {
if (!sourceRestored) fs.writeFileSync(sourcePath, originalSource);
await client.close().catch(() => {
if (client.child.exitCode === null) client.child.kill("SIGKILL");
});
}
}
async function main() {
requireEnabled();
const manifest = readJson(manifestPath);
assert.strictEqual(manifest.kind, "disasmer-public-release-dryrun");
assert.strictEqual(manifest.default_hosted_coordinator_endpoint, serviceEndpoint);
assert.strictEqual(serviceAddr, "disasmer.michelpaulissen.com:443");
const workRoot = fs.mkdtempSync(path.join(os.tmpdir(), "disasmer-cli-happy-"));
const installDir = path.join(workRoot, "install");
const checkout = path.join(workRoot, "public-repo");
const downloadPath = path.join(workRoot, "release.tar");
const extractedArtifact = path.join(workRoot, "artifact");
ensureDir(installDir);
run("tar", ["-xzf", binaryArchive(manifest), "-C", installDir]);
const publicSource = stagePublicCheckout(manifest, checkout);
const disasmer = executable(installDir, "disasmer");
const disasmerNode = executable(installDir, "disasmer-node");
const disasmerDap = executable(installDir, "disasmer-debug-dap");
const projectDir = path.join(checkout, "examples/launch-build-demo");
const suffix = String(Date.now());
const requestedProject = `project-${suffix}`;
const workerNode = `worker-${suffix}`;
const login = runJson(
disasmer,
["login", "--browser", "--json", "--project-id", requestedProject],
{
cwd: projectDir,
env: { ...process.env, DISASMER_BROWSER_OPEN_COMMAND: browserOpenCommand },
}
);
assert.strictEqual(login.plan.coordinator, serviceEndpoint);
assert.strictEqual(login.boundary.scoped_cli_session_received, true);
assert.strictEqual(login.boundary.provider_tokens_exposed_to_cli, false);
assert.strictEqual(login.boundary.provider_tokens_sent_to_nodes, false);
const loginSession = login.coordinator_response.session;
assert(loginSession, "hosted browser login omitted its scoped session");
const sessionSecret =
loginSession.cli_session_secret || loginSession.session_secret;
assert(sessionSecret, "hosted browser login omitted the CLI session credential");
const tenant = loginSession.tenant;
const project = loginSession.project;
const user = loginSession.user;
for (const [name, value] of Object.entries({ tenant, project, user })) {
assert.strictEqual(typeof value, "string", `hosted session omitted ${name}`);
assert.notStrictEqual(value, "", `hosted session returned an empty ${name}`);
}
const receivedRequestedProject =
project === requestedProject || project.endsWith(`-${requestedProject}`);
assert(
receivedRequestedProject,
"login did not return the requested server-owned default project"
);
const scope = [
"--coordinator",
serviceEndpoint,
"--tenant",
tenant,
"--project-id",
project,
"--user",
user,
"--json",
];
const projectInit = runJson(
disasmer,
[
"project",
"init",
...scope,
"--new-project",
project,
"--name",
"CLI Happy Path",
"--yes",
],
{ cwd: projectDir }
);
assert.strictEqual(projectInit.command, "project init");
assert.strictEqual(projectInit.project_config_written, true);
assert.strictEqual(projectInit.coordinator_response.type, "project_created");
const projectList = runJson(disasmer, ["project", "list", ...scope], {
cwd: projectDir,
});
assert(projectList.project_count >= 1);
const projectSelect = runJson(
disasmer,
["project", "select", ...scope, project],
{ cwd: projectDir }
);
assert.strictEqual(projectSelect.command, "project select");
const inspection = runJson(disasmer, ["inspect", "--project", ".", "--json"], {
cwd: projectDir,
});
assert(
inspection.metadata.environments.some((environment) => environment.name === "linux")
);
assert(Array.isArray(inspection.source_provider_statuses));
assert(inspection.source_provider_manifest);
const runReport = runJson(
disasmer,
["run", "build", "--project", ".", "--json"],
{ cwd: projectDir }
);
assert.strictEqual(runReport.command, "run");
assert.strictEqual(runReport.status, "main_launched");
assert.strictEqual(runReport.task_launch.type, "main_launched");
const processId = runReport.process;
const parkedStatus = await waitForCli(
"hosted coordinator main to park before node enrollment",
() =>
runJson(
disasmer,
["process", "status", ...scope, "--process", processId],
{ cwd: projectDir }
),
(status) =>
status.live_process?.main_state === "running" &&
status.live_process?.main_wait_state === "waiting_for_node" &&
status.live_process?.connected_nodes?.length === 0,
120000
);
const grant = runJson(disasmer, ["node", "enroll", ...scope], {
cwd: projectDir,
});
assert.strictEqual(grant.command, "node enroll");
const attach = runJson(
disasmer,
[
"node",
"attach",
"--coordinator",
serviceEndpoint,
"--tenant",
tenant,
"--project-id",
project,
"--node",
workerNode,
"--enrollment-grant",
grant.enrollment_grant.grant,
"--json",
],
{ cwd: projectDir }
);
assert.strictEqual(attach.command, "node attach");
assert.strictEqual(attach.boundary.used_enrollment_exchange, true);
const credentialDir = path.join(projectDir, ".disasmer", "nodes");
const credentialFiles = fs
.readdirSync(credentialDir)
.filter((file) => file.endsWith(".json"));
assert.strictEqual(credentialFiles.length, 1, "expected one persisted node credential");
const credentialPath = path.join(credentialDir, credentialFiles[0]);
const credentialBefore = fs.readFileSync(credentialPath);
const credentialDigest = sha256(credentialBefore);
if (process.platform !== "win32") {
assert.strictEqual(fs.statSync(credentialPath).mode & 0o777, 0o600);
}
const workerArgs = [
"--coordinator",
serviceEndpoint,
"--tenant",
tenant,
"--project-id",
project,
"--node",
workerNode,
"--worker",
"--project-root",
projectDir,
"--assignment-poll-ms",
"500",
"--emit-ready",
];
const spawnWorker = () =>
spawnJsonLines(disasmerNode, workerArgs, {
cwd: projectDir,
env: { ...process.env },
});
let worker = spawnWorker();
try {
const firstReady = await worker.waitFor(
(value) => value.node_status === "ready",
"first persisted worker ready"
);
assert.strictEqual(firstReady.node, workerNode);
const completedTasks = await waitForCli(
"real hosted flagship completion",
() =>
runJson(disasmer, ["task", "list", ...scope, "--process", processId], {
cwd: projectDir,
}),
(tasks) => completedFlagship(rawTaskEvents(tasks))
);
const firstEvents = rawTaskEvents(completedTasks);
assert(
firstEvents
.filter((event) => event.executor === "node")
.every((event) => event.node === workerNode)
);
assert(
firstEvents.some(
(event) =>
event.task_definition === "package_release" &&
event.terminal_state === "completed" &&
contentAddressedArtifactId(event, "release.tar")
)
);
await stopChild(worker.child);
worker = spawnWorker();
const secondReady = await worker.waitFor(
(value) => value.node_status === "ready",
"restarted persisted worker ready"
);
assert.strictEqual(secondReady.node, workerNode);
assert.strictEqual(sha256(fs.readFileSync(credentialPath)), credentialDigest);
const nodeStatus = runJson(
disasmer,
["node", "status", ...scope, "--node", workerNode],
{ cwd: projectDir }
);
assert(JSON.stringify(nodeStatus.response).includes(workerNode));
const sourceEvent = firstEvents.find(
(event) =>
event.task_definition === "prepare_source" &&
event.terminal_state === "completed"
);
assert(sourceEvent, "flagship omitted its completed source task");
const restartTask = runJson(
disasmer,
[
"task",
"restart",
...scope,
sourceEvent.task,
"--process",
processId,
"--yes",
],
{ cwd: projectDir }
);
assert.strictEqual(restartTask.restart_request.accepted, true);
const restartedTask = restartTask.response.restarted_task_instance;
assert(restartedTask && restartedTask !== sourceEvent.task);
const restartedCompletion = await worker.waitFor(
(value) =>
value.node_status === "completed" && value.virtual_thread === restartedTask,
"restarted node to complete work under the persisted identity",
5 * 60 * 1000
);
assert.strictEqual(restartedCompletion.terminal_state, "completed");
assert.strictEqual(sha256(fs.readFileSync(credentialPath)), credentialDigest);
const processStatus = runJson(
disasmer,
["process", "status", ...scope, "--process", processId],
{ cwd: projectDir }
);
assert.strictEqual(processStatus.command, "process status");
assert(processStatus.current_task_count >= 5);
const logs = runJson(
disasmer,
["logs", ...scope, "--process", processId],
{ cwd: projectDir }
);
assert(logs.log_entries.length >= 4);
const artifacts = runJson(
disasmer,
["artifact", "list", ...scope, "--process", processId],
{ cwd: projectDir }
);
const releaseArtifact = artifacts.artifacts.find((artifact) => {
const digest = artifact.digest;
return (
typeof digest === "string" &&
/^sha256:[0-9a-f]{64}$/.test(digest) &&
artifact.artifact === `release.tar-${digest.slice("sha256:".length)}`
);
});
assert(releaseArtifact, "hosted flagship did not publish release.tar");
assert.strictEqual(releaseArtifact.state, "metadata_flushed");
const download = runJson(
disasmer,
[
"artifact",
"download",
...scope,
releaseArtifact.artifact,
"--to",
downloadPath,
],
{ cwd: projectDir }
);
assert.strictEqual(download.download_session.link_issued, true);
assert.strictEqual(download.local_download.local_bytes_written_by_cli, true);
assert.strictEqual(download.local_download.verified_digest, releaseArtifact.digest);
assert.strictEqual(sha256(fs.readFileSync(downloadPath)), releaseArtifact.digest);
ensureDir(extractedArtifact);
const archiveEntries = run("tar", ["-tf", downloadPath]);
assert.match(archiveEntries, /(?:^|\n)hello-disasmer(?:\n|$)/);
run("tar", ["-xf", downloadPath, "-C", extractedArtifact]);
const builtExecutable = path.join(extractedArtifact, "hello-disasmer");
assert(fs.existsSync(builtExecutable));
const builtOutput = run(builtExecutable, []).trim();
assert.strictEqual(builtOutput, "hello from a real Disasmer build");
const abortFlagship = runJson(
disasmer,
["process", "abort", ...scope, "--process", processId, "--yes"],
{ cwd: projectDir }
);
assert.strictEqual(abortFlagship.abort_request.accepted, true);
assert.strictEqual(abortFlagship.abort_request.process_slot_released, true);
await waitForCli(
"flagship process slot release before live DAP launch",
() =>
runJson(
disasmer,
["process", "status", ...scope, "--process", processId],
{ cwd: projectDir }
),
(status) => status.state === "not_active",
120000
);
const dapEditRestart = await runLiveDapEditRestart({
disasmerDap,
disasmer,
projectDir,
scope,
worker,
});
const restart = runJson(
disasmer,
["process", "restart", ...scope, "--process", dapEditRestart.process, "--yes"],
{ cwd: projectDir }
);
assert.strictEqual(restart.restart_request.accepted, true);
const cancel = runJson(
disasmer,
["process", "cancel", ...scope, "--process", dapEditRestart.process, "--yes"],
{ cwd: projectDir }
);
assert.strictEqual(cancel.cancel_request.accepted, true);
const tenantIsolation = await runSecondTenantIsolation({
firstTenant: tenant,
firstProject: project,
firstProcess: dapEditRestart.process,
firstNode: workerNode,
firstArtifact: releaseArtifact.artifact,
manifest,
});
const abortDap = runJson(
disasmer,
["process", "abort", ...scope, "--process", dapEditRestart.process, "--yes"],
{ cwd: projectDir }
);
assert.strictEqual(abortDap.abort_request.process_slot_released, true);
const securityNode = await prepareLiveNodeCredentialSecurity({
disasmer,
projectDir,
scope,
tenant,
project,
suffix,
});
const agentCredentialSecurity = await runLiveAgentCredentialSecurity({
disasmer,
projectDir,
scope,
sessionSecret,
tenant,
project,
suffix,
});
await stopChild(worker.child);
const liveSoak = await runLiveSoak({
disasmer,
projectDir,
scope,
securityNode,
});
const revokedNodeCredential = await revokeSecurityNode({
disasmer,
projectDir,
scope,
securityNode,
});
const quotaPreallocation = await runLiveQuotaPreallocation({
sessionSecret,
suffix,
});
const serviceRestart = await restartHostedServiceAndResume({
disasmer,
disasmerNode,
projectDir,
scope,
workerArgs,
workerNode,
credentialPath,
credentialDigest,
});
if (serviceRestart.worker) worker = serviceRestart.worker;
const userCredentialSecurity = await finishUserCredentialSecurity({
disasmer,
projectDir,
scope,
sessionSecret,
});
const configProvenance = configurationProvenance();
const fullReleasePassed =
strictFullRelease &&
tenantIsolation.executed &&
liveSoak.executed &&
serviceRestart.executed &&
Boolean(userCredentialSecurity.expired) &&
manifest.source_tree_clean === true &&
configProvenance.clean;
const report = {
kind: "disasmer-cli-happy-path-live",
release_name: manifest.release_name,
source_commit: manifest.source_commit,
public_tree_identity: manifest.public_tree_identity,
service_addr: serviceAddr,
default_hosted_coordinator_endpoint: serviceEndpoint,
public_repository_url: publicSource.published ? publicSource.url : null,
public_source: publicSource,
tenant,
project,
worker_node: workerNode,
process: processId,
signed_in: true,
received_default_project: receivedRequestedProject,
process_started_before_node: true,
parked_main_observed: {
main_state: parkedStatus.live_process.main_state,
main_wait_state: parkedStatus.live_process.main_wait_state,
connected_nodes: parkedStatus.live_process.connected_nodes,
},
node_enrollment_grants_used: 2,
persisted_node_credential_mode:
process.platform === "win32" ? null : fs.statSync(credentialPath).mode & 0o777,
persisted_node_credential_digest: credentialDigest,
node_restarted_without_grant: true,
restarted_node_completed_task: restartedTask,
flagship_task_definitions: [
"prepare_source",
"compile_linux",
"package_release",
],
rootless_podman_flagship_completed: true,
downstream_release_artifact: releaseArtifact,
artifact_download: {
bytes_written: download.local_download.bytes_written,
verified_digest: download.local_download.verified_digest,
executable_output: builtOutput,
},
flagship_process_aborted_for_dap: true,
live_dap_edit_restart: dapEditRestart,
process_restart_requested: true,
process_cancel_requested: true,
dap_process_aborted: true,
tenant_isolation: tenantIsolation,
credential_security: {
user: userCredentialSecurity,
node: {
node: securityNode.node,
credential_digest: securityNode.credential_digest,
...securityNode.evidence,
revoked: revokedNodeCredential.denied,
},
agent: agentCredentialSecurity,
},
quota_preallocation: quotaPreallocation,
live_soak: liveSoak,
hosted_service_restart: {
...serviceRestart,
worker: undefined,
},
release_binding: {
manifest: manifestPath,
source_commit: manifest.source_commit,
source_tree_clean: manifest.source_tree_clean,
public_tree_identity: manifest.public_tree_identity,
binary_digests: manifest.binary_digests,
deployment: serviceRestart.after || deploymentProvenance(),
configuration: configProvenance,
},
commands,
acceptance_result: fullReleasePassed ? "passed" : "partial",
};
ensureDir(path.dirname(reportPath));
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
console.log(`CLI happy-path live smoke passed: ${reportPath}`);
} finally {
await stopChild(worker?.child);
}
}
main().catch((error) => {
console.error(error.stack || error.message);
process.exit(1);
});