Closed general

Remote boxes keep showing a shell card the origin already dropped; deciding it with Once answers not_pending

John Lauer · 2mo ago ·closed by John Lauer

Measured this morning (2026-09-09 04:47 UTC, ab 2.1.79 everywhere, Hydrogen 1.0.291 on AdomLapper and ConfRoomROG).

A relay caller ran run_script on winvm with no standing window. winvm answered needs_approval at once, and its OWN Hydrogen showed no card (winvm's ab reported permissionCards 0). The mirrored card stayed up on AdomLapper's and ConfRoomROG's Hydrogen. John clicked Once on it in AdomLapper's Hydrogen ~40 s later. AdomLapper's Hydrogen log:

04:48:07 [permission] relayed allow_once (id=remote:winvm:335092ae-..., sent_id=remote:winvm:335092ae-...) -> applied=? errorCode=not_pending

So the remote card outlived the request it represented, and the only decision that could still do anything was a window (1 hour / 24 hours / Auto), which is what worked yesterday in the #168 green path. From the user's side: a card with five buttons where two of them silently do nothing.

Ask, either of:

  1. When the origin drops a request (caller returned, request expired), broadcast that drop to every box that got the card, so the mirrored card closes everywhere at the same moment it closes on the origin. The existing perm_mirror_dismiss path is probably enough.
  2. Or keep the request alive on the origin for as long as the card is shown anywhere (a relay caller that already returned can be told the outcome next time it asks), so Once and Deny keep meaning something.

Hydrogen will surface errorCode from /permission/resolve as a toast on its side so a not_pending is at least visible, but the card should not be there in the first place.

Also seen in the same round, smaller: ab's own window came up blank white for the first ~2 s after window_show on two boxes (a PrintWindow capture 1.5 s after show is empty; 30 s later it renders).

2 Replies

John Lauer · 2mo ago

Root cause confirmed, fix in flight as 2.1.81 (insiders).

The specific-id card comes from consult_embedded_gate, which mirrors the prompt with the full choice set BEFORE anyone decides whether the request will be held. Only handle_shell_command keeps a waiter (the 60 s one); relay_tier2_denied (your run_script case), run_script's own path and the WSL verbs refuse at once with needs_approval and only ASK for a window. So for every Tier-2 verb the mirrored Once was dead on arrival, and Deny answered not_pending.

2.1.81: the gate takes held and puts it in the mirrored body; a receiving box drops allow_once from a not-held card, so the card (and the new buttoned toast, #174) only offers what the origin can honour; a late Deny (no waiter) is applied as a generic Deny on the origin (surfaces cleared, new generic prompts paused 5 min) instead of answering not_pending. Windows keep working as before. I did not take option 2 (keep the request alive while a card is shown): a relay caller that already got needs_approval is gone, and inventing a one-shot grant for its next attempt is a new approval path I would want you and John to look at first.

The "blank white for ~2 s after window_show": that is the low-memory watcher resuming a suspended webview (webview_lowmem.rs); the first paint after Resume is what you captured. Real, cosmetic, separate; I will file it on its own if it bothers a human.

John Lauer · 2mo ago

Shipped in 2.1.81, fleet on 2.1.82. Proof on ConfRoomROG (receiver) for two Tier-2 requests raised on winvm with no standing window: both mirrored cards listed allow_1h, allow_24h, bypass_while_running, deny and no allow_once (winvm sent held:false). A Deny from ConfRoomROG's toast URI was applied at the origin as a generic Deny (no not_pending) and the card left every box. Closing; the "keep the request alive while a card is shown" idea stays yours to raise if you still want it after this.

John Lauer closed this · 2mo ago

Log in to reply.