From 38dbe1d3a388ab83449498e61c66ad9de136d01d Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 28 Oct 2026 18:47:13 +0100 Subject: [PATCH] =?UTF-8?q?Let=20a=20node=20agent=20hear=20the=20creation?= =?UTF-8?q?=20of=20the=20seat's=20placement=20and=20candidates,=20so=20it?= =?UTF-8?q?=20binds=20them=20with=20no=20timer=20(ADR=200567=20=C2=A79,=20?= =?UTF-8?q?issue=201126)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go/buscompose/buscompose_test.go | 7 ++++++- go/buscompose/nats.go | 3 +++ go/buscompose/state.go | 6 ++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/go/buscompose/buscompose_test.go b/go/buscompose/buscompose_test.go index 1f23a6c..52a7d8a 100644 --- a/go/buscompose/buscompose_test.go +++ b/go/buscompose/buscompose_test.go @@ -35,7 +35,7 @@ func beyondTheControllers(user string) map[string]bool { if !ok { return out } - for _, s := range append(readGrants("node-engine_have", "*.node"), NodeComposeReads(node)...) { + for _, s := range append(append(readGrants("node-engine_have", "*.node"), NodeComposeReads(node)...), NodeComposeAdvisories()...) { out[s] = true } return out @@ -129,6 +129,11 @@ func TestTheCopiedComposerComposesAsTheControllers(t *testing.T) { t.Errorf("a node is granted %s: %v, want %v", bucket, !granted, granted) } } + // It hears the creation of those two buckets, and of no other. + if got := NodeComposeAdvisories(); len(got) != 2 || got[0] != "$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-assigner_placement" || + got[1] != "$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-assigner_candidates" { + t.Errorf("a node hears the creation of %v", got) + } raw, err := os.ReadFile(filepath.Join("testdata", "controller-user-lists.json")) if err != nil { t.Fatal(err) diff --git a/go/buscompose/nats.go b/go/buscompose/nats.go index 0173496..70d7cc9 100644 --- a/go/buscompose/nats.go +++ b/go/buscompose/nats.go @@ -931,6 +931,9 @@ func PermissionsFor(p Principal) (Permissions, error) { // And the backup holder's word that it backed the module up (ADR 0539 clause 16), whichever module holds // node-backup: one event, by its name. sub = append(sub, NodeAgentHears...) + // And the creation of the seat mesh-placement's two states it composes from (ADR 0567 §9), so a node agent started + // before they exist binds each when the bus says it was made, with no timer; no other bucket's. + sub = append(sub, NodeComposeAdvisories()...) // And, while the vault serves it, the vault's move-in in its own name and nobody else's (issue 676): the // values sealed to this machine, which only it opens, handed in sealed to the vault's key for the call. if p.MovesIntoTheVault { diff --git a/go/buscompose/state.go b/go/buscompose/state.go index 9fb71f9..017d05b 100644 --- a/go/buscompose/state.go +++ b/go/buscompose/state.go @@ -326,6 +326,12 @@ func NodeComposeReads(node string) []string { return append(out, readGrants(ResolvedBucket, node+".>")...) } +// NodeComposeAdvisories are the stream-created advisories of the seat mesh-placement's placement and candidates +// (ADR 0567 §9): what a node agent hears to bind each once it is made. Those two and no wider. +func NodeComposeAdvisories() []string { + return []string{StreamCreatedAdvisory(delivery.PlacementBucket), StreamCreatedAdvisory(delivery.PlacementCandidatesBucket)} +} + // ServedReadGrants is the read of each key of mesh-config's resolved values named, `..` // (Principal.ServedReads, issue 1126): a get and a watch of that key alone. Each part is checked as every subject token // is, so a key cannot widen the grant.