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)

This commit is contained in:
2026-11-03 16:41:06 +01:00
parent f6e214533b
commit 189f28e4e1
9 changed files with 85 additions and 241 deletions
+4 -1
View File
@@ -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_<name>`).
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) {
+37 -9
View File
@@ -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
})
}
})
}
-137
View File
@@ -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 <id>` or `tools <binary>`.
By string `json:"by,omitempty"`
Kind string `json:"kind"`
What string `json:"what"`
}
// onController says whether a name `<owner>.<rest>` 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
}
@@ -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 // "<kind> <what>"
}{
{"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)
}
}
+21
View File
@@ -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,
+5
View File
@@ -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
+13 -3
View File
@@ -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
+5 -17
View File
@@ -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
-16
View File
@@ -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"},
}