main
Kyle Bergstedt Publish 1.1.112 8d0a92c 5d ago

name: adom-vscode-container description: "Identify the Adom workspace (hd = local Hydrogen workspace — WSL2 on Windows, the systemd-nspawn workspace machine on macOS; hw = Adom cloud container), check and inject the Adom api key at /var/run/adom/api-key, discover the extension's real port when 8821 is taken, and feature-detect verbs via GET /health. Trigger words: container type, hd or hw, what kind of container, am I in hydrogen desktop, cloud container, container identity, api key status, is the api key good, refresh api key, inject api key, /var/run/adom/api-key, session key expiring, port discovery, port.json, ADOM_VSCODE_PORT, health verbs, feature detect, carbon url, vscode proxy uri."

Parent skill: adom-vscode

adom-vscode-container, identity + api key + discovery

Container identity

adom-vscode container

Returns kind plus the raw evidence so callers can gate precisely:

  • kind: "hd": local Hydrogen workspace — the union of the per-platform markers:
    • Windows (WSL2): /etc/profile.d/hd-env.sh exists (markers.hdEnvSh), or a WSL kernel — microsoft/wsl in uname -r (markers.wslKernel).
    • macOS (Lima VM + systemd-nspawn machine): /etc/profile.d/hydrogen-env.sh exists (markers.hydrogenEnvSh), or ADOM_HYDROGEN_MACHINE is set in the extension-host env (markers.hydrogenMachine). Neither Windows marker is present there — the kernel is a plain generic Linux kernel — which is why the machine used to report kind:"unknown" and refuse exec (fixed 2026-09-17).
  • kind: "hw": Adom cloud container. Marker: VSCODE_PROXY_URI contains .adom.cloud (markers.cloudProxy). The two macOS markers are ignored when this one is set, so a cloud container can never be widened into hd by an env var.
  • kind: "unknown": none of the markers matched (report the markers, don't guess).

Also included: hostname, os (from /etc/os-release), kernel, arch, workspaceUser, and env.carbonUrl (ADOM_CARBON_URL), env.hydrogenUrl, env.vscodeProxyUri, plus env.hydrogenMachine (ADOM_HYDROGEN_MACHINE), the markers object itself and execAllowed (whether the exec verbs are enabled here; they are local-workspace only, kind:hd). HTTP: GET /container.

API key (/var/run/adom/api-key)

adom-vscode apikey status         # exists, readable, size, mtime, ageSeconds, sha256_8
adom-vscode apikey set --stdin < new-key.txt
adom-vscode apikey set <value>
  • status never prints the key itself, only a sha256 fingerprint (first 8 hex), so it is safe to echo into logs and chat.
  • set writes through passwordless sudo (the path is root-owned), creating /var/run/adom if needed, mode 644, trailing newline normalized. Use it when the frontend completes a login refresh and needs the container to pick up the new session key.
  • Typical flow with the queue (adom-vscode ships NO built-in watcher; the polling process and its cadence belong to HD or another owner): that process sees the key aging, pushes a login/refresh-needed event (see adom-vscode-queue); frontend refreshes the session and calls apikey set; the process confirms via apikey status (fresh mtime + new fingerprint).

HTTP: GET /apikey, POST /apikey {value}.

Port discovery (when 8821 is not the port)

The extension prefers 127.0.0.1:8821. If busy it walks 8822..8831, then an ephemeral port, and writes the truth to:

~/.local/share/adom-vscode/port.json
# { "port": N, "preferredPort": 8821, "pid": ..., "version": ..., "startedAt": ... }
  • The CLI resolves automatically: ADOM_VSCODE_PORT env var > port.json > 8821.
  • External services expecting 8821 should read port.json when 8821 refuses.
  • This is not hypothetical: on the macOS workspace machine the live extension has been on 8822 (preferredPort: 8821) since a detached host took 8821. Read port.json; never hard-code 8821. Anything that silences or forwards the port (a remote.portsAttributes entry, a port-hint registration) must cover the whole 8821-8831 fallback range, not just 8821.
  • The extension also drops an adom-ports claim breadcrumb when that CLI exists.

Feature detection

adom-vscode health

GET /health returns {version, port, preferredPort, portFile, verbs: [...]}. Gate on the verbs array (e.g. exec.stream, queue.push, apikey.set), not on version strings. Note: exec / exec.stream only appear on HD-local containers; on cloud (hw) containers they are disabled by security policy and absent from the roster (execAllowed: false). An extension that answers /health without a verbs field predates 1.1.10 and needs a window reload or update.