Adom Hydrogen (Windows / WSL2)
Public Made by Adomby adom
The Windows (WSL2) platform layer for Hydrogen.
ed6a0ea
name: hydrogen-adom-bridge-triage description: >- Triage Adom Bridge (ab) when a Hydrogen workspace cannot reach it, including ab crashing on boot after a bad release: crash.log first, out-of-band checks, reinstall. Use when ab won't connect, no desktop client connected, ab never starts, or the bridge keeps dying.
Parent skill: hydrogen-adom-bridge (works with hydrogen-doctor, hydrogen-key-values, adom-bridge)
Triage: Adom Bridge won't connect
ping returning "No desktop client connected" tells you only that the
desktop half is absent. It does not tell you whether ab is uninstalled,
signed out, misconfigured, or installed and crash-looping. Those need
opposite fixes, and the last one is invisible from the container unless you
go looking for it.
The failure that motivated this skill. ab 2.0.91 panicked on startup, died in under a second, and was respawned by Hydrogen's supervisor every ~30-60s — 367 times over ~3 hours. From the container it looked exactly like "the user never installed it." The user's own words were "when I launch it from the Windows menu it never actually starts." The fix was one version newer on the wiki. Time wasted before reading
crash.log: most of the session.
The ladder — cheapest first, and read crash.log EARLY
1. Which half is broken? (container-side, free)
curl -sf http://127.0.0.1:8766/health # connectedClients, usingDefaultToken
adom-bridge --ai-thread "<your thread>" ping
adom-bridge --ai-thread "<your thread>" targets
connectedClients: 0 with a healthy relay ⇒ the container is fine; every
remaining step is about the desktop. Do not start reconfiguring the container.
2. Ask Hydrogen — it can see the host while ab is dead
Hydrogen's control API (127.0.0.1:47084, port from
~/.adom/hydrogen-ports.json) is reachable from the workspace and does not
depend on ab:
curl -s http://127.0.0.1:47084/diagnostics/all | python3 -m json.tool
Look for the adom-bridge-direct check. Its failure text names the cause and
carries a fix field. Note it probes ab's direct API, so read the port caveat
in Gotchas below before trusting a "silent range" message.
3. Read ab's crash log. Do this before touching any config.
This is the step that collapses a long session into two minutes.
| What | Windows path | From a WSL2 workspace |
|---|---|---|
| Crash log | %LOCALAPPDATA%\Adom Bridge\crash.log |
/mnt/c/Users/<user>/AppData/Local/Adom Bridge/crash.log |
| Install dir | %LOCALAPPDATA%\Adom Bridge\ |
same, without crash.log |
| Direct-API port | %USERPROFILE%\.adom\direct-api-port |
/mnt/c/Users/<user>/.adom/direct-api-port |
| Embedded marker | %LOCALAPPDATA%\Adom Bridge\embedded.json |
same |
| Server list | %USERPROFILE%\.adom\config.json |
/mnt/c/Users/<user>/.adom/config.json |
C="/mnt/c/Users/<user>/AppData/Local/Adom Bridge/crash.log"
ls -l "$C" # mtime is the whole story
tail -40 "$C"
Read the mtime first. A crash.log modified seconds or minutes ago means
ab is crash-looping right now — it is installed, it is being launched, and
it is dying. That single fact rules out the entire "not installed / not signed
in / wrong URL" branch you were about to explore.
A crash loop looks like one repeating panic with a marching clock:
[2026-08-31 19:44:04] PANIC: panicked at src\carbon_runtime.rs:113:5:
there is no reactor running, must be called from the context of a Tokio 1.x runtime
[2026-08-31 19:44:55] PANIC: panicked at src\carbon_runtime.rs:113:5:
...
Useful shape checks:
grep -c "PANIC" "$C" # how bad
grep -n "PANIC" "$C" | head -3 # when it STARTED
grep -v "carbon_runtime\|no reactor running" "$C" | tail # other panic classes
A panic in a path under src\... is ab's own code — i.e. a shipped
regression, not a broken machine. A panic inside .cargo\registry\... is a
dependency/host-environment issue and usually a different story.
4. Compare the installed build against the wiki
"/mnt/c/Users/<user>/AppData/Local/Adom Bridge/adom-bridge-cli.exe" --version
adom-wiki release list adom/adom-bridge # newest version + assets
A -dirty suffix in the installed version means it was built locally, not
installed from a signed wiki release — common on a developer's own machine and
a strong hint that it is ahead of, or diverged from, anything published.
If the installed build is older than the wiki's newest and crash.log
shows a repeating panic, the newer release is the fix. Go to step 5.
5. Install the newer release
adom-wiki release download adom/adom-bridge <version> # verifies integrity
sha256sum ./Adom.Bridge_<version>_windows_x64-setup.exe # confirm independently
cp ./Adom.Bridge_<version>_windows_x64-setup.exe /mnt/c/Users/<user>/AppData/Local/Temp/
cd /mnt/c/Users/<user>/AppData/Local/Temp
/mnt/c/Windows/System32/taskkill.exe /F /IM adom-bridge.exe # release the mmap lock
./Adom.Bridge_<version>_windows_x64-setup.exe /S /NOLAUNCH
tr -d '\r' < ./adom-bridge-install.log | tail -20 # [START]...[DONE]
Use /NOLAUNCH when Hydrogen manages ab (the usual case in a Hydrogen
workspace — see the hd-managed marker in the install dir). Hydrogen's
supervisor spawns ab itself with --embedded --start-hidden --relay-url ... --session-token ...; let it, or you get a standalone instance that the
single-instance logic then fights.
6. Verify — three independent signals, not one
sleep 45 # let the supervisor respawn
/mnt/c/Windows/System32/tasklist.exe /FI "IMAGENAME eq adom-bridge.exe"
curl -sf http://127.0.0.1:24680/health # {"service":"adom-bridge","pid":...}
adom-bridge --ai-thread "<thread>" targets # name, os, version
ls -l "$C" # crash.log mtime must STOP advancing
The crash.log mtime going quiet is the one that proves you fixed the loop rather than catching ab between crashes. Give it more than one crash interval before declaring success.
The out-of-band channels (why you are not blind)
When ab is dead you have not lost the host — you have lost one route to it.
Hydrogen's control API —
127.0.0.1:47084, always up if Hydrogen is running. Diagnostics, ports, container state. Seehydrogen-api.WSL2 interop (Windows hosts) — a Hydrogen Windows workspace is a WSL2 distro, so
/mnt/cis the real Windows filesystem and Windows.exes are directly executable from the workspace:ls /mnt/c/Users # is it mounted? cat /proc/sys/fs/binfmt_misc/WSLInterop # "enabled"? /mnt/c/Windows/System32/tasklist.exe # proof it worksThis is how you read crash logs, check versions, and run an installer without ab. On macOS/Linux Hydrogen hosts this section does not apply; use the platform bootstrap's equivalent and the control API.
Do NOT do these while guessing
Every one of these was tried during the incident above. None helped, and the first two left changes that had to be reverted.
| Don't | Why |
|---|---|
register_container when Hydrogen already manages ab |
Creates a second connection to the same workspace relay alongside the managed hydrogen entry — a duplicate path the user sees in the Connections list. It also writes three cloud keys, not one. Check for the managed entry / the hd-managed marker first. |
| Rotate the relay auth token | For a cloud container the auth model is the Adom account, not a relay token — setup_desktop deliberately emits auth_token: "". Rotating adds a mismatch failure mode to a system that is already broken, and the relay's usingDefaultToken warning assumes a directly-exposed port, which a proxied container relay is not. |
Conclude "not installed" / "not signed in" from ping alone |
Indistinguishable from a crash loop without reading crash.log. |
| Restart the relay, re-register, re-sign-in as a first move | All container-side; none of them touch a desktop process that is panicking. |
Reverting a mistaken register_container
If you (or a previous agent) registered a container that should not exist, removing it locally is not enough — ab re-reads the cloud profile at boot, at sign-in, and hourly, so a local-only removal comes back. Remove both:
TOK=$(cat /var/run/adom/api-key)
# 1. cloud profile (the authoritative copy)
curl -s -X PATCH https://carbon.adom.inc/user/hydrogen/settings \
-H "Authorization: Bearer $TOK" -H 'Content-Type: application/json' \
-d '{"key":"bridge.container.<id>","value":null}'
# 2. ab's local list
adom-bridge --ai-thread "<thread>" \
server_remove '{"name":"<name>","reason":"<why>"}'
register_container writes bridge.container.<id>, bridge.last_slug, and
bridge.enabled. bridge.last_slug will be left pointing at the entry you
just deleted; null it too. Its previous value is not recoverable — which is
the real argument for not calling register_container speculatively.
Verify with server_list and by re-reading hydrogen_preferences.settings
(see hydrogen-key-values). Removing a relay entry you are talking through
severs your own transport (adom/adom-bridge#387) — check ping's transport
field first; if it says direct-http you are on the loopback API and a relay
entry removal cannot cut you off.
Gotchas that cost time
- ab's direct API is on
24680(stable since ab 2.0.27);47200-47209is legacy. A diagnostic that probes only the legacy range can report ab unreachable when it is merely elsewhere.%USERPROFILE%\.adom\direct-api-portis authoritative (written at bind, removed on clean shutdown), so a stale file plus a silent port is itself a crash signal. - The 2.0.92 installer reserves
24680-24689vianetsh excludedportrange(best-effort, needs elevation) to stop Windows HNS grabbing the range. - Caller identity is mandatory. Verbs refuse with
errorCode: caller_identity_requiredunless you pass--ai-thread "<a name the user would recognize>". Gated verbs additionally need a per-callreason. Seeadom-bridge-caller-identity. embedded.jsonwithentered_via: "launch-flag"plus a dead Hydrogen makes ab exit by design ("no hanging chad"). Pre-1.8.74 builds did not delete the stale marker, leaving ab unlaunchable — a different crash-on-boot cause with the same symptom, so confirm which onecrash.logactually shows.
When it is genuinely not a crash
If crash.log is old and quiet and no ab process exists, you are in ordinary
setup territory: installed? signed in? does the workspace's connection appear
in the Connections list? Then follow adom-bridge / adom-bridge-auth. The
point of this skill is to make you prove that before you go there.
---
name: hydrogen-adom-bridge-triage
description: >-
Triage Adom Bridge (ab) when a Hydrogen workspace cannot reach it, including
ab crashing on boot after a bad release: crash.log first, out-of-band checks,
reinstall. Use when ab won't connect, no desktop client connected, ab never
starts, or the bridge keeps dying.
---
Parent skill: **hydrogen-adom-bridge** (works with **hydrogen-doctor**, **hydrogen-key-values**, **adom-bridge**)
# Triage: Adom Bridge won't connect
`ping` returning **"No desktop client connected"** tells you only that the
desktop half is absent. It does **not** tell you whether ab is uninstalled,
signed out, misconfigured, or **installed and crash-looping**. Those need
opposite fixes, and the last one is invisible from the container unless you
go looking for it.
> **The failure that motivated this skill.** ab 2.0.91 panicked on startup,
> died in under a second, and was respawned by Hydrogen's supervisor every
> ~30-60s — 367 times over ~3 hours. From the container it looked exactly like
> "the user never installed it." The user's own words were *"when I launch it
> from the Windows menu it never actually starts."* The fix was one version
> newer on the wiki. Time wasted before reading `crash.log`: most of the session.
## The ladder — cheapest first, and read crash.log EARLY
### 1. Which half is broken? (container-side, free)
```bash
curl -sf http://127.0.0.1:8766/health # connectedClients, usingDefaultToken
adom-bridge --ai-thread "<your thread>" ping
adom-bridge --ai-thread "<your thread>" targets
```
`connectedClients: 0` with a healthy relay ⇒ the container is fine; **every
remaining step is about the desktop**. Do not start reconfiguring the container.
### 2. Ask Hydrogen — it can see the host while ab is dead
Hydrogen's control API (`127.0.0.1:47084`, port from
`~/.adom/hydrogen-ports.json`) is reachable from the workspace and does **not**
depend on ab:
```bash
curl -s http://127.0.0.1:47084/diagnostics/all | python3 -m json.tool
```
Look for the `adom-bridge-direct` check. Its failure text names the cause and
carries a `fix` field. Note it probes ab's direct API, so read the port caveat
in **Gotchas** below before trusting a "silent range" message.
### 3. **Read ab's crash log. Do this before touching any config.**
This is the step that collapses a long session into two minutes.
| What | Windows path | From a WSL2 workspace |
|---|---|---|
| Crash log | `%LOCALAPPDATA%\Adom Bridge\crash.log` | `/mnt/c/Users/<user>/AppData/Local/Adom Bridge/crash.log` |
| Install dir | `%LOCALAPPDATA%\Adom Bridge\` | same, without `crash.log` |
| Direct-API port | `%USERPROFILE%\.adom\direct-api-port` | `/mnt/c/Users/<user>/.adom/direct-api-port` |
| Embedded marker | `%LOCALAPPDATA%\Adom Bridge\embedded.json` | same |
| Server list | `%USERPROFILE%\.adom\config.json` | `/mnt/c/Users/<user>/.adom/config.json` |
```bash
C="/mnt/c/Users/<user>/AppData/Local/Adom Bridge/crash.log"
ls -l "$C" # mtime is the whole story
tail -40 "$C"
```
**Read the mtime first.** A `crash.log` modified *seconds or minutes ago* means
ab is crash-looping **right now** — it is installed, it is being launched, and
it is dying. That single fact rules out the entire "not installed / not signed
in / wrong URL" branch you were about to explore.
A crash loop looks like one repeating panic with a marching clock:
```
[2026-08-31 19:44:04] PANIC: panicked at src\carbon_runtime.rs:113:5:
there is no reactor running, must be called from the context of a Tokio 1.x runtime
[2026-08-31 19:44:55] PANIC: panicked at src\carbon_runtime.rs:113:5:
...
```
Useful shape checks:
```bash
grep -c "PANIC" "$C" # how bad
grep -n "PANIC" "$C" | head -3 # when it STARTED
grep -v "carbon_runtime\|no reactor running" "$C" | tail # other panic classes
```
A panic in a path under `src\...` is **ab's own code** — i.e. a shipped
regression, not a broken machine. A panic inside `.cargo\registry\...` is a
dependency/host-environment issue and usually a different story.
### 4. Compare the installed build against the wiki
```bash
"/mnt/c/Users/<user>/AppData/Local/Adom Bridge/adom-bridge-cli.exe" --version
adom-wiki release list adom/adom-bridge # newest version + assets
```
A `-dirty` suffix in the installed version means it was built locally, not
installed from a signed wiki release — common on a developer's own machine and
a strong hint that it is ahead of, or diverged from, anything published.
If the installed build is older than the wiki's newest **and** `crash.log`
shows a repeating panic, **the newer release is the fix**. Go to step 5.
### 5. Install the newer release
```bash
adom-wiki release download adom/adom-bridge <version> # verifies integrity
sha256sum ./Adom.Bridge_<version>_windows_x64-setup.exe # confirm independently
cp ./Adom.Bridge_<version>_windows_x64-setup.exe /mnt/c/Users/<user>/AppData/Local/Temp/
cd /mnt/c/Users/<user>/AppData/Local/Temp
/mnt/c/Windows/System32/taskkill.exe /F /IM adom-bridge.exe # release the mmap lock
./Adom.Bridge_<version>_windows_x64-setup.exe /S /NOLAUNCH
tr -d '\r' < ./adom-bridge-install.log | tail -20 # [START]...[DONE]
```
**Use `/NOLAUNCH` when Hydrogen manages ab** (the usual case in a Hydrogen
workspace — see the `hd-managed` marker in the install dir). Hydrogen's
supervisor spawns ab itself with `--embedded --start-hidden --relay-url
... --session-token ...`; let it, or you get a standalone instance that the
single-instance logic then fights.
### 6. Verify — three independent signals, not one
```bash
sleep 45 # let the supervisor respawn
/mnt/c/Windows/System32/tasklist.exe /FI "IMAGENAME eq adom-bridge.exe"
curl -sf http://127.0.0.1:24680/health # {"service":"adom-bridge","pid":...}
adom-bridge --ai-thread "<thread>" targets # name, os, version
ls -l "$C" # crash.log mtime must STOP advancing
```
The crash.log mtime going quiet is the one that proves you fixed the loop
rather than catching ab between crashes. Give it more than one crash interval
before declaring success.
## The out-of-band channels (why you are not blind)
When ab is dead you have not lost the host — you have lost one route to it.
1. **Hydrogen's control API** — `127.0.0.1:47084`, always up if Hydrogen is
running. Diagnostics, ports, container state. See `hydrogen-api`.
2. **WSL2 interop (Windows hosts)** — a Hydrogen Windows workspace is a WSL2
distro, so `/mnt/c` is the real Windows filesystem and Windows `.exe`s are
directly executable from the workspace:
```bash
ls /mnt/c/Users # is it mounted?
cat /proc/sys/fs/binfmt_misc/WSLInterop # "enabled"?
/mnt/c/Windows/System32/tasklist.exe # proof it works
```
This is how you read crash logs, check versions, and run an installer
**without ab**. On macOS/Linux Hydrogen hosts this section does not apply;
use the platform bootstrap's equivalent and the control API.
## Do NOT do these while guessing
Every one of these was tried during the incident above. None helped, and the
first two left changes that had to be reverted.
| Don't | Why |
|---|---|
| `register_container` when Hydrogen already manages ab | Creates a **second** connection to the same workspace relay alongside the managed `hydrogen` entry — a duplicate path the user sees in the Connections list. It also writes **three** cloud keys, not one. Check for the managed entry / the `hd-managed` marker first. |
| Rotate the relay auth token | For a cloud container the auth model is the **Adom account**, not a relay token — `setup_desktop` deliberately emits `auth_token: ""`. Rotating adds a mismatch failure mode to a system that is already broken, and the relay's `usingDefaultToken` warning assumes a directly-exposed port, which a proxied container relay is not. |
| Conclude "not installed" / "not signed in" from `ping` alone | Indistinguishable from a crash loop without reading `crash.log`. |
| Restart the relay, re-register, re-sign-in as a first move | All container-side; none of them touch a desktop process that is panicking. |
## Reverting a mistaken `register_container`
If you (or a previous agent) registered a container that should not exist,
removing it locally is **not enough** — ab re-reads the cloud profile at boot,
at sign-in, and hourly, so a local-only removal comes back. Remove both:
```bash
TOK=$(cat /var/run/adom/api-key)
# 1. cloud profile (the authoritative copy)
curl -s -X PATCH https://carbon.adom.inc/user/hydrogen/settings \
-H "Authorization: Bearer $TOK" -H 'Content-Type: application/json' \
-d '{"key":"bridge.container.<id>","value":null}'
# 2. ab's local list
adom-bridge --ai-thread "<thread>" \
server_remove '{"name":"<name>","reason":"<why>"}'
```
`register_container` writes **`bridge.container.<id>`, `bridge.last_slug`, and
`bridge.enabled`**. `bridge.last_slug` will be left pointing at the entry you
just deleted; null it too. Its previous value is **not recoverable** — which is
the real argument for not calling `register_container` speculatively.
Verify with `server_list` and by re-reading `hydrogen_preferences.settings`
(see `hydrogen-key-values`). Removing a relay entry you are *talking through*
severs your own transport (adom/adom-bridge#387) — check `ping`'s `transport`
field first; if it says `direct-http` you are on the loopback API and a relay
entry removal cannot cut you off.
## Gotchas that cost time
- **ab's direct API is on `24680`** (stable since ab 2.0.27); `47200-47209` is
legacy. A diagnostic that probes only the legacy range can report ab
unreachable when it is merely elsewhere. `%USERPROFILE%\.adom\direct-api-port`
is authoritative (written at bind, removed on clean shutdown), so a *stale*
file plus a silent port is itself a crash signal.
- The 2.0.92 installer reserves `24680-24689` via `netsh excludedportrange`
(best-effort, needs elevation) to stop Windows HNS grabbing the range.
- **Caller identity is mandatory.** Verbs refuse with
`errorCode: caller_identity_required` unless you pass
`--ai-thread "<a name the user would recognize>"`. Gated verbs additionally
need a per-call `reason`. See `adom-bridge-caller-identity`.
- `embedded.json` with `entered_via: "launch-flag"` plus a dead Hydrogen makes
ab exit by design ("no hanging chad"). Pre-1.8.74 builds did not delete the
stale marker, leaving ab unlaunchable — a different crash-on-boot cause with
the same symptom, so confirm which one `crash.log` actually shows.
## When it is genuinely not a crash
If `crash.log` is old and quiet and no ab process exists, you are in ordinary
setup territory: installed? signed in? does the workspace's connection appear
in the Connections list? Then follow `adom-bridge` / `adom-bridge-auth`. The
point of this skill is to make you *prove* that before you go there.