Files
nucleic/docs/MACOS_VM_PROVISIONING_REPAIR_PLAN.md
T

8.1 KiB

macOS VM base-image provisioning repair

Status: implementation in progress
Opened: 2026-07-23

Objective

Make the macOS 27 golden-base build genuinely unattended: no unrecognized GUI permission sheets, no blind keystrokes into whichever dialog happens to have focus, and no base marked ready until a production-shaped clone can boot, mount its workspace, and execute through the in-guest agent.

The existing lightweight MDM is the policy plane for permissions macOS allows device management to grant. Host-side display capture and virtual HID remain the default computer-use path, so guest Screen Recording and Accessibility are not prerequisites for an ordinary usable base.

Findings

1. MDM is implemented but disconnected

MacVMEngine+MDM.swift contains Mode A enrollment and Mode B profile-push helpers, while NucleicMDM contains a CA, enrollment profile, HTTPS server, command queue, and tests. The production base build never calls those helpers. It runs the shell provisioner and then answers the Network Volumes dialog with a separate OCR/click pass.

The MacVMBaseStatus.mdmEnrolled and profilesInstalled fields therefore describe planned state, not a readiness invariant. Recipe v7 can be stamped while both remain false.

2. Provisioning creates policy prompts before policy exists

The bootstrap enters through Apple's VirtioFS automount, then remounts the same directory at a custom path and executes the full provisioner there. The custom mount is treated as a network volume, so the build touches a protected resource before any Network Volumes policy is installed.

The later consent pass repairs only NucleicVMAgent access. It is exact-English OCR for Allow, cannot establish which dialog owns that button, and is fragile when more than one sheet is visible.

3. GUI fallback handling is not a state machine

When Command Line Tools are absent from the Software Update catalog, the guest launches xcode-select --install and writes a sentinel. While the sentinel exists, the host sends Return every three seconds. Return may accept the CLT sheet, an unrelated privacy sheet, or any other focused control. Provisioning must recognize the expected UI state or stop; it must never treat all dialogs as equivalent.

4. The MDM integration is still spike-grade

  • UAMDM approval opens Device Management and sends one Return; Install, authentication, and verification are placeholders.
  • The built-in FDA and notification profiles are not copied into the shipped app.
  • The CA, MDM configuration, device identity, and installed profile identifiers are not persisted per base.
  • Enrollment orchestration uses registry-based run(name:), but maintenance boots hold a direct MacVMInstance.
  • A drained command queue may contain errored commands, and enrollment returning false is not currently fatal.
  • CA trust, guest-to-host TLS reachability, initial no-APNs command drain, real mdmclient signatures, and headless UAMDM approval still need a live macOS 27 validation pass.

5. macOS 27 narrows the policy scope

Classic MDM profiles remain appropriate for Full Disk Access, Network Volumes, and targeted notification settings. Classic PPPC Accessibility grants are removed in macOS 27 in favor of the declarative com.apple.configuration.app-settings privacy model. Screen Recording cannot be silently granted.

This does not block the base: pixel computer use is host-side and needs no guest TCC. Semantic AX remains optional until the lightweight MDM supports Declarative Device Management.

Implementation plan

P0 — deterministic bootstrap

  • Package the built-in MDM policy profiles in Resources/macvm.
  • Add Network Volumes to the VM-agent PPPC policy.
  • Resolve packaged policy assets through one tested engine helper.
  • Stop blindly confirming the graphical CLT installer.
  • Keep the unattended path headless; if CLT cannot be installed headlessly, fail with an actionable diagnostic rather than wait behind an unknown sheet.
  • Do not execute the full provisioner from a custom network-volume mount before policy exists.
  • Record screenshots, recognized text, frontmost UI state, and TCC attribution when an unexpected modal blocks progress.

P1 — production Mode A enrollment

  • Split the base build into:
    1. declarative account creation and minimal exec-agent install through the automount;
    2. MDM enrollment and required policy installation;
    3. full toolchain provisioning after policy is active.
  • Refactor enrollment to operate on a direct MacVMInstance.
  • Persist the MDM CA, configuration, device identity, and profile identifiers beside the base metadata with credential-appropriate permissions.
  • Replace the UAMDM placeholder with a bounded, state-aware Device Management flow that enters the generated guest password only in the expected authentication sheet.
  • Treat enrollment false, command errors, missing profile identifiers, or failed functional probes as fatal.
  • Make re-provisioning update policy without attempting a second enrollment.
  • Require mdmEnrolled, required profiles, agent readiness, and a production-shaped workspace mount before stamping the provisioning recipe.
  • Remove MacVMEngine+ConsentGrant.swift's Network Volumes click path once the MDM path is proven.

P2 — live macOS 27 validation

  • Validate local-CA trust without a SecurityAgent prompt.
  • Validate guest-to-host gateway/TLS reachability and check whether the listener introduces host Local Network or incoming-connection prompts.
  • Validate enrollment's initial command drain without an MDM APNs push certificate.
  • Capture real mdmclient request signatures, then enable strict signature verification.
  • Verify reboot, clone, reprovision, and app-update behavior.
  • Test a non-English guest and reject any automation dependent on English-only button matching.

P3 — optional semantic AX policy

  • Implement Declarative Device Management enablement and declaration endpoints.
  • Deliver macOS 27 app-settings privacy defaults for Accessibility/PostEvent only when semantic AX is enabled.
  • Keep Screen Recording and semantic AX out of ordinary base readiness.

Release gate

A base is releasable only when a clean macOS 27 build proves:

  • no unrecognized GUI sheets from restore through shutdown;
  • required MDM enrollment and exact profile identifiers survive reboot;
  • the first production-shaped workspace mount succeeds without a prompt;
  • VM-agent FDA works without a Data Access Blocked notification;
  • Tips/BTM noise is suppressed without disabling arbitrary app notifications;
  • re-provisioning performs no duplicate enrollment;
  • a fresh clone boots, mounts, executes, and shuts down unattended.

Implementation checkpoint — persisted Mode A state

  • mdm-state.json now retains a versioned CA, configuration, device certificate/private key, PKCS#12 password, and the profile identifiers verified in the guest. It is mode 0600, remains in the golden-base directory, and is deliberately excluded by the explicit clone copier.
  • Mode A accepts a direct maintenance MacVMInstance and uses the existing bounded vsock/nash exec channel instead of requiring the base to appear in the ordinary session registry.
  • A drained MDM queue is no longer sufficient: every UUID from the current policy batch must report Acknowledged, enrollment must report User Approved, and the guest's XML profile inventory must contain the exact top-level identifiers before they are persisted.
  • The production provisioning call site remains intentionally disabled until the macOS 27 UAMDM UI flow and guest-to-host listener binding are validated; enabling the current placeholder would replace one unattended prompt failure with another.

Verification notes

  • The two existing policy files parse as configuration-profile plists.
  • scripts/provision-macos-guest.sh and scripts/build-macos-base.sh pass bash -n.
  • The MDM protocol unit tests cover generated artifacts and a synthetic TLS command drain, but do not exercise a real macOS guest or mdmclient.