Files
mesh-catalog/modules/mesh-build-image/README.md
T
jschoubben 1c4739698a CAT-BI1: a copy keeps its wait for the mirror's outcome in the state mirror-waits
The image seat's published and refused reach one instance of the module, not
necessarily the one whose copy waits, so an outcome held only in memory was
dropped. The instance that hears it now writes it into the copy's key, and the
copy reads it there, woken by its watch of the state; build_image_mirror_waits
reads the state.

Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
2026-11-04 15:58:37 +01:00

5.4 KiB

mesh-build-image — the image build module

Design 47, release R4c-9 (novox/hq ADR 0487, ADR 0466 §1, ADR 0469; issue 1703). The module that builds a module's image artifact from the Dockerfile in the module's own directory, on a build machine, as an account of its own (runs-as, no login shell, home at mode 0700). It serves every module's image, whatever the base, so it is not a build module for one module. It claims no seat: it is assigned to the build machines and runs dark until the builder uses it.

Rootless

The tool is buildah, which the buildah module installs and provides as rootless-image-build with the machine's reach: a machine without it refuses this module at assignment, naming buildah. The build runs as the module's own account in a user namespace (--isolation oci), with buildah's image store and runtime state in /var/lib/mesh-build-image-store (outside the home), no mount of the host, no container runtime socket, no registry credential and an environment built from scratch.

The account's subordinate user and group ranges are not declared anywhere: the node-engine makes every user account with useradd, which allocates them. The module's health reads /etc/subuid and /etc/subgid and is unhealthy when the module cannot run: without a range for the account, without buildah, when a user namespace cannot be made, or when the disk is under its floor. A node where /dev/net/tun cannot be opened stays healthy and is not eligible (buildah makes a RUN line's network with pasta, which needs the tun driver; on 2026-10-28 a build node had the device node and no driver, so every image build there failed with "Failed to open() /dev/net/tun: No such device"): the seat's puller judges by a capacity of 0 with that reason (capacity.<node>, which build_usage shows), so the asks go to a node that can build them, and its health says "not eligible" with the cause, so a node that cannot build here now never holds the module's delivery for every node. The kernel module loads tun at boot; a look that passes again wakes the puller.

The Dockerfile's steps may use the network (ADR 0487 §6); nothing a step can read holds a key.

Steps

  • build_image_fetch fetches a commit over SSH into a workspace (a base commit makes the merge preview, as the other build modules' fetch). Long: answers a step id and ends on its key in steps.
  • build_image_build builds one image artifact: buildah bud with the Dockerfile from names (in the module's directory or a subdirectory of it) as file and the module's own directory as context, so a COPY names paths from the module's root (issue 1703), --target, and the images build.on pins as --build-arg. Answers the image's digest. With publish: true (a merged trunk commit only) the module's own process then writes it as an OCI layout in the workspace and publishes it through the seat container-image-registry (publish, publish-part, which the manifest invokes; design 51 Publishing) as <module>/<artifact> at the ask's version (and a tag only when the call gives one): its config, layers and manifest by digest, of which only the parts the holder lacks travel. The key carries the layout's manifest digest and the seat's reference container-image-registry/<module>/<artifact>@sha256:<digest>; a publish the holder does not take fails not stored:. Without publish (a pull request) nothing is published.
  • build_image_copy copies an upstream artifact (module, artifact, from) into the image seat by its mirror, by the digest the manifest pins (ADR 0498 §4): the holder copies it and says published or refused on the seat's events, which the manifest consumes; the step ends on that outcome (or fails not stored: after 30 minutes without one) and answers the digest and the seat's reference. A from without a digest fails not pinned: before the seat is asked. It needs no workspace, no buildah, and reads no outside registry itself. The copy's wait is kept in the state mirror-waits under its digest before the seat is asked: the seat's events reach one instance of the module, which writes the outcome into that key, and the waiting copy reads it there, woken by its watch of the state. A copy removes its wait when it ends; one a stopped process left goes after two days. build_image_mirror_waits reads that state.
  • It refuses, naming why, an image whose build context is a whole repository (context) or whose build.on names another module's artifact (unsupportedImage in build.go): those forms belong to the images build-agent builds until it is removed at R4g.
  • build_image_release {workspace} removes a workspace at once (no guard, no floor; one already gone is no error, one a running step holds is refused); its caller calls it once its last step answered. A failed fetch removes its own workspace, and one no caller released is removed two hours after its last use (ADR 0509).
  • A fetch and a build are refused, refused: <node> has N GiB free under the floor of 20 GiB (on <dir>), and the health is unhealthy with that text, while the work directory's or the image store's filesystem has less than 20 GiB free (issue 1839). A copy writes nothing to the disk and is not refused.
  • build_image_step, build_image_steps, build_image_status and build_image_health read.

The module holds no registry address and no registry credential: every write goes through the image seat's verbs, and the holder checks each publish with the builder.