Skip to content

macOS guests 1/6: VZ runtime, machine images and instance lifecycle - #498

Draft
chruffins wants to merge 1 commit into
mainfrom
spike/macos-guests
Draft

chruffins wants to merge 1 commit into
mainfrom
spike/macos-guests

Conversation

@chruffins

@chruffins chruffins commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

Summary

PR 1 of 6 for experimental macOS guests on Apple silicon, through the normal image and instance APIs. VZ only. This is not production macOS support. Base: main.

  • Mac boot and platform identity in the shared VM configuration and the VZ shim. The Linux boot path is unchanged, and QEMU rejects Mac boot.
  • Offline cmd/import-macos: imports a stopped bundle by cloning disk and aux, hashing content and config, and publishing a local ready darwin/arm64 image.
  • Per-instance clones of the boot disk and aux. CPU and memory come from the image. An explicit size or vcpus that differs from the image is rejected (400).
  • Identity admission: one active instance per preserved machine identifier, enforced in the manager and by a process-lifetime lock in the shim.
  • Unsupported operations are rejected explicitly, not routed through Linux code: snapshot, fork, standby, restore, update, volumes, env/commands/credentials, health/restart/auto-standby policies, shaping. Snapshot schedules and ingress targeting a macOS guest are refused at admission. Restore and fork refuse Linux guests on a macOS-only server.
  • Opt-in macos_only mode: no Linux kernel or initrd downloads, builders, or Caddy/DNS; Linux creates are rejected. Loopback listen_address for the HTTP API.
  • Stats: CPU and memory are sampled from the Virtualization VM process, which is found through the disk it holds open. Network counters stay 0 under NAT. Logs: hypeman and vmm (VZ shim log) sources. Memory reclaim reports macOS guests as unsupported.
  • Exec, stat, and file copy are gated on the shared guest agent declared by the image. The agent lands in PR 3.

Per-operation status is in docs/macos-support.md. Other PR docs link to it instead of repeating the unsupported list.

Validation

Sequential runs on an Apple M5 host with 16 GiB, using an installed macOS template:

  • Imported image is ready. Create returns 201 (VZ, Running). A duplicate machine identifier returns 409. Standby returns 400.
  • Stop and start work. An API restart recovers the running guest without rebooting it. Delete returns 204, and the imported image stays ready.
  • Live stats: 119 s CPU and 2.67 GB RSS for an 8 GB guest, matching a separate process sample. vmm logs stream. Reclaim returns unsupported with no balloon change.
  • Unit tests cover macOS admission and lifecycle, import, platform validation, identity, DHCP leases, VZ and QEMU rejection, API status mapping, stats parsing, and log mapping.
  • Not run: the full Linux regression suite. Several legacy tests need mkfs.ext4 or Docker Hub access, which the test host does not have.

Limitations

  • Running means the VMM is running, not that the guest is ready. Readiness and orderly guest shutdown come with PR 3.
  • Preserved identity is reused, not rekeyed. Only one active instance may use an identifier.
  • Mixed-mode ingress startup is not validated. An earlier spike hit a storage-converter panic there. Macos-only mode skips ingress.
  • No registry pulls or tagging for macOS images in this PR. Imported image files must stay immutable.
  • No external review verdict. Independent review is blocked by an authentication error (401).

Security fixes in this revision

  • golang.org/x/net and golang.org/x/sys are back to the versions on main (v0.55.0 and v0.45.0). The previous revision downgraded them to v0.53.0 and v0.43.0. The downgrade reintroduced GHSA-5cv4-jp36-h3mw, a denial-of-service bug in the HTML parser fixed in x/net v0.55.0. The other x/ modules were aligned to main too.
  • Ingress resolution refuses macOS guests at runtime (IngressResolver.ResolveInstanceIP). Their addresses are observed NAT leases, so the public route must not reach them. Test: TestIngressResolverRefusesMacOSGuest.

@github-actions

github-actions Bot commented Oct 7, 2026 •

Copy link
Copy Markdown
-->

✱ stlc build

✅ go code · compare

Your SDK build was successful.

generate ✅ → bootstrap ✅ → format ✅

116 files generated at 8492b25 (pushed)

go get github.com/kernel/hypeman-go-staging@8492b253f1db32d89973f369fd87b6624270461e
✅ python code · compare

Your SDK build was successful.

generate ✅ → bootstrap ✅ → format ✅

232 files generated at 20b9781 (pushed)

✅ typescript code · compare

Your SDK build was successful.

generate ✅ → bootstrap ✅ → format ✅

138 files generated at e0448f8 (pushed)

Diagnostics: ❗ 0 new / 1 total error, 💡 0 new / 5 total note
LevelCodeMessageTargets
Build metadata
Buildbd_76XHcpGX-light-acacia
Timestamp2026-10-12T22:10:53.527Z
stlc8413509
Spec hashbcf6d8b9600b
Config hash659c3687c3f0

This comment is auto-generated by stlc and is kept up to date as you push.
If you push new commits, re-run this workflow to update this comment.
Last updated: 2026-10-12 22:11:21 UTC

@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 2 potential issues.

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 4849059. Configure here.

Comment thread lib/instances/macos.go
meta, err := m.loadMetadata(entry.Name())
if err != nil {
return fmt.Errorf("check Mac identity admission: %w", 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.

Identity admission fails on metadata errors

Medium Severity

checkMacOSIdentityAvailable treats any loadMetadata failure as fatal, including missing metadata for an in-progress create. A concurrent Linux or macOS create that has already made a guest directory but not yet written metadata causes an unrelated macOS start or create to fail admission.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 4849059. Configure here.

}
if exit, ok := e.(*exec.ExitError); !ok || exit.ExitCode() != 1 {
return nil, fmt.Errorf("cannot verify storage is closed: %v", e)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

lsof probe leaks process groups

Medium Severity

The import lsof probe runs through exec.CommandContext without a dedicated process group and without SIGKILLing that group on every completion path. Context cancel only stops the wrapper, so lsof descendants can leak after success, ErrWaitDelay, or a wrapper that exits first.

Fix in Cursor Fix in Web

Triggered by learned rule: Subprocess binary probes must use process groups and kill on all completion paths

Reviewed by Cursor Bugbot for commit 4849059. Configure here.

@chruffins chruffins changed the title Experimental local macOS guests through VZ and instance API macOS guests 1/6: native VZ runtime and instance lifecycle Oct 8, 2026
@chruffins

Copy link
Copy Markdown
Contributor Author

Live QA of the reviewed combined PR1–3 sources plus PR4 patches on Apple silicon passed normal authenticated HTTP create from a digest-pinned, previously HTTP-pulled installed macOS OCI artifact (registry offline). Verified actual macOS 27.0.1/26A434 cold boot over pinned-key SSH, inherited 4CPU/8GiB template defaults, duplicate-identity HTTP409, live-guest API restart recovery with unchanged guest boot time, HTTP stop/start with fresh boot time and durable guest-only marker, and unchanged full SHA256 of the immutable materialized base disk+aux afterward. Stop/start here used system-agent-disabled fallback, not verified graceful root-agent shutdown. QA used an isolated data dir/slot; original instance/API/storage were untouched and the QA VM/API ended stopped.

Runtime issue found: the long QA data directory generated an overlong AF_UNIX socket path. VZ reported VM started, then control listener failed listen unix .../vz.sock: bind: invalid argument, resulting in HTTP500 after ~30s. VMM exit and complete create rollback were confirmed. Retrying via a short private data-dir alias passed. Recommend validating the resulting control/vsock socket lengths before VM launch and returning a clear admission error rather than starting then failing. This is not fixed by the workaround and may affect other Unix-socket consumers too.

@chruffins

Copy link
Copy Markdown
Contributor Author

Simplification update at e6e6ab7: shared bounded complete-machine bundle decoding and one macOS request-default preparation; shared concrete create/start network + boot-config preparation. macOS preserves template MAC and avoids Linux allocation/proxy/config-disk dependencies; Linux rollback and request preservation have synthetic tests. Focused instance/API/image race tests passed 3 runs. Identity leases and stopped-storage/export requirements remain.

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 chruffins changed the title macOS guests 1/6: native VZ runtime and instance lifecycle macOS guests 1/6: VZ runtime, machine images and instance lifecycle Oct 11, 2026
@chruffins
chruffins added this pull request to stack #507 October 11, 2026 18:29
@chruffins
chruffins force-pushed the spike/macos-guests branch 2 times, most recently from fe64b62 to 321aedb Compare October 12, 2026 01:34
Experimental, administrator-provisioned macOS guests on Apple silicon through the
normal image and instance APIs. Unsupported operations are rejected explicitly.
Per-operation status: docs/macos-support.md.

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