From d283efdc9f69744f9adb43f8e2f90e1d005c6551 Mon Sep 17 00:00:00 2001 From: jochen Date: Mon, 2 Nov 2026 18:28:41 +0100 Subject: [PATCH] mesh-grants: the machine's runtime hears a node's compositions bucket made later for the bundles it carries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ADR 0593 §6: a bundle such as the provisioner reads mesh-host.composed on the runtime's connection, so the runtime account gets the stream listing and the creation advisory beside the read of each bucket. Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL --- .../internal/buscompose/buscompose_test.go | 11 +++++++++++ modules/mesh-grants/internal/buscompose/nats.go | 5 +++++ 2 files changed, 16 insertions(+) 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