Skip to content

macOS guests 7/7: base publication tooling - #536

Draft
chruffins wants to merge 1 commit into
feat/macos-image-buildsfrom
feat/macos-base-tooling
Draft

chruffins wants to merge 1 commit into
feat/macos-image-buildsfrom
feat/macos-base-tooling

Conversation

@chruffins

@chruffins chruffins commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Operator tooling for publishing a macOS base image, split out of the native build PR. Stacked on #503; base is feat/macos-image-builds.

  • cmd/publish-macos-base: takes an exclusively owned, stopped macvm bundle and a guest audit report, APFS-clones it through the existing import check, publishes it with StreamingMachinePublisher to builds/base-..., verifies stored blobs and the manifest digest, and writes an immutable 0600 receipt. Requires explicit --stopped and --clean-install-reviewed attestations.
  • scripts/macos/audit-base.py: fail-closed policy audit run inside the guest over GuestService: pinned agent and plist hashes, exact account list, empty or baselined homes, no credential or browser state, toolchain probe. Evidence contains hashes and versions only.
  • scripts/macos/example/build.sh: offline example build source.
  • docs/macos-base-images.md: release flow, audit staging rules, publication command and remaining gates.
  • CI: test-macos-pipeline-contract runs the audit unit tests and a syntax check of the example.

No HTTP endpoint is added. Nothing in the build path depends on this; it is the only way to get a base from a local bundle into the registry.

Testing

  • go test ./cmd/publish-macos-base — pass
  • go vet ./cmd/publish-macos-base — pass
  • python3 -m unittest discover -s scripts/macos -p 'test_*.py' — 3 tests pass
  • sh -n scripts/macos/example/build.sh — pass
  • Not run: a real base publication on Apple silicon.

Note

Medium Risk
Introduces registry signing-secret handling and a new macOS base publication path tied to image import/publish, though it remains offline operator tooling with strict validation and masked errors.

Overview
Adds an operator-only path to turn a stopped local macvm bundle plus guest audit evidence into a verified internal-registry base image—no new HTTP API.

publish-macos-base validates a strict guest audit JSON and a private three-file bundle (disk.img, aux.img, config.json), APFS-clones via existing ImportMacOSImage, uploads with StreamingMachinePublisher, and writes a 0600 receipt only after manifest verification (failures remove partial receipts). Publication requires explicit --stopped and --clean-install-reviewed attestations and runs only on Apple Silicon macOS (stub elsewhere).

audit-base.py is a fail-closed in-guest policy check (pinned agent/plist, account list, home baseline, forbidden credential/browser paths, toolchain probes) that emits hash-only JSON for the publisher. Docs in macos-base-images.md, an offline example/build.sh, and CI job test-macos-pipeline-contract (Python contract tests + shell syntax check).

Reviewed by Cursor Bugbot for commit 47b5565. Configure here.

@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 47b5565. Configure here.

pid = re.search(r"\bpid = ([0-9]+)\b", job_state)
require(pid is not None, "agent-pid-missing")
process = command("/bin/ps", "-p", pid.group(1), "-o", "uid=", "-o", "comm=").split(None, 1)
require(process == ["0", args.agent], "agent-process-mismatch")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Audit process check never matches

High Severity

The running-agent check compares ps comm output to the full --agent path. Darwin comm is the executable basename and is length-limited, so a correctly provisioned LaunchDaemon fails with agent-process-mismatch and the audit cannot emit a passing report.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 47b5565. Configure here.

for item in Path(root).iterdir():
require(not item.name.startswith(("hypeman-build-", "profile", *args.runtime_state_prefix)), "runtime-state")
require(not list(Path("/private/etc/ssh").glob("ssh_host_*")), "host-ssh-keys")
require(not Path("/Library/Preferences/com.apple.loginwindow.plist").exists(), "review-autologin-state")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Autologin check rejects normal installs

Medium Severity

The autologin probe fails if /Library/Preferences/com.apple.loginwindow.plist exists. Setup Assistant writes that file for last-user state without setting autoLoginUser, so a required clean install is rejected as review-autologin-state.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 47b5565. Configure here.

@chruffins
chruffins force-pushed the feat/macos-image-builds branch from eb55dc0 to ae39970 Compare October 29, 2026 20:07
publish-macos-base turns a stopped, audited macvm bundle into a verified OCI
machine image in the internal registry and writes an immutable receipt.
audit-base.py checks a guest for a clean, agent-provisioned base before
publication. Includes the example build source and the contract CI job.
@chruffins
chruffins force-pushed the feat/macos-base-tooling branch from 47b5565 to 335855c Compare October 29, 2026 20:07

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