From 5795e03fad7b2501cf98952835aaa5b77743e262 Mon Sep 17 00:00:00 2001 From: jochen Date: Tue, 13 Oct 2026 01:16:10 +0200 Subject: [PATCH] Read the one genesis bundle mesh-host carries now (hq issue 146, ADR 0331) mesh-host #96 replaced foundation-first-node-nats.lock with foundation-first-node.lock, and the test that holds the installer's first user list to the controller's composition still read the removed file, so repo-check failed on main and every pull request. The captured copy moves to mesh-host 7a41e12, with the placement guard at the same commit. --- internal/broker/genesis_template_test.go | 2 +- testdata/beside/CAPTURED | 4 +-- ...e-nats.lock => foundation-first-node.lock} | 33 ++++++++++++++----- .../apply/placement_guard.go.captured | 14 ++++++++ 4 files changed, 42 insertions(+), 11 deletions(-) rename testdata/beside/mesh-host/examples/{foundation-first-node-nats.lock => foundation-first-node.lock} (87%) diff --git a/internal/broker/genesis_template_test.go b/internal/broker/genesis_template_test.go index 91b13d31..b598cbae 100644 --- a/internal/broker/genesis_template_test.go +++ b/internal/broker/genesis_template_test.go @@ -82,7 +82,7 @@ func theCarriedAccounts(t *testing.T) string { // the commit the mesh runs, or as captured in testdata/beside (internal/beside, novox/hq issue 432). func theTemplate(t *testing.T) []map[string]any { t.Helper() - path := filepath.Join(beside.Dir(t, "mesh-host"), "examples", "foundation-first-node-nats.lock") + path := filepath.Join(beside.Dir(t, "mesh-host"), "examples", "foundation-first-node.lock") raw, err := os.ReadFile(path) if err != nil { t.Fatal(err) diff --git a/testdata/beside/CAPTURED b/testdata/beside/CAPTURED index a37baf3b..0750cad9 100644 --- a/testdata/beside/CAPTURED +++ b/testdata/beside/CAPTURED @@ -8,7 +8,7 @@ private addresses. That is acceptable while mesh-controller is private; if it ev a second place to clean besides the catalogue itself. mesh-catalog b9de001833b1b61b297182b8e3bcdae1cbdeace9 modules/*/module.json, modules/nats/Dockerfile -mesh-host a76baa4b47f07a8b2bab065603414530bd02b53a examples/foundation-first-node-nats.lock +mesh-host 7a41e120635b3f7b6cb94cca8b3fc4093e4e6110 examples/foundation-first-node.lock mesh-sdk 33917f91ac3cc213b879b32f3c70e2aa70395f98 conformance/events/module-event.json To move them, from this repository's root, with the two repositories checked out beside it: @@ -18,7 +18,7 @@ To move them, from this repository's root, with the two repositories checked out git -C ../mesh-catalog archive modules | tar -x -C testdata/beside/mesh-catalog --wildcards \ 'modules/*/module.json' 'modules/nats/Dockerfile' find testdata/beside -name module.json -exec mv {} {}.captured \; - git -C ../mesh-host archive examples/foundation-first-node-nats.lock | tar -x -C testdata/beside/mesh-host + git -C ../mesh-host archive examples/foundation-first-node.lock | tar -x -C testdata/beside/mesh-host and write the commits here. The SDK is captured at the commit go.mod pins for git.novox.be/novox/mesh-sdk/go (the last part of its pseudo-version; novox/hq issue 449), which internal/link's conformance test holds it to: diff --git a/testdata/beside/mesh-host/examples/foundation-first-node-nats.lock b/testdata/beside/mesh-host/examples/foundation-first-node.lock similarity index 87% rename from testdata/beside/mesh-host/examples/foundation-first-node-nats.lock rename to testdata/beside/mesh-host/examples/foundation-first-node.lock index 25d84627..289e3e2a 100644 --- a/testdata/beside/mesh-host/examples/foundation-first-node-nats.lock +++ b/testdata/beside/mesh-host/examples/foundation-first-node.lock @@ -1,11 +1,17 @@ -// foundation-first-node-nats.lock — what a machine must be before a mesh exists, on the bus being -// built (novox/hq ADR 0106, design 25). +// foundation-first-node.lock — what a machine must be before a mesh exists: the one genesis bundle +// (novox/hq ADR 0331 §2), on the bus the mesh runs on (ADR 0106, ADR 0131, design 25). // -// The same twelve steps as foundation-first-node.lock, with one difference that matters: **the mesh -// writes its own user list, and at genesis there is no mesh yet to write it.** So this carries the -// first one — the controller's own account, at a well-known bootstrap password, exactly as the store -// is reached at `postgres:bootstrap` and the old bus at `guest:guest`. It is rotated with those, and -// from the controller's first composition onward the file is the controller's to write. +// The whole bootstrap (novox/hq 03-DESIGN/01-to-be/07-the-foundation.md): a container runtime, a +// filter, a store, a database per context, those contexts' schemas, the bus with its certificate +// and its first user list, and the control plane running on top of them. +// +// **The mesh writes its own user list, and at genesis there is no mesh yet to write it.** So this +// carries the first one — the controller's own account — and the control plane's address names the +// same account. Both say `bootstrap`, and both are placeholders: the installer makes the account's +// password, puts it in the address and its hash in the list (mesh-host internal/bootstrap +// rootsecrets.go), and refuses a bundle whose bus connection it does not recognise. Until the first +// node has enrolled the installer places the list the controller composes; from the bus's adoption +// as the `nats` module on, the module receives it like any module (ADR 0331 §1, §5). // // The accounts file is its own file beside the server's configuration, because the server's own // settings belong to whoever raises it and the users belong to the mesh (design 25 §4). Both live in @@ -15,7 +21,18 @@ // // No `verify` on the TLS block, deliberately — that setting makes the server demand a *client* // certificate, and nothing in the mesh presents one: a host pins this server's exact certificate and -// authenticates with a password (ADR 0004, design 25 §4). +// authenticates with a password (ADR 0004, design 25 §4). The control plane pins it too, through +// MESH_BROKER_CERTIFICATE, and dials the bus on the machine's own loopback. +// +// PINNED BY DIGEST, and the digest is not decoration: a tag can be made to point at a different +// image, and this file is applied on a machine with no mesh to ask about anything (ADR 0006). The +// registry prefix is the lab's; the digests are upstream's. +// +// The store's data is a NAMED VOLUME, not a directory on the machine. A directory the host +// creates is owned by root, and the database runs as somebody else inside the container — so it +// could not write, and the container crash-looped. A named volume lets the image set up its own +// ownership, and outlives the container, which is what you want for the thing holding the mesh's +// state. { "declaration": 1, diff --git a/testdata/beside/mesh-host/internal/apply/placement_guard.go.captured b/testdata/beside/mesh-host/internal/apply/placement_guard.go.captured index 01112d50..e23ef454 100644 --- a/testdata/beside/mesh-host/internal/apply/placement_guard.go.captured +++ b/testdata/beside/mesh-host/internal/apply/placement_guard.go.captured @@ -523,6 +523,20 @@ func refusedPlaces(resources []declaration.Resource) map[string]error { switch res := r.(type) { case *declaration.Directory: err = refusePath(res.Path, placingDirectory, moduleOfID(res.ID), res.Owner) + // And outside every home, the walk's judgement before anything is applied (novox/hq issue 358): a + // link another account could make in its path refuses it, and with it what is below it, whatever + // order they are declared in. The apply walks again at the moment of use. A directory handed over + // keeps its own, stricter walk (issue 356), which refuses any link at all. + if err == nil && meshDirsByDescriptor && homeAbove(res.Path) == "" && + !HandedOver()[handOverKey(res.ID, res.Path)] { + if judged := judgeMeshDir(res.Path); judged != nil { + err = judged + var link *LinkInPathError + if errors.As(judged, &link) { + err = &PlacementRefusedError{Path: res.Path, Why: "it " + link.why()} + } + } + } case *declaration.Access: err = refusePath(res.Path, placingAccess, moduleOfID(res.ID), "") default: