The installer carries a builder and builds the control plane it raises

It carried the thing it was going to run; it now carries the thing that makes
it. One artifact either way — but a mesh raised this way holds a control plane
it built from a repository and a commit it can name, and can therefore build
again. A mesh handed a finished image could not, and had no way to find that
out until somebody needed it to.

A build step sits between load and bundle, because the bundle must name an
image and that image no longer arrives finished. Everything after it is
unchanged: a locally built image is named by the digest of its own
configuration, which is exactly what the carried one was named by.

Refused in preflight when nothing says what to build, so a run that cannot
finish says so before it has changed anything.
This commit is contained in:
2026-09-13 04:08:58 +02:00
parent cab83b61c8
commit e1a2fe7323
10 changed files with 312 additions and 37 deletions
+15 -11
View File
@@ -58,13 +58,17 @@ host:
@echo "built for $(SYSTEM) carrying $(BUNDLE)"
# The installer, carrying the control plane's image:
# make bootstrap IMAGE=mesh-control:v1.2.3
# make bootstrap IMAGE=mesh-builder:v1.2.3
#
# The image is BUILT ELSEWHERE and handed over — mesh-control's own `make image` — and embedded
# here at release time. Not built on the machine being bootstrapped, and not fetched: the forge
# that holds mesh-control's source runs on the mesh, so a bootstrap that had to fetch or build the
# control plane would need a mesh in order to raise one. Carrying it breaks that cycle, the same
# way carrying the bundle breaks the "copy it onto a machine and run it" one (novox/hq ADR 0005).
# **The carried image is the BUILDER** (novox/hq ADR 0073). It used to be the control plane, on the
# argument that the forge holding the source runs on the mesh, so building at genesis would need a
# mesh in order to raise one. That argument was about the *control plane's* source, and it is
# answered by ADR 0071: the source comes from a mesh that already exists, which is not the one being
# raised. What cannot be fetched is the thing that does the fetching, and that is what is carried.
#
# The image is BUILT ELSEWHERE and handed over — mesh-control's own `make builder-image` — and
# embedded here at release time, the same way carrying the bundle breaks the "copy it onto a machine
# and run it" cycle (novox/hq ADR 0005).
#
# The saved image occupies the embed slot for the length of one build and the placeholder goes
# back, exactly as `host:` does with the bundle. Nothing large is ever committed.
@@ -84,15 +88,15 @@ host:
BOOTSTRAP_OUT ?= mesh-bootstrap
bootstrap:
@test -n "$(IMAGE)" || { echo "IMAGE= is required; an installer carrying no control-plane image cannot raise a mesh"; exit 1; }
@test -n "$(IMAGE)" || { echo "IMAGE= is required; an installer carrying no builder image cannot raise a mesh"; exit 1; }
@case "$(IMAGE)" in sha256:*) echo "IMAGE=$(IMAGE) is an image id. The installer identifies the carried image by its tag, because an id is the digest of a configuration that a runtime rewrites as it loads. Pass a name:tag"; exit 1;; esac
@docker image inspect "$(IMAGE)" >/dev/null 2>&1 || { echo "this machine does not hold $(IMAGE) — build it in mesh-control with 'make image'"; exit 1; }
@test -n "$$(docker image inspect --format '{{len .RepoTags}}' "$(IMAGE)" | grep -v '^0$$')" || { echo "$(IMAGE) has no repository tag, so the saved archive would carry no name the installer can ask a runtime about. Tag it first: docker tag $(IMAGE) mesh-control:<version>"; exit 1; }
@cp internal/image/control-plane.tar internal/image/control-plane.tar.placeholder
@docker save --output internal/image/control-plane.tar "$(IMAGE)"
@test -n "$$(docker image inspect --format '{{len .RepoTags}}' "$(IMAGE)" | grep -v '^0$$')" || { echo "$(IMAGE) has no repository tag, so the saved archive would carry no name the installer can ask a runtime about. Tag it first: docker tag $(IMAGE) mesh-builder:<version>"; exit 1; }
@cp internal/image/builder.tar internal/image/builder.tar.placeholder
@docker save --output internal/image/builder.tar "$(IMAGE)"
@CGO_ENABLED=0 go build -ldflags="-s -w -X main.version=$(VERSION)" -o "$(BOOTSTRAP_OUT)" ./cmd/mesh-bootstrap; \
status=$$?; \
mv internal/image/control-plane.tar.placeholder internal/image/control-plane.tar; \
mv internal/image/builder.tar.placeholder internal/image/builder.tar; \
exit $$status
@echo "built $(BOOTSTRAP_OUT) carrying $(IMAGE)"