diff --git a/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/holder_test.go b/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/holder_test.go index 49e8c2a0..0c5d84dd 100644 --- a/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/holder_test.go +++ b/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/holder_test.go @@ -5,6 +5,8 @@ import ( "strings" "testing" "time" + + sdk "git.novox.be/novox/mesh-sdk/go/delivery" ) // The holder, end to end over fakes (novox/hq ADR 0239 *How it is checked*, cut by ADR 0539 §17): a pull request's head @@ -238,6 +240,17 @@ func TestAMergedGroupEndsMergedWithSourceMainMoved(t *testing.T) { if w.source.main == nil || w.source.main.Heads["novox/a"] != strings.Repeat("p", 12) || w.source.main.Heads["novox/b"] != strings.Repeat("q", 12) { t.Fatalf("source.main is %+v, wanted both merges", w.source.main) } + // A merge that leaves no module.json carries no trees (issue 1367): the gate reads it, and refuses nothing. + msg := sdk.Forge{Kind: sdk.ForgeMerged, Repository: "novox/a", Number: 9, Head: strings.Repeat("e", 40), + Merge: strings.Repeat("f", 40), Root: "r"} + body, err := sdk.EncodeForge(msg) + if err != nil { + t.Fatalf("a merged message with no trees is refused: %v", err) + } + w.h.Forged("mesh-delivery-forge-bridge.novox.a.merged", body) + if len(w.h.refusals) != 0 { + t.Errorf("the gate refused a merged message with no trees: %v", w.h.refusals) + } } // ADR 0419 §2, issue 978: a description's `after:` line is not read, and says so once on its pull request. The diff --git a/modules/mesh-delivery-gate/go.mod b/modules/mesh-delivery-gate/go.mod index c35b2a88..c0544f39 100644 --- a/modules/mesh-delivery-gate/go.mod +++ b/modules/mesh-delivery-gate/go.mod @@ -3,7 +3,7 @@ module mesh-delivery-gate go 1.26.0 require ( - git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128 + git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261103174208-b7abe203a929 github.com/nats-io/nats-server/v2 v2.11.17 github.com/nats-io/nats.go v1.54.0 ) diff --git a/modules/mesh-delivery-gate/go.sum b/modules/mesh-delivery-gate/go.sum index 43cc4ec7..abad2969 100644 --- a/modules/mesh-delivery-gate/go.sum +++ b/modules/mesh-delivery-gate/go.sum @@ -1,5 +1,5 @@ -git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128 h1:aI1DhZAuqVBXbybSqhLNOGX5rUOrn9T0cHNCDUQF7IQ= -git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY= +git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261103174208-b7abe203a929 h1:RtAFbUJRP1qO9AIMM2ue+cXNzqj7WZ7IOlSqNvttwuU= +git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261103174208-b7abe203a929/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY= github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op h1:Z/MZK75wC/NSrkgqeNIa7jexam9uWzhLmFTSCPI/kn0= github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op/go.mod h1:FQyySiasQQM8735Ddel3MRojmy4dA1IqCeyJ5jmPMbI= github.com/google/go-tpm v0.9.8 h1:slArAR9Ft+1ybZu0lBwpSmpwhRXaa85hWtMinMyRAWo= diff --git a/modules/mesh-provisioner-client/go.mod b/modules/mesh-provisioner-client/go.mod index 2d0b5146..ea5c9d1f 100644 --- a/modules/mesh-provisioner-client/go.mod +++ b/modules/mesh-provisioner-client/go.mod @@ -3,7 +3,7 @@ module mesh-provisioner-client go 1.26.0 require ( - git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128 + git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261103174208-b7abe203a929 github.com/nats-io/nats-server/v2 v2.11.17 github.com/nats-io/nats.go v1.54.0 golang.org/x/sys v0.48.0 diff --git a/modules/mesh-provisioner-client/go.sum b/modules/mesh-provisioner-client/go.sum index 43cc4ec7..abad2969 100644 --- a/modules/mesh-provisioner-client/go.sum +++ b/modules/mesh-provisioner-client/go.sum @@ -1,5 +1,5 @@ -git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128 h1:aI1DhZAuqVBXbybSqhLNOGX5rUOrn9T0cHNCDUQF7IQ= -git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY= +git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261103174208-b7abe203a929 h1:RtAFbUJRP1qO9AIMM2ue+cXNzqj7WZ7IOlSqNvttwuU= +git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261103174208-b7abe203a929/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY= github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op h1:Z/MZK75wC/NSrkgqeNIa7jexam9uWzhLmFTSCPI/kn0= github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op/go.mod h1:FQyySiasQQM8735Ddel3MRojmy4dA1IqCeyJ5jmPMbI= github.com/google/go-tpm v0.9.8 h1:slArAR9Ft+1ybZu0lBwpSmpwhRXaa85hWtMinMyRAWo= diff --git a/modules/mesh-provisioner-client/module.json b/modules/mesh-provisioner-client/module.json index 095128a8..8fca6444 100644 --- a/modules/mesh-provisioner-client/module.json +++ b/modules/mesh-provisioner-client/module.json @@ -7,6 +7,7 @@ "scope": "node" } ], + "core-credentials": true, "own-secrets": { "provisioner-broker": "${dir:state}/broker" }, diff --git a/modules/mesh-provisioner/cmd/mesh-provisioner/holder.go b/modules/mesh-provisioner/cmd/mesh-provisioner/holder.go index 4a63e956..5d8fa5dc 100644 --- a/modules/mesh-provisioner/cmd/mesh-provisioner/holder.go +++ b/modules/mesh-provisioner/cmd/mesh-provisioner/holder.go @@ -68,7 +68,10 @@ const ( ModulesRead = delivery.IndexerModule + ".modules" ManifestsRead = delivery.IndexerModule + ".manifests" HaveRead = "node-engine.have" - ComposedRead = "mesh-host.composed" + // BuildsRead is the builder's builds, `.`: the key of the build a provider runs names the + // digest of the manifest it was built from (novox/hq ADR 0603 clause 5). + BuildsRead = delivery.BuilderModule + ".builds" + ComposedRead = "mesh-host.composed" ) // BoundReads are the providers' entries this module's manifest reads, `.bound`, whose changes it hears. Which @@ -819,6 +822,14 @@ func (h *Holder) haveOutcome(p *provisions.Provision) { case have.Phase == delivery.PhaseFailed || have.Health == delivery.HealthFailed: p.State, p.Why = provisions.Failed, orSaid(have.Detail, where+" failed") case have.Phase == delivery.PhaseRunning && have.Health == delivery.HealthOK && (wantFP == "" || have.FP == wantFP): + // A provider's have counts as its outcome only from a build whose manifest offers the provision (ADR 0603 clause 5). + // Refused only when that manifest is known and does not offer it: a have with no builder key (an old-form build, an + // adopted module) or an unreadable manifest keeps the outcome it had. + if offers, known := h.buildOffers(p.Provider.Module, have.FP, p.Provision); known && !offers { + p.State, p.Why = provisions.Asked, "waits for "+where+" to run a build that offers "+p.Provision+": the build it runs ("+ + have.FP+") does not" + return + } p.State, p.Why = provisions.Provided, "" default: p.State, p.Why = provisions.Asked, fmt.Sprintf("waits for %s to run healthy at the build its want names (it is %s, "+ @@ -826,6 +837,56 @@ func (h *Holder) haveOutcome(p *provisions.Provision) { } } +// buildOffers says whether the build of a module at a fingerprint, as the builder keyed it, was made from a manifest that +// offers the provision; known is false when the builder holds no such key, the key names no manifest, or the indexer holds +// no such manifest (the caller then does not refuse). +func (h *Holder) buildOffers(module, fingerprint, provision string) (offers, known bool) { + key, err := delivery.BuilderBuildKey(module, fingerprint) + if err != nil { + return false, false + } + be, err := h.Store.Get(BuildsRead, key) + if err != nil || be == nil { + return false, false + } + b, err := delivery.DecodeBuilderBuild(be.Value) + if err != nil || b.Manifest == "" { + return false, false + } + mk, err := delivery.ManifestsKey(b.Manifest) + if err != nil { + return false, false + } + me, err := h.Store.Get(ManifestsRead, mk) + if err != nil || me == nil { + return false, false + } + man, err := delivery.DecodeManifest(me.Value) + if err != nil { + return false, false + } + var decl struct { + Provides []json.RawMessage `json:"provides"` + } + if json.Unmarshal([]byte(man.JSON), &decl) != nil { + return false, false + } + for _, raw := range decl.Provides { + var name string + if json.Unmarshal(raw, &name) != nil { + var full struct { + Name string `json:"name"` + } + _ = json.Unmarshal(raw, &full) + name = full.Name + } + if name == provision { + return true, true + } + } + return false, true +} + // keepsEntries says whether a provider's outcome for one provision is its entries (its manifest at the trunk declares the // state `bound`) rather than its own have. When any offer of the provider says `answered-by: helper`, only those offers // are answered by its entries: a module running the helper for one provision and making another some other way (gitea: diff --git a/modules/mesh-provisioner/cmd/mesh-provisioner/holder_integration_test.go b/modules/mesh-provisioner/cmd/mesh-provisioner/holder_integration_test.go index 6ab8f93e..91e53cc2 100644 --- a/modules/mesh-provisioner/cmd/mesh-provisioner/holder_integration_test.go +++ b/modules/mesh-provisioner/cmd/mesh-provisioner/holder_integration_test.go @@ -224,6 +224,34 @@ func TestTheHolderPlansProvidesAndAsksOnTheBus(t *testing.T) { t.Fatal(err) } settled(HaveRead, node+".postgres") + // The running build's manifest, as the builder keyed it: one that does not offer the provision keeps the key asked + // (ADR 0603 clause 5), one that does lets the have count. + buildsKey := mustKey(delivery.BuilderBuildKey("postgres", fpsOf["postgres"])) + offered := func(provides string) { + raw := `{"module":"postgres","version":"1","provides":` + provides + `}` + digest := delivery.ManifestDigest([]byte(raw)) + value, _ := delivery.EncodeManifest(delivery.Manifest{Module: "postgres", JSON: raw}) + if err := s.Put(ManifestsRead, mustKey(delivery.ManifestsKey(digest)), json.RawMessage(value)); err != nil { + t.Fatal(err) + } + build, err := delivery.EncodeBuilderBuild(delivery.BuilderBuild{State: delivery.BuildDone, Manifest: digest}) + if err != nil { + t.Fatal(err) + } + if err := s.Put(BuildsRead, buildsKey, json.RawMessage(build)); err != nil { + t.Fatal(err) + } + } + offered(`["other"]`) + for i := 0; i < 2; i++ { + if _, err := h.ComposeNode(ctx, node); err != nil { + t.Fatal(err) + } + } + if p := state(key); p.State != provisions.Asked { + t.Fatalf("a build not offering the provision turned it %s", p.State) + } + offered(`["postgres-database"]`) for i := 0; i < 2; i++ { // the credential judged first, then what depends on it if _, err := h.ComposeNode(ctx, node); err != nil { t.Fatal(err) diff --git a/modules/mesh-provisioner/cmd/mesh-provisioner/holder_test.go b/modules/mesh-provisioner/cmd/mesh-provisioner/holder_test.go index 63504236..f99f945a 100644 --- a/modules/mesh-provisioner/cmd/mesh-provisioner/holder_test.go +++ b/modules/mesh-provisioner/cmd/mesh-provisioner/holder_test.go @@ -189,6 +189,28 @@ func TestAKeysStateFollowsItsProvidersOutcome(t *testing.T) { t.Errorf("the provider's have %s: %+v, want %s", row.have, p, row.state) } } + // A have counts as the provider's outcome only from a build whose manifest offers the provision (ADR 0603 clause 5): + // refused when the build's manifest is known and does not offer it, provided when it does. + bfp := "src2:" + strings.Repeat("bb", 32) + buildKey := mustKey(delivery.BuilderBuildKey("systemd-resolved", bfp)) + for _, row := range []struct{ name, provides, state, why string }{ + {"a build whose manifest does not offer it", `["other"]`, provisions.Asked, "offers split-dns"}, + {"a build whose manifest offers it", `[{"name":"split-dns"}]`, provisions.Provided, ""}, + } { + raw := `{"module":"systemd-resolved","version":"1","provides":` + row.provides + `}` + digest := delivery.ManifestDigest([]byte(raw)) + value, _ := delivery.EncodeManifest(delivery.Manifest{Module: "systemd-resolved", JSON: raw}) + _ = m.s.Put(ManifestsRead, mustKey(delivery.ManifestsKey(digest)), json.RawMessage(value)) + build, err := delivery.EncodeBuilderBuild(delivery.BuilderBuild{State: delivery.BuildDone, Manifest: digest}) + if err != nil { + t.Fatal(err) + } + _ = m.s.Put(BuildsRead, buildKey, json.RawMessage(build)) + m.compose(t, "g14") + if p := m.state(t, split); p.State != row.state || !strings.Contains(p.Why, row.why) { + t.Errorf("%s: %+v, want %s", row.name, p, row.state) + } + } if e, _ := m.s.Get(PlansState, "g14.gitea"); e == nil { t.Error("the plan is not written") } diff --git a/modules/mesh-provisioner/cmd/mesh-provisioner/provisioner_test.go b/modules/mesh-provisioner/cmd/mesh-provisioner/provisioner_test.go index 7ef2233a..c856a63b 100644 --- a/modules/mesh-provisioner/cmd/mesh-provisioner/provisioner_test.go +++ b/modules/mesh-provisioner/cmd/mesh-provisioner/provisioner_test.go @@ -8,8 +8,6 @@ import ( "strings" "sync" "testing" - - "git.novox.be/novox/mesh-sdk/go/delivery" ) // memStore is the bus as mesh-provisioner reaches it, in memory: the states it keeps. @@ -326,13 +324,15 @@ func TestTheManifestDeclaresWhatTheCodeKeeps(t *testing.T) { t.Errorf("the manifest lists %s, which is not served", tool) } } - for _, r := range append(slices.Clone(WatchedReads), ManifestsRead) { + // The builds read serves only haveOutcome's offer check (ADR 0603 clause 5: which manifest the build a provider runs + // was made from) and makes no plan, so ADR 0593 §8 ("it composes nothing") holds. + for _, r := range append(slices.Clone(WatchedReads), ManifestsRead, BuildsRead) { if !slices.Contains(m.Reads, r) { t.Errorf("the manifest does not read %s, which the holder reads", r) } } - // It composes nothing (ADR 0593 §8): no build, and no config value, is read to make a plan. - for _, r := range []string{delivery.BuilderModule + ".builds", "mesh-config.resolved"} { + // It composes nothing (ADR 0593 §8): no config value is read to make a plan. + for _, r := range []string{"mesh-config.resolved"} { if slices.Contains(m.Reads, r) { t.Errorf("the manifest reads %s, which only a composer reads", r) } diff --git a/modules/mesh-provisioner/go.mod b/modules/mesh-provisioner/go.mod index f9cfe363..03d2bf48 100644 --- a/modules/mesh-provisioner/go.mod +++ b/modules/mesh-provisioner/go.mod @@ -3,7 +3,7 @@ module mesh-provisioner go 1.26.0 require ( - git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128 + git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261103174208-b7abe203a929 github.com/nats-io/nats.go v1.54.0 ) diff --git a/modules/mesh-provisioner/go.sum b/modules/mesh-provisioner/go.sum index 43cc4ec7..abad2969 100644 --- a/modules/mesh-provisioner/go.sum +++ b/modules/mesh-provisioner/go.sum @@ -1,5 +1,5 @@ -git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128 h1:aI1DhZAuqVBXbybSqhLNOGX5rUOrn9T0cHNCDUQF7IQ= -git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY= +git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261103174208-b7abe203a929 h1:RtAFbUJRP1qO9AIMM2ue+cXNzqj7WZ7IOlSqNvttwuU= +git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261103174208-b7abe203a929/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY= github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op h1:Z/MZK75wC/NSrkgqeNIa7jexam9uWzhLmFTSCPI/kn0= github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op/go.mod h1:FQyySiasQQM8735Ddel3MRojmy4dA1IqCeyJ5jmPMbI= github.com/google/go-tpm v0.9.8 h1:slArAR9Ft+1ybZu0lBwpSmpwhRXaa85hWtMinMyRAWo= diff --git a/modules/mesh-provisioner/module.json b/modules/mesh-provisioner/module.json index e14cb7e3..7b512a6f 100644 --- a/modules/mesh-provisioner/module.json +++ b/modules/mesh-provisioner/module.json @@ -20,6 +20,7 @@ "mesh-delivery-indexer.modules", "mesh-delivery-indexer.manifests", "node-engine.have", + "mesh-delivery-builder.builds", "mesh-host.composed", "gitea.bound", "keycloak.bound", @@ -45,6 +46,7 @@ "plans", "refused" ], + "core-credentials": true, "own-secrets": { "broker": "${dir:state}/broker" }, diff --git a/modules/mesh-vault/cmd/mesh-vault/core.go b/modules/mesh-vault/cmd/mesh-vault/core.go index b0264c61..38fa165a 100644 --- a/modules/mesh-vault/cmd/mesh-vault/core.go +++ b/modules/mesh-vault/cmd/mesh-vault/core.go @@ -24,10 +24,6 @@ import ( "time" ) -// CoreModules are the core's modules whose own secrets the controller carries sealed by the vault: the same fixed -// list as the controller's (ADR 0349 §16). The node-engine and the controller have no own secrets of their own here. -var CoreModules = []string{"mesh-provisioner", "mesh-provisioner-client", "nats"} - // nodeKeyFile is a machine's sealing key as its engine stated it. type nodeKeyFile struct { Node string `json:"node"` diff --git a/modules/mesh-vault/cmd/mesh-vault/core_test.go b/modules/mesh-vault/cmd/mesh-vault/core_test.go index 5ea24183..e73ce5f7 100644 --- a/modules/mesh-vault/cmd/mesh-vault/core_test.go +++ b/modules/mesh-vault/cmd/mesh-vault/core_test.go @@ -14,7 +14,6 @@ import ( "encoding/pem" "os" "path/filepath" - "reflect" "strings" "sync" "testing" @@ -56,13 +55,6 @@ func clientsBroker(sealingKey string) MoveOf { Generation: "g-1", SealingKey: sealingKey} } -// The list is the controller's (mesh-controller's core list, ADR 0349 §16): named here so a change to either is seen. -func TestTheCoreIsTheBusTheProvisionerAndItsClient(t *testing.T) { - if want := []string{"mesh-provisioner", "mesh-provisioner-client", "nats"}; !reflect.DeepEqual(CoreModules, want) { - t.Fatalf("the core's modules are %v, want %v", CoreModules, want) - } -} - // A node-engine states its machine's sealing key in its own name at the move, and only its own machine's. func TestAMoveRecordsTheSealingKeyItsNodeEngineStates(t *testing.T) { v := newVault(t, nil) diff --git a/modules/mesh-vault/cmd/mesh-vault/move.go b/modules/mesh-vault/cmd/mesh-vault/move.go index 3950af49..269cf31f 100644 --- a/modules/mesh-vault/cmd/mesh-vault/move.go +++ b/modules/mesh-vault/cmd/mesh-vault/move.go @@ -292,7 +292,10 @@ func (v *Vault) StateNodeKey(caller, key string) error { } for _, r := range all { parts := strings.Split(r.Name, "/") - if len(parts) == 4 && parts[0] == "own" && parts[1] == node && slices.Contains(CoreModules, parts[2]) { + // Every own secret of the machine is said changed: which modules carry theirs sealed by the vault is what their + // manifests say (core-credentials), read by the node-engine, which composes again and reseals those alone; the vault + // keeps no list of modules (issue 774, the operator 2026-11-03). + if len(parts) == 4 && parts[0] == "own" && parts[1] == node { v.announce(Changed{Name: r.Name, Generation: r.Generation, At: v.Now().UTC(), By: caller}) } } diff --git a/modules/mesh-vault/cmd/mesh-vault/nodekey_test.go b/modules/mesh-vault/cmd/mesh-vault/nodekey_test.go index cc721a48..cb24e41e 100644 --- a/modules/mesh-vault/cmd/mesh-vault/nodekey_test.go +++ b/modules/mesh-vault/cmd/mesh-vault/nodekey_test.go @@ -2,7 +2,7 @@ package main // Issue 786 (ADR 0372): a machine's node-engine states its own sealing key to the vault, in its own name, at start and // when it changes, whether or not it ever moved a secret in. The caller is the bus user the runtime proved; a changed -// key (or a first one) raises `secret-changed` for the machine's core secrets, the same key says nothing. +// key (or a first one) raises `secret-changed` for the machine's own secrets, the same key says nothing. import ( "git.novox.be/novox/mesh-sdk/go/seal" @@ -76,7 +76,8 @@ func TestANodeEngineStatesItsOwnKeyAndAChangeRaisesItsCoreSecrets(t *testing.T) t.Error("a key was recorded for g14 by ace's engine") } got := changedNames(v.bus)[before:] - want := []string{"own/ace/mesh-provisioner-client/provisioner-broker", "own/ace/nats/broker"} + // Every own secret of the machine: which carry the vault's box their manifests say, read by the node-engine (issue 774). + want := []string{"own/ace/mesh-provisioner-client/provisioner-broker", "own/ace/nats/broker", "own/ace/postgres/superuser-password"} if strings.Join(got, ",") != strings.Join(want, ",") { t.Errorf("secret-changed was raised for %v, want %v", got, want) } @@ -85,16 +86,16 @@ func TestANodeEngineStatesItsOwnKeyAndAChangeRaisesItsCoreSecrets(t *testing.T) if _, err := tool.runAs("node.ace", map[string]any{"key": pub}); err != nil { t.Fatal(err) } - if n := len(changedNames(v.bus)); n != before+2 { - t.Errorf("the same key raised %d more", n-before-2) + if n := len(changedNames(v.bus)); n != before+3 { + t.Errorf("the same key raised %d more", n-before-3) } // A different key raises them again. other, _ := aSealingKey(t) if _, err := tool.runAs("node.ace", map[string]any{"key": other}); err != nil { t.Fatal(err) } - if n := len(changedNames(v.bus)); n != before+4 { - t.Errorf("a changed key raised %d, want 2", n-before-2) + if n := len(changedNames(v.bus)); n != before+6 { + t.Errorf("a changed key raised %d, want 3", n-before-3) } if got, _, _ := v.Store.NodeKey("ace"); got != other { t.Error("the changed key was not recorded") diff --git a/modules/mesh-vault/go.mod b/modules/mesh-vault/go.mod index 1a304645..55f6b0da 100644 --- a/modules/mesh-vault/go.mod +++ b/modules/mesh-vault/go.mod @@ -3,7 +3,7 @@ module mesh-vault go 1.26.0 require ( - git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261103142843-f8bd12e4b7b1 + git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261103174208-b7abe203a929 github.com/nats-io/nats-server/v2 v2.11.17 github.com/nats-io/nats.go v1.54.0 golang.org/x/crypto v0.57.0 diff --git a/modules/mesh-vault/go.sum b/modules/mesh-vault/go.sum index 0111c9e9..abad2969 100644 --- a/modules/mesh-vault/go.sum +++ b/modules/mesh-vault/go.sum @@ -1,5 +1,5 @@ -git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261103142843-f8bd12e4b7b1 h1:T66tg9T/1hHg6KN/bDScHPT41IHkxLAGGO8VJiAOlb8= -git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261103142843-f8bd12e4b7b1/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY= +git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261103174208-b7abe203a929 h1:RtAFbUJRP1qO9AIMM2ue+cXNzqj7WZ7IOlSqNvttwuU= +git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261103174208-b7abe203a929/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY= github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op h1:Z/MZK75wC/NSrkgqeNIa7jexam9uWzhLmFTSCPI/kn0= github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op/go.mod h1:FQyySiasQQM8735Ddel3MRojmy4dA1IqCeyJ5jmPMbI= github.com/google/go-tpm v0.9.8 h1:slArAR9Ft+1ybZu0lBwpSmpwhRXaa85hWtMinMyRAWo= diff --git a/modules/nats/go.mod b/modules/nats/go.mod index 68625c89..a6aa5c98 100644 --- a/modules/nats/go.mod +++ b/modules/nats/go.mod @@ -3,7 +3,7 @@ module nats-tools go 1.26.0 require ( - git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128 + git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261103174208-b7abe203a929 github.com/klauspost/compress v1.20.0 github.com/nats-io/nats-server/v2 v2.11.17 github.com/nats-io/nats.go v1.54.0 diff --git a/modules/nats/go.sum b/modules/nats/go.sum index 43cc4ec7..abad2969 100644 --- a/modules/nats/go.sum +++ b/modules/nats/go.sum @@ -1,5 +1,5 @@ -git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128 h1:aI1DhZAuqVBXbybSqhLNOGX5rUOrn9T0cHNCDUQF7IQ= -git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY= +git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261103174208-b7abe203a929 h1:RtAFbUJRP1qO9AIMM2ue+cXNzqj7WZ7IOlSqNvttwuU= +git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261103174208-b7abe203a929/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY= github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op h1:Z/MZK75wC/NSrkgqeNIa7jexam9uWzhLmFTSCPI/kn0= github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op/go.mod h1:FQyySiasQQM8735Ddel3MRojmy4dA1IqCeyJ5jmPMbI= github.com/google/go-tpm v0.9.8 h1:slArAR9Ft+1ybZu0lBwpSmpwhRXaa85hWtMinMyRAWo= diff --git a/modules/nats/module.json b/modules/nats/module.json index 01f36795..93f518d7 100644 --- a/modules/nats/module.json +++ b/modules/nats/module.json @@ -3,7 +3,7 @@ "version": "1", "upgrade": { "policy": "record", - "why": "the bus: replaced only as a planned step a person starts (`bus upgrade`), its streams snapshotted first and checked after (hq ADR 0236); the controller holds this whatever is said here" + "why": "the bus keeps the streams of the whole mesh: each move waits for a person's yes, which the operator's Approve of the release that carries its build gives, and its node backs its streams up before it applies (hq ADR 0236, ADR 0601)" }, "provides": [ { @@ -21,6 +21,7 @@ } ], "bus-users": "/var/lib/nats-module/conf/accounts.conf", + "core-credentials": true, "own-secrets": { "broker": "${dir:mesh-state}/broker", "broker-nats-tools": "${dir:tools-secret}/broker-nats-tools"