EE-D2: the tool runner's runtime hears every event subject

Where the runtime's permissions are composed, a runtime carrying the tool
runner subscribes mesh-sdk's EveryEvent (every seat's, module's and module
stream's events) and nothing else for it, replacing the per-family list.

Builds: design 34 every-event §6, §8, §25, §27

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
This commit is contained in:
2026-11-12 05:32:04 +01:00
parent 54a9159b25
commit 69a0004fe6
3 changed files with 77 additions and 1 deletions
@@ -20,6 +20,7 @@ import (
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds"
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-sdk/buildstep"
"git.novox.be/novox/mesh-sdk/go/announce"
sdkbc "git.novox.be/novox/mesh-sdk/go/buscompose"
"git.novox.be/novox/mesh-sdk/go/catalogue"
"git.novox.be/novox/mesh-sdk/go/seatdef"
"github.com/nats-io/nats.go/jetstream"
@@ -60,6 +61,7 @@ func TestTheCopiedComposerComposesAsTheControllers(t *testing.T) {
t.Run("a module's ledger is written by its ledger account alone", aLedgerIsWrittenByItsLedgerAccountAlone)
t.Run("the bus module's process alone keeps every module's bus state and serves the broker's verbs", theBusModulesProcessKeepsBusState)
t.Run("a process serving a verb is granted its calls and chunk subjects, and the reverse goes to an account naming it", chunkSubjectsFollowTheAnswerer)
t.Run("the tool runner's runtime hears every event subject and nothing wider", theToolRunnerHearsEveryEvent)
// ADR 0567 §3, §9: a node reads every key of the seat's assignments and its candidates (it composes with the whole
// proposal laid over), and none of its open proposals; it writes neither.
reads := strings.Join(NodeComposeReads("laptop"), " ")
@@ -1447,3 +1449,70 @@ func chunkSubjectsFollowTheAnswerer(t *testing.T) {
t.Errorf("the keeper's restore grant reaches a stream that is not restricted")
}
}
// EE-D2 (design 34 every-event §6, §8, §25, §27; ADR 0670 §1, §3, ADR 0671 §1): the runtime carrying the tool runner
// subscribes the three event subjects of mesh-sdk's EveryEvent, beside a module with a restricted stream, and nothing
// wider; it publishes no other module's or seat's event and derives no durable consumer or acknowledgement on that
// stream; a runtime carrying no tool runner gets none of the three.
func theToolRunnerHearsEveryEvent(t *testing.T) {
shop := func() Declared {
return Declared{Module: "shop", Serves: []string{"sell"}, Emits: []string{"sold"},
Streams: []ModuleStream{{Module: "shop", Name: "LEDGER", Events: []string{"booked"}, Restricted: true}}}
}
r := Records{Nodes: []string{"a", "b"}, Assigned: map[string][]Declared{
"a": {{Module: "node-tools", ToolRunner: true}, shop()},
"b": {shop()}}}
users, err := Users(r)
if err != nil {
t.Fatal(err)
}
every := sdkbc.EveryEvent()
if len(every) != 3 {
t.Fatalf("EveryEvent answers %v, want three subjects", every)
}
wider := []string{">", "mesh.>", "mesh.*.>", "mesh.seat.>", "mesh.mod.>", "mesh.stream.>"}
runners := 0
for _, u := range users {
p, err := PermissionsFor(u)
if err != nil {
t.Fatalf("%s: %v", u.Username(), err)
}
runs := false
for _, d := range u.Carries {
runs = runs || d.ToolRunner
}
for _, e := range every {
if slices.Contains(p.Subscribe, e) != runs {
t.Errorf("%s (tool runner %v) subscribes %s: %v", u.Username(), runs, e, !runs)
}
}
for _, s := range p.Subscribe {
if slices.Contains(wider, s) {
t.Errorf("%s subscribes %s, wider than every event", u.Username(), s)
}
}
if !runs {
continue
}
runners++
for _, s := range p.Subscribe {
if strings.HasPrefix(s, "$JS.") && strings.Contains(s, "LEDGER") {
t.Errorf("the tool runner's runtime hears %s on the restricted stream", s)
}
}
for _, s := range []string{"mesh.mod.other.event.x", "mesh.seat.mesh-catalog.event.manifest-accepted",
"mesh.stream.other.event.x"} {
if MayPublish(p, s) {
t.Errorf("the tool runner's runtime may publish another's event %s", s)
}
}
for _, s := range p.Publish {
if strings.Contains(s, "LEDGER") || strings.HasPrefix(s, "$JS.ACK.") {
t.Errorf("the tool runner's runtime may %s on the restricted stream", s)
}
}
}
if runners != 1 {
t.Errorf("%d runtimes carry the tool runner, want 1", runners)
}
}
@@ -1192,6 +1192,13 @@ func permissionsFor(p Principal) (Permissions, error) {
pub = append(pub, logGrants(d.Module, logNames(d.Logs))...)
pub = append(pub, readLogGrants(d.ReadLogs)...)
}
// **And every event subject** (design 34 every-event §6, §25; novox/hq ADR 0670 §1, ADR 0671 §1): the runtime
// carrying the tool runner hears every seat's, module's and module stream's events, a restricted stream
// included, as a plain subscription: no publish, no durable consumer, no acknowledgement and no deny is added
// for it, and a runtime carrying no tool runner gets none of them.
if slices.ContainsFunc(p.Carries, func(d Declared) bool { return d.ToolRunner }) {
sub = append(sub, sdkbc.EveryEvent()...)
}
// **Never the traffic of a trusted holder** (novox/hq ADR 0259 §8): the machine's runtime runs as the
// operator's account, which every agent runs as, so a module saying warrants or speaking for a kind
// that proves its sender is never composed into it — refused here, naming it, whatever registration
@@ -589,7 +589,7 @@ func Composed(d Declared) bool { return d.Module != EngineModule }
func CarriedByRunner(d Declared) bool { return Composed(d) && d.RunsAs == "" && runnerServes(d) }
// runnerServes says the machine's tool runner has something of this module to serve (novox/hq ADR 0436 §1,
// ADR 0437 §4, issue 1329): tools or loads it answers, a seat it holds, event families a session waits on,
// ADR 0437 §4, issue 1329): tools or loads it answers, a seat it holds,
// a bundle the runtime loads (Loads), or a check its health asks. A module with none of these is a process of its own on its own credential, and
// carrying it would hand every session on the machine the runner's account with that module's state, seat
// traffic and streams. A module with no account of its own (NoAccount) is spoken for by the runner, so it