Every value an approval sets is in the note, whole where the sender is proven; the note is kept whole (issue 383)
mesh/merge-gate head bb5a342863ee merged into trunk 9c166398f204 — pass: builds claude-code, desk-channel, mesh-knowledge, messenger, telegram → ace, g14, …
mesh/repo-check head bb5a342863ee merged into trunk 9c166398f204 — pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-…
mesh/delivery-group group fix/383-an-ask-reads-at-a-glance checking: 1 of 2 member(s) ready
mesh/delivery superseded: a newer head of the same pull request

The review of mesh-controller #304: a proposal of more than three keys showed only the names of the rest,
and its Details could carry no value, so Approve bound values the operator never saw. The router now
shows an ask's WholeDetails in the note in place of its Details on a kind that proves who answers, held
to the secret rule alone, and refuses an ask whose whole details carry a secret's shape. Telegram keeps
the note whole when a message is too long, cutting the body first and saying so, and escapes the note
as every other part. SDK at mesh-sdk f474544.
This commit is contained in:
jochen
2026-10-13 17:16:50 +02:00
parent eadf301f9f
commit bb5a342863
17 changed files with 137 additions and 40 deletions
+26 -9
View File
@@ -172,22 +172,39 @@ func (h *Holder) openAsk(r *Record) *AskRecord {
// rule, is shown. The ask's state does not matter: a message edited to say how the ask ended still shows what
// was approved.
func (h *Holder) wholeOf(r *Record, kind string) string {
if a := h.provenAsk(r, kind); a != nil {
return a.Whole
}
return ""
}
// wholeDetailsOf is an ask's Details with their values whole (asks.Ask.WholeDetails), shown in the note in place of
// its Details where wholeOf would show its whole words: every value an approval sets, so the operator reads exactly
// what they approve (the review of mesh-controller #304). Empty elsewhere.
func (h *Holder) wholeDetailsOf(r *Record, kind string) string {
if a := h.provenAsk(r, kind); a != nil {
return a.WholeDetails
}
return ""
}
// provenAsk is the ask of an ask's own record when the kind proves who answers and was given the ask's answers;
// nil otherwise.
func (h *Holder) provenAsk(r *Record, kind string) *asks.Ask {
if r == nil || r.Origin != "ask" || r.Ask == "" || kind == "" || h.Bench == nil {
return ""
return nil
}
e := h.askOf(r.Ask)
if e == nil || e.rec.Ask.Whole == "" {
return ""
}
if !h.Bench.Promises(kind, "verified-sender") {
return ""
if e == nil || !h.Bench.Promises(kind, "verified-sender") {
return nil
}
for _, tk := range e.rec.Tickets {
if tk.Kind == kind {
return e.rec.Ask.Whole
a := e.rec.Ask
return &a
}
}
return ""
return nil
}
// carriers are the kinds that can carry an answer of a level now: ready, offering a choice, and for
@@ -474,7 +491,7 @@ func (h *Holder) TakeAsk(verb, asker string, body json.RawMessage) error {
// The whole words are shown only where the sender is proven (wholeOf), so a path or an address may stand
// in them; a secret's shape leaves the mesh nowhere, and an ask carrying one is refused as a whole, never
// shown with its explanation alone (novox/hq issue 383).
if refusal, ok := CheckSecret(a.Whole); !ok {
if refusal, ok := CheckSecret(a.Whole + "\n" + a.WholeDetails); !ok {
h.refuseAsk(asker, a, "its whole words carry "+refusal.String()+", which may not leave the mesh on any channel: withhold it")
return nil
}
+8 -2
View File
@@ -885,11 +885,17 @@ func (h *Holder) sayOn(r *Record, what, kind string) Message {
note = append(note, line)
}
}
for _, line := range strings.Split(strings.TrimSpace(rec.Ask.Details), "\n") {
// On a kind that proves who answers, the Details whole (issue 383): every value an approval sets, held to
// the secret rule alone, as the whole words are.
details, passes := rec.Ask.Details, Check
if wd := h.wholeDetailsOf(r, kind); wd != "" {
details, passes = wd, CheckSecret
}
for _, line := range strings.Split(strings.TrimSpace(details), "\n") {
if line = strings.TrimSpace(line); line == "" {
continue
}
if refusal, ok := Check(line); !ok {
if refusal, ok := passes(line); !ok {
line = "(a line carrying " + refusal.What + " is withheld here)"
}
note = append(note, line)
@@ -1,6 +1,7 @@
package main
import (
"fmt"
"strings"
"testing"
"time"
@@ -153,3 +154,57 @@ func TestAnAskWhoseWholeWordsCarryASecretIsRefused(t *testing.T) {
}
}
}
// The review of mesh-controller #304: a proposal of five keys, one of them 300 characters long, shows two keys at
// a glance; every key and every value, whole, is in the note on a kind that proves who answers, so the operator can
// read exactly what Approve sets. The desk, which proves nothing, shows them masked.
func TestEveryKeyAndItsWholeValueIsInTheNoteWhereTheSenderIsProven(t *testing.T) {
h, tg, _, c, store, _ := newRouter(t)
link(h, store, c, "42", c.now().Add(-2*time.Hour))
var long strings.Builder
long.WriteString("library=/storage")
for i := 0; long.Len() < 300; i++ {
long.WriteString(fmt.Sprintf("/album-%02d", i))
}
value := long.String()[:300]
keys := map[string]string{"a": "1", "b": "2", "c": "3", "d": "4", "shares": value}
masked := map[string]string{"a": "1", "b": "2", "c": "3", "d": "4", "shares": "library=‹path›"}
lines := func(values map[string]string) string {
var out []string
for _, k := range []string{"a", "b", "c", "d", "shares"} {
out = append(out, "+ "+k+": "+values[k])
}
return strings.Join(out, "\n")
}
a := proposalAsk(c, "s-9")
a.Explanation = "Changes 5 setting(s) of mounts on shanks.\n+ a: 1\n+ b: 2\nand 3 more, each whole below: c, d, shares"
a.Whole = ""
a.Details = "Fingerprint 1234 5678 9abc def0 1234 5678.\n" + lines(masked)
a.WholeDetails = "Fingerprint 1234 5678 9abc def0 1234 5678.\n" + lines(keys)
submit(t, h, "mesh-controller", a)
settle(h, c)
if len(tg.sends) != 1 {
t.Fatalf("telegram got %d messages", len(tg.sends))
}
phone := tg.sends[0]
for k, v := range keys {
if !strings.Contains(phone.Note, "+ "+k+": "+v) {
t.Errorf("the phone's note lacks %s whole:\n%s", k, phone.Note)
}
}
if strings.Contains(phone.Body, "/storage") || strings.Count(phone.Body, "\n") > 3 {
t.Errorf("the body is not the glance:\n%s", phone.Body)
}
desk := h.sayOn(h.open["ask.mesh-controller.s-9"], "raised", "desktop")
if strings.Contains(desk.Text(), "/storage") || !strings.Contains(desk.Note, "+ shares: library=‹path›") {
t.Errorf("the desk shows a value whole, or not every key:\n%s", desk.Note)
}
// A secret's shape in the whole details refuses the ask as a whole.
b := proposalAsk(c, "s-10")
b.WholeDetails = "+ token: ghp_abcdefghijklmnopqrstuvwxyz0123456789"
submit(t, h, "mesh-controller", b)
settle(h, c)
if _, kept := store.asks["mesh-controller.s-10"]; kept || len(tg.sends) != 1 {
t.Errorf("an ask whose whole details carry a secret was taken")
}
}