## Keyboard shortcuts

Press `←` or `→` to navigate between chapters

Press `S` or `/` to search in the book

Press `?` to show this help

Press `Esc` to hide this help

# How vmctl Works

## State Directory

vmctl stores all VM state under `$XDG_DATA_HOME/vmctl/` (typically `~/.local/share/vmctl/`):

    ~/.local/share/vmctl/
      vms.json              # VM registry (name -> handle mapping)
      images/               # Downloaded image cache
      vms/
        <vm-name>/          # Per-VM working directory
          overlay.qcow2     # Copy-on-write disk overlay
          seed.iso          # Cloud-init NoCloud ISO
          qmp.sock          # QEMU Machine Protocol socket
          console.sock      # Serial console socket
          console.log       # Boot/cloud-init log
          provision.log     # Provisioning output log
          id_ed25519_generated      # Auto-generated SSH private key
          id_ed25519_generated.pub  # Auto-generated SSH public key
          pidfile            # QEMU process PID

## QCOW2 Overlays

vmctl never modifies the base image directly. Instead, it creates a QCOW2 copy-on-write overlay on top of the original. This means:

- Multiple VMs can share the same base image.
- The base image stays clean in the cache.
- Destroying a VM just deletes the overlay.

If you specify `disk` in your VMFile, the overlay is resized to that size and the guest filesystem can be grown.

## RouterHypervisor

All hypervisor operations go through a `RouterHypervisor` that dispatches to the appropriate backend based on the platform:

- Linux -> `QemuBackend`
- illumos -> `PropolisBackend`
- Testing -> `NoopBackend`

Each backend implements the same `Hypervisor` trait, so the CLI code is platform-agnostic.

## The Up Flow

When you run `vmctl up`, the following happens for each VM defined in `VMFile.kdl`:

1. Parse - Read and validate the VMFile.
2. Resolve - Download images (if URL), generate SSH keys (if cloud-init enabled), resolve paths.
3. Prepare - Create work directory, QCOW2 overlay, cloud-init seed ISO, allocate MAC address and SSH port.
4. Start - Launch QEMU with the correct arguments, wait for QMP socket.
5. Provision - Wait for SSH to become available (up to 120 seconds), then run each provisioner in order.

If a VM is already running, `vmctl up` skips it. If it's stopped, it restarts and re-provisions.