package main import ( "context" "encoding/json" "errors" "fmt" "strings" "testing" "time" "github.com/novox/mesh-controller/internal/broker" "github.com/novox/mesh-controller/internal/conditions" "github.com/novox/mesh-controller/internal/link" "github.com/novox/mesh-controller/internal/secrets" ) const typed = "123456789:AAEhBP0av28P4XFQnIuR-o-7Xnz1kkUzW3g" // aDesk is the desk path with a prompt the test answers as the operator would, and what it was asked kept. func aDesk(t *testing.T, answer func(args map[string]any) (json.RawMessage, error)) (deskGive, *[]string, *[]link.HandAct, *[]map[string]any) { t.Helper() var accepted []string var acts []link.HandAct var asked []map[string]any return deskGive{ declares: func(module, name string) error { if module != "telegram" || name != "telegram-token" { return errors.New(module + " does not declare " + name + " as an own secret") } return nil }, ask: func(machine string, args map[string]any) (json.RawMessage, error) { asked = append(asked, args) return answer(args) }, accept: func(value string) (bool, error) { accepted = append(accepted, value); return false, nil }, record: func(a link.HandAct) error { acts = append(acts, a); return nil }, }, &accepted, &acts, &asked } func sealedTo(t *testing.T, value string) func(args map[string]any) (json.RawMessage, error) { return func(args map[string]any) (json.RawMessage, error) { sealed, err := secrets.Seal(args["seal_to"].(string), []byte(value+"\n")) if err != nil { t.Fatal(err) } raw, _ := json.Marshal(map[string]any{"sealed": sealed}) return raw, nil } } // novox/hq ADR 0259 §10: the value typed at the desk is sealed as `secret accept` seals it, and is in no // answer, no prompt argument and no act recorded. func TestASecretGivenAtTheDeskIsSealedAndSaidNowhere(t *testing.T) { d, accepted, acts, asked := aDesk(t, sealedTo(t, typed)) words, err := d.give("anchor", "telegram", "telegram-token", "laptop") if err != nil { t.Fatal(err) } if len(*accepted) != 1 || (*accepted)[0] != typed { t.Fatalf("the value sealed is not what was typed, its line ending taken off") } if len(*acts) != 1 || (*acts)[0].Verb != "secret accept" || (*acts)[0].Cause != "given-at-the-desk" || !strings.Contains((*acts)[0].Why, "at the desk on laptop") { t.Errorf("the act: %+v", *acts) } raw, _ := json.Marshal(struct { Words string Acts []link.HandAct Asked []map[string]any }{words, *acts, *asked}) if strings.Contains(string(raw), typed) || strings.Contains(string(raw), "AAEhBP0") { t.Fatal("the value appears in what was said, asked or recorded") } if !strings.Contains(words, "push anchor") || !strings.Contains(words, "given at the desk on laptop") { t.Errorf("%q", words) } if p := (*asked)[0]; p["seal_to"] == "" || p["timeout_seconds"] != deskPromptWithin { t.Errorf("the prompt was asked %v", p) } } func TestNothingIsAskedForASecretTheMeshWouldRefuse(t *testing.T) { for _, c := range [][2]string{{"telegram", "chat-id"}, {"nobody", "telegram-token"}} { d, accepted, _, asked := aDesk(t, sealedTo(t, typed)) if _, err := d.give("anchor", c[0], c[1], "laptop"); err == nil || !strings.Contains(err.Error(), "nobody was asked") { t.Errorf("%v: %v", c, err) } if len(*asked) != 0 || len(*accepted) != 0 { t.Errorf("%v: the operator was asked anyway", c) } } d, _, _, _ := aDesk(t, sealedTo(t, typed)) if _, err := d.give("anchor", "telegram", "telegram-token", ""); err == nil { t.Error("no desk was refused nowhere") } } func TestADismissedEmptyLateOrForeignAnswerTakesNothing(t *testing.T) { for want, answer := range map[string]func(map[string]any) (json.RawMessage, error){ "not answered within 25 seconds": func(map[string]any) (json.RawMessage, error) { return json.RawMessage(`{"cancelled":true,"timed_out":true}`), nil }, "was dismissed": func(map[string]any) (json.RawMessage, error) { return json.RawMessage(`{"cancelled":true}`), nil }, "answered empty": sealedTo(t, " "), "not sealed to this call": func(map[string]any) (json.RawMessage, error) { other, _, _ := secrets.Keypair() sealed, _ := secrets.Seal(other, []byte(typed)) raw, _ := json.Marshal(map[string]any{"sealed": sealed}) return raw, nil }, "could not be asked": func(map[string]any) (json.RawMessage, error) { return nil, errors.New("no session answers") }, } { d, accepted, acts, _ := aDesk(t, answer) _, err := d.give("anchor", "telegram", "telegram-token", "laptop") if err == nil || !strings.Contains(err.Error(), want) || strings.Contains(err.Error(), typed) { t.Errorf("want %q, got %v", want, err) } if len(*accepted) != 0 || len(*acts) != 0 { t.Errorf("%s: something was taken or recorded", want) } } } func TestTheGiveVerbRunsTheDeskPathAndTheControllerMayAskTheDesk(t *testing.T) { argv, err := argvFor("give", map[string]any{"node": "anchor", "module": "telegram", "secret": "telegram-token", "at": "laptop"}) if err != nil || strings.Join(argv, " ") != "secret ask anchor telegram telegram-token --at laptop" { t.Fatalf("%v %v", argv, err) } if _, err := argvFor("give", map[string]any{"node": "anchor", "module": "telegram", "secret": "telegram-token"}); err == nil { t.Error("give without a desk was taken") } // secret-ask is the same line, with the desk the module's machine unless named (novox/hq ADR 0277). argv, err = argvFor("secret-ask", map[string]any{"node": "anchor", "module": "telegram", "secret": "telegram-token"}) if err != nil || strings.Join(argv, " ") != "secret ask anchor telegram telegram-token" { t.Fatalf("%v %v", argv, err) } argv, err = argvFor("secret-ask", map[string]any{"node": "anchor", "module": "telegram", "secret": "telegram-token", "at": "laptop"}) if err != nil || strings.Join(argv, " ") != "secret ask anchor telegram telegram-token --at laptop" { t.Fatalf("%v %v", argv, err) } perms, err := broker.PermissionsFor(broker.Principal{Kind: broker.KindController}) if err != nil { t.Fatal(err) } found := false for _, p := range perms.Publish { found = found || p == "mesh.seat.node-launcher.tool.secret.*" } if !found { t.Error("the controller may not ask the desk's prompt") } } // The review of 2026-10-09 (M4): the desk's prompt says who asks in words the caller does not choose — the // controller, which the bus alone lets ask it — and what for, from names the controller checked; the prompt // carries no free text of the caller's. func TestThePromptIsAskedByNameNeverByWordsTheCallerChose(t *testing.T) { d, _, _, asked := aDesk(t, sealedTo(t, typed)) if _, err := d.give("anchor", "telegram", "telegram-token", "laptop"); err != nil { t.Fatal(err) } p := (*asked)[0] if p["module"] != "telegram" || p["secret"] != "telegram-token" || p["node"] != "anchor" { t.Errorf("the prompt was not asked by name: %v", p) } for _, free := range []string{"prompt", "message"} { if _, there := p[free]; there { t.Errorf("the prompt carries the caller's %s: %v", free, p) } } } // Every value given for a module's own secret is announced as a condition, on every channel (the review of // 2026-10-09, M4): a bot token changed by somebody else is a channel that now answers for them. func TestAValueGivenAtTheDeskIsAnnounced(t *testing.T) { d, _, _, _ := aDesk(t, sealedTo(t, typed)) var said []string d.announce = func(node, module, name, how string) error { said = append(said, node+" "+module+" "+name+" "+how) return nil } if _, err := d.give("anchor", "telegram", "telegram-token", "laptop"); err != nil { t.Fatal(err) } if len(said) != 1 || !strings.Contains(said[0], "anchor telegram telegram-token") || !strings.Contains(said[0], "laptop") { t.Fatalf("announced %v", said) } o := secretGivenObservation("anchor", "telegram", "telegram-token", "at the desk on laptop", time.Date(2026, 10, 9, 12, 3, 0, 0, time.UTC)) if o.Severity != conditions.Urgent || !strings.Contains(o.Explanation, "telegram token") || len(o.Actions) == 0 || o.Key() == "" { t.Errorf("the announcement %+v", o) } if strings.Contains(o.Summary+o.Explanation+o.Said, typed) { t.Error("the announcement carries the value") } } // **The secret-given condition says itself in its own plain words** (hq issue 359): the words it carries pass // the plain rule, from the controller's terminal and from a desk, so the keeper keeps them — they once held a // clock time, and the operator read "Novox needs a look … a problem it calls secret given" instead. Its // severity and its answer stay: it is heard on every channel, and silenced by the operator. func TestTheSecretGivenConditionSaysItselfInPlainWords(t *testing.T) { at := time.Date(2026, 10, 9, 23, 32, 0, 0, time.Local) for _, how := range []string{"at the controller's terminal", "at the desk on laptop"} { o := secretGivenObservation("anchor", "telegram", "telegram-token", how, at) w := conditions.Words{Headline: o.Headline, Explanation: o.Explanation, Resolved: o.Resolved, Needs: o.Needs, Actions: o.Actions} if why, ok := conditions.PlainWords(w, o.Machine); !ok { t.Fatalf("given %s, the words are not plain: %s", how, why) } k, _ := withConditionsInMemory(t) c, err := k.Observe(t.Context(), o) if err != nil { t.Fatal(err) } if c.Headline != "New secret given for telegram on anchor" || c.Severity != conditions.Urgent || !strings.HasPrefix(c.Explanation, conditions.NeedsYou+" silence this") || !strings.Contains(c.Explanation, "telegram token of telegram on anchor was given "+how) || len(c.Actions) != 1 || c.Actions[0].Label != "Silence for a week" { t.Errorf("given %s, the keeper said %q / %q (%s, %v)", how, c.Headline, c.Explanation, c.Severity, c.Actions) } if strings.Contains(c.Headline+c.Explanation+c.Resolved+c.Needs, typed) { t.Error("the words carry the value") } } // A long module name keeps the headline and the resolved line within their bounds. for _, module := range []string{"a-module-with-a-rather-long-name-indeed", "a-module-with-a-name-so-long-that-no-headline-could-ever-hold-it"} { o := secretGivenObservation("anchor", module, "api-key", "at the controller's terminal", at) if why, ok := conditions.PlainWords(conditions.Words{Headline: o.Headline, Explanation: o.Explanation, Resolved: o.Resolved, Needs: o.Needs, Actions: o.Actions}, o.Machine); !ok { t.Errorf("the module %s: %s", module, why) } } } // The bus lets the controller alone ask the desk's prompt (the review of 2026-10-09, M4): the runtime, which // carries every agent's calls, and a person granted every tool are denied it, however wide their grant. func TestOnlyTheControllerMayAskTheDesksPrompt(t *testing.T) { for _, p := range []broker.Principal{ {Kind: broker.KindNodeTools, Node: "laptop"}, {Kind: broker.KindPerson, Module: "operator", Invokes: []string{"*"}}, {Kind: broker.KindModule, Node: "laptop", Module: "lab", Invokes: []string{"seat:node-launcher.secret"}}, } { perms, err := broker.PermissionsFor(p) if err != nil { t.Fatal(err) } for _, subject := range []string{"mesh.seat.node-launcher.tool.secret.laptop", "mesh.seat.node-launcher.tool.secret", "mesh.mod.rofi.tool.node-launcher.secret", "mesh.mod.rofi.tool.node-launcher.secret.laptop"} { if broker.MayPublish(perms, subject) { t.Errorf("%s may publish %s", p.Username(), subject) } } } perms, _ := broker.PermissionsFor(broker.Principal{Kind: broker.KindController}) if !broker.MayPublish(perms, "mesh.seat.node-launcher.tool.secret.laptop") { t.Error("the controller may not ask the desk's prompt") } } // A value for a secret comes from the terminal or the desk, never through a verb (the review of 2026-10-09, // M4): `secret accept` with a value, run for a verb, is refused before anything is read. func TestASecretValueIsNeverAcceptedThroughAVerb(t *testing.T) { t.Setenv(verbVar, "mesh-controller.command") for _, args := range [][]string{ {"accept", "anchor", "telegram", "telegram-token", "--from", "/dev/null"}, {"accept", "anchor", "app", "db", "--from", "/dev/null", "--provider", "store"}, } { err := secretCommand(context.Background(), args) if err == nil || !strings.Contains(err.Error(), "never through a verb") { t.Errorf("%v: %v", args, err) } } } // The `give` and `secret-ask` verbs' own line passes the terminal-only rule of ADR 0266, and no `secret accept` // does: a value, a file, a provider or an extra word is still the terminal's alone (novox/hq ADR 0277). func TestOnlyTheGiveLinePassesTheTerminalRuleForSecrets(t *testing.T) { for _, argv := range [][]string{ {"secret", "ask", "anchor", "telegram", "telegram-token", "--at", "laptop"}, {"secret", "ask", "anchor", "telegram", "telegram-token"}, } { if err := terminalOnly(argv); err != nil { t.Errorf("%v refused: %v", argv, err) } } for _, argv := range [][]string{ {"secret", "accept", "anchor", "telegram", "telegram-token", "--at-desk", "laptop"}, {"secret", "ask", "anchor", "telegram", "telegram-token", "--from", "/tmp/x"}, {"secret", "ask", "anchor", "telegram", "telegram-token", "--at", "laptop", "--local"}, {"secret", "ask", "anchor", "telegram", "--at", "laptop"}, {"secret", "accept", "anchor", "telegram", "telegram-token"}, {"secret", "accept", "anchor", "telegram", "telegram-token", "--from", "/tmp/x"}, {"secret", "accept", "anchor", "telegram", "telegram-token", "--at-desk", "laptop", "--local"}, {"secret", "accept", "anchor", "telegram", "--provider", "--at-desk", "laptop"}, {"secret", "export", "anchor", "telegram", "telegram-token", "--at-desk", "laptop"}, } { if err := terminalOnly(argv); err == nil { t.Errorf("%v passed the terminal rule", argv) } } } // The confirmation review of 2026-10-09, N1-give: a desk's prompt is answered over the desk machine's bus, and // on a desk machine agents run as the operator, who holds its runtime's credential — so a trusted party's // secret (a module running as an account of its own: the Telegram bot's token) is never taken at a desk. // Refused before anybody is asked to type, whoever called, naming the terminal's line. func TestATrustedPartysSecretIsNeverTakenAtADesk(t *testing.T) { d, accepted, acts, asked := aDesk(t, sealedTo(t, typed)) d.trusted = func(module string) (bool, error) { return module == "telegram", nil } _, err := d.give("anchor", "telegram", "telegram-token", "laptop") if err == nil || !strings.Contains(err.Error(), "controller's terminal alone") || !strings.Contains(err.Error(), "secret accept anchor telegram telegram-token") { t.Fatalf("a trusted party's secret was taken at the desk, or refused without the line: %v", err) } if len(*asked)+len(*accepted)+len(*acts) != 0 { t.Errorf("asked %v, accepted %d, recorded %v", *asked, len(*accepted), *acts) } d.trusted = func(string) (bool, error) { return false, errors.New("the store did not answer") } if _, err := d.give("anchor", "telegram", "telegram-token", "laptop"); err == nil || len(*asked) != 0 { t.Errorf("a module not known to be untrusted was asked at the desk: %v", err) } } // And who may answer the desk's prompt at all: only the runtime of the machine it is asked on, carrying the // launcher that holds the seat there — never the controller, another machine's runtime, or a module's own // account (the confirmation review of 2026-10-09, N1-give). func TestOnlyTheDeskMachinesLauncherMayAnswerItsPrompt(t *testing.T) { launcher := broker.Declared{Module: "rofi", Holds: []broker.Seat{{Name: "node-launcher", Scope: "node", Serves: []string{"run", "secret"}}}} subject := "mesh.seat.node-launcher.tool.secret.laptop" for _, c := range []struct { p broker.Principal answers bool }{ {broker.Principal{Kind: broker.KindNodeTools, Node: "laptop", Module: broker.RuntimeModule, Carries: []broker.Declared{launcher}}, true}, {broker.Principal{Kind: broker.KindNodeTools, Node: "anchor", Module: broker.RuntimeModule, Carries: []broker.Declared{launcher}}, false}, {broker.Principal{Kind: broker.KindController}, false}, {broker.Principal{Kind: broker.KindModule, Node: "laptop", Module: "lab"}, false}, {broker.Principal{Kind: broker.KindNode, Node: "laptop"}, false}, } { perms, err := broker.PermissionsFor(c.p) if err != nil { t.Fatal(err) } if got := broker.MaySubscribe(perms, subject); got != c.answers { t.Errorf("%s may answer %s: %v, want %v", c.p.Username(), subject, got, c.answers) } } } // N1-give at the controller's terminal (the confirmation review of 2026-10-09): a trusted party's secret is // announced before it is kept, and not kept when the announcement fails; another module's is kept first and // a failed announcement is said, not undone. func TestATrustedPartysSecretGivenAtTheTerminalIsAnnouncedBeforeItIsKept(t *testing.T) { var order []string announce := func(fail bool) func() error { return func() error { order = append(order, "announce") if fail { return errors.New("no channel") } return nil } } keep := func() (bool, error) { order = append(order, "keep"); return false, nil } order = nil if _, unannounced, err := keepGiven(true, announce(false), keep); err != nil || unannounced != nil || strings.Join(order, ",") != "announce,keep" { t.Errorf("trusted: %v %v, order %v; want announced, then kept", unannounced, err, order) } order = nil if _, _, err := keepGiven(true, announce(true), keep); err == nil || strings.Join(order, ",") != "announce" { t.Errorf("trusted, announcement failed: %v, order %v; want refused and nothing kept", err, order) } order = nil if _, unannounced, err := keepGiven(false, announce(true), keep); err != nil || unannounced == nil || strings.Join(order, ",") != "keep,announce" { t.Errorf("not trusted: %v %v, order %v; want kept, then the failed announcement said", unannounced, err, order) } order = nil failing := func() (bool, error) { order = append(order, "keep"); return false, errors.New("store away") } if _, _, err := keepGiven(false, announce(false), failing); err == nil || strings.Join(order, ",") != "keep" { t.Errorf("not trusted, keep failed: %v, order %v; want refused and nothing announced", err, order) } } // A machine the mesh does not know, as the secret's or as the desk, is refused before anybody is asked to type. func TestAGiveNamingAMachineTheMeshDoesNotKnowAsksNobody(t *testing.T) { for _, unknown := range []string{"elsewhere", "nodesk"} { d, accepted, acts, asked := aDesk(t, func(map[string]any) (json.RawMessage, error) { t.Fatal("the desk was asked") return nil, nil }) d.known = func(machine string) error { if machine == unknown { return errors.New("no node " + machine) } return nil } node, desk := "anchor", "laptop" if unknown == "elsewhere" { node = unknown } else { desk = unknown } _, err := d.give(node, "telegram", "telegram-token", desk) if err == nil || !strings.Contains(err.Error(), "nobody was asked") || !strings.Contains(err.Error(), unknown) { t.Errorf("%s: %v", unknown, err) } if len(*accepted)+len(*acts)+len(*asked) != 0 { t.Errorf("%s: something happened: %v %v %v", unknown, *accepted, *acts, *asked) } } } // novox/hq ADR 0277: the prompt names who asked — the controller's word on the bus's caller, cut to a name's // characters — and never a word the caller chose: there is no argument for it. func TestThePromptNamesWhoAskedFromTheBussWordAlone(t *testing.T) { d, _, acts, asked := aDesk(t, sealedTo(t, typed)) d.askedBy = "g14/claude-code, through the mesh-controller seat" if _, err := d.give("anchor", "telegram", "telegram-token", "laptop"); err != nil { t.Fatal(err) } if got := (*asked)[0]["asked_by"]; got != "g14/claude-code" { t.Errorf("asked_by %q", got) } if len(*acts) != 1 || !strings.Contains((*acts)[0].Why, "asked by g14/claude-code") { t.Errorf("the record: %+v", *acts) } for in, want := range map[string]string{ "jochen at a shell on novox": "jochen at a shell on novox", "laptop/agent": "laptop/agent", "Your bank asks\nType your PIN, now": "Your bank asks-Type your PIN", "": "an unnamed caller", "x": "an unnamed caller", strings.Repeat("a", 100): strings.Repeat("a", 80), "--prompt, something else": "an unnamed caller", } { if got := askedByName(in); got != want { t.Errorf("askedByName(%q) = %q, want %q", in, got, want) } } // The verb's schema has no argument that reaches the prompt's words. for _, verb := range []string{"give", "secret-ask"} { for _, free := range []string{"asked_by", "prompt", "message", "value", "from"} { if _, err := argvFor(verb, map[string]any{"node": "anchor", "module": "telegram", "secret": "telegram-token", "at": "laptop", free: "x"}); err == nil { t.Errorf("%s takes %s", verb, free) } } } } // An ask for a secret is bounded before anybody is asked to type (ADR 0277): the record refuses it, nothing is // asked; and how every ask ends is recorded. func TestASecretAskIsBoundedAndItsEndRecorded(t *testing.T) { d, accepted, _, asked := aDesk(t, sealedTo(t, typed)) var ended []string d.open = func(node, module, name, desk string) (int64, error) { return 7, nil } d.end = func(id int64, outcome string) error { ended = append(ended, fmt.Sprintf("%d %s", id, outcome)) return nil } if _, err := d.give("anchor", "telegram", "telegram-token", "laptop"); err != nil { t.Fatal(err) } d.open = func(node, module, name, desk string) (int64, error) { return 0, errors.New("an ask for telegram-token of telegram on anchor is still open") } _, err := d.give("anchor", "telegram", "telegram-token", "laptop") if err == nil || !strings.Contains(err.Error(), "nobody was asked to type anything") || !strings.Contains(err.Error(), "still open") { t.Errorf("a second ask: %v", err) } if len(*asked) != 1 || len(*accepted) != 1 { t.Errorf("asked %d, accepted %d", len(*asked), len(*accepted)) } d.open = func(node, module, name, desk string) (int64, error) { return 8, nil } d.ask = func(string, map[string]any) (json.RawMessage, error) { return json.RawMessage(`{"cancelled":true}`), nil } if _, err := d.give("anchor", "telegram", "telegram-token", "laptop"); !errors.Is(err, errNothingGiven) { t.Errorf("a dismissed prompt: %v", err) } if strings.Join(ended, "; ") != "7 given; 8 dismissed" { t.Errorf("ended: %v", ended) } } // A secret ask cannot be turned into a secret read: the line carries no value, the answer carries none, and every // other `secret` line is the terminal's. func TestASecretAskIsNeverASecretRead(t *testing.T) { t.Setenv(verbVar, "mesh-controller.secret-ask") for _, args := range [][]string{ {"ask", "anchor", "telegram", "telegram-token", "the-value"}, {"ask", "anchor", "telegram"}, {"ask", "anchor", "telegram", "telegram-token", "--from", "/dev/null"}, } { if err := secretCommand(context.Background(), args); err == nil { t.Errorf("secret %v was taken", args) } } for _, args := range [][]string{{"recover", "anchor", "telegram", "telegram-token"}, {"export"}} { if err := terminalOnly(append([]string{"secret"}, args...)); err == nil { t.Errorf("secret %v passed the terminal rule", args) } } }