Skip to content

fix(sandbox): boot the guest server via a launcher, not a CLI - #200

Merged
quickbeard merged 1 commit into
mainfrom
sandbox-guest-launcher
Aug 12, 2026
Merged

quickbeard merged 1 commit into
mainfrom
sandbox-guest-launcher

Conversation

@quickbeard

Copy link
Copy Markdown
Owner

Summary

Follow-up to the image pipeline (#199): the first real end-to-end VM boot on macOS/arm64 (via script/e2e-vz.ts against a locally built image) revealed the guest never actually served, and fixes it. The whole stack now boots green.

The bug

The providers assumed the mounted server bundle (dist/node/node.js) was a CLI with a serve subcommand — that's how the WSL sidecar works, because it installs the full opencode CLI binary. But the desktop app's bundle is a library module (export { Server, Config, bootstrap }), which the desktop boots by calling Server.listen({...}) directly (packages/desktop/src/main/sidecar.ts). So node node.js serve --hostname … --port … imported the module, executed nothing, and exited 0 → init exited → Kernel panic - Attempted to kill init.

The fix

  • image/guest/server-launch.mjs — a tiny guest launcher that imports the bundle and calls Server.listen({ port, hostname, username, password, cors:["oc://renderer"] }) with values from the boot env, mirroring the desktop sidecar. Baked at /opt/codev-guest/server-launch.mjs; both the vz and bwrap providers run it instead of a fictional CLI.
  • image/Dockerfile — install the two externalized bundle deps (jsonc-parser, @lydell/node-pty) into /opt/node_modules so ESM parent-dir resolution finds them without modifying the host bundle (and node-pty's arch-correct prebuild comes via its optionalDependencies); add busybox-static for a clean poweroff.
  • boot.ts — sandboxBootEnv now carries CODEV_SERVER_HOSTNAME/CODEV_SERVER_PORT and sandboxServeArgs is gone; new SANDBOX_GUEST_LAUNCHER constant. The vz boot.json and both providers updated to match.
  • image/guest/codev-init — run the launcher, seed /etc/skel into the guest home, and busybox poweroff -f with a /proc/sysrq-trigger fallback so the helper's guestDidStop fires cleanly.
  • build-image.ts — clear only *.zip from the office staging dir so its tracked .gitignore/.keep survive (a prior run had deleted them).

Verification

Real boot, not a fake helper:

availability: { supported: true, warning: null }
create   1.9s
start
server: http://127.0.0.1:55016 (user codev)
health: OK
e2e OK

Chain exercised: image download → SHA-256 verify → unpack → COW disk clone → codev-vmm boot → custom 6.6 kernel → virtio-fs mounts → launcher → Server.listen → vsock↔loopback bridge → authenticated GET /global/health from the host. Built image sizes (arm64, base toolchain, no office layer): vz-arm64 235 MiB, bwrap-arm64 222 MiB compressed.

bun test 60 pass / 2 skip, bun typecheck clean.

Notes

  • Fork-only files only; no upstream edits, no DIVERGENCES.md rows.
  • Office-layer build (your codev-office bundle) and the x64 arch are the remaining pipeline runs before publishing a real versioned manifest; this PR is the correctness fix that makes any built image actually boot.

🤖 Generated with Claude Code

First real VM boot (via script/e2e-vz.ts on macOS/arm64) surfaced that the
guest never served: the mounted bundle dist/node/node.js is a library
module (exports Server/Config/bootstrap), not a CLI, so `node node.js
serve ...` executed nothing and init exited, panicking the kernel.

- image/guest/server-launch.mjs: import the bundle and call
  Server.listen({...}) with env-provided credentials/hostname/port —
  the same entry the desktop sidecar uses. Baked at
  /opt/codev-guest/server-launch.mjs; both providers run it.
- image/Dockerfile: install the two externalized bundle deps
  (jsonc-parser, @lydell/node-pty) into /opt/node_modules so ESM
  resolution finds them without touching the host bundle; add
  busybox-static for a clean poweroff.
- boot.ts: sandboxBootEnv now carries CODEV_SERVER_HOSTNAME/PORT and
  drops sandboxServeArgs; export SANDBOX_GUEST_LAUNCHER. Both providers
  and the vz boot.json updated.
- codev-init: run the launcher, seed /etc/skel into the home, poweroff
  via busybox with a sysrq fallback.
- build-image.ts: clear only *.zip from the office staging dir so its
  tracked .gitignore/.keep survive a build.

Verified end-to-end: create -> codev-vmm boot -> kernel -> virtio-fs ->
launcher -> Server.listen -> vsock bridge -> authenticated /global/health
returns OK. 60 tests pass, typecheck clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@quickbeard
quickbeard merged commit 34a7919 into main Aug 12, 2026
0 of 5 checks passed
@quickbeard
quickbeard deleted the sandbox-guest-launcher branch August 12, 2026 04:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant