From 39830983ccf186229d42dc87879faf17afbfe206 Mon Sep 17 00:00:00 2001 From: jochen Date: Tue, 13 Oct 2026 02:55:59 +0200 Subject: [PATCH] to-be 17: genesis assigns the builder before the package registry exists and hands it the forge it raised (issue 121, ADR 0336) --- 03-DESIGN/01-to-be/17-raising-a-mesh.md | 32 ++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/03-DESIGN/01-to-be/17-raising-a-mesh.md b/03-DESIGN/01-to-be/17-raising-a-mesh.md index 400b4dee..8b8bf196 100644 --- a/03-DESIGN/01-to-be/17-raising-a-mesh.md +++ b/03-DESIGN/01-to-be/17-raising-a-mesh.md @@ -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. |