Merge pull request 'to-be 17: genesis assigns the builder before the package registry exists and hands it the forge it raised (issue 121, ADR 0336)' (#337) from fix/121-genesis-package-registry into main

This commit was merged in pull request #337.
This commit is contained in:
2026-10-13 03:37:18 +00:00
+31 -1
View File
@@ -5,7 +5,7 @@ code:
- mesh-host cmd/mesh-bootstrap
- mesh-host internal/bootstrap
- mesh-lab test/integration/whole-mesh-full.test.ts
updated: 2026-09-22
updated: 2026-10-13
decisions:
- "ADR 0100"
- "ADR 0101"
@@ -16,6 +16,7 @@ decisions:
- "ADR 0005"
- "ADR 0010"
- "ADR 0036"
- "ADR 0336"
---
# Raising a mesh
@@ -145,6 +146,29 @@ plus the one thing only a builder needs. The account is issued before the machin
because a builder that arrives without its credential starts, finds nothing it may read, and waits,
which looks exactly like a builder with no work.
**The builder is assigned before the package registry exists, and is handed the forge genesis raised**
(ADR 0336). The builder's module, `build-agent`, requires the package registry
(`npm-package-registry`) only optionally (ADR 0330), so assigning it on a mesh where nothing provides one is
not refused: the requirement is listed unbound by `plan` and `node`, and binds with nobody's act on the
composition after the gitea module is assigned. Genesis is not reordered for it. The forge raised at the
packages step is a plain container, not the gitea module, and provides nothing, and the gitea module needs
the store, the vault and a route, which come after the base.
So the packages step, once it has raised the forge and made its account `mesh-builder`, seals the builder on
this machine one own-secret, the **genesis document** (`genesis-package-registry`): the forge's address, the
port this machine gave the package registry, the npm path, and the account with its password. The manifest
declares it as issued outside the mesh, so no other machine is ever given one. The sdk step publishes the
SDK with that account and the base step resolves it with the same one. Nothing else in genesis needs the
registry.
A real binding wins over the genesis document whenever both are present, and every build's log says which of
the two it resolved from. A build that needs the registry (a `package` artifact, an image whose Dockerfile
names `.npmrc`, a TypeScript artifact with an `@novox/` dependency) on a builder that has neither fails before
anything is built or published. Its outcome names the machine, says the requirement is unbound there, and
names the gitea module as what provides it. It never builds without the credential and never resolves the
mesh's scope from the public registry. Go artifacts, images that do not name `.npmrc` and upstream images
need no registry. The gitea module's own server is an upstream image, so building the forge needs no forge.
**The core modules are built.** Each is named by a repository and a path
(ADR 0069), and the builder is
asked for each in turn: it clones, reads the manifest at that path, produces what it declares,
@@ -226,6 +250,11 @@ is not yet wired is the genesis step that raises gitea and publishes the SDK ahe
the toolchain's own manifest still names the SDK by a git URL. Until both land, the base build clones
the SDK inside `docker build`, which is slow and names a branch head rather than a version.
**The genesis credential outlives the binding.** Once the gitea module is assigned the builder resolves the
registry from its binding, but the genesis document and the `mesh-builder` account are still there, so the
mesh holds two credentials for one registry. Withdrawing them is not decided; the
issue tracker holds it as issue 602.
## How these rules are checked
| Rule | Checked by |
@@ -236,6 +265,7 @@ the SDK inside `docker build`, which is slow and names a branch head rather than
| An image is named exactly | A machine refuses a bundle naming an image by tag. The refusal is exercised, not assumed. |
| The installer is what installed this | **Nothing.** See above. |
| The builder can arrive on a fresh mesh | The installer carries it and installs it as its last step, and the genesis bed raises a machine by running the installer. A bed that raises one any other way fails its own acceptance check. |
| The builder is assigned before the package registry exists | The build-agent manifest's own test holds the requirement optional and the genesis document declared and issued outside the mesh; the controller's resolution test over the real catalogue assigns the builder with no provider, lists the requirement unbound, and binds it once gitea is offered. The builder's tests refuse a package, an `.npmrc` image and an `@novox/` TypeScript build with neither binding nor genesis document, before anything is built, and build a Go artifact; they read the genesis document only without a binding. The installer's test seals the document at the packages step and gives the builder no `serves` setting and no `npm-password`. **Not yet proved live:** a genesis bed from bare passing the builder, packages, sdk and base steps. |
| The controller a mesh runs is one it built | The genesis bed asserts the running controller is pinned to a digest this mesh's own registry serves, for an image built from a named repository and commit — not one the installer carried. |
| A core module is built rather than only carried | The controller is rebuilt from its own repository and path, and the running mesh is upgraded to the result — the same path any module takes. |
| Installing produced a mesh that can produce | A module with source of its own is asked for and comes back pinned to a digest this mesh's registry assigned, not to a placeholder. **Done by hand on a raised machine, not yet by a bed** — it built the shared base and then a module naming that base. Nothing automated asserts it, which makes this the weakest check on this page. |