From 56891abe2029a75b14ec084a124c8bd9a31d4a7e Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 4 Nov 2026 12:15:57 +0100 Subject: [PATCH] =?UTF-8?q?TLS-5=20grants:=20the=20tool=20runner's=20consu?= =?UTF-8?q?mes=20of=20the=20owners'=20views=20get=20their=20durable=20cons?= =?UTF-8?q?umers=20and=20its=20findings=20state=20its=20grant,=20as=20any?= =?UTF-8?q?=20manifest's=20(A7's=20request;=20ADR=200615=20=C2=A75)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Nothing in the composition changes for it: the consumers on module streams are made since DLV-G1. The row proves it on the tool runner's own manifest. Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig --- modules/mesh-grants/grants_test.go | 31 ++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/modules/mesh-grants/grants_test.go b/modules/mesh-grants/grants_test.go index fb2896e0..9a64e6fa 100644 --- a/modules/mesh-grants/grants_test.go +++ b/modules/mesh-grants/grants_test.go @@ -2271,6 +2271,37 @@ func TestGrants(t *testing.T) { } }) + // TLS-5 (A7's request; ADR 0615 §5): the tool runner, which consumes the owners' views events and keeps a findings state, + // gets its durable consumer on each views stream, made by the part, and its own findings bucket, as any manifest's. + t.Run("the tool runner's consumes of the owners' views get their consumers, and its findings state its grant", func(t *testing.T) { + b := fixture() + b.manifestOf("mesh-delivery-indexer", `{"module":"mesh-delivery-indexer","version":"1","own-secrets":{"broker":"/var/lib/i/broker"},`+ + `"emits":["views.manifests","views.modules"],"streams":[{"name":"indexer-views","events":["views.*"],"max-age-days":7,"max-mib":256}]}`) + b.manifestOf("mesh-delivery-assigner", `{"module":"mesh-delivery-assigner","version":"1","own-secrets":{"broker":"/var/lib/as/broker"},`+ + `"emits":["views.placement","views.seat-holders"],"streams":[{"name":"assigner-views","events":["views.*"],"max-age-days":7,"max-mib":256}]}`) + b.manifestOf("node-tools", `{"module":"node-tools","version":"1","tool-runner":true,"own-secrets":{"broker":"/var/lib/nt/broker"},`+ + `"consumes":["mesh-delivery-assigner.views.placement","mesh-delivery-assigner.views.seat-holders","mesh-delivery-indexer.views.manifests",`+ + `"mesh-delivery-indexer.views.modules"],"state":[{"name":"findings","history":5}]}`) + for _, m := range []string{"mesh-delivery-indexer", "mesh-delivery-assigner", "node-tools"} { + b.assign("a", m) + } + p := partOn(b) + p.Consumers = b + p.Compose() + for _, c := range []string{"INDEXER_VIEWS.a_node-tools", "ASSIGNER_VIEWS.a_node-tools"} { + if !slices.Contains(b.consumerCreates, c) { + t.Errorf("the tool runner's consumer %s was not made: %v", c, b.consumerCreates) + } + } + users := shadowOf(t, b) + runner := users[slices.IndexFunc(users, func(u grants.User) bool { return u.User == "a.node-tools" })] + for _, s := range []string{"$JS.API.CONSUMER.MSG.NEXT.INDEXER_VIEWS.a_node-tools", "$KV.node-tools_findings.>"} { + if !slices.ContainsFunc(runner.Publish, func(g string) bool { return buscompose.SubjectsOverlap(g, s) }) { + t.Errorf("the tool runner may not publish %s: %v", s, runner.Publish) + } + } + }) + // 774/AC4 (novox/hq ADR 0561 §2): every account the vault mints that the records compose is written to the state // accounts once, with the bus's address and a module's claims, read by the vault alone; a key is deleted when placement // no longer names its user, and never in a pass whose reads failed.