Skip to content

macOS guests 3/6: Darwin guest service, readiness and exec - #500

Open
chruffins wants to merge 2 commits into
feat/macos-oci-imagesfrom
feat/macos-guest-service
Open

chruffins wants to merge 2 commits into
feat/macos-oci-imagesfrom
feat/macos-guest-service

Conversation

@chruffins

@chruffins chruffins commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Third of six macOS guest PRs. Gives Darwin guests a system guest agent (a root LaunchDaemon serving the existing GuestService on vsock 2222), and changes how macOS instances report readiness, run exec, and shut down.

  • An image opts in with guest_agent: true in its platform config. Images without it behave as before.
  • With the agent, a macOS instance is Initializing until the agent answers a readiness probe, then Running, like Linux guests. Without it, Running still means "VMM running".
  • Exec, cp and stat need the agent. Otherwise they return 501 before any WebSocket upgrade.
  • Exec on macOS runs through the agent and is cancelled when the client's WebSocket fails.
  • Stop gives macOS guests a 30-second minimum window and does not shorten it when the shutdown RPC fails.
  • The guest display is now 3024x1964 at 160 ppi (1512x982 points at 2x). It was 1280x800 at 80 ppi. This is cmd/vz-shim/macos_arm64.go, in its own commit (d461bab).

Existing and new flows

Readiness (macOS only; Linux paths are unchanged):

Existing: start -> VMM starts -> state = Running
          (deriveRunningState: stored.MacOS != nil -> StateRunning)

New:      start -> admitMacOSBoot (boot lock, socket-path and identity checks) -> VMM starts
          if !image.guest_agent:          -> state = Running            (unchanged result)
          if  image.guest_agent:
              loop: probe agent with exec "/usr/bin/true" (pooled conn)
                    no answer yet         -> state = Initializing
                    answer                -> state = Running, transition at GuestAgentReadyAt

Exec, cp, stat:

Existing: if inst.MacOS != nil -> 501 "exec is not implemented for experimental macOS instances"

New:      reason := GuestOperationUnsupportedReason(op)
          if reason != ""                  -> 501 with reason, before the WebSocket upgrade
                                              (macOS without guest_agent)
          else                             -> run through the agent
          macOS only: a WebSocket read or write error cancels the session context,
                      and the guest agent cancels the command and its process group

Stop:

Existing: graceful shutdown RPC; if it was not sent and the wait exceeds
          shutdownFailureFallbackWait, shorten the wait to that fallback
          stop timeout = requested timeout

New:      same, except macOS keeps its full grace period (no shortening)
          macOS: stop timeout = max(requested, 30s)

Boot admission (refactor, same checks): startAndBootVM calls admitMacOSBoot, which returns a release func, instead of running the socket-path and identity checks inline.

Change size

Totals from the PR: +2,038 / −93 across 49 files. Of the 2,038 added lines, 1,060 are tests (17 files) and 127 are docs (3 files). The remaining 851 are implementation lines.

Approximate meaningful implementation size: about 600 lines. These are estimates, not Git counts. Excluded: tests, docs, mechanical wiring (delete, manager, vsock and network call-site updates, about 20 lines), and moved code.

Chunk Main files Raw added Approx. meaningful
Darwin guest agent: vsock listener, readiness probe, network, shutdown policy vsock_darwin.go, vsock_darwin_nocgo.go, ready_darwin.go, network_darwin.go, shutdown_darwin.go, shutdown_policy.go, main.go ~242 ~200
Exec session lifecycle in the guest agent exec_lifecycle.go, exec.go ~350 ~250
Readiness and boot state query.go, start.go, macos.go, guest_os.go, types.go, create.go ~200 ~120
Host-side exec, cp and stat gating cmd/api/api/exec.go, cp.go, guest_unsupported.go, lib/guest/client.go ~50 ~25
Stop timing stop.go 8 ~8

Behavior changes

  • Readiness: an agent-enabled macOS instance is Initializing until the agent answers, so it reports Running later than before. This is the only change for macOS instances that declare the agent.
  • Exec on macOS without the agent: still 501, with a different message.
  • Shutdown: Darwin accepts only an orderly SIGTERM, and the agent must run as root. Otherwise the shutdown RPC fails.
  • Display: the VM's device config changes, so a snapshot taken at 1280x800 may not restore. Not tested.

Rollout

Merge order in the stack: #498 (merged 2026-10-19), then #499, then this PR, then #506, then #514. #503 is not needed for v0. This PR's base is feat/macos-oci-images (#499), so it needs #499 merged or rebased onto main first.

Testing

  • go test ./lib/system/guest_agent/ -count=1 -race — ok (7.8 s)
  • go test ./lib/guest/ -count=1 — ok
  • gofmt -l on the 46 changed .go files — clean
  • Not run: go build ./..., go test for ./lib/instances/, ./cmd/api/..., and ./cmd/vz-shim/. This checkout lacks the embedded binaries those packages need (cloud-hypervisor, the guest agent, caddy; produced by make), so they fail at setup. Darwin builds and the cgo vz shim were not compiled. The display change has not been booted.
  • Earlier native QA, from the original description and not re-run for the current head: six runs on Apple silicon (macOS 27.0.1 host and guests), plus a VZ restore spike. The simplification pass after run 6 was not re-run natively.

Caveats / Not included

Review focus

  • lib/system/guest_agent/exec_lifecycle.go: cancellation, process-group kill, and bounded output drain.
  • The readiness change in lib/instances/query.go (deriveRunningState) and macOSRunningPhase.

Stack

Each PR is one commit on the one below it.

  1. macOS guests 1/6: VZ runtime, machine images and instance lifecycle #498 VZ runtime, machine images and instance lifecycle (merged)
  2. macOS guests 2/6: OCI machine-image integration #499 OCI machine images
  3. macOS guests 3/6: Darwin guest service, readiness and exec #500 Darwin guest agent, readiness and exec (this PR)
  4. macOS guests 4/6: snapshots, schedules and stopped forks #506 snapshots, standby and stopped clones
  5. macOS guests 5/6: guest-port forwarding #514 guest-port forwarding
  6. macOS guests 6/6: experimental machine-image builds #503 machine-image builds (not needed for v0; can land later)

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 8f6dd1c. Configure here.

Comment thread lib/system/guest_agent/vsock_darwin.go
@chruffins

Copy link
Copy Markdown
Contributor Author

Real native Darwin GuestService2222 QA passed in an isolated cold-booted OCI guest using the current shared binary, user-launched at UID501 only. Host shared client verified id -u=501, synthetic file copy-to/from exact bytes, and client deadline cancellation of shell + sleep descendant (child PID no longer existed). Three repeated race runs passed. Shutdown correctly returned PermissionDenied for nonroot; guest remained running. Fixed two QA harness assumptions before the passing run: CopyFromInstance's destination is a directory, and deadline may surface as gRPC DeadlineExceeded rather than errors.Is(context.DeadlineExceeded).

Root daemon was not installed: guest noninteractive sudo is unavailable and no privileged provisioning was performed. System image capability remained false. These results prove native transport/user-level exec/files/cancellation and shutdown admission, not normal API system-agent installation/readiness or successful graceful root shutdown. Those remain gates. Original guest/API/storage untouched; isolated QA VM ended stopped.

@chruffins
chruffins force-pushed the feat/macos-guest-service branch 5 times, most recently from a1ed358 to 243ce9a Compare October 9, 2026 21:53
@chruffins
chruffins changed the base branch from spike/macos-guests to feat/macos-oci-images October 9, 2026 21:53
@chruffins

Copy link
Copy Markdown
Contributor Author

Simplification update at 243ce9a: common command setup/process-group cancellation and bounded wait/drain/final-status handling for both TTY modes. Permanent tests cover pre-cancelled starts and failed output sends cancelling/reaping commands. Full agent/client race suites passed 3 runs; combined post-restack focused suites also passed 3 runs. Base changed to feat/macos-oci-images so the image rollback fix and bundle consolidation are inherited once rather than duplicated. Root provisioning remains undone.

All PRs remain draft. This is source-level/synthetic validation, not a new live boot or production build proof. Default Codex independent review was attempted but is still blocked by authentication (HTTP401). Published atomically after checking reviewer heads with explicit per-ref force-with-lease; prior heads preserved locally.

@chruffins

Copy link
Copy Markdown
Contributor Author

Root QA follow-up pushed as ae3685d; desktop restacked atomically with explicit leases. Manually installed shared system-role agent survived isolated normal-API cold boots. Repeated race-instrumented authenticated API tests verified UID0 exec (both TTY modes), root-owned0600 file copy round trips, and descendant cancellation. Live testing reproduced and fixed Darwin readiness using the nonexistent /bin/true; /usr/bin/true now probes successfully and normal GET persists GuestAgentReadyAt without a Linux workload marker. Fixed premature shutdown fallback after a lost Darwin RPC reply: configured grace period is retained while still requiring owned VMM exit; Linux policy is unchanged. Final normal API stop accepted the RPC and confirmed VMM exit/storage closure without forced fallback. Corrected stale opt-in test calls left after runtime simplification. Focused instances/API/agent/client tests pass race,count3; default independent autoreview remains401-blocked. Manual QA provisioning is not automatic image installation or end-to-end build/publication proof. PR remains draft.

@chruffins
chruffins force-pushed the feat/macos-guest-service branch 2 times, most recently from c336b30 to 1cae99b Compare October 11, 2026 16:31
@chruffins
chruffins force-pushed the feat/macos-guest-service branch from 1cae99b to 24fad86 Compare October 11, 2026 16:55
@chruffins
chruffins force-pushed the feat/macos-guest-service branch from 24fad86 to 2df3f56 Compare October 11, 2026 17:16
@chruffins
chruffins force-pushed the feat/macos-guest-service branch from 2df3f56 to ebf6c6e Compare October 11, 2026 17:30
@chruffins
chruffins force-pushed the feat/macos-guest-service branch from ebf6c6e to fbfd9d2 Compare October 11, 2026 18:11
@chruffins chruffins changed the title macOS guests 3/6: Darwin shared GuestService macOS guests 3/6: Darwin guest service, readiness and exec Oct 11, 2026
@chruffins
chruffins added this pull request to stack #507 October 11, 2026 18:29
@chruffins
chruffins force-pushed the feat/macos-guest-service branch 2 times, most recently from 39e247a to f472950 Compare October 11, 2026 21:49
@chruffins
chruffins force-pushed the feat/macos-guest-service branch 2 times, most recently from dd32757 to 8c7482e Compare October 14, 2026 00:19
@chruffins
chruffins force-pushed the feat/macos-guest-service branch from 8c7482e to 9d13f55 Compare October 14, 2026 02:27
@chruffins
chruffins force-pushed the feat/macos-guest-service branch 2 times, most recently from 5bb5b41 to a8f7048 Compare October 14, 2026 06:31
@chruffins
chruffins force-pushed the feat/macos-guest-service branch from a8f7048 to 0462e06 Compare October 14, 2026 06:51
@chruffins
chruffins force-pushed the feat/macos-guest-service branch 2 times, most recently from 85e8d2f to cf64b2e Compare October 14, 2026 16:55
@chruffins
chruffins force-pushed the feat/macos-guest-service branch from cf64b2e to c0da4ad Compare October 14, 2026 17:08
@chruffins
chruffins removed this pull request from stack #508 October 14, 2026 17:08
@chruffins
chruffins added this pull request to stack #515 October 14, 2026 17:10
@chruffins
chruffins marked this pull request as ready for review October 14, 2026 19:12
@chruffins
chruffins force-pushed the feat/macos-guest-service branch 2 times, most recently from a8ff002 to 7a480d4 Compare October 15, 2026 00:27
@chruffins
chruffins force-pushed the feat/macos-guest-service branch from 7a480d4 to 3a0d7a5 Compare October 15, 2026 16:40
@chruffins
chruffins force-pushed the feat/macos-guest-service branch from 3a0d7a5 to 77b12e8 Compare October 15, 2026 19:26
chruffins and others added 2 commits October 20, 2026 20:13
Shared GuestService on host-CID vsock 2222, root system agent, agent readiness
probe, agent-gated exec, copy and stat, and a macOS stop floor. Darwin exec runs
its own bounded lifecycle in exec_lifecycle.go. Linux exec, boot markers, stop
and the guest client are unchanged.
The guest display was fixed at 1280x800 at 80 ppi. Use 3024x1964 at 160 ppi,
which is 1512x982 points at 2x. Changes the display only on the macOS guest
path; the Linux path is untouched.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant