package bootstrap import ( "bytes" "context" "encoding/json" "fmt" ) // BuilderModule is the module that lets a mesh produce anything at all: the build agent, which holds // the node seat `node-build-agent` (novox/hq ADR 0190). The one-holder `builder` module it replaced // left the catalogue on 2026-10-03, and genesis went on asking for it (issue 596). const BuilderModule = "build-agent" // BuilderRepository is what its image is called in this mesh's own registry. const BuilderRepository = "mesh-builder" // Builder is what installing it produced. type Builder struct { Published Published Installed Installed // Account is true when a broker account was issued for it here. Account bool } // InstallBuilder gives a fresh mesh the thing that makes everything else. // // **Without this a mesh can run and cannot produce** (novox/hq ADR 0073). Genesis ends with a // control plane, a store, a queue and a registry — and almost every module in the catalogue is // waiting to be built, because a manifest names artifacts and nothing has made them. The builder is // one of the few things that cannot be built by the thing it is, so it is carried; and it is // already here, because it is what built the control plane. // // So this is the same two acts the control plane went through, in the same order and for the same // reason: publish the image so the mesh names it by a digest its own registry assigned rather than // by a local identity nothing else can fetch, then install it as an ordinary module pinned to that. // // And then the part only it needs: a broker account. A builder takes work from a queue and // announces what it made, and it holds its own credential for that like any module — asking for a // generic one produced an account that could do neither, which is what made this worth its own step // rather than a line in another. func InstallBuilder(ctx context.Context, o Options, d Deps, control controlPlane, imageID string, say func(string)) (Builder, error) { var out Builder published, err := publishAs(ctx, o, d, imageID, BuilderRepository, say) out.Published = published if err != nil { return out, err } manifest, err := readManifest(o.Catalogue, BuilderModule) if err != nil { return out, fmt.Errorf("%w\n"+ "This is the manifest that makes the build agent an ordinary module. Without it the mesh "+ "has the image and no way to run it, so nothing can be built here", err) } pinned, places, err := pinTheCarriedBuilder(manifest, published.Reference) if err != nil { return out, err } say(fmt.Sprintf(" pinned to %s, named in %d place(s)", published.Reference, places)) installed, err := registerAndAssign(ctx, o, control, BuilderModule, pinned, say) out.Installed = installed if err != nil { return out, err } // Before the push, so the account is in the declaration the machine receives rather than in // the one after it. A build agent that arrives without its credential starts, finds nothing it // may read, and waits — which looks exactly like an agent with no work. The same act as any // module's account (`module issue`), which a re-run repeats harmlessly. if _, err := control.tell(ctx, "module", "issue", BuilderModule, "--node", o.Node); err != nil { return out, fmt.Errorf("the build agent has no account on the bus, so it can take no work: %w", err) } out.Account = true say(" bus account " + BuilderModule + " on " + o.Node + ", scoped to what it consumes and emits") out.Installed.Pushed, err = pushNode(ctx, o, control, say) return out, err } // pinTheCarriedBuilder names the image this installer carried and published in the build agent's // manifest, in place of the artifact the mesh would build for it. // // **The one module genesis cannot have built** (novox/hq ADR 0073): the build agent is what builds, // and at this step the mesh has no build agent. The catalogue's manifest names its server container by // the artifact it builds (`"artifact": "server"`, an image built from mesh-controller's // cmd/mesh-builder); the image this installer carries is that build, made from the same program. So // every container built from that image artifact runs the published reference instead, and the rest // of the manifest — its build, its seat, its requirements — is the catalogue's as written. The mesh // rebuilds it from the catalogue like any module from then on. // // A manifest still carrying the old placeholder digest is pinned the old way. One naming neither an // image artifact nor a placeholder is refused: there would be nothing to put the carried image in. func pinTheCarriedBuilder(manifest []byte, reference string) ([]byte, int, error) { if bytes.Contains(manifest, []byte(placeholderDigest)) { return pinPlaceholder(manifest, reference, BuilderModule) } var m map[string]any if err := json.Unmarshal(manifest, &m); err != nil { return nil, 0, fmt.Errorf("the %s module's manifest is not readable: %w", BuilderModule, err) } images := map[string]bool{} if build, ok := m["build"].(map[string]any); ok { artifacts, _ := build["artifacts"].([]any) for _, a := range artifacts { if art, ok := a.(map[string]any); ok && art["kind"] == "image" { if name, ok := art["name"].(string); ok { images[name] = true } } } } places := 0 resources, _ := m["resources"].([]any) for _, r := range resources { res, ok := r.(map[string]any) if !ok || res["type"] != "container" { continue } if name, ok := res["artifact"].(string); ok && images[name] { delete(res, "artifact") res["image"] = reference places++ } } if places == 0 { return nil, 0, fmt.Errorf( "the %s module's manifest runs no container from an image it builds, so there is nowhere to "+ "put the builder this installer carried and published (%s)", BuilderModule, reference) } pinned, err := json.MarshalIndent(m, "", " ") if err != nil { return nil, 0, err } return append(pinned, '\n'), places, nil }