Rename mesh-control -> mesh-controller, substrate -> foundation

One name per thing, per the HQ glossary: the module/container/image/binary/repo
becomes mesh-controller, the seat the-controller, and the store+broker pair the
foundation (embedded base bundles, default template and example lock renamed with
their go:embed directives). No behaviour change — a pure vocabulary rename.

Claude-Session: https://claude.ai/code/session_01D6qtiYU3P9jk3pnAXyAFyx
This commit is contained in:
2026-09-16 18:40:40 +02:00
parent 49b80d8516
commit 5d6e8fbe7a
89 changed files with 785 additions and 785 deletions
+20 -20
View File
@@ -37,7 +37,7 @@ export interface GenesisOptions {
/** A checkout of mesh-catalog's `modules/` on this workstation. */
catalogDir: string;
/**
* The substrate TEMPLATE's content — not a bundle. The installer produces the bundle from it,
* The foundation TEMPLATE's content — not a bundle. The installer produces the bundle from it,
* replacing the control plane's image with the id of the image it carries.
*/
bundleTemplate: string;
@@ -91,7 +91,7 @@ export function stepIn(said: string): string {
export async function genesis(o: GenesisOptions): Promise<GenesisResult> {
const node = o.node;
const registry = o.registry ?? "127.0.0.1:5000";
const modules = o.catalogueModules ?? ["distribution", "mesh-control", "builder"];
const modules = o.catalogueModules ?? ["distribution", "mesh-controller", "builder"];
const catalogueOnMachine = o.catalogueOnMachine ?? "/opt/mesh-catalog";
const log = o.log ?? (() => {});
@@ -147,9 +147,9 @@ export async function genesis(o: GenesisOptions): Promise<GenesisResult> {
}
report.push(` catalogue full checkout at ${catalogueOnMachine} (${modules.join(", ")} + phase two)`);
const local = join(tmpdir(), `mesh-lab-substrate-${process.pid}-${node}.lock`);
const local = join(tmpdir(), `mesh-lab-foundation-${process.pid}-${node}.lock`);
writeFileSync(local, o.bundleTemplate);
await push(o.instanceId, node, local, "/tmp/substrate-template.lock");
await push(o.instanceId, node, local, "/tmp/foundation-template.lock");
// Supervise the host as a service (the real install path) when asked — the only way it survives a
// reboot. Installs the shipped packaging in the machine, then lets --host-service start+enable it.
@@ -165,7 +165,7 @@ export async function genesis(o: GenesisOptions): Promise<GenesisResult> {
BOOTSTRAP_PATH,
`--source ${o.source}`,
`--source-ref ${o.sourceRef}`,
`--bundle /tmp/substrate-template.lock`,
`--bundle /tmp/foundation-template.lock`,
`--catalog ${catalogueOnMachine}`,
`--node ${node}`,
`--registry ${registry}`,
@@ -212,9 +212,9 @@ export async function genesis(o: GenesisOptions): Promise<GenesisResult> {
// ------------------------------------------------------------------------------------------
// 1. The control plane answers, asked of the PERMANENT container by name.
const answered = await on(`docker exec mesh-control /mesh-control status`, 60_000);
const answered = await on(`docker exec mesh-controller /mesh-controller status`, 60_000);
report.push(` control plane ${answered.ok ? answered.out.split("\n")[0] : "NO ANSWER"}`);
if (!answered.ok) return stop("after the last step", `mesh-control does not answer:\n${answered.out}`);
if (!answered.ok) return stop("after the last step", `mesh-controller does not answer:\n${answered.out}`);
// 2. The registry replies on /v2/. A container that is up is not a registry that serves.
const v2 = await on(`curl -s -o /dev/null -w '%{http_code}' --max-time 10 http://${registry}/v2/`);
@@ -224,17 +224,17 @@ export async function genesis(o: GenesisOptions): Promise<GenesisResult> {
// 3. THE PIVOT COMPLETED — the running control plane is pinned by a digest THIS MESH'S REGISTRY
// assigned, not by an image id (ADR 0067 states this check in as many words).
const pinnedTo = (await on(`docker inspect --format '{{.Config.Image}}' mesh-control`)).out.trim();
const pinnedTo = (await on(`docker inspect --format '{{.Config.Image}}' mesh-controller`)).out.trim();
report.push(` pinned to ${pinnedTo || "(nothing)"}`);
if (/^sha256:[0-9a-f]{64}$/.test(pinnedTo)) {
return stop("after the last step",
`mesh-control is running from ${pinnedTo}, which is an IMAGE ID — the digest of the image's ` +
`mesh-controller is running from ${pinnedTo}, which is an IMAGE ID — the digest of the image's ` +
`own configuration, which no registry ever served. The pivot did not happen, so this mesh ` +
`cannot upgrade itself (novox/hq ADR 0067, "the pivot completed").`);
}
if (!new RegExp(`^${registry.replaceAll(".", "\\.")}/mesh-control@sha256:[0-9a-f]{64}$`).test(pinnedTo)) {
if (!new RegExp(`^${registry.replaceAll(".", "\\.")}/mesh-controller@sha256:[0-9a-f]{64}$`).test(pinnedTo)) {
return stop("after the last step",
`mesh-control is running from ${pinnedTo || "nothing this bed could read"}, which is not a ` +
`mesh-controller is running from ${pinnedTo || "nothing this bed could read"}, which is not a ` +
`digest assigned by ${registry}.`);
}
@@ -249,34 +249,34 @@ export async function genesis(o: GenesisOptions): Promise<GenesisResult> {
// installer that quietly built something else would satisfy a weaker check and raise a mesh
// nobody asked for.
const wanted = o.sourceRef.slice(0, 8);
if (!new RegExp(`built mesh-control from ${wanted}`).test(said)) {
if (!new RegExp(`built mesh-controller from ${wanted}`).test(said)) {
return stop("after the last step",
`the installer never said it built mesh-control from ${wanted}. What runs may have been ` +
`the installer never said it built mesh-controller from ${wanted}. What runs may have been ` +
`carried rather than made here, which is a mesh that cannot rebuild its own control plane. ` +
`The installer said:\n${said.split("\n").filter((l) => /built|build/.test(l)).join("\n") || "(nothing about building)"}`);
}
report.push(` built here mesh-control from ${wanted}, by the carried builder`);
report.push(` built here mesh-controller from ${wanted}, by the carried builder`);
// 3b. And the registry really serves it. A reference is a claim; a tag list is the registry agreeing.
const tags = await on(`curl -s --max-time 10 http://${registry}/v2/mesh-control/tags/list`);
const tags = await on(`curl -s --max-time 10 http://${registry}/v2/mesh-controller/tags/list`);
report.push(` registry holds ${tags.out.trim() || "nothing"}`);
if (!tags.out.includes("genesis")) {
return stop("after the last step",
`${registry} does not serve mesh-control, so the digest the container is pinned to names an ` +
`${registry} does not serve mesh-controller, so the digest the container is pinned to names an ` +
`image nothing can pull: ${tags.out.trim()}`);
}
// 4. The temporary control plane is GONE. The name is the audit.
const temp = await on(`docker inspect --format '{{.State.Status}}' temp-mesh-control`);
report.push(` temp-mesh-control ${temp.ok ? `STILL HERE (${temp.out.trim()})` : "gone"}`);
const temp = await on(`docker inspect --format '{{.State.Status}}' temp-mesh-controller`);
report.push(` temp-mesh-controller ${temp.ok ? `STILL HERE (${temp.out.trim()})` : "gone"}`);
if (temp.ok) {
return stop("after the last step",
`temp-mesh-control is still ${temp.out.trim()}. Two control planes are consuming this mesh's ` +
`temp-mesh-controller is still ${temp.out.trim()}. Two control planes are consuming this mesh's ` +
`broker queues; neither is wrong and the pivot is not finished.`);
}
// 5. And the mesh has heard from its one node.
const nodes = await on(`docker exec mesh-control /mesh-control node list`);
const nodes = await on(`docker exec mesh-controller /mesh-controller node list`);
report.push(` node list ${nodes.out.trim().split("\n").join(" | ")}`);
const line = nodes.out.split("\n").map((l) => l.trim()).find((l) => l.startsWith(`${node} `));
if (!line || !/^\S+\s+here\b/.test(line)) {