Pin arm64 guest images and raw disks

Task 10057 · Done · Coder · created · updated

## Epic requirement (verbatim) "Implement microVM on Windows and MacOS" (Task 10021 PRD ask #1, `in_scope`). Operator decisions that bind every child (verbatim from the PRD/Architecture): - D1 **Full parity** with Linux. Each session gets its own Linux guest kernel. The CP egress allowlist is enforced on the node. Control calls are vsock-bound (or an equivalent per-VM channel). No NodeLink inside the VM. No root in the guest. The disk is retained on `system_failed`. Console, sizing and placement UX are the same. - D2 **All three:** fleet runners, advisor chats and human console sessions. D3 **The Linux guest's set** of providers. D4 macOS = Apple Silicon, Windows = x64. D5 cloud Windows too. D7 same plan feature (`billing_plans.microvm_allowed` + `orgs.microvm_allowed_override`). - AD1 Windows = WSL2 + the existing Cloud Hypervisor/KVM runtime (Windows 10 refused). AD2 Mac guests arm64 only, no Rosetta. AD3 Windows test PC = `win-t8703`. AD5 probe m8i.large exactly; metal (m7i.metal-24xl) is the fallback. AD6 Mac oracles deferred until the Mac is enrolled on dev. AD7 AC2(a) live is dropped; unit tests map every `microvm_prereq` code to its sentence. **Quality bar (verbatim).** Every refusal shown to an operator is one plain sentence naming what is missing and how to fix it (`docs/standards/ui-copy-standards.md`). No generic 400, no stack trace, no Linux-specific wording on a Mac or Windows node. Boot-to-runner latency on Mac and Win is reported in the build's evidence. It is not gated, but it must not be degraded by more than 2× against a Linux MicroVM node of similar size without the Architect recording why. ## This child: arm64 pins, raw conversion, APFS-clone disk backend (Architecture §3.3) Requirements this child serves (PRD §4, verbatim): - R6. Install and update. The macOS installer and the Windows service install and converge everything the VM runtime needs: binaries, images and helpers. The A2 prerequisite is the only manual step. Updating a node keeps its VMs' contract. - R7. Linux unchanged. Linux MicroVM behaviour, the static pool and the unmarked Mac/Windows behaviour do not regress. The full settled Architecture (Task 10022) and PRD (Task 10021) are the epic's prerequisite artifacts; read them with `task_read`/`artifact_read` when this body is shorter than you need. Section numbers (§) refer to the Architecture. ### Approach (settled, realize it) - `packaging/microvm-pins.toml` gains arm64 artifacts with `arch = "aarch64"` beside the x86_64 ones, under the same licence gate: `ubuntu-noble-cloudimg-arm64` (a dated serial, `fetch = "node"`) and `nodejs-linux-arm64` (`fetch = "node"`). No CH, firmware or virtiofsd for arm64 (VZ provides VMM, EFI and virtio-fs). Rewrite the header comment "every pin is x86_64" (`microvm-pins.toml:27`) to describe the present. `tools/microvm_pins.py` selects pins by `(arch, os)`; x86_64 Linux selection unchanged; `(x86_64, windows)` selects the same CH/CLOUDHV.fd/virtiofsd/noble-image/node set as Linux (the Windows bundle's `microvm/` payload runs inside the WSL distro). - `apps/jaahd/microvm_qcow2.py` gains `to_raw(src, dst)`: a pure-Python qcow2 reader that inflates zlib-compressed clusters (Ubuntu's `.img` is compressed qcow2), output a sparse raw file. Keep the no-GPL-tooling rule (no `qemu-img`). - **Layering by APFS clone, not a backing chain** on macOS: base `base-<pin>.raw` → project layer `layer-<project>-<gen>.raw` → per-session disk, each a `clonefile(2)` (via `ctypes` from `libSystem`) of the one before. Retention/destroy unchanged (the VM dir holds its disk). `microvm_images` asks ONE function `disk_backend()` → `qcow2-chain` on Linux/WSL, `apfs-clone` on macOS; no other module branches. Boot growth: the raw root is truncated to the effective disk size; cloud-init `growpart` runs as on Linux. Acceptance criteria this child owns (PRD §5, verbatim): - AC17 — Pins are licence-gated per architecture (A3). Verify: `python3 tools/microvm_pins.py` (the licence gate) passes with the arm64 guest images/binaries pinned beside the x86_64 ones. - AC15 — Linux and static behaviour unchanged (R7). Verify: `./dc check full` is green. On dev, a Linux MicroVM node still passes AC3. An unmarked Mac and Win still run a static task as before. Credentials & reach (Architect §8, verbatim rows): - AC15 gate, AC16, AC17 | Worktree only (`./dc check full`, `tools/check_context.py`, `tools/microvm_pins.py`) | **verified**: nothing beyond the worktree. Out of scope (BA, verbatim — do NOT touch): - Do **not** change Linux MicroVM behaviour, its pins, its egress default list (`apps/api/app/microvm_egress.py`) or its sizing defaults, beyond what is needed to add the OS dimension. - Do **not** change the static worktree pool or dynamic worktrees on any OS. - Do **not** add a new plan term or billing price (D7). - Do **not** mount NodeLinks inside a VM on any OS. - Do **not** add Multi-Node Hosts / co-located nodes (`vnode_host`), GitHub Actions runners or SDK-agent support to macOS or Windows hosts. - Do **not** support Intel Macs, Windows on ARM, or cloud-launched macOS nodes. - Do **not** relax the host-session (unmarked) capability table of ADR-0145. - Do **not** describe the boundary as stronger than each hypervisor gives ("strong isolation", "defence in depth" — ADR-0129 anti-pattern). - No new console page. Existing surfaces only (A4). - Operate and verify Jaah only through the console. No CLI, SQL or cloud-console workaround for live state (CLAUDE.md hard rule). Architect boundaries (declined, do NOT build): a Windows native Hyper-V runtime; Rosetta in Mac guests (no x86_64 binfmt); boot-latency optimisation (pre-booted snapshots). Also do NOT touch: the x86_64 pins' values or the Linux qcow2 chain path; microvmd / seed (macOS-runtime child). Done means: - AC17 — `python3 tools/microvm_pins.py` exits 0 with both arm64 pins present and licence-checked (output cited); selection unit tests: `(x86_64, linux)` returns exactly today's set, `(x86_64, windows)` returns the same set, `(aarch64, macos)` returns the arm64 image + node and no CH/firmware/virtiofsd. - `to_raw` unit tests: a committed small compressed-qcow2 fixture (generated by a test-side pure-Python writer, no `qemu-img`) converts to raw whose bytes match the expected image, unallocated clusters stay sparse (allocated blocks < virtual size), a corrupt header raises a readable error. - `disk_backend()` returns `qcow2-chain` on Linux and `apfs-clone` on macOS (platform patched); the APFS clone path is exercised on the `macos-15` CI job (a clone shares blocks; a write to the clone diverges only the written range) — job log cited. - AC15 — `./dc check full` green (cite GATE_RESULT); existing Linux `microvm_images`/`microvm_qcow2` tests unchanged and green.

Outcome

Added arm64 pins with (arch, os) selection, a pure-Python qcow2-to-raw converter (byte-identical to qemu-img on the real image) and disk_backend() with APFS clones. Quick gate green; merge lane merged #6918 (90c3b90). Deploys superseded, so the macos-15 APFS job is not yet observed. No UI surface affected. Memory: none applied.

Pull request

Artifacts

Activity

  • Draft
  • Ready
  • Runner started
  • Runner ended (done)