tart

package
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Aug 28, 2026 License: Apache-2.0 Imports: 30 Imported by: 0

Documentation

Index

Constants

View Source
const (
	NixVolumeFileName = "nix.img"
	NixVolumeSizeGB   = 30
	NixVolumeLabel    = "DevcellNix"
)
View Source
const ProvisionedMarkerPath = "/private/var/devcell-provisioned"

ProvisionedMarkerPath is on the boot disk's writable Data volume. /private/var persists across tart clone. We use /private/var (not /var) to avoid any symlink indirection during early boot.

Variables

This section is empty.

Functions

func ApplyDiskPatch

func ApplyDiskPatch(diskPath string, cfg InitConfig, pubKey string, obs Observer) error

ApplyDiskPatch is not available on non-darwin platforms.

func ArtifactDir

func ArtifactDir(home, cellName string) string

ArtifactDir returns the path to VM artifacts for a given cell. Deprecated: use TemplateDir for per-template paths or CellHome for per-cell paths.

func BuildExecCommand

func BuildExecCommand(spec ExecSpec) string

BuildExecCommand constructs a shell command string for `tart exec <vm> bash -l -c <cmd>`. Sources the nix daemon profile, cds into the project dir, sets env vars, and runs the binary.

When RunAsUser is set, the entire command is wrapped with `sudo -u <user> -i bash -l -c '...'` so the session runs as the specified user (matching Docker's HOST_USER model).

func BuildPreflight

func BuildPreflight(baseDisk string) error

BuildPreflight checks whether a build can proceed.

func BuildProvisionCommands

func BuildProvisionCommands(stack string, modules []string) []string

BuildProvisionCommands returns the SSH commands to provision a build VM.

func BuildSSHArgv

func BuildSSHArgv(spec Spec, host string) []string

BuildSSHArgv constructs the SSH argv for running a command inside a macOS VM:

ssh -p <port> -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null
    <user>@<host> -t bash -l -c '<nix source; cd ~/project; env KEY=VAL binary flags>'

The remote command is wrapped in `bash -l -c "..."` so that the login shell sources profiles and puts home-manager-installed binaries on PATH.

When ProjectDir is set, the command cds into ~/basename(ProjectDir) first, mirroring Docker's --workdir behaviour.

It is a pure function: no I/O, no exec.

func CellHome

func CellHome(home, cellName string) string

CellHome returns the per-cell persistent home directory. Layout: ~/.devcell/<cellName>/ Same path on both Linux (bind-mount → /home/<user>) and macOS (VirtioFS → /Users/<user>).

func CollectSSHPubKeys

func CollectSSHPubKeys(sshDir string) string

CollectSSHPubKeys reads all *.pub files from sshDir and returns their contents concatenated (one key per line). Returns "" if the directory doesn't exist or contains no .pub files.

func DeterministicMAC

func DeterministicMAC(cellName string) string

DeterministicMAC derives a stable locally-administered MAC address from a cell name. Same cell → same MAC → same DHCP lease, avoiding lease accumulation across VM restarts and reinits.

func DownloadIPSW

func DownloadIPSW(ctx context.Context, path string, download DownloadFunc) error

DownloadIPSW downloads a restore image with completion tracking and retry. A ".done" marker file signals that a previous download completed successfully; if present, the download is skipped. Partial files are left on disk for the DownloadFunc to resume via Range headers. On failure, retries up to 3 times with backoff.

func DownloadIPSWObserved

func DownloadIPSWObserved(ctx context.Context, path string, download DownloadFunc, obs Observer) error

DownloadIPSWObserved is like DownloadIPSW but reports progress to obs.

func EncodeKcpassword

func EncodeKcpassword(password string) []byte

EncodeKcpassword XOR-encodes a password using Apple's kcpassword scheme. The password is null-padded to a multiple of 12 bytes, then each byte is XORed with the corresponding byte of the 13-byte repeating key.

func EnsureHomeDir

func EnsureHomeDir(home, cellName string) (string, error)

EnsureHomeDir creates the CellHome directory if it doesn't exist. Returns the path to the (existing or newly created) directory. Replaces EnsureHomeVolume — home is now a VirtioFS directory mount, not a disk image.

func EnsureNixVolume

func EnsureNixVolume(home string) (string, error)

EnsureNixVolume creates a sparse nix store disk image if it doesn't exist. Returns the path to the (existing or newly created) image.

func ExecuteBootCommands

func ExecuteBootCommands(client *VNCClient, commands []BootDirective) error

ExecuteBootCommands runs a sequence of boot directives against a VNC server. Returns an error annotated with the directive index if sending fails.

func ExecuteBootCommandsObserved

func ExecuteBootCommandsObserved(client *VNCClient, commands []BootDirective, obs Observer) error

ExecuteBootCommandsObserved is like ExecuteBootCommands but reports progress to obs, including which directive is currently executing.

func ExecuteBootCommandsWithScreenshots

func ExecuteBootCommandsWithScreenshots(client *VNCClient, commands []BootDirective, obs Observer, screenshotBefore func(idx int, label string)) error

ExecuteBootCommandsWithScreenshots runs boot directives with an optional pre-action screenshot callback. If screenshotBefore is non-nil, it is called before each KeyDirective, KeyDownDirective, and TypeDirective with the directive index and a short label describing the action.

func FindLeaseByMAC

func FindLeaseByMAC(leases []DHCPLease, mac string) (string, bool)

FindLeaseByMAC returns the IP address for the given MAC address. Comparison is case-insensitive.

func FindTextOnScreen

func FindTextOnScreen(_ *image.RGBA, _ string) (image.Rectangle, bool)

FindTextOnScreen is a stub for non-Darwin platforms. OCR-based screen detection requires Apple Vision framework (macOS only).

func GenerateCheckProvisionedScript

func GenerateCheckProvisionedScript() string

GenerateCheckProvisionedScript returns a script that checks for the provisioned marker. Exits 0 if present, 1 if not.

func GenerateCreateSessionUserScript

func GenerateCreateSessionUserScript(username string) string

GenerateCreateSessionUserScript returns a script that creates a macOS user matching the host's $USER, mirroring Docker's HOST_USER entrypoint model. The user gets admin group membership (for sudo) and a home directory. Idempotent: skips if the user already exists.

func GenerateGrantSSHdFDAScript

func GenerateGrantSSHdFDAScript() string

GenerateGrantSSHdFDAScript returns a boot-time script that grants Full Disk Access to sshd in the macOS TCC database. Must run as a LaunchDaemon under launchd (not over SSH) — launchd has the TCC context to modify the database. After running, SSH sessions can call rename() on /etc/ files (required by the Nix installer).

Uses logger(1) to write structured status to the unified log — the serial console daemon's "composedMessage CONTAINS devcell" predicate picks these up, so the host sees progress via streamSerial() without SSH.

func GenerateHomeMountScript

func GenerateHomeMountScript(cellName, username string) string

GenerateHomeMountScript returns a guest-side script that mounts the CellHome VirtioFS share at /Users/<username>. The host passes --dir home:<cellHome> to tart run.

func GenerateNixDarwinActivateScript

func GenerateNixDarwinActivateScript(stack, flakeDir string) string

GenerateNixDarwinActivateScript returns the nix-darwin activation command. nix-darwin manages system-level config (LaunchDaemons, /etc/) and user packages — a superset of home-manager. The flakeDir must contain a flake with darwinConfigurations.<stack>.

func GenerateNixDiskPrepScript

func GenerateNixDiskPrepScript(cellName string) string

GenerateNixDiskPrepScript returns a script that reformats the external VirtIO disk as JHFS+ with the "DevcellNix" label BEFORE the Nix installer runs. This prevents a label collision: the Determinate installer creates an APFS volume named "Nix Store" and then encrypts it by name. If a pre-existing HFS+ volume with the same label exists (from a previous build), diskutil finds the wrong volume and the encrypt step fails.

func GenerateNixInstallScript

func GenerateNixInstallScript() string

GenerateNixInstallScript returns a script that installs Nix using the official multi-user installer. The installer creates an APFS volume named "Nix Store" on the boot disk — GenerateNixStoreSwapScript runs afterward to migrate the nix store to our external JHFS+ disk.

We use the official installer (not Determinate) because nix-darwin's activation checks for /usr/local/bin/determinate-nixd and aborts if found — the two daemon management layers conflict.

func GenerateNixStoreSwapScript

func GenerateNixStoreSwapScript(cellName string) string

GenerateNixStoreSwapScript returns a script that migrates the nix store from the Determinate installer's APFS volume to our external JHFS+ disk. Assumes GenerateNixDiskPrepScript already formatted the disk as JHFS+ "DevcellNix" and unmounted it. This script runs after "Install Nix":

  1. Mounts the pre-formatted "DevcellNix" volume
  2. Copies the initial nix store (small — daemon + profiles only)
  3. Swaps the /nix mount from installer's APFS to our JHFS+
  4. Installs a boot-time LaunchDaemon for clone VMs
  5. Restarts the nix daemon on the new mount

func GenerateNixVolumeMountScript

func GenerateNixVolumeMountScript(cellName string) string

GenerateNixVolumeMountScript returns a guest-side script that: 1. Finds the external VirtIO disk (non-boot, matching expected size) 2. Formats it as JHFS+ on first use, writes .devcell.json metadata 3. Mounts the JHFS+ volume 4. Creates the /nix firmlink via synthetic.conf and mounts the volume there

On subsequent runs, the disk is already formatted — it just mounts.

func GeneratePreflightDiagScript

func GeneratePreflightDiagScript() string

GeneratePreflightDiagScript returns a diagnostic script that checks the VM environment before Nix installation. Output goes to the debug log via io.MultiWriter so failures are diagnosable without re-running.

func GenerateProjectMountScript

func GenerateProjectMountScript(tag, username, projectBasename string) string

GenerateProjectMountScript returns a script to mount the project VirtioFS share into the user's home directory, mirroring Docker's bind-mount behavior.

func GenerateProvisionedMarkerScript

func GenerateProvisionedMarkerScript() string

GenerateProvisionedMarkerScript returns a script that stamps a marker file indicating provisioning completed successfully. Written to /private/var (boot disk) rather than ~ because VirtioFS may shadow the home directory at runtime.

func GenerateSSHEnablementScript

func GenerateSSHEnablementScript() string

GenerateSSHEnablementScript returns a shell script to enable SSH on macOS.

func GenerateSSHKeyPair

func GenerateSSHKeyPair(dir string) (string, error)

GenerateSSHKeyPair creates an ed25519 key pair and writes them to the artifact dir. Returns the public key in OpenSSH authorized_keys format.

func GenerateSSHKeyScript

func GenerateSSHKeyScript(pubKey string) string

GenerateSSHKeyScript returns a script to inject an SSH public key.

func GenerateSetupSessionHomeScript

func GenerateSetupSessionHomeScript(username string) string

GenerateSetupSessionHomeScript returns a script that symlinks the CellHome VirtioFS share into the session user's home directory, mirroring how the build-time "Mount home volume" step sets up /Users/admin.

func GenerateSudoersScript

func GenerateSudoersScript(username string) string

GenerateSudoersScript returns a script to enable passwordless sudo.

func GenerateUserPlist

func GenerateUserPlist(username, password string, uid, gid int) ([]byte, error)

GenerateUserPlist builds a macOS dslocal user record as a binary plist. The record mirrors what Directory Services expects in /var/db/dslocal/nodes/Default/users/<username>.plist.

func GenerateVerifySSHdFDAScript

func GenerateVerifySSHdFDAScript() string

GenerateVerifySSHdFDAScript returns a script that checks whether sshd has Full Disk Access in the TCC database. Run over SSH before Nix installation to confirm the boot-time grant succeeded. Exits 0 if granted, 1 if not.

func GenerateVirtioFSMountScript

func GenerateVirtioFSMountScript(tag, mountPoint string) string

GenerateVirtioFSMountScript returns a script to mount a VirtioFS share.

Tart 2.30+ bundles --dir shares under Apple's automount VirtioFS device (tag "com.apple.virtio-fs.automount") instead of creating individual VirtioFS devices per share. macOS automounts these at /Volumes/My Shared Files/<tag>/. The script tries mount_virtiofs first (individual device), then falls back to symlinking from the automount path.

func IPSWCacheDir

func IPSWCacheDir(home string) string

IPSWCacheDir returns the shared IPSW cache directory (not per-cell).

func IPSWCachePath

func IPSWCachePath(home string) string

IPSWCachePath returns the path to the cached restore.ipsw file.

func ImageName

func ImageName(stack string, modules []string) string

ImageName returns the disk image filename for a stack + optional modules.

func InstanceVMName

func InstanceVMName(cellName string) string

InstanceVMName returns the tart VM name for a per-cell running instance.

func NixVolumePath

func NixVolumePath(home string) string

NixVolumePath returns the path to the global nix store disk image. Layout: ~/.devcell/darwin/nix-store.img Shared across all cells — mirrors Docker's single devcell-nix-store volume.

func ParseTartOutput

func ParseTartOutput(r io.Reader, fn func(TartProgress)) error

ParseTartOutput reads lines from r and calls fn for each parsed progress event.

func PreflightCheck

func PreflightCheck(goos, goarch string) error

PreflightCheck validates the host can run macOS VMs. Returns nil if all checks pass, or an error describing what's wrong. Pure function (takes OS/arch as params for testability).

func PreflightCheckHost

func PreflightCheckHost() error

PreflightCheckHost calls PreflightCheck with runtime values.

func PrepareArtifactDir

func PrepareArtifactDir(dir string) error

PrepareArtifactDir creates the artifact directory if it doesn't exist.

func ProbeSSH

func ProbeSSH(host string, port uint16) error

ProbeSSH does a single TCP dial to check if SSH is accepting connections.

func ProvisionCommands

func ProvisionCommands(cfg InitConfig, pubKey string) []string

ProvisionCommands returns the SSH commands to run after first boot.

func ProvisionSSHCommands

func ProvisionSSHCommands(stack string, modules []string) []string

ProvisionSSHCommands returns the SSH commands to provision a VM with a stack. Commands are run in order via SSH.

func RunSSHCommand

func RunSSHCommand(host string, port uint16, user, keyPath, command string, stdout, stderr io.Writer) error

RunSSHCommand executes a command on the VM over SSH using key-based auth.

func RunSSHCommandPassword

func RunSSHCommandPassword(host string, port uint16, user, password, command string, stdout, stderr io.Writer) error

RunSSHCommandPassword executes a command on the VM over SSH using password auth. Tries both keyboard-interactive (macOS default via PAM) and plain password.

func ScreenBrightness

func ScreenBrightness(img *image.RGBA) float64

ScreenBrightness returns the mean brightness (0.0–1.0) of an RGBA image. 0.0 = fully black, 1.0 = fully white.

func ScreenshotDir

func ScreenshotDir(projectDir string) string

ScreenshotDir returns the directory for debug VNC screenshots.

func SparseCopy

func SparseCopy(src, dst string) error

SparseCopy copies src to dst, preserving sparseness where the OS supports it.

func SparseCopyWithProgress

func SparseCopyWithProgress(src, dst string, fn func(CopyProgress)) error

SparseCopyWithProgress copies src to dst with progress reporting. The callback fn is called after each 1MB chunk is written.

func StackTag

func StackTag(stack string, modules []string) string

StackTag returns the canonical tag for a stack + optional modules. Shared naming logic between Docker images and tart VMs. Examples: "ultimate", "dev-linear-plex-a1b2c3d4".

func TartClone

func TartClone(ctx context.Context, ref, localName string) error

TartClone runs `tart clone <ref> <localName>`. Parallel to Docker: exec.Command("docker", "pull", tag).

func TartCloneWithProgress

func TartCloneWithProgress(ctx context.Context, ref, localName string, fn func(TartProgress), errOut io.Writer) error

TartCloneWithProgress runs `tart clone` with CI=1 to get parseable progress, calling fn for each progress event. Remaining stderr is written to errOut.

func TartCreate

func TartCreate(ctx context.Context, name, ipswPath string, diskSizeGB int) error

TartCreate creates a VM from an IPSW file via `tart create`.

func TartDelete

func TartDelete(ctx context.Context, name string) error

TartDelete runs `tart delete <name>`. Parallel to Docker: exec.Command("docker", "rm", name).

func TartExec

func TartExec(ctx context.Context, name string, command []string, stdout, stderr *os.File) error

TartExec runs a command inside a running VM via `tart exec`.

func TartExecInteractive

func TartExecInteractive(ctx context.Context, name string, command []string) error

TartExecInteractive runs an interactive PTY session via `tart exec -t -i`.

func TartIP

func TartIP(ctx context.Context, name string) (string, error)

TartIP runs `tart ip <name>` and returns the guest IP.

func TartPreflight

func TartPreflight() (string, error)

TartPreflight verifies the tart binary is installed and returns its version. Parallel to Docker: exec.LookPath("docker") + `docker version`.

func TartSet

func TartSet(ctx context.Context, name string, cpus uint, memoryMB uint64) error

TartSet configures VM resources via `tart set`.

func TartStop

func TartStop(ctx context.Context, name string) error

TartStop runs `tart stop <name>`.

func TartVersion

func TartVersion(ctx context.Context) string

TartVersion returns the tart CLI version string.

func TemplateDir

func TemplateDir(home, stack string, modules []string) string

TemplateDir returns the path to per-template VM artifacts. Layout: ~/.devcell/darwin/<stackTag>/

func TemplateVMName

func TemplateVMName(stack string, modules []string) string

TemplateVMName returns the tart VM name for a built template image.

func UnmountDiskImage

func UnmountDiskImage(mount *DiskMount) error

UnmountDiskImage is not available on non-darwin platforms.

func WaitForGuestIP

func WaitForGuestIP(mac string, timeout, interval time.Duration, stateFunc ...VMStateFunc) (string, error)

WaitForGuestIP is not available on non-darwin platforms.

func WaitForSSH

func WaitForSSH(host string, port uint16, timeout, interval time.Duration) error

WaitForSSH polls a TCP connection to host:port until it succeeds or the timeout elapses. Returns nil on success, or an error describing the failure. Pure network check — no authentication, just verifies the port is accepting.

func WaitForSSHObserved

func WaitForSSHObserved(host string, port uint16, timeout, interval time.Duration, obs Observer, stateFunc ...VMStateFunc) error

WaitForSSHObserved is like WaitForSSH but reports each poll attempt to obs. An optional VMStateFunc checks VM liveness each iteration — if the VM enters "stopped" or "error" state, polling aborts immediately instead of waiting the full timeout.

Types

type AcquireDecision

type AcquireDecision int

AcquireDecision describes what AcquireDarwinVM will do.

const (
	DecisionExternal       AcquireDecision = iota // external VM — just wait for SSH
	DecisionAlreadyRunning                        // managed VM already accepting connections
	DecisionStartVM                               // need to start managed VM
	DecisionCloneTemplate                         // clone from template, then start
)

func DecideAcquire

func DecideAcquire(external bool, alreadyRunning bool) AcquireDecision

DecideAcquire is the pure-function decision: what should AcquireDarwinVM do?

func DecideAcquireEx

func DecideAcquireEx(external, alreadyRunning, hasTemplate bool) AcquireDecision

DecideAcquireEx extends DecideAcquire with template awareness.

func (AcquireDecision) String

func (d AcquireDecision) String() string

type AcquireInputs

type AcquireInputs struct {
	VMName       string            // tart VM name (instance, e.g. "DIMM-tart")
	TemplateName string            // template VM to clone from if instance doesn't exist
	SharedDirs   map[string]string // tag -> host path (VirtioFS via --dir)
	Disks        []string          // raw disk image paths (VirtIO block devices via --disk)
	SSHHost      string
	SSHPort      uint16
	ExternalVM   bool // user explicitly configured SSH target — skip lifecycle
	SSHTimeout   time.Duration
	InitFunc     func() error // called to auto-init when VM doesn't exist; nil = error instead
}

AcquireInputs are the parameters for AcquireDarwinVM.

func (*AcquireInputs) ApplyDefaults

func (a *AcquireInputs) ApplyDefaults()

ApplyDefaults fills zero values.

func (*AcquireInputs) Validate

func (a *AcquireInputs) Validate() error

Validate checks required fields.

type AcquireResult

type AcquireResult struct {
	VM      *VM    // non-nil only if we started a managed VM (caller must shut it down)
	SSHHost string // resolved SSH host (guest IP or external)
	SSHPort uint16 // resolved SSH port
	Managed bool   // true if we started the VM (vs. external/already-running)
}

AcquireResult holds the outcome of AcquireDarwinVM.

func AcquireDarwinVM

func AcquireDarwinVM(_ context.Context, _ AcquireInputs) (*AcquireResult, error)

AcquireDarwinVM is not available on this platform.

type ArtifactPaths

type ArtifactPaths struct {
	Dir           string // parent directory
	Disk          string // disk.img
	AuxStorage    string // aux-storage.img
	HWModel       string // hardware-model.json
	MachineID     string // machine-id.json
	SSHPrivateKey string // id_ed25519 (per-cell generated key)
	SSHPublicKey  string // id_ed25519.pub
}

ArtifactPaths holds the paths to all VM artifact files. Deprecated: use TemplatePaths + CellSSHPaths instead.

func LoadArtifacts

func LoadArtifacts(home, cellName string) (ArtifactPaths, error)

LoadArtifacts loads artifact paths and validates they exist. Returns an error with a helpful message if any are missing.

func NewArtifactPaths

func NewArtifactPaths(home, cellName string) ArtifactPaths

NewArtifactPaths returns paths for a cell's Darwin VM artifacts. Deprecated: use NewTemplatePaths + NewCellSSHPaths instead.

func (ArtifactPaths) Exists

func (a ArtifactPaths) Exists() bool

Exists checks if all required artifact files exist.

func (ArtifactPaths) MissingFiles

func (a ArtifactPaths) MissingFiles() []string

MissingFiles returns a list of artifact files that don't exist on disk.

type BootDirective

type BootDirective interface {
	// contains filtered or unexported methods
}

BootDirective represents one step in the boot command sequence.

func GenerateBootCommands

func GenerateBootCommands(username, password string) []BootDirective

GenerateBootCommands returns the boot command sequence for macOS Setup Assistant automation. Uses OCR-based screen detection for timing and the Tart-proven keystroke sequence for navigation.

func ParseBootToken

func ParseBootToken(token string) (BootDirective, error)

ParseBootToken parses a single boot command token.

type BuildConfig

type BuildConfig struct {
	CellName string
	HomeDir  string
	Stack    string
	Modules  []string
	CPUs     uint
	MemoryGB uint64
	SSHPort  uint16
	Username string
}

BuildConfig holds the parameters for building a macOS VM image.

func (*BuildConfig) ApplyDefaults

func (c *BuildConfig) ApplyDefaults()

ApplyDefaults fills zero-value fields.

func (*BuildConfig) ArtifactDir

func (c *BuildConfig) ArtifactDir() string

ArtifactDir returns the VM artifacts path.

func (*BuildConfig) BaseImagePath

func (c *BuildConfig) BaseImagePath() string

BaseImagePath returns the path to the base disk image (from init).

func (*BuildConfig) BuildImagePath

func (c *BuildConfig) BuildImagePath() string

BuildImagePath returns the path for the ephemeral build image.

func (*BuildConfig) FinalImagePath

func (c *BuildConfig) FinalImagePath() string

FinalImagePath returns the path for the final built image.

func (*BuildConfig) Validate

func (c *BuildConfig) Validate() error

Validate checks required fields.

type CellSSHPaths

type CellSSHPaths struct {
	Dir        string // ~/.devcell/<cellName>/.ssh/
	PrivateKey string // id_ed25519
	PublicKey  string // id_ed25519.pub
}

CellSSHPaths holds paths to per-cell SSH keys. Layout: ~/.devcell/<cellName>/.ssh/

func NewCellSSHPaths

func NewCellSSHPaths(home, cellName string) CellSSHPaths

NewCellSSHPaths returns paths for a cell's SSH keys.

type ClickTextDirective

type ClickTextDirective struct {
	Text    string
	Timeout time.Duration
}

ClickTextDirective finds text on screen via OCR and clicks its center. Polls until the text appears or Timeout expires.

type CopyProgress

type CopyProgress struct {
	BytesCopied int64
	TotalBytes  int64
}

CopyProgress reports bytes copied vs total during a sparse copy.

func (CopyProgress) Percent

func (p CopyProgress) Percent() int

func (CopyProgress) String

func (p CopyProgress) String() string

type DHCPLease

type DHCPLease struct {
	Name      string
	IPAddress string
	HWAddress string // MAC address without type prefix (e.g. "aa:bb:cc:dd:ee:ff")
}

DHCPLease represents a single entry from macOS /var/db/dhcpd_leases.

func ParseDHCPLeases

func ParseDHCPLeases(content string) []DHCPLease

ParseDHCPLeases parses the macOS DHCP lease file format. Each entry is enclosed in { } and contains key=value pairs. hw_address may have a type prefix (e.g. "1,aa:bb:cc:dd:ee:ff") which is stripped.

type DiskMount

type DiskMount struct {
	DeviceNode string
	MountPoint string
}

DiskMount holds the result of mounting a disk image.

func MountDiskImage

func MountDiskImage(diskPath string) (*DiskMount, error)

MountDiskImage is not available on non-darwin platforms.

type DownloadFunc

type DownloadFunc func(ctx context.Context, path string) error

DownloadFunc fetches a macOS restore image to the given path. The implementation may resume a partial download if the file already exists (e.g., vz uses HTTP Range headers based on existing file size).

type ExecSpec

type ExecSpec struct {
	Binary     string   // binary to run (e.g. "zsh", "claude")
	Flags      []string // default flags for the binary
	UserArgs   []string // user-provided args
	EnvVars    []string // KEY=VAL pairs to set in the environment
	ProjectDir string   // host project path — basename is used for cd ~/basename
	RunAsUser  string   // if set, wrap command with sudo -u <user> -i
}

ExecSpec describes a command to run inside a tart VM via `tart exec`.

type InitConfig

type InitConfig struct {
	CellName string
	HomeDir  string
	Stack    string
	Username string
	Password string
	CPUs     uint
	MemoryGB uint64
	DiskGB   uint64
	SSHPort  uint16
}

InitConfig holds the parameters for a full VM initialization.

func (*InitConfig) ApplyDefaults

func (c *InitConfig) ApplyDefaults()

ApplyDefaults fills zero-value fields with sensible defaults.

func (*InitConfig) ArtifactDir

func (c *InitConfig) ArtifactDir() string

ArtifactDir returns the path where VM artifacts will be stored.

func (*InitConfig) DiskSizeBytes

func (c *InitConfig) DiskSizeBytes() int64

DiskSizeBytes returns the disk image size in bytes.

func (*InitConfig) Validate

func (c *InitConfig) Validate() error

Validate checks that all required fields are set.

type InitPhase

type InitPhase int

InitPhase names the stages of VM initialization.

const (
	PhasePreflight InitPhase = iota
	PhaseDownloadIPSW
	PhaseInstallMacOS
	PhaseFirstBoot
	PhaseEnableSSH
	PhaseInjectSSHKey
	PhaseInstallNix
	PhaseMountNixhome
	PhaseActivateDarwin
	PhaseShutdown
)

func (InitPhase) String

func (p InitPhase) String() string

String returns a human-readable phase name.

type KeyDirective

type KeyDirective struct {
	Key SpecialKey
}

KeyDirective sends a single special key press (down + up).

type KeyDownDirective

type KeyDownDirective struct {
	Key SpecialKey
}

KeyDownDirective sends a key-down event without a corresponding key-up. Used for modifier key holds (Shift+Tab, Alt+Space, etc.).

type KeyUpDirective

type KeyUpDirective struct {
	Key SpecialKey
}

KeyUpDirective sends a key-up event. Pairs with a prior KeyDownDirective.

type LaunchAction

type LaunchAction int

LaunchAction is one VM-acquisition step.

const (
	ActionUseLocal LaunchAction = iota // disk image exists locally
	ActionBuild                        // build from IPSW (full init + provision)
	ActionDryRun                       // dry-run mode, no VM work
	ActionPullTart                     // pull pre-built image from Tart OCI registry
)

func DecideLaunchActions

func DecideLaunchActions(in LaunchInputs) []LaunchAction

DecideLaunchActions returns the ordered fallback sequence.

DryRun                → [DryRun]
ExplicitBuild         → [Build]
DiskExists            → [UseLocal]
TartRef (cold start)  → [PullTart]
cold start            → [Build]

type LaunchInputs

type LaunchInputs struct {
	DryRun        bool   // --dry-run set
	ExplicitBuild bool   // --build set, force rebuild
	DiskExists    bool   // disk image exists at expected path
	TartRef       string // OCI image ref for Tart pull (e.g. ghcr.io/cirruslabs/macos-sequoia-base:latest)
}

LaunchInputs are the inputs to DecideLaunchActions.

type NopObserver

type NopObserver struct{}

NopObserver silently discards all events.

func (NopObserver) Logf

func (NopObserver) Logf(string, ...any)

func (NopObserver) Progress

func (NopObserver) Progress(float64, string)

type Observer

type Observer interface {
	// Logf emits a debug-level message.
	Logf(format string, args ...any)
	// Progress reports a step's completion fraction (0.0–1.0) with a message.
	Progress(fraction float64, message string)
}

Observer receives progress events from long-running tart operations. Methods are called synchronously on the caller's goroutine.

type PatchFile

type PatchFile struct {
	Path    string      // absolute path relative to Data volume mount point
	Perms   fs.FileMode // e.g. 0400, 0600, 0644
	Owner   string      // "root:wheel" or "user:staff"
	Content []byte      // file content (nil = empty file / touch)
	MkdirP  bool        // create parent directories if missing
}

PatchFile describes a single file to write during offline disk injection.

func PatchManifest

func PatchManifest(cfg InitConfig, pubKey string) []PatchFile

PatchManifest returns the ordered list of files to write for offline disk injection. All paths are relative to the Data volume mount point.

type PreflightResult

type PreflightResult struct {
	VMExists bool
}

PreflightResult holds the outcome of InitPreflight.

func InitPreflight

func InitPreflight(goos, goarch string, artifactDir string) (PreflightResult, error)

InitPreflight checks whether initialization can proceed. Returns a PreflightResult with VMExists=true if disk.img already exists (callers decide whether to prompt, force-overwrite, or abort). Returns an error only for hard blockers (wrong OS/arch).

type ProvisionStep

type ProvisionStep struct {
	Name          string
	Command       string
	NeedsPassword bool // true = use password auth (before SSH key is injected)
}

ProvisionStep pairs a human-readable phase name with the SSH command to run.

func ProvisionSteps

func ProvisionSteps(cfg InitConfig, pubKey string, offlineProvisioned bool) []ProvisionStep

ProvisionSteps returns the SSH provisioning commands with human-readable names. Steps before key injection use NeedsPassword=true since the generated SSH key isn't in authorized_keys yet.

When offlineProvisioned is true, SSH enablement, key injection, and sudo configuration are skipped — they were written directly to the disk image by ApplyDiskPatch. Only Nix install and home-manager activation remain.

type Spec

type Spec struct {
	VMName        string
	CPUs          uint
	MemoryGB      uint64
	DiskPath      string            // path to disk.img
	AuxPath       string            // path to aux-storage.img
	HWModelPath   string            // path to hardware-model.json
	MachineIDPath string            // path to machine-id.json
	SharedDirs    map[string]string // tag -> host path (VirtioFS)
	SSHPort       uint16            // forwarded port for SSH (default 22)
	SSHUser       string            // guest username (default "devcell")
	MACAddr       string            // reuse a specific MAC (colon-separated); empty = random
	Binary        string            // agent binary (claude, zsh, etc.)
	DefaultFlags  []string
	UserArgs      []string
	EnvVars       []string // KEY=VALUE pairs
	ProjectDir    string   // host project directory
	SSHKeyPath    string   // path to SSH private key (optional; adds -i flag)
}

Spec holds everything needed to configure and connect to a macOS VM.

func (*Spec) ApplyDefaults

func (s *Spec) ApplyDefaults()

ApplyDefaults fills in zero-value fields with sensible defaults.

func (*Spec) Validate

func (s *Spec) Validate() error

Validate returns an error if required fields are missing.

type SpecialKey

type SpecialKey int

SpecialKey is a named key for VNC key events.

const (
	KeySpace SpecialKey = iota
	KeyTab
	KeyReturn
	KeyEscape
	KeyLeftShift
	KeyLeftAlt
	KeyLeftCtrl
	KeyLeft
	KeyRight
	KeyUp
	KeyDown
	KeyF2
	KeyF5
)

func (SpecialKey) VNCKeyCode

func (k SpecialKey) VNCKeyCode() uint32

VNCKeyCode returns the X11 keysym for a SpecialKey.

type TartConfig

type TartConfig struct {
	OS         string      `json:"os"`
	Arch       string      `json:"arch"`
	CPUCount   int         `json:"cpuCount"`
	MemorySize uint64      `json:"memorySize"`
	Display    TartDisplay `json:"display"`
	MACAddress string      `json:"macAddress"`
}

TartConfig represents the VM configuration from a Tart config.json.

func AcquireFromTart

func AcquireFromTart(ctx context.Context, ref, localName string) (TartConfig, string, error)

AcquireFromTart clones a Tart OCI image to local storage and returns the parsed VM config plus the VM directory path (containing disk.img, nvram.bin, config.json). The caller uses ToSpec(vmDir) to build a Spec for booting.

Parallel to the old ExtractTartImage which pulled OCI layers via go-containerregistry. This shells out to `tart clone` — same pattern as Docker's exec.Command("docker", "pull", tag) in runner.go.

func ParseTartConfig

func ParseTartConfig(data []byte) (TartConfig, error)

ParseTartConfig parses a Tart VM config JSON blob.

func (TartConfig) MemoryGB

func (c TartConfig) MemoryGB() uint64

MemoryGB returns memory size in whole gigabytes.

func (TartConfig) ToSpec

func (c TartConfig) ToSpec(artifactDir string) Spec

ToSpec converts a TartConfig to a tart Spec.

type TartDisplay

type TartDisplay struct {
	Width  int `json:"width"`
	Height int `json:"height"`
}

TartDisplay holds display resolution from a Tart config.

type TartGetInfo

type TartGetInfo struct {
	OS         string `json:"OS"`
	CPU        int    `json:"CPU"`
	Memory     uint64 `json:"Memory"`
	Disk       int    `json:"Disk"`
	DiskFormat string `json:"DiskFormat"`
	Size       string `json:"Size"`
	Display    string `json:"Display"`
	Running    bool   `json:"Running"`
	State      string `json:"State"`
}

TartGetInfo is the JSON output of `tart get <name> --format json`. Parallel to Docker: `docker image inspect <tag>`.

func TartGet

func TartGet(ctx context.Context, name string) (TartGetInfo, error)

TartGet runs `tart get <name> --format json` and returns the parsed info. Parallel to Docker: exec.Command("docker", "image", "inspect", tag).

type TartListEntry

type TartListEntry struct {
	Name   string `json:"Name"`
	State  string `json:"State"`
	Source string `json:"Source"`
	Disk   int    `json:"Disk"`
}

TartListEntry represents one VM in `tart list` output.

func TartList

func TartList(ctx context.Context) ([]TartListEntry, error)

TartList returns running VM names by parsing `tart list --format json`.

type TartProgress

type TartProgress struct {
	Percent int
	Message string
	Raw     string
}

TartProgress represents a single progress event parsed from tart CLI output. When CI=1, tart's SimpleConsoleLogger emits one line per update: "pulling manifest...", "0%", "42%", "100%", etc.

func ParseTartProgressLine

func ParseTartProgressLine(line string) (TartProgress, bool)

ParseTartProgressLine parses a single line of tart output. Returns the parsed progress and true, or zero value and false for empty lines.

type TemplatePaths

type TemplatePaths struct {
	Dir        string // parent directory
	Disk       string // disk.img
	AuxStorage string // aux-storage.img
	HWModel    string // hardware-model.json
	MachineID  string // machine-id.json
}

TemplatePaths holds paths to per-template VM platform artifacts. Layout: ~/.devcell/darwin/<stackTag>/

func NewTemplatePaths

func NewTemplatePaths(home, stack string, modules []string) TemplatePaths

NewTemplatePaths returns paths for a template's VM artifacts.

func (TemplatePaths) Exists

func (tp TemplatePaths) Exists() bool

Exists checks if all required template artifact files exist.

func (TemplatePaths) MissingFiles

func (tp TemplatePaths) MissingFiles() []string

MissingFiles returns a list of template artifact files that don't exist on disk.

type TypeDirective

type TypeDirective struct {
	Text string
}

TypeDirective types a string of characters.

type VM

type VM struct {
	Name string
	// contains filtered or unexported fields
}

VM wraps a tart VM managed via the tart CLI.

func TartRun

func TartRun(ctx context.Context, name string, dirs map[string]string, disks []string) (*VM, error)

TartRun starts a VM headlessly via `tart run --no-graphics`. dirs are VirtioFS shared directories (tag → host path). disks are raw disk image paths attached as VirtIO block devices. The returned VM holds the background process; the caller must call Stop().

func (*VM) ForceStop

func (vm *VM) ForceStop() error

ForceStop kills the tart run process immediately.

func (*VM) IP

func (vm *VM) IP(ctx context.Context) (string, error)

IP returns the guest IP address via `tart ip`.

func (*VM) State

func (vm *VM) State() string

State returns the current VM state by querying `tart get`.

func (*VM) Stderr

func (vm *VM) Stderr() string

Stderr returns any stderr output captured from the tart run process.

func (*VM) Stop

func (vm *VM) Stop() error

Stop gracefully shuts down the VM via `tart stop`.

func (*VM) WaitForIP

func (vm *VM) WaitForIP(ctx context.Context, timeout, interval time.Duration) (string, error)

WaitForIP polls `tart ip` until it returns an IP or the timeout expires.

type VMStateFunc

type VMStateFunc func() string

VMStateFunc returns the current VM state (e.g. "running", "stopped", "error"). Passed to polling functions so they can fail fast if the VM crashes.

type VNCClient

type VNCClient struct {
	// contains filtered or unexported fields
}

VNCClient is a minimal VNC (RFB) client for sending keystrokes and pointer events. It implements just enough of the RFB protocol for Setup Assistant automation, including OCR-based screen detection.

func DialVNC

func DialVNC(addr string, timeout time.Duration) (*VNCClient, error)

DialVNC connects to a VNC server with no authentication and completes the RFB handshake.

func DialVNCAuth

func DialVNCAuth(addr, password string, timeout time.Duration) (*VNCClient, error)

DialVNCAuth connects to a VNC server with VNC Authentication (RFB security type 2).

func NewVNCClient

func NewVNCClient(conn net.Conn) (*VNCClient, error)

NewVNCClient wraps an existing connection (for testing with mock servers).

func (*VNCClient) CaptureFramebuffer

func (c *VNCClient) CaptureFramebuffer() (*image.RGBA, error)

CaptureFramebuffer grabs the current screen as an in-memory RGBA image without writing to disk. Used for screen-content analysis (brightness polling).

func (*VNCClient) CaptureScreenshot

func (c *VNCClient) CaptureScreenshot(path string) error

CaptureScreenshot requests a full framebuffer update from the VNC server and saves it as a PNG file. The VNC client must already be connected.

Uses the server's default pixel format (from ServerInit) — does NOT send SetPixelFormat or SetEncodings, because Apple's _VZVNCServer crashes (SIGTRAP) when it receives a SetEncodings with only RAW encoding.

func (*VNCClient) Click

func (c *VNCClient) Click(x, y uint16) error

Click sends a left mouse button press and release at the given coordinates.

func (*VNCClient) Close

func (c *VNCClient) Close() error

Close closes the VNC connection.

func (*VNCClient) Height

func (c *VNCClient) Height() uint16

Height returns the framebuffer height from ServerInit.

func (*VNCClient) SendKey

func (c *VNCClient) SendKey(key SpecialKey) error

SendKey sends a key-down then key-up event for a special key.

func (*VNCClient) SendKeyDown

func (c *VNCClient) SendKeyDown(key SpecialKey) error

SendKeyDown sends a key-down event without a corresponding key-up.

func (*VNCClient) SendKeyEvent

func (c *VNCClient) SendKeyEvent(event VNCKeyEvent) error

SendKeyEvent sends a single VNC key event.

func (*VNCClient) SendKeyUp

func (c *VNCClient) SendKeyUp(key SpecialKey) error

SendKeyUp sends a key-up event. Pairs with a prior SendKeyDown.

func (*VNCClient) SendPointerEvent

func (c *VNCClient) SendPointerEvent(buttonMask uint8, x, y uint16) error

SendPointerEvent sends a VNC pointer event (mouse movement / button press). buttonMask bit 0 = left button, bit 1 = middle, bit 2 = right.

func (*VNCClient) TypeString

func (c *VNCClient) TypeString(s string) error

TypeString sends key events for each character in the string.

func (*VNCClient) Width

func (c *VNCClient) Width() uint16

Width returns the framebuffer width from ServerInit.

type VNCKeyEvent

type VNCKeyEvent struct {
	DownFlag bool
	Key      uint32 // X11 keysym
}

VNCKeyEvent represents a single VNC key press or release.

func EncodeCharacter

func EncodeCharacter(c rune) []VNCKeyEvent

EncodeCharacter returns the key events for typing a single character. Uppercase letters include shift key events.

func EncodeSpecialKey

func EncodeSpecialKey(k SpecialKey) []VNCKeyEvent

EncodeSpecialKey returns the down+up pair for a special key.

func EncodeString

func EncodeString(s string) []VNCKeyEvent

EncodeString returns key events for typing a full string.

type WaitDirective

type WaitDirective struct {
	Duration time.Duration
}

WaitDirective pauses for a duration before the next step.

type WaitTextDirective

type WaitTextDirective struct {
	Text    string
	Timeout time.Duration
}

WaitTextDirective polls the VNC framebuffer via OCR until the given text appears on screen. Times out after Timeout.

Jump to

Keyboard shortcuts

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