AM-D3 path: the node-engine takes the runner's reading and fingerprint into the node's record

The grants part decides a node's withheld subjects from the runner's own
reading tied to the runner that stated it; the merge into capabilities stays
for placement.

Builds: design 46 7 §6 (its node's node-engine takes both into the node's record), lead answer A-203 (runner-capabilities, runner-fingerprint)

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
This commit is contained in:
2026-11-12 06:06:16 +01:00
parent a3b646b3c9
commit 8734d5ee1a
5 changed files with 20 additions and 4 deletions
+1 -1
View File
@@ -311,7 +311,7 @@ func (q *Session) nodeFactsWithRecord(r Report, before *nodeengine.HaveByNodeNod
n := nodeFacts(r)
if q.Keys != nil {
keys := q.Keys()
keys.Stated = q.runnerStated
keys.Stated, keys.StatedBy = q.runnerStated, q.runnerFingerprint
n.Record = recordOf(q.Membership.Node, r, keys)
}
if before != nil && before.Record != nil {
+4 -2
View File
@@ -344,7 +344,8 @@ func TestTheNodeKeyCarriesTheNodesFacts(t *testing.T) {
keys := RecordKeys{TunnelKey: "tk", SealingKey: "sk", ServingKey: "pk", Certificate: "cert",
Trusts: []string{"controller key sha256:aa", "vault key sha256:bb"}} // and what it trusts (L3 s6, ADR 0550 §2)
q.Keys = func() RecordKeys { return keys }
l.stated = map[string]string{CapabilitiesKey: "ledger-calls, bus-state-calls"} // the tool runner's calls (ENG-2)
l.stated = map[string]string{CapabilitiesKey: "ledger-calls, bus-state-calls", // the tool runner's calls (ENG-2)
FingerprintKey: "sha256:runner"} // and the runner that stated them (design 46 approval mark §6)
rep.Profile = map[string]any{"capabilities": []any{map[string]any{"name": "systemd", "present": true},
map[string]any{"name": "docker", "present": false}, map[string]any{"name": "apt", "present": true}},
"machine": map[string]any{"networks": []any{map[string]any{"link": "eth0", "network": "192.0.2.0/24"}}}}
@@ -357,7 +358,8 @@ func TestTheNodeKeyCarriesTheNodesFacts(t *testing.T) {
wantRecord := nodeengine.NodeRecord{V: sdkkeys.Version, Node: "n1", TunnelKey: "tk", SealingKey: "sk", ServingKey: "pk",
Certificate: "cert", Trusts: keys.Trusts, Capabilities: []string{"apt", "bus-state-calls", "ledger-calls", "systemd", CapabilityWantParts}, OutwardLinks: []string{"eth0"}, NetworksSaid: true,
Networks: []nodeengine.AttachedNetwork{{Link: "eth0", Network: "192.0.2.0/24"}}, Contract: NodeKeyContract,
ManifestReads: catalogue.ManifestReads()} // what it reads of a manifest: its contract's (ADR 0614 §4)
ManifestReads: catalogue.ManifestReads(), // what it reads of a manifest: its contract's (ADR 0614 §4)
RunnerCapabilities: []string{"bus-state-calls", "ledger-calls"}, RunnerFingerprint: "sha256:runner"}
if !nodeengineclient.SameNodeRecord(*got.Record, wantRecord) {
t.Errorf("record = %+v, want %+v", *got.Record, wantRecord)
}
+9
View File
@@ -33,6 +33,9 @@ type RecordKeys struct {
// Stated is the capabilities its tool runner states of the calls it answers (CapabilitiesKey), which the record says
// beside the machine's own: a module whose manifest asks for one is held off a machine whose runner does not state it.
Stated []string
// StatedBy is the fingerprint of the tool runner that stated Stated, as its PING carries it (FingerprintKey): the record
// takes both, so a reading is tied to the runner that made it (design 46 approval mark §6, lead answer A-203).
StatedBy string
}
// buildReads are the words and forms this engine reads of a manifest (novox/hq ADR 0614 §4): the reader capabilities of
@@ -135,7 +138,13 @@ func recordOf(node string, r Report, keys RecordKeys) *nodeengine.NodeRecord {
if c != "" && !slices.Contains(rec.Capabilities, c) {
rec.Capabilities = append(rec.Capabilities, c)
}
if c != "" {
rec.RunnerCapabilities = append(rec.RunnerCapabilities, c)
}
}
// The runner's own reading beside the merge, which stays for placement (design 46 approval mark §6, lead answer A-203).
sort.Strings(rec.RunnerCapabilities)
rec.RunnerFingerprint = keys.StatedBy
sort.Strings(rec.Capabilities)
if p.Machine != nil {
rec.NetworksSaid = true
+3 -1
View File
@@ -92,6 +92,8 @@ type Session struct {
// runnerStated is the capabilities this machine's tool runner last stated of the calls it answers (askRunner), said in the
// node's record. Held under saying.
runnerStated []string
// runnerFingerprint is the fingerprint of the runner that stated runnerStated (FingerprintKey). Held under saying.
runnerFingerprint string
// saying is held while a facts event is noted, so the haves are written in the order the facts are made.
saying sync.Mutex
@@ -581,5 +583,5 @@ func (q *Session) askRunner(ctx context.Context, bus Bus) {
stated = append(stated, c)
}
}
q.runnerStated = stated
q.runnerStated, q.runnerFingerprint = stated, strings.TrimSpace(md[FingerprintKey])
}
+3
View File
@@ -56,6 +56,9 @@ func (l *natsLink) Ping(ctx context.Context, service, id string) error {
const (
ToolRunnerService = "node-tools"
CapabilitiesKey = "capabilities"
// FingerprintKey is the metadata key of its PING it states its own fingerprint under, beside CapabilitiesKey (design 46
// approval mark §6, lead answer A-203).
FingerprintKey = "fingerprint"
)
// Stater is a link that can ask what a service's instance states of itself in its PING: its metadata.