One-node bed: SDK-by-version, rename, and the store/broker upgrade proofs #27

Merged
jschoubben merged 26 commits from feat/a-bed-that-hands-over-nothing into main 2026-09-16 21:25:34 +00:00
Showing only changes of commit 61abeb7f6b - Show all commits
+20 -5
View File
@@ -12,6 +12,7 @@
* description of genesis, and both the single-node bed and the four-node bed call it.
*/
import { existsSync, writeFileSync } from "node:fs";
import { execFileSync } from "node:child_process";
import { tmpdir } from "node:os";
import { join, resolve } from "node:path";
import { exec, instanceNameOf, push } from "../../src/lifecycle/operate.ts";
@@ -110,13 +111,27 @@ export async function genesis(o: GenesisOptions): Promise<GenesisResult> {
// The catalogue. `mesh-bootstrap --catalog` reads manifests from a CHECKOUT on the machine,
// because at this moment the mesh has no forge, no build machine and — until the registry step
// finishes — no registry. A manifest is a file, and somebody has to have put it there.
await must(`mkdir -p ${modules.map((m) => `${catalogueOnMachine}/modules/${m}`).join(" ")}`);
//
// **The WHOLE checkout, not the three genesis names.** `--catalog` is documented as a checkout
// of the catalogue repository, and phase two reads more from it than genesis does — postgres,
// mesh-catalog, the packet filter, whatever extras. Stocking only the bootstrap three left
// phase two unable to read a manifest that was never put there (novox/hq installer step 14). The
// production equivalent is an operator with a full checkout, or the installer cloning the repo;
// the lab stands in for that by copying the whole tree once.
const bundleTar = join(tmpdir(), `mesh-lab-catalogue-${process.pid}-${node}.tar`);
execFileSync("tar", ["-cf", bundleTar, "-C", o.catalogDir, "."]);
await must(`mkdir -p ${catalogueOnMachine}/modules`);
await push(o.instanceId, node, bundleTar, "/tmp/catalogue.tar");
await must(`tar -xf /tmp/catalogue.tar -C ${catalogueOnMachine}/modules`);
// The three genesis itself needs must be present, or the pivot cannot even begin — checked here
// rather than discovered at step 8, where the message is about a missing file.
for (const module of modules) {
const from = resolve(o.catalogDir, module, "module.json");
if (!existsSync(from)) return stop("preparing the catalogue", `the catalogue has no ${module}/module.json at ${from}`);
await push(o.instanceId, node, from, `${catalogueOnMachine}/modules/${module}/module.json`);
const at = `${catalogueOnMachine}/modules/${module}/module.json`;
if (!(await on(`test -f ${at}`)).ok) {
return stop("preparing the catalogue", `the catalogue has no ${module}/module.json`);
}
}
report.push(` catalogue ${modules.join(", ")} at ${catalogueOnMachine}`);
report.push(` catalogue full checkout at ${catalogueOnMachine} (${modules.join(", ")} + phase two)`);
const local = join(tmpdir(), `mesh-lab-substrate-${process.pid}-${node}.lock`);
writeFileSync(local, o.bundleTemplate);