Documentation
¶
Overview ¶
Package vm provides VM lifecycle management including the Backend interface abstraction, instance types, state persistence, and QMP communication.
Index ¶
- func CleanupVMDir(inst *Instance)
- func DefaultBackend() string
- func MixtapesDir(baseDir string) string
- func PrepareAppleVirtDisk(_ string, _ *Instance) error
- func PrepareQEMUDisk(baseDir string, inst *Instance, platform *QEMUPlatformConfig) error
- func ResolveBackend(baseDir string, cfg interface{ ... }, requestedBackend string) string
- func ResolveMixtapePath(baseDir, mixtape string) (string, error)
- func VMsDir(baseDir string) string
- type Backend
- type Instance
- func (inst *Instance) DiskPath() string
- func (inst *Instance) EFIVarsPath() string
- func (inst *Instance) JcardPath() string
- func (inst *Instance) LoadState() (*StateFile, error)
- func (inst *Instance) PIDFilePath() string
- func (inst *Instance) QCOWDiskPath() string
- func (inst *Instance) SerialLogPath() string
- func (inst *Instance) VMHostLogPath() string
- func (inst *Instance) VMHostPIDPath() string
- func (inst *Instance) VMHostSocketPath() string
- type KernelArtifacts
- type QEMUBackend
- func (q *QEMUBackend) Boot(ctx context.Context, inst *Instance) error
- func (q *QEMUBackend) Destroy(ctx context.Context, inst *Instance) error
- func (q *QEMUBackend) Down(ctx context.Context, inst *Instance, timeout time.Duration) error
- func (q *QEMUBackend) ForceDown(_ context.Context, inst *Instance) error
- func (q *QEMUBackend) List(ctx context.Context) ([]*Instance, error)
- func (q *QEMUBackend) LoadInstance(name string) (*Instance, error)
- func (q *QEMUBackend) Start(ctx context.Context, inst *Instance) error
- func (q *QEMUBackend) Status(_ context.Context, inst *Instance) (State, error)
- func (q *QEMUBackend) Up(ctx context.Context, inst *Instance) error
- func (q *QEMUBackend) WaitQEMU() error
- type QEMUPlatformConfig
- type QMPClient
- type State
- type StateFile
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func CleanupVMDir ¶
func CleanupVMDir(inst *Instance)
CleanupVMDir removes the VM directory. Used by the daemon when vmhost spawn fails and the VM directory needs to be cleaned up.
func DefaultBackend ¶
func DefaultBackend() string
DefaultBackend returns "qemu" on non-Apple-Silicon platforms.
func MixtapesDir ¶
MixtapesDir returns the path to the mixtapes directory.
func PrepareAppleVirtDisk ¶
PrepareAppleVirtDisk is not available on this platform.
func PrepareQEMUDisk ¶
func PrepareQEMUDisk(baseDir string, inst *Instance, platform *QEMUPlatformConfig) error
PrepareQEMUDisk creates the VM directory and sets up the disk image for a QEMU-backed VM. This runs in the daemon before spawning the vmhost process. It handles:
- Creating the VM directory
- Resolving the mixtape image
- Creating qcow2 overlay or copying raw image
- Initializing EFI vars (if needed)
- Saving jcard.toml
- Writing initial state.json
The inst.Dir field is set on the instance upon return.
func ResolveBackend ¶
func ResolveBackend(baseDir string, cfg interface{ GetMixtape() string }, requestedBackend string) string
ResolveBackend determines the backend type for a VM. Precedence:
- Explicit requestedBackend parameter (from CLI flag or config)
- MB_BACKEND environment variable
- Platform default: "applevirt" on darwin/arm64, "qemu" elsewhere
func ResolveMixtapePath ¶
ResolveMixtapePath resolves a mixtape reference to a disk image path. The mixtape string can be "name" (implies :latest) or "name:tag". The on-disk layout is mixtapes/{name}/{tag}/.
It searches for disk images in the following order:
- stereos.img (raw, preferred for Apple Virt framework -- from OCI pull)
- stereos.qcow2 (qcow2, QEMU -- from OCI pull)
- nixos.img (raw, legacy naming)
- nixos.qcow2 (qcow2, legacy naming)
- mixtapeDir itself as a bare image file
Types ¶
type Backend ¶
type Backend interface {
// Up creates and starts a new sandbox VM from the given instance configuration.
// It creates the VM directory, disk overlay, boots the hypervisor, waits for
// stereosd to become ready, injects secrets, and mounts shared directories.
Up(ctx context.Context, inst *Instance) error
// Start re-boots an existing stopped sandbox. The VM directory and disk
// must already exist from a previous Up call. It allocates new ports,
// starts the hypervisor, and runs post-boot provisioning.
Start(ctx context.Context, inst *Instance) error
// Down gracefully stops the VM. Sends shutdown via vsock to stereosd first,
// then falls back to ACPI shutdown via QMP, then force kill after timeout.
Down(ctx context.Context, inst *Instance, timeout time.Duration) error
// ForceDown immediately terminates the VM process.
ForceDown(ctx context.Context, inst *Instance) error
// Destroy stops the VM (if running) and removes all its on-disk resources.
Destroy(ctx context.Context, inst *Instance) error
// Status returns the current state of the VM.
Status(ctx context.Context, inst *Instance) (State, error)
// List returns all known VM instances by scanning the vms directory.
List(ctx context.Context) ([]*Instance, error)
// LoadInstance reads a persisted VM instance by name.
LoadInstance(name string) (*Instance, error)
}
Backend abstracts the VM hypervisor. QEMU today, Apple Virtualization.framework and KVM/libvirt in the future. All VM operations go through this interface.
func NewPlatformBackend ¶
NewPlatformBackend returns the appropriate VM backend for Linux. This returns the QEMU backend configured for KVM acceleration with native vsock support via vhost-vsock-pci.
type Instance ¶
type Instance struct {
// Name is the unique human-readable name for this sandbox.
Name string `json:"name"`
// Dir is the path to this VM's resource directory (~/.mb/vms/<name>/).
Dir string `json:"dir"`
// PID is the hypervisor process ID (e.g. QEMU PID).
PID int `json:"pid"`
// QMPSocket is the path to the QMP unix socket (QEMU only).
QMPSocket string `json:"qmp_socket"`
// VsockPort is the host-side port for vsock communication with stereosd.
// For QEMU with TCP fallback, this is a TCP port on localhost.
VsockPort int `json:"vsock_port"`
// SSHPort is the host port forwarded to guest port 22.
SSHPort int `json:"ssh_port"`
// VMState is the current lifecycle state.
VMState State `json:"state"`
// SSHKeyPath is the path to the ephemeral SSH private key for this
// sandbox, stored in the VM directory. Used by `mb ssh` to pass
// -i <key> to OpenSSH.
SSHKeyPath string `json:"ssh_key_path,omitempty"`
// Config is the jcard configuration for this instance.
Config *config.JcardConfig `json:"-"`
// contains filtered or unexported fields
}
Instance represents a single VM and its on-disk resources.
func LoadInstanceFromDisk ¶
LoadInstanceFromDisk reads a persisted VM instance by name from the vms directory, regardless of backend type.
func (*Instance) EFIVarsPath ¶
EFIVarsPath returns the path to this VM's writable EFI variable store.
func (*Instance) PIDFilePath ¶
PIDFilePath returns the path to the hypervisor PID file.
func (*Instance) QCOWDiskPath ¶
QCOWDiskPath returns the path to this VM's qcow2 overlay disk.
func (*Instance) SerialLogPath ¶
SerialLogPath returns the path to this VM's serial console log.
func (*Instance) VMHostLogPath ¶
VMHostLogPath returns the path to the vmhost log file.
func (*Instance) VMHostPIDPath ¶
VMHostPIDPath returns the path to the vmhost process PID file.
func (*Instance) VMHostSocketPath ¶
VMHostSocketPath returns the path to the vmhost control socket.
type KernelArtifacts ¶
type KernelArtifacts struct {
Kernel string // Path to bzImage
Initrd string // Path to initrd
Cmdline string // Kernel command line (contents of cmdline file)
}
KernelArtifacts holds the resolved paths for direct kernel boot. These correspond to the bzImage, initrd, and cmdline files produced by the stereOS build (system.build.kernelArtifacts in image.nix).
func ResolveKernelArtifacts ¶
func ResolveKernelArtifacts(baseDir, mixtape string) *KernelArtifacts
ResolveKernelArtifacts checks if a mixtape has kernel artifacts for direct kernel boot. It looks for bzImage, initrd, and cmdline files in the resolved mixtape directory.
The mixtape string can be "name" (implies latest) or "name:tag". The on-disk layout is mixtapes/{name}/{tag}/.
Returns nil if kernel artifacts are not available (caller should fall back to EFI boot).
type QEMUBackend ¶
type QEMUBackend struct {
// contains filtered or unexported fields
}
QEMUBackend implements the Backend interface using QEMU. Platform-specific behavior (acceleration, vsock transport, EFI paths) is configured via QEMUPlatformConfig, injected at construction time by the build-tagged NewPlatformBackend() functions.
In the vmhost architecture, each QEMU VM runs as a child process of a dedicated vmhost process. QEMU no longer daemonizes; the vmhost process owns its lifecycle.
func NewQEMUBackend ¶
func NewQEMUBackend(baseDir string, platform *QEMUPlatformConfig) *QEMUBackend
NewQEMUBackend creates a new QEMU backend with the given base directory and platform-specific configuration.
func (*QEMUBackend) Boot ¶
func (q *QEMUBackend) Boot(ctx context.Context, inst *Instance) error
Boot is the exported entry point for vmhost processes. It allocates ports, starts QEMU as a child process, and runs post-boot provisioning (stereosd handshake, secret injection, shared directory mounting). The VM directory and disk must already exist (created by the daemon).
func (*QEMUBackend) Destroy ¶
func (q *QEMUBackend) Destroy(ctx context.Context, inst *Instance) error
Destroy stops the VM and removes all resources.
func (*QEMUBackend) ForceDown ¶
func (q *QEMUBackend) ForceDown(_ context.Context, inst *Instance) error
ForceDown immediately terminates the VM process.
func (*QEMUBackend) List ¶
func (q *QEMUBackend) List(ctx context.Context) ([]*Instance, error)
List returns all VM instances owned by the QEMU backend by scanning the vms directory. VMs with a different backend field in state.json are skipped so that multiple backends can coexist in the same base directory.
func (*QEMUBackend) LoadInstance ¶
func (q *QEMUBackend) LoadInstance(name string) (*Instance, error)
LoadInstance reads a persisted VM instance by name.
func (*QEMUBackend) Start ¶
func (q *QEMUBackend) Start(ctx context.Context, inst *Instance) error
Start re-boots an existing stopped sandbox. The VM directory and disk are reused from a previous Up call. It re-reads the saved jcard.toml from the VM directory, allocates new ports, and boots QEMU.
func (*QEMUBackend) Up ¶
func (q *QEMUBackend) Up(ctx context.Context, inst *Instance) error
Up creates and starts a new sandbox VM from the given instance configuration. It creates the VM directory, disk, and EFI vars from scratch, then delegates to boot() for port allocation, QEMU launch, and post-boot provisioning.
func (*QEMUBackend) WaitQEMU ¶
func (q *QEMUBackend) WaitQEMU() error
WaitQEMU blocks until the QEMU child process exits. Returns nil on clean exit, or an error if QEMU crashed.
type QEMUPlatformConfig ¶
type QEMUPlatformConfig struct {
// Accelerator is the QEMU acceleration method: "hvf", "kvm", or "tcg".
Accelerator string
// Binary is the QEMU system emulator binary name (e.g.,
// "qemu-system-aarch64" for ARM64 guests).
Binary string
// MachineType is the QEMU machine type (e.g., "virt" for ARM64,
// "q35" for x86_64). Defaults to "virt" if empty.
MachineType string
// EFISearchPaths is an ordered list of paths to search for EFI firmware.
// The special prefix "{qemu_prefix}" is replaced at runtime with the
// resolved QEMU installation prefix (parent of the bin/ directory).
// The first existing file wins.
EFISearchPaths []string
// ControlPlaneMode controls how the host communicates with stereosd
// inside the guest VM.
//
// "tcp" -- Forward stereosd port via QEMU user-mode networking
// (hostfwd). Works on all platforms but the control plane
// shares the guest network stack. This is the only option
// on macOS/HVF where vsock devices are unavailable.
//
// "vsock" -- Attach a vhost-vsock device and connect via AF_VSOCK.
// Requires Linux/KVM. Provides an isolated control plane
// that works even with network.mode = "none".
//
// Future modes: "virtio-serial" (chardev unix socket via
// virtio-serial-device, works on macOS without guest networking).
ControlPlaneMode string
// VsockDevice is the QEMU device model name for native vsock.
// Only used when ControlPlaneMode is "vsock".
// Typically "vhost-vsock-pci" on x86 and "vhost-vsock-device" on ARM.
VsockDevice string
// DirectKernelBoot enables -kernel/-initrd/-append boot, bypassing
// EFI firmware and GRUB. Requires kernel artifacts (bzImage, initrd,
// cmdline) in the mixtape directory. When enabled and kernel artifacts
// are available, EFI pflash drives and EFI vars initialization are
// skipped entirely. Falls back to EFI boot if artifacts are missing.
DirectKernelBoot bool
// DiskAIO is the async I/O backend for QEMU disk drives.
// Set to "io_uring" on Linux 5.1+ for best performance, or leave
// empty to use QEMU's default (threads). Not supported on macOS.
DiskAIO string
// DiskCache is the cache mode for QEMU disk drives.
// Set to "none" (O_DIRECT) when using io_uring for best performance,
// or leave empty to use QEMU's default (writeback).
DiskCache string
}
QEMUPlatformConfig contains platform-specific settings for the QEMU backend. It is populated by the build-tagged NewPlatformBackend() functions and injected into QEMUBackend at construction time.
This struct captures the differences between running QEMU on macOS/HVF (Apple Silicon) vs. Linux/KVM, without scattering platform conditionals throughout the QEMU backend code.
func (*QEMUPlatformConfig) DefaultMachineType ¶
func (p *QEMUPlatformConfig) DefaultMachineType() string
DefaultMachineType returns the machine type, defaulting to "virt".
type QMPClient ¶
type QMPClient struct {
// contains filtered or unexported fields
}
QMPClient communicates with a running QEMU instance over its QMP (QEMU Machine Protocol) unix domain socket.
func DialQMP ¶
DialQMP connects to a QMP unix socket, reads the greeting, and negotiates capabilities to enter command mode.
func (*QMPClient) QueryStatus ¶
QueryStatus returns the VM run state (e.g. "running", "paused", "shutdown").
type StateFile ¶
type StateFile struct {
Name string `json:"name"`
CreatedAt time.Time `json:"created_at"`
Mixtape string `json:"mixtape"`
CPUs int `json:"cpus"`
Memory string `json:"memory"`
Disk string `json:"disk"`
NetworkMode string `json:"network_mode"`
SSHPort int `json:"ssh_port"`
VsockPort int `json:"vsock_port"`
ConfigPath string `json:"config_path,omitempty"`
// SSHKeyPath is the path to the ephemeral SSH private key, relative
// to the VM directory or absolute. Persisted so the key survives
// daemon restarts.
SSHKeyPath string `json:"ssh_key_path,omitempty"`
// Backend identifies which Backend implementation owns this VM.
Backend string `json:"backend,omitempty"`
// PlatformData holds opaque backend-specific persistent state.
// Each backend can use this to store identity or configuration data
// that must survive across daemon restarts and up/down cycles.
//
// Examples:
// - Apple Virt: serialized vz.GenericMachineIdentifier (gives the
// guest a stable MAC address, EFI NVRAM, etc.)
// - QEMU: could store a generated machine UUID for SMBIOS identity
// (-smbios type=1,uuid=...) if needed in the future.
PlatformData []byte `json:"platform_data,omitempty"`
}
StateFile is the persistent metadata for a VM stored in state.json.
func LoadStateFromDisk ¶
LoadStateFromDisk reads the state.json for a named VM.