Hydrogen Desktop (macOS / Lima)
Public Made by Adomby adom
The macOS platform layer for Hydrogen Desktop.
name: hd-workspace-lifecycle
description: Workspace machine (Adom-Workspace) lifecycle management for Hydrogen Desktop on macOS. Import/export/remove/terminate, code-server-as-HD-child, the resume reality-check, recovery after sleep/hibernate, and every hard-won rule about what NOT to do — above all, NEVER run a global teardown; touch ONLY Adom-Workspace so the user's other Lima VMs / machines stay safe. MUST READ before any workspace operation. Trigger words — machine, workspace, Adom-Workspace, restart workspace, terminate machine, remove, machinectl import-tar, machinectl remove, code-server, workspace broken, workspace unhealthy, machine hung, fix workspace, /workspace/status.
Hydrogen Desktop — Workspace Machine Lifecycle
This is the machine-runtime version (default). The legacy Docker equivalent (
HD_RUNTIME=docker) is in the docker/ bucket.
Under the machine runtime the workspace is the Adom-Workspace machine (Ubuntu, a
systemd-nspawn machine imported with machinectl import-tar), running locally inside
HD's hd-builder Lima VM on the user's Mac. /home/adom/project lives inside the
machine — there is no Docker volume.
ABSOLUTE RULE — NEVER do a global teardown
Operate ONLY on Adom-Workspace. Never run any global teardown that hits the shared
Lima VM or other machines.
Forbidden, no exceptions:
- ❌ stopping or deleting the
hd-builderLima VM itself (kills every machine inside it) - ❌
machinectl terminateon any machine other thanAdom-Workspace - ❌ blanket killing of
machinectl/ nsenter processes - ❌ restarting / re-provisioning the
hd-builderLima VM - ❌
machinectl removeon anything butAdom-Workspace - ❌ global
machinectlteardown
Why this matters: the user may run other Lima VMs or machinectl machines, and
Docker-for-Mac keeps its own VM. Any global teardown — stopping/deleting the shared
hd-builder VM or wiping other machines — would knock those out. It's harmless to HD's
own Adom-Workspace, but it looks to the user like HD broke their other VMs / Docker —
terrible first-run UX. So every HD operation must be scoped to Adom-Workspace and
nothing else.
Allowed, scoped operations:
- ✅
machinectl terminate Adom-Workspace(this is what "Restart" does) - ✅
machinectl remove Adom-Workspace(destructive wipe, this machine only) - ✅
machinectl import-tar Adom-Workspace <tarball>(fresh import) - ✅
limactl shell hd-builder -- sudo nsenter -t <leader> -a -S 1001 -G 1001 -- bash -lc '<cmd>'(exec inside the machine)
In-machine exec — NEVER docker exec
To run a command inside the workspace:
limactl shell hd-builder -- sudo nsenter -t <leader> -a -S 1001 -G 1001 -- bash -lc '<cmd>'
# or, root variant (drop -S/-G):
limactl shell hd-builder -- sudo nsenter -t <leader> -a -- bash -lc 'su - adom -c "..."'
<leader> is the machine's leader PID. There is no docker exec under this runtime,
and no Docker container to exec into.
Workspace Lifecycle
Import (fresh setup)
setup_and_start → MacosVmRuntime::setup_and_start → fresh_import:
download adom-golden.tar.gz (the full pre-baked golden image, downloaded
anonymously from the adom-inc/hd-lima-image GitHub release, SHA256-verified)
then machinectl import-tar Adom-Workspace adom-golden.tar.gz, then start code-server.
Log proof: [golden-tarball] downloaded … SHA256 OK → [setup] machine setup_and_start completed
and machinectl list shows Adom-Workspace running.
code-server is an HD-OWNED CHILD process
code-server (CODE_SERVER_CHILD in runtime/macos.rs) runs as a child of HD, deliberately
tied to HD's lifetime — NOT a detached nohup (a detached process dies when the
machine tears down the session anyway). Consequence: every HD restart (rebuild / crash /
relaunch) orphans the old code-server, so a fresh HD can come up with the machine
running but the editor DEAD ("Starting AI Environment" forever) even when setup is
otherwise complete.
Fix (already in HD): HD re-ensures code-server on every launch
([macos-startup] re-ensured code-server on launch). And the resume reality-check in
run_all_machine requires code-server reachable on the host port 7380 — not just
"machine present" — before it trusts the workspace as ready.
Restart
"Restart Workspace" = machinectl terminate Adom-Workspace + re-ensure code-server.
NOT docker restart. This is the fix for a workspace that's gone unreachable after
laptop sleep/hibernate.
Stop
machinectl terminate Adom-Workspace. The machine stays present (and your project files
persist); it just isn't running.
Export / backup
machinectl export-tar Adom-Workspace <file>.tar snapshots the machine to a tarball.
Re-import with machinectl import-tar Adom-Workspace <file>.tar. (Scoped to this machine,
so safe.)
Remove (DESTRUCTIVE wipe)
machinectl remove Adom-Workspace — deletes the machine AND everything in it, including
/home/adom/project. This is the only destructive wipe, and it names ONLY
Adom-Workspace. Never remove any other machine.
Control API
The HD control API is on loopback http://127.0.0.1:<dynamic> (NOT 9001). The
port is dynamic per launch — read the live URL from ~/.adom/hd-control-url. From
inside the machine, reach the host via the adom-host gateway alias (vz does NOT
mirror loopback, so 127.0.0.1 inside the machine is NOT the Mac).
| Endpoint | What it does |
|---|---|
GET /workspace/status |
Reports the machine state — present? running? code-server reachable on :7380? |
POST /workspace/unregister |
Destructive: machinectl remove Adom-Workspace (this machine only) |
GET /container-stats |
Live CPU/RAM/disk from the workspace_stats() probe (see hd-container-stats) |
BASE="$(cat ~/.adom/hd-control-url)" # http://127.0.0.1:<dynamic>
# Workspace status (from inside the machine, via the adom-host gateway)
curl -s "$BASE/workspace/status"
# Destructive wipe of ONLY Adom-Workspace
curl -s -X POST "$BASE/workspace/unregister"
Diagnosing Workspace Problems
"Workspace unhealthy" / editor stuck on "Starting AI Environment"
- Cause: code-server child was orphaned by an HD restart, or the machine went unreachable after sleep.
- Check:
[macos] Workspace unhealthy …in the HD log (the real signal). NOTE: a[docker] Container runtime unhealthy …line under the machine runtime is benign noise — a leftover heartbeat with no container bound. Ignore it; look at the[machine]line. - Fix: Restart Workspace (Adom menu) =
machinectl terminate Adom-Workspace+ re-ensure code-server. Verify code-server answers on :7380.
Machine running but editor dead
- Cause: code-server (HD's child) isn't up even though
machinectl listshows running. - Fix: relaunch HD (it re-ensures code-server on launch), or Restart Workspace. Confirm via the resume reality-check — code-server reachable on :7380, not just "machine present."
Lima VM not available at all
- Cause: the
hd-builderLima VM isn't up / hasn't been provisioned. - Fix: the "Lima VM not available" floaty → "Run Setup Steps" walks the user through provisioning it. See hd-workspace-monitoring.
Recovery Checklist
When the workspace is broken, follow this order:
- Check the Lima VM itself:
limactl list/limactl shell hd-builder -- systemctl status(NOT stopping or deletinghd-builder). - Check the machine:
machinectl list— isAdom-Workspacelisted and running? - If running but editor dead: code-server child orphaned → Restart Workspace
(
machinectl terminate Adom-Workspace+ re-ensure code-server), or relaunch HD. - Verify code-server: it must answer on host :7380 (the resume reality-check).
- Last resort (destructive):
machinectl remove Adom-Workspacethen re-import viasetup_and_start— wipes the machine and re-importsadom-golden.tar.gz. - NEVER reach for a global teardown to "fix" things — stopping/deleting the
hd-builderVM or running globalmachinectlteardown breaks the user's other Lima VMs / machines, doesn't help your machine, and angers the user.
What NOT To Do (learned the hard way)
- Do NOT stop or delete the
hd-builderLima VM — global, kills every machine in it, breaks the user's other Lima VMs / Docker-for-Mac's VM. - Do NOT
machinectl terminateormachinectl removeany machine exceptAdom-Workspace. - Do NOT blanket-kill
machinectl/ nsenter processes — same blast radius as tearing down the VM. - Do NOT use
docker exec/docker restart— there is no container in this runtime. - Do NOT trust "machine present" as healthy — require code-server reachable on :7380.
- Do NOT treat
[docker] … unhealthyin the log as a real failure under the machine runtime — it's benign; the real signal is[macos] Workspace unhealthy ….
Related skills
- hd-workspace-monitoring — the 15s poll, floaty states, lifecycle dialog, auto-start
- hd-container-stats — title-bar CPU/RAM bars +
workspace_stats()probe - hd-networking — port 7380 code-server, control port 47084, host reachability via the
adom-hostgateway
---
name: hd-workspace-lifecycle
description: Workspace machine (`Adom-Workspace`) lifecycle management for Hydrogen Desktop on macOS. Import/export/remove/terminate, code-server-as-HD-child, the resume reality-check, recovery after sleep/hibernate, and every hard-won rule about what NOT to do — above all, NEVER run a global teardown; touch ONLY Adom-Workspace so the user's other Lima VMs / machines stay safe. MUST READ before any workspace operation. Trigger words — machine, workspace, Adom-Workspace, restart workspace, terminate machine, remove, machinectl import-tar, machinectl remove, code-server, workspace broken, workspace unhealthy, machine hung, fix workspace, /workspace/status.
---
# Hydrogen Desktop — Workspace Machine Lifecycle
> This is the machine-runtime version (default). The legacy Docker equivalent
> (`HD_RUNTIME=docker`) is in the docker/ bucket.
Under the machine runtime the workspace is the **`Adom-Workspace` machine** (Ubuntu, a
systemd-nspawn machine imported with `machinectl import-tar`), running locally inside
HD's `hd-builder` Lima VM on the user's Mac. `/home/adom/project` lives inside the
machine — there is no Docker volume.
## ABSOLUTE RULE — NEVER do a global teardown
**Operate ONLY on `Adom-Workspace`. Never run any global teardown that hits the shared
Lima VM or other machines.**
Forbidden, no exceptions:
- ❌ stopping or deleting the `hd-builder` Lima VM itself (kills every machine inside it)
- ❌ `machinectl terminate` on any machine other than `Adom-Workspace`
- ❌ blanket killing of `machinectl` / nsenter processes
- ❌ restarting / re-provisioning the `hd-builder` Lima VM
- ❌ `machinectl remove` on anything but `Adom-Workspace`
- ❌ global `machinectl` teardown
**Why this matters:** the user may run other Lima VMs or `machinectl` machines, and
Docker-for-Mac keeps its own VM. Any global teardown — stopping/deleting the shared
`hd-builder` VM or wiping other machines — would knock those out. It's harmless to HD's
own `Adom-Workspace`, but it looks to the user like HD broke their other VMs / Docker —
terrible first-run UX. So every HD operation must be scoped to `Adom-Workspace` and
nothing else.
Allowed, scoped operations:
- ✅ `machinectl terminate Adom-Workspace` (this is what "Restart" does)
- ✅ `machinectl remove Adom-Workspace` (destructive wipe, this machine only)
- ✅ `machinectl import-tar Adom-Workspace <tarball>` (fresh import)
- ✅ `limactl shell hd-builder -- sudo nsenter -t <leader> -a -S 1001 -G 1001 -- bash -lc '<cmd>'` (exec inside the machine)
## In-machine exec — NEVER `docker exec`
To run a command inside the workspace:
```bash
limactl shell hd-builder -- sudo nsenter -t <leader> -a -S 1001 -G 1001 -- bash -lc '<cmd>'
# or, root variant (drop -S/-G):
limactl shell hd-builder -- sudo nsenter -t <leader> -a -- bash -lc 'su - adom -c "..."'
```
`<leader>` is the machine's leader PID. There is no `docker exec` under this runtime,
and no Docker container to exec into.
## Workspace Lifecycle
### Import (fresh setup)
`setup_and_start` → `MacosVmRuntime::setup_and_start` → `fresh_import`:
download `adom-golden.tar.gz` (the full pre-baked golden image, downloaded
anonymously from the `adom-inc/hd-lima-image` GitHub release, SHA256-verified)
then `machinectl import-tar Adom-Workspace adom-golden.tar.gz`, then start code-server.
Log proof: `[golden-tarball] downloaded … SHA256 OK` → `[setup] machine setup_and_start completed`
and `machinectl list` shows `Adom-Workspace` running.
### code-server is an HD-OWNED CHILD process
code-server (`CODE_SERVER_CHILD` in `runtime/macos.rs`) runs as a child of HD, deliberately
tied to HD's lifetime — **NOT a detached `nohup`** (a detached process dies when the
machine tears down the session anyway). Consequence: every HD restart (rebuild / crash /
relaunch) orphans the old code-server, so a fresh HD can come up with the machine
running but the editor DEAD ("Starting AI Environment" forever) even when setup is
otherwise complete.
Fix (already in HD): HD **re-ensures code-server on every launch**
(`[macos-startup] re-ensured code-server on launch`). And the resume reality-check in
`run_all_machine` requires code-server reachable on the host port **7380** — not just
"machine present" — before it trusts the workspace as ready.
### Restart
"Restart Workspace" = `machinectl terminate Adom-Workspace` + re-ensure code-server.
NOT `docker restart`. This is the fix for a workspace that's gone unreachable after
laptop sleep/hibernate.
### Stop
`machinectl terminate Adom-Workspace`. The machine stays present (and your project files
persist); it just isn't running.
### Export / backup
`machinectl export-tar Adom-Workspace <file>.tar` snapshots the machine to a tarball.
Re-import with `machinectl import-tar Adom-Workspace <file>.tar`. (Scoped to this machine,
so safe.)
### Remove (DESTRUCTIVE wipe)
`machinectl remove Adom-Workspace` — deletes the machine AND everything in it, including
`/home/adom/project`. This is the only destructive wipe, and it names ONLY
`Adom-Workspace`. Never remove any other machine.
## Control API
The HD control API is on loopback **`http://127.0.0.1:<dynamic>`** (NOT 9001). The
port is dynamic per launch — read the live URL from `~/.adom/hd-control-url`. From
inside the machine, reach the host via the `adom-host` gateway alias (vz does NOT
mirror loopback, so 127.0.0.1 inside the machine is NOT the Mac).
| Endpoint | What it does |
|---|---|
| `GET /workspace/status` | Reports the machine state — present? running? code-server reachable on :7380? |
| `POST /workspace/unregister` | Destructive: `machinectl remove Adom-Workspace` (this machine only) |
| `GET /container-stats` | Live CPU/RAM/disk from the `workspace_stats()` probe (see hd-container-stats) |
```bash
BASE="$(cat ~/.adom/hd-control-url)" # http://127.0.0.1:<dynamic>
# Workspace status (from inside the machine, via the adom-host gateway)
curl -s "$BASE/workspace/status"
# Destructive wipe of ONLY Adom-Workspace
curl -s -X POST "$BASE/workspace/unregister"
```
## Diagnosing Workspace Problems
### "Workspace unhealthy" / editor stuck on "Starting AI Environment"
- **Cause**: code-server child was orphaned by an HD restart, or the machine went
unreachable after sleep.
- **Check**: `[macos] Workspace unhealthy …` in the HD log (the real signal). NOTE: a
`[docker] Container runtime unhealthy …` line under the machine runtime is **benign
noise** — a leftover heartbeat with no container bound. Ignore it; look at the
`[machine]` line.
- **Fix**: Restart Workspace (Adom menu) = `machinectl terminate Adom-Workspace` + re-ensure
code-server. Verify code-server answers on :7380.
### Machine running but editor dead
- **Cause**: code-server (HD's child) isn't up even though `machinectl list` shows running.
- **Fix**: relaunch HD (it re-ensures code-server on launch), or Restart Workspace.
Confirm via the resume reality-check — code-server reachable on :7380, not just
"machine present."
### Lima VM not available at all
- **Cause**: the `hd-builder` Lima VM isn't up / hasn't been provisioned.
- **Fix**: the "Lima VM not available" floaty → "Run Setup Steps" walks the user through
provisioning it. See [hd-workspace-monitoring](../hd-workspace-monitoring/SKILL.md).
## Recovery Checklist
When the workspace is broken, follow this order:
1. **Check the Lima VM itself**: `limactl list` / `limactl shell hd-builder -- systemctl status`
(NOT stopping or deleting `hd-builder`).
2. **Check the machine**: `machinectl list` — is `Adom-Workspace` listed and running?
3. **If running but editor dead**: code-server child orphaned → Restart Workspace
(`machinectl terminate Adom-Workspace` + re-ensure code-server), or relaunch HD.
4. **Verify code-server**: it must answer on host :7380 (the resume reality-check).
5. **Last resort (destructive)**: `machinectl remove Adom-Workspace` then re-import via
`setup_and_start` — wipes the machine and re-imports `adom-golden.tar.gz`.
6. **NEVER** reach for a global teardown to "fix" things — stopping/deleting the
`hd-builder` VM or running global `machinectl` teardown breaks the user's other
Lima VMs / machines, doesn't help your machine, and angers the user.
## What NOT To Do (learned the hard way)
- Do NOT stop or delete the `hd-builder` Lima VM — global, kills every machine in it,
breaks the user's other Lima VMs / Docker-for-Mac's VM.
- Do NOT `machinectl terminate` or `machinectl remove` any machine except `Adom-Workspace`.
- Do NOT blanket-kill `machinectl` / nsenter processes — same blast radius as tearing
down the VM.
- Do NOT use `docker exec` / `docker restart` — there is no container in this runtime.
- Do NOT trust "machine present" as healthy — require code-server reachable on :7380.
- Do NOT treat `[docker] … unhealthy` in the log as a real failure under the machine
runtime — it's benign; the real signal is `[macos] Workspace unhealthy …`.
## Related skills
- [hd-workspace-monitoring](../hd-workspace-monitoring/SKILL.md) — the 15s poll, floaty states, lifecycle dialog, auto-start
- [hd-container-stats](../hd-container-stats/SKILL.md) — title-bar CPU/RAM bars + `workspace_stats()` probe
- [hd-networking](../hd-networking/SKILL.md) — port 7380 code-server, control port 47084, host reachability via the `adom-host` gateway