From 189f28e4e134fe56cc06482e28e25ab366cac524 Mon Sep 17 00:00:00 2001 From: jochen Date: Tue, 3 Nov 2026 11:52:30 +0100 Subject: [PATCH] mesh-sdk's catalogue package names no module: the tool runner says it is one (tool-runner), the foundation is what is computed, the kept streams are the judge's, and the ended controller's dependence check goes with it (issue 774, the operator 2026-11-03) --- go/buscompose/health_grants.go | 5 +- go/catalogue/amqp_is_not_a_provision_test.go | 46 +++++-- go/catalogue/controller_dependence.go | 137 ------------------- go/catalogue/controller_dependence_test.go | 58 -------- go/catalogue/invokes_test.go | 21 +++ go/catalogue/manifest.go | 5 + go/catalogue/runtime.go | 16 ++- go/catalogue/seat_dependencies.go | 22 +-- go/catalogue/streams.go | 16 --- 9 files changed, 85 insertions(+), 241 deletions(-) delete mode 100644 go/catalogue/controller_dependence.go delete mode 100644 go/catalogue/controller_dependence_test.go diff --git a/go/buscompose/health_grants.go b/go/buscompose/health_grants.go index a06f64d..cf66b51 100644 --- a/go/buscompose/health_grants.go +++ b/go/buscompose/health_grants.go @@ -30,6 +30,9 @@ func ControllerLeftoverConsumersOn(nodes []string) []string { return out } +// endedController is the name the ended controller had as a module: its buckets are named for it (ADR 0590 §6). +const endedController = "mesh-controller" + // ControllerLeftoverBuckets are the ended controller's buckets, each by its local name (`KV_mesh-controller_`). var ControllerLeftoverBuckets = []string{"asked", "assignments", "builds", "calls", "checks-running", "composed", "condition-history", "conditions", "declared-config", "declared-packages", "delivery-merges", "grants", "hand-acts", @@ -40,7 +43,7 @@ var ControllerLeftoverBuckets = []string{"asked", "assignments", "builds", "call func ControllerLeftoverStreams(nodes []string) []string { out := []string{"CONTROL", AssignmentsStream} for _, b := range ControllerLeftoverBuckets { - out = append(out, "KV_"+BucketName(catalogue.EndedControllerName, b)) + out = append(out, "KV_"+BucketName(endedController, b)) } for _, n := range nodes { if safeSubject.MatchString(n) { diff --git a/go/catalogue/amqp_is_not_a_provision_test.go b/go/catalogue/amqp_is_not_a_provision_test.go index eb0744c..01aba6e 100644 --- a/go/catalogue/amqp_is_not_a_provision_test.go +++ b/go/catalogue/amqp_is_not_a_provision_test.go @@ -1,9 +1,12 @@ package catalogue import ( + "go/ast" + "go/parser" + "go/token" "os" "path/filepath" - "regexp" + "strconv" "strings" "testing" @@ -19,6 +22,7 @@ import ( // design 28 task 5.4, not converted. func TestNoCatalogueManifestNamesAmqp(t *testing.T) { modules, err := filepath.Glob(filepath.Join(beside.Catalogue(t), "*", "module.json")) + modules0 := modules if err != nil || len(modules) == 0 { t.Fatalf("no manifests in the catalogue, so this proved nothing: %v", err) } @@ -32,24 +36,48 @@ func TestNoCatalogueManifestNamesAmqp(t *testing.T) { filepath.Base(filepath.Dir(path))) } } - // Nor does this package name the modules that once had a rule of their own here (issue 774, the operator 2026-11-03): - // what each is, its manifest says. mesh-provisioner is also a seat's name, named where the seat is. + // Nor does this package's code name any module (issue 774, the operator 2026-11-03): what a module is, its manifest + // says. Every string literal of its non-test files is read; a module's name is refused there unless it is also a + // seat's or a provision's name, or one of the words below, which name something else. t.Run("the package names no module", func(t *testing.T) { - named := regexp.MustCompile(`"(nats|mesh-provisioner-client|mesh-provisioner)"`) + modules := map[string]bool{} + for _, path := range modules0 { + modules[filepath.Base(filepath.Dir(path))] = true + } + // The mesh's modules held outside the catalogue: the node-engine, the private network, the tool runner, the + // ended controller and the delivery's parts. + for _, m := range []string{"mesh-host", "mesh-wireguard", "node-tools", "mesh-controller", "mesh-delivery-assigner", + "mesh-delivery-builder", "mesh-delivery-checker", "mesh-delivery-forge-bridge", "mesh-delivery-indexer", + "mesh-delivery-teller", "mesh-delivery-view", "mesh-delivery-watchman", "mesh-enrolment", "mesh-grants"} { + modules[m] = true + } + // Words that are a module's name and name something else here: a language, an environment's rendering, a package. + elsewhere := map[string]bool{"go": true, "systemd": true, "nftables": true} + fset := token.NewFileSet() files, _ := filepath.Glob("*.go") for _, f := range files { if strings.HasSuffix(f, "_test.go") { continue } - raw, err := os.ReadFile(f) + parsed, err := parser.ParseFile(fset, f, nil, 0) if err != nil { t.Fatal(err) } - for i, line := range strings.Split(string(raw), "\n") { - if named.MatchString(line) && !strings.Contains(line, "{Name: "+named.FindString(line)+", Scope:") { - t.Errorf("%s:%d names a module: %s", f, i+1, strings.TrimSpace(line)) + ast.Inspect(parsed, func(n ast.Node) bool { + lit, ok := n.(*ast.BasicLit) + if !ok || lit.Kind != token.STRING { + return true } - } + word, err := strconv.Unquote(lit.Value) + if err != nil || !modules[word] || elsewhere[word] { + return true + } + if _, seat := SeatNamed(word); seat { + return true + } + t.Errorf("%s names the module %s", fset.Position(lit.Pos()), word) + return true + }) } }) } diff --git a/go/catalogue/controller_dependence.go b/go/catalogue/controller_dependence.go deleted file mode 100644 index 8c598b0..0000000 --- a/go/catalogue/controller_dependence.go +++ /dev/null @@ -1,137 +0,0 @@ -package catalogue - -import ( - "fmt" - "slices" - "sort" - "strings" -) - -// EndedControllerName is the name the ended controller had, as its module and as its seat: no manifest may name it. -const EndedControllerName = "mesh-controller" - -// The kinds of a declaration that names the controller. -const ( - DependsRead = "read" - DependsInvoke = "invoke" - DependsConsume = "consume" - DependsUses = "uses" - DependsWaits = "waits" - DependsClaims = "claims" - DependsRequires = "requires" - DependsIs = "is" -) - -// Dependency is one declaration of a manifest that names the controller. -type Dependency struct { - Module string `json:"module"` - // By is the account declaring it when it is not the module's own: `process ` or `tools `. - By string `json:"by,omitempty"` - Kind string `json:"kind"` - What string `json:"what"` -} - -// onController says whether a name `.` is the controller's. -func onController(name string) bool { return strings.HasPrefix(name, EndedControllerName+".") } - -// invokesController says whether an invoke names a verb of the controller's seat or a tool of its module. `*` names none: -// it is the console's grant to every tool. -func invokesController(invoke string) bool { - if seat, _, ok := SeatInvoke(invoke); ok { - return seat == EndedControllerName - } - return onController(invoke) -} - -// Dependencies are every declaration naming the controller in the manifests given, by module. -func Dependencies(manifests map[string]Manifest) []Dependency { - var out []Dependency - add := func(module, by, kind, what string) { - if d := (Dependency{Module: module, By: by, Kind: kind, What: what}); !slices.Contains(out, d) { - out = append(out, d) - } - } - for name, m := range manifests { - if name == EndedControllerName || m.Module == EndedControllerName { - add(name, "", DependsIs, EndedControllerName) - } - for _, r := range m.Reads { - if onController(r) { - add(name, "", DependsRead, r) - } - } - for _, i := range m.Invokes { - if invokesController(i) { - add(name, "", DependsInvoke, i) - } - } - for _, c := range m.Consumes { - if onController(c) { - add(name, "", DependsConsume, c) - } - } - for _, w := range m.Waits { - if onController(w) { - add(name, "", DependsWaits, w) - } - } - for _, u := range m.Uses { - if u == EndedControllerName { - add(name, "", DependsUses, u) - } - } - for _, c := range m.Claims { - if c.Name == EndedControllerName { - add(name, "", DependsClaims, c.Name) - } - } - for _, r := range m.Requires { - if r == EndedControllerName || onController(r) { - add(name, "", DependsRequires, r) - } - } - for _, a := range m.BusAccounts() { - by := "process " + a.ID - if a.Tools { - by = "tools " + a.ID - } - for _, i := range a.Bus.Invokes { - if invokesController(i) { - add(name, by, DependsInvoke, i) - } - } - } - } - sort.Slice(out, func(i, j int) bool { - a, b := out[i], out[j] - if a.Module != b.Module { - return a.Module < b.Module - } - if a.Kind != b.Kind { - return a.Kind < b.Kind - } - if a.What != b.What { - return a.What < b.What - } - return a.By < b.By - }) - return out -} - -// ManifestRefusals is the check over a repository's manifests, by module name: one reason per declaration naming the -// controller, none when it passes. None given fails it, since nothing read can hide a declaration. -func ManifestRefusals(manifests map[string]Manifest) []string { - if len(manifests) == 0 { - return []string{"no manifest was read: with nothing read, nothing naming the controller can be judged absent"} - } - var out []string - for _, d := range Dependencies(manifests) { - who := d.Module - if d.By != "" { - who += " (" + d.By + ")" - } - out = append(out, fmt.Sprintf("%s declares %s %s: the controller ended by its unassignment and nothing may name it "+ - "(ADR 0590 clause 5)", who, d.Kind, d.What)) - } - return out -} diff --git a/go/catalogue/controller_dependence_test.go b/go/catalogue/controller_dependence_test.go deleted file mode 100644 index e6d2ce3..0000000 --- a/go/catalogue/controller_dependence_test.go +++ /dev/null @@ -1,58 +0,0 @@ -package catalogue - -import ( - "slices" - "strings" - "testing" -) - -// Issue 1126 B1/AC1, B2/AC1 (ADR 0590 clause 5): the seat set holds no mesh-controller, and the dependence check refuses -// every manifest that reads (the lease too), invokes, consumes, waits on, uses, claims, requires or is mesh-controller. -func TestNothingMayNameTheController(t *testing.T) { - for _, s := range DefaultSeats() { - if s.Name == EndedControllerName { - t.Errorf("the seat set still holds %s", s.Name) - } - } - if _, ok := SeatNamed(EndedControllerName); ok { - t.Errorf("SeatNamed still finds %s", EndedControllerName) - } - for _, row := range []struct { - name, manifest string - want []string // " " - }{ - {"a read of the lease", `{"module":"mod-l","version":"1","reads":["mesh-controller.lease"]}`, []string{"read mesh-controller.lease"}}, - {"the controller's own manifest", `{"module":"mesh-controller","version":"1"}`, []string{"is mesh-controller"}}, - {"a claim of its seat", `{"module":"a-controller","version":"1","claims":[{"name":"mesh-controller","scope":"mesh"}]}`, - []string{"claims mesh-controller"}}, - {"each declaration kind; every tool (*) is none", - `{"module":"mod-i","version":"1","invokes":["seat:mesh-controller.root-free","mesh-controller.status","*","nats.nats_users"],` + - `"consumes":["mesh-controller.artifacts-gone","mod-x.thing"],"uses":["mesh-controller"],"waits":["mesh-controller.checked"],` + - `"requires":["mesh-controller"]}`, - []string{"consume mesh-controller.artifacts-gone", "invoke mesh-controller.status", "invoke seat:mesh-controller.root-free", - "requires mesh-controller", "uses mesh-controller", "waits mesh-controller.checked"}}, - {"a process's own invoke", `{"module":"mod-p","version":"1","resources":[{"id":"account","type":"user","name":"mod-p"},` + - `{"id":"worker","type":"process","run":["./w"],"user":"mod-p","bus":{"invokes":["seat:mesh-controller.held"]}}]}`, - []string{"invoke seat:mesh-controller.held"}}, - {"a module on its own seats", `{"module":"mod-ok","version":"1","reads":["mesh-grants.accounts"],"invokes":["*"]}`, nil}, - } { - m, err := Decode([]byte(row.manifest)) - if err != nil { - t.Fatalf("%s: %v", row.name, err) - } - var got []string - for _, d := range Dependencies(map[string]Manifest{m.Module: m}) { - got = append(got, d.Kind+" "+d.What) - } - if !slices.Equal(got, row.want) { - t.Errorf("%s: dependencies %q, want %q", row.name, got, row.want) - } - refused := ManifestRefusals(map[string]Manifest{m.Module: m}) - if len(refused) != len(row.want) || len(refused) > 0 && !strings.Contains(refused[0], "ADR 0590") { - t.Errorf("%s: refused %q", row.name, refused) - } - } - if got := ManifestRefusals(map[string]Manifest{}); len(got) != 1 || !strings.Contains(got[0], "no manifest") { - t.Errorf("no manifest given: %q", got) - } -} diff --git a/go/catalogue/invokes_test.go b/go/catalogue/invokes_test.go index 32e79d4..e7725b7 100644 --- a/go/catalogue/invokes_test.go +++ b/go/catalogue/invokes_test.go @@ -31,6 +31,27 @@ func TestAManifestMaySayWhatItInvokes(t *testing.T) { t.Errorf("%s: carries the core's credentials %v", raw, !core) } } + // And that it is the tool runner (issue 774): read, written back, and found among others by that alone. + runner, err := decodeForTest([]byte(`{"module":"any-name","version":"1","tool-runner":true}`)) + if err != nil || !IsToolRunner(runner) || runner.UnknownField() != "" { + t.Fatalf("a tool runner: %v %q", err, runner.UnknownField()) + } + if back, _ := json.Marshal(runner); !strings.Contains(string(back), `"tool-runner":true`) { + t.Errorf("a tool runner written back as %s", back) + } + other, _ := decodeForTest([]byte(`{"module":"node-tools","version":"1"}`)) + if got, ok := ToolRunnerOf([]Manifest{other, runner}); !ok || got.Module != "any-name" { + t.Errorf("the tool runner found is %q, %v: by what it says, never by its name", got.Module, ok) + } + // A module's seats are derived from what it declares, whatever its name: a service on a machine is the service + // manager's, the node-engine's name exempts nothing; a module whose resources are computed depends on none. + host, _ := decodeForTest([]byte(`{"module":"mesh-host","version":"1","resources":[{"id":"s","type":"service","name":"s.service"}]}`)) + if got := DependsOn(nil, host); len(got) != 1 || got[0] != ServiceManagerSeat { + t.Errorf("a service by the node-engine's name depends on %v", got) + } + if got := DependsOn(nil, Manifest{Module: "net", Computed: "net", Resources: host.Resources}); len(got) != 0 { + t.Errorf("a computed module depends on %v", got) + } // And its upgrade policy, which holds for the bus as for any module: providing the bus derives nothing (issue 774). for raw, want := range map[string]string{ `{"module":"bus","version":"1","provides":[{"name":"mesh-bus"}],"upgrade":{"policy":"record","why":"its streams"}}`: PolicyRecord + " " + FromModule, diff --git a/go/catalogue/manifest.go b/go/catalogue/manifest.go index e552842..485e190 100644 --- a/go/catalogue/manifest.go +++ b/go/catalogue/manifest.go @@ -801,6 +801,11 @@ type Manifest struct { // start, and is then replaced. OwnSecrets OwnSecrets `json:"own-secrets,omitempty"` + // ToolRunner says this module is the node's tool runner (issue 774): the one program on each node that loads every + // assigned module's tools bundle, served by its runtime process and the only module that may declare `waits`. Said by + // the module, never by a name in mesh-sdk. + ToolRunner bool `json:"tool-runner,omitempty"` + // CoreCredentials says this module's own secrets are the core's credentials (novox/hq ADR 0349 §16, issue 755): the // vault seals each to the machine, and the node-engine writes the vault's box into its file, never a value of its // own (Rendering.VaultSealed). Said by the module, never by a list in mesh-sdk (the operator, 2026-11-03: mesh-sdk names diff --git a/go/catalogue/runtime.go b/go/catalogue/runtime.go index 1d75f9a..65dd795 100644 --- a/go/catalogue/runtime.go +++ b/go/catalogue/runtime.go @@ -1,8 +1,18 @@ package catalogue -// RuntimeModule is the module that is the node's tool runtime. Mirrored in the broker package, -// which composes a principal of its own for it; the agreement test there holds the two to one string. -const RuntimeModule = "node-tools" +// IsToolRunner says whether a manifest is the node's tool runner's, as it says itself (`tool-runner`, issue 774): the +// one program on each node that loads every assigned module's tools bundle. Never known here by its name. +func IsToolRunner(m Manifest) bool { return m.ToolRunner } + +// ToolRunnerOf is the module among manifests that says it is the tool runner, and whether one does. +func ToolRunnerOf(manifests []Manifest) (Manifest, bool) { + for _, m := range manifests { + if IsToolRunner(m) { + return m, true + } + } + return Manifest{}, false +} // BundleRoot is where a machine keeps the tools bundles the mesh delivers to it: under the mesh's // own directory, beside the daemons the host unpacks there, and never where a package manager also diff --git a/go/catalogue/seat_dependencies.go b/go/catalogue/seat_dependencies.go index e136a71..3e3d53a 100644 --- a/go/catalogue/seat_dependencies.go +++ b/go/catalogue/seat_dependencies.go @@ -24,24 +24,12 @@ var appliedThrough = map[string]string{ "container": ContainerRuntimeSeat, } -// foundationModules are the pieces genesis lays before any module exists (novox/hq ADR 0207 §5): -// the host and the private network. Registered as modules so they can be assigned, but what they -// declare is the installation's, not a module's, so it is never judged. The third piece, the -// bootstrap container runtime, is not a module at all: its package and service are in the genesis -// bundle the host applies itself, and never pass through a resolution here. -// -// The private network's module is overlay.Name, written out because the overlay package composes -// on top of this one; its resources are computed, which exempts it by the rule below as well. -var foundationModules = map[string]bool{ - "mesh-host": true, - "mesh-wireguard": true, -} - -// isFoundation is whether a module's declarations are the foundation's rather than its own. A -// module whose resources are computed is the mesh's by construction — the private network's peer -// list and its tools are the controller's, written per node — so it counts whatever its name. +// isFoundation is whether a module's declarations are the foundation's rather than its own: a module whose resources are +// computed (`computed`, the private network's peer list written per node) is the mesh's by construction. Read from the +// manifest alone, never from its name (issue 774, the operator 2026-11-03): the node-engine's own manifest declares no +// resource a seat applies, so it depends on none by the rules below. func isFoundation(m Manifest) bool { - return foundationModules[m.Module] || m.Computed != "" + return m.Computed != "" } // DependsOn is every seat a module needs held on its node: derived from the resource types it diff --git a/go/catalogue/streams.go b/go/catalogue/streams.go index f47111f..d33a2df 100644 --- a/go/catalogue/streams.go +++ b/go/catalogue/streams.go @@ -370,19 +370,3 @@ func uncovered(patterns []string) []string { slices.Sort(out) return out } - -// **A module keeps its data in the bus's predefined forms** (the lead and the operator, 2026-10-29; the operator's rule of -// 2026-10-22, "data storage is done using our predefined nats bus options"): a state, a log or an object store, which the -// runtime reads and writes for its tools and other modules read by a declared `reads`. A stream of its own is outside -// them and refused, naming it. StreamsKeptUntilMoved are the streams declared before this check, each allowed by name -// until its module moves it; the list only shrinks. -var StreamsKeptUntilMoved = map[string][]string{ - "brother": {"device-asks"}, - "mesh-build-go": {"toolchains-go"}, - "mesh-build-node": {"toolchains-node"}, - "mesh-mail": {"mail", "mail-sends"}, - "only-office": {"convert-asks"}, - "mesh-delivery-assigner": {"placement"}, - "mesh-delivery-checker": {"checks"}, - "mesh-delivery-forge-bridge": {"forge"}, -}