AM-D4: the real bus delivers no marked call to a runtime denied the marks

A deny composed into the user list must hold on the server itself: the
runtime keeps its allow of the module's tools, and neither the tool's subject
nor the caller-named subject process.Call sends on first reaches it.

Builds: design 46 7 §6 (integration, the grants part's real-bus test)

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
This commit is contained in:
2026-11-12 06:20:21 +01:00
parent 7bc4836eb0
commit 611fafb58e
2 changed files with 56 additions and 0 deletions
@@ -623,3 +623,58 @@ func theToolRunnerHearsEveryEventOnTheBus(t *testing.T) {
}
hears(ledger, "the restricted stream after its grant value changed")
}
// aRuntimeDeniedTheMarksHearsNoMarkedCall, run by TestTheGrantsPartWhole, defends design 46 approval mark §6 on a real
// server (AM-D4): a runtime account composed with the deny, its node's tool runner stating no approval-mark/1, holds
// `mesh.mod.<module>.tool.>` and is delivered no call on a marked tool's subject nor on the caller-named subject
// process.Call sends on first; a call of an unmarked tool still reaches it.
func aRuntimeDeniedTheMarksHearsNoMarkedCall(t *testing.T) {
records := buscompose.Records{Nodes: []string{"a"}, Assigned: map[string][]buscompose.Declared{"a": {
{Module: "node-tools", ToolRunner: true},
{Module: "shell", Serves: []string{"execute", "read"}, Marked: []string{"execute"}},
{Module: "asker", Invokes: []string{"shell.execute", "shell.read"}},
}}, Readings: map[string]buscompose.RunnerReading{"a": {Capabilities: []string{"bus-state-calls"}, Fingerprint: "sha256:runner"}},
Running: map[string]string{"a.node-tools": "sha256:runner"}}
bus := startComposedBus(t, records)
runner, _ := bus.dial("a.node-tools")
heard := make(chan *nats.Msg, 16)
for _, s := range []string{"mesh.mod.shell.tool.>", "mesh.mod.shell.call.>"} {
if _, err := runner.ChanSubscribe(s, heard); err != nil {
t.Fatal(err)
}
}
if err := runner.Flush(); err != nil {
t.Fatal(err)
}
asker, askerRefused := bus.dial("a.asker")
// The caller-named subject is process.Call's first: mesh.mod.<module>.call.<tool>.<caller's token>.
for _, subject := range []string{"mesh.mod.shell.tool.execute", "mesh.mod.shell.call.execute.asker~a", "mesh.mod.shell.tool.read"} {
if err := asker.Publish(subject, []byte(`{}`)); err != nil {
t.Fatal(err)
}
}
if err := asker.Flush(); err != nil {
t.Fatal(err)
}
select {
case e := <-askerRefused:
t.Fatalf("the asker may not call: %s", e)
default:
}
select {
case m := <-heard:
if m.Subject != "mesh.mod.shell.tool.read" {
t.Errorf("the runtime denied the marks was delivered %s", m.Subject)
}
case <-time.After(5 * time.Second):
t.Fatal("the unmarked call did not reach the runtime")
}
if err := runner.Flush(); err != nil {
t.Fatal(err)
}
select {
case m := <-heard:
t.Errorf("the runtime denied the marks was delivered %s", m.Subject)
default:
}
}
@@ -628,6 +628,7 @@ func TestTheGrantsPartWhole(t *testing.T) {
// And what the part composes holds on a server that loads it (one integration test per module, the test budget).
t.Run("the composed grants hold on a server that loads them", theBusNodeFollowsTheSealedListUnderTheComposedGrants)
t.Run("the tool runner hears every event on a server that loads its grants", theToolRunnerHearsEveryEventOnTheBus)
t.Run("a runtime denied the marks is delivered no call on a marked subject", aRuntimeDeniedTheMarksHearsNoMarkedCall)
}
// lockedLog is the part's log, read by the test while the part writes it.