From f6e214533be3d57e7518f3d27605c501924b3ad9 Mon Sep 17 00:00:00 2001 From: jochen Date: Tue, 3 Nov 2026 11:36:43 +0100 Subject: [PATCH] mesh-sdk names no module: the core's credentials are what a manifest says (core-credentials), and the bus's upgrade policy is the one its manifest declares (issue 774, the operator 2026-11-03) --- go/catalogue/amqp_is_not_a_provision_test.go | 21 ++++++++++++++++++++ go/catalogue/core_credentials.go | 9 ++++----- go/catalogue/declaration.go | 2 +- go/catalogue/invokes_test.go | 20 +++++++++++++++++++ go/catalogue/manifest.go | 2 +- go/catalogue/upgrade.go | 15 ++++---------- go/delivery/composed.go | 5 +++-- 7 files changed, 54 insertions(+), 20 deletions(-) diff --git a/go/catalogue/amqp_is_not_a_provision_test.go b/go/catalogue/amqp_is_not_a_provision_test.go index adbeb2d..eb0744c 100644 --- a/go/catalogue/amqp_is_not_a_provision_test.go +++ b/go/catalogue/amqp_is_not_a_provision_test.go @@ -3,6 +3,7 @@ package catalogue import ( "os" "path/filepath" + "regexp" "strings" "testing" @@ -31,4 +32,24 @@ 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. + t.Run("the package names no module", func(t *testing.T) { + named := regexp.MustCompile(`"(nats|mesh-provisioner-client|mesh-provisioner)"`) + files, _ := filepath.Glob("*.go") + for _, f := range files { + if strings.HasSuffix(f, "_test.go") { + continue + } + raw, err := os.ReadFile(f) + 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)) + } + } + } + }) } diff --git a/go/catalogue/core_credentials.go b/go/catalogue/core_credentials.go index bf8a374..e28cc0c 100644 --- a/go/catalogue/core_credentials.go +++ b/go/catalogue/core_credentials.go @@ -4,11 +4,10 @@ import ( "encoding/json" ) -// CoreCredentialModules are the modules whose own secrets are the core's credentials: the bus, mesh-provisioner and -// its client. The node-engine and the controller hold no own secret of a module's. **Fixed here, in the controller's -// code, as the clause says** — and the vault keeps the same list (mesh-vault's CoreModules), answering the controller -// for these alone. -var CoreCredentialModules = []string{"mesh-provisioner", "mesh-provisioner-client", "nats"} +// CarriesCoreCredentials says whether a module's own secrets are the core's credentials, as its manifest says +// (Manifest.CoreCredentials): the vault seals them to the machine, and the node-engine and the vault hold no list of +// such modules. +func CarriesCoreCredentials(m Manifest) bool { return m.CoreCredentials } // VaultSealedField is the field of a file the vault sealed to its machine (mesh-host's File.VaultSealed). const VaultSealedField = "vault-sealed" diff --git a/go/catalogue/declaration.go b/go/catalogue/declaration.go index 49e7254..0da1418 100644 --- a/go/catalogue/declaration.go +++ b/go/catalogue/declaration.go @@ -84,7 +84,7 @@ type Rendering struct { // makes. Nil on every composition that is sent, and on every other question, which then refuse // a secret not made with a *NotMadeError. Foreseen map[string]map[string]bool - // VaultSealed is, for a module on the core's list (CoreCredentialModules), each own secret as the vault sealed it + // VaultSealed is, for a module whose manifest says its own secrets are the core's credentials (core-credentials), each own secret as the vault sealed it // to this machine (the mesh's sealed box, as the vault answered it), keyed like Needed (novox/hq ADR 0349 §16, // issue 755). Given only for a machine whose engine opens the box (VaultBoxContract): its file then carries the // box and never the controller's own sealed value. Nil everywhere else, and the composition is as it was. diff --git a/go/catalogue/invokes_test.go b/go/catalogue/invokes_test.go index 3fcb3bd..32e79d4 100644 --- a/go/catalogue/invokes_test.go +++ b/go/catalogue/invokes_test.go @@ -27,5 +27,25 @@ func TestAManifestMaySayWhatItInvokes(t *testing.T) { if strings.Contains(string(back), "core-credentials") != core { t.Errorf("%s written back as %s", raw, back) } + if CarriesCoreCredentials(m) != core { + t.Errorf("%s: carries the core's credentials %v", raw, !core) + } + } + // 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, + `{"module":"bus","version":"1","provides":[{"name":"mesh-bus"}]}`: PolicyRoll + " " + FromDefault, + } { + m, err := decodeForTest([]byte(raw)) + if err != nil { + t.Fatal(err) + } + if policy, from, _ := DerivedUpgrade(m); policy+" "+from != want { + t.Errorf("%s: derived %s %s, want %s", raw, policy, from, want) + } + chosen := &PersonPolicy{Policy: PolicyTogether, Why: "w", By: "a person"} + if policy, from, _, _ := Upgrade(m, chosen); policy != PolicyTogether || from != FromPerson { + t.Errorf("%s: a person's choice gave %s %s", raw, policy, from) + } } } diff --git a/go/catalogue/manifest.go b/go/catalogue/manifest.go index adaea5a..e552842 100644 --- a/go/catalogue/manifest.go +++ b/go/catalogue/manifest.go @@ -640,7 +640,7 @@ type Manifest struct { // Upgrade is how this module's new builds reach its machines (novox/hq ADR 0236): rolled out one // machine first and gated when unsaid; `together`, or `record` — wait for a person's push — with - // why. A person's choice through the `upgrade` verb stands over it; the bus records whatever it says. + // why. A person's choice through the `upgrade` verb stands over it. The bus says `record` itself. Upgrade *UpgradePolicy `json:"upgrade,omitempty"` // Unhealthy is how loud this module's unhealthy condition is (novox/hq ADR 0355): a warning when unsaid; diff --git a/go/catalogue/upgrade.go b/go/catalogue/upgrade.go index a89e994..07feb24 100644 --- a/go/catalogue/upgrade.go +++ b/go/catalogue/upgrade.go @@ -30,25 +30,19 @@ type UpgradePolicy struct { const ( FromPerson = "person" FromModule = "module" - FromBus = "the bus" FromData = "irreplaceable data" FromDefault = "default" ) // DerivedUpgrade is the policy a module's manifest gives it when no person has chosen one, with where -// it came from and why (ADR 0236): +// it came from and why (ADR 0236), from what the manifest says alone (the operator, 2026-11-03: no module is +// treated apart here; the bus says `record` in its own manifest): // -// - **the bus is never rolled**: a module that provides the mesh's bus records, whatever it says — its -// upgrade is a planned step a person starts (to-be 45 §8); // - a module that says its policy has it; // - a module that keeps irreplaceable data records — sending the previous build cannot undo what a new // one did to data that cannot be had again, so a person takes it, after a backup; // - everything else rolls out, one machine first. func DerivedUpgrade(m Manifest) (policy, from, why string) { - if ProvidesBus(m) { - return PolicyRecord, FromBus, "the bus is replaced only as a planned step a person starts (`bus upgrade`): " + - "its streams are snapshotted first and checked after" - } if m.Upgrade != nil && m.Upgrade.Policy != "" { return m.Upgrade.Policy, FromModule, m.Upgrade.Why } @@ -126,11 +120,10 @@ func (p PersonPolicy) Check() error { // Upgrade is a module's policy and where it comes from (ADR 0565 clause 7): the one derivation every reader takes, // `mesh-wants.upgrade` and each node agent's judgement of a move alike. A person's choice over the module's word, its -// irreplaceable data and the default; the bus is never rolled, whoever says so (ADR 0236). By names the person when one -// chose. +// irreplaceable data and the default. By names the person when one chose. func Upgrade(m Manifest, chosen *PersonPolicy) (policy, from, why, by string) { policy, from, why = DerivedUpgrade(m) - if from == FromBus || chosen == nil { + if chosen == nil { return policy, from, why, "" } return chosen.Policy, FromPerson, chosen.Why, chosen.By diff --git a/go/delivery/composed.go b/go/delivery/composed.go index 5fdb5f5..20c04ac 100644 --- a/go/delivery/composed.go +++ b/go/delivery/composed.go @@ -164,8 +164,9 @@ type ComposedDigest struct { } // The upgrade policies and their sources a composition's value names: catalogue.PolicyRoll, PolicyTogether and -// PolicyRecord, and catalogue.FromPerson, FromModule, FromBus, FromData and FromDefault, the same words (the compose -// package's test holds them equal; this package does not import the catalogue). +// PolicyRecord, and catalogue.FromPerson, FromModule, FromData and FromDefault, the same words (the compose package's +// test holds them equal; this package does not import the catalogue). "the bus" is the word an older reader derived +// for the bus module, still read from a composition it wrote; nothing derives it now. var ( composedPolicies = []string{"roll", "together", "record"} composedPolicyFroms = []string{"person", "module", "the bus", "irreplaceable data", "default"}