Skip to content

Support Linux hosts (KVM) and add Void Linux as a managed environment - #2

Open
EnigmataTech wants to merge 1 commit into
NAME0x0:mainfrom
EnigmataTech:void-linux-support
Open

Support Linux hosts (KVM) and add Void Linux as a managed environment#2
EnigmataTech wants to merge 1 commit into
NAME0x0:mainfrom
EnigmataTech:void-linux-support

Conversation

@EnigmataTech

Copy link
Copy Markdown

Summary

  • Pane's QEMU engine now also runs on Linux hosts (KVM instead of WHPX, unzip/curl instead of tar.exe/curl.exe, XDG data-dir resolution), in addition to Windows.
  • Adds Void Linux as a second first-class managed environment alongside Arch, selectable per-session via --family arch/--family void on install-desktop, launch, runtime, and provision.
  • New xbps/runit provisioning path parallel to the existing pacman/systemd one; nothing about existing Arch behavior changes.
  • Fixes a couple of general provision_via_serial reliability bugs uncovered while testing Void (getty respawn race, unpaced serial writes) — not Void-specific, benefits Arch too.
  • chpasswd now given an explicit crypt method, since some base images (Void's included) have no default ENCRYPT_METHOD and silently no-op otherwise.
  • dbus now enabled alongside the display manager (hard prerequisite for lightdm/gdm/sddm).

Note: unlike Arch, there's no hosted/auto-downloaded Void base image yet — a .paneimg has to be built and registered manually via --register-base-image for now.

Test plan

  • cargo test — 202/202 passing (all pre-existing Arch tests untouched)
  • Manual end-to-end on a hand-built Void base image: boot → XFCE + lightdm install → credential provisioning → working graphical login over VNC (KVM, Linux host)
  • Would appreciate a look at the Windows/WHPX path from someone who can test it there

🤖 Generated with Claude Code

…a managed environment

QEMU engine now runs on Linux (KVM instead of WHPX, unzip/curl instead of
tar.exe/curl.exe, XDG data dir resolution) in addition to Windows, so the
same binary can drive a remote/headless Linux host over the network.

On top of that, add Void Linux as a second first-class managed environment
alongside Arch, selectable per-session via --family:
- new DistroFamily::Void, xbps/runit provisioning path alongside pacman/systemd
- ext4 reader gains a prefix-matched fallback for kernel/initramfs filenames,
  since Void versions them (vmlinuz-<ver>) instead of Arch's fixed names
- provision_via_serial hardened: wait for the getty session to stop
  respawning before sending commands, and flow-control each send against
  the guest's terminal echo instead of a blind fixed delay -- fixes a real
  corruption bug in guest provisioning, not Void-specific
- chpasswd now given an explicit crypt method (some base images have no
  default ENCRYPT_METHOD, so it silently no-ops otherwise)
- dbus enabled alongside the display manager (hard prerequisite for
  lightdm/gdm/sddm, previously missing)

Validated end-to-end on a Void base image: boot, XFCE + lightdm install,
credential provisioning, and a working graphical login over VNC.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

@NAME0x0 NAME0x0 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the full diff and built/ran the branch on Windows: cargo build is clean and 275/275 tests pass on x86_64-pc-windows-msvc (the count is higher than your 202 because main has moved; your branch's merge-base is current main, no conflicts). So the Windows/WHPX path you flagged in the test plan compiles and its test surface is green here — I have not booted a guest on Windows from this branch yet.

The general fixes in here are the best part and stand on their own merit, independent of Void: the flow-controlled serial send (waiting for the guest echo instead of a blind 700ms), the getty-respawn settle loop, chpasswd -c SHA512, and enabling dbus before the display manager. Those are real Arch bugs. Thanks.

One blocking issue, one related, and a few smaller things.


1. Family scoping is incomplete — --family void will silently boot Arch (blocking)

runtime_for_family makes base_os_image family-aware. Everything else in the session root stayed family-blind, and the default session name is pane for every subcommand:

Path Family-scoped?
images/{family}-base.paneimg yes (this PR)
disks/root-overlay.qcow2 no
engines/distro-kernel.img no
engines/distro-initramfs.img no
state/base-os-image.json no
disks/user-data.qcow2 no

Both derivation paths are cache-first:

  • extract_from_base_image (src/app.rs:3046) — if cache.exists() { return Ok(cache) }
  • ensure_qcow2_overlay (src/qemu.rs:438) — if overlay.exists() { return Ok(()) }

So on any machine that has already run Arch in session pane:

pane launch --family void

resolves the cached Arch vmlinuz, the cached Arch initramfs, and the existing root overlay whose qcow2 backing file is arch-base.paneimg. It boots Arch. No error, no warning, nothing Void involved — and --family void is right there in the command line saying otherwise.

This can't show up in your testing because a fresh Linux host with only Void registered has no Arch cache to collide with, but it's the default path for every existing Windows user who tries the new flag.

Smallest fix that closes it: fold family.slug() into those filenames the same way you did for the base image, e.g. root-overlay-{slug}.qcow2, distro-kernel-{slug}.img, distro-initramfs-{slug}.img. Alternatively give runtime_for_family a family-suffixed session root so the whole tree is separated — heavier, but it also fixes #2 below for free and leaves no other shared-state landmines.

Either way it'd be worth a test asserting two families in the same session resolve to different overlay/kernel/initramfs paths, since the existing runtime_for_family_derives_base_image_name test only covers the one field that was actually changed.

2. state/base-os-image.json is shared across families

register_base_os_image writes to paths.base_os_metadata, which runtime_for_family leaves as state/base-os-image.json. Registering a Void image into a session that already has Arch overwrites the Arch registration record — size, digest, format — while arch-base.paneimg is still sitting on disk. pane doctor / pane status then verify the Arch image against the Void digest and report it as corrupt.

Falls out of whichever fix you pick for #1.

3. --family accepts four values that can't work

The blanket ValueEnum derive on DistroFamily exposes ubuntu, debian, fedora, and unknown on every flag, and they show up in --help. They parse fine and then fail downstream with a missing-image error. Given is_mvp_supported() already encodes the real answer, it'd be better to reject them at parse time (a separate CLI-facing enum, or a value parser gated on is_mvp_supported) so --help only advertises what exists.

4. Smaller things

  • extract_file_by_name picks the lexicographically-first match, not the newest. The sort is contains("fallback") then a.0.cmp(&b.0), so with two kernels in /boot you get whichever name sorts first — for vmlinuz-6.9.x vs vmlinuz-6.10.x that happens to land on 6.10, but only by accident of string ordering. Fine for a single-kernel base image; worth at least a comment noting the assumption, since the whole point of the function is handling distros that version the filename.
  • let _ = name; at the end of extract_file_by_name is dead — either drop the binding or use it in the "derived X from the base image" log line, which would actually be useful for confirming which kernel got picked.
  • accel_flag() returns kvm for every non-Windows target, including macOS, where it should be hvf. Pane doesn't claim macOS today so this isn't urgent, but cfg!(windows)/else is a bit blunt for something named after the accelerator.
  • unzip is a new hard dependency on Linux with no preflight. ensure_qemu_available gives a nice actionable error for a missing QEMU; a missing unzip gets Could not run unzip to extract the base image archive: ... from io::Error. Worth the same treatment.
  • The Linux QEMU-missing hints are Arch-specific. ensure_qemu_available and qemu_not_found_message both suggest sudo pacman -S qemu-desktop, which is wrong advice on a Debian, Fedora, or (ironically) Void host. Something distro-neutral like "install your distribution's qemu-system-x86_64 package" would travel better.
  • curl.execurl is fine on Windows (CreateProcess appends .exe when the name has no extension), just noting it's a behavior change on the Windows path inside a Linux-support PR.

5. Void's catalog tier — worth discussing, not blocking

docs/product-contract.md has a Support Policy stating an environment shouldn't be surfaced unless it has import, bootstrap, launch, reconnect, reset/repair, logging, known-good defaults, and a support statement — "hidden or locked is better than exposed but unreliable" — and "Managed Environment Order" names Ubuntu LTS as the second first-class environment.

This PR sets Void to stage: Current, tier: FirstClass, launchable_now: true, while (by your own note) there's no hosted or auto-downloadable base image, no GUI exposure, and no Windows validation. That's a direct conflict with the written contract. Two coherent ways out — mark Void CuratedPreview / launchable_now: false until it has a hosted image, or amend product-contract.md and vision.md to change the stated environment order — but the current state has code and docs disagreeing. Happy to defer this until the above are sorted.

6. Docs

No docs changed in this PR. If it lands as-is, at minimum: --family is missing from docs/cli-reference.md; README.md says Windows-only in four places including a FAQ that answers "Yes"; and the README FAQ on distro support doesn't mention Void. I can take that pass once the code shape settles — no need for you to.


Nothing here is structural. #1 and #2 are one filename change away, and the rest is polish. The KVM/XDG/host-detection work and the serial-reliability fixes are solid.

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.

2 participants