fix: make qemu guest sessions usable for all bundled tools - #76
Draft
xai wants to merge 3 commits into
Draft
Conversation
The opencode bundle's 1.4 GB uncompressed initramfs could not be unpacked in the fixed 4096 MiB guest: the kernel holds the image and the ramfs it unpacks into at the same time, so unpacking failed and the guest panicked without an init. Strip the npm/pip/uv caches and /boot from the rootfs, compress the image with zstd, and derive the guest memory from the built sizes so a large bundle gets the memory it needs. An undersized bundle is now rejected on the host instead of dying inside the kernel. The compressed image is padded to a 4-byte boundary and the per-run concatenation keeps every segment aligned, because unpack_to_rootfs() only recognizes the appended overlay archive there.
The emulated serial console carries no window size, so the guest console reported 0x0. Terminal UIs that trust it rendered one column wide (codex, opencode) and those with a fallback drew into a fixed 80x24 box regardless of the real terminal (claude, pi). Seed the console from the host terminal in the generated run script. The size is applied once at startup: the console is not a controlling terminal in the guest, so a later resize delivers no SIGWINCH.
opencode keeps its state in opencode.db, a WAL-mode SQLite database inside its
config store, which the qemu backend mounts over 9p. WAL shared-memory files
need mmap semantics there, so every query failed ("Failed query: PRAGMA
wal_checkpoint(PASSIVE)"), the storage layer died before the app logged
anything, and the TUI painted an empty frame. codex already declares
qemuStoreCacheMmap for the same reason.
Also populate /etc/hosts in the bundle. Docker bind-mounts it into the
provisioning container, so the export left an empty placeholder and the guest
resolved "localhost" through musl built-ins, answering ::1 first.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What it does
Fixes #75.
Three defects kept
--backend qemusessions from working with the bundled tools.into a ramfs that becomes the rootfs, and during unpacking the kernel holds both
the image and its contents. opencode's 1.4 GB image did not fit the fixed
4096 MiB guest and panicked without an init. The builder now strips build-time
caches (npm, pip, uv) and
/boot, compresses the image with zstd, and recordspacked/unpacked sizes so the host derives guest memory from the actual bundle
(never below
qemuMinMemoryMiB). An undersized bundle is rejected on the hostinstead of dying inside the kernel. The compressed image is padded to a 4-byte
boundary so the kernel still recognizes the per-run overlay archive appended to it.
codex and opencode rendered one column wide and claude and pi fell back to a fixed
80x24 box. The generated run script now seeds the console from the host terminal.
opencode.dbruns in WAL mode inside theconfig store, and WAL shared-memory files need mmap semantics there; every query
failed and the TUI painted an empty frame. opencode now declares
qemuStoreCacheMmap, as codex already did. The bundle also gets a populated/etc/hosts— Docker bind-mounts it into the provisioning container, so the exportleft an empty placeholder and the guest answered
::1forlocalhostfirst.How to test
Requires Docker (bundle build) and
qemu-system-x86_64. Resize your terminal tosomething clearly wider than 80 columns first.
enclave --backend qemu --tool opencode— the bundle builds, the guest boots, andthe TUI comes up filling the terminal with no
Failed query: PRAGMA wal_checkpointerrors. Send a prompt, exit, restart: the session history is still there.
--tool codex,--tool claude,--tool pi— each TUI uses the fullterminal width, not one column and not 80x24.
stty sizematches your host terminal, andgetent hosts localhostanswers127.0.0.1.Follow-ups
The console size is applied once at startup. Resizing the host terminal mid-session
does not reach the guest, because the console is not a controlling terminal there and
no
SIGWINCHis delivered.Breaking changes
Review checklist