vz-macos

command module
v0.0.0-...-eebc809 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 19, 2026 License: MIT Imports: 96 Imported by: 0

README

cove

macOS VMs that suspend, snapshot, and script.

Go Platform License

cove is a CLI for creating and managing macOS and Linux virtual machines on Apple Silicon using Apple's Virtualization.framework. Pure Go, cgo-free (purego).

Install

brew install tmc/tap/cove

Or from source:

go install github.com/tmc/vz-macos@latest

Quick Start

cove up -user myuser                    # install + provision + boot (one command)

Or step by step:

cove install                            # download IPSW and install macOS
cove inject -user myuser                # provision user, skip Setup Assistant
cove run                                # boot with native GUI window

On first launch, cove auto-signs itself with the required Virtualization.framework entitlements. No manual codesign step needed.

Features

Suspend and Resume

VMs suspend to disk on quit and resume where they left off. Cold boot with -no-resume.

Snapshots

VM state snapshots and APFS copy-on-write disk snapshots. Checkpoint before risky changes, restore in seconds.

cove disk-snapshot save before-update
cove disk-snapshot restore before-update
VZScript Engine

Declarative recipes for guest VM configuration. Built on rsc.io/script with guest-agent and OCR commands.

cove vzscript list                      # list built-in recipes
cove vzscript run homebrew golang       # install Homebrew, then Go (deps resolved)
cove vzscript run ./custom.vzscript     # run a custom script

Guest commands: guest-exec, guest-shell, guest-cp, guest-write, guest-read. UI commands: ocr-click, ocr-wait, type, key, click, screenshot.

SIP Management

Disable or enable System Integrity Protection with automated recovery boot.

cove sip disable-auto -user admin -password secret -confirm
cove run -recovery -gui -unattended -boot-commands ~/.vz/vms/default/sip-disable-commands.txt
Guest Agent

A vsock gRPC agent injected into the guest at install time. Execute commands, transfer files, manage proxy settings, share clipboard -- all without SSH.

cove run -clipboard -proxy http://192.168.64.1:8080
Native GUI Window

macOS-native window with toolbar, menu bar, and frame persistence per VM. Multi-display support with resolution presets.

cove run -display 4k
cove run -display 1920x1080 -display 1024x768
Linux VMs

Ubuntu Server with cloud-init automated install. EFI boot, Virtio GPU, serial console, Rosetta x86-64 translation.

cove install -linux
cove run -linux -gui -rosetta
Shared Folders

VirtioFS volume mounts with runtime hot-add.

cove run -share ~/projects -share /data:ro

Comparison

cove Lume Tart UTM
Language Go (purego) Swift Swift Swift/Obj-C
Suspend/resume Yes No No Yes
VM state snapshots Yes No No Yes
Disk snapshots (APFS COW) Yes No No No
Script engine VZScript (rsc.io/script) No No No
Guest agent vsock gRPC vsock gRPC No SPICE agent
SIP management Automated No No Manual
Unattended provisioning Disk injection + OCR Cloud-init Packer Manual
Linux VMs Yes Yes Yes Yes (QEMU)
x86 guests No No No Yes (QEMU)
GUI Native AppKit Electron None Native AppKit
Control API Unix socket (protobuf JSON) HTTP REST None AppleScript
Open source MIT Apache-2.0 AGPL-3.0 Apache-2.0

Usage Examples

One-Command Setup
# Install, provision, and boot with vzscripts
cove up -user dev -vzscripts homebrew,golang
Headless CI Runner
cove install -ipsw ~/cache/restore.ipsw
sudo cove inject -user ci -password secret -skip-setup-assistant
cove run -headless -cpu 4 -memory 8
Control a Running VM
TOKEN=$(cat ~/.vz/vms/default/control.token)
echo '{"type":"status","auth_token":"'$TOKEN'"}' | nc -U ~/.vz/vms/default/control.sock
echo '{"type":"screenshot","auth_token":"'$TOKEN'"}' | nc -U ~/.vz/vms/default/control.sock
Recovery and SIP
cove sip disable-auto -user admin -password secret -confirm
cove run -recovery -no-resume -gui -unattended \
  -usb ~/.vz/vms/default/recovery-disk.img \
  -boot-commands ~/.vz/vms/default/sip-disable-commands.txt

Architecture

cove uses Apple's Virtualization.framework through purego for cgo-free Objective-C interop. VMs are stored in ~/.vz/vms/<name>/ with disk images, identity files, and a Unix domain control socket.

Project Structure
cove/
├── main.go                     # CLI entry point, subcommand routing
├── macos.go                    # macOS VM configuration and lifecycle
├── linux.go                    # Linux VM configuration
├── installer.go                # macOS installation from IPSW
├── linux_installer.go          # Cloud-init based Linux installation
│
├── provision.go                # Core provisioning types and orchestration
├── provision_cli.go            # inject/provision CLI handling
├── provision_mount.go          # Disk mount/unmount for injection
├── provision_launchdaemon.go   # LaunchDaemon plist generation
├── provision_autologin.go      # kcpassword + loginwindow auto-login
│
├── control_socket.go           # Unix socket server for VM control
├── control_client.go           # Programmatic control client
├── ctl.go                      # ctl subcommand CLI
│
├── screenshots.go              # CGWindowListCreateImage capture
├── screen_detection_ocr.go     # OCR-based UI state detection
├── ocr.go                      # Vision framework OCR bindings
│
├── vzscript.go                 # VZScript engine (rsc.io/script)
├── vzscript_apply.go           # VZScript CLI and runner
├── vzscripts/                  # Built-in recipes (.vzscript)
│
├── agent_inject.go             # Cross-compile and inject guest agent
├── agent_client.go             # Agent client API
├── cmd/vz-agent/               # In-guest agent daemon (vsock gRPC)
│
├── snapshots.go                # VM state + disk-level snapshots
├── sip.go                      # SIP management
├── up.go                       # "up" command orchestrator
├── boot_commands.go            # Boot command DSL parser
├── unattended.go               # Unattended install orchestrator
│
├── proto/                      # Protobuf definitions (agent + control)
├── internal/autosign/          # Auto-signing with entitlements
└── swift/VZControl/            # Swift package for control socket client

Requirements

  • Apple Silicon Mac (M1/M2/M3/M4)
  • macOS 12.0+ (Monterey or later)
  • Xcode Command Line Tools

Feature Maturity

Maturity Features
GA install, run, provisioning (inject), vzscripts, suspend/resume
Beta snapshots, guest agent, clipboard sharing, shared folders
Experimental UTM import, memory balloon, Windows stub

Security

  • Control socket: per-VM bearer token, owner-only permissions (0600)
  • Guest agent: unencrypted gRPC over vsock, scoped to host-VM boundary
  • Entitlements: auto-signed on first launch with com.apple.security.virtualization and com.apple.security.hypervisor

Contributing

git clone https://github.com/tmc/vz-macos
cd cove
go build -o cove .
codesign -s - -f --entitlements internal/autosign/vz.entitlements ./cove
./cove run

Run tests:

go test -short ./...
make release-check    # vet + test + goreleaser snapshot

License

MIT -- see LICENSE.

References

Documentation

Overview

agent_client.go - Host-side connect-go client for the guest agent.

Connects to the vz-agent daemon running inside the guest via VZVirtioSocketDevice on vsock port 1024.

agent_control.go - Bridge between control socket and GRPC guest agents.

Extends the control socket with agent commands that delegate to the vz-agent processes running inside the guest. Two agents:

  • Daemon (port 1024): root context, system ops
  • User agent (port 1025): user session, TCC/FDA grants

New command types:

agent-connect      - Establish vsock connection to guest daemon agent
agent-ping         - Check if daemon agent is alive
agent-info         - Get guest system information
agent-exec         - Run a command in the guest (as root)
agent-user-exec    - Run a command in the user session (TCC/FDA)
agent-read         - Read a file from the guest
agent-write        - Write a file to the guest
agent-shutdown     - Graceful guest shutdown via agent

agent_inject.go - Cross-compile and inject the vz-agent binary into a VM disk.

The vz-agent daemon runs inside the guest as a LaunchDaemon and exposes a GRPC API over vsock port 1024. The host connects via VZVirtioSocketDevice.

Injection writes two files to the Data volume:

  • /usr/local/bin/vz-agent (the binary)
  • /Library/LaunchDaemons/com.github.tmc.vz-macos.vz-agent.plist (the LaunchDaemon)

The LaunchDaemon is configured with KeepAlive=true so launchd restarts the agent if it crashes. It runs as root to allow user management and file operations across the guest filesystem.

authorization.go — Native macOS privilege escalation via AuthorizationServices.

This uses the Security framework to obtain an authorization reference and then launches a privileged tool with AuthorizationExecuteWithPrivileges. It avoids collecting the raw host admin password in app code.

blocks.go - Objective-C block support for completion handlers

boot_commands.go - Recovery automation helpers shared by unattended setup and vzscript

cgevent_helpers.go - CGEvent input helpers.

Delegates to github.com/tmc/apple/x/vzkit/input for the implementation. Provides backwards-compatible wrappers for existing callsites.

clipboard.go - Host↔guest clipboard sharing via SPICE agent.

Delegates to github.com/tmc/apple/x/vzkit/clipboard for the implementation.

clone.go - VM cloning functionality

control_client.go - Programmatic client for VM control socket

control_socket.go - Socket-based control for keyboard, mouse, and screenshots

control_socket_commands.go - Key code constants and modifier flags for VM control socket.

Message types are defined in proto/controlpb/control.proto.

control_socket_ocr.go - OCR-driven automation methods for ControlServer

ctl.go - CLI for interacting with VM control socket

ctl_ready.go - "ctl ready" subcommand: pluggable readiness probes.

agent-ping answers whether the guest agent process is alive. Orchestrators often need to know whether the *environment* is ready: Xcode CLI tools, Go, Homebrew, etc. The ready command runs a set of named checks via the agent and reports per-check pass/fail.

Checks are implemented host-side: each check is one agent-exec RPC against a small command (typically "which <tool>" or a tool-specific "version" invocation). The set of built-in checks is defined in readyChecks below; any name not in the map is treated as a generic "which <name>" probe so callers can pass arbitrary tool names without code changes.

diskguard.go - Disk image attach/detach safety.

Delegates to github.com/tmc/apple/x/vzkit/disk for the implementation.

display.go - Multi-display support for VMs

This file delegates to vzkit for core display configuration. App-specific helpers (verbose logging) remain here.

cove - macOS and Linux VM management using Apple's Virtualization framework

Overview

cove is a command-line tool for creating, provisioning, and running virtual machines using Apple's Virtualization.framework. It supports both macOS and Linux guests on Apple Silicon Macs.

The tool uses purego for cgo-free Objective-C interop, making it a pure Go implementation that interfaces directly with Apple's native frameworks.

Quick Start

Install and provision a macOS VM:

# Download and install macOS
./cove install -ipsw ~/Downloads/UniversalMac_14.0_RestoreImage.ipsw

# Provision VM for automatic user creation
./cove provision -user testuser -password secret123 -skip-setup-assistant

# Verify provisioning files are correct
./cove verify

# Run the VM with GUI
./cove run -gui

Architecture

The tool is organized into several components:

main.go           - CLI entry point and VM configuration
installer.go      - macOS installation from IPSW restore images
provision.go      - Disk provisioning and LaunchDaemon setup
password.go       - Password hashing and auto-login encoding
control_socket.go - Unix socket server for VM control
screenshots.go    - Screen capture using CGWindowListCreateImage
linux.go          - Linux VM support with EFI boot
linux_installer.go - Cloud-init based Linux installation

Provisioning System

The provisioning system enables fully non-interactive VM setup by injecting files directly into the VM's disk image before first boot.

Key concepts:

  1. APFS Volume Mounting - VM disks contain multiple APFS volumes; the "Data" volume holds user data and configuration files.

  2. LaunchDaemon Injection - A plist and script are written to /Library/LaunchDaemons/ to run at boot and create the user account.

  3. Setup Assistant Bypass - The .AppleSetupDone marker tells macOS to skip the first-boot setup wizard.

  4. Auto-Login - kcpassword and loginwindow.plist enable automatic login to the provisioned user account.

See provision.go and password.go for detailed documentation of each component.

Control Socket

Running VMs expose a Unix domain socket for control and monitoring:

~/.vz/vms/<name>/control.sock
~/.vz/vms/<name>/control.token

Commands are sent as JSON (protobuf JSON mapping of ControlRequest). The "type" field selects the handler; command payloads use the matching field name:

{"type":"ping","auth_token":"<token>"}                     - Health check
{"type":"status","auth_token":"<token>"}                   - VM state and capabilities
{"type":"screenshot","auth_token":"<token>"}               - Capture display (base64 PNG in response)
{"type":"screenshot","auth_token":"<token>","screenshot":{"scale":0.5}} - Capture at half resolution
{"type":"key","auth_token":"<token>","key":{"key_code":36}} - Send keypress (Return)
{"type":"text","auth_token":"<token>","text":{"text":"hello"}} - Type text string
{"type":"pause","auth_token":"<token>"}                    - Pause VM
{"type":"resume","auth_token":"<token>"}                   - Resume VM

See proto/control.proto for the full schema.

Example usage:

TOKEN=$(cat ~/.vz/vms/default/control.token)
echo "{\"type\":\"status\",\"auth_token\":\"$TOKEN\"}" | nc -U ~/.vz/vms/default/control.sock

Linux VM Support

Linux VMs use a different boot mechanism than macOS:

  1. EFI Boot - VZEFIBootLoader with NVRAM variable store
  2. Virtio GPU - VZVirtioGraphicsDeviceConfiguration for display
  3. Cloud-init - Automatic installation via NoCloud datasource

Install and run a Linux VM:

./cove install -linux -provision-user ubuntu -provision-password secret
./cove run -linux -gui

VM Directory Structure

Each VM is stored in ~/.vz/vms/<name>/ with these files:

disk.img      - Main storage (sparse APFS/ext4 image)
aux.img       - macOS auxiliary storage (NVRAM, etc.)
hw.model      - Hardware model identifier (macOS only)
machine.id    - Machine identifier (macOS only)
efi.nvram    - EFI variable store (Linux only)
control.sock  - Control socket (when running)
control.token - Control socket auth token (owner-read only)

Entitlements

cove signs itself on first launch if the required virtualization entitlements are missing. No manual codesign step is required.

Required entitlements (internal/autosign/vz.entitlements):

com.apple.security.virtualization          - Basic VM capability
com.apple.security.hypervisor             - Hypervisor access

Requirements

  • Apple Silicon Mac (M1/M2/M3/M4)
  • macOS 12.0+ (Monterey or later)
  • Xcode Command Line Tools (for the automatic first-launch codesign step)
  • IPSW restore image for macOS installation

References

guest_tools.go - Download and inject UTM's pre-built SPICE guest tools.

UTM distributes macOS SPICE guest tools as a .pkg installer inside a .img disk image. The tools provide host↔guest clipboard sharing via the SPICE agent protocol (VZSpiceAgentPortAttachment on the host side).

The injection workflow:

  1. Download utm-guest-tools-macos-<version>.img from GitHub releases
  2. Mount the .img to extract the .pkg installer
  3. Copy the .pkg into the VM disk image at /var/db/vz-guest-tools.pkg
  4. Install a LaunchDaemon that runs "installer -pkg" on first boot

After installation, spice-vdagent and spice-vdagentd run as system services. macOS may prompt the user to allow these binaries on first launch.

Requirements: macOS 13+ host, macOS 15+ guest for clipboard sharing.

helper.go — privileged helper daemon for cove.

cove's GUI install path needs to chown files to root:wheel on a mounted VM disk image. Calling AuthorizationCreate from a background goroutine can hang indefinitely when no foreground NSWindow is available (filed: vz-macos-hdz), and even when it works it prompts for credentials on every run.

The helper is a small LaunchDaemon that runs as root and listens on /var/run/cove-helper.sock. It accepts a single op (exec_script) that runs a bash script as root. Authentication is by peer UID: only the user who installed the helper can talk to it. Installation requires one admin auth (via runElevatedBashNative) and persists across reboots.

Trust model: the helper is "cove's elevated half." Once installed, it trusts the installing user's cove binary to send sensible scripts. This is no worse than a passwordless sudoers rule for cove.

installer.go - macOS installation following Code-Hex/vz patterns

IPSW download and restore image handling for macOS VMs.

This file contains native NSURLSession-based download functionality for Apple IPSW restore images, with support for resumable downloads.

iterm2_proxy.go - WebSocket-to-vsock relay for iTerm2 API access.

Bridges host-side WebSocket clients to the guest's iTerm2 WebSocket API running on vsock port 1912. Each incoming WebSocket connection opens a new vsock connection to the guest and relays binary frames bidirectionally.

keychain.go — Native macOS keychain access via purego Security.framework bindings.

Mirrors the pattern in authorization.go. Used by gateway_token.go to store the cove serve master token as a generic password without shelling out to /usr/bin/security.

Linux VM support for cove.

Supports two boot modes: - Direct kernel boot: uses VZLinuxBootLoader with kernel + initrd - EFI boot: uses VZEFIBootLoader with ISO image for installation

Linux VM installation support using cloud-init autoinstall.

This implements unattended Ubuntu Server installation using: - cloud-init NoCloud datasource for initial configuration - Ubuntu autoinstall for fully automated installation

The installer creates a cloud-init ISO containing user-data and meta-data files that Ubuntu reads during installation to configure: - User account with password - SSH server - Hostname

This example demonstrates running macOS and Linux virtual machines using the generated purego bindings for Apple's Virtualization framework.

memory.go - Memory balloon runtime control

Core balloon operations delegate to vzkit. The control socket command handler and protobuf integration remain here.

networking.go - Advanced networking support (NAT, bridged, VMNet)

ocr.go - Vision framework OCR for screen text recognition.

Delegates to github.com/tmc/apple/x/vzkit/ocr for the implementation. Type aliases maintain backwards compatibility with existing callsites.

ocr_debug.go - Debug visualization for OCR results.

Delegates to github.com/tmc/apple/x/vzkit/ocr for the implementation.

ocr_search_options.go - OCR search region and options.

Delegates to github.com/tmc/apple/x/vzkit/ocr for the implementation.

password.go - macOS password hash and auto-login support

Overview

This file implements macOS password hashing and auto-login credential encoding. These are used for direct user plist creation (advanced mode) and auto-login configuration.

Password Hash Format (SALTED-SHA512-PBKDF2)

Since macOS 10.8 (Mountain Lion), passwords are stored using PBKDF2 with SHA-512. The hash is stored in the user's plist at:

/var/db/dslocal/nodes/Default/users/<username>.plist

The ShadowHashData structure contains:

SALTED-SHA512-PBKDF2:
  entropy:    128-byte derived key (PBKDF2 output)
  iterations: 45000 (macOS 10.14+ default)
  salt:       32 random bytes

The derivation formula is:

entropy = PBKDF2(password, salt, iterations, keyLength=128, hashFunc=SHA512)

Security Considerations

The 45000 iteration count provides reasonable protection against offline brute-force attacks while maintaining acceptable login performance. This value is the macOS default as of macOS 10.14 Mojave.

For VM provisioning, security is less critical since:

  • The password is already known (provided at injection time)
  • VMs are typically development/testing environments
  • The disk image can be accessed directly anyway

Auto-Login (kcpassword)

macOS auto-login stores the password in /etc/kcpassword using a simple XOR cipher. This is intentionally weak because:

  1. Auto-login fundamentally requires recoverable credentials
  2. Physical access to the machine defeats most protections anyway
  3. The feature is opt-in and disabled by default

The encoding uses an 11-byte key that repeats:

Key: 0x7D, 0x89, 0x52, 0x23, 0xD2, 0xBC, 0xDD, 0xEA, 0xA3, 0xB9, 0x1F

The password is XOR'd byte-by-byte with this key, then padded to a multiple of 12 bytes with random data (11 bytes of padding + null terminator pattern).

kcpassword Encoding Algorithm

func encode(password string) []byte {
    key := []byte{0x7D, 0x89, 0x52, 0x23, 0xD2, 0xBC, 0xDD, 0xEA, 0xA3, 0xB9, 0x1F}
    result := make([]byte, 0)
    for i, b := range []byte(password) {
        result = append(result, b ^ key[i % len(key)])
    }
    // Pad to multiple of 12 bytes
    padLen := 12 - (len(result) % 12)
    if padLen < 12 {
        result = append(result, randomBytes(padLen)...)
    }
    return result
}

Auto-Login Configuration Files

Two files work together to enable auto-login:

  1. /etc/kcpassword - XOR-encoded password (this file)
  2. /Library/Preferences/com.apple.loginwindow.plist - Contains: - autoLoginUser: username to auto-login - GuestEnabled: false - SHOWFULLNAME: false

Both files must be owned by root (uid=0) for security, though loginwindow.plist is less strict than LaunchDaemon plists.

User Plist Structure

For direct user creation (bypassing sysadminctl), the user plist contains:

name:           ["username"]           - Account short name
realname:       ["Full Name"]          - Display name
uid:            ["501"]                - User ID (501+ for regular users)
gid:            ["20"]                 - Primary group (20 = staff)
home:           ["/Users/username"]    - Home directory path
shell:          ["/bin/zsh"]           - Login shell
generateduid:   ["<UUID>"]             - Unique identifier
ShadowHashData: <binary plist>         - Password hash (nested plist)

The ShadowHashData is itself a binary plist embedded as a data blob within the outer user plist. This nested structure is how macOS stores password hashes securely.

macOS Version Compatibility

Version         Password Format         Auto-Login
macOS 10.8+     SALTED-SHA512-PBKDF2    kcpassword XOR
macOS 10.14+    45000 iterations        Same
macOS 14/15     Same format             May require FileVault disabled

Usage Examples

Generate password hash for direct user plist creation:

shadowHash, err := GenerateShadowHashData("secretpassword")
userPlist, err := CreateUserPlist("testuser", "Test User", "secretpassword", 501, true)
plistData, err := EncodeUserPlist(userPlist)
os.WriteFile("/path/to/user.plist", plistData, 0600)

Encode password for auto-login:

encoded := EncodeKcpassword("secretpassword")
os.WriteFile("/Volumes/Data/private/etc/kcpassword", encoded, 0600)

postinstall.go — Run vzscripts after VM installation.

provision.go - Auto-provisioning infrastructure for macOS VMs

Overview

This file implements automatic user provisioning for macOS virtual machines. The goal is to achieve fully non-interactive provisioning:

Boot → User created → Auto-login → Desktop ready

Architecture

Provisioning works by injecting files directly into the VM's disk image before first boot. This approach is more reliable than GUI automation because it operates at the filesystem level and doesn't depend on screen detection.

The injection process mounts the VM's APFS "Data" volume and writes:

  1. LaunchDaemon plist (/Library/LaunchDaemons/com.github.tmc.vz-macos.provision.plist)
  2. Provisioning script (/var/db/vz-provision.sh)
  3. Setup Assistant bypass marker (/var/db/.AppleSetupDone)
  4. Auto-login credentials (/etc/kcpassword + loginwindow.plist)

Boot Sequence

When the VM boots, macOS processes these files in order:

Kernel → launchd → LaunchDaemons → WindowServer → loginwindow → Desktop
                       ↑                              ↑
               provision script runs          checks .AppleSetupDone
               creates user account           skips Setup Assistant

LaunchDaemon Provisioning

The LaunchDaemon (com.github.tmc.vz-macos.provision.plist) is configured with RunAtLoad=true, which causes launchd to execute the provisioning script immediately at boot. The script uses sysadminctl to create the user account with proper credentials.

CRITICAL: LaunchDaemon plists must be owned by root:wheel (uid=0, gid=0). macOS launchd silently ignores daemons with incorrect ownership as a security measure. The provision command handles this by writing files as the current user, then running a single targeted "sudo chown root:wheel" on just the files that need it — minimizing the scope of elevated privileges.

APFS Volume Handling

macOS VMs use APFS with multiple volumes. The user data lives on the "Data" volume, which is separate from the system volume. When mounting disk images:

  1. hdiutil attach creates device nodes (e.g., /dev/disk27)
  2. APFS containers synthesize additional devices (e.g., /dev/disk30)
  3. The Data volume may mount at /Volumes/Data, /Volumes/Data 1, etc.
  4. diskutil info must be used to find the actual mount point

IMPORTANT: APFS volumes from disk images have ownership disabled by default. The provision command calls "diskutil enableOwnership" before writing files, otherwise chown operations silently do nothing.

Setup Assistant Bypass

The .AppleSetupDone marker file tells loginwindow to skip Setup Assistant. This file is checked at:

/var/db/.AppleSetupDone (guest path)
/Volumes/Data/private/var/db/.AppleSetupDone (host path when mounted)

Additional bypass mechanisms:

  • .skipbuddy in /Library/User Template/English.lproj/ suppresses first-login dialogs
  • .SetupRegComplete in /Library/Receipts/ (older macOS versions)

Auto-Login Configuration

Auto-login requires two files working together:

  1. kcpassword (/etc/kcpassword): XOR-encoded password using a fixed 11-byte key
  2. loginwindow.plist: Contains autoLoginUser key with the username

The kcpassword encoding is intentionally weak (security through obscurity) because auto-login inherently requires storing credentials. See password.go for the encoding implementation.

Usage

Complete provisioning workflow:

# Install macOS to create VM
./cove install -ipsw restore.ipsw

# Provision the VM (will prompt for sudo to fix file ownership)
./cove provision -user testuser -password secret123 -skip-setup-assistant

# Verify provisioning succeeded
./cove verify

# Run VM - should boot directly to desktop
./cove run -gui

Troubleshooting

Common issues and solutions:

WRONG_OWNER verification error:
  → Re-run provision (will prompt for sudo to fix ownership)
  → Ensure no other Data volumes are mounted

User not created on boot:
  → Check /var/log/vz-provision.log inside VM
  → Verify LaunchDaemon has root:wheel ownership

Setup Assistant still appears:
  → Ensure -skip-setup-assistant flag was used
  → Check .AppleSetupDone exists with correct path

Auto-login not working:
  → May be disabled by FileVault
  → Check kcpassword encoding matches password

File Path Mapping

When the VM disk is mounted on the host, paths map as follows:

Guest Path              → Host Path (on Data volume)
/var/db/                → /Volumes/Data/private/var/db/
/Library/               → /Volumes/Data/Library/
/Users/                 → /Volumes/Data/Users/
/etc/                   → /Volumes/Data/private/etc/

File Organization

The provisioning subsystem is split across multiple files:

provision.go             - Types, staging utilities, orchestration
provision_cli.go         - CLI handler, InjectOptions, password prompts
provision_mount.go       - APFS disk mount, partition discovery, ownership
provision_inject.go      - File staging: LaunchDaemon, auto-login, user plist, SSH keys
provision_verify.go      - File verification command
provision_automation.go  - Setup Assistant keyboard automation

provision_inject.go - File staging and injection logic for VM provisioning.

This file consolidates the various provisioning file operations:

  • LaunchDaemon plist and provisioning script generation
  • Auto-login configuration (kcpassword + loginwindow.plist)
  • User plist creation and admin group management
  • SSH key injection

Each operation has two variants: an "inject" function that writes directly to a mounted disk, and a "stage" function that writes to a staging directory for later application via applyStagedFiles.

rosetta.go - Rosetta 2 support for Linux VMs (x86-64 binary translation on ARM64)

screen_detection.go - Detect current macOS UI state from screenshots

screen_detection_ocr.go - OCR-based screen state detection

screenshots.go - Screenshot capture, diff, and compression for VM control.

Generic image utilities delegate to github.com/tmc/apple/x/vzkit/capture. ControlServer methods remain here since they depend on VM-specific state.

setup_assistant.go - Automate macOS Setup Assistant via OCR-driven navigation

snapshots.go - VM state save/restore (snapshots) support

Two types of snapshots are supported:

  1. VM State Snapshots: Save/restore CPU and memory state (fast resume) - Delegates to vzkit.SnapshotManager - Stored in vmDir/snapshots/<name>.vmstate - VM must be paused to save, stopped to restore

  2. Disk Snapshots: Save/restore disk image state (clone the disk) - Uses APFS clonefile for instant copy-on-write snapshots - Stored in vmDir/disk-snapshots/<name>/

template.go - VM template save/create functionality

Templates allow saving a configured VM as a reusable base for creating new VMs. Two storage modes are supported:

  • Compressed (default): disk.img is gzip compressed for space efficiency
  • Fast (clonefile): disk.img is stored uncompressed, enabling instant CoW creation

The fast mode uses APFS clonefile which creates a copy-on-write clone that initially takes no additional disk space and only grows as blocks differ.

unattended.go - Unattended macOS install orchestrator

up.go — Single command to install, provision, and optionally run vzscripts.

up_setup_script.go — Run a plain text setup script via the guest agent.

usb.go - USB mass storage device support

UTM bundle support for cove. Allows running existing UTM Apple VMs without modification.

UTM bundle structure (.utm directory):

MyVM.utm/
├── config.plist           # PropertyList XML config
├── screenshot.png         # Optional thumbnail
└── Data/                  # All VM data files
    ├── disk.img           # Main disk image
    ├── AuxiliaryStorage   # macOS NVRAM
    └── vm.vzsave          # Saved VM state (macOS 14+)

vm_ops.go - VM management operations (delete, rename, export, import)

vm_registry.go - VM discovery and management

vsock.go - Host-side vsock infrastructure for guest agent communication.

Uses VZVirtioSocketDevice from Apple's Virtualization framework to establish bidirectional socket connections with the guest agent over vsock.

vzscript.go - Script engine for VM provisioning.

Extends rsc.io/script with commands for interacting with a guest VM via the control socket and guest agent, plus OCR-driven GUI automation. Scripts are standard txtar archives executed by rsc.io/script.

Guest commands:

guest-wait [timeout]        Wait for VM boot and agent connectivity
guest-ping                  Check agent connectivity
guest-exec <args...>        Run a command in the guest
guest-shell <file>          Run a local script file in the guest via bash
guest-terminal <file>       Run a local script file in Terminal.app (visible to user)
guest-write <dst> <src>     Copy a local file to the guest
guest-read <path>           Read a guest file to stdout
guest-cp <host> <guest>     Copy a file or directory host→guest (streaming)
host-cp <host> <guest>      Copy a host file/directory to guest (long timeout)
append-path <dir>           Add a directory to system PATH via /etc/paths.d/

UI automation commands (via control socket):

ocr-click <text> [timeout] [region]  Find text via OCR and click it
ocr-wait <text> [timeout] [region]   Wait until text appears
ocr-gone <text> [timeout] [region]   Wait until text disappears
ocr                         Run OCR on current screen; stdout is all text
screenshot [file]           Capture VM screen to file
type <text>                 Type text into the VM
type-keycodes <text>        Type text using per-key keycode events
key <spec>                  Send key event (e.g. "return", "tab", "cmd+v")
click <x> <y>              Click at normalized coordinates (0-1)
wait-prompt-clear <text> [timeout]  Wait until a prompt clears or progresses
detect-page                 Detect Setup Assistant page via OCR
detect-screen               Detect screen state (desktop/login/setup)

Conditions:

[screen:desktop]                    True if current screen matches state
[page:language]                     True if current SA page matches name
[text-visible:Continue]             True if text is visible on screen
[text-visible:Authorized+user]      Space and punctuation use URL encoding
[text-visible:%5By%2Fn%5D]          Encoded form of "[y/n]"

Example:

# Phase 1: OCR-driven GUI automation
ocr-wait Continue 120s
ocr-click Continue
wait 2s

# Phase 2: Guest agent commands
guest-wait 3m
guest-ping
guest-shell install.sh

-- install.sh --
#!/bin/bash
xcode-select --install

vzscript_apply.go - CLI for running vzscript recipes.

Source Files

Directories

Path Synopsis
cmd
vz-agent command
vz-agent is a guest agent daemon for VMs managed by cove.
vz-agent is a guest agent daemon for VMs managed by cove.
internal
assets
Package assets embeds static resources for cove.
Package assets embeds static resources for cove.
autosign
Package autosign checks for required entitlements at startup and ad-hoc signs the binary if they are missing, then re-execs.
Package autosign checks for required entitlements at startup and ad-hoc signs the binary if they are missing, then re-execs.
diskimages2
Package diskimages2 provides convenience wrappers around the generated DiskImages2 framework bindings.
Package diskimages2 provides convenience wrappers around the generated DiskImages2 framework bindings.
Package proto contains protocol buffer definitions for cove.
Package proto contains protocol buffer definitions for cove.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL