From 5f126021b7e5b0670bdf95ed4a031539c71aaea3 Mon Sep 17 00:00:00 2001 From: jochen Date: Tue, 22 Sep 2026 19:53:50 +0200 Subject: [PATCH] Keep the originals the carried bundle writes over beside the node's state (hq ADR 0100) --- internal/bootstrap/apply.go | 10 +++++++-- internal/bootstrap/apply_test.go | 38 ++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/internal/bootstrap/apply.go b/internal/bootstrap/apply.go index 372793d..82c6a10 100644 --- a/internal/bootstrap/apply.go +++ b/internal/bootstrap/apply.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "path/filepath" "strings" "github.com/novox/mesh-host/internal/apply" @@ -46,8 +47,13 @@ func ApplyBundle(ctx context.Context, o Options, sys system.System, d *declarati return apply.Report{}, err } - report, updated, applyErr := apply.Apply(ctx, sys, d, known, store.OriginCarried, run, - func(line string) { say(" " + strings.TrimPrefix(line, " ")) }, refuseSealed) + // The bundle writes over whatever the machine has at the paths the foundation needs — a + // distribution's own /etc/nftables.conf among them — so it keeps the original of each file it + // has no record of, beside the node's state, exactly as a declaration from the mesh does + // (novox/hq ADR 0100). + report, updated, applyErr := apply.ApplyKeeping(ctx, sys, d, known, store.OriginCarried, run, + func(line string) { say(" " + strings.TrimPrefix(line, " ")) }, refuseSealed, + apply.KeepIn(filepath.Dir(o.State))) // Saved whichever way it went, for the reason `mesh-host` gives: what was applied before a // failure is on the machine either way, and a host that did not record it would believe it diff --git a/internal/bootstrap/apply_test.go b/internal/bootstrap/apply_test.go index e3f45e6..486b452 100644 --- a/internal/bootstrap/apply_test.go +++ b/internal/bootstrap/apply_test.go @@ -3,8 +3,13 @@ package bootstrap import ( "context" "errors" + "os" + "path/filepath" "strings" "testing" + + "github.com/novox/mesh-host/internal/declaration" + "github.com/novox/mesh-host/internal/system" ) // `mesh-host` is built for one operating system and pins it at link time. An installer run by hand @@ -89,3 +94,36 @@ func TestASealedFileInAFoundationIsRefusedWithAReason(t *testing.T) { t.Errorf("the refusal does not say why there is no key: %v", err) } } + +// Defends novox/hq ADR 0100: the carried bundle keeps the original of a file it writes over that +// the host has no record of — the distribution's own ruleset, say. +func TestTheBundleKeepsTheOriginalOfWhatItWritesOver(t *testing.T) { + dir := t.TempDir() + conf := filepath.Join(dir, "nftables.conf") + if err := os.WriteFile(conf, []byte("# the distribution's own\n"), 0o644); err != nil { + t.Fatal(err) + } + d, err := declaration.ParseFileTrusted([]byte(`{"declaration":1,"resources":[ + {"id":"base-filter","type":"file","path":"` + conf + `","content":"table inet mesh {}\n"}]}`)) + if err != nil { + t.Fatal(err) + } + sys, err := system.For("arch") + if err != nil { + t.Fatal(err) + } + o := Options{State: filepath.Join(dir, "state.json")} + report, err := ApplyBundle(context.Background(), o, sys, d, nil, quietly) + if err != nil { + t.Fatal(err) + } + detail := report.Outcomes[0].Detail + at := strings.Index(detail, "kept at ") + if at < 0 { + t.Fatalf("the bundle wrote over a file it had no record of and kept nothing: %q", detail) + } + if got, err := os.ReadFile(detail[at+len("kept at "):]); err != nil || + string(got) != "# the distribution's own\n" { + t.Errorf("the kept original is %q (%v)", got, err) + } +}