Hydrogen Desktop Bootstrap for Windows (apps + skills)
Public Made by Adomby adom
The Windows (WSL2) platform layer for Hydrogen Desktop.
name: hd-port-watcher
description: >
Port reachability in Hydrogen Desktop — and why you do NOT need to set up
port forwarding. Under WSL2 networkingMode=mirrored (HD's default, which the
setup cascade hard-targets), the distro SHARES the Windows loopback, so every
port a service binds in the workspace — including 127.0.0.1-only OAuth
callback servers from VS Code extensions (Codex, Copilot, GitLens) — is
reachable at the same localhost:<port> on Windows automatically. No daemon,
no proxies, no manual mapping. The old port-watcher daemon was a Docker/NAT-era
mechanism and is not used under mirrored networking. Trigger words: port
forward, dynamic port, OAuth callback, Codex auth, extension auth, port mapping,
localhost port, port watcher, port proxy, does port forwarding work, why can't
I reach my port.
HD Port Watcher — you don't port-watch anymore (mirrored networking does it)
TL;DR for the AI: do NOT try to set up port forwarding in HD. Under the default WSL2
networkingMode=mirrored, ports just work. If alocalhost:<port>from the workspace isn't reachable on Windows, the fix is almost never "forward the port" — it's "confirm mirrored is on" (see below), or the service didn't actually bind.
Why there's nothing to do (mirrored networking)
HD's setup cascade hard-targets networkingMode=mirrored in .wslconfig. Under
mirrored, the WSL2 distro shares the Windows loopback interface, so any listener
in the distro — 0.0.0.0:<port> AND 127.0.0.1:<port> — is reachable at the same
localhost:<port> on Windows with zero setup. This is why HD's own OAuth host-proxy
is explicitly skipped under mirrored (lib.rs: "under WSL2 mirrored it's
redundant… host proxy not needed").
The legacy daemon (Docker / non-mirrored only — historical)
The port-watcher daemon below existed for the pre-mirroring world: the Docker/NAT
runtime, where the distro could NOT reach Windows loopback, so a daemon watched
/proc/net/tcp for new listeners and HD spun up host-side TCP proxies. It closed
two gaps that DO NOT EXIST under mirrored:
127.0.0.1-only listeners. NAT-mode WSL2 did NOT forward loopback-only listeners. VS Code extensions (Codex, Copilot, GitLens) start OAuth callback servers that frequently bind127.0.0.1; without mirroring the browser redirect tolocalhost:{port}/auth/callback?code=...hit a port that didn't exist on Windows.- Auto-forward lag / wedge. WSL2 localhost-forwarding can lag or break
even for
0.0.0.0listeners (a real HD-logged failure mode: "code-server alive in distro but Windows can't reach 127.0.0.1:7380"). The watcher gives HD a reliable host-side proxy that doesn't depend on WSL2's forwarder.
The Solution
A lightweight daemon inside the distro monitors /proc/net/tcp for new
listening ports. When one appears (that wasn't there at boot and isn't a known
service), it notifies HD's control API. HD creates a host-side TCP proxy on
that same port, forwarding through code-server's /proxy/{port} to reach the
distro. The browser redirect just works — even for loopback-only callback
servers WSL2 wouldn't have forwarded.
Architecture
WSL2 distro (Adom-Workspace): Windows Host:
┌─────────────────────┐ ┌──────────────────────┐
│ Codex creates server │ │ HD Control API │
│ on 127.0.0.1:1455 │ │ (:47084) │
│ (WSL2 won't forward) │ │ │
│ port-watcher.sh │──POST──→ │ /port-forward │
│ detects port 1455 │ open │ creates TCP proxy │
│ │ │ on localhost:1455 │
│ code-server │ │ │
│ /proxy/1455/ ←───────┼───────── │ proxy forwards here │
└─────────────────────┘ └──────────────────────┘
↑
Browser redirects to
localhost:1455/callback
What Gets Forwarded (and what doesn't)
FORWARDED (temporary, appeared after boot):
- OAuth callback ports from extensions (Codex, Copilot, etc.) — especially
127.0.0.1-only ones WSL2 cannot auto-forward - Any new TCP listener that wasn't running when the distro started
- Auto-expires after 5 minutes (OAuth callbacks are fast)
NOT FORWARDED (known services, stay behind /proxy/{port} or auto-forward):
- code-server (8080 inside distro; auto-forwarded as 7380 mapping/
0.0.0.0) - relay WS/HTTP (8765, 8766)
- adom-vscode (8821)
- shotlog (8820)
- Any port in the KNOWN_PORTS list
- Any port that was listening at distro boot time
- Any port ≤ 1023
This keeps the local WSL workspace behaving like cloud Hydrogen — known
services use /proxy/{port} (or WSL2 auto-forward for 0.0.0.0 binds), and
only short-lived / loopback-only OAuth ports get a dedicated host proxy.
API Endpoints
POST /port-forward
Create or remove a host-side TCP proxy. Called by the distro's watcher.
{"port": 1455, "action": "open"} // Create proxy
{"port": 1455, "action": "close"} // Remove proxy
GET /port-forward
List currently forwarded ports.
{"ok": true, "forwarded_ports": [1455], "count": 1}
The Watcher Daemon
scripts/port-watcher.sh runs inside the distro:
- Polls
/proc/net/tcpevery 2 seconds - Diffs against previous scan
- New port (not in KNOWN_PORTS whitelist) → POST
/port-forwardopen - Port gone → POST
/port-forwardclose - Port open > 5 minutes → auto-expire (likely a service, not OAuth)
No boot snapshot — the whitelist is the sole source of truth. Services like shotlog can start at any time (days/weeks later) and will never get a host proxy as long as their port is in the whitelist.
The watcher reaches HD at 127.0.0.1:<control>/port-forward — WSL2 mirrored
networking shares loopback with the Windows host, so 127.0.0.1 from inside the
distro reaches HD's host listener. The control port is dynamic per launch; the
live control URL is in ~/.adom/hd-control-url.
Installation
The watcher is a bash script (port-watcher.sh) deployed into the distro
during setup steps. Installation uses wsl -d Adom-Workspace, NOT
docker cp/docker exec:
# Copy the script into the distro (from Windows host, via the \\wsl$ share or wsl cat)
wsl -d Adom-Workspace -u root -- bash -c 'cat > /usr/local/bin/port-watcher.sh' < port-watcher.sh
wsl -d Adom-Workspace -u root -- chmod +x /usr/local/bin/port-watcher.sh
# Launch detached inside the distro
wsl -d Adom-Workspace -u adom -- bash -c 'nohup bash /usr/local/bin/port-watcher.sh >/tmp/port-watcher.log 2>&1 &'
The script parses /proc/net/tcp directly in awk — it filters listener rows
(state 0A), splits the hex local_address, and converts the port with
strtonum("0x" …). No Python is involved.
UI: Toast + Port Mappings Dialog
Toast notification: When a port is forwarded, HD shows an info toast:
"Port 1455 forwarded to workspace". When removed: "Port 1455 forwarding stopped".
Powered by the port-forward-changed Tauri event.
Port Mappings dialog: Accessible from the Adom menu → Admin → "Port Mappings".
Shows auto-forwarded 0.0.0.0 ports AND dynamic watcher-created proxies. Dynamic
ports have a "Stop" button. Matches the API Explorer visual style.
Adding New Known Services
When a new service is added to the distro that runs a persistent TCP server,
add its port to the KNOWN_PORTS whitelist string in port-watcher.sh
(scripts/port-watcher.sh:16):
KNOWN_PORTS="8080 8765 8766 8821 8820 36035 47084 47083 47085 1420 7380 8770 9000"
This prevents it from getting a dedicated host proxy. Services should bind
0.0.0.0 (auto-forwarded) or use /proxy/{port} instead, consistent with web
Hydrogen.
Why Both Layers
There are TWO distinct forwarding mechanisms. The /proc/net/tcp watcher above
is Layer 2. Layer 1 is a completely separate path — the OAuth-callback
localhost proxy baked into HD itself — and it's why VS Code extension logins
"just work" without any watcher round-trip.
Layer 1 — the OAuth-callback localhost proxy (zero-latency, the primary path)
HD intercepts top-level WebView2 navigations via
setup_frame_navigation_interception() (hd-app/src/lib.rs:75). On every
navigation it runs extract_oauth_callback_port() (lib.rs:384), which
triple-URL-decodes the target URL (urlencoding::decode x3) and scans each
decode layer — plus the raw and the percent-encoded
redirect_uri=http%3A%2F%2Flocalhost%3A… form — for
redirect_uri=http://localhost:<port>. When it finds one, it calls
start_oauth_callback_proxy(port) (lib.rs:411), which pre-binds
127.0.0.1:<port> on the Windows host (via hd_control::reuse_bind_tcp) and
waits up to ~5 min for one inbound request. When the browser redirects to
localhost:<port>/callback?code=…, HD's pre-bound listener accepts it and
forwards the full path into the distro via code-server:
http://localhost:<code_server_port>/proxy/<port>/<path> (lib.rs:454), then
streams code-server's response back to the browser.
So the callback port is bound the instant the OAuth URL is seen leaving the WebView — before the provider ever redirects — which is why Codex / Copilot OAuth completes with zero manual setup and zero watcher latency.
Control / callback endpoints (HD control API, port 47084):
POST /start-oauth-proxy— manually pre-bind a callback port (same effect as the navigation interceptor; used by the manual fallback).GET /oauth/callback— HD's own built-in callback receiver (used by the Claude Code PKCE flow); do not call directly.
Failure mode the AI may see: if the proxy binds but code-server can't be
reached, HD returns a styled 502 Bad Gateway page (dark #0d1117
background, teal #00b8b0 "OAuth callback proxy" heading) reading roughly "HD
tried to forward this OAuth callback to the container via code-server proxy
(port …/proxy/…), but it failed. The extension that started this OAuth flow may
need its callback port (…) mapped to the host." Seeing that page in a browser
means Layer 1 fired but the distro-side proxy hop failed — check the HD log for
[oauth-proxy] lines and that code-server is alive.
Layer 2 — the port watcher (/proc/net/tcp daemon, the safety net)
The bash daemon documented above. Safety net for extensions that open OAuth
URLs through mechanisms that bypass top-level WebView navigations, AND the only
thing that surfaces 127.0.0.1-only callback servers WSL2 silently won't
forward. Note: when an app has already registered its port via the
adom-port-hints protocol, the watcher skips it (it only owns the fallback for
third-party extensions that don't know HD exists).
Layer 3 — manual
POST /start-oauth-proxy or POST /port-forward (or
adom-cli port-forward register) for cases where both automatic layers miss.
Port Conflicts
If the host port is already taken by another Windows app, the forwarding fails silently. The OAuth callback will break. Mitigation: extensions typically pick a different random port on retry. HD logs the conflict.
How OAuth Callbacks Work End-to-End
- Extension (Codex) starts HTTP server on
127.0.0.1:1455inside the distro - port-watcher.sh detects port 1455 (not in boot snapshot, not known) — note WSL2 would NOT have auto-forwarded this loopback-only listener
- Watcher POSTs
{"port":1455,"action":"open"}to HD - HD creates TCP proxy:
localhost:1455→ code-server/proxy/1455/ - Extension opens browser with
redirect_uri=http://localhost:1455/callback - User authenticates, browser redirects to
localhost:1455/callback?code=... - HD's proxy receives the request, forwards into the distro via code-server
- Extension receives callback, completes auth
- Extension closes its server, port 1455 disappears
- Watcher POSTs
{"port":1455,"action":"close"}, HD removes proxy
Debugging
All debugging runs inside the distro via wsl -d Adom-Workspace -u adom -- ...,
NEVER docker exec:
# Check what ports the watcher sees
wsl -d Adom-Workspace -u adom -- bash -c "cat /proc/net/tcp | awk '\$4 == \"0A\" { split(\$2, a, \":\"); printf \"%d\\n\", strtonum(\"0x\" a[2]) }' | sort -n"
# Check the watcher's own log
wsl -d Adom-Workspace -u adom -- cat /tmp/port-watcher.log
# Check what HD has forwarded (from Windows host)
curl -sf http://127.0.0.1:47084/port-forward
# Manually forward a port (for testing, from Windows host)
curl -sf -X POST http://127.0.0.1:47084/port-forward \
-H Content-Type:application/json \
-d '{"port":1455,"action":"open"}'
# Check HD log for port-fwd events
# Look for [port-fwd] entries
Related skills
hd-networking—0.0.0.0auto-forward, reaching the host at127.0.0.1(mirrored networking / loopback), NEVER do thesehd-ports— bind0.0.0.0first; fall back to the port-forward registry for127.0.0.1-only services or unreliable auto-forward
---
name: hd-port-watcher
description: >
Port reachability in Hydrogen Desktop — and why you do NOT need to set up
port forwarding. Under WSL2 networkingMode=mirrored (HD's default, which the
setup cascade hard-targets), the distro SHARES the Windows loopback, so every
port a service binds in the workspace — including `127.0.0.1`-only OAuth
callback servers from VS Code extensions (Codex, Copilot, GitLens) — is
reachable at the same `localhost:<port>` on Windows automatically. No daemon,
no proxies, no manual mapping. The old port-watcher daemon was a Docker/NAT-era
mechanism and is not used under mirrored networking. Trigger words: port
forward, dynamic port, OAuth callback, Codex auth, extension auth, port mapping,
localhost port, port watcher, port proxy, does port forwarding work, why can't
I reach my port.
---
# HD Port Watcher — you don't port-watch anymore (mirrored networking does it)
> **TL;DR for the AI: do NOT try to set up port forwarding in HD.** Under the
> default WSL2 `networkingMode=mirrored`, ports just work. If a `localhost:<port>`
> from the workspace isn't reachable on Windows, the fix is almost never "forward
> the port" — it's "confirm mirrored is on" (see below), or the service didn't
> actually bind.
## Why there's nothing to do (mirrored networking)
HD's setup cascade hard-targets `networkingMode=mirrored` in `.wslconfig`. Under
mirrored, the WSL2 distro shares the Windows loopback interface, so **any** listener
in the distro — `0.0.0.0:<port>` AND `127.0.0.1:<port>` — is reachable at the same
`localhost:<port>` on Windows with zero setup. This is why HD's own OAuth host-proxy
is explicitly **skipped** under mirrored (`lib.rs`: *"under WSL2 mirrored it's
redundant… host proxy not needed"*).
## The legacy daemon (Docker / non-mirrored only — historical)
The `port-watcher` daemon below existed for the pre-mirroring world: the Docker/NAT
runtime, where the distro could NOT reach Windows loopback, so a daemon watched
`/proc/net/tcp` for new listeners and HD spun up host-side TCP proxies. It closed
two gaps that DO NOT EXIST under mirrored:
1. **`127.0.0.1`-only listeners.** NAT-mode WSL2 did NOT forward loopback-only
listeners. VS Code extensions (Codex, Copilot, GitLens) start OAuth callback
servers that frequently bind `127.0.0.1`; without mirroring the browser redirect
to `localhost:{port}/auth/callback?code=...` hit a port that didn't exist on Windows.
2. **Auto-forward lag / wedge.** WSL2 localhost-forwarding can lag or break
even for `0.0.0.0` listeners (a real HD-logged failure mode:
"code-server alive in distro but Windows can't reach 127.0.0.1:7380"). The
watcher gives HD a reliable host-side proxy that doesn't depend on WSL2's
forwarder.
## The Solution
A lightweight daemon inside the distro monitors `/proc/net/tcp` for new
listening ports. When one appears (that wasn't there at boot and isn't a known
service), it notifies HD's control API. HD creates a host-side TCP proxy on
that same port, forwarding through code-server's `/proxy/{port}` to reach the
distro. The browser redirect just works — even for loopback-only callback
servers WSL2 wouldn't have forwarded.
## Architecture
```
WSL2 distro (Adom-Workspace): Windows Host:
┌─────────────────────┐ ┌──────────────────────┐
│ Codex creates server │ │ HD Control API │
│ on 127.0.0.1:1455 │ │ (:47084) │
│ (WSL2 won't forward) │ │ │
│ port-watcher.sh │──POST──→ │ /port-forward │
│ detects port 1455 │ open │ creates TCP proxy │
│ │ │ on localhost:1455 │
│ code-server │ │ │
│ /proxy/1455/ ←───────┼───────── │ proxy forwards here │
└─────────────────────┘ └──────────────────────┘
↑
Browser redirects to
localhost:1455/callback
```
## What Gets Forwarded (and what doesn't)
**FORWARDED (temporary, appeared after boot):**
- OAuth callback ports from extensions (Codex, Copilot, etc.) — especially
`127.0.0.1`-only ones WSL2 cannot auto-forward
- Any new TCP listener that wasn't running when the distro started
- Auto-expires after 5 minutes (OAuth callbacks are fast)
**NOT FORWARDED (known services, stay behind /proxy/{port} or auto-forward):**
- code-server (8080 inside distro; auto-forwarded as 7380 mapping/`0.0.0.0`)
- relay WS/HTTP (8765, 8766)
- adom-vscode (8821)
- shotlog (8820)
- Any port in the KNOWN_PORTS list
- Any port that was listening at distro boot time
- Any port ≤ 1023
This keeps the local WSL workspace behaving like cloud Hydrogen — known
services use `/proxy/{port}` (or WSL2 auto-forward for `0.0.0.0` binds), and
only short-lived / loopback-only OAuth ports get a dedicated host proxy.
## API Endpoints
### POST /port-forward
Create or remove a host-side TCP proxy. Called by the distro's watcher.
```json
{"port": 1455, "action": "open"} // Create proxy
{"port": 1455, "action": "close"} // Remove proxy
```
### GET /port-forward
List currently forwarded ports.
```json
{"ok": true, "forwarded_ports": [1455], "count": 1}
```
## The Watcher Daemon
`scripts/port-watcher.sh` runs inside the distro:
1. Polls `/proc/net/tcp` every 2 seconds
2. Diffs against previous scan
3. New port (not in KNOWN_PORTS whitelist) → POST `/port-forward` open
4. Port gone → POST `/port-forward` close
5. Port open > 5 minutes → auto-expire (likely a service, not OAuth)
No boot snapshot — the whitelist is the sole source of truth. Services
like shotlog can start at any time (days/weeks later) and will never
get a host proxy as long as their port is in the whitelist.
The watcher reaches HD at `127.0.0.1:<control>/port-forward` — WSL2 mirrored
networking shares loopback with the Windows host, so `127.0.0.1` from inside the
distro reaches HD's host listener. The control port is dynamic per launch; the
live control URL is in `~/.adom/hd-control-url`.
## Installation
The watcher is a bash script (`port-watcher.sh`) deployed into the distro
during setup steps. Installation uses `wsl -d Adom-Workspace`, NOT
`docker cp`/`docker exec`:
```bash
# Copy the script into the distro (from Windows host, via the \\wsl$ share or wsl cat)
wsl -d Adom-Workspace -u root -- bash -c 'cat > /usr/local/bin/port-watcher.sh' < port-watcher.sh
wsl -d Adom-Workspace -u root -- chmod +x /usr/local/bin/port-watcher.sh
# Launch detached inside the distro
wsl -d Adom-Workspace -u adom -- bash -c 'nohup bash /usr/local/bin/port-watcher.sh >/tmp/port-watcher.log 2>&1 &'
```
The script parses `/proc/net/tcp` directly in `awk` — it filters listener rows
(state `0A`), splits the hex `local_address`, and converts the port with
`strtonum("0x" …)`. No Python is involved.
## UI: Toast + Port Mappings Dialog
**Toast notification:** When a port is forwarded, HD shows an info toast:
"Port 1455 forwarded to workspace". When removed: "Port 1455 forwarding stopped".
Powered by the `port-forward-changed` Tauri event.
**Port Mappings dialog:** Accessible from the Adom menu → Admin → "Port Mappings".
Shows auto-forwarded `0.0.0.0` ports AND dynamic watcher-created proxies. Dynamic
ports have a "Stop" button. Matches the API Explorer visual style.
## Adding New Known Services
When a new service is added to the distro that runs a persistent TCP server,
add its port to the `KNOWN_PORTS` whitelist string in `port-watcher.sh`
(`scripts/port-watcher.sh:16`):
```bash
KNOWN_PORTS="8080 8765 8766 8821 8820 36035 47084 47083 47085 1420 7380 8770 9000"
```
This prevents it from getting a dedicated host proxy. Services should bind
`0.0.0.0` (auto-forwarded) or use `/proxy/{port}` instead, consistent with web
Hydrogen.
## Why Both Layers
There are TWO distinct forwarding mechanisms. The `/proc/net/tcp` watcher above
is **Layer 2**. Layer 1 is a completely separate path — the OAuth-callback
**localhost proxy** baked into HD itself — and it's why VS Code extension logins
"just work" without any watcher round-trip.
### Layer 1 — the OAuth-callback localhost proxy (zero-latency, the primary path)
HD intercepts top-level WebView2 navigations via
`setup_frame_navigation_interception()` (`hd-app/src/lib.rs:75`). On every
navigation it runs `extract_oauth_callback_port()` (`lib.rs:384`), which
**triple-URL-decodes** the target URL (`urlencoding::decode` x3) and scans each
decode layer — plus the raw and the percent-encoded
`redirect_uri=http%3A%2F%2Flocalhost%3A…` form — for
`redirect_uri=http://localhost:<port>`. When it finds one, it calls
`start_oauth_callback_proxy(port)` (`lib.rs:411`), which **pre-binds**
`127.0.0.1:<port>` on the Windows host (via `hd_control::reuse_bind_tcp`) and
waits up to ~5 min for one inbound request. When the browser redirects to
`localhost:<port>/callback?code=…`, HD's pre-bound listener accepts it and
forwards the full path into the distro via code-server:
`http://localhost:<code_server_port>/proxy/<port>/<path>` (`lib.rs:454`), then
streams code-server's response back to the browser.
So the callback port is bound the *instant* the OAuth URL is seen leaving the
WebView — before the provider ever redirects — which is why Codex / Copilot
OAuth completes with zero manual setup and zero watcher latency.
**Control / callback endpoints** (HD control API, port 47084):
- `POST /start-oauth-proxy` — manually pre-bind a callback port (same effect as
the navigation interceptor; used by the manual fallback).
- `GET /oauth/callback` — HD's own built-in callback receiver (used by the
Claude Code PKCE flow); do not call directly.
**Failure mode the AI may see:** if the proxy binds but code-server can't be
reached, HD returns a styled **502 Bad Gateway** page (dark `#0d1117`
background, teal `#00b8b0` "OAuth callback proxy" heading) reading roughly *"HD
tried to forward this OAuth callback to the container via code-server proxy
(port …/proxy/…), but it failed. The extension that started this OAuth flow may
need its callback port (…) mapped to the host."* Seeing that page in a browser
means Layer 1 fired but the distro-side proxy hop failed — check the HD log for
`[oauth-proxy]` lines and that code-server is alive.
### Layer 2 — the port watcher (`/proc/net/tcp` daemon, the safety net)
The bash daemon documented above. Safety net for extensions that open OAuth
URLs through mechanisms that bypass top-level WebView navigations, AND the only
thing that surfaces `127.0.0.1`-only callback servers WSL2 silently won't
forward. Note: when an app has already registered its port via the
adom-port-hints protocol, the watcher skips it (it only owns the fallback for
third-party extensions that don't know HD exists).
### Layer 3 — manual
`POST /start-oauth-proxy` or `POST /port-forward` (or
`adom-cli port-forward register`) for cases where both automatic layers miss.
## Port Conflicts
If the host port is already taken by another Windows app, the forwarding
fails silently. The OAuth callback will break. Mitigation: extensions
typically pick a different random port on retry. HD logs the conflict.
## How OAuth Callbacks Work End-to-End
1. Extension (Codex) starts HTTP server on `127.0.0.1:1455` inside the distro
2. port-watcher.sh detects port 1455 (not in boot snapshot, not known) —
note WSL2 would NOT have auto-forwarded this loopback-only listener
3. Watcher POSTs `{"port":1455,"action":"open"}` to HD
4. HD creates TCP proxy: `localhost:1455` → code-server `/proxy/1455/`
5. Extension opens browser with `redirect_uri=http://localhost:1455/callback`
6. User authenticates, browser redirects to `localhost:1455/callback?code=...`
7. HD's proxy receives the request, forwards into the distro via code-server
8. Extension receives callback, completes auth
9. Extension closes its server, port 1455 disappears
10. Watcher POSTs `{"port":1455,"action":"close"}`, HD removes proxy
## Debugging
All debugging runs inside the distro via `wsl -d Adom-Workspace -u adom -- ...`,
NEVER `docker exec`:
```bash
# Check what ports the watcher sees
wsl -d Adom-Workspace -u adom -- bash -c "cat /proc/net/tcp | awk '\$4 == \"0A\" { split(\$2, a, \":\"); printf \"%d\\n\", strtonum(\"0x\" a[2]) }' | sort -n"
# Check the watcher's own log
wsl -d Adom-Workspace -u adom -- cat /tmp/port-watcher.log
# Check what HD has forwarded (from Windows host)
curl -sf http://127.0.0.1:47084/port-forward
# Manually forward a port (for testing, from Windows host)
curl -sf -X POST http://127.0.0.1:47084/port-forward \
-H Content-Type:application/json \
-d '{"port":1455,"action":"open"}'
# Check HD log for port-fwd events
# Look for [port-fwd] entries
```
## Related skills
- `hd-networking` — `0.0.0.0` auto-forward, reaching the host at `127.0.0.1`
(mirrored networking / loopback), NEVER do these
- `hd-ports` — bind `0.0.0.0` first; fall back to the port-forward registry for
`127.0.0.1`-only services or unreliable auto-forward