diff --git a/go/buscompose/buscompose_test.go b/go/buscompose/buscompose_test.go index d1a3d8c..f3705d2 100644 --- a/go/buscompose/buscompose_test.go +++ b/go/buscompose/buscompose_test.go @@ -1,6 +1,7 @@ package buscompose import ( + "slices" "encoding/json" "os" "path/filepath" @@ -44,7 +45,8 @@ func mayBeyondTheControllers(user string) map[string]bool { if _, ok := strings.CutPrefix(user, "node."); !ok { return nil } - return map[string]bool{VaultOwnCall(Principal{Kind: KindNode, Node: strings.TrimPrefix(user, "node.")}): true} + return map[string]bool{VaultOwnCall(Principal{Kind: KindNode, Node: strings.TrimPrefix(user, "node.")}): true, + VaultSecretChanged: true} } // quotedToken is a quoted token of a composed list. @@ -218,6 +220,9 @@ func TestTheCopiedComposerComposesAsTheControllers(t *testing.T) { if plain, _ := PermissionsFor(Principal{Kind: KindNode, Node: "one"}); MayPublish(plain, VaultOwnCall(one)) { t.Error("a node asks own while the vault does not serve it") } + if !slices.Contains(perms.Subscribe, VaultSecretChanged) { + t.Errorf("node one does not hear the vault's secret-changed: %v", perms.Subscribe) + } }) t.Run("the controller's golden server configuration", func(t *testing.T) { seat := Seat{Name: "telegram-sender", Accepts: []string{"send"}, Emits: []string{"delivered", "failed"}} diff --git a/go/buscompose/nats.go b/go/buscompose/nats.go index 067fbd4..54a04da 100644 --- a/go/buscompose/nats.go +++ b/go/buscompose/nats.go @@ -935,6 +935,9 @@ func PermissionsFor(p Principal) (Permissions, error) { // compositions it makes itself. if p.AsksItsOwnSecrets { pub = append(pub, VaultOwnCall(p)) + // And the vault's word that a secret changed (names and generations, never a value), so the node composes again + // on it rather than at the next unrelated change (ADR 0547 §2). + sub = append(sub, VaultSecretChanged) } // And, on the node holding mesh-broker, the grants part's sealed user list it follows from take-over (novox/hq ADR // 0547 clause 7): one key, and an object by its name. @@ -1810,3 +1813,7 @@ const ( func VaultOwnCall(p Principal) string { return "mesh.seat." + VaultOwn.Seat + "." + CallKind + "." + VaultOwn.Verb + "." + CallerToken(p.Username()) } + +// VaultSecretChanged is the vault's event that a secret changed (a name and its generation, never a value), which a +// node composing its own hears to compose again (issue 1126, ADR 0547 §2). +const VaultSecretChanged = "mesh.mod." + VaultOwn.Seat + ".event.secret-changed"