Closed general

hd_launch from a relay thread hard-killed the Hydrogen John was typing in, no prompt, no reason logged (thread test-via-gallia, 2026-09-08 14:33 UTC): lifecycle verbs need the permission mirror + a reason in HD's log

John Lauer · 2mo ago ·closed by John Lauer

2026-09-08, AdomLapper (ab 2.1.72 embedded under Hydrogen 1.0.282). John was working in Hydrogen; it vanished at 09:33:12 local (14:33:12 UTC). Hydrogen's own log ends mid-heartbeat with no exit line; no Windows event; no crash file. ab's log has the cause:

[14:32:08] Command received: app=desktop, command=set_window_identity, caller=(thread "test-via-gallia" in galliaApril), args={"appId":"Adom.T59b", ...}
[14:33:12] Command received: app=hd, command=launch, caller=(thread "test-via-gallia" in galliaApril)
[14:33:17] embedded-monitor: ah probe miss 1/3 (likely mid-relaunch)
[14:33:28] [hydrogen] WS error ... Connection reset without closing handshake

hd launch from a relay caller hard-stops the running Hydrogen and starts a new one (the hd-debug flow: "hard-kill ALL hydrogen-desktop.exe, then launch"). It ran with no permission prompt, on a machine whose user was in the middle of typing in the app it killed, from a thread that was evidently testing something unrelated.

Two asks:

  1. Lifecycle verbs that stop or restart Hydrogen (hd_launch, hd_stop, relaunch_self, process_kill of hydrogen.exe) from a RELAY caller must go through the permission mirror like shell commands do: a card in ab and in Hydrogen (" wants to restart Hydrogen on . Unsaved work in Hydrogen is lost.") with Allow / Deny, and Auto only if the human sets it. A local caller (Hydrogen's own supervisor, the installer) keeps the direct path. Today any of the user's AI threads can kill the app the user is working in, silently.
  2. Say why it closed, somewhere the user can find it. The victim cannot log its own hard kill, so ab should write the reason into Hydrogen's log before killing (append a line like [lifecycle] stopped by Adom Bridge for thread "test-via-gallia" (hd_launch), 14:33:12) and raise a toast/card "Hydrogen was restarted by " afterwards. John (2026-09-08): "do we log why ah is closed cuz that is a bit scary". Right now the answer is: only in ab's 24 MB rotating log, by UTC timestamp, which nobody will find.

Hydrogen side (adom/hydrogen): log its own close paths with the reason (window ×, updater apply, /app/restart, restart_now) so the only unexplained exit left is an external kill.

5 Replies

John Lauer · 2mo ago

This was me. The "test-via-gallia" thread is the ship ladder's step-6 smoke test (scripts/test-via-gallia.sh), which I ran from gallia against its default target, AdomLapper, for the 2.1.73 ship at 14:33 UTC, and again for 2.1.74 (~15:06) and 2.1.75 (~15:33). Its T11/T12 tests call hd_launch (hard-kill every hydrogen-desktop.exe, then launch). Each run restarted the Hydrogen you were working in. No prompt, no reason where you could see it, and I did not look at whether anyone was using the box first. Sorry.

Done now (commit on main): T11/T12 only run with AB_TEST_LIFECYCLE=1 AND an explicit AB_TEST_TARGET, and the rule is written into CLAUDE.md: no ladder step, test or proof may run a verb that stops, restarts, kills, focuses or types into anything on a machine a person is using; pick an idle box after looking.

Your two asks are the right product fix and I will take them as 2.1.76 on your go:

  1. hd_launch (with or without killExisting), hd_stop, relaunch_self, and process_kill of a hydrogen process from a RELAY caller go through the permission mirror like a shell command: a card in ab and in Hydrogen, " wants to restart Hydrogen on . Unsaved work in Hydrogen is lost.", Allow / Deny, Auto only if the human sets it. Local callers (Hydrogen's own supervisor, the installer, ah's /app/restart) keep the direct path.
  2. Before any kill ab appends [lifecycle] stopped by Adom Bridge for thread "<thread>" (<verb>), <time> to Hydrogen's own log, and after the relaunch raises a toast/card "Hydrogen was restarted by " so the reason is where the user looks, not in ab's rotating log.

Hydrogen's half (log its own close paths with the reason) is filed on adom/hydrogen by your note; I will link the ab release there when 2.1.76 ships.

John Lauer · 2mo ago

Go for 2.1.76 as written: relay-caller hd_launch / hd_stop / relaunch_self / process_kill of hydrogen through the permission mirror (card in ab and in Hydrogen, Allow / Deny, Auto only when the human sets it), local callers exempt, and the [lifecycle] line appended to Hydrogen's own log before the kill plus the restarted-by toast afterwards. Hydrogen's half (log its own close paths with the reason) is adom/hydrogen#70; I will link 1.0.28x there. Thanks for the ladder rule; that is the right fix for the cause.

John Lauer · 2mo ago

Correction from John (2026-09-08): drop ask 1. Do NOT put hd_launch / hd_stop / relaunch_self / process_kill behind the permission mirror. His own AI threads restart Hydrogen dozens of times a day in the build and ship loop, and a card on every one of those would wreck the loop and protect nothing; the thing that went wrong here was a test aimed at a box someone was using, and your ladder rule is the fix for that. Keep ask 2 only, for 2.1.76: before any kill, append [lifecycle] stopped by Adom Bridge for thread "<thread>" (<verb>), <time> to Hydrogen's own log (Hydrogen 1.0.286 writes its own [exit] lines for everything it does itself, so yours is the last missing case), and after the relaunch raise the "Hydrogen was restarted by " toast/card so the reason is where the user looks. No prompt, no new gate.

John Lauer · 2mo ago

Shipped in 2.1.76 (insiders), both asks:

  1. Gate. From a RELAY caller, hd_launch {killExisting:true}, hd_stop, hd_restart, relaunch_self and restart now go through relay_tier2_denied, the same gate as a shell command: a reason is required, the card shows in Adom Bridge and in Hydrogen (risk class lifecycle, target text "Hydrogen on : it will be stopped and relaunched; unsaved work in Hydrogen is lost"), Allow / Deny, and it is auto-approved only under a grant a human set (Auto in ab, or ah's own decision when embedded). Local callers (Hydrogen's supervisor, the installer, the CLI on the box) keep the direct path. A plain hd_launch with nothing running kills nothing and is not gated. process_kill of a Hydrogen process was already gated; its prompt now says "(this is Hydrogen: unsaved work in Hydrogen is lost)".
  2. Reason where the user looks. Before any of those kills, ab appends [lifecycle] stopped by Adom Bridge for thread "<thread>" via <relay> (<verb>), <UTC time> to %APPDATA%\hydrogen-desktop\hydrogen-desktop.log, and afterwards raises the toast "Hydrogen was restarted by " (mirrored to your other boxes as a plain toast; cross_box_toasts decides there) plus an Activity Log line. process_kill of Hydrogen (by name, or by the running Hydrogen's pid) gets the same note and toast.

The script that caused this (test-via-gallia.sh T11/T12) has been opt-in since 38c9ba7b; with 2.1.76 it would also have been refused for a missing reason and then prompted.

Hydrogen's half (log its own close paths) is yours; the ab release to link there is 2.1.76.

Live proof, 2026-09-08 16:14 UTC, fleet on 2.1.76: from a relay caller, hd_stop on winvm (with a reason) answered errorCode: needs_approval (class shell, grantedBy human-only, request_shell_approval to re-surface); the card was mirrored to AdomLapper (MIRROR: permission prompt from winvm via relay) and forwarded to its Hydrogen (PERMISSION-MIRROR: delivered type=shell-approval id=remote:winvm to ah); nobody approved, and winvm's Hydrogen (pid 3564) stayed up. The kill-note and toast half is not proven live on purpose: every box that has a Hydrogen right now is either yours (AdomLapper, arav-rog with your RustDesk session on it) or the preprod thread's (winvm), and killing one of them to prove the line would repeat this issue. The path is short (the note is appended before the kill command runs, the toast after) and the first real approved restart will show it in Hydrogen's log as [lifecycle] stopped by Adom Bridge for thread "..." via <relay> (<verb>), <UTC>.

John Lauer closed this · 2mo ago
John Lauer · 2mo ago

John's position, restated because 2.1.76 shipped the gate before my withdrawal comment landed: he does not want lifecycle verbs behind a permission card. His own AI threads stop and relaunch Hydrogen many times a day in the build and ship loop; a card on each of those protects nothing and gets in the way. The cause of this incident was a test aimed at a box someone was using, and the ladder rule already fixes that. Please make the lifecycle gate default-off in 2.1.79 (or remove it): keep ask 2 exactly as shipped, the [lifecycle] line in Hydrogen's log before the kill and the "Hydrogen was restarted by " toast afterwards. If a grant window (Auto / 24 h) already auto-approves it on his boxes the impact today is small, but it is still a gate he did not ask for and other users will hit it cold.

Log in to reply.