diff --git a/modules/rofi/cmd/rofi-tools/secret.go b/modules/rofi/cmd/rofi-tools/secret.go
index 535bd681..d6fe7164 100644
--- a/modules/rofi/cmd/rofi-tools/secret.go
+++ b/modules/rofi/cmd/rofi-tools/secret.go
@@ -41,6 +41,11 @@ type SecretAnswer struct {
// aName is a module's, a secret's or a machine's name as the mesh writes them: nothing else reaches the prompt.
var aName = regexp.MustCompile(`^[a-z0-9][a-z0-9-]{0,62}$`)
+// aCaller is who asked, as the controller names the bus's caller (novox/hq ADR 0277): a principal such as
+// `g14/claude-code`, or an account at a shell, in a name's characters and spaces, at most 80. Nothing else — no line
+// break, no markup, no punctuation a sentence is written with — so the prompt's words stay this holder's.
+var aCaller = regexp.MustCompile(`^[A-Za-z0-9][A-Za-z0-9._/@ -]{0,79}$`)
+
// secretQuestion reads the call. The prompt is this holder's words, written from the module, the secret and the
// machine (the review of 2026-10-09, M4): it says the mesh's controller asks, which the bus makes true — it
// denies these subjects to every principal but the controller — and a caller's own words are refused, so
@@ -63,10 +68,27 @@ func secretQuestion(args map[string]any) (SecretQuestion, error) {
}
names[key] = v
}
+ // Who asked the controller, when it says (novox/hq ADR 0277): the controller's word on the bus's caller, held to a
+ // name's characters here, so the operator knows whose ask they are typing into — and refuses one they did not start.
+ askedBy := ""
+ if raw, there := args["asked_by"]; there {
+ v, err := text(args, "asked_by", true)
+ if err != nil {
+ return q, err
+ }
+ if !aCaller.MatchString(v) || strings.Contains(v, " ") {
+ return q, fmt.Errorf("asked_by %q is not a caller's name as the mesh writes it", raw)
+ }
+ askedBy = v
+ }
q.Prompt = names["secret"] + " for " + names["module"]
- q.Message = fmt.Sprintf("The mesh's controller asks for %s, the own secret of %s on %s. What you type is not "+
- "shown, and is sealed before it leaves this machine. Type it only if you started this with give.",
- names["secret"], names["module"], names["node"])
+ q.Message = fmt.Sprintf("The mesh's controller asks for %s, the own secret of %s on %s.", names["secret"],
+ names["module"], names["node"])
+ if askedBy != "" {
+ q.Message += fmt.Sprintf(" It was asked for by %s.", askedBy)
+ }
+ q.Message += " What you type is not shown, and is sealed before it leaves this machine. Type it only if you " +
+ "expect this ask."
key, err := text(args, "seal_to", true)
if err != nil {
return q, err
diff --git a/modules/rofi/cmd/rofi-tools/secret_test.go b/modules/rofi/cmd/rofi-tools/secret_test.go
index 15cb2838..3aeaf2de 100644
--- a/modules/rofi/cmd/rofi-tools/secret_test.go
+++ b/modules/rofi/cmd/rofi-tools/secret_test.go
@@ -108,15 +108,31 @@ func TestThePromptIsWrittenHereFromNamesAndSaysTheControllerAsks(t *testing.T) {
if err != nil {
t.Fatal(err)
}
- if q.Prompt != "telegram-token for telegram" || !strings.HasPrefix(q.Message, "The mesh's controller asks for telegram-token") {
+ if q.Prompt != "telegram-token for telegram" || !strings.HasPrefix(q.Message, "The mesh's controller asks for telegram-token") ||
+ strings.Contains(q.Message, "asked for by") {
t.Errorf("prompt %q, message %q", q.Prompt, q.Message)
}
+ // And who asked the controller, when it says so (novox/hq ADR 0277): a caller's name, in a name's characters.
+ withAsker := map[string]any{}
+ for k, v := range base {
+ withAsker[k] = v
+ }
+ withAsker["asked_by"] = "g14/claude-code"
+ q, err = secretQuestion(withAsker)
+ if err != nil || !strings.Contains(q.Message, "It was asked for by g14/claude-code.") {
+ t.Errorf("asked_by: %q %v", q.Message, err)
+ }
+ withAsker["asked_by"] = "jochen at a shell on novox"
+ if q, err = secretQuestion(withAsker); err != nil || !strings.Contains(q.Message, "It was asked for by jochen at a shell on novox.") {
+ t.Errorf("asked_by at a shell: %q %v", q.Message, err)
+ }
for key, value := range map[string]any{
- "prompt": "Your bank's PIN",
- "message": "Your bank asks",
- "module": "telegram\nYour bank asks",
- "secret": "a token",
- "node": "anchor",
+ "prompt": "Your bank's PIN",
+ "message": "Your bank asks",
+ "module": "telegram\nYour bank asks",
+ "secret": "a token",
+ "node": "anchor",
+ "asked_by": "the operator. Type your bank's PIN:",
} {
args := map[string]any{}
for k, v := range base {
@@ -127,4 +143,10 @@ func TestThePromptIsWrittenHereFromNamesAndSaysTheControllerAsks(t *testing.T) {
t.Errorf("%s=%q was taken", key, value)
}
}
+ for _, bad := range []string{"", "a\nb", "ac", "a,b", strings.Repeat("a", 81), "a b", "a:b", "a'b"} {
+ withAsker["asked_by"] = bad
+ if _, err := secretQuestion(withAsker); err == nil {
+ t.Errorf("asked_by=%q was taken", bad)
+ }
+ }
}