package main import ( "context" "errors" "strings" "testing" "time" "github.com/novox/mesh-controller/internal/catalogue" "github.com/novox/mesh-controller/internal/conditions" "github.com/novox/mesh-controller/internal/inventory" "github.com/novox/mesh-controller/internal/link" ) var rootNow = time.Date(2026, 10, 9, 12, 0, 0, 0, time.UTC) // A machine is root-free only on a positive measure of each thing (the review of 2026-10-09, H2/H3): every // read succeeded, an agent account is named and judged confined by the node-engine, execute is not served. func TestRootFreeIsAPositiveMeasureAndNothingElse(t *testing.T) { pass := rootFacts{Machine: "anchor", AgentNamed: true, Confined: true, ConfinedWhy: "the agent account agents cannot become root without a person (judged 2026-10-09 12:00)"} if v := judgeRoot(pass, rootNow); !v.Free || v.Machine != "anchor" || !v.Judged.Equal(rootNow) { t.Fatalf("the one pass: %+v", v) } fails := map[string]func(*rootFacts){ "a read failed": func(f *rootFacts) { f.Unread = []string{"the store did not answer"} }, "no agent account named": func(f *rootFacts) { f.AgentNamed, f.Confined = false, false }, "not confined": func(f *rootFacts) { f.Confined = false }, "nothing said of it": func(f *rootFacts) { f.ConfinedWhy = "" }, "execute served": func(f *rootFacts) { f.Execute, f.ExecuteWhy = true, "the bus hears execute answered there" }, "confined, but agent read": func(f *rootFacts) { f.Unread, f.ConfinedWhy = []string{"x"}, "" }, } for name, mutate := range fails { f := pass mutate(&f) if v := judgeRoot(f, rootNow); v.Free || v.Why == "" { t.Errorf("%s: judged %+v", name, v) } } } // The reader fails closed on every case the review named: the agent account read only where the coding-agent // module runs (old :225), no account to measure taken for a pass (old :246), and a measure that did not answer // taken for "not root" (old :114, :123). func TestTheRootReaderFailsClosed(t *testing.T) { shell := catalogue.Manifest{Module: "zsh", Claims: []catalogue.Claim{{Name: loginShellSeat, Serves: []string{"execute"}}}, Settings: map[string]catalogue.SettingDeclaration{"execute": {Default: "withhold"}}} reader := func() *rootReader { return &rootReader{ entries: []inventory.Entry{{Manifest: shell, On: []string{"anchor"}}}, heard: map[string]map[string]map[string]bool{}, confined: func(context.Context, string, time.Time) (bool, bool, string, error) { return true, true, "the agent account agents cannot become root without a person", nil }, settings: func(context.Context, string, string) ([]catalogue.Layer, error) { return nil, nil }, } } ctx := context.Background() if v := judgeRootFree(ctx, reader(), []string{"anchor"}, rootNow)[0]; !v.Free { t.Fatalf("the control: agents confined, execute withheld and unheard, everything read: %+v", v) } cases := map[string]func(*rootReader){ "no agent account named, no coding-agent module there": func(r *rootReader) { r.confined = func(context.Context, string, time.Time) (bool, bool, string, error) { return false, false, "anchor names no agent account: agents run as the operator account (ops)", nil } }, "the node-engine's verdict not read": func(r *rootReader) { r.confined = func(context.Context, string, time.Time) (bool, bool, string, error) { return false, false, "", errors.New("the store did not answer") } }, "a stale verdict": func(r *rootReader) { r.confined = func(context.Context, string, time.Time) (bool, bool, string, error) { return true, false, "the agent account agents is not judged: the machine's newest statement was heard at …", nil } }, "the bus not asked": func(r *rootReader) { r.asked = errors.New("no bus") }, "the placements not read": func(r *rootReader) { r.read = errors.New("no store") }, "the holder's setting not read": func(r *rootReader) { r.settings = func(context.Context, string, string) ([]catalogue.Layer, error) { return nil, errors.New("no store") } }, "execute heard on the bus": func(r *rootReader) { r.heard = map[string]map[string]map[string]bool{loginShellSeat: {"execute": {"anchor": true}}} }, "a holder that serves execute": func(r *rootReader) { r.entries[0].Manifest.Settings = nil }, } for name, mutate := range cases { r := reader() mutate(r) if v := judgeRootFree(ctx, r, []string{"anchor"}, rootNow)[0]; v.Free { t.Errorf("%s: judged free: %+v", name, v) } } // A machine with no login shell holder at all: still the bus must hear none. r := reader() r.entries = nil r.heard = map[string]map[string]map[string]bool{loginShellSeat: {"execute": {"anchor": true}}} if v := judgeRootFree(ctx, r, []string{"anchor"}, rootNow)[0]; v.Free { t.Errorf("execute answered by a module nobody assigned: %+v", v) } } // The probe says agent-root, by the same judgement, on each machine where the router or a module of its own // account runs and that is not root-free; urgent and in plain words; nothing where every one is free. func TestTheProbeSaysEachMachineThatIsNotRootFree(t *testing.T) { entries := []inventory.Entry{ {Manifest: catalogue.Manifest{Module: "telegram", RunsAs: "telegram"}, On: []string{"anchor"}}, {Manifest: catalogue.Manifest{Module: "messenger", RunsAs: "messenger", Claims: []catalogue.Claim{{Name: routerSeat}}}, On: []string{"anchor"}}, {Manifest: catalogue.Manifest{Module: "xorg", Claims: []catalogue.Claim{{Name: catalogue.DisplayServerSeat}}}, On: []string{"laptop"}}, } trusted := trustedMachines(entries) if len(trusted["anchor"]) != 2 || len(trusted["laptop"]) != 0 { t.Fatalf("trusted %v", trusted) } r := &rootReader{entries: entries, heard: map[string]map[string]map[string]bool{}, confined: func(_ context.Context, node string, _ time.Time) (bool, bool, string, error) { return false, false, node + " names no agent account: agents run as the operator account (ops)", nil }, settings: func(context.Context, string, string) ([]catalogue.Layer, error) { return nil, nil }} got := rootObservations(context.Background(), r, trusted, rootNow) if len(got) != 1 || got[0].Machine != "anchor" || got[0].Kind != kindRootNotFree || got[0].Severity != conditions.Urgent || !strings.Contains(got[0].Summary, "messenger, telegram") || !strings.Contains(got[0].Summary, "names no agent account") { t.Fatalf("said %+v", got) } o := got[0] if why, ok := conditions.PlainWords(conditions.Words{Headline: o.Headline, Explanation: o.Explanation, Needs: o.Needs, Resolved: o.Resolved}, o.Machine); !ok { t.Errorf("not plain: %s", why) } r.confined = func(context.Context, string, time.Time) (bool, bool, string, error) { return true, true, "confined", nil } if got := rootObservations(context.Background(), r, trusted, rootNow); len(got) != 0 { t.Errorf("said of a free machine: %+v", got) } } // novox/hq ADR 0268: the login shell counts only where its execute is served, and fails closed. func TestTheLoginShellCountsOnlyWhereExecuteIsServed(t *testing.T) { val := func(v any) *any { return &v } cases := []struct { name string claims bool setting *any heard, asked bool served bool saysInTheWhys string }{ {"withheld by the setting and silent on the bus", true, val("withhold"), false, true, false, ""}, {"withheld by the setting, the machine not yet pushed", true, val("withhold"), true, true, true, "the bus hears"}, {"the setting serves", true, val("serve"), false, true, true, "setting there is serve"}, {"a wrong value withholds, as the holder does", true, val("Serve"), false, true, false, ""}, {"the setting withholds, the bus could not be asked", true, val("withhold"), false, false, true, "could not be asked"}, {"a holder with no such setting that claims execute", true, nil, false, true, true, "claims execute"}, {"a holder with no such setting that does not claim it, heard all the same", false, nil, true, true, true, "the bus hears"}, {"a holder with no such setting that does not claim it, silent", false, nil, false, true, false, ""}, } for _, c := range cases { served, why := loginShellServed("zsh", c.claims, c.setting, c.heard, c.asked) if served != c.served || (c.saysInTheWhys != "" && !strings.Contains(why, c.saysInTheWhys)) { t.Errorf("%s: served %v (%q), want %v saying %q", c.name, served, why, c.served, c.saysInTheWhys) } } } // The root-free verb is the serving controller's alone, answered in its process and never as a command; a // controller not serving answers an error, which the router reads as no machine free. func TestRootFreeIsAnsweredOnlyByTheServingController(t *testing.T) { was := doctorFrom doctorFrom = nil t.Cleanup(func() { doctorFrom = was }) if _, err := rootFreeAnswer(context.Background(), "anchor", rootNow); err == nil { t.Error("a controller not serving judged a machine") } if !inProcess["root-free"] { t.Error("root-free is not answered in the serving process") } if _, err := argvFor("root-free", map[string]any{"machines": "anchor"}); err == nil { t.Error("root-free ran as a command") } // The router names its machines as a list (its contract with this verb); one text separated by commas is // the same; anything else in the list is refused. for _, given := range []any{[]any{"anchor", "relay"}, "anchor, relay"} { a, err := readArguments("root-free", map[string]any{"machines": given}) if err != nil || a.given["machines"] != "anchor,relay" && a.given["machines"] != "anchor, relay" { t.Errorf("root-free given %v read %v (%v)", given, a, err) } } if _, err := readArguments("root-free", map[string]any{"machines": []any{"anchor", 7}}); err == nil { t.Error("root-free took a number for a machine") } if _, err := readArguments("status", map[string]any{"machines": []any{"anchor"}}); err == nil { t.Error("a verb that takes no list took one") } } // The confirmation review of 2026-10-09: ADR 0266's quiet window (#175) keeps the self-check from raising // agent-can-become-root while the node-engine's setuid search runs and no complete one judges. It must not make root-free answer free: // root-free needs a complete, fresh verdict. A verdict still waiting for the search is "not judged" to // agentConfined, so the machine is not root-free, whatever the quiet says — and the same statement, complete // and healthy, is the control. func TestAMachineWaitingForItsFirstSetuidSearchIsNotRootFree(t *testing.T) { now := rootNow statement := func(state, reason string) inventory.NodeHealth { return inventory.NodeHealth{Node: "anchor", Contract: link.RootContract, SaidAt: now, HeardAt: now, Resources: []inventory.ResourceHealth{{Module: "claude-code", Resource: "agent", Kind: link.KindAccount, Target: "agents", State: state, Reason: reason, Root: link.RootNever}}} } judged := func(h inventory.NodeHealth) rootVerdict { confined, why := judgedConfined("agents", h, true, now) return judgeRoot(rootFacts{Machine: "anchor", AgentNamed: true, Confined: confined, ConfinedWhy: why}, now) } if v := judged(statement(link.StateHealthy, "")); !v.Free { t.Fatalf("the control: a complete healthy verdict, fresh: %+v", v) } pending := statement(link.StateUnknown, link.ReasonRootPending+": the search runs") if rootVerdictKind("agents", pending, true, now) != verdictPending { t.Fatal("the statement is not one the quiet window counts as waiting for the search") } if v := judged(pending); v.Free { t.Errorf("a machine whose setuid search is pending was judged root-free: %+v", v) } } // The confirmation review of 2026-10-09, on #154 beside ADR 0266: D-root keeps ADR 0266's quiet window — nothing // raised while the one thing unjudged is the setuid search, within searchQuietFor — while the root-free verb // still answers the machine not free; and D-root's condition has a key of its own, apart from DA's. func TestRootNotFreeIsQuietWhileTheFirstSearchRunsAndKeyedApartFromDA(t *testing.T) { entries := []inventory.Entry{{Manifest: catalogue.Manifest{Module: "telegram", RunsAs: "telegram"}, On: []string{"anchor"}}} r := &rootReader{entries: entries, heard: map[string]map[string]map[string]bool{}, confined: func(context.Context, string, time.Time) (bool, bool, string, error) { return true, false, "the agent account agents is not judged: the search for setuid programs runs", nil }, settings: func(context.Context, string, string) ([]catalogue.Layer, error) { return nil, nil }, quiet: func(context.Context, string, time.Time) bool { return true }} trusted := trustedMachines(entries) if got := rootObservations(context.Background(), r, trusted, rootNow); len(got) != 0 { t.Errorf("raised while the search runs: %+v", got) } if v := judgeRootFree(context.Background(), r, []string{"anchor"}, rootNow)[0]; v.Free || !v.Quiet { t.Errorf("root-free while the search runs: %+v", v) } // Quiet hides nothing else: the login shell served as well is said. r.heard = map[string]map[string]map[string]bool{loginShellSeat: {"execute": {"anchor": true}}} if got := rootObservations(context.Background(), r, trusted, rootNow); len(got) != 1 { t.Errorf("a second failure was kept quiet: %+v", got) } // Past searchQuietFor, said. r.heard, r.quiet = map[string]map[string]map[string]bool{}, func(context.Context, string, time.Time) bool { return false } got := rootObservations(context.Background(), r, trusted, rootNow) if len(got) != 1 { t.Fatalf("a search past searchQuietFor was not said: %+v", got) } // One key per judgement: DA's is machine..agent-root, this one its own. da := conditions.Observation{Scope: conditions.ScopeMachine, ID: "anchor", Token: "agent-root", Machine: "anchor"} if got[0].Key() == da.Key() { t.Errorf("D-root and DA share the key %s", da.Key()) } }