Freeze and instantly thaw Windows-on-ARM VMs on Apple Silicon. Build a Windows 11 ARM golden image from your own ISO, then — in one command — clone it, boot it headless, push a binary in over SSH, run it, and capture its stdout/stderr and exit code. Snapshot a warm WSL2 environment and resume it in seconds. Built for testing cross-platform binaries inside Windows.
frost is built on the
vmkitengine (packages/vmkit) — the reusable VM configuration + QEMU launch library that powers it.
Status: end-to-end validated. The full loop (build golden → instant clone → boot → push binary → run → capture exit code → teardown) works on Apple Silicon (verified on M2 Max, macOS 26, QEMU 11 + Hypervisor.framework).
Running and automatically testing software inside Windows-on-ARM on a Mac is largely unaddressed — existing VM image-builder tooling is almost entirely x86. frost fills that gap: a reproducible, license-clean way to build a Windows-ARM test VM and drive a binary through it from the host.
Distribution model — ship the recipe, not the image. We never redistribute Windows. You supply your own Windows ISO and license; the image is built locally on your machine and owned by you.
- Apple Silicon Mac, macOS 13+ (tested on macOS 26).
brew install qemu swtpm;brew install hudochenkov/sshpass/sshpass.- Your own Windows 11 ARM64 ISO + a VirtIO driver ISO (for guest networking).
- ~25 GB free disk per golden build (a slimmed golden is ~13 GB; clones are tiny overlays).
# 1) Build a golden image from your own ISO (unattended install + provisioning).
# --slim shrinks it (~20G → ~13G); --sysprep generalizes it (optional).
bin/build-golden.sh --iso Win11_ARM64.iso --virtio virtio-win.iso --slim
# 2) Run a test: instant clone → boot → push a binary over SSH → run → capture →
# teardown (golden untouched). The script's exit code IS the guest command's
# exit code, so CI can assert on it.
bin/test-run.sh --binary ./mytool.exe --run 'C:\Windows\Temp\mytool.exe --flag'frost resolves tool locations and credentials from the environment, falling back to Homebrew defaults — so a packaged build, CI, or a non-standard install can override them without editing scripts:
| Variable | Purpose | Default |
|---|---|---|
FROST_QEMU_BIN_DIR |
directory containing qemu-system-* |
/opt/homebrew/bin |
FROST_QEMU_SHARE_DIR |
directory containing the EDK2 *.fd firmware |
/opt/homebrew/share/qemu |
FROST_SWTPM |
swtpm executable |
<bin>/swtpm |
FROST_VMKIT |
prebuilt vmkit engine binary (else built on first use) |
— |
FROST_SSH_PASS |
default guest SSH password (the --pass flag still overrides) |
Test1234! |
The default password is a convenience for throwaway, host-only test VMs; set
FROST_SSH_PASS (or pass --pass) for anything you keep.
your Windows ISO ─▶ build-golden.sh ─▶ golden.qcow2 (bootable, self-contained, ~13G)
(unattended install via dism /Apply-Image; provisions netkvm + OpenSSH)
│
per test: golden ──qcow2 overlay clone (instant)──▶ boot headless (~20-30s)
──ssh: push binary, run──▶ capture stdout/stderr + exit code ──▶ delete clone
Key design decisions, all verified the hard way (details in docs/WINDOWS-CONFIG.md
and docs/AUTOUNATTEND-NOTES.md):
- dism
/Apply-Imageinstall flow (notsetup.exe) — reliable, scriptable, and bypasses the flaky modern-OOBE path. ramfbgraphics (not virtio-gpu) — the latter hangs the Windows bootloader.- NVMe system disk — the firmware enumerates it and can boot it (AHCI cannot).
usb-storage,removable=onfor the answer file — modern Windows Setup only scans removable media forautounattend.xml.- VirtIO
netkvminjected during install → guest networking → host↔guest SSH. - qcow2 backing-file overlays for clones — each test run is a throwaway diff over the shared read-only golden, so clones are instant and tiny.
Some binaries need a hypervisor inside the guest (e.g. WSL2). The hardware-accelerated (hvf)
path can't give a Windows guest a usable hypervisor on Apple Silicon, so frost runs the guest
under full software emulation with a firmware that exposes EL3 (bin/build-emu-firmware.sh
bin/test-run-emulated.sh --firmware). A real WSL2 Linux kernel boots this way (verified on M2) — slower than hvf but hardware-agnostic. For ~10 s per-test startup, snapshot a warm WSL2 state and resume it (bin/build-wsl-snapshot.sh/bin/test-run-snapshot.sh).
Plain binaries (including x86_64 via Windows' emulation) need none of this. See
docs/NESTED-VIRT-KNOWHOW.md for the recipe and usage.
.
├── bin/
│ ├── frost agent-friendly CLI dispatcher (subcommands; --dry-run, --json)
│ ├── build-golden.sh build a golden image from your ISO (+ --slim, --sysprep)
│ ├── build-wsl-golden.sh derive a WSL2-ready golden (VMPlatform + WSL pkg + distro)
│ ├── test-run.sh the test harness (clone → boot → push → run → capture → teardown)
│ ├── test-run-emulated.sh emulation harness (--firmware) for in-guest hypervisor (WSL2)
│ ├── build-emu-firmware.sh build the EL3 ArmVirtQemuKernel firmware (container) for WSL2
│ ├── convert-to-virtio-blk.sh make a savevm-capable (virtio-blk) golden via viostor boot-start
│ ├── build-wsl-snapshot.sh (root) one-time: vmnet boot + warm WSL2 + savevm a resumable snapshot
│ └── test-run-snapshot.sh (root) fast harness: loadvm-resume (~10s) → run → capture
├── packages/vmkit/ Swift core engine (SPM): VMConfiguration → QEMUArgumentBuilder + lifecycle
├── docs/ know-how: QEMU config, autounattend, nested-virt/WSL2, disk formats
├── share/ committed assets — answer file + guest provisioning scripts
│ ├── autounattend/ autounattend.xml (de-branded, dism-apply flow)
│ └── provision/ *.ps1 (setup, WSL install, viostor, boot test)
├── work/ runtime only (VM disks, firmware, swtpm, ISOs) — git-ignored
├── THIRD_PARTY_NOTICES.md required third-party license notices
├── LICENSE · NOTICE Apache-2.0
└── README.md
- A future GUI/app drives CLI engines as subprocesses (
vmkit/qemu/swtpm). The subprocess boundary is also the clean GPL boundary (the frontend never links libqemu). - Integration language is Swift (
packages/vmkit); each engine uses whatever fits.
cd packages/vmkit && swift build && swift test
# print the exact QEMU command a profile would launch (install|hvf|emulated|snapshot):
swift run vmkit args --profile hvf --disk golden.qcow2 --vars golden-vars.fdTHIRD_PARTY_NOTICES.md— third-party license notices.docs/WINDOWS-CONFIG.md— the verified-working QEMU config for Windows 11 ARM, and why.docs/AUTOUNATTEND-NOTES.md— unattended-install findings (removable media, OOBE, NVMe).docs/UNATTEND-KNOWHOW.md— answer-file optimization catalog + per-profile trade-offs.docs/NESTED-VIRT-KNOWHOW.md— running WSL2/Hyper-V in the guest: the EL3-firmware recipe, the pre-baked WSL golden, and snapshot/resume fast-start.
frost is licensed under the Apache License 2.0 (see LICENSE). frost orchestrates QEMU,
swtpm, EDK2/OVMF and VirtIO drivers as separate subprocesses — it does not link or redistribute
their code; see THIRD_PARTY_NOTICES.md.