Beds for the seed file and the foundation's filter; beds deliver secrets as files; a bundle-raised bed derives the anchor's filter #40

Merged
jschoubben merged 8 commits from feat/migration-blockers into main 2026-09-21 11:48:59 +00:00
2 changed files with 70 additions and 0 deletions
Showing only changes of commit 599d41eb42 - Show all commits
+30
View File
@@ -433,3 +433,33 @@ test("the operator recovers a root secret with a key the mesh never held, from t
assert.ok(listed.some((k) => k.module === "mesh-vault" && k.name === "broker"), JSON.stringify(served));
assert.ok(!JSON.stringify(served).includes(onDisk), "secret_export returned a plaintext value");
});
test("a seeded file is created once, and what a program grows in it survives the next push", {
skip, timeout: 600_000,
}, async () => {
// novox/hq ADR 0087, issue 035. A file that says create-once is written when absent and left
// alone when present — content, mode and owner — so an access list a program persists into is
// not restored to its seed behind the program's back on every reconcile.
const manifest = JSON.stringify({
module: "seed-test", version: "1",
resources: [
{ id: "dir", type: "directory", path: "/var/lib/seed-test", mode: "0755" },
{ id: "acl", type: "file", path: "/var/lib/seed-test/acl.conf", mode: "0600", "create-once": true,
content: "user default on\n" },
],
});
await must(`printf %s ${quote(manifest)} > /tmp/seed-test.json && docker cp /tmp/seed-test.json mesh-controller:/seed-test.json`);
await mesh("module add /seed-test.json");
await mesh(`assign ${MACHINE} seed-test`);
await mesh(`push ${MACHINE}`);
await settled();
assert.equal(await must(`cat /var/lib/seed-test/acl.conf`), "user default on\n");
// The program grows it.
await must(`printf 'user app-one on >secret\n' >> /var/lib/seed-test/acl.conf`);
// A second push: the mesh reconciles everything it declared, and leaves the seed alone.
await mesh(`push ${MACHINE}`);
await settled();
assert.equal(await must(`cat /var/lib/seed-test/acl.conf`), "user default on\nuser app-one on >secret\n",
"the seed was restored and what the program wrote into it was wiped");
});
+40
View File
@@ -40,6 +40,7 @@ import { test, before, after } from "node:test";
import assert from "node:assert/strict";
import { existsSync, readFileSync, writeFileSync, appendFileSync } from "node:fs";
import { execFileSync } from "node:child_process";
import net from "node:net";
import { resolve } from "node:path";
import { loadScenario } from "../../src/declaration/parse.ts";
import { raise } from "../../src/lifecycle/raise.ts";
@@ -247,6 +248,31 @@ async function mesh(command: string, timeoutMs?: number): Promise<string> {
}
}
/** The machine's address on the lab's uplink bridge — the one the workstation can dial. */
async function uplinkAddressOf(machine: string): Promise<string> {
const name = await instanceNameOf(instanceId, machine);
const listed = (await incus(["list", name, "--format", "csv", "-c", "4"], 30_000)).stdout;
const addresses = listed.split(/[,\s]+/).map((a: string) => a.trim()).filter((a: string) => /^10\./.test(a));
assert.ok(addresses.length > 0, `no uplink address for ${machine} in:\n${listed}`);
return addresses[0] as string;
}
/** Try to open a TCP connection every two seconds to each port, and remember whether any attempt ever succeeded. */
function probeFromOutside(address: string, ports: number[]): { stop(): void; seen(): Map<number, boolean> } {
const seen = new Map<number, boolean>(ports.map((p) => [p, false]));
const attempt = () => {
for (const port of ports) {
const socket = net.connect({ host: address, port, timeout: 1000 });
socket.once("connect", () => { seen.set(port, true); socket.destroy(); });
socket.once("timeout", () => socket.destroy());
socket.once("error", () => socket.destroy());
}
};
attempt();
const timer = setInterval(attempt, 2000);
return { stop: () => clearInterval(timer), seen: () => seen };
}
/** Until the anchor reports the last declaration genesis pushed as applied and current. */
async function settledAfterGenesis(withinMs = 300_000): Promise<void> {
const deadline = Date.now() + withinMs;
@@ -508,6 +534,11 @@ before(async () => {
// nothing held: no image is pre-resolved, because none is here to resolve to.
await step("R1", GENESIS, null, async () => {
try {
// Probed from the workstation for the whole install (novox/hq ADR 0088, issue 054): the
// store's client port must never answer from outside the machine, while the bus's must
// come to — which is also what proves the probe reaches the machine at all.
const probe = probeFromOutside(await uplinkAddressOf(CONTROL), [5432, 5671]);
try {
raised = await genesis({
instanceId,
node: CONTROL,
@@ -538,6 +569,15 @@ before(async () => {
...(binary ? { hostBinary: binary } : {}),
log: (m) => console.log(m),
});
} finally {
probe.stop();
}
const seen = probe.seen();
assert.equal(seen.get(5432), false,
"the store's port answered from outside the machine during the install — the base filter did not hold (issue 054)");
assert.equal(seen.get(5671), true,
"the bus never answered from outside during the install, so the probe proves nothing — is the uplink address right?");
raised.report.push(` filtered 5432 never answered from outside during the install; 5671 did`);
} catch (err) {
throw new Error(`the installer never ran: ${(err as Error).message}`);
}