fix(smolmachines): forward guest env on every exec + chown /home/node
test / unit (pull_request) Successful in 26s
test / integration (pull_request) Successful in 40s

Two issues kept claude's TUI from drawing after launch:

1. smolvm pack remaps OCI-layer ownership to the host invoker's
   uid (501 on macOS) instead of preserving the image's
   USER node (uid 1000). /home/node ends up owned by some uid
   that doesn't exist in the VM, so when claude runs as node it
   can't appendFileSync to ~/.claude.json on startup — fails
   with ENOENT and the TUI hangs. Fix: chown -R node:node
   /home/node after machine_start, before provision.

2. smolvm machine_create -e sets env on PID 1 but it doesn't
   propagate to fresh exec process trees (verified empirically:
   `smolvm machine exec -- printenv` shows none of the
   machine_create env vars). Claude was running with no
   HTTPS_PROXY / CLAUDE_CODE_OAUTH_TOKEN / NODE_EXTRA_CA_CERTS,
   so even the auth-validation step bailed silently. Fix:
   thread `guest_env` through to the SmolmachinesBottle handle
   and re-pass every entry via `-e K=V` on every machine_exec
   call (interactive claude and shell exec both).

Also fills in the same `CLAUDE_CODE_OAUTH_TOKEN=egress-
placeholder` + telemetry-off env the docker backend's
forwarded_env carries, plus the NODE_EXTRA_CA_CERTS /
SSL_CERT_FILE / REQUESTS_CA_BUNDLE trust trio.

Verified end-to-end on Docker Desktop / macOS: claude's TUI
renders cleanly with the bypass-permissions banner.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-27 15:18:21 -04:00
parent 35edf50f21
commit 91955ec59f
3 changed files with 64 additions and 3 deletions
+18 -2
View File
@@ -117,10 +117,26 @@ def launch(
_smolvm.machine_start(plan.machine_name)
stack.callback(_smolvm.machine_stop, plan.machine_name)
# 5. Provision (CA / prompt / skills / git / supervise).
# 5. Reclaim /home/node for the node user. smolvm's pack
# process remaps OCI-layer ownership to the host invoker's
# uid (501 on macOS) rather than preserving the image's
# uid 1000 — so without this chown, node can't write its
# own dotfiles (claude appendFileSync on
# ~/.claude.json bails with ENOENT/EPERM and the TUI hangs
# without surfacing the error).
_smolvm.machine_exec(
plan.machine_name,
["chown", "-R", "node:node", "/home/node"],
)
# 6. Provision (CA / prompt / skills / git / supervise).
prompt_path = provision(plan, plan.machine_name)
yield SmolmachinesBottle(plan.machine_name, prompt_path=prompt_path)
yield SmolmachinesBottle(
plan.machine_name,
prompt_path=prompt_path,
guest_env=plan.guest_env,
)
finally:
stack.close()