Skip to content

macOS guests 3/6: Darwin shared GuestService - #500

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

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

Conversation

@chruffins

@chruffins chruffins commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Stack

PR 3/6 of macOS guest integration; based on #498. Draft foundation for Darwin GuestService, readiness and networking. It does not depend on the OCI implementation in #499.

Implemented

  • Build the existing guest-agent for Darwin and reuse the shared gRPC GuestService on vsock 2222, including exec and file/stat RPCs. No permanent parallel prototype protocol.
  • Native Darwin AF_VSOCK listener with host-CID admission, close-on-exec descriptors and net.Conn deadlines; retain the existing Linux vsock listener.
  • Darwin orderly shutdown through the root-only OS command, with signal/privilege/cancellation validation instead of signaling launchd/PID 1.
  • Explicit Unimplemented for Darwin network identity reconfiguration; no NAT/static-IP/policy parity claim.
  • Honor exec stream cancellation; fix the empty-command shell default; stream non-TTY stdout/stderr in bounded chunks rather than buffering all command output.
  • Shared in-process gRPC tests for exec stdout/stderr/env/exit, output-before-exit, cancellation and file round-trips; Darwin shutdown policy and deadline tests.
  • Opt-in guest_agent image declaration enables normal exec/copy vsock paths and graceful-stop attempts; existing templates remain unmanaged and can explicitly disable integration.
  • Probe/persist system-agent readiness independently of macOS VMM-running state, without inventing Linux workload markers.
  • Cancel exec on WebSocket disconnect and stop resize forwarding when an exec session ends.

Validation

  • Full guest-agent package tests pass on Darwin with CGO enabled and disabled. The latter verifies native vsock is explicitly unavailable without CGO.
  • Linux/arm64 CGO-disabled guest-agent test executable cross-compiles; Linux test execution remains pending on an appropriate runner.
  • Focused host instance/API tests pass: macOS capability admission, request defaults/opt-out, readiness persistence/separation, existing boot-marker/state regressions and WebSocket disconnect cancellation.
  • Shared guest client/agent package tests pass.
  • Formatting/diff checks pass.
  • Independent automated review attempted but blocked by authentication 401; no clean independent-review claim.

Tests use an in-memory gRPC transport, temporary files and short-lived test commands. Shutdown policy tests stub the command: no actual host/guest shutdown, agent installation or live API deployment occurred.

Remaining draft gates

  • Live test-guest AF_VSOCK handshake and provisioning of the root LaunchDaemon.
  • Live normal API exec/files, readiness and graceful stop/teardown/recovery validation. Host integration is covered by focused tests, not a provisioned guest handshake.
  • Coordinate desktop session selection with Windows Add Windows guest control and ConPTY #431 rather than invent a conflicting proto extension.
  • Root/desktop handoff authorization, image provisioning and TCC/session permissions.
  • Broader PTY/backpressure/large-output, descendant-process cancellation, transfer failure/size and privilege/logging security tests.
  • Network capabilities/address observation; identity reconfiguration stays unsupported until implemented and validated.

No VM images, credentials, private notes or benchmark evidence are included. The live benchmark guest and its prototype agents remain untouched.

@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.

if err != nil {
return n, &net.OpError{Op: "read", Net: "vsock", Err: err}
}
return n, err

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Read wraps EOF as OpError

Low Severity

vmConn.Read wraps every os.File.Read failure, including bare io.EOF, in net.OpError. Standard net.Conn implementations leave io.EOF unwrapped, and gRPC/HTTP2 compare with == io.EOF for a clean close. Guest disconnects can be treated as transport errors instead of orderly EOF.

Fix in Cursor Fix in Web

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

@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 and others added 15 commits October 11, 2026 00:38
- Persist macOS guest-agent readiness on the public read path by hydrating
  boot markers for macOS instances too.
- Kill the command's process group on cancellation or timeout so shell
  descendants do not outlive the command.
- Bound how long a cancelled exec waits for output to drain, so a client
  that stopped reading cannot hold the handler open.
- Hold the fork lock across Darwin vsock accept so an accepted descriptor
  is not inherited by a concurrent fork before it is marked close-on-exec.
The guest agent runs commands as root, as the Linux agent does, and the host
API is the only authorization boundary. Exec timeout ends the command; it
does not bound a healthy output stream.
- Send the final exit code under the same bound as command output, so a
  client that stopped reading cannot hold the handler past a timeout.
- Kill the command's process group from the context rather than only through
  exec.Cmd.Cancel, which is not called once the direct child has exited.
- Make the drain bound a server field instead of a package variable, so tests
  do not mutate shared state read by handlers that outlive them.
Hydration and persistence duplicated the missing-marker checks, the serial
log parse, marker assignment and the readiness probe. Extract them into one
applyBootMarkers routine. Hydration keeps its scan throttling and rescan
bookkeeping; persistence keeps its save and metrics.
- A command that exits 0 while a background child still holds its output
  returns exec.ErrWaitDelay after the wait delay. That is a normal exit, so
  report its status instead of failing the RPC.
- A command that hits its deadline reports 124 (GNU timeout convention). The
  group kill leaves a process state behind, so the old check never fired.
- Share exit-code selection between the TTY and non-TTY paths.
- Define StoredMetadata.GuestAgentEnabled once and use it in exec, cp, stop,
  vsock and boot-marker code instead of five copies of the expression.
- Make the websocket exec cancel function required and drop the nil checks.
- Remove the unreachable empty-command checks and the redundant Cwd guard.
- Run exec under one cancellable context. The stream-send error path and
  caller disconnect cancel the same context, and killGroupOnDone is the only
  place the process group is killed. This drops the second context and the
  command cancel hooks, which both killed the same group.
- Keep the already-cancelled error before start, as before.
- Define the default ready-file path once per OS in ready_linux.go and
  ready_darwin.go instead of three copies across transport files.
- Remove PR-status wording from the guest-agent doc. It described this patch
  rather than the code and would rot after merge.
Runs on every host with fixture capabilities instead of skipping off Apple silicon.
… rules

- runBounded runs a call on its own goroutine under the drain bound. The
  command wait, output drain and exit-code send each repeated the goroutine,
  channel and bound by hand.
- StoredMetadata.programMarkerSettled states the macOS rule that no workload
  start marker is awaited. Hydration and the agent probe gate use it.
- Boot markers and the metrics readiness check read GuestAgentEnabled instead
  of the raw skip flag. For Linux records these are identical.
- requestDarwinShutdown and its policy test move to untagged files, so the
  shutdown policy runs on Linux CI. Only the euid and command stay darwin-only.
- The two stalled-client exec tests share one helper.
@chruffins
chruffins force-pushed the feat/macos-guest-service branch from ae3685d to c336b30 Compare October 11, 2026 04:41

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