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)

This commit is contained in:
2026-11-03 16:41:06 +01:00
parent b6cc9572f2
commit f6e214533b
7 changed files with 54 additions and 20 deletions
@@ -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))
}
}
}
})
}
+4 -5
View File
@@ -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"
+1 -1
View File
@@ -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.
+20
View File
@@ -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)
}
}
}
+1 -1
View File
@@ -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;
+4 -11
View File
@@ -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
+3 -2
View File
@@ -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"}