mesh.test.ts: meshboard gets a slug (ADR 0049); the builder-as-module test is retired, genesis proves it

This commit is contained in:
2026-09-21 23:38:58 +02:00
parent 080150addb
commit 7c81902571
+7 -112
View File
@@ -326,7 +326,8 @@ test("a credential reaches both ends and the mesh holds neither", { skip, timeou
// real program takes a credential: a sealed file is a password alone, and almost nothing reads
// one. The mesh cannot compose the document — it discarded the value — so the module supplies it
// with `${secret:...}` in it and the host, the only thing that sees both halves, fills it in.
await must("anchor", `printf %s '{"module":"meshboard","version":"1",` +
// A slug, so its identity on a backend stays within an S3 access key's 20 characters (ADR 0049).
await must("anchor", `printf %s '{"module":"meshboard","version":"1","slug":"board",` +
`"requires":["postgres-database"],"contributes":{"postgres-database":{"name":"meshboard"}},` +
`"binds":{"postgres-database":"/etc/meshboard/database.json"},` +
`"secrets":{"postgres-database":"/etc/meshboard/database.password"},` +
@@ -814,117 +815,11 @@ test("a machine filters exactly what its modules declared, and nothing else", {
"the port stayed open after the module that wanted it was removed");
});
test("the builder is a module the mesh assigns, with a credential the mesh delivered", {
skip: skip || (!builder ? "set MESH_LAB_BUILDER to a built mesh-builder" : false),
timeout: 900_000,
}, async () => {
// Until this, the builder was a program somebody started on a machine with whatever credential
// they had to hand — in practice the broker's administrative one. A program documented as
// holding its own credential and given somebody else's is worse than one with no story at all.
//
// So: the mesh issues a scoped account, seals it to the machine, and delivers it with the
// declaration. Nobody types it and the mesh cannot read it back.
await must("anchor", `mkdir -p /root/builder && printf %s '{"module":"self-builder","version":"1",` +
`"requires":["artifact-store"],"capabilities":["container-runtime"],` +
`"claims":[{"name":"the-build-machine","scope":"node"}],` +
`"binds":{"artifact-store":"/var/lib/mesh/builder/artifact-store.json"},` +
`"own-secrets":{"broker":"/var/lib/mesh/builder/broker"},` +
`"build":{"artifacts":[{"name":"builder","kind":"upstream",` +
`"from":"${pinned("mesh-builder")}"}]},` +
`"resources":[` +
`{"id":"state","type":"directory","path":"/var/lib/mesh/builder","mode":"0700"},` +
`{"id":"workspace","type":"directory","path":"/var/lib/mesh/builder/workspace","mode":"0700"},` +
`{"id":"run","type":"container","name":"mesh-builder","artifact":"builder",` +
`"network":"host",` +
`"volumes":["/var/lib/mesh/builder:/var/lib/mesh/builder",` +
`"/var/run/docker.sock:/var/run/docker.sock"],` +
`"env":{"MESH_BROKER_FILE":"/var/lib/mesh/builder/broker",` +
`"MESH_BINDING":"/var/lib/mesh/builder/artifact-store.json",` +
`"MESH_WORKSPACE":"/var/lib/mesh/builder/workspace"}}]}' > /root/builder/module.json`);
await must("anchor", `cd /root/builder && git init -q . && git add -A && ` +
`git -c user.email=lab -c user.name=lab commit -qm builder`);
// The builder's own image is built by the builder that is already running — the same
// chicken-and-egg as the registry, resolved the same way. The one started by hand does this
// last piece of work and is then replaced by the module it just built.
await mesh("build /root/builder --wait 300s", 420_000);
// The mesh makes the account and seals the URL to this machine. Nothing is printed that would
// work if it were pasted somewhere else.
const issued = await mesh("builder issue lab-builder --node anchor");
assert.match(issued, /sealed to anchor/, issued);
assert.doesNotMatch(issued, /amqps:\/\/lab-builder:/,
"the credential was printed, so the one copy that matters is on a terminal");
// Now the hand-started one goes, or two builders race for the same queue and whichever answers
// proves nothing. By process name: `pkill -f` matches the shell running it too, which kills the
// connection carrying the command and hangs the caller waiting for a reply that will never
// come. Cost an hour once, in this file.
await on("anchor", `pkill -x mesh-builder`);
await new Promise((r) => setTimeout(r, 2000));
assert.ok(!(await on("anchor", `pgrep -x mesh-builder`)).ok,
"the hand-started builder is still running, so this would test that one");
await mesh("assign anchor self-builder");
await mesh("push anchor");
await new Promise((r) => setTimeout(r, 20_000));
const running = await must("anchor", `docker ps --format '{{.Names}}'`);
assert.match(running, /mesh-builder/,
`the builder was assigned and is not running:\n${running}\n` +
`${(await on("anchor", `tail -30 /var/log/mesh-host.log`)).out}`);
// Running is not connected. A builder that cannot reach the broker sits there, and every
// outward sign — the container is up, the credential is on disk — says it is working.
await new Promise((r) => setTimeout(r, 5000));
const said = await on("anchor", `docker logs mesh-builder 2>&1 | tail -20`);
assert.doesNotMatch(said.out, /cannot reach the broker/,
`the builder is running and cannot reach the broker:\n${said.out}`);
// The credential arrived, is readable only by the machine, and is the scoped account rather
// than the broker's own.
assert.match(await must("anchor", `stat -c %a /var/lib/mesh/builder/broker`), /^600/);
const credential = await must("anchor", `cat /var/lib/mesh/builder/broker`);
assert.match(credential, /"url":"amqps:\/\/lab-builder:/,
"the builder is using an account that is not its own");
assert.doesNotMatch(credential, /guest:guest/, "the builder holds the broker's own account");
// And what to check the broker against. A mesh's broker presents a certificate of the mesh's
// own, so a URL alone reaches only a broker some public authority vouches for — which is no
// mesh broker at all, and fails at TLS with an error about an unknown authority.
assert.match(credential, /"fingerprint":"(sha256:)?[0-9a-f]{64}"/,
`the builder was given nothing to verify the broker with:\n${credential}`);
// And it works: the mesh asks this builder to build something, and it does. Answering is the
// only proof that the delivered credential authenticates — a container that is up with a
// credential it cannot use looks identical from outside.
// Somewhere the builder can actually see. A builder that is a module runs in a container, so
// the machine's filesystem is not its own — a path like /root only works for a builder somebody
// started on the host, which is what the first build above used. In a real mesh a module is
// cloned from the forge over a URL; here it goes in the directory the module already mounts,
// which is the same fact wearing different clothes.
const repo = "/var/lib/mesh/builder/repositories/built";
await must("anchor", `mkdir -p ${repo} && printf %s '{"module":"built","version":"1",` +
`"resources":[{"id":"marker","type":"file","path":"/etc/built","content":"yes","mode":"0644"}]}' ` +
`> ${repo}/module.json`);
await must("anchor", `cd ${repo} && git init -q . && git add -A && ` +
`git -c user.email=lab -c user.name=lab commit -qm built`);
try {
await mesh(`build ${repo} --wait 300s`, 420_000);
} catch (why) {
// The builder's own account of itself. Without it the failure is "nothing consumed the
// queue", which names no cause and is the same sentence whether the credential was refused,
// the queue was never declared, or the process died three seconds in.
const said = (await on("anchor", `docker logs mesh-builder 2>&1 | tail -40`)).out;
throw new Error(`${(why as Error).message}\n\nwhat the builder said:\n${said}`);
}
// Naming the module, and not merely containing its name: `builds` says "nothing has been built
// yet" when there is nothing, and that sentence contains the word this was matching on.
const recorded = await mesh("builds built");
assert.doesNotMatch(recorded, /nothing has been built/,
`the build was accepted and no build was recorded against the module:\n${recorded}`);
assert.match(recorded, /built/, recorded);
});
// The builder as a module the mesh assigns, with a credential the mesh delivered, is what genesis
// proves now (genesis-single installs the catalogue's builder through the installer, novox/hq ADR
// 0069). The test that lived here declared the builder's image as an upstream artifact by the bare
// image ID the lab holds, which is not a reference a registry copy can fetch (ADR 0096); retired
// 2026-09-21 rather than rewritten into a second genesis.
test("rotating a credential moves both ends, and the old one stops working", {
skip, timeout: 900_000,