Raise a process-form controller at genesis as the container it replaces (hq issue 223) #87

Merged
mesh-admin merged 1 commits from fix/issue-223-genesis-pivots-to-the-controllers-container into main 2026-10-03 23:50:49 +00:00
Contributor

novox/hq issue 223, option (b). Genesis pivots to the controller as an image and container, as it does today. The first push then hands that container over to the process, through replaces (#86).

Why genesis can't raise the process directly. The process's bundle is fetched from the artifact store and compiled in the mesh's Go toolchain. Genesis makes both of those long after the controller. Asked to build the process-form manifest at genesis, the carried builder refuses: "holds no copy of mesh-tools-go".

Shape

  • Step 3 (build):
    • Clone the controller at the requested commit, using the carried builder's git (--entrypoint git), and read its module.json.
    • Image form (an older controller): build through the builder, exactly as before.
    • Process form: docker build the repository's own Dockerfile (the one make image uses). Then hand step 9 genesis's own container shape:
      • the process is swapped for a container, with the id the process names in replaces (server) and name mesh-controller;
      • it runs the built image on the host network, with args: [serve] and the process's env unchanged;
      • every host path the env names is mounted at the same path, read-only;
      • it runs as the image's own USER 65534, so secrets-owner is 65534:65534 until the process's account takes the files over;
      • prepares is dropped: the temporary controller from the same commit already migrated the stores, and a pinned image gives the controller nothing to derive a step from.
  • Steps 4 to 9: unchanged. They pin, register, deliver the stores, push, and wait for the mesh-controller container, the same way they handle an image-form manifest.
  • First push after genesis: whenever the mesh first builds mesh-controller from its repository, the registered manifest becomes the process form. Its declaration names replaces: [mesh-controller.server], which is what the host recorded for the genesis container, so the host hands over and one controller remains.
  • Manifest: carries nothing extra. mesh-controller #253 only pins the Dockerfile's default GO_BASE, because genesis builds it with no arguments.

Tests

  • genesis_form_test.go:
    • a process-form controller is built from its Dockerfile and never by the builder;
    • its genesis manifest is the container under the replaces id, with no process and no build/prepares;
    • step 9 pins it, finds the container, and delivers the stores from its env;
    • an image-form controller still goes through the builder;
    • a process naming zero or two replacements is refused.
  • genesis_handover_test.go: applies the controller's first process declaration against a host state holding the genesis container under <module>.<genesis id>. The process is started first, the container is removed after, and nothing is left behind.
  • The three existing build tests now answer the clone.
  • Without the build.go change, the process-form test fails. go test ./... passes.
  • Checked by hand: the genesis form of mesh-controller #253's real module.json is accepted by the controller (parse, no installation problems, composes to mesh-controller.server), and the host accepts the composed declaration.

apply.ForTests is new: it lets the bootstrap's test apply a process in temporary directories.

Not changed: the image-form genesis path. I noticed, but did not touch, that an image-form controller declaring prepares would be refused by module add in the same way, because a pinned registry image is not an artifact-store reference.

novox/hq issue 223, option (b). Genesis pivots to the controller as an image and container, as it does today. The first push then hands that container over to the process, through `replaces` (#86). **Why genesis can't raise the process directly.** The process's bundle is fetched from the artifact store and compiled in the mesh's Go toolchain. Genesis makes both of those long after the controller. Asked to build the process-form manifest at genesis, the carried builder refuses: "holds no copy of mesh-tools-go". **Shape** - **Step 3 (build):** - Clone the controller at the requested commit, using the carried builder's `git` (`--entrypoint git`), and read its `module.json`. - **Image form** (an older controller): build through the builder, exactly as before. - **Process form:** `docker build` the repository's own Dockerfile (the one `make image` uses). Then hand step 9 genesis's own container shape: - the process is swapped for a container, with the **id the process names in `replaces`** (`server`) and name `mesh-controller`; - it runs the built image on the host network, with `args: [serve]` and the process's env unchanged; - every host path the env names is mounted at the same path, read-only; - it runs as the image's own `USER 65534`, so `secrets-owner` is `65534:65534` until the process's account takes the files over; - `prepares` is dropped: the temporary controller from the same commit already migrated the stores, and a pinned image gives the controller nothing to derive a step from. - **Steps 4 to 9:** unchanged. They pin, register, deliver the stores, push, and wait for the `mesh-controller` container, the same way they handle an image-form manifest. - **First push after genesis:** whenever the mesh first builds mesh-controller from its repository, the registered manifest becomes the process form. Its declaration names `replaces: [mesh-controller.server]`, which is what the host recorded for the genesis container, so the host hands over and one controller remains. - **Manifest:** carries nothing extra. mesh-controller #253 only pins the Dockerfile's default `GO_BASE`, because genesis builds it with no arguments. **Tests** - `genesis_form_test.go`: - a process-form controller is built from its Dockerfile and never by the builder; - its genesis manifest is the container under the `replaces` id, with no process and no `build`/`prepares`; - step 9 pins it, finds the container, and delivers the stores from its env; - an image-form controller still goes through the builder; - a process naming zero or two replacements is refused. - `genesis_handover_test.go`: applies the controller's first process declaration against a host state holding the genesis container under `<module>.<genesis id>`. The process is started first, the container is removed after, and nothing is left behind. - The three existing build tests now answer the clone. - Without the `build.go` change, the process-form test fails. `go test ./...` passes. - Checked by hand: the genesis form of mesh-controller #253's real `module.json` is accepted by the controller (parse, no installation problems, composes to `mesh-controller.server`), and the host accepts the composed declaration. `apply.ForTests` is new: it lets the bootstrap's test apply a process in temporary directories. **Not changed:** the image-form genesis path. I noticed, but did not touch, that an image-form controller declaring `prepares` would be refused by `module add` in the same way, because a pinned registry image is not an artifact-store reference.
mesh-admin added 1 commit 2026-10-03 23:49:31 +00:00
The controller's manifest now declares a Go bundle the host runs as a
process (novox/hq issue 213). Genesis cannot run that: the bundle is
fetched from the artifact store and compiled in a toolchain, and the mesh
makes both long after the controller. The builder, asked to build the
manifest at genesis, refuses for lack of the Go toolchain. So genesis
raises the controller as before, as a container, and the first push hands
it over to the process through `replaces` (issue 223, option b).

- Step 3 clones the controller at the commit with the carried builder's
  git and reads its manifest. In the image form (an older controller) it
  builds through the builder as before. In the process form it builds the
  repository's own Dockerfile and hands step 9 a manifest of its own
  shape: the process becomes a container with the id the process
  `replaces`, the image genesis built, host network, and every host path
  the process's env names mounted at that same path read-only. Secrets
  belong to the image's user (65534) until the process's account takes
  them over. `prepares` is dropped: the temporary controller from the
  same commit already migrated the stores, and a pinned image is
  nothing the controller can derive a step from.
- Steps 4 to 9 are unchanged: they take the manifest as they did.
- apply.ForTests lets the bootstrap's test apply a process.

The first composed declaration from the process manifest names
`mesh-controller.server`, which is what the host recorded for the genesis
container, so the first apply hands over and leaves one controller.
mesh-admin merged commit 2d5e76434b into main 2026-10-03 23:50:49 +00:00
mesh-admin deleted branch fix/issue-223-genesis-pivots-to-the-controllers-container 2026-10-03 23:50:49 +00:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: novox/mesh-host#87