From 9280513afab3f74a6061eb0c5c6ceba97dfc5143 Mon Sep 17 00:00:00 2001 From: jochen Date: Tue, 22 Sep 2026 18:03:45 +0200 Subject: [PATCH] Refuse token issue --adopted for a converged node and point to adopt, rather than flip it quietly (hq ADR 0100) --- cmd/mesh-controller/nodes.go | 17 ++++++++++++----- cmd/mesh-controller/nodes_test.go | 15 ++++++++++----- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/cmd/mesh-controller/nodes.go b/cmd/mesh-controller/nodes.go index 1ca2257..d7ce9dd 100644 --- a/cmd/mesh-controller/nodes.go +++ b/cmd/mesh-controller/nodes.go @@ -279,13 +279,20 @@ func issueFor(ctx context.Context, inv *inventory.Inventory, existing, fresh str } name = node.Name } - // Saying adopted makes the node adopted. Not saying it leaves the node as it is: re-issuing a - // token for an adopted node does not converge it — converging is its own act, previewed - // (novox/hq ADR 0100). - if adopted { - if err := inv.SetAdopted(ctx, name, true); err != nil { + // Not saying adopted leaves the node as it is: re-issuing a token for an adopted node does not + // converge it — converging is its own act, previewed (novox/hq ADR 0100). And saying it for a + // node already converged is refused rather than done quietly: returning a node to adopted is + // its own act too, which unloads the mesh's filter and enables the found firewall again. + if adopted && fresh == "" { + node, err := inv.NodeByName(ctx, name) + if err != nil { return inventory.Issued{}, err } + if !node.Adopted { + return inventory.Issued{}, fmt.Errorf("%s is converged, and a token does not change "+ + "that: run `adopt %s` to return it to adopted, then issue the token without "+ + "--adopted", name, name) + } } return inv.IssueToken(ctx, name, validFor) diff --git a/cmd/mesh-controller/nodes_test.go b/cmd/mesh-controller/nodes_test.go index c9f55a7..86b9558 100644 --- a/cmd/mesh-controller/nodes_test.go +++ b/cmd/mesh-controller/nodes_test.go @@ -139,11 +139,16 @@ func TestATokenIssuedAdoptedSaysSoAndReissuingDoesNotConverge(t *testing.T) { if !again.Node.Adopted { t.Fatal("re-issuing without --adopted converged the node; converging is its own act") } - existing, err := issueFor(ctx, open.inventory, "laptop", "", true, time.Hour) - if err != nil { + // --adopted for a node already converged is refused, and points at the act that does it. + if _, err := issueFor(ctx, open.inventory, "laptop", "", true, time.Hour); err == nil || + !strings.Contains(err.Error(), "adopt laptop") { + t.Fatalf("--adopted on a converged node was not refused: %v", err) + } + if n, _ := open.inventory.NodeByName(ctx, "laptop"); n.Adopted { + t.Fatal("a refused token flipped the node to adopted") + } + // And said for a node that is adopted already, it is the ordinary re-issue. + if _, err := issueFor(ctx, open.inventory, "joiner", "", true, time.Hour); err != nil { t.Fatal(err) } - if !existing.Node.Adopted { - t.Fatal("--adopted on an existing record did not make it adopted") - } }