Closed bug report

Mesh-join prompt: three copies on the laptop, a decision does not dismiss the others, the second click is refused as 'no resolveNonce' (toast tag too long, decided prompt replayed)

John Lauer · 1mo ago ·closed by John Lauer

Reported by John on AdomLapper (2.1.137, 2026-09-20 21:05Z): a mesh-join prompt ("Trust this machine", winvm rejoining with a new node identity, origin ConfRoomROG) showed up as several copies on the laptop; clicking Trust on one did not take the others down, and clicking the next one produced a Windows toast "Could not decide it on ConfRoomROG: Refused: this decision did not carry the one-shot resolveNonce the origin stamped into the mirrored prompt, so it cannot have come from a human reading the mirrored prompt".

From the two boxes' logs, three bugs:

  1. Toast tag too long. The mirrored card's toast tag is remote:ConfRoomROG:mesh-join:<64-hex node id> (92 chars); Windows caps a toast tag at 64. The first toast spawn logged Exception setting "Tag": The size of the notification tag is too large but still SHOWED the toast (untagged, so no dismiss can ever find it), then ab's retry showed a second, tagged copy. Two toasts for one prompt; only one can be dismissed.
  2. A decided prompt is replayed. One second after ConfRoomROG accepted the Trust, its replay_to_peer(AdomLapper) ("link formed after it was raised", #197) re-sent the same pending prompt to the laptop, so a fresh card appeared for a prompt that was already decided.
  3. A stale duplicate click is reported as a refusal. The second click hit the consumed one-shot nonce (correct security-wise) but the human is told "Could not decide it" with the nonce prose. When the id already has a recorded decision, the right answer is "already decided: trusted on AdomLapper at 21:06:07" and the stale card/toast on the clicking box should simply go away.

Fix plan: (1) hash any toast tag over 64 chars deterministically (same mapping for show and dismiss) and make a Tag failure abort the show instead of showing untagged; (2) replay_to_peer skips prompts that already have a decision (and the decision path removes the prompt from the live registry before any fan-out or grant carry); (3) perm_mirror_resolve answers already_decided with the recorded decision/box/time (from permission_history, #191) for a consumed nonce, and the clicking box dismisses its copy instead of toasting a refusal. Also confirm a decision on ANY surface dismisses every copy of a mesh-join card on the same box (window card, toast, Hydrogen).

1 Reply

John Lauer · 1mo ago

Fixed in insiders 2.1.138 (all four boxes pick it up on their tick; arav-rog took it through the real staged-installer path in 15 s).

What was wrong, from the two logs

  1. The mirrored card's toast tag remote:ConfRoomROG:mesh-join:<64-hex node id> is 92 chars; Windows caps a toast tag at 64. $toast.Tag threw, PowerShell's default is non-terminating, so Show still ran and the toast appeared untagged (no dismiss can ever find it); ab's retry then showed a second, also untagged, copy. Two toasts for one prompt.
  2. One second after your Trust landed on ConfRoomROG, its replay_to_peer(AdomLapper) ("link formed after the prompt was raised") re-sent the same prompt from a snapshot taken before the decision, so a fresh card appeared for a prompt that was already decided.
  3. Your second click hit the consumed one-shot nonce (correct security-wise), but the answer was the nonce lecture as a "Could not decide it" toast instead of "already decided".

What changed

  • toast_tag_fit: any tag over 64 chars keeps its readable prefix and gets a stable 16-hex hash tail; applied on show AND on dismiss so they agree. The toast script now starts with $ErrorActionPreference = 'Stop', so a bad Tag aborts before Show instead of showing an untagged toast.
  • replay_to_peer re-checks the live registry at send time; a prompt decided meanwhile is not replayed.
  • perm_mirror_resolve on a consumed nonce for a prompt that has a recorded decision (from permission_history, #191) answers errorCode: already_decided {decision, decidedOn, decidedAt} and re-fans the dismiss so the clicker's leftover copies drop; the clicking box shows no warning toast for that case. An unknown or still-live id is still refused as before.

Proof (arav-rog on 2.1.138)

(a) notify_user with a 93-char id -> ab log:
    Toast PowerShell process spawned (tag=remote:ConfRoomROG:mesh-join:ababababababababab~6d44abe108ab2162)
    no "Toast child FAILED", no "Toast RETRY" (the 21:05Z lines just above it in the same log show the old failure + retry)

(b) perm_mirror_resolve {id: <a decided prompt>, resolveNonce: "bogus-duplicate-click"}:
    {"errorCode":"already_decided","decision":"timeout","decidedOn":"arav-rog","decidedAt":"2026-09-19T19:35:38.590Z",
     "error":"Already decided: timeout on arav-rog at 2026-09-19T19:35:38.590Z"}
    perm_mirror_resolve {id: "never-existed-207", ...} -> still refused (no decision on record)

unit test: toast_tag_tests::long_tags_fit_and_stay_stable (<=64 chars, prefix kept, deterministic) passes

One cosmetic leftover I will fold into the next build: because that reply's error text contains the decision word ("timeout"), the generic status stamp labels the reply status: timeout with the "raise timeoutSeconds" hint, which is wrong for already_decided; the reply will carry its own status so the stamp leaves it alone.

Your three clicks were the untagged toast, its untagged retry and the replayed card; on 2.1.138 there is one tagged toast, it goes away on the first decision anywhere, and nothing is replayed after a decision.

John Lauer closed this · 1mo ago

Log in to reply.