Keep carrying a module whose bundle the tool runtime loads, as route-proxy's helper writes its state on the runner's account (issue 1329)
This commit is contained in:
@@ -636,3 +636,58 @@ func TestTheToolRunnerStillCarriesAModuleWithToolsAndItsOwnCredential(t *testing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// routeProxyExcerpt is the part of mesh-control's modules/route-proxy/module.json at 70cb217601f9 (read 2026-10-20)
|
||||
// that decides its grants: its state, what it reads, its own broker secret, and the bundle `provider` that the machine's
|
||||
// runtime loads (`loads`) though the module declares no tools.
|
||||
const routeProxyExcerpt = `{
|
||||
"module": "route-proxy",
|
||||
"state": ["bound"],
|
||||
"reads": ["mesh-controller.assignments"],
|
||||
"own-secrets": { "broker": "${dir:mesh-state}/broker" },
|
||||
"build": { "artifacts": [
|
||||
{ "name": "provider", "kind": "bundle", "language": "go", "from": "cmd/route-provider", "binary": "route-provider", "loads": ["route-provider"] }
|
||||
] }
|
||||
}`
|
||||
|
||||
// 1329 (review): a module with no tools whose bundle the runtime loads stays carried, because its helper writes its state
|
||||
// and reads on the runner's account; the delivery's five parts are still dropped.
|
||||
func TestTheToolRunnerStillCarriesAModuleWhoseBundleItLoads(t *testing.T) {
|
||||
var m struct {
|
||||
Module string `json:"module"`
|
||||
State []string `json:"state"`
|
||||
Reads []string `json:"reads"`
|
||||
Build struct {
|
||||
Artifacts []struct {
|
||||
Loads []string `json:"loads"`
|
||||
} `json:"artifacts"`
|
||||
} `json:"build"`
|
||||
}
|
||||
if err := json.Unmarshal([]byte(routeProxyExcerpt), &m); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
proxy := Declared{Module: m.Module, Reads: m.Reads}
|
||||
for _, s := range m.State {
|
||||
proxy.State = append(proxy.State, Bucket{Module: m.Module, Name: s, History: 1})
|
||||
}
|
||||
for _, a := range m.Build.Artifacts {
|
||||
proxy.Loads = proxy.Loads || len(a.Loads) > 0
|
||||
}
|
||||
streams := map[string]ModuleStream{"mesh-delivery-forge-bridge": {Module: "mesh-delivery-forge-bridge", Name: "FORGE"}}
|
||||
all := composed(t, Records{Nodes: []string{"novox"}, Assigned: map[string][]Declared{"novox": {
|
||||
{Module: RuntimeModule}, proxy, declaredOf(t, bridgeManifest, streams), declaredOf(t, indexerManifest, streams),
|
||||
declaredOf(t, tellerManifest, streams), declaredOf(t, watchmanManifest, streams),
|
||||
}}})
|
||||
runner := all["runner@novox"]
|
||||
if !canWrite(runner, "route-proxy_bound", "k") {
|
||||
t.Error("novox's tool runner does not carry route-proxy, whose bundle it loads")
|
||||
}
|
||||
if !canWrite(all["route-proxy@novox"], "route-proxy_bound", "k") {
|
||||
t.Error("route-proxy lost its own credential")
|
||||
}
|
||||
for _, b := range []string{"mesh-delivery-teller_answers", "mesh-delivery-forge-bridge_heads", "mesh-delivery-indexer_modules", "mesh-delivery-watchman_stuck"} {
|
||||
if canWrite(runner, b, "k") {
|
||||
t.Errorf("novox's tool runner may write %s", b)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,6 +69,10 @@ type Declared struct {
|
||||
// Checks are the module's own tools its health asks, each `<module>.<tool>` (novox/hq ADR 0240, to-be
|
||||
// 48 §3): the machine's node-engine asks them of its own node tools, and is granted that and no more.
|
||||
Checks []string
|
||||
// Loads says the module has a compiled bundle whose entrypoints the machine's tool runtime loads and runs
|
||||
// (novox/hq ADR 0175; issue 1329): a helper that reaches the bus on the runner's account, so the runner
|
||||
// must carry the module although it declares no tools.
|
||||
Loads bool
|
||||
// RunsAs is the account the module runs as in a runtime of its own (novox/hq ADR 0259 §8): it is never
|
||||
// carried by the machine's runtime, and reaches the bus on its own account.
|
||||
RunsAs string
|
||||
@@ -394,11 +398,11 @@ func boundBucketsOf(r Records) []string {
|
||||
|
||||
// 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,
|
||||
// or a check its health asks. A module with none of these is a process of its own on its own credential, and
|
||||
// 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
|
||||
// stays carried, and so does the runtime module itself. Whether the module has its own broker secret is not
|
||||
// the test: a module with tools and its own credential is still carried (ADR 0175).
|
||||
func runnerServes(d Declared) bool {
|
||||
return d.Module == RuntimeModule || d.NoAccount || len(d.Serves) > 0 || len(d.Holds) > 0 || len(d.Waits) > 0 || len(d.Checks) > 0
|
||||
return d.Module == RuntimeModule || d.NoAccount || d.Loads || len(d.Serves) > 0 || len(d.Holds) > 0 || len(d.Waits) > 0 || len(d.Checks) > 0
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user