Add a macOS image pipeline mirroring the Windows one: unattended install,
generalization and user creation, driven end to end in QEMU on a KVM host.
lib/images/macos:
- makeOpenCore: OSX-KVM OpenCore ESP with a config.plist rewritten per image —
SMBIOS model + serial/MLB (macserial) + UUID + ROM=en0 MAC (built-in NIC pinned
to PciRoot(0x0)/Pci(0x12,0x0)) for Apple ID / iMessage / App Store; boot disk;
OpenCore self-entry hidden. ident.nix derives MAC+UUID from a seed so the NixOS
module and CLI know the NIC MAC at eval time.
- makeImage: one QEMU session driven by vm-driver.py (QMP + screenshot settle
detection + OCR of the menu bar) — boots the recovery via OpenCore, opens
Terminal (Ctrl-F2 -> Utilities -> Terminal), types the bootstrap command;
vmix-install.sh erases the disk as APFS, lays down the host-extracted installer
app skeleton + a byte-exact SharedSupport.dmg (raw disk mapped to that byte range
of the pkg, dd'd in — Recovery's xar truncates an 18 GB member), runs
startosinstall with the vmix agent pkg. OpenCore is then copied into the image's
ESP so it boots standalone with OVMF.
- customizeImage / templates: boot the image with a FAT-then-HFS+ VMIX volume; the
vmix agent (LaunchDaemon) runs a script as root, records status and powers off —
the macOS counterpart of Windows Audit Mode. generalize creates the admin user +
auto-login (kcpassword), suppresses Setup Assistant, sets hostname/timezone,
grows APFS, and assigns a fresh SMBIOS identity. Templates: noUpdates,
performance, remoteAccess (ssh + screen sharing).
- fetchRecovery: Apple recovery BaseSystem, retried until the pinned Tahoe build
(osrecovery load-balances Sequoia/Tahoe during the rollout). makeAgentPkg builds
a distribution flat pkg on Linux (xar+bom+cpio) for startosinstall --installpackage.
CLI: vmix build/copy/run for macOS (run --macos --vnc, reads the image's MAC from
its ESP), and a `vmix macserial` helper. NixOS module: disks.os.file carrying
_vmixOsType="macos" auto-enables the macOS QEMU profile (AppleSMC+OSK, Skylake
CPU spoof, AHCI system disk, VMware SVGA, pinned NIC); macos.{enable,cpu,mac}.
Status: proven through the installer prepare phase (SharedSupport.dmg mounts,
version 26.6.2 read, SU catalog loads). Two blockers remain, documented in
lib/images/macos/README.md: (1) startosinstall's OSISVerifyBaseSystemOperation
rejects the byte-identical plain-UDIF SharedSupport as "pkgdmg missing a footer"
in this Tahoe recovery/VM; (2) Apple's CDN unreliably serves the Tahoe recovery
during rollout (self-hosting the verified BaseSystem is the robust fix).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XsESshRCoBoUVWV9qKURUF
The NixOS module was importing lib directly with the host's pkgs,
causing image customization to use the host's guestfs-tools instead
of vmix's locked version. guestfs-tools 1.52.2 (from host nixpkgs)
has a bug that overwrites /boot/grub/grub.cfg with resolv.conf
content, breaking VM boot.
Now vmixLib is built once in flake.nix with vmix's own nixpkgs and
passed through the overlay to pkgs.vmixLib. Removes overlay.nix and
module.nix as the logic is inlined in flake.nix.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Images:
- laptopUpstream: bare OS install with AHCI, no templates
- laptopSlim: essentials only (debloat, registry tweaks)
- laptop: full (essentials + all apps)
- win10/win11 images use rec for self-references
CLI:
- preserve recovery partition (4) during disk copy
- expand partition 3 up to partition 4 boundary
- remove VNC CLI flag (use vncDisplay in nix configs instead)
Flake:
- add devShell with vmix alias and PS1 prompt
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extract all vmix CLI logic (build, copy, run) from flake.nix into
cli.nix. flake.nix is now 30 lines — just wiring.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Laptop images now use AHCI storage + e1000 network instead of VirtIO.
This fixes "inaccessible boot device" on real hardware — the AHCI→NVMe
driver transition is handled by Windows, unlike VirtIO→NVMe which isn't.
- makeImage: useAHCI flag switches disk to ide-hd and network to e1000
- customizeImage: auto-detects useAHCI from original image, propagates it
- win10/win11 laptop images: useAHCI = true
- vmix run: --ahci flag for running laptop images in QEMU
- generalize: PlainText password tags in OOBE unattend XML
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
SDL display:
- try SDL, auto-fallback to headless if it fails (no crash)
- SDL_VIDEODRIVER=x11 to avoid wayland socket path issues
- suppress XDG_RUNTIME_DIR warnings
Disk copy:
- zap-all before writing to clear old partition tables
- delete recovery partition (4) before resizing partition 3
- use parted resizepart (preserves partition GUID for BCD)
- remote: nix-shell for sgdisk/parted/ntfsresize on target
- remote: lz4 compression for faster streaming
- remote: pv progress bar with disk size
- -y/--yes flag to skip confirmation prompt
Generalize:
- delay-oobe-run=true defers OOBE + activation to real hardware
- clean cached Autounattend from Windows\Panther before sysprep
- taskkill sysprep.exe on first login (CopyProfile artifact)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
CLI:
- `vmix run <qcow2>` boots image with QEMU (SDL if DISPLAY, snapshot mode)
- --generalize supports delay-oobe-run=true to defer OOBE + activation
to first boot on real hardware (for physical disk deployments)
Templates:
- essentials.virtioDrivers: installs VirtIO drivers only (no guest agent)
used in laptop bundle for network access during Office download
- generalize: delayOobeRun flag controls sysprep /shutdown vs /reboot
delays OOBE, user creation and HWID activation to target device
Build:
- suppress XDG_RUNTIME_DIR and homeless-shelter warnings in SDL mode
- remove invalid ICH9-LMB global properties
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>