Verify MicroVM on Mac hardware

Task 10061 · Queued · 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: C5 — Mac on-hardware verification (oracle-only, with fixes allowed) Requirements this child serves (PRD §4, verbatim): - R1. Mark. An Apple Silicon macOS node and an x64 Windows node (self-hosted, or cloud-launched for Windows) can be marked MicroVM through the existing Node detail toggle. Conditions: the org's plan allows MicroVM, the node reports a usable hypervisor, and it has no held static worktrees. When the node does not qualify, the refusal is a readable sentence naming the missing prerequisite. The Linux-only `NO_KVM` sentence must not be shown to a Mac or Windows node for a reason that no longer applies. - R2. Sessions in VMs. On a marked Mac or Windows node, every runner, advisor and console session runs in its own VM with its own Linux guest kernel. Each session is a `microvms` row, never a `worktrees` row. - R3. Parity of contract. Every bullet of `docs/policies/microvm-nodes.md` holds on these OSes: sizing resolver and source display; placement admission by free RAM/disk and hypervisor usability, with readable wait reasons, and `failing_nodes`; the intent/fact rail; lifecycle: destroy at done, reset, delete or console end; retain on `system_failed` for `microvm_retention_hours`; liveness reclaim; the egress allowlist: default ∪ project additions − removals, plus the CP and forge hosts, with private, link-local and node-own addresses refused and each refusal logged; per-VM control-call binding: a guest can act only on its own task; no root and no `sudo` in the guest, with a rootless engine; no NodeLink mount in a VM; the API-key sign-in served without a key in the guest environment. - R4. Providers. Inside a VM, a Mac or Windows node runs every provider an Ubuntu MicroVM node runs. Placement and the account picker must not refuse such a provider because of the host OS when the session lands in a VM. The host-session rules stay as the ADR-0145 table states for unmarked nodes. - 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. ### Approach (settled, realize it) Run the Mac oracles on the operator's Apple Silicon Mac node on dev, through the console as the operator, and fix defects found (each fix in this child's PR, with a unit test). **Prerequisite (AD6): the Mac must be enrolled on dev.** If it is not on the dev Nodes list (All orgs), ask ONE `Access` blocker round naming exactly that (enroll the Apple Silicon Mac on dev with the console one-liner, in an org whose plan allows MicroVM). This is a sequencing deferral, not a scope-down: the epic does not close while this child is open. Record boot-to-runner latency vs `gmind-microvm-1`; if > 2×, record why. Record which share path held for the token dir (risk R-b: VZ virtio-fs with `guest_ids()`, or the copy-with-write-back fallback). Acceptance criteria this child owns on Mac (PRD §5, verbatim): - AC1 — The mark turns on for a qualifying node (R1). Verify: Open Nodes → Mac (then Win) → MicroVM toggle → turn on. Observe: the toggle stays on, the MicroVM chip shows on the node, and the node's free static worktrees are gone from its worktree list. - AC2 — A non-qualifying node gets a readable, specific refusal (R1). Every state is covered: hypervisor prerequisite unmet, plan off, held worktrees, live VMs while turning off. Verify: (a) On Win with the A2 prerequisite disabled (and on a Mac where the Architect's prerequisite is unmet), turn on → observe a sentence naming that prerequisite; it must not say "needs a Linux node". (b) In an org whose plan and override deny MicroVM → observe "Your plan doesn't include MicroVM nodes." (c) With a user-held worktree → observe the held-worktrees sentence. (d) With a live VM, turn off → observe "Wait for N MicroVM… to end." - AC3 — A fleet task runs end-to-end in a VM (R2, R3). Verify: Create a project whose only node is Mac (MicroVM on). Start a small coder task. Observe: a VM record appears for the task (org MicroVM tab / VM records) and no worktree is leased; the runner works, opens a PR and reaches `done`; the VM record then shows destroyed. Repeat on Win. - AC4 — Advisor and console sessions run in VMs (R2). Verify: On each node, open an advisor chat and a console session for the project. Observe: each gets its own VM record; console attach is interactive and typing works; an answer to a question card reaches the session; ending the console destroys its VM. - AC5 — Guest isolation holds (R3). Verify: In a console VM session on each node, run `uname -s` → `Linux`; `sudo -n true` → fails; `mount` / `ls` at the NodeLink path → no NodeLink mounted. With two concurrent sessions on the same node, a file one writes in its tree is not visible from the other. - AC6 — Egress is enforced (R3). Verify: In a VM session: `curl -sI https://github.com` → succeeds; a host not on the project allowlist → refused; the node's own LAN IP and `169.254.169.254` → refused. Add the host to the project's egress additions (ProjectEditPage) → a new session reaches it. Each refusal is visible in the node's egress refusal log/report. - AC7 — Control calls are bound to the VM's task (R3). Verify: From inside a task VM, call a task-scoped control verb naming another task's id → refused. The runner's own `task_get` returns its own task. - AC8 — The provider set is the Linux guest's (R4). Verify: On Mac (host supports only Claude Code), run a task with a Codex account (and one other non-macOS provider an Ubuntu MicroVM node runs) → it is placed in a VM and runs. On an UNmarked Mac, the same account is still refused with the ADR-0145 sentence. - AC9 — Lifecycle and retention (R3). Verify: Reset a running task → its VM is destroyed. Delete a task → destroyed. Force a task to `system_failed` → its VM shows `retained`, then destroyed after `microvm_retention_hours`. Verify the last step by setting the retention to its minimum on dev, or by reading the lifecycle record. - AC10 — Sizing applies and shows its source (R3). Verify: Set the node size on Node detail (overriding the project size). Start a task → inside the VM `nproc` and `free -m` match the effective size, and the console shows the value with source "node". - AC11 — Placement refusals are readable (R3). Verify: Set a VM size larger than the node's free RAM. Start a task → it waits with the readable placement reason, and no VM is created. - AC12 — Liveness reclaim (R3). Verify: Kill the guest agent session inside a task VM → the attempt ends through broken-session reclaim within the same bound as a Linux node, and the task is redispatched or failed as on Linux. - AC14 — Install is turnkey apart from A2 (R6). Verify: Enroll a fresh Mac and a fresh Windows machine with the per-OS one-liner from the console. Perform only the documented prerequisite. Turn MicroVM on → AC3 passes with no other manual step. Update the node package → a subsequent AC3 run still passes. - 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): - AC1–AC12, AC14 (console flows) | `org/console/dev-login` (operator login), dev console `https://app.dev.jaah.ai` | **verified**: `credential_get` succeeded; the console was opened from this node (`h8agi5dw`), signed in, Nodes list read. The credential's stored `base_url` names the legacy dev host, which does not answer from this node; the oracle uses `app.dev.jaah.ai`. - AC1–AC12, AC14 on Mac | The operator's Apple Silicon Mac node on dev | **deferred** (AD6: "enroll it later"): the dev Nodes list with "All orgs" checked shows 9 nodes and none is macOS. C3 runs its Mac oracles once the Mac is enrolled. The build is not blocked, because C3's unit-level work and CI do not need it. - AC15 Mac static half | The Mac | **deferred**: owned by C5 (AD6). 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). Also do NOT touch: Windows or Linux runtime code; scope beyond fixing defects these oracles expose. Done means (all on the Mac on dev; screenshots/records in the report): - AC15 Mac static half — BEFORE marking: the unmarked Mac runs a static task to `done`. - AC1; AC2(b)(c)(d) (AC2(a) live dropped, AD7); AC3, AC4, AC5, AC6 (incl. the Mac's own LAN IP and `169.254.169.254` refused), AC7, AC8 (Codex + DeepSeek or Pi in a VM; the same Codex account refused on an unmarked Mac with the ADR-0145 sentence), AC9, AC10, AC11, AC12, AC14 — each passes as written. - Boot-to-runner latency recorded vs `gmind-microvm-1`, with the reason if > 2×; the token-share path that held (R-b) recorded. - If any fix was needed: `./dc check full` green (cite GATE_RESULT).

Activity

  • Draft
  • Ready