diff --git a/modules/mesh-grants/internal/buscompose/buscompose_test.go b/modules/mesh-grants/internal/buscompose/buscompose_test.go index 9e81a20..7f837df 100644 --- a/modules/mesh-grants/internal/buscompose/buscompose_test.go +++ b/modules/mesh-grants/internal/buscompose/buscompose_test.go @@ -924,6 +924,17 @@ func everyNodesCompositions(t *testing.T) { if MayPublish(view, "$JS.API.DIRECT.GET.KV_mesh-host_composed.x") { t.Error("the read is granted as one bucket mesh-host_composed") } + // A bundle reading them (the provisioner, carried by the machine's runtime) reads them on the runtime's connection. + carried := Principal{Kind: KindNodeTools, Node: "one", Module: RuntimeModule, Nodes: []string{"one", "two"}, + Carries: []Declared{{Module: "mesh-provisioner", Reads: []string{delivery.ComposedRead}}}} + runner, err := PermissionsFor(carried) + if err != nil { + t.Fatal(err) + } + if !MayPublish(runner, "$JS.API.STREAM.NAMES") || !MaySubscribe(runner, "$JS.EVENT.ADVISORY.STREAM.CREATED.KV_composed-three") || + !MayPublish(runner, "$JS.API.DIRECT.GET.KV_composed-two.$KV.composed-two.two.node") { + t.Errorf("the runtime carrying the provisioner cannot read every node's compositions") + } m := MembershipFor("one", Declared{Module: "mesh-delivery-view", Reads: []string{delivery.ComposedRead}}, Placements{}) if !slices.ContainsFunc(m.State, func(s StateIssued) bool { return s.Name == delivery.ComposedRead && s.PerNode && s.Bucket == delivery.ComposedBucketPrefix && !s.Writes diff --git a/modules/mesh-grants/internal/buscompose/nats.go b/modules/mesh-grants/internal/buscompose/nats.go index 09e3d3b..ae61236 100644 --- a/modules/mesh-grants/internal/buscompose/nats.go +++ b/modules/mesh-grants/internal/buscompose/nats.go @@ -1026,6 +1026,11 @@ func PermissionsFor(p Principal) (Permissions, error) { for _, d := range p.Carries { pub = append(pub, stateGrants(stateAccess{Module: d.Module, Node: p.Node, Keeps: stateNames(d.State), PerMachine: perMachineNames(d.State), Reads: d.Reads, KeyedReads: d.KeyedReads, Nodes: p.Nodes})...) + // A bundle reads every node's compositions on the runtime's own connection (ADR 0593 ยง6), which hears a node's + // bucket made later by any stream's creation. + if slices.Contains(d.Reads, delivery.ComposedRead) && !slices.Contains(sub, familyCreatedAdvisory) { + sub = append(sub, familyCreatedAdvisory) + } } // **And it keeps the logs of the modules it carries** (novox/hq ADR 0297): each module's own, the // union over them. That one module's code does not append to another's log through it is the