The node-engine's witness ping is granted by the tool runner's name its manifest gives, any service's instance on its own node while none says so, never a name in mesh-grants (ADR 0601 §4; review round 1, finding 4)

This commit is contained in:
2026-11-03 16:44:47 +01:00
parent 0fa00d5e6c
commit 6d24922172
5 changed files with 48 additions and 11 deletions
@@ -91,6 +91,32 @@ func TestTheCopiedComposerComposesAsTheControllers(t *testing.T) {
t.Fatal(err)
}
golden(t, "user-lists.json", append(raw, '\n'))
// ADR 0601 §4: the node-engine's witness pings the tool runner by the name its manifest gives it, and with none saying so
// any service's instance on its own node, never a name written here.
t.Run("the witness pings the tool runner by its manifest", func(t *testing.T) {
for runner, want := range map[string]string{"runner-x": "$SRV.PING.runner-x.a", "": "$SRV.PING.*.a"} {
placed := []Declared{{Module: "web"}}
if runner != "" {
placed = append(placed, Declared{Module: runner, ToolRunner: true})
}
users, err := Users(Records{Nodes: []string{"a"}, Assigned: map[string][]Declared{"a": placed}})
if err != nil {
t.Fatal(err)
}
nodes := 0
for _, u := range users {
if u.Kind == KindNode {
nodes++
}
if u.Kind == KindNode && (u.Runner != runner || !slices.Contains(WitnessSubjects(u), want)) {
t.Errorf("runner %q: the node principal %+v witnesses %v, want %s", runner, u, WitnessSubjects(u), want)
}
}
if nodes == 0 {
t.Errorf("runner %q: no node principal composed", runner)
}
}
})
// An asker on a seat pulled by kind hears what that seat's workers gave up on, and no other stream's dead letters
// (novox/hq issue 734: a check ask given up during a bus reload left its try running for ever).
t.Run("an asker reads its seat's dead letters", func(t *testing.T) {
@@ -208,6 +208,9 @@ type Principal struct {
// `<module>.<tool>` (novox/hq ADR 0240, to-be 48 §3): asked of the instance on its own machine and
// nowhere else.
Checks []string
// Runner is, for a node, the module placed there whose manifest says it is the tool runner (`tool-runner`): the
// service the node-engine's witness pings (WitnessSubjects). Empty when none says so.
Runner string
// MovesIntoTheVault is a machine principal whose node-engine may ask the vault's `move-in` in its own name (design
// 57's V3, issue 676): set while the vault's holder serves it, so the engine can hand in the values sealed to its
// machine, which nothing else can open.
@@ -1603,7 +1606,7 @@ func discovering() []string {
// WitnessSubjects are the subjects a machine's node-engine publishes to witness the core builds it
// places (novox/hq ADR 0236): its own node tools' PING. The answers come to the machine's own inbox.
func WitnessSubjects(p Principal) []string {
return append([]string{pingSubject(p.Node)}, CheckSubjects(p)...)
return append([]string{pingSubject(p.Runner, p.Node)}, CheckSubjects(p)...)
}
// CheckSubjects are the tools a machine's node-engine asks as declared health checks (novox/hq ADR 0240,
@@ -1627,10 +1630,15 @@ func CheckSubjects(p Principal) []string {
return out
}
// pingSubject is a node-engine's witness's ping of its own node's tools (novox/hq ADR 0236).
func pingSubject(node string) string { return "$SRV.PING." + nodeToolsService + "." + node }
const nodeToolsService = "node-tools"
// pingSubject is a node-engine's witness's ping of its own node's tool runner (novox/hq ADR 0236), named as the runner's
// manifest says it is (ADR 0601 §4), never by a name here. With no manifest on the node saying so yet (an older build's,
// the runner kept as it runs), any service's instance on this node: the ping of its own node alone, whatever it is called.
func pingSubject(runner, node string) string {
if runner == "" {
runner = "*"
}
return "$SRV.PING." + runner + "." + node
}
// VaultOwnCall is the subject a machine principal's node-engine asks the vault's `own` on, in its own name.
func VaultOwnCall(p Principal) string {
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -175,17 +175,20 @@ func Users(r Records) ([]Principal, error) {
grants := -1
for _, node := range sortedCopy(r.Nodes) {
var checks []string
bus := false
bus, runner := false, ""
for _, d := range r.Assigned[node] {
if d.SnapshotsTheBus {
bus = true
}
if d.ToolRunner {
runner = d.Module // the node-engine's witness pings it by the name its manifest gives (ADR 0601 §4)
}
checks = append(checks, d.Checks...)
}
// **One user per login the node has** (novox/hq ADR 0583 §1): the one in use, and while it rotates the other too,
// each with the node's permissions.
for _, login := range nodeLoginsOf(r, node) {
out = append(out, Principal{Kind: KindNode, Node: node, Login: login, Checks: checks,
out = append(out, Principal{Kind: KindNode, Node: node, Login: login, Checks: checks, Runner: runner,
MovesIntoTheVault: servesMoveIn(r), StatesItsKeyToTheVault: servesVerb(r, VaultNodeKey),
AsksItsOwnSecrets: servesVerb(r, VaultOwn), AsksTheVaultToSign: servesVerb(r, VaultSign), Uses: asksSeatOf(r),
FollowsGrantsUserList: bus, ServedReads: servedOn(served, node), RotatesItsLogin: servesVerb(r, VaultRotateNode)})