From 32a99144f65e27c13b7db4fdb6d78b4fecd21f50 Mon Sep 17 00:00:00 2001 From: jochen Date: Tue, 3 Nov 2026 23:29:17 +0100 Subject: [PATCH] =?UTF-8?q?The=20node=20record=20states=20the=20reader=20c?= =?UTF-8?q?apabilities=20of=20the=20mesh-sdk=20the=20engine=20is=20built?= =?UTF-8?q?=20with=20(ADR=200614=20=C2=A74,=20issue=20774)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit recordOf writes catalogue.ManifestReads in NodeRecord.ManifestReads, once per build, so the assigner moves a want only to an engine that reads its manifest. A row holds the engine's own manifest to the baseline's words, so its new build is never held for a word only it reads. Claude-Session: https://claude.ai/code/session_01JG73KG7AfSdh6FP8VrBqCn --- internal/link/have_test.go | 3 ++- internal/link/record.go | 9 ++++++++- packaging/operator_keys_test.go | 7 +++++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/internal/link/have_test.go b/internal/link/have_test.go index f092ed2..2fc4c41 100644 --- a/internal/link/have_test.go +++ b/internal/link/have_test.go @@ -270,7 +270,8 @@ func TestTheNodeKeyCarriesTheNodesFacts(t *testing.T) { } wantRecord := delivery.NodeRecord{V: delivery.Version, Node: "n1", TunnelKey: "tk", SealingKey: "sk", ServingKey: "pk", Certificate: "cert", Trusts: keys.Trusts, Capabilities: []string{"apt", "systemd"}, OutwardLinks: []string{"eth0"}, NetworksSaid: true, - Networks: []catalogue.AttachedNetwork{{Link: "eth0", Network: "192.0.2.0/24"}}, Contract: NodeKeyContract} + Networks: []catalogue.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) if !delivery.SameNodeRecord(*got.Record, wantRecord) { t.Errorf("record = %+v, want %+v", *got.Record, wantRecord) } diff --git a/internal/link/record.go b/internal/link/record.go index f176bab..df204af 100644 --- a/internal/link/record.go +++ b/internal/link/record.go @@ -3,6 +3,7 @@ package link import ( "encoding/json" "sort" + "sync" "git.novox.be/novox/mesh-sdk/go/catalogue" "git.novox.be/novox/mesh-sdk/go/delivery" @@ -27,6 +28,11 @@ type RecordKeys struct { Trusts []string } +// manifestReads is what this engine reads of a manifest (novox/hq ADR 0614 §4): the reader capabilities of the mesh-sdk it +// is built with, stated in its record so the assigner moves a want only to an engine that reads its manifest. Computed +// once: it is the build's, and never changes while it runs. +var manifestReads = sync.OnceValue(catalogue.ManifestReads) + // recordOf is the node's record from one facts report and its keys; nil for a report that carries no profile (one that // says nothing of the machine, such as a rekey), so the record last written stands. func recordOf(node string, r Report, keys RecordKeys) *delivery.NodeRecord { @@ -47,7 +53,8 @@ func recordOf(node string, r Report, keys RecordKeys) *delivery.NodeRecord { return nil } rec := &delivery.NodeRecord{V: delivery.Version, Node: node, TunnelKey: keys.TunnelKey, SealingKey: keys.SealingKey, - ServingKey: keys.ServingKey, Certificate: keys.Certificate, Authority: keys.Authority, Trusts: keys.Trusts} + ServingKey: keys.ServingKey, Certificate: keys.Certificate, Authority: keys.Authority, Trusts: keys.Trusts, + ManifestReads: manifestReads()} for _, c := range p.Capabilities { if c.Present && c.Name != "" { rec.Capabilities = append(rec.Capabilities, c.Name) diff --git a/packaging/operator_keys_test.go b/packaging/operator_keys_test.go index b86f6c4..5c77c08 100644 --- a/packaging/operator_keys_test.go +++ b/packaging/operator_keys_test.go @@ -75,6 +75,13 @@ func TestTheManifestConsumesTheBackupEvent(t *testing.T) { if err != nil { t.Fatal(err) } + // ADR 0614 §4: the node-engine's own manifest uses only words every running engine reads (the baseline, what an engine + // stating none is read as), so its new build is never held for a word only it reads. + if needs, err := catalogue.ManifestNeeds(raw); err != nil || len(needs) == 0 { + t.Fatalf("the manifest's needs: %v, %v", needs, err) + } else if missing := catalogue.MissingManifestReads(needs, nil); len(missing) != 0 { + t.Errorf("the manifest needs %v, which an engine stating nothing does not read: its new build would be held", missing) + } // 774 (the alert table): the state health, one key per node, the node's own health report, kept until // written again (no TTL: a node that stops writing keeps its last word, which the watchman reads with its time). // ADR 0577 clause 1: and the state data, one key per module placed on the node that declares data items, kept until