Hydrogen Desktop (macOS / Lima)
Public Made by Adomby adom
The macOS platform layer for Hydrogen Desktop.
name: hd-networking description: > Port architecture, hostnames, and networking rules for Hydrogen Desktop. MUST READ before adding ports, exposing a service to your macOS host, referencing host URLs from inside the workspace, or wiring any service communication. Trigger words — HD port, HD network, port mapping, proxy, 127.0.0.1, loopback, adom-host gateway, hd-control-url, VSCODE_PROXY_URI, relay URL, code-server proxy, container networking, ADOM_CARBON_URL, ADOM_HYDROGEN_URL, direct connect, 8770, 7380.
Hydrogen Desktop — Networking & Port Architecture
This is the machine-runtime version (default). For the legacy Docker container runtime (
HD_RUNTIME=docker) see the docker/ bucket.
Core Principle: expose 0.0.0.0 listeners to the Mac
The HD workspace is the Adom-Workspace machine (a systemd-nspawn machine booted
inside the hd-builder Lima VM), not a Docker container. macOS does not do
WSL2-style magic loopback mirroring — vz does NOT mirror loopback. Instead, Lima
exposes the machine's listeners to the Mac: any service that binds
0.0.0.0:<port> inside the workspace machine is reachable at
127.0.0.1:<sameport> on the Mac — no Docker -p mapping, no proxy hop
required. The port number is the same on both sides.
The two corollaries that drive everything below:
- Bind
0.0.0.0, not127.0.0.1. A127.0.0.1-only listener inside the machine is NOT exposed to the Mac — the Mac can't see it. A workspace service that binds loopback-only is invisible to the host until you route it explicitly (seehd-port-watcher). - There are NO Docker port mappings. code-server binds
0.0.0.0:7380inside the machine and is therefore reachable atlocalhost:7380on the Mac automatically. Same for a dev server on0.0.0.0:5173→localhost:5173.
In-machine services (no port mapping needed)
| Machine Port | Service | Reachable on the Mac at |
|---|---|---|
| 7380 | code-server (VS Code) | localhost:7380 (exposed by Lima) |
| 8765 | relay WS | bind 0.0.0.0 → localhost:8765, or /proxy/8765/ |
| 8766 | relay HTTP | bind 0.0.0.0 → localhost:8766, or /proxy/8766/ |
| 5173 | your dev server | bind 0.0.0.0 → localhost:5173 |
All of these are also reachable through code-server's /proxy/<port>/ URL
(see below), which remains the preferred path for browser-facing HTTP/WS.
Host-Side Ports (Tauri app, native macOS listeners)
These ports run on the macOS host inside the HD Tauri process. They are native TCP listeners on the Mac — not inside the workspace machine.
| Port | Service | Notes |
|---|---|---|
| 1420 | Dev file server | Dev mode only, serves build/ |
| 8770 | adom-desktop direct connect | Owned by Adom Desktop, NOT HD |
| 47080 | HD discovery server | |
| 47083 | HD reverse proxy | Proxies to code-server |
| 47084 | HD control API | Health, setup, eval, CDP, /workspace/status, /port-forward |
| 47085 | WebView2 CDP | Chrome DevTools Protocol |
These are configurable via ~/Library/Application Support/hydrogen-desktop/ports.json.
Reaching a host service FROM the machine: use the adom-host gateway, NOT
127.0.0.1. macOS/vz does NOT mirror loopback, so a host listener on
127.0.0.1:<port> is not visible at the machine's own loopback. Instead the host
binds 0.0.0.0, and the machine reaches it via the adom-host gateway hostname:
adom-host:<port>. The machine's /etc/hosts maps adom-host to the gateway IP —
no extra firewall rule needed. (host.docker.internal is a Docker-ism HD
does NOT use — inside the machine it resolves to a different interface, not where
HD listens, so curls to it fail; use adom-host instead.)
Hostnames — reach the host at adom-host
From inside the machine → host machine
adom-host
macOS/vz does NOT mirror loopback, so use the adom-host gateway alias to reach
host listeners from inside the workspace machine. The machine's /etc/hosts maps
adom-host to the gateway IP, so adom-host:<port> reaches the Mac's host
listeners directly (the host binds 0.0.0.0). Used for:
ADOM_CARBON_URL=http://adom-host:{proxy_port}ADOM_HYDROGEN_URL=http://adom-host:{proxy_port}- Direct connect probe:
http://adom-host:8770/health - HD control API: the live URL in
~/.adom/hd-control-url(http://adom-host:<dynamic>)
The control port is dynamic per launch — read it from ~/.adom/hd-control-url
(your non-interactive Bash shells don't source .bashrc/profile.d, so the env var
alone is unreliable): BASE="$(cat ~/.adom/hd-control-url)".
From the host → machine services (via proxy)
http://127.0.0.1:{code_server_port}/proxy/{machine_port}/
ws://127.0.0.1:{code_server_port}/proxy/{machine_port}/
Example: relay health → http://127.0.0.1:7380/proxy/8766/health
Example: relay WS → ws://127.0.0.1:7380/proxy/8765/
This path always works for 0.0.0.0 AND 127.0.0.1-bound machine services,
because code-server itself is inside the machine and proxies to them locally.
It is the preferred path for browser-facing HTTP/WS even though direct
exposure also works for 0.0.0.0 listeners.
From the host → host-side services
http://127.0.0.1:{port}/
Control API → http://127.0.0.1:47084/health
Direct connect → http://127.0.0.1:8770/health
Relay Architecture
The relay (adom-desktop serve) runs inside the machine on ports 8765
(WebSocket) and 8766 (HTTP). It binds 0.0.0.0, so it is reachable both via
Lima's port exposure (localhost:8765 / localhost:8766 on the Mac) and via the
code-server proxy. The proxy path is preferred for connection stability:
- adom-desktop on the Mac connects as a WS client via the
code-server proxy:
ws://127.0.0.1:7380/proxy/8765/ - Registration happens via
server_addon the direct connect API (port 8770):{ "name": "HD Local Machine", "url": "ws://127.0.0.1:7380/proxy/8765/", "autoConnect": true } - Health checks use the proxy path:
http://127.0.0.1:7380/proxy/8766/health
This matches cloud containers where adom-desktop connects via
wss://<slug>.adom.cloud/proxy/8765/.
VSCODE_PROXY_URI
Inside the machine, VSCODE_PROXY_URI is set by code-server:
https://<slug>.adom.cloud/proxy/{{port}}/ (cloud)
http://localhost:7380/proxy/{{port}}/ (HD local, approximate)
Tools that build proxy URLs should use VSCODE_PROXY_URI when
available, falling back to http://127.0.0.1:{code_server}/proxy/{port}/.
In-machine command form
The workspace is the Adom-Workspace machine inside the hd-builder Lima VM, so
you reach it by entering its namespaces via nsenter, NEVER docker exec
(<leader> is the machine's leader PID):
limactl shell hd-builder -- sudo nsenter -t <leader> -a -S 1001 -G 1001 -- bash -lc '<cmd>'
# or the simpler form for a single command:
limactl shell hd-builder -- sudo nsenter -t <leader> -a -S 1001 -G 1001 -- <cmd>
NEVER Do These
- NEVER bind
127.0.0.1-only for a service that must be reachable from the Mac. macOS won't expose it (vz does NOT mirror loopback). Bind0.0.0.0, or register it with the port-forward registry (seehd-port-watcher). - NEVER use
host.docker.internalfor machine→host communication — it's a Docker-ism HD doesn't use; inside the machine it resolves to the wrong interface. Reach the host via theadom-hostgateway (vz does NOT mirror loopback, so127.0.0.1does NOT reach the host); read HD's live control URL from~/.adom/hd-control-url. - NEVER assume the host's
127.0.0.1is reachable from inside the machine. macOS/vz does NOT mirror loopback — use theadom-hostgateway alias (adom-host:<port>); the host binds0.0.0.0and the machine's/etc/hostsmapsadom-hostto the gateway. No firewall allow needed. - NEVER
docker exec/docker cpagainst the workspace. It's theAdom-Workspacemachine; enter it vialimactl shell hd-builder -- sudo nsenter -t <leader> -a -S 1001 -G 1001 --. - NEVER hardcode port numbers in machine code. Read from env vars
(
ADOM_CARBON_URL,ADOM_HYDROGEN_URL) or discover via the direct connect API. - NEVER assume adom-desktop direct connect (8770) is owned by HD. It's owned by Adom Desktop. HD's direct-api thread binds to 8770 as a fallback but Adom Desktop takes priority when both are running.
---
name: hd-networking
description: >
Port architecture, hostnames, and networking rules for Hydrogen Desktop.
MUST READ before adding ports, exposing a service to your macOS host,
referencing host URLs from inside the workspace, or wiring any service
communication.
Trigger words — HD port, HD network, port mapping, proxy, 127.0.0.1, loopback,
adom-host gateway, hd-control-url,
VSCODE_PROXY_URI, relay URL, code-server proxy, container networking,
ADOM_CARBON_URL, ADOM_HYDROGEN_URL, direct connect, 8770, 7380.
---
# Hydrogen Desktop — Networking & Port Architecture
> This is the machine-runtime version (default). For the legacy Docker container
> runtime (`HD_RUNTIME=docker`) see the docker/ bucket.
## Core Principle: expose `0.0.0.0` listeners to the Mac
The HD workspace is the `Adom-Workspace` machine (a systemd-nspawn machine booted
inside the `hd-builder` Lima VM), not a Docker container. macOS does **not** do
WSL2-style magic loopback mirroring — vz does NOT mirror loopback. Instead, Lima
exposes the machine's listeners to the Mac: **any service that binds
`0.0.0.0:<port>` inside the workspace machine is reachable at
`127.0.0.1:<sameport>` on the Mac** — no Docker `-p` mapping, no proxy hop
required. The port number is the same on both sides.
The two corollaries that drive everything below:
- **Bind `0.0.0.0`, not `127.0.0.1`.** A `127.0.0.1`-only listener inside the
machine is NOT exposed to the Mac — the Mac can't see it. A workspace service
that binds loopback-only is invisible to the host until you route it explicitly
(see `hd-port-watcher`).
- **There are NO Docker port mappings.** code-server binds `0.0.0.0:7380`
inside the machine and is therefore reachable at `localhost:7380` on the Mac
automatically. Same for a dev server on `0.0.0.0:5173` → `localhost:5173`.
## In-machine services (no port mapping needed)
| Machine Port | Service | Reachable on the Mac at |
|--------------|---------|-------------------------|
| 7380 | code-server (VS Code) | `localhost:7380` (exposed by Lima) |
| 8765 | relay WS | bind `0.0.0.0` → `localhost:8765`, or `/proxy/8765/` |
| 8766 | relay HTTP | bind `0.0.0.0` → `localhost:8766`, or `/proxy/8766/` |
| 5173 | your dev server | bind `0.0.0.0` → `localhost:5173` |
All of these are also reachable through code-server's `/proxy/<port>/` URL
(see below), which remains the preferred path for browser-facing HTTP/WS.
## Host-Side Ports (Tauri app, native macOS listeners)
These ports run on the macOS host inside the HD Tauri process. They are
native TCP listeners on the Mac — not inside the workspace machine.
| Port | Service | Notes |
|------|---------|-------|
| 1420 | Dev file server | Dev mode only, serves `build/` |
| 8770 | adom-desktop direct connect | Owned by Adom Desktop, NOT HD |
| 47080 | HD discovery server | |
| 47083 | HD reverse proxy | Proxies to code-server |
| 47084 | HD control API | Health, setup, eval, CDP, `/workspace/status`, `/port-forward` |
| 47085 | WebView2 CDP | Chrome DevTools Protocol |
These are configurable via `~/Library/Application Support/hydrogen-desktop/ports.json`.
**Reaching a host service FROM the machine:** use the `adom-host` gateway, NOT
`127.0.0.1`. macOS/vz does NOT mirror loopback, so a host listener on
`127.0.0.1:<port>` is not visible at the machine's own loopback. Instead the host
binds `0.0.0.0`, and the machine reaches it via the `adom-host` gateway hostname:
`adom-host:<port>`. The machine's `/etc/hosts` maps `adom-host` to the gateway IP —
no extra firewall rule needed. (`host.docker.internal` is a Docker-ism HD
does NOT use — inside the machine it resolves to a *different* interface, not where
HD listens, so curls to it fail; use `adom-host` instead.)
## Hostnames — reach the host at `adom-host`
### From inside the machine → host machine
```
adom-host
```
macOS/vz does NOT mirror loopback, so use the `adom-host` gateway alias to reach
host listeners from inside the workspace machine. The machine's `/etc/hosts` maps
`adom-host` to the gateway IP, so `adom-host:<port>` reaches the Mac's host
listeners directly (the host binds `0.0.0.0`). Used for:
- `ADOM_CARBON_URL=http://adom-host:{proxy_port}`
- `ADOM_HYDROGEN_URL=http://adom-host:{proxy_port}`
- Direct connect probe: `http://adom-host:8770/health`
- HD control API: the live URL in `~/.adom/hd-control-url` (`http://adom-host:<dynamic>`)
The control port is **dynamic per launch** — read it from `~/.adom/hd-control-url`
(your non-interactive Bash shells don't source `.bashrc`/`profile.d`, so the env var
alone is unreliable): `BASE="$(cat ~/.adom/hd-control-url)"`.
### From the host → machine services (via proxy)
```
http://127.0.0.1:{code_server_port}/proxy/{machine_port}/
ws://127.0.0.1:{code_server_port}/proxy/{machine_port}/
```
Example: relay health → `http://127.0.0.1:7380/proxy/8766/health`
Example: relay WS → `ws://127.0.0.1:7380/proxy/8765/`
This path always works for `0.0.0.0` AND `127.0.0.1`-bound machine services,
because code-server itself is inside the machine and proxies to them locally.
It is the preferred path for browser-facing HTTP/WS even though direct
exposure also works for `0.0.0.0` listeners.
### From the host → host-side services
```
http://127.0.0.1:{port}/
```
Control API → `http://127.0.0.1:47084/health`
Direct connect → `http://127.0.0.1:8770/health`
## Relay Architecture
The relay (`adom-desktop serve`) runs inside the machine on ports 8765
(WebSocket) and 8766 (HTTP). It binds `0.0.0.0`, so it is reachable both via
Lima's port exposure (`localhost:8765` / `localhost:8766` on the Mac) and via the
code-server proxy. The proxy path is preferred for connection stability:
1. **adom-desktop on the Mac** connects as a WS client via the
code-server proxy: `ws://127.0.0.1:7380/proxy/8765/`
2. **Registration** happens via `server_add` on the direct connect
API (port 8770):
```json
{
"name": "HD Local Machine",
"url": "ws://127.0.0.1:7380/proxy/8765/",
"autoConnect": true
}
```
3. **Health checks** use the proxy path:
`http://127.0.0.1:7380/proxy/8766/health`
This matches cloud containers where adom-desktop connects via
`wss://<slug>.adom.cloud/proxy/8765/`.
## VSCODE_PROXY_URI
Inside the machine, `VSCODE_PROXY_URI` is set by code-server:
```
https://<slug>.adom.cloud/proxy/{{port}}/ (cloud)
http://localhost:7380/proxy/{{port}}/ (HD local, approximate)
```
Tools that build proxy URLs should use `VSCODE_PROXY_URI` when
available, falling back to `http://127.0.0.1:{code_server}/proxy/{port}/`.
## In-machine command form
The workspace is the `Adom-Workspace` machine inside the `hd-builder` Lima VM, so
you reach it by entering its namespaces via `nsenter`, NEVER `docker exec`
(`<leader>` is the machine's leader PID):
```bash
limactl shell hd-builder -- sudo nsenter -t <leader> -a -S 1001 -G 1001 -- bash -lc '<cmd>'
# or the simpler form for a single command:
limactl shell hd-builder -- sudo nsenter -t <leader> -a -S 1001 -G 1001 -- <cmd>
```
## NEVER Do These
1. **NEVER bind `127.0.0.1`-only** for a service that must be reachable from
the Mac. macOS won't expose it (vz does NOT mirror loopback). Bind `0.0.0.0`,
or register it with the port-forward registry (see `hd-port-watcher`).
2. **NEVER use `host.docker.internal` for machine→host** communication — it's a
Docker-ism HD doesn't use; inside the machine it resolves to the wrong interface.
Reach the host via the `adom-host` gateway (vz does NOT mirror loopback, so
`127.0.0.1` does NOT reach the host); read HD's live control URL from
`~/.adom/hd-control-url`.
3. **NEVER assume the host's `127.0.0.1` is reachable from inside the machine.**
macOS/vz does NOT mirror loopback — use the `adom-host` gateway alias
(`adom-host:<port>`); the host binds `0.0.0.0` and the machine's `/etc/hosts`
maps `adom-host` to the gateway. No firewall allow needed.
4. **NEVER `docker exec` / `docker cp` against the workspace.** It's the
`Adom-Workspace` machine; enter it via
`limactl shell hd-builder -- sudo nsenter -t <leader> -a -S 1001 -G 1001 --`.
5. **NEVER hardcode port numbers** in machine code. Read from env vars
(`ADOM_CARBON_URL`, `ADOM_HYDROGEN_URL`) or discover via the direct
connect API.
6. **NEVER assume adom-desktop direct connect (8770) is owned by HD.**
It's owned by Adom Desktop. HD's direct-api thread binds to 8770
as a fallback but Adom Desktop takes priority when both are running.