Repository: lima-vm/lima

Issue:
Add timeout to cloud-init progress monitoring
The `watchCloudInitProgress` function could run indefinitely if cloud-init gets stuck or fails to complete, potentially consuming resources and causing issues. A context timeout should be added to prevent indefinite monitoring while allowing sufficient time for normal cloud-init completion.



Discussion:
@norio-nomura: copy from https://github.com/lima-vm/lima/pull/3846#discussion_r2296852850

> Can you share how you were able to find the bug?

It can be reproduced on my Mac (Apple M2 Pro) with the following:
```console
limactl start template://ubuntu-lts --name=default --progress --set .containerd.user=false
```
built on current HEAD
```console
$ limactl --version
limactl version 1.2.0-193-gd4f4911e
```@norio-nomura: #3904 does not fix this.
@olamilekan000: > [#3904](https://github.com/lima-vm/lima/pull/3904) does not fix this.

Can you check again.@norio-nomura: It stops before the completion of cloud-init.
```console
➜ ~10:50:18 10:50:21~ (pull/3904) limactl start template://ubuntu-lts --name=default --progress --set .containerd.user=false
INFO[0000] Terminal is not available, proceeding without opening an editor 
INFO[0000] Starting the instance "default" with internal VM driver "vz" 
WARN[0000] vmType vz: ignoring [VMOpts]                 
INFO[0000] Attempting to download the image              arch=aarch64 digest="sha256:bbecbb88100ee65497927ed0da247ba15af576a8855004182cf3c87265e25d35" location="https://cloud-images.ubuntu.com/releases/noble/release-20250704/ubuntu-24.04-server-cloudimg-arm64.img"
INFO[0000] Using cache "/Users/norio/Library/Caches/lima/download/by-url-sha256/87505913829585726d6c8aac9c6981c46b8d67142ea5e1af8d9f2d7e872e3ecb/data" 
INFO[0000] Converting "/Users/norio/.lima/default/basedisk" (qcow2) to a raw disk "/Users/norio/.lima/default/diffdisk" 
3.50 GiB / 3.50 GiB [---------------------------------------] 100.00% 1.34 GiB/s
INFO[0003] Expanding to 100GiB                          
INFO[0003] [hostagent] hostagent socket created at /Users/norio/.lima/default/ha.sock 
INFO[0003] [hostagent] Starting VZ (hint: to watch the boot progress, see "/Users/norio/.lima/default/serial*.log") 
INFO[0003] SSH Local Port: 60022                        
INFO[0003] [hostagent] [VZ] - vm state change: running  
INFO[0003] [hostagent] Waiting for the essential requirement 1 of 2: "ssh" 
INFO[0013] [hostagent] Waiting for the essential requirement 1 of 2: "ssh" 
INFO[0013] [hostagent] The essential requirement 1 of 2 is satisfied 
INFO[0013] [hostagent] Waiting for the essential requirement 2 of 2: "user session is ready for ssh" 
INFO[0025] [hostagent] Waiting for the essential requirement 2 of 2: "user session is ready for ssh" 
INFO[0025] [hostagent] The essential requirement 2 of 2 is satisfied 
INFO[0025] [hostagent] Waiting for the guest agent to be running 
INFO[0025] [hostagent] Guest agent is running           
INFO[0025] Cloud-init provisioning started...           
INFO[0025] [hostagent] Waiting for the final requirement 1 of 1: "boot scripts must have finished" 
INFO[0025] [hostagent] Not forwarding TCP 127.0.0.53:53 
INFO[0025] [hostagent] Not forwarding TCP 127.0.0.54:53 
INFO[0025] [hostagent] Not forwarding TCP [::]:22       
INFO[0025] [cloud-init] Cloud-init v. 25.1.2-0ubuntu0~24.04.1 running 'init-local' at Mon, 25 Aug 2025 01:50:28 +0000. Up 2.44 seconds. 
INFO[0025] [hostagent] Not forwarding UDP 127.0.0.54:53 
INFO[0025] [cloud-init] Cloud-init v. 25.1.2-0ubuntu0~24.04.1 running 'init' at Mon, 25 Aug 2025 01:50:31 +0000. Up 4.66 seconds. 
INFO[0025] [hostagent] Not forwarding UDP 127.0.0.53:53 
INFO[0025] [cloud-init] ci-info: +++++++++++++++++++++++++++++++++++++++Net device info+++++++++++++++++++++++++++++++++++++++ 
INFO[0025] [hostagent] Not forwarding UDP 192.168.5.15:68 
INFO[0025] [cloud-init] ci-info: +--------+------+------------------------------+---------------+--------+-------------------+ 
INFO[0025] [cloud-init] ci-info: | Device |  Up  |           Address            |      Mask     | Scope  |     Hw-Address    | 
INFO[0025] [hostagent] The final requirement 1 of 1 is satisfied 
INFO[0025] [cloud-init] ci-info: +--------+------+------------------------------+---------------+--------+-------------------+ 
INFO[0025] [cloud-init] ci-info: |  eth0  | True |         192.168.5.15         | 255.255.255.0 | global | 52:55:55:01:9c:69 | 
INFO[0025] READY. Run `lima` to open the shell.         
➜ ~10:50:47 10:53:21~ (pull/3904) limactl --version
limactl version 1.2.0-194-g9a5bf59e
```@norio-nomura: The issue has a mix of two problems.
- `limactl start --progress` does not stop if it receives `hostagentevents.Event.Status.Running=true` before receiving `Event.Status.CloudInitProgress.Completed=true`
- `limactl hostagent --progress` does not stop the `ssh -F ... sudo tail -n +1 -f /var/log/cloud-init-output.log` process even if it detects "cloud-init", "finished".@olamilekan000: > It stops before the completion of cloud-init.
> 
> ➜ ~10:50:18 10:50:21~ (pull/3904) limactl start template://ubuntu-lts --name=default --progress --set .containerd.user=false
> INFO[0000] Terminal is not available, proceeding without opening an editor 
> INFO[0000] Starting the instance "default" with internal VM driver "vz" 
> WARN[0000] vmType vz: ignoring [VMOpts]                 
> INFO[0000] Attempting to download the image              arch=aarch64 digest="sha256:bbecbb88100ee65497927ed0da247ba15af576a8855004182cf3c87265e25d35" location="https://cloud-images.ubuntu.com/releases/noble/release-20250704/ubuntu-24.04-server-cloudimg-arm64.img"
> INFO[0000] Using cache "/Users/norio/Library/Caches/lima/download/by-url-sha256/87505913829585726d6c8aac9c6981c46b8d67142ea5e1af8d9f2d7e872e3ecb/data" 
> INFO[0000] Converting "/Users/norio/.lima/default/basedisk" (qcow2) to a raw disk "/Users/norio/.lima/default/diffdisk" 
> 3.50 GiB / 3.50 GiB [---------------------------------------] 100.00% 1.34 GiB/s
> INFO[0003] Expanding to 100GiB                          
> INFO[0003] [hostagent] hostagent socket created at /Users/norio/.lima/default/ha.sock 
> INFO[0003] [hostagent] Starting VZ (hint: to watch the boot progress, see "/Users/norio/.lima/default/serial*.log") 
> INFO[0003] SSH Local Port: 60022                        
> INFO[0003] [hostagent] [VZ] - vm state change: running  
> INFO[0003] [hostagent] Waiting for the essential requirement 1 of 2: "ssh" 
> INFO[0013] [hostagent] Waiting for the essential requirement 1 of 2: "ssh" 
> INFO[0013] [hostagent] The essential requirement 1 of 2 is satisfied 
> INFO[0013] [hostagent] Waiting for the essential requirement 2 of 2: "user session is ready for ssh" 
> INFO[0025] [hostagent] Waiting for the essential requirement 2 of 2: "user session is ready for ssh" 
> INFO[0025] [hostagent] The essential requirement 2 of 2 is satisfied 
> INFO[0025] [hostagent] Waiting for the guest agent to be running 
> INFO[0025] [hostagent] Guest agent is running           
> INFO[0025] Cloud-init provisioning started...           
> INFO[0025] [hostagent] Waiting for the final requirement 1 of 1: "boot scripts must have finished" 
> INFO[0025] [hostagent] Not forwarding TCP 127.0.0.53:53 
> INFO[0025] [hostagent] Not forwarding TCP 127.0.0.54:53 
> INFO[0025] [hostagent] Not forwarding TCP [::]:22       
> INFO[0025] [cloud-init] Cloud-init v. 25.1.2-0ubuntu0~24.04.1 running 'init-local' at Mon, 25 Aug 2025 01:50:28 +0000. Up 2.44 seconds. 
> INFO[0025] [hostagent] Not forwarding UDP 127.0.0.54:53 
> INFO[0025] [cloud-init] Cloud-init v. 25.1.2-0ubuntu0~24.04.1 running 'init' at Mon, 25 Aug 2025 01:50:31 +0000. Up 4.66 seconds. 
> INFO[0025] [hostagent] Not forwarding UDP 127.0.0.53:53 
> INFO[0025] [cloud-init] ci-info: +++++++++++++++++++++++++++++++++++++++Net device info+++++++++++++++++++++++++++++++++++++++ 
> INFO[0025] [hostagent] Not forwarding UDP 192.168.5.15:68 
> INFO[0025] [cloud-init] ci-info: +--------+------+------------------------------+---------------+--------+-------------------+ 
> INFO[0025] [cloud-init] ci-info: | Device |  Up  |           Address            |      Mask     | Scope  |     Hw-Address    | 
> INFO[0025] [hostagent] The final requirement 1 of 1 is satisfied 
> INFO[0025] [cloud-init] ci-info: +--------+------+------------------------------+---------------+--------+-------------------+ 
> INFO[0025] [cloud-init] ci-info: |  eth0  | True |         192.168.5.15         | 255.255.255.0 | global | 52:55:55:01:9c:69 | 
> INFO[0025] READY. Run `lima` to open the shell.         
> ➜ ~10:50:47 10:53:21~ (pull/3904) limactl --version
> limactl version 1.2.0-194-g9a5bf59e

it now works fine.
```
➜  lima git:(add-timeout-context-to-progress-monitoring) ✗ limactl start template://ubuntu-lts --vm-type qemu  --name=defaultz --progress  --set .containerd.user=false
? Creating an instance "defaultz" Proceed with the current configuration
WARN[0000] treating lima version "<unknown>" from "/Users/olalekanodukoya/.lima/defaultz/lima-version" as very latest release 
WARN[0000] treating lima version "<unknown>" from "/Users/olalekanodukoya/.lima/defaultz/lima-version" as very latest release 
INFO[0000] Starting the instance "defaultz" with internal VM driver "qemu" 
INFO[0001] QEMU binary "/opt/homebrew/bin/qemu-system-aarch64" seems properly signed with the "com.apple.security.hypervisor" entitlement 
INFO[0001] Attempting to download the image              arch=aarch64 digest="sha256:bbecbb88100ee65497927ed0da247ba15af576a8855004182cf3c87265e25d35" location="https://cloud-images.ubuntu.com/releases/noble/release-20250704/ubuntu-24.04-server-cloudimg-arm64.img"
INFO[0001] Using cache "/Users/olalekanodukoya/Library/Caches/lima/download/by-url-sha256/87505913829585726d6c8aac9c6981c46b8d67142ea5e1af8d9f2d7e872e3ecb/data" 
WARN[0000] [hostagent] treating lima version "<unknown>" from "/Users/olalekanodukoya/.lima/defaultz/lima-version" as very latest release 
INFO[0000] [hostagent] hostagent socket created at /Users/olalekanodukoya/.lima/defaultz/ha.sock 
INFO[0001] [hostagent] Using system firmware ("/opt/homebrew/share/qemu/edk2-aarch64-code.fd") 
INFO[0001] [hostagent] Starting QEMU (hint: to watch the boot progress, see "/Users/olalekanodukoya/.lima/defaultz/serial*.log") 
INFO[0002] SSH Local Port: 61126                        
INFO[0001] [hostagent] Waiting for the essential requirement 1 of 2: "ssh" 
INFO[0011] [hostagent] Waiting for the essential requirement 1 of 2: "ssh" 
INFO[0012] [hostagent] The essential requirement 1 of 2 is satisfied 
INFO[0012] [hostagent] Waiting for the essential requirement 2 of 2: "user session is ready for ssh" 
INFO[0023] [hostagent] Waiting for the essential requirement 2 of 2: "user session is ready for ssh" 
INFO[0023] [hostagent] The essential requirement 2 of 2 is satisfied 
INFO[0023] [hostagent] Waiting for the guest agent to be running 
INFO[0023] [hostagent] Forwarding "/run/lima-guestagent.sock" (guest) to "/Users/olalekanodukoya/.lima/defaultz/ga.sock" (host) 
INFO[0024] Cloud-init provisioning started...           
INFO[0023] [hostagent] Guest agent is running           
INFO[0023] [hostagent] Waiting for the final requirement 1 of 1: "boot scripts must have finished" 
INFO[0024] [cloud-init] Cloud-init v. 25.1.2-0ubuntu0~24.04.1 running 'init-local' at Mon, 25 Aug 2025 02:04:59 +0000. Up 4.04 seconds. 
INFO[0024] [cloud-init] Cloud-init v. 25.1.2-0ubuntu0~24.04.1 running 'init' at Mon, 25 Aug 2025 02:05:01 +0000. Up 6.49 seconds. 
INFO[0024] [cloud-init] ci-info: +++++++++++++++++++++++++++++++++++++++Net device info+++++++++++++++++++++++++++++++++++++++ 
INFO[0024] [cloud-init] ci-info: +--------+------+------------------------------+---------------+--------+-------------------+ 
INFO[0023] [hostagent] Not forwarding TCP [::]:22       
INFO[0024] [cloud-init] ci-info: | Device |  Up  |           Address            |      Mask     | Scope  |     Hw-Address    | 
INFO[0023] [hostagent] Not forwarding UDP 127.0.0.54:53 
INFO[0024] [cloud-init] ci-info: +--------+------+------------------------------+---------------+--------+-------------------+ 
INFO[0023] [hostagent] Not forwarding UDP 127.0.0.53:53 
INFO[0024] [cloud-init] ci-info: |  eth0  | True |         192.168.5.15         | 255.255.255.0 | global | 52:55:55:ac:da:18 | 
INFO[0023] [hostagent] Not forwarding UDP 192.168.5.15:68 
INFO[0024] [cloud-init] ci-info: |  eth0  | True | fe80::5055:55ff:feac:da18/64 |       .       |  link  | 52:55:55:ac:da:18 | 
INFO[0024] [cloud-init] ci-info: |   lo   | True |          127.0.0.1           |   255.0.0.0   |  host  |         .         | 
INFO[0023] [hostagent] The final requirement 1 of 1 is satisfied 
INFO[0024] [cloud-init] ci-info: |   lo   | True |           ::1/128            |       .       |  host  |         .         | 
INFO[0024] [cloud-init] ci-info: +--------+------+------------------------------+---------------+--------+-------------------+ 
INFO[0023] [hostagent] Not forwarding TCP 127.0.0.54:53 
INFO[0024] [cloud-init] ci-info: ++++++++++++++++++++++++++++++Route IPv4 info++++++++++++++++++++++++++++++ 
INFO[0023] [hostagent] Not forwarding TCP 127.0.0.53:53 
INFO[0024] [cloud-init] ci-info: +-------+-------------+-------------+-----------------+-----------+-------+ 
INFO[0024] [cloud-init] ci-info: | Route | Destination |   Gateway   |     Genmask     | Interface | Flags | 
INFO[0024] [cloud-init] ci-info: +-------+-------------+-------------+-----------------+-----------+-------+ 
INFO[0024] [cloud-init] ci-info: |   0   |   0.0.0.0   | 192.168.5.2 |     0.0.0.0     |    eth0   |   UG  | 
INFO[0024] [cloud-init] ci-info: |   1   | 192.168.5.0 |   0.0.0.0   |  255.255.255.0  |    eth0   |   U   | 
INFO[0024] [cloud-init] ci-info: |   2   | 192.168.5.2 |   0.0.0.0   | 255.255.255.255 |    eth0   |   UH  | 
INFO[0024] [cloud-init] ci-info: |   3   | 192.168.5.3 |   0.0.0.0   | 255.255.255.255 |    eth0   |   UH  | 
INFO[0024] [cloud-init] ci-info: +-------+-------------+-------------+-----------------+-----------+-------+ 
INFO[0024] [cloud-init] ci-info: +++++++++++++++++++Route IPv6 info+++++++++++++++++++ 
INFO[0024] [cloud-init] ci-info: +-------+-------------+---------+-----------+-------+ 
INFO[0024] [cloud-init] ci-info: | Route | Destination | Gateway | Interface | Flags | 
INFO[0024] [cloud-init] ci-info: +-------+-------------+---------+-----------+-------+ 
INFO[0024] [cloud-init] ci-info: |   0   |  fe80::/64  |    ::   |    eth0   |   U   | 
INFO[0024] [cloud-init] ci-info: |   2   |    local    |    ::   |    eth0   |   U   | 
INFO[0024] [cloud-init] ci-info: |   3   |  multicast  |    ::   |    eth0   |   U   | 
INFO[0024] [cloud-init] ci-info: +-------+-------------+---------+-----------+-------+ 
INFO[0024] [cloud-init] 2025-08-25 02:05:01,713 - loggers.py[DEPRECATED]: Deprecated cloud-config provided: users.0.ssh-authorized-keys:  Deprecated in version 18.3. Use **ssh_authorized_keys** instead., users.0.uid:  Changed in version 22.3. The use of ``string`` type is deprecated. Use an ``integer`` instead. 
INFO[0024] [cloud-init] Generating public/private rsa key pair. 
INFO[0024] [cloud-init] Your identification has been saved in /etc/ssh/ssh_host_rsa_key 
INFO[0024] [cloud-init] Your public key has been saved in /etc/ssh/ssh_host_rsa_key.pub 
INFO[0024] [cloud-init] The key fingerprint is:         
INFO[0024] [cloud-init] SHA256:RnqvmiT9IYFcxJvD7SEGEhALTQWMW8Zqk/H7ev/LpH8 root@lima-defaultz 
INFO[0024] [cloud-init] The key's randomart image is:   
INFO[0024] [cloud-init] +---[RSA 3072]----+             
INFO[0024] [cloud-init] |+O++...          |             
INFO[0024] [cloud-init] |ooB ...          |             
INFO[0024] [cloud-init] |.=+. o.+.        |             
INFO[0024] [cloud-init] |o+ o oBoo        |             
INFO[0024] [cloud-init] |. . +.o+S.       |             
INFO[0024] [cloud-init] |   . . +..       |             
INFO[0024] [cloud-init] |    o + ...      |             
INFO[0024] [cloud-init] |     = ++o E     |             
INFO[0024] [cloud-init] |   .o ++==o      |             
INFO[0024] [cloud-init] +----[SHA256]-----+             
INFO[0024] [cloud-init] Generating public/private ecdsa key pair. 
INFO[0024] [cloud-init] Your identification has been saved in /etc/ssh/ssh_host_ecdsa_key 
INFO[0024] [cloud-init] Your public key has been saved in /etc/ssh/ssh_host_ecdsa_key.pub 
INFO[0024] [cloud-init] The key fingerprint is:         
INFO[0024] [cloud-init] SHA256:cqV0KbERCK81GjutKTNplM8o6lPcnePY5/7P8nH+Tb4 root@lima-defaultz 
INFO[0024] [cloud-init] The key's randomart image is:   
INFO[0024] [cloud-init] +---[ECDSA 256]---+             
INFO[0024] [cloud-init] |    .. .+.       |             
INFO[0024] [cloud-init] |     ..  + .     |             
INFO[0024] [cloud-init] |    . + + +      |             
INFO[0024] [cloud-init] |   . B o =       |             
INFO[0024] [cloud-init] |  + * + S        |             
INFO[0024] [cloud-init] | . B = *         |             
INFO[0024] [cloud-init] |. O = + .    . ..|             
INFO[0024] [cloud-init] |.+ + . o . .. +o.|             
INFO[0024] [cloud-init] |o..     +o..++ E*|             
INFO[0024] [cloud-init] +----[SHA256]-----+             
INFO[0024] [cloud-init] Generating public/private ed25519 key pair. 
INFO[0024] [cloud-init] Your identification has been saved in /etc/ssh/ssh_host_ed25519_key 
INFO[0024] [cloud-init] Your public key has been saved in /etc/ssh/ssh_host_ed25519_key.pub 
INFO[0024] [cloud-init] The key fingerprint is:         
INFO[0024] [cloud-init] SHA256:6srYD8sg15twPGvSHQPjIosBBF06DZEhTBOLewnkPFA root@lima-defaultz 
INFO[0024] [cloud-init] The key's randomart image is:   
INFO[0024] [cloud-init] +--[ED25519 256]--+             
INFO[0024] [cloud-init] |BBE=.            |             
INFO[0024] [cloud-init] |*+=+             |             
INFO[0024] [cloud-init] |+=o .            |             
INFO[0024] [cloud-init] |.o.oo            |             
INFO[0024] [cloud-init] |o o. o  S        |             
INFO[0024] [cloud-init] |o..o. o.         |             
INFO[0024] [cloud-init] |o++o*..o         |             
INFO[0024] [cloud-init] |oo.OoO.          |             
INFO[0024] [cloud-init] |  .oXoo          |             
INFO[0024] [cloud-init] +----[SHA256]-----+             
INFO[0024] [cloud-init] Cloud-init v. 25.1.2-0ubuntu0~24.04.1 running 'modules:config' at Mon, 25 Aug 2025 02:05:03 +0000. Up 8.34 seconds. 
INFO[0024] [cloud-init] Cloud-init v. 25.1.2-0ubuntu0~24.04.1 running 'modules:final' at Mon, 25 Aug 2025 02:05:05 +0000. Up 10.20 seconds. 
INFO[0024] [cloud-init] + LIMA_CIDATA_MNT=/mnt/lima-cidata 
INFO[0024] [cloud-init] + LIMA_CIDATA_DEV=/dev/disk/by-label/cidata 
INFO[0024] [cloud-init] + mkdir -p -m 700 /mnt/lima-cidata 
INFO[0024] [cloud-init] + mount -o ro,mode=0700,dmode=0700,overriderockperm,exec,uid=0 /dev/disk/by-label/cidata /mnt/lima-cidata 
INFO[0024] [cloud-init] + export LIMA_CIDATA_MNT        
INFO[0024] [cloud-init] + exec /mnt/lima-cidata/boot.sh 
INFO[0024] [cloud-init] LIMA 2025-08-25T03:05:05+01:00| Executing /mnt/lima-cidata/boot/00-alpine-user-group.sh 
INFO[0024] [cloud-init] LIMA 2025-08-25T03:05:05+01:00| Executing /mnt/lima-cidata/boot/00-check-rtc-and-wait-ntp.sh 
INFO[0024] [cloud-init] LIMA 2025-08-25T03:05:05+01:00| Executing /mnt/lima-cidata/boot/00-modprobe.sh 
INFO[0024] [cloud-init] Loading kernel module "fuse"    
INFO[0024] [cloud-init] Loading kernel module "tun"     
INFO[0024] [cloud-init] Loading kernel module "tap"     
INFO[0024] [cloud-init] Loading kernel module "bridge"  
INFO[0024] [cloud-init] Loading kernel module "veth"    
INFO[0024] [cloud-init] Loading kernel module "ip_tables" 
INFO[0024] [cloud-init] Loading kernel module "ip6_tables" 
INFO[0024] [cloud-init] Loading kernel module "iptable_nat" 
INFO[0024] [cloud-init] Loading kernel module "ip6table_nat" 
INFO[0024] [cloud-init] Loading kernel module "iptable_filter" 
INFO[0024] [cloud-init] Loading kernel module "ip6table_filter" 
INFO[0024] [cloud-init] Loading kernel module "nf_tables" 
INFO[0024] [cloud-init] Loading kernel module "x_tables" 
INFO[0024] [cloud-init] Loading kernel module "xt_MASQUERADE" 
INFO[0024] [cloud-init] Loading kernel module "xt_addrtype" 
INFO[0024] [cloud-init] Loading kernel module "xt_comment" 
INFO[0024] [cloud-init] Loading kernel module "xt_conntrack" 
INFO[0024] [cloud-init] Loading kernel module "xt_mark" 
INFO[0024] [cloud-init] Loading kernel module "xt_multiport" 
INFO[0024] [cloud-init] Loading kernel module "xt_nat"  
INFO[0024] [cloud-init] Loading kernel module "xt_tcpudp" 
INFO[0024] [cloud-init] Loading kernel module "overlay" 
INFO[0024] [cloud-init] LIMA 2025-08-25T03:05:05+01:00| Executing /mnt/lima-cidata/boot/00-reboot-if-required.sh 
INFO[0024] [cloud-init] + command -v dnf                
INFO[0024] [cloud-init] LIMA 2025-08-25T03:05:05+01:00| Executing /mnt/lima-cidata/boot/01-alpine-ash-as-bash.sh 
INFO[0024] [cloud-init] LIMA 2025-08-25T03:05:05+01:00| Executing /mnt/lima-cidata/boot/02-wsl2-setup.sh 
INFO[0024] [cloud-init] LIMA 2025-08-25T03:05:05+01:00| Executing /mnt/lima-cidata/boot/04-persistent-data-volume.sh 
INFO[0024] [cloud-init] + test -f /etc/alpine-release   
INFO[0024] [cloud-init] + exit 0                        
INFO[0025] [cloud-init] LIMA 2025-08-25T03:05:05+01:00| Executing /mnt/lima-cidata/boot/05-lima-disks.sh 
INFO[0025] [cloud-init] + test 0 -gt 0                  
INFO[0025] [cloud-init] + exit 0                        
INFO[0025] [cloud-init] LIMA 2025-08-25T03:05:05+01:00| Executing /mnt/lima-cidata/boot/05-lima-mounts.sh 
INFO[0025] [cloud-init] + [[ qemu == \v\z ]]            
INFO[0025] [cloud-init] + exit 0                        
INFO[0025] [cloud-init] LIMA 2025-08-25T03:05:05+01:00| Executing /mnt/lima-cidata/boot/05-rosetta-volume.sh 
INFO[0025] [cloud-init] + '[' false '!=' true ']'       
INFO[0025] [cloud-init] + exit 0                        
INFO[0025] [cloud-init] LIMA 2025-08-25T03:05:05+01:00| Executing /mnt/lima-cidata/boot/06-etc-hosts.sh 
INFO[0025] [cloud-init] + sed -i /host.lima.internal/d /etc/hosts 
INFO[0025] [cloud-init] + echo -e '192.168.5.2\thost.lima.internal' 
INFO[0025] [cloud-init] LIMA 2025-08-25T03:05:05+01:00| Executing /mnt/lima-cidata/boot/07-etc-environment.sh 
INFO[0025] [cloud-init] + test ! -f /etc/environment    
INFO[0025] [cloud-init] + cat /etc/environment          
INFO[0025] [cloud-init] + orig=PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin" 
INFO[0025] [cloud-init] + [ -e /etc/environment ]       
INFO[0025] [cloud-init] + sed -i /#LIMA-START/,/#LIMA-END/d /etc/environment 
INFO[0025] [cloud-init] + cat /mnt/lima-cidata/etc_environment 
INFO[0025] [cloud-init] + command -v loginctl           
INFO[0025] [cloud-init] + cat /etc/environment          
INFO[0025] [cloud-init] + [ PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin" != PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin" 
INFO[0025] [cloud-init] #LIMA-START                     
INFO[0025] [cloud-init] #LIMA-END ]                     
INFO[0025] [cloud-init] + loginctl terminate-user olalekanodukoya 
INFO[0025] [cloud-init] + cp /mnt/lima-cidata/meta-data /run/lima-ssh-ready 
INFO[0025] [cloud-init] LIMA 2025-08-25T03:05:05+01:00| Executing /mnt/lima-cidata/boot/08-shell-prompt.sh 
INFO[0025] [cloud-init] + [ defaultz = default ]        
INFO[0025] [cloud-init] LIMA 2025-08-25T03:05:05+01:00| Executing /mnt/lima-cidata/boot/09-host-dns-setup.sh 
INFO[0025] [cloud-init] + readonly chain=LIMADNS        
INFO[0025] [cloud-init] + command -v iptables           
INFO[0025] [cloud-init] + chain_exists                  
INFO[0025] [cloud-init] + iptables --table nat -n --list LIMADNS 
INFO[0025] [cloud-init] + iptables --table nat --new-chain LIMADNS 
INFO[0025] [cloud-init] + iptables --table nat --insert PREROUTING 1 --jump LIMADNS 
INFO[0025] [cloud-init] + iptables --table nat --insert OUTPUT 1 --jump LIMADNS 
INFO[0025] [cloud-init] + iptables --table nat --flush LIMADNS 
INFO[0025] [cloud-init] + [ -n 56242 ]                  
INFO[0025] [cloud-init] + [ 56242 -ne 0 ]               
INFO[0025] [cloud-init] + iptables --table nat --append LIMADNS --destination 192.168.5.3 --protocol udp --dport 53 --jump DNAT --to-destination 192.168.5.2:56242 
INFO[0025] [cloud-init] + [ -n 61127 ]                  
INFO[0025] [cloud-init] + [ 61127 -ne 0 ]               
INFO[0025] [cloud-init] + iptables --table nat --append LIMADNS --destination 192.168.5.3 --protocol tcp --dport 53 --jump DNAT --to-destination 192.168.5.2:61127 
INFO[0025] [cloud-init] LIMA 2025-08-25T03:05:05+01:00| Executing /mnt/lima-cidata/boot/10-alpine-prep.sh 
INFO[0025] [cloud-init] + test -f /etc/alpine-release   
INFO[0025] [cloud-init] + exit 0                        
INFO[0025] [cloud-init] LIMA 2025-08-25T03:05:05+01:00| Executing /mnt/lima-cidata/boot/11-colorterm-environment.sh 
INFO[0025] [cloud-init] + [ -d /etc/ssh/sshd_config.d ] 
INFO[0025] [cloud-init] + [ -e /etc/ssh/sshd_config.d/10-acceptenv-colorterm.conf ] 
INFO[0025] [cloud-init] + echo AcceptEnv COLORTERM      
INFO[0025] [cloud-init] + [ -f /sbin/openrc-run ]       
INFO[0025] [cloud-init] + command -v systemctl          
INFO[0025] [cloud-init] + systemctl -q is-active ssh    
INFO[0025] [cloud-init] + systemctl reload ssh          
INFO[0025] [cloud-init] LIMA 2025-08-25T03:05:05+01:00| Executing /mnt/lima-cidata/boot/20-rootless-base.sh 
INFO[0025] [cloud-init] + command -v systemctl          
INFO[0025] [cloud-init] + [ -O /home/olalekanodukoya.linux ] 
INFO[0025] [cloud-init] + grep -q # Lima BEGIN /home/olalekanodukoya.linux/.profile 
INFO[0025] [cloud-init] + cat                           
INFO[0025] [cloud-init] + uname -r                      
INFO[0025] [cloud-init] + compare_version.sh 6.8.0-63-generic -lt 5.13 
INFO[0025] [cloud-init] + cat                           
INFO[0025] [cloud-init] + chown olalekanodukoya /home/olalekanodukoya.linux/.profile 
INFO[0025] [cloud-init] + grep -q # Lima BEGIN /home/olalekanodukoya.linux/.bashrc 
INFO[0025] [cloud-init] + cat                           
INFO[0025] [cloud-init] + uname -r                      
INFO[0025] [cloud-init] + compare_version.sh 6.8.0-63-generic -lt 5.13 
INFO[0025] [cloud-init] + cat                           
INFO[0025] [cloud-init] + chown olalekanodukoya /home/olalekanodukoya.linux/.bashrc 
INFO[0025] [cloud-init] + grep -q # Lima BEGIN /home/olalekanodukoya.linux/.zshrc 
INFO[0025] [cloud-init] grep: /home/olalekanodukoya.linux/.zshrc: No such file or directory 
INFO[0025] [cloud-init] + cat                           
INFO[0025] [cloud-init] + uname -r                      
INFO[0025] [cloud-init] + compare_version.sh 6.8.0-63-generic -lt 5.13 
INFO[0025] [cloud-init] + cat                           
INFO[0025] [cloud-init] + chown olalekanodukoya /home/olalekanodukoya.linux/.zshrc 
INFO[0025] [cloud-init] + [ ! -e /etc/systemd/system/user@.service.d/lima.conf ] 
INFO[0025] [cloud-init] + mkdir -p /etc/systemd/system/user@.service.d 
INFO[0025] [cloud-init] + cat                           
INFO[0025] [cloud-init] + systemctl daemon-reload       
INFO[0025] [cloud-init] + sysctl_conf=/etc/sysctl.d/99-lima.conf 
INFO[0025] [cloud-init] + [ ! -e /etc/sysctl.d/99-lima.conf ] 
INFO[0025] [cloud-init] + [ -e /proc/sys/kernel/unprivileged_userns_clone ] 
INFO[0025] [cloud-init] + echo kernel.unprivileged_userns_clone=1 
INFO[0025] [cloud-init] + echo net.ipv4.ping_group_range = 0 2147483647 
INFO[0025] [cloud-init] + echo net.ipv4.ip_unprivileged_port_start=0 
INFO[0025] [cloud-init] + sysctl --system               
INFO[0025] [cloud-init] * Applying /usr/lib/sysctl.d/10-apparmor.conf ... 
INFO[0025] [cloud-init] * Applying /etc/sysctl.d/10-bufferbloat.conf ... 
INFO[0025] [cloud-init] * Applying /etc/sysctl.d/10-console-messages.conf ... 
INFO[0025] [cloud-init] * Applying /etc/sysctl.d/10-ipv6-privacy.conf ... 
INFO[0025] [cloud-init] * Applying /etc/sysctl.d/10-kernel-hardening.conf ... 
INFO[0025] [cloud-init] * Applying /etc/sysctl.d/10-magic-sysrq.conf ... 
INFO[0025] [cloud-init] * Applying /etc/sysctl.d/10-map-count.conf ... 
INFO[0025] [cloud-init] * Applying /etc/sysctl.d/10-network-security.conf ... 
INFO[0025] [cloud-init] * Applying /etc/sysctl.d/10-ptrace.conf ... 
INFO[0025] [cloud-init] * Applying /etc/sysctl.d/10-zeropage.conf ... 
INFO[0025] [cloud-init] * Applying /usr/lib/sysctl.d/50-pid-max.conf ... 
INFO[0025] [cloud-init] * Applying /etc/sysctl.d/99-cloudimg-ipv6.conf ... 
INFO[0025] [cloud-init] * Applying /etc/sysctl.d/99-lima.conf ... 
INFO[0025] [cloud-init] * Applying /usr/lib/sysctl.d/99-protect-links.conf ... 
INFO[0025] [cloud-init] * Applying /etc/sysctl.d/99-sysctl.conf ... 
INFO[0025] [cloud-init] * Applying /etc/sysctl.conf ... 
INFO[0025] [cloud-init] kernel.apparmor_restrict_unprivileged_userns = 1 
INFO[0025] [cloud-init] net.core.default_qdisc = fq_codel 
INFO[0025] [cloud-init] kernel.printk = 4 4 1 7         
INFO[0025] [cloud-init] net.ipv6.conf.all.use_tempaddr = 2 
INFO[0025] [cloud-init] net.ipv6.conf.default.use_tempaddr = 2 
INFO[0025] [cloud-init] kernel.kptr_restrict = 1        
INFO[0025] [cloud-init] kernel.sysrq = 176              
INFO[0025] [cloud-init] vm.max_map_count = 1048576      
INFO[0025] [cloud-init] net.ipv4.conf.default.rp_filter = 2 
INFO[0025] [cloud-init] net.ipv4.conf.all.rp_filter = 2 
INFO[0025] [cloud-init] kernel.yama.ptrace_scope = 1    
INFO[0025] [cloud-init] vm.mmap_min_addr = 32768        
INFO[0025] [cloud-init] kernel.pid_max = 4194304        
INFO[0025] [cloud-init] net.ipv6.conf.all.use_tempaddr = 0 
INFO[0025] [cloud-init] net.ipv6.conf.default.use_tempaddr = 0 
INFO[0025] [cloud-init] kernel.unprivileged_userns_clone = 1 
INFO[0025] [cloud-init] net.ipv4.ping_group_range = 0 2147483647 
INFO[0025] [cloud-init] net.ipv4.ip_unprivileged_port_start = 0 
INFO[0025] [cloud-init] fs.protected_fifos = 1          
INFO[0025] [cloud-init] fs.protected_hardlinks = 1      
INFO[0025] [cloud-init] fs.protected_regular = 2        
INFO[0025] [cloud-init] fs.protected_symlinks = 1       
INFO[0025] [cloud-init] + subuid_begin=524288           
INFO[0025] [cloud-init] + [ 501 -ge 524288 ]            
INFO[0025] [cloud-init] + grep -qw olalekanodukoya /etc/subuid 
INFO[0025] [cloud-init] + echo olalekanodukoya:524288:1073741824 
INFO[0025] [cloud-init] + subuid_begin=524288           
INFO[0025] [cloud-init] + [ 501 -ge 524288 ]            
INFO[0025] [cloud-init] + grep -qw olalekanodukoya /etc/subgid 
INFO[0025] [cloud-init] + echo olalekanodukoya:524288:1073741824 
INFO[0025] [cloud-init] + systemctl start systemd-logind.service 
INFO[0025] [cloud-init] + loginctl enable-linger olalekanodukoya 
INFO[0025] [cloud-init] LIMA 2025-08-25T03:05:05+01:00| Executing /mnt/lima-cidata/boot/25-guestagent-base.sh 
INFO[0025] [cloud-init] + [ 9p = reverse-sshfs ]        
INFO[0025] [cloud-init] + install -m 755 /mnt/lima-cidata/lima-guestagent /usr/local/bin/lima-guestagent 
INFO[0025] [cloud-init] + [ -f /sbin/openrc-run ]       
INFO[0025] [cloud-init] + rm -f /home/olalekanodukoya.linux/.config/systemd/user/lima-guestagent.service 
INFO[0025] [cloud-init] + [ 0 != 0 ]                    
INFO[0025] [cloud-init] + [  !=  ]                      
INFO[0025] [cloud-init] + sudo /usr/local/bin/lima-guestagent install-systemd --debug=false 
INFO[0025] [cloud-init] time="2025-08-25T03:05:06+01:00" level=info msg="Written file \"/etc/systemd/system/lima-guestagent.service\"" 
INFO[0025] [cloud-init] time="2025-08-25T03:05:06+01:00" level=info msg="Executing: systemctl --system daemon-reload" 
INFO[0025] [cloud-init] time="2025-08-25T03:05:06+01:00" level=info msg="Executing: systemctl --system enable lima-guestagent.service" 
INFO[0025] [cloud-init] Created symlink /etc/systemd/system/multi-user.target.wants/lima-guestagent.service → /etc/systemd/system/lima-guestagent.service. 
INFO[0025] [cloud-init] time="2025-08-25T03:05:06+01:00" level=info msg="Executing: systemctl --system start lima-guestagent.service" 
INFO[0025] [cloud-init] time="2025-08-25T03:05:06+01:00" level=info msg="Executing: systemctl --system try-restart lima-guestagent.service" 
INFO[0025] [cloud-init] time="2025-08-25T03:05:06+01:00" level=info msg=Done 
INFO[0025] [cloud-init] LIMA 2025-08-25T03:05:06+01:00| Executing /mnt/lima-cidata/boot/30-install-packages.sh 
INFO[0025] [cloud-init] + INSTALL_IPTABLES=0            
INFO[0025] [cloud-init] + [  = 1 ]                      
INFO[0025] [cloud-init] + [  = 1 ]                      
INFO[0025] [cloud-init] + [ 56242 -ne 0 ]               
INFO[0025] [cloud-init] + INSTALL_IPTABLES=1            
INFO[0025] [cloud-init] + [ -d /mnt/lima-cidata/provision.dependency ] 
INFO[0025] [cloud-init] + [  = 1 ]                      
INFO[0025] [cloud-init] + REMOUNT_VIRTIOFS=0            
INFO[0025] [cloud-init] + command -v apt-get            
INFO[0025] [cloud-init] + head -c 4 /usr/bin/apt-get    
INFO[0025] [cloud-init] + grep -qP \x7fELF              
INFO[0025] [cloud-init] + pkgs=                         
INFO[0025] [cloud-init] + [ 9p = reverse-sshfs ]        
INFO[0025] [cloud-init] + [ 1 = 1 ]                     
INFO[0025] [cloud-init] + [ ! -e /usr/sbin/iptables ]   
INFO[0025] [cloud-init] + [  = 1 ]                      
INFO[0025] [cloud-init] + [ -n  ]                       
INFO[0025] [cloud-init] + [ 0 = 1 ]                     
INFO[0025] [cloud-init] LIMA 2025-08-25T03:05:06+01:00| Executing /mnt/lima-cidata/boot/35-setup-packages.sh 
INFO[0025] [cloud-init] + [ 9p = reverse-sshfs ]        
INFO[0025] [cloud-init] + SETUP_DNS=0                   
INFO[0025] [cloud-init] + [ -n 56242 ]                  
INFO[0025] [cloud-init] + [ 56242 -ne 0 ]               
INFO[0025] [cloud-init] + SETUP_DNS=1                   
INFO[0025] [cloud-init] + [ -n 61127 ]                  
INFO[0025] [cloud-init] + [ 61127 -ne 0 ]               
INFO[0025] [cloud-init] + SETUP_DNS=1                   
INFO[0025] [cloud-init] + [ 1 = 1 ]                     
INFO[0025] [cloud-init] + /mnt/lima-cidata/boot/09-host-dns-setup.sh 
INFO[0025] [cloud-init] + readonly chain=LIMADNS        
INFO[0025] [cloud-init] + command -v iptables           
INFO[0025] [cloud-init] + chain_exists                  
INFO[0025] [cloud-init] + iptables --table nat -n --list LIMADNS 
INFO[0025] [cloud-init] + iptables --table nat --flush LIMADNS 
INFO[0025] [cloud-init] + [ -n 56242 ]                  
INFO[0025] [cloud-init] + [ 56242 -ne 0 ]               
INFO[0025] [cloud-init] + iptables --table nat --append LIMADNS --destination 192.168.5.3 --protocol udp --dport 53 --jump DNAT --to-destination 192.168.5.2:56242 
INFO[0025] [cloud-init] + [ -n 61127 ]                  
INFO[0025] [cloud-init] + [ 61127 -ne 0 ]               
INFO[0025] [cloud-init] + iptables --table nat --append LIMADNS --destination 192.168.5.3 --protocol tcp --dport 53 --jump DNAT --to-destination 192.168.5.2:61127 
INFO[0025] [cloud-init] LIMA 2025-08-25T03:05:06+01:00| Executing /mnt/lima-cidata/boot/40-install-containerd.sh 
INFO[0025] [cloud-init] + : default                     
INFO[0025] [cloud-init] + : overlayfs                   
INFO[0025] [cloud-init] + '[' '' '!=' 1 ']'             
INFO[0025] [cloud-init] + '[' '' '!=' 1 ']'             
INFO[0025] [cloud-init] + exit 0                        
INFO[0025] [cloud-init] LIMA 2025-08-25T03:05:06+01:00| Exiting with code 0 
INFO[0025] [cloud-init] Cloud-init v. 25.1.2-0ubuntu0~24.04.1 finished at Mon, 25 Aug 2025 02:05:06 +0000. Datasource DataSourceNoCloud [seed=/dev/sr0].  Up 11.46 seconds 
INFO[0025] Cloud-init progress monitoring done.         
INFO[0025] READY. Run `limactl shell defaultz` to open the shell. 
```@norio-nomura: confirmed that `limitl start --progress` exits as expected on "Cloud-init","finished". 👍🏻 
I hope `ssh -F ... sudo tail -n +1 -f /var/log/cloud-init-output.log` process can be killed too without waiting 10 minutes timeout.@olamilekan000: Nice catch!

Solution patch:
diff --git a/cmd/limactl/clone.go b/cmd/limactl/clone.go
index 04060082..18e1c5a6 100644
--- a/cmd/limactl/clone.go
+++ b/cmd/limactl/clone.go
@@ -12,11 +12,12 @@ import (
 	"github.com/spf13/cobra"
 
 	"github.com/lima-vm/lima/v2/cmd/limactl/editflags"
+	"github.com/lima-vm/lima/v2/pkg/driverutil"
 	"github.com/lima-vm/lima/v2/pkg/instance"
+	"github.com/lima-vm/lima/v2/pkg/limatype/filenames"
 	"github.com/lima-vm/lima/v2/pkg/limayaml"
 	networks "github.com/lima-vm/lima/v2/pkg/networks/reconcile"
 	"github.com/lima-vm/lima/v2/pkg/store"
-	"github.com/lima-vm/lima/v2/pkg/store/filenames"
 	"github.com/lima-vm/lima/v2/pkg/yqutil"
 )
 
@@ -79,6 +80,9 @@ func cloneAction(cmd *cobra.Command, args []string) error {
 		if err != nil {
 			return err
 		}
+		if err := driverutil.ResolveVMType(ctx, y, filePath); err != nil {
+			return fmt.Errorf("failed to resolve vm for %q: %w", filePath, err)
+		}
 		if err := limayaml.Validate(y, true); err != nil {
 			return saveRejectedYAML(yBytes, err)
 		}
diff --git a/cmd/limactl/copy.go b/cmd/limactl/copy.go
index 6d84353b..9d7a0055 100644
--- a/cmd/limactl/copy.go
+++ b/cmd/limactl/copy.go
@@ -17,6 +17,7 @@ import (
 	"github.com/spf13/cobra"
 
 	"github.com/lima-vm/lima/v2/pkg/ioutilx"
+	"github.com/lima-vm/lima/v2/pkg/limatype"
 	"github.com/lima-vm/lima/v2/pkg/sshutil"
 	"github.com/lima-vm/lima/v2/pkg/store"
 )
@@ -63,7 +64,7 @@ func copyAction(cmd *cobra.Command, args []string) error {
 	if err != nil {
 		return err
 	}
-	instances := make(map[string]*store.Instance)
+	instances := make(map[string]*limatype.Instance)
 	scpFlags := []string{}
 	scpArgs := []string{}
 	debug, err := cmd.Flags().GetBool("debug")
@@ -114,7 +115,7 @@ func copyAction(cmd *cobra.Command, args []string) error {
 				}
 				return err
 			}
-			if inst.Status == store.StatusStopped {
+			if inst.Status == limatype.StatusStopped {
 				return fmt.Errorf("instance %q is stopped, run `limactl start %s` to start the instance", instName, instName)
 			}
 			if legacySSH {
diff --git a/cmd/limactl/disk.go b/cmd/limactl/disk.go
index 1f8d8e5b..acc78ce2 100644
--- a/cmd/limactl/disk.go
+++ b/cmd/limactl/disk.go
@@ -19,8 +19,9 @@ import (
 	"github.com/spf13/cobra"
 
 	"github.com/lima-vm/lima/v2/pkg/imgutil/proxyimgutil"
+	"github.com/lima-vm/lima/v2/pkg/limatype"
+	"github.com/lima-vm/lima/v2/pkg/limatype/filenames"
 	"github.com/lima-vm/lima/v2/pkg/store"
-	"github.com/lima-vm/lima/v2/pkg/store/filenames"
 )
 
 func newDiskCommand() *cobra.Command {
@@ -243,7 +244,7 @@ func diskDeleteAction(cmd *cobra.Command, args []string) error {
 	if err != nil {
 		return err
 	}
-	var instances []*store.Instance
+	var instances []*limatype.Instance
 	for _, instName := range instNames {
 		inst, err := store.Inspect(ctx, instName)
 		if err != nil {
@@ -344,7 +345,7 @@ func diskUnlockAction(cmd *cobra.Command, args []string) error {
 					diskName, disk.Instance, inst.Errors)
 				continue
 			}
-			if inst.Status == store.StatusRunning {
+			if inst.Status == limatype.StatusRunning {
 				logrus.Warnf("Cannot unlock disk %q used by running instance %q", diskName, disk.Instance)
 				continue
 			}
@@ -402,7 +403,7 @@ func diskResizeAction(cmd *cobra.Command, args []string) error {
 	if disk.Instance != "" {
 		inst, err := store.Inspect(ctx, disk.Instance)
 		if err == nil {
-			if inst.Status == store.StatusRunning {
+			if inst.Status == limatype.StatusRunning {
 				return fmt.Errorf("cannot resize disk %q used by running instance %q. Please stop the VM instance", diskName, disk.Instance)
 			}
 		}
diff --git a/cmd/limactl/edit.go b/cmd/limactl/edit.go
index 8db0d8f7..c503e54a 100644
--- a/cmd/limactl/edit.go
+++ b/cmd/limactl/edit.go
@@ -14,12 +14,15 @@ import (
 	"github.com/spf13/cobra"
 
 	"github.com/lima-vm/lima/v2/cmd/limactl/editflags"
+	"github.com/lima-vm/lima/v2/pkg/driverutil"
 	"github.com/lima-vm/lima/v2/pkg/editutil"
 	"github.com/lima-vm/lima/v2/pkg/instance"
+	"github.com/lima-vm/lima/v2/pkg/limatype"
+	"github.com/lima-vm/lima/v2/pkg/limatype/dirnames"
+	"github.com/lima-vm/lima/v2/pkg/limatype/filenames"
 	"github.com/lima-vm/lima/v2/pkg/limayaml"
 	networks "github.com/lima-vm/lima/v2/pkg/networks/reconcile"
 	"github.com/lima-vm/lima/v2/pkg/store"
-	"github.com/lima-vm/lima/v2/pkg/store/filenames"
 	"github.com/lima-vm/lima/v2/pkg/uiutil"
 	"github.com/lima-vm/lima/v2/pkg/yqutil"
 )
@@ -46,12 +49,12 @@ func editAction(cmd *cobra.Command, args []string) error {
 
 	var filePath string
 	var err error
-	var inst *store.Instance
+	var inst *limatype.Instance
 
 	if arg == "" {
 		arg = DefaultInstanceName
 	}
-	if err := store.ValidateInstName(arg); err == nil {
+	if err := dirnames.ValidateInstName(arg); err == nil {
 		inst, err = store.Inspect(ctx, arg)
 		if err != nil {
 			if errors.Is(err, os.ErrNotExist) {
@@ -59,7 +62,7 @@ func editAction(cmd *cobra.Command, args []string) error {
 			}
 			return err
 		}
-		if inst.Status == store.StatusRunning {
+		if inst.Status == limatype.StatusRunning {
 			return errors.New("cannot edit a running instance")
 		}
 		filePath = filepath.Join(inst.Dir, filenames.LimaYAML)
@@ -118,6 +121,9 @@ func editAction(cmd *cobra.Command, args []string) error {
 	if err != nil {
 		return err
 	}
+	if err := driverutil.ResolveVMType(ctx, y, filePath); err != nil {
+		return fmt.Errorf("failed to resolve vm for %q: %w", filePath, err)
+	}
 	if err := limayaml.Validate(y, true); err != nil {
 		return saveRejectedYAML(yBytes, err)
 	}
diff --git a/cmd/limactl/editflags/editflags.go b/cmd/limactl/editflags/editflags.go
index eba0042e..7a0f5a07 100644
--- a/cmd/limactl/editflags/editflags.go
+++ b/cmd/limactl/editflags/editflags.go
@@ -15,6 +15,8 @@ import (
 	"github.com/sirupsen/logrus"
 	"github.com/spf13/cobra"
 	flag "github.com/spf13/pflag"
+
+	"github.com/lima-vm/lima/v2/pkg/registry"
 )
 
 // RegisterEdit registers flags related to in-place YAML modification, for `limactl edit`.
@@ -75,6 +77,15 @@ func RegisterEdit(cmd *cobra.Command, commentPrefix string) {
 	_ = cmd.RegisterFlagCompletionFunc("disk", func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) {
 		return []string{"10", "30", "50", "100", "200"}, cobra.ShellCompDirectiveNoFileComp
 	})
+
+	flags.String("vm-type", "", commentPrefix+"Virtual machine type")
+	_ = cmd.RegisterFlagCompletionFunc("vm-type", func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) {
+		var drivers []string
+		for k := range registry.List() {
+			drivers = append(drivers, k)
+		}
+		return drivers, cobra.ShellCompDirectiveNoFileComp
+	})
 }
 
 // RegisterCreate registers flags related to in-place YAML modification, for `limactl create`.
@@ -92,11 +103,6 @@ func RegisterCreate(cmd *cobra.Command, commentPrefix string) {
 		return []string{"user", "system", "user+system", "none"}, cobra.ShellCompDirectiveNoFileComp
 	})
 
-	flags.String("vm-type", "", commentPrefix+"Virtual machine type (qemu, vz)") // colima-compatible
-	_ = cmd.RegisterFlagCompletionFunc("vm-type", func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) {
-		return []string{"qemu", "vz"}, cobra.ShellCompDirectiveNoFileComp
-	})
-
 	flags.Bool("plain", false, commentPrefix+"Plain mode. Disables mounts, port forwarding, containerd, etc.")
 }
 
@@ -177,6 +183,7 @@ func YQExpressions(flags *flag.FlagSet, newInstance bool) ([]string, error) {
 			false,
 		},
 		{"mount-type", d(".mountType = %q"), false, false},
+		{"vm-type", d(".vmType = %q"), false, false},
 		{"mount-inotify", d(".mountInotify = %s"), false, true},
 		{"mount-writable", d(".mounts[].writable = %s"), false, false},
 		{
@@ -262,7 +269,6 @@ func YQExpressions(flags *flag.FlagSet, newInstance bool) ([]string, error) {
 			false,
 		},
 		{"disk", d(".disk= \"%sGiB\""), false, false},
-		{"vm-type", d(".vmType = %q"), true, false},
 		{"plain", d(".plain = %s"), true, false},
 	}
 	var exprs []string
diff --git a/cmd/limactl/factory-reset.go b/cmd/limactl/factory-reset.go
index b774aef1..58e6d1af 100644
--- a/cmd/limactl/factory-reset.go
+++ b/cmd/limactl/factory-reset.go
@@ -14,8 +14,8 @@ import (
 
 	"github.com/lima-vm/lima/v2/pkg/cidata"
 	"github.com/lima-vm/lima/v2/pkg/instance"
+	"github.com/lima-vm/lima/v2/pkg/limatype/filenames"
 	"github.com/lima-vm/lima/v2/pkg/store"
-	"github.com/lima-vm/lima/v2/pkg/store/filenames"
 )
 
 func newFactoryResetCommand() *cobra.Command {
diff --git a/cmd/limactl/genschema.go b/cmd/limactl/genschema.go
index 0739023e..d69d74cc 100644
--- a/cmd/limactl/genschema.go
+++ b/cmd/limactl/genschema.go
@@ -15,7 +15,7 @@ import (
 	orderedmap "github.com/wk8/go-ordered-map/v2"
 
 	"github.com/lima-vm/lima/v2/pkg/jsonschemautil"
-	"github.com/lima-vm/lima/v2/pkg/limayaml"
+	"github.com/lima-vm/lima/v2/pkg/limatype"
 )
 
 func newGenSchemaCommand() *cobra.Command {
@@ -52,7 +52,7 @@ func genschemaAction(cmd *cobra.Command, args []string) error {
 		return err
 	}
 
-	schema := jsonschema.Reflect(&limayaml.LimaYAML{})
+	schema := jsonschema.Reflect(&limatype.LimaYAML{})
 	// allow Disk to be either string (name) or object (struct)
 	schema.Definitions["Disk"].Type = "" // was: "object"
 	schema.Definitions["Disk"].OneOf = []*jsonschema.Schema{
@@ -72,10 +72,10 @@ func genschemaAction(cmd *cobra.Command, args []string) error {
 		{Type: "object"},
 	}
 	properties := schema.Definitions["LimaYAML"].Properties
-	getProp(properties, "os").Enum = toAny(limayaml.OSTypes)
-	getProp(properties, "arch").Enum = toAny(limayaml.ArchTypes)
-	getProp(properties, "mountType").Enum = toAny(limayaml.MountTypes)
-	getProp(properties, "vmType").Enum = toAny(limayaml.VMTypes)
+	getProp(properties, "os").Enum = toAny(limatype.OSTypes)
+	getProp(properties, "arch").Enum = toAny(limatype.ArchTypes)
+	getProp(properties, "mountType").Enum = toAny(limatype.MountTypes)
+	getProp(properties, "vmType").Enum = toAny(limatype.VMTypes)
 	j, err := json.MarshalIndent(schema, "", "    ")
 	if err != nil {
 		return err
diff --git a/cmd/limactl/list.go b/cmd/limactl/list.go
index 97c6afa5..791d9f01 100644
--- a/cmd/limactl/list.go
+++ b/cmd/limactl/list.go
@@ -16,6 +16,7 @@ import (
 	"github.com/sirupsen/logrus"
 	"github.com/spf13/cobra"
 
+	"github.com/lima-vm/lima/v2/pkg/limatype"
 	"github.com/lima-vm/lima/v2/pkg/store"
 )
 
@@ -172,7 +173,7 @@ func listAction(cmd *cobra.Command, args []string) error {
 	}
 
 	// get the state and config for all the requested instances
-	var instances []*store.Instance
+	var instances []*limatype.Instance
 	for _, instanceName := range instanceNames {
 		instance, err := store.Inspect(ctx, instanceName)
 		if err != nil {
diff --git a/cmd/limactl/main.go b/cmd/limactl/main.go
index 867d3036..d5a51ea4 100644
--- a/cmd/limactl/main.go
+++ b/cmd/limactl/main.go
@@ -20,8 +20,8 @@ import (
 	"github.com/lima-vm/lima/v2/pkg/debugutil"
 	"github.com/lima-vm/lima/v2/pkg/driver/external/server"
 	"github.com/lima-vm/lima/v2/pkg/fsutil"
+	"github.com/lima-vm/lima/v2/pkg/limatype/dirnames"
 	"github.com/lima-vm/lima/v2/pkg/osutil"
-	"github.com/lima-vm/lima/v2/pkg/store/dirnames"
 	"github.com/lima-vm/lima/v2/pkg/version"
 )
 
@@ -44,6 +44,7 @@ func main() {
 	}
 	rootCmd := newApp()
 	if err := executeWithPluginSupport(rootCmd, os.Args[1:]); err != nil {
+		server.StopAllExternalDrivers()
 		handleExitCoder(err)
 		logrus.Fatal(err)
 	}
diff --git a/cmd/limactl/prune.go b/cmd/limactl/prune.go
index a17f717e..ee1196e9 100644
--- a/cmd/limactl/prune.go
+++ b/cmd/limactl/prune.go
@@ -5,6 +5,7 @@ package main
 
 import (
 	"context"
+	"fmt"
 	"maps"
 	"os"
 
@@ -12,6 +13,8 @@ import (
 	"github.com/spf13/cobra"
 
 	"github.com/lima-vm/lima/v2/pkg/downloader"
+	"github.com/lima-vm/lima/v2/pkg/driverutil"
+	"github.com/lima-vm/lima/v2/pkg/limatype"
 	"github.com/lima-vm/lima/v2/pkg/limayaml"
 	"github.com/lima-vm/lima/v2/pkg/store"
 	"github.com/lima-vm/lima/v2/pkg/templatestore"
@@ -64,8 +67,8 @@ func pruneAction(cmd *cobra.Command, _ []string) error {
 	return nil
 }
 
-func knownLocations(ctx context.Context) (map[string]limayaml.File, error) {
-	locations := make(map[string]limayaml.File)
+func knownLocations(ctx context.Context) (map[string]limatype.File, error) {
+	locations := make(map[string]limatype.File)
 
 	// Collect locations from instances
 	instances, err := store.Instances()
@@ -94,13 +97,16 @@ func knownLocations(ctx context.Context) (map[string]limayaml.File, error) {
 		if err != nil {
 			return nil, err
 		}
+		if err := driverutil.ResolveVMType(ctx, y, t.Name); err != nil {
+			return nil, fmt.Errorf("failed to resolve vm for %q: %w", t.Name, err)
+		}
 		maps.Copy(locations, locationsFromLimaYAML(y))
 	}
 	return locations, nil
 }
 
-func locationsFromLimaYAML(y *limayaml.LimaYAML) map[string]limayaml.File {
-	locations := make(map[string]limayaml.File)
+func locationsFromLimaYAML(y *limatype.LimaYAML) map[string]limatype.File {
+	locations := make(map[string]limatype.File)
 	for _, f := range y.Images {
 		locations[downloader.CacheKey(f.Location)] = f.File
 		if f.Kernel != nil {
diff --git a/cmd/limactl/restart.go b/cmd/limactl/restart.go
index a46bd09e..7416b84e 100644
--- a/cmd/limactl/restart.go
+++ b/cmd/limactl/restart.go
@@ -21,6 +21,7 @@ func newRestartCommand() *cobra.Command {
 	}
 
 	restartCmd.Flags().BoolP("force", "f", false, "Force stop and restart the instance")
+	restartCmd.Flags().Bool("progress", false, "Show provision script progress by tailing cloud-init logs")
 	return restartCmd
 }
 
@@ -40,12 +41,16 @@ func restartAction(cmd *cobra.Command, args []string) error {
 	if err != nil {
 		return err
 	}
+	progress, err := cmd.Flags().GetBool("progress")
+	if err != nil {
+		return err
+	}
 
 	if force {
-		return instance.RestartForcibly(ctx, inst)
+		return instance.RestartForcibly(ctx, inst, progress)
 	}
 
-	return instance.Restart(ctx, inst)
+	return instance.Restart(ctx, inst, progress)
 }
 
 func restartBashComplete(cmd *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
diff --git a/cmd/limactl/shell.go b/cmd/limactl/shell.go
index 3019178f..7fe79405 100644
--- a/cmd/limactl/shell.go
+++ b/cmd/limactl/shell.go
@@ -23,7 +23,7 @@ import (
 	"github.com/lima-vm/lima/v2/pkg/envutil"
 	"github.com/lima-vm/lima/v2/pkg/instance"
 	"github.com/lima-vm/lima/v2/pkg/ioutilx"
-	"github.com/lima-vm/lima/v2/pkg/limayaml"
+	"github.com/lima-vm/lima/v2/pkg/limatype"
 	networks "github.com/lima-vm/lima/v2/pkg/networks/reconcile"
 	"github.com/lima-vm/lima/v2/pkg/sshutil"
 	"github.com/lima-vm/lima/v2/pkg/store"
@@ -36,6 +36,11 @@ lima command is provided as an alias for limactl shell $LIMA_INSTANCE. $LIMA_INS
 By default, the first 'ssh' executable found in the host's PATH is used to connect to the Lima instance.
 A custom ssh alias can be used instead by setting the $` + sshutil.EnvShellSSH + ` environment variable.
 
+Environment Variables:
+  --preserve-env: Propagates host environment variables to the guest instance.
+                  Use LIMA_SHELLENV_ALLOW to specify which variables to allow.
+                  Use LIMA_SHELLENV_BLOCK to specify which variables to block (extends default blocklist with +).
+
 Hint: try --debug to show the detailed logs, if it seems hanging (mostly due to some SSH issue).
 `
 
@@ -86,7 +91,7 @@ func shellAction(cmd *cobra.Command, args []string) error {
 		}
 		return err
 	}
-	if inst.Status == store.StatusStopped {
+	if inst.Status == limatype.StatusStopped {
 		startNow, err := askWhetherToStart()
 		if err != nil {
 			return err
@@ -142,7 +147,7 @@ func shellAction(cmd *cobra.Command, args []string) error {
 	if workDir != "" {
 		changeDirCmd = fmt.Sprintf("cd %s || exit 1", shellescape.Quote(workDir))
 		// FIXME: check whether y.Mounts contains the home, not just len > 0
-	} else if len(inst.Config.Mounts) > 0 || inst.VMType == limayaml.WSL2 {
+	} else if len(inst.Config.Mounts) > 0 || inst.VMType == limatype.WSL2 {
 		hostCurrentDir, err := os.Getwd()
 		if err == nil && runtime.GOOS == "windows" {
 			hostCurrentDir, err = mountDirFromWindowsDir(ctx, inst, hostCurrentDir)
@@ -265,8 +270,8 @@ func shellAction(cmd *cobra.Command, args []string) error {
 	return sshCmd.Run()
 }
 
-func mountDirFromWindowsDir(ctx context.Context, inst *store.Instance, dir string) (string, error) {
-	if inst.VMType == limayaml.WSL2 {
+func mountDirFromWindowsDir(ctx context.Context, inst *limatype.Instance, dir string) (string, error) {
+	if inst.VMType == limatype.WSL2 {
 		distroName := "lima-" + inst.Name
 		return ioutilx.WindowsSubsystemPathForLinux(ctx, dir, distroName)
 	}
diff --git a/cmd/limactl/show-ssh.go b/cmd/limactl/show-ssh.go
index ff53dff7..ecaf98e4 100644
--- a/cmd/limactl/show-ssh.go
+++ b/cmd/limactl/show-ssh.go
@@ -13,10 +13,10 @@ import (
 	"github.com/sirupsen/logrus"
 	"github.com/spf13/cobra"
 
+	"github.com/lima-vm/lima/v2/pkg/limatype/dirnames"
+	"github.com/lima-vm/lima/v2/pkg/limatype/filenames"
 	"github.com/lima-vm/lima/v2/pkg/sshutil"
 	"github.com/lima-vm/lima/v2/pkg/store"
-	"github.com/lima-vm/lima/v2/pkg/store/dirnames"
-	"github.com/lima-vm/lima/v2/pkg/store/filenames"
 )
 
 const showSSHExample = `
diff --git a/cmd/limactl/start.go b/cmd/limactl/start.go
index 4aaf6233..4b6c2dc0 100644
--- a/cmd/limactl/start.go
+++ b/cmd/limactl/start.go
@@ -17,14 +17,17 @@ import (
 	"github.com/spf13/pflag"
 
 	"github.com/lima-vm/lima/v2/cmd/limactl/editflags"
+	"github.com/lima-vm/lima/v2/pkg/driverutil"
 	"github.com/lima-vm/lima/v2/pkg/editutil"
 	"github.com/lima-vm/lima/v2/pkg/instance"
 	"github.com/lima-vm/lima/v2/pkg/limatmpl"
+	"github.com/lima-vm/lima/v2/pkg/limatype"
+	"github.com/lima-vm/lima/v2/pkg/limatype/dirnames"
+	"github.com/lima-vm/lima/v2/pkg/limatype/filenames"
 	"github.com/lima-vm/lima/v2/pkg/limayaml"
 	networks "github.com/lima-vm/lima/v2/pkg/networks/reconcile"
 	"github.com/lima-vm/lima/v2/pkg/registry"
 	"github.com/lima-vm/lima/v2/pkg/store"
-	"github.com/lima-vm/lima/v2/pkg/store/filenames"
 	"github.com/lima-vm/lima/v2/pkg/templatestore"
 	"github.com/lima-vm/lima/v2/pkg/uiutil"
 	"github.com/lima-vm/lima/v2/pkg/yqutil"
@@ -203,7 +206,7 @@ func printGlobalFlags(cmd *cobra.Command) {
 	}
 }
 
-func loadOrCreateInstance(cmd *cobra.Command, args []string, createOnly bool) (*store.Instance, error) {
+func loadOrCreateInstance(cmd *cobra.Command, args []string, createOnly bool) (*limatype.Instance, error) {
 	ctx := cmd.Context()
 	var arg string // can be empty
 	if len(args) > 0 {
@@ -223,7 +226,7 @@ func loadOrCreateInstance(cmd *cobra.Command, args []string, createOnly bool) (*
 		return nil, err
 	}
 	if name != "" {
-		err := store.ValidateInstName(name)
+		err := dirnames.ValidateInstName(name)
 		if err != nil {
 			return nil, err
 		}
@@ -258,7 +261,7 @@ func loadOrCreateInstance(cmd *cobra.Command, args []string, createOnly bool) (*
 		tty = false
 	}
 	var tmpl *limatmpl.Template
-	if err := store.ValidateInstName(arg); arg == "" || err == nil {
+	if err := dirnames.ValidateInstName(arg); arg == "" || err == nil {
 		tmpl = &limatmpl.Template{Name: name}
 		if arg == "" {
 			if name == "" {
@@ -313,7 +316,7 @@ func loadOrCreateInstance(cmd *cobra.Command, args []string, createOnly bool) (*
 			if _, err := store.Inspect(ctx, tmpl.Name); err == nil {
 				return nil, fmt.Errorf("instance %q already exists", tmpl.Name)
 			}
-		} else if err := store.ValidateInstName(tmpl.Name); err != nil {
+		} else if err := dirnames.ValidateInstName(tmpl.Name); err != nil {
 			return nil, err
 		}
 	}
@@ -342,7 +345,7 @@ func loadOrCreateInstance(cmd *cobra.Command, args []string, createOnly bool) (*
 	return instance.Create(cmd.Context(), tmpl.Name, tmpl.Bytes, saveBrokenYAML)
 }
 
-func applyYQExpressionToExistingInstance(ctx context.Context, inst *store.Instance, yq string) (*store.Instance, error) {
+func applyYQExpressionToExistingInstance(ctx context.Context, inst *limatype.Instance, yq string) (*limatype.Instance, error) {
 	if strings.TrimSpace(yq) == "" {
 		return inst, nil
 	}
@@ -360,6 +363,9 @@ func applyYQExpressionToExistingInstance(ctx context.Context, inst *store.Instan
 	if err != nil {
 		return nil, err
 	}
+	if err := driverutil.ResolveVMType(ctx, y, filePath); err != nil {
+		return nil, fmt.Errorf("failed to resolve vm for %q: %w", filePath, err)
+	}
 	if err := limayaml.Validate(y, true); err != nil {
 		rejectedYAML := "lima.REJECTED.yaml"
 		if writeErr := os.WriteFile(rejectedYAML, yBytes, 0o644); writeErr != nil {
@@ -563,15 +569,15 @@ func startAction(cmd *cobra.Command, args []string) error {
 		return fmt.Errorf("errors inspecting instance: %+v", inst.Errors)
 	}
 	switch inst.Status {
-	case store.StatusRunning:
+	case limatype.StatusRunning:
 		logrus.Infof("The instance %q is already running. Run `%s` to open the shell.",
 			inst.Name, instance.LimactlShellCmd(inst.Name))
 		// Not an error
 		return nil
-	case store.StatusStopped:
+	case limatype.StatusStopped:
 		// NOP
 	default:
-		logrus.Warnf("expected status %q, got %q", store.StatusStopped, inst.Status)
+		logrus.Warnf("expected status %q, got %q", limatype.StatusStopped, inst.Status)
 	}
 	ctx := cmd.Context()
 	err = networks.Reconcile(ctx, inst.Name)
diff --git a/cmd/limactl/template.go b/cmd/limactl/template.go
index 6b8eb367..8880da9e 100644
--- a/cmd/limactl/template.go
+++ b/cmd/limactl/template.go
@@ -13,9 +13,10 @@ import (
 	"github.com/sirupsen/logrus"
 	"github.com/spf13/cobra"
 
+	"github.com/lima-vm/lima/v2/pkg/driverutil"
 	"github.com/lima-vm/lima/v2/pkg/limatmpl"
+	"github.com/lima-vm/lima/v2/pkg/limatype/dirnames"
 	"github.com/lima-vm/lima/v2/pkg/limayaml"
-	"github.com/lima-vm/lima/v2/pkg/store/dirnames"
 	"github.com/lima-vm/lima/v2/pkg/yqutil"
 )
 
@@ -87,6 +88,10 @@ func fillDefaults(ctx context.Context, tmpl *limatmpl.Template) error {
 	if err == nil {
 		tmpl.Bytes, err = limayaml.Marshal(tmpl.Config, false)
 	}
+	if err := driverutil.ResolveVMType(ctx, tmpl.Config, filePath); err != nil {
+		logrus.Warnf("failed to resolve VM type for %q: %v", filePath, err)
+		return nil
+	}
 	return err
 }
 
@@ -246,6 +251,10 @@ func templateValidateAction(cmd *cobra.Command, args []string) error {
 		if err != nil {
 			return err
 		}
+		if err := driverutil.ResolveVMType(ctx, y, filePath); err != nil {
+			logrus.Warnf("failed to resolve VM type for %q: %v", filePath, err)
+			return nil
+		}
 		if err := limayaml.Validate(y, false); err != nil {
 			return fmt.Errorf("failed to validate YAML file %q: %w", arg, err)
 		}
diff --git a/cmd/limactl/tunnel.go b/cmd/limactl/tunnel.go
index 43f3555d..68b16cbf 100644
--- a/cmd/limactl/tunnel.go
+++ b/cmd/limactl/tunnel.go
@@ -15,6 +15,7 @@ import (
 	"github.com/spf13/cobra"
 
 	"github.com/lima-vm/lima/v2/pkg/freeport"
+	"github.com/lima-vm/lima/v2/pkg/limatype"
 	"github.com/lima-vm/lima/v2/pkg/sshutil"
 	"github.com/lima-vm/lima/v2/pkg/store"
 )
@@ -72,7 +73,7 @@ func tunnelAction(cmd *cobra.Command, args []string) error {
 		}
 		return err
 	}
-	if inst.Status == store.StatusStopped {
+	if inst.Status == limatype.StatusStopped {
 		return fmt.Errorf("instance %q is stopped, run `limactl start %s` to start the instance", instName, instName)
 	}
 
diff --git a/cmd/nerdctl.lima b/cmd/nerdctl.lima
index b398e075..9a5205a6 100755
--- a/cmd/nerdctl.lima
+++ b/cmd/nerdctl.lima
@@ -1,3 +1,4 @@
 #!/bin/sh
 set -eu
+# Use --preserve-env to pass through environment variables from host machine into guest instance
 exec limactl shell --preserve-env default nerdctl "$@"
diff --git a/hack/test-preserve-env.sh b/hack/test-preserve-env.sh
new file mode 100755
index 00000000..20959107
--- /dev/null
+++ b/hack/test-preserve-env.sh
@@ -0,0 +1,94 @@
+#!/usr/bin/env bash
+
+# SPDX-FileCopyrightText: Copyright The Lima Authors
+# SPDX-License-Identifier: Apache-2.0
+
+set -eu -o pipefail
+
+scriptdir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+# shellcheck source=common.inc.sh
+source "${scriptdir}/common.inc.sh"
+
+if [ "$#" -ne 1 ]; then
+	ERROR "Usage: $0 NAME"
+	exit 1
+fi
+
+NAME="$1"
+
+INFO "Testing --preserve-env flag"
+
+# Environment variable propagation with --preserve-env
+INFO "=== Environment variable propagation with --preserve-env ==="
+test_var_name="LIMA_TEST_PRESERVE_ENV_VAR"
+test_var_value="test-value-${RANDOM}"
+
+got="$(LIMA_SHELLENV_ALLOW="LIMA_TEST_*" LIMA_TEST_PRESERVE_ENV_VAR="$test_var_value" limactl shell --preserve-env "$NAME" printenv "$test_var_name" 2>/dev/null || echo "NOT_FOUND")"
+INFO "$test_var_name: expected=${test_var_value}, got=${got}"
+if [ "$got" != "$test_var_value" ]; then
+	ERROR "Environment variable was not propagated with --preserve-env"
+	exit 1
+fi
+
+# Clean up before next test
+unset LIMA_TEST_PRESERVE_ENV_VAR
+unset LIMA_SHELLENV_ALLOW
+
+# Environment variable NOT propagated without --preserve-env
+INFO "=== Environment variable NOT propagated without --preserve-env ==="
+got_without_flag="$(LIMA_TEST_PRESERVE_ENV_VAR="$test_var_value" limactl shell "$NAME" printenv "$test_var_name" 2>/dev/null || echo "NOT_FOUND")"
+INFO "$test_var_name without --preserve-env: got=${got_without_flag}"
+if [ "$got_without_flag" != "NOT_FOUND" ]; then
+	ERROR "Environment variable was unexpectedly propagated without --preserve-env"
+	exit 1
+fi
+
+# Blocked environment variables should not be propagated even with --preserve-env
+INFO "=== Blocked environment variables are not propagated with --preserve-env ==="
+blocked_var_name="HOME"
+fake_home="/tmp/fake-home-${RANDOM}"
+got_blocked="$(HOME="$fake_home" limactl shell --preserve-env "$NAME" printenv "$blocked_var_name" 2>/dev/null || echo "NOT_FOUND")"
+INFO "$blocked_var_name: host=${fake_home}, guest=${got_blocked}"
+if [ "$got_blocked" = "$fake_home" ]; then
+	ERROR "Blocked environment variable $blocked_var_name was propagated"
+	exit 1
+fi
+
+# LIMA_SHELLENV_BLOCK functionality
+INFO "=== LIMA_SHELLENV_BLOCK with custom pattern ==="
+custom_test_var="LIMA_TEST_CUSTOM_BLOCK"
+custom_test_value="should-be-blocked"
+got_custom_blocked="$(LIMA_SHELLENV_BLOCK="+LIMA_TEST_CUSTOM_*" LIMA_TEST_CUSTOM_BLOCK="$custom_test_value" limactl shell --preserve-env "$NAME" printenv "$custom_test_var" 2>/dev/null || echo "NOT_FOUND")"
+INFO "$custom_test_var with LIMA_SHELLENV_BLOCK: got=${got_custom_blocked}"
+if [ "$got_custom_blocked" != "NOT_FOUND" ]; then
+	ERROR "Custom blocked environment variable was propagated"
+	exit 1
+fi
+
+# Clean up before next test
+unset LIMA_TEST_CUSTOM_BLOCK 2>/dev/null || true
+unset LIMA_SHELLENV_BLOCK 2>/dev/null || true
+
+# LIMA_SHELLENV_ALLOW functionality
+INFO "=== LIMA_SHELLENV_ALLOW with custom pattern ==="
+allow_test_var="LIMA_TEST_ALLOW_VAR"
+allow_test_value="should-be-allowed"
+got_allowed="$(LIMA_SHELLENV_ALLOW="LIMA_TEST_ALLOW_*" LIMA_TEST_ALLOW_VAR="$allow_test_value" limactl shell --preserve-env "$NAME" printenv "$allow_test_var" 2>/dev/null || echo "NOT_FOUND")"
+INFO "$allow_test_var with LIMA_SHELLENV_ALLOW: got=${got_allowed}"
+if [ "$got_allowed" != "$allow_test_value" ]; then
+	ERROR "Allowed environment variable was not propagated"
+	exit 1
+fi
+
+# Non-allowed variables are blocked when LIMA_SHELLENV_ALLOW is set
+INFO "=== Non-allowed variables are blocked when LIMA_SHELLENV_ALLOW is set ==="
+other_test_var="LIMA_TEST_OTHER_VAR"
+other_test_value="should-be-blocked"
+got_other="$(LIMA_SHELLENV_ALLOW="LIMA_TEST_ALLOW_*" LIMA_TEST_OTHER_VAR="$other_test_value" limactl shell --preserve-env "$NAME" printenv "$other_test_var" 2>/dev/null || echo "NOT_FOUND")"
+INFO "$other_test_var with LIMA_SHELLENV_ALLOW (should be blocked): got=${got_other}"
+if [ "$got_other" != "NOT_FOUND" ]; then
+	ERROR "Non-allowed environment variable was propagated when LIMA_SHELLENV_ALLOW was set"
+	exit 1
+fi
+
+INFO "All --preserve-env tests passed"
diff --git a/hack/test-templates.sh b/hack/test-templates.sh
index 58158a41..f01d249e 100755
--- a/hack/test-templates.sh
+++ b/hack/test-templates.sh
@@ -58,6 +58,7 @@ declare -A CHECKS=(
 	["provision-data"]=""
 	["param-env-variables"]=""
 	["set-user"]=""
+	["preserve-env"]="1"
 )
 
 case "$NAME" in
@@ -276,6 +277,10 @@ if [[ -n ${CHECKS["mount-home"]} ]]; then
 	"${scriptdir}"/test-mount-home.sh "$NAME"
 fi
 
+if [[ -n ${CHECKS["preserve-env"]} ]]; then
+	"${scriptdir}"/test-preserve-env.sh "$NAME"
+fi
+
 # Use GHCR to avoid hitting Docker Hub rate limit
 nginx_image="ghcr.io/stargz-containers/nginx:1.19-alpine-org"
 alpine_image="ghcr.io/containerd/alpine:3.14.0"
diff --git a/hack/test-upgrade.sh b/hack/test-upgrade.sh
index 696295f6..e9be6c15 100755
--- a/hack/test-upgrade.sh
+++ b/hack/test-upgrade.sh
@@ -95,8 +95,11 @@ INFO "==========================================================================
 INFO "Installing the new Lima ${NEWVER}"
 install_lima "${NEWVER}"
 
+INFO "Editing the instance to specify vm-type as qemu explicitly"
+limactl edit --vm-type=qemu "${LIMA_INSTANCE}"
+
 INFO "Restarting the instance"
-limactl start --tty=false "${LIMA_INSTANCE}" || show_lima_log
+limactl start --tty=false --vm-type=qemu "${LIMA_INSTANCE}" || show_lima_log
 lima nerdctl info
 
 INFO "Confirming that the host filesystem is still mounted"
diff --git a/hack/tools/go.mod b/hack/tools/go.mod
index c28ba0c2..f986ad7f 100644
--- a/hack/tools/go.mod
+++ b/hack/tools/go.mod
@@ -15,7 +15,7 @@ tool (
 require (
 	github.com/containerd/ltag v0.3.0
 	github.com/golangci/golangci-lint/v2 v2.4.0
-	github.com/yoheimuta/protolint v0.56.3
+	github.com/yoheimuta/protolint v0.56.4
 	google.golang.org/grpc v1.75.0
 	google.golang.org/protobuf v1.36.8
 	mvdan.cc/sh/v3 v3.12.0
diff --git a/hack/tools/go.sum b/hack/tools/go.sum
index ac45ef88..319e6e04 100644
--- a/hack/tools/go.sum
+++ b/hack/tools/go.sum
@@ -632,8 +632,8 @@ github.com/ykadowak/zerologlint v0.1.5 h1:Gy/fMz1dFQN9JZTPjv1hxEk+sRWm05row04Yoo
 github.com/ykadowak/zerologlint v0.1.5/go.mod h1:KaUskqF3e/v59oPmdq1U1DnKcuHokl2/K1U4pmIELKg=
 github.com/yoheimuta/go-protoparser/v4 v4.14.2 h1:/P/LlX1CF9NaTWEltGcIZVvNlPbhABuAnBtAWpb3+74=
 github.com/yoheimuta/go-protoparser/v4 v4.14.2/go.mod h1:AHNNnSWnb0UoL4QgHPiOAg2BniQceFscPI5X/BZNHl8=
-github.com/yoheimuta/protolint v0.56.3 h1:BUe3HFUHNsindB+UIRk9m/tn8nuKCqCdNkF2Nt01S9Y=
-github.com/yoheimuta/protolint v0.56.3/go.mod h1:XrnOc0O5mckLR1GAOjqMPdb3R3ZEfLkMpLoq5RxxoG0=
+github.com/yoheimuta/protolint v0.56.4 h1:FWvXjVNRaKJWJFxsnilRZhfQ4tc3KS8VVGWecxnLXLo=
+github.com/yoheimuta/protolint v0.56.4/go.mod h1:XrnOc0O5mckLR1GAOjqMPdb3R3ZEfLkMpLoq5RxxoG0=
 github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
diff --git a/pkg/cidata/cidata.go b/pkg/cidata/cidata.go
index 0a8c57aa..7e633018 100644
--- a/pkg/cidata/cidata.go
+++ b/pkg/cidata/cidata.go
@@ -24,15 +24,17 @@ import (
 	"github.com/sirupsen/logrus"
 
 	"github.com/lima-vm/lima/v2/pkg/debugutil"
+	"github.com/lima-vm/lima/v2/pkg/driver"
 	"github.com/lima-vm/lima/v2/pkg/instance/hostname"
 	"github.com/lima-vm/lima/v2/pkg/iso9660util"
+	"github.com/lima-vm/lima/v2/pkg/limatype"
+	"github.com/lima-vm/lima/v2/pkg/limatype/filenames"
 	"github.com/lima-vm/lima/v2/pkg/limayaml"
 	"github.com/lima-vm/lima/v2/pkg/localpathutil"
 	"github.com/lima-vm/lima/v2/pkg/networks"
 	"github.com/lima-vm/lima/v2/pkg/networks/usernet"
 	"github.com/lima-vm/lima/v2/pkg/osutil"
 	"github.com/lima-vm/lima/v2/pkg/sshutil"
-	"github.com/lima-vm/lima/v2/pkg/store/filenames"
 )
 
 var netLookupIP = func(host string) []net.IP {
@@ -116,7 +118,7 @@ func setupEnv(instConfigEnv map[string]string, propagateProxyEnv bool, slirpGate
 	return env, nil
 }
 
-func templateArgs(ctx context.Context, bootScripts bool, instDir, name string, instConfig *limayaml.LimaYAML, udpDNSLocalPort, tcpDNSLocalPort, vsockPort int, virtioPort string) (*TemplateArgs, error) {
+func templateArgs(ctx context.Context, bootScripts bool, instDir, name string, instConfig *limatype.LimaYAML, udpDNSLocalPort, tcpDNSLocalPort, vsockPort int, virtioPort string) (*TemplateArgs, error) {
 	if err := limayaml.Validate(instConfig, false); err != nil {
 		return nil, err
 	}
@@ -136,14 +138,19 @@ func templateArgs(ctx context.Context, bootScripts bool, instDir, name string, i
 		Containerd:         Containerd{System: *instConfig.Containerd.System, User: *instConfig.Containerd.User, Archive: archive},
 		SlirpNICName:       networks.SlirpNICName,
 
-		RosettaEnabled: *instConfig.Rosetta.Enabled,
-		RosettaBinFmt:  *instConfig.Rosetta.BinFmt,
-		VMType:         *instConfig.VMType,
-		VSockPort:      vsockPort,
-		VirtioPort:     virtioPort,
-		Plain:          *instConfig.Plain,
-		TimeZone:       *instConfig.TimeZone,
-		Param:          instConfig.Param,
+		VMType:     *instConfig.VMType,
+		VSockPort:  vsockPort,
+		VirtioPort: virtioPort,
+		Plain:      *instConfig.Plain,
+		TimeZone:   *instConfig.TimeZone,
+		Param:      instConfig.Param,
+	}
+
+	if instConfig.VMOpts.VZ.Rosetta.Enabled != nil {
+		args.RosettaEnabled = *instConfig.VMOpts.VZ.Rosetta.Enabled
+	}
+	if instConfig.VMOpts.VZ.Rosetta.BinFmt != nil {
+		args.RosettaEnabled = *instConfig.VMOpts.VZ.Rosetta.BinFmt
 	}
 
 	firstUsernetIndex := limayaml.FirstUsernetIndex(instConfig)
@@ -164,7 +171,7 @@ func templateArgs(ctx context.Context, bootScripts bool, instDir, name string, i
 			return nil, err
 		}
 		args.SlirpGateway = usernet.GatewayIP(subnet)
-		if *instConfig.VMType == limayaml.VZ {
+		if *instConfig.VMType == limatype.VZ {
 			args.SlirpDNS = usernet.GatewayIP(subnet)
 		} else {
 			args.SlirpDNS = usernet.DNSIP(subnet)
@@ -188,11 +195,11 @@ func templateArgs(ctx context.Context, bootScripts bool, instDir, name string, i
 
 	var fstype string
 	switch *instConfig.MountType {
-	case limayaml.REVSSHFS:
+	case limatype.REVSSHFS:
 		fstype = "sshfs"
-	case limayaml.NINEP:
+	case limatype.NINEP:
 		fstype = "9p"
-	case limayaml.VIRTIOFS:
+	case limatype.VIRTIOFS:
 		fstype = "virtiofs"
 	}
 	hostHome, err := localpathutil.Expand("~")
@@ -228,11 +235,11 @@ func templateArgs(ctx context.Context, bootScripts bool, instDir, name string, i
 	}
 
 	switch *instConfig.MountType {
-	case limayaml.REVSSHFS:
+	case limatype.REVSSHFS:
 		args.MountType = "reverse-sshfs"
-	case limayaml.NINEP:
+	case limatype.NINEP:
 		args.MountType = "9p"
-	case limayaml.VIRTIOFS:
+	case limatype.VIRTIOFS:
 		args.MountType = "virtiofs"
 	}
 
@@ -272,7 +279,7 @@ func templateArgs(ctx context.Context, bootScripts bool, instDir, name string, i
 		for _, addr := range instConfig.DNS {
 			args.DNSAddresses = append(args.DNSAddresses, addr.String())
 		}
-	case firstUsernetIndex != -1 || *instConfig.VMType == limayaml.VZ:
+	case firstUsernetIndex != -1 || *instConfig.VMType == limatype.VZ:
 		args.DNSAddresses = append(args.DNSAddresses, args.SlirpDNS)
 	case *instConfig.HostResolver.Enabled:
 		args.UDPDNSLocalPort = udpDNSLocalPort
@@ -316,10 +323,10 @@ func templateArgs(ctx context.Context, bootScripts bool, instDir, name string, i
 	args.BootCmds = getBootCmds(instConfig.Provision)
 
 	for i, f := range instConfig.Provision {
-		if f.Mode == limayaml.ProvisionModeDependency && *f.SkipDefaultDependencyResolution {
+		if f.Mode == limatype.ProvisionModeDependency && *f.SkipDefaultDependencyResolution {
 			args.SkipDefaultDependencyResolution = true
 		}
-		if f.Mode == limayaml.ProvisionModeData {
+		if f.Mode == limatype.ProvisionModeData {
 			args.DataFiles = append(args.DataFiles, DataFile{
 				FileName:    fmt.Sprintf("%08d", i),
 				Overwrite:   strconv.FormatBool(*f.Overwrite),
@@ -333,7 +340,7 @@ func templateArgs(ctx context.Context, bootScripts bool, instDir, name string, i
 	return &args, nil
 }
 
-func GenerateCloudConfig(ctx context.Context, instDir, name string, instConfig *limayaml.LimaYAML) error {
+func GenerateCloudConfig(ctx context.Context, instDir, name string, instConfig *limatype.LimaYAML) error {
 	args, err := templateArgs(ctx, false, instDir, name, instConfig, 0, 0, 0, "")
 	if err != nil {
 		return err
@@ -356,7 +363,7 @@ func GenerateCloudConfig(ctx context.Context, instDir, name string, instConfig *
 	return os.WriteFile(filepath.Join(instDir, filenames.CloudConfig), config, 0o444)
 }
 
-func GenerateISO9660(ctx context.Context, instDir, name string, instConfig *limayaml.LimaYAML, udpDNSLocalPort, tcpDNSLocalPort int, guestAgentBinary, nerdctlArchive string, vsockPort int, virtioPort string) error {
+func GenerateISO9660(ctx context.Context, drv driver.Driver, instDir, name string, instConfig *limatype.LimaYAML, udpDNSLocalPort, tcpDNSLocalPort int, guestAgentBinary, nerdctlArchive string, vsockPort int, virtioPort string) error {
 	args, err := templateArgs(ctx, true, instDir, name, instConfig, udpDNSLocalPort, tcpDNSLocalPort, vsockPort, virtioPort)
 	if err != nil {
 		return err
@@ -371,21 +378,33 @@ func GenerateISO9660(ctx context.Context, instDir, name string, instConfig *lima
 		return err
 	}
 
+	driverScripts, err := drv.BootScripts()
+	if err != nil {
+		return fmt.Errorf("failed to get boot scripts: %w", err)
+	}
+
+	for filename, content := range driverScripts {
+		layout = append(layout, iso9660util.Entry{
+			Path:   fmt.Sprintf("boot/%s", filename),
+			Reader: strings.NewReader(string(content)),
+		})
+	}
+
 	for i, f := range instConfig.Provision {
 		switch f.Mode {
-		case limayaml.ProvisionModeSystem, limayaml.ProvisionModeUser, limayaml.ProvisionModeDependency:
+		case limatype.ProvisionModeSystem, limatype.ProvisionModeUser, limatype.ProvisionModeDependency:
 			layout = append(layout, iso9660util.Entry{
 				Path:   fmt.Sprintf("provision.%s/%08d", f.Mode, i),
 				Reader: strings.NewReader(f.Script),
 			})
-		case limayaml.ProvisionModeData:
+		case limatype.ProvisionModeData:
 			layout = append(layout, iso9660util.Entry{
 				Path:   fmt.Sprintf("provision.%s/%08d", f.Mode, i),
 				Reader: strings.NewReader(*f.Content),
 			})
-		case limayaml.ProvisionModeBoot:
+		case limatype.ProvisionModeBoot:
 			continue
-		case limayaml.ProvisionModeAnsible:
+		case limatype.ProvisionModeAnsible:
 			continue
 		default:
 			return fmt.Errorf("unknown provision mode %q", f.Mode)
@@ -433,7 +452,7 @@ func GenerateISO9660(ctx context.Context, instDir, name string, instConfig *lima
 		})
 	}
 
-	if args.VMType == limayaml.WSL2 {
+	if args.VMType == limatype.WSL2 {
 		layout = append(layout, iso9660util.Entry{
 			Path:   "ssh_authorized_keys",
 			Reader: strings.NewReader(strings.Join(args.SSHPubKeys, "\n")),
@@ -456,10 +475,10 @@ func getCert(content string) Cert {
 	return Cert{Lines: lines}
 }
 
-func getBootCmds(p []limayaml.Provision) []BootCmds {
+func getBootCmds(p []limatype.Provision) []BootCmds {
 	var bootCmds []BootCmds
 	for _, f := range p {
-		if f.Mode == limayaml.ProvisionModeBoot {
+		if f.Mode == limatype.ProvisionModeBoot {
 			lines := []string{}
 			for _, line := range strings.Split(f.Script, "\n") {
 				if line == "" {
diff --git a/pkg/driver/driver.go b/pkg/driver/driver.go
index f9811f6e..2ecaef08 100644
--- a/pkg/driver/driver.go
+++ b/pkg/driver/driver.go
@@ -7,7 +7,7 @@ import (
 	"context"
 	"net"
 
-	"github.com/lima-vm/lima/v2/pkg/store"
+	"github.com/lima-vm/lima/v2/pkg/limatype"
 )
 
 // Lifecycle defines basic lifecycle operations.
@@ -15,13 +15,13 @@ type Lifecycle interface {
 	// Validate returns error if the current driver isn't support for given config
 	Validate(_ context.Context) error
 
-	// Initialize is called on creating the instance for initialization.
+	// Create is called on creating the instance for the first time.
 	// (e.g., creating "vz-identifier" file)
 	//
-	// Initialize MUST return nil when it is called against an existing instance.
+	// Create MUST return nil when it is called against an existing instance.
 	//
-	// Initialize does not create the disks.
-	Initialize(_ context.Context) error
+	// Create does not create the disks.
+	Create(_ context.Context) error
 
 	// CreateDisk returns error if the current driver fails in creating disk
 	CreateDisk(_ context.Context) error
@@ -34,6 +34,12 @@ type Lifecycle interface {
 	// Stop will terminate the running vm instance.
 	// It returns error if there are any errors during Stop
 	Stop(_ context.Context) error
+
+	Delete(_ context.Context) error
+
+	InspectStatus(_ context.Context, inst *limatype.Instance) string
+
+	BootScripts() (map[string][]byte, error)
 }
 
 // GUI defines GUI-related operations.
@@ -54,12 +60,6 @@ type SnapshotManager interface {
 	ListSnapshots(ctx context.Context) (string, error)
 }
 
-// Registration defines operations for registering and unregistering the driver instance.
-type Registration interface {
-	Register(ctx context.Context) error
-	Unregister(ctx context.Context) error
-}
-
 // GuestAgent defines operations for the guest agent.
 type GuestAgent interface {
 	// ForwardGuestAgent returns if the guest agent sock needs forwarding by host agent.
@@ -74,13 +74,17 @@ type Driver interface {
 	Lifecycle
 	GUI
 	SnapshotManager
-	Registration
 	GuestAgent
 
 	Info() Info
 
-	// SetConfig sets the configuration for the instance.
-	Configure(inst *store.Instance) *ConfiguredDriver
+	// Configure sets the configuration for the instance.
+	Configure(inst *limatype.Instance) *ConfiguredDriver
+
+	AcceptConfig(cfg *limatype.LimaYAML, filePath string) error
+	FillConfig(cfg *limatype.LimaYAML, filePath string) error
+
+	SSHAddress(ctx context.Context) (string, error)
 }
 
 type ConfiguredDriver struct {
@@ -88,9 +92,15 @@ type ConfiguredDriver struct {
 }
 
 type Info struct {
-	DriverName  string `json:"driverName"`
-	CanRunGUI   bool   `json:"canRunGui,omitempty"`
-	VsockPort   int    `json:"vsockPort"`
-	VirtioPort  string `json:"virtioPort"`
-	InstanceDir string `json:"instanceDir,omitempty"`
+	VsockPort   int            `json:"vsockPort"`
+	VirtioPort  string         `json:"virtioPort"`
+	InstanceDir string         `json:"instanceDir,omitempty"`
+	Features    DriverFeatures `json:"features"`
+}
+
+type DriverFeatures struct {
+	DriverName           string `json:"driverName"`
+	CanRunGUI            bool   `json:"canRunGui,omitempty"`
+	DynamicSSHAddress    bool   `json:"dynamicSSHAddress"`
+	SkipSocketForwarding bool   `json:"skipSocketForwarding"`
 }
diff --git a/pkg/driver/external/client/methods.go b/pkg/driver/external/client/methods.go
index 2b95f85f..af9d974b 100644
--- a/pkg/driver/external/client/methods.go
+++ b/pkg/driver/external/client/methods.go
@@ -14,7 +14,7 @@ import (
 
 	"github.com/lima-vm/lima/v2/pkg/driver"
 	pb "github.com/lima-vm/lima/v2/pkg/driver/external"
-	"github.com/lima-vm/lima/v2/pkg/store"
+	"github.com/lima-vm/lima/v2/pkg/limatype"
 )
 
 func (d *DriverClient) Validate(ctx context.Context) error {
@@ -32,10 +32,10 @@ func (d *DriverClient) Validate(ctx context.Context) error {
 	return nil
 }
 
-func (d *DriverClient) Initialize(ctx context.Context) error {
+func (d *DriverClient) Create(ctx context.Context) error {
 	d.logger.Debug("Initializing driver instance")
 
-	_, err := d.DriverSvc.Initialize(ctx, &emptypb.Empty{})
+	_, err := d.DriverSvc.Create(ctx, &emptypb.Empty{})
 	if err != nil {
 		d.logger.Errorf("Initialization failed: %v", err)
 		return err
@@ -102,6 +102,27 @@ func (d *DriverClient) Stop(ctx context.Context) error {
 	return nil
 }
 
+func (d *DriverClient) Delete(ctx context.Context) error {
+	d.logger.Debug("Deleting driver instance")
+
+	_, err := d.DriverSvc.Delete(ctx, &emptypb.Empty{})
+	if err != nil {
+		d.logger.Errorf("Failed to delete driver instance: %v", err)
+		return err
+	}
+
+	d.logger.Debug("Driver instance deleted successfully")
+	return nil
+}
+
+func (d *DriverClient) AcceptConfig(_ *limatype.LimaYAML, _ string) error {
+	return errors.New("pre-configured driver action not implemented in client driver")
+}
+
+func (d *DriverClient) FillConfig(_ *limatype.LimaYAML, _ string) error {
+	return errors.New("pre-configured driver action not implemented in client driver")
+}
+
 func (d *DriverClient) RunGUI() error {
 	d.logger.Debug("Running GUI for the driver instance")
 
@@ -204,32 +225,6 @@ func (d *DriverClient) ListSnapshots(ctx context.Context) (string, error) {
 	return resp.Snapshots, nil
 }
 
-func (d *DriverClient) Register(ctx context.Context) error {
-	d.logger.Debug("Registering driver instance")
-
-	_, err := d.DriverSvc.Register(ctx, &emptypb.Empty{})
-	if err != nil {
-		d.logger.Errorf("Failed to register driver instance: %v", err)
-		return err
-	}
-
-	d.logger.Debug("Driver instance registered successfully")
-	return nil
-}
-
-func (d *DriverClient) Unregister(ctx context.Context) error {
-	d.logger.Debug("Unregistering driver instance")
-
-	_, err := d.DriverSvc.Unregister(ctx, &emptypb.Empty{})
-	if err != nil {
-		d.logger.Errorf("Failed to unregister driver instance: %v", err)
-		return err
-	}
-
-	d.logger.Debug("Driver instance unregistered successfully")
-	return nil
-}
-
 func (d *DriverClient) ForwardGuestAgent() bool {
 	d.logger.Debug("Checking if guest agent needs to be forwarded")
 
@@ -262,7 +257,7 @@ func (d *DriverClient) Info() driver.Info {
 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 	defer cancel()
 
-	resp, err := d.DriverSvc.GetInfo(ctx, &emptypb.Empty{})
+	resp, err := d.DriverSvc.Info(ctx, &emptypb.Empty{})
 	if err != nil {
 		d.logger.Errorf("Failed to get driver info: %v", err)
 		return driver.Info{}
@@ -278,7 +273,7 @@ func (d *DriverClient) Info() driver.Info {
 	return info
 }
 
-func (d *DriverClient) Configure(inst *store.Instance) *driver.ConfiguredDriver {
+func (d *DriverClient) Configure(inst *limatype.Instance) *driver.ConfiguredDriver {
 	d.logger.Debugf("Setting config for instance %s with SSH local port %d", inst.Name, inst.SSHLocalPort)
 
 	instJSON, err := inst.MarshalJSON()
@@ -290,7 +285,7 @@ func (d *DriverClient) Configure(inst *store.Instance) *driver.ConfiguredDriver
 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 	defer cancel()
 
-	_, err = d.DriverSvc.SetConfig(ctx, &pb.SetConfigRequest{
+	_, err = d.DriverSvc.Configure(ctx, &pb.SetConfigRequest{
 		InstanceConfigJson: instJSON,
 	})
 	if err != nil {
@@ -303,3 +298,32 @@ func (d *DriverClient) Configure(inst *store.Instance) *driver.ConfiguredDriver
 		Driver: d,
 	}
 }
+
+func (d *DriverClient) InspectStatus(_ context.Context, _ *limatype.Instance) string {
+	return ""
+}
+
+func (d *DriverClient) SSHAddress(ctx context.Context) (string, error) {
+	d.logger.Debug("Getting SSH address for the driver instance")
+
+	resp, err := d.DriverSvc.SSHAddress(ctx, &emptypb.Empty{})
+	if err != nil {
+		d.logger.Errorf("Failed to get SSH address: %v", err)
+		return "", err
+	}
+
+	d.logger.Debugf("SSH address retrieved: %s", resp.Address)
+	return resp.Address, nil
+}
+
+func (d *DriverClient) BootScripts() (map[string][]byte, error) {
+	d.logger.Debug("Getting boot scripts for the driver instance")
+	resp, err := d.DriverSvc.BootScripts(context.Background(), &emptypb.Empty{})
+	if err != nil {
+		d.logger.Errorf("Failed to get boot scripts: %v", err)
+		return nil, err
+	}
+
+	d.logger.Debugf("Boot scripts retrieved successfully: %d scripts", len(resp.Scripts))
+	return resp.Scripts, nil
+}
diff --git a/pkg/driver/external/driver.pb.go b/pkg/driver/external/driver.pb.go
index 96ae1b4b..60519120 100644
--- a/pkg/driver/external/driver.pb.go
+++ b/pkg/driver/external/driver.pb.go
@@ -22,6 +22,94 @@ const (
 	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
 )
 
+type BootScriptsResponse struct {
+	state         protoimpl.MessageState `protogen:"open.v1"`
+	Scripts       map[string][]byte      `protobuf:"bytes,1,rep,name=scripts,proto3" json:"scripts,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
+	unknownFields protoimpl.UnknownFields
+	sizeCache     protoimpl.SizeCache
+}
+
+func (x *BootScriptsResponse) Reset() {
+	*x = BootScriptsResponse{}
+	mi := &file_pkg_driver_external_driver_proto_msgTypes[0]
+	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+	ms.StoreMessageInfo(mi)
+}
+
+func (x *BootScriptsResponse) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*BootScriptsResponse) ProtoMessage() {}
+
+func (x *BootScriptsResponse) ProtoReflect() protoreflect.Message {
+	mi := &file_pkg_driver_external_driver_proto_msgTypes[0]
+	if x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use BootScriptsResponse.ProtoReflect.Descriptor instead.
+func (*BootScriptsResponse) Descriptor() ([]byte, []int) {
+	return file_pkg_driver_external_driver_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *BootScriptsResponse) GetScripts() map[string][]byte {
+	if x != nil {
+		return x.Scripts
+	}
+	return nil
+}
+
+type SSHAddressResponse struct {
+	state         protoimpl.MessageState `protogen:"open.v1"`
+	Address       string                 `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"`
+	unknownFields protoimpl.UnknownFields
+	sizeCache     protoimpl.SizeCache
+}
+
+func (x *SSHAddressResponse) Reset() {
+	*x = SSHAddressResponse{}
+	mi := &file_pkg_driver_external_driver_proto_msgTypes[1]
+	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+	ms.StoreMessageInfo(mi)
+}
+
+func (x *SSHAddressResponse) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*SSHAddressResponse) ProtoMessage() {}
+
+func (x *SSHAddressResponse) ProtoReflect() protoreflect.Message {
+	mi := &file_pkg_driver_external_driver_proto_msgTypes[1]
+	if x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use SSHAddressResponse.ProtoReflect.Descriptor instead.
+func (*SSHAddressResponse) Descriptor() ([]byte, []int) {
+	return file_pkg_driver_external_driver_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *SSHAddressResponse) GetAddress() string {
+	if x != nil {
+		return x.Address
+	}
+	return ""
+}
+
 type InfoResponse struct {
 	state         protoimpl.MessageState `protogen:"open.v1"`
 	InfoJson      []byte                 `protobuf:"bytes,1,opt,name=info_json,json=infoJson,proto3" json:"info_json,omitempty"`
@@ -31,7 +119,7 @@ type InfoResponse struct {
 
 func (x *InfoResponse) Reset() {
 	*x = InfoResponse{}
-	mi := &file_pkg_driver_external_driver_proto_msgTypes[0]
+	mi := &file_pkg_driver_external_driver_proto_msgTypes[2]
 	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 	ms.StoreMessageInfo(mi)
 }
@@ -43,7 +131,7 @@ func (x *InfoResponse) String() string {
 func (*InfoResponse) ProtoMessage() {}
 
 func (x *InfoResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_pkg_driver_external_driver_proto_msgTypes[0]
+	mi := &file_pkg_driver_external_driver_proto_msgTypes[2]
 	if x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -56,7 +144,7 @@ func (x *InfoResponse) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use InfoResponse.ProtoReflect.Descriptor instead.
 func (*InfoResponse) Descriptor() ([]byte, []int) {
-	return file_pkg_driver_external_driver_proto_rawDescGZIP(), []int{0}
+	return file_pkg_driver_external_driver_proto_rawDescGZIP(), []int{2}
 }
 
 func (x *InfoResponse) GetInfoJson() []byte {
@@ -76,7 +164,7 @@ type StartResponse struct {
 
 func (x *StartResponse) Reset() {
 	*x = StartResponse{}
-	mi := &file_pkg_driver_external_driver_proto_msgTypes[1]
+	mi := &file_pkg_driver_external_driver_proto_msgTypes[3]
 	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 	ms.StoreMessageInfo(mi)
 }
@@ -88,7 +176,7 @@ func (x *StartResponse) String() string {
 func (*StartResponse) ProtoMessage() {}
 
 func (x *StartResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_pkg_driver_external_driver_proto_msgTypes[1]
+	mi := &file_pkg_driver_external_driver_proto_msgTypes[3]
 	if x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -101,7 +189,7 @@ func (x *StartResponse) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use StartResponse.ProtoReflect.Descriptor instead.
 func (*StartResponse) Descriptor() ([]byte, []int) {
-	return file_pkg_driver_external_driver_proto_rawDescGZIP(), []int{1}
+	return file_pkg_driver_external_driver_proto_rawDescGZIP(), []int{3}
 }
 
 func (x *StartResponse) GetSuccess() bool {
@@ -127,7 +215,7 @@ type SetConfigRequest struct {
 
 func (x *SetConfigRequest) Reset() {
 	*x = SetConfigRequest{}
-	mi := &file_pkg_driver_external_driver_proto_msgTypes[2]
+	mi := &file_pkg_driver_external_driver_proto_msgTypes[4]
 	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 	ms.StoreMessageInfo(mi)
 }
@@ -139,7 +227,7 @@ func (x *SetConfigRequest) String() string {
 func (*SetConfigRequest) ProtoMessage() {}
 
 func (x *SetConfigRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_pkg_driver_external_driver_proto_msgTypes[2]
+	mi := &file_pkg_driver_external_driver_proto_msgTypes[4]
 	if x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -152,7 +240,7 @@ func (x *SetConfigRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use SetConfigRequest.ProtoReflect.Descriptor instead.
 func (*SetConfigRequest) Descriptor() ([]byte, []int) {
-	return file_pkg_driver_external_driver_proto_rawDescGZIP(), []int{2}
+	return file_pkg_driver_external_driver_proto_rawDescGZIP(), []int{4}
 }
 
 func (x *SetConfigRequest) GetInstanceConfigJson() []byte {
@@ -171,7 +259,7 @@ type ChangeDisplayPasswordRequest struct {
 
 func (x *ChangeDisplayPasswordRequest) Reset() {
 	*x = ChangeDisplayPasswordRequest{}
-	mi := &file_pkg_driver_external_driver_proto_msgTypes[3]
+	mi := &file_pkg_driver_external_driver_proto_msgTypes[5]
 	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 	ms.StoreMessageInfo(mi)
 }
@@ -183,7 +271,7 @@ func (x *ChangeDisplayPasswordRequest) String() string {
 func (*ChangeDisplayPasswordRequest) ProtoMessage() {}
 
 func (x *ChangeDisplayPasswordRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_pkg_driver_external_driver_proto_msgTypes[3]
+	mi := &file_pkg_driver_external_driver_proto_msgTypes[5]
 	if x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -196,7 +284,7 @@ func (x *ChangeDisplayPasswordRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use ChangeDisplayPasswordRequest.ProtoReflect.Descriptor instead.
 func (*ChangeDisplayPasswordRequest) Descriptor() ([]byte, []int) {
-	return file_pkg_driver_external_driver_proto_rawDescGZIP(), []int{3}
+	return file_pkg_driver_external_driver_proto_rawDescGZIP(), []int{5}
 }
 
 func (x *ChangeDisplayPasswordRequest) GetPassword() string {
@@ -215,7 +303,7 @@ type GetDisplayConnectionResponse struct {
 
 func (x *GetDisplayConnectionResponse) Reset() {
 	*x = GetDisplayConnectionResponse{}
-	mi := &file_pkg_driver_external_driver_proto_msgTypes[4]
+	mi := &file_pkg_driver_external_driver_proto_msgTypes[6]
 	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 	ms.StoreMessageInfo(mi)
 }
@@ -227,7 +315,7 @@ func (x *GetDisplayConnectionResponse) String() string {
 func (*GetDisplayConnectionResponse) ProtoMessage() {}
 
 func (x *GetDisplayConnectionResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_pkg_driver_external_driver_proto_msgTypes[4]
+	mi := &file_pkg_driver_external_driver_proto_msgTypes[6]
 	if x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -240,7 +328,7 @@ func (x *GetDisplayConnectionResponse) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use GetDisplayConnectionResponse.ProtoReflect.Descriptor instead.
 func (*GetDisplayConnectionResponse) Descriptor() ([]byte, []int) {
-	return file_pkg_driver_external_driver_proto_rawDescGZIP(), []int{4}
+	return file_pkg_driver_external_driver_proto_rawDescGZIP(), []int{6}
 }
 
 func (x *GetDisplayConnectionResponse) GetConnection() string {
@@ -259,7 +347,7 @@ type CreateSnapshotRequest struct {
 
 func (x *CreateSnapshotRequest) Reset() {
 	*x = CreateSnapshotRequest{}
-	mi := &file_pkg_driver_external_driver_proto_msgTypes[5]
+	mi := &file_pkg_driver_external_driver_proto_msgTypes[7]
 	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 	ms.StoreMessageInfo(mi)
 }
@@ -271,7 +359,7 @@ func (x *CreateSnapshotRequest) String() string {
 func (*CreateSnapshotRequest) ProtoMessage() {}
 
 func (x *CreateSnapshotRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_pkg_driver_external_driver_proto_msgTypes[5]
+	mi := &file_pkg_driver_external_driver_proto_msgTypes[7]
 	if x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -284,7 +372,7 @@ func (x *CreateSnapshotRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use CreateSnapshotRequest.ProtoReflect.Descriptor instead.
 func (*CreateSnapshotRequest) Descriptor() ([]byte, []int) {
-	return file_pkg_driver_external_driver_proto_rawDescGZIP(), []int{5}
+	return file_pkg_driver_external_driver_proto_rawDescGZIP(), []int{7}
 }
 
 func (x *CreateSnapshotRequest) GetTag() string {
@@ -303,7 +391,7 @@ type ApplySnapshotRequest struct {
 
 func (x *ApplySnapshotRequest) Reset() {
 	*x = ApplySnapshotRequest{}
-	mi := &file_pkg_driver_external_driver_proto_msgTypes[6]
+	mi := &file_pkg_driver_external_driver_proto_msgTypes[8]
 	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 	ms.StoreMessageInfo(mi)
 }
@@ -315,7 +403,7 @@ func (x *ApplySnapshotRequest) String() string {
 func (*ApplySnapshotRequest) ProtoMessage() {}
 
 func (x *ApplySnapshotRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_pkg_driver_external_driver_proto_msgTypes[6]
+	mi := &file_pkg_driver_external_driver_proto_msgTypes[8]
 	if x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -328,7 +416,7 @@ func (x *ApplySnapshotRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use ApplySnapshotRequest.ProtoReflect.Descriptor instead.
 func (*ApplySnapshotRequest) Descriptor() ([]byte, []int) {
-	return file_pkg_driver_external_driver_proto_rawDescGZIP(), []int{6}
+	return file_pkg_driver_external_driver_proto_rawDescGZIP(), []int{8}
 }
 
 func (x *ApplySnapshotRequest) GetTag() string {
@@ -347,7 +435,7 @@ type DeleteSnapshotRequest struct {
 
 func (x *DeleteSnapshotRequest) Reset() {
 	*x = DeleteSnapshotRequest{}
-	mi := &file_pkg_driver_external_driver_proto_msgTypes[7]
+	mi := &file_pkg_driver_external_driver_proto_msgTypes[9]
 	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 	ms.StoreMessageInfo(mi)
 }
@@ -359,7 +447,7 @@ func (x *DeleteSnapshotRequest) String() string {
 func (*DeleteSnapshotRequest) ProtoMessage() {}
 
 func (x *DeleteSnapshotRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_pkg_driver_external_driver_proto_msgTypes[7]
+	mi := &file_pkg_driver_external_driver_proto_msgTypes[9]
 	if x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -372,7 +460,7 @@ func (x *DeleteSnapshotRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use DeleteSnapshotRequest.ProtoReflect.Descriptor instead.
 func (*DeleteSnapshotRequest) Descriptor() ([]byte, []int) {
-	return file_pkg_driver_external_driver_proto_rawDescGZIP(), []int{7}
+	return file_pkg_driver_external_driver_proto_rawDescGZIP(), []int{9}
 }
 
 func (x *DeleteSnapshotRequest) GetTag() string {
@@ -391,7 +479,7 @@ type ListSnapshotsResponse struct {
 
 func (x *ListSnapshotsResponse) Reset() {
 	*x = ListSnapshotsResponse{}
-	mi := &file_pkg_driver_external_driver_proto_msgTypes[8]
+	mi := &file_pkg_driver_external_driver_proto_msgTypes[10]
 	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 	ms.StoreMessageInfo(mi)
 }
@@ -403,7 +491,7 @@ func (x *ListSnapshotsResponse) String() string {
 func (*ListSnapshotsResponse) ProtoMessage() {}
 
 func (x *ListSnapshotsResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_pkg_driver_external_driver_proto_msgTypes[8]
+	mi := &file_pkg_driver_external_driver_proto_msgTypes[10]
 	if x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -416,7 +504,7 @@ func (x *ListSnapshotsResponse) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use ListSnapshotsResponse.ProtoReflect.Descriptor instead.
 func (*ListSnapshotsResponse) Descriptor() ([]byte, []int) {
-	return file_pkg_driver_external_driver_proto_rawDescGZIP(), []int{8}
+	return file_pkg_driver_external_driver_proto_rawDescGZIP(), []int{10}
 }
 
 func (x *ListSnapshotsResponse) GetSnapshots() string {
@@ -435,7 +523,7 @@ type ForwardGuestAgentResponse struct {
 
 func (x *ForwardGuestAgentResponse) Reset() {
 	*x = ForwardGuestAgentResponse{}
-	mi := &file_pkg_driver_external_driver_proto_msgTypes[9]
+	mi := &file_pkg_driver_external_driver_proto_msgTypes[11]
 	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 	ms.StoreMessageInfo(mi)
 }
@@ -447,7 +535,7 @@ func (x *ForwardGuestAgentResponse) String() string {
 func (*ForwardGuestAgentResponse) ProtoMessage() {}
 
 func (x *ForwardGuestAgentResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_pkg_driver_external_driver_proto_msgTypes[9]
+	mi := &file_pkg_driver_external_driver_proto_msgTypes[11]
 	if x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -460,7 +548,7 @@ func (x *ForwardGuestAgentResponse) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use ForwardGuestAgentResponse.ProtoReflect.Descriptor instead.
 func (*ForwardGuestAgentResponse) Descriptor() ([]byte, []int) {
-	return file_pkg_driver_external_driver_proto_rawDescGZIP(), []int{9}
+	return file_pkg_driver_external_driver_proto_rawDescGZIP(), []int{11}
 }
 
 func (x *ForwardGuestAgentResponse) GetShouldForward() bool {
@@ -474,7 +562,14 @@ var File_pkg_driver_external_driver_proto protoreflect.FileDescriptor
 
 const file_pkg_driver_external_driver_proto_rawDesc = "" +
 	"\n" +
-	" pkg/driver/external/driver.proto\x1a\x1bgoogle/protobuf/empty.proto\"+\n" +
+	" pkg/driver/external/driver.proto\x1a\x1bgoogle/protobuf/empty.proto\"\x8e\x01\n" +
+	"\x13BootScriptsResponse\x12;\n" +
+	"\ascripts\x18\x01 \x03(\v2!.BootScriptsResponse.ScriptsEntryR\ascripts\x1a:\n" +
+	"\fScriptsEntry\x12\x10\n" +
+	"\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" +
+	"\x05value\x18\x02 \x01(\fR\x05value:\x028\x01\".\n" +
+	"\x12SSHAddressResponse\x12\x18\n" +
+	"\aaddress\x18\x01 \x01(\tR\aaddress\"+\n" +
 	"\fInfoResponse\x12\x1b\n" +
 	"\tinfo_json\x18\x01 \x01(\fR\binfoJson\"?\n" +
 	"\rStartResponse\x12\x18\n" +
@@ -497,29 +592,29 @@ const file_pkg_driver_external_driver_proto_rawDesc = "" +
 	"\x15ListSnapshotsResponse\x12\x1c\n" +
 	"\tsnapshots\x18\x01 \x01(\tR\tsnapshots\"B\n" +
 	"\x19ForwardGuestAgentResponse\x12%\n" +
-	"\x0eshould_forward\x18\x01 \x01(\bR\rshouldForward2\xf8\b\n" +
+	"\x0eshould_forward\x18\x01 \x01(\bR\rshouldForward2\xa9\t\n" +
 	"\x06Driver\x12:\n" +
-	"\bValidate\x12\x16.google.protobuf.Empty\x1a\x16.google.protobuf.Empty\x12<\n" +
-	"\n" +
-	"Initialize\x12\x16.google.protobuf.Empty\x1a\x16.google.protobuf.Empty\x12<\n" +
+	"\bValidate\x12\x16.google.protobuf.Empty\x1a\x16.google.protobuf.Empty\x128\n" +
+	"\x06Create\x12\x16.google.protobuf.Empty\x1a\x16.google.protobuf.Empty\x12<\n" +
 	"\n" +
 	"CreateDisk\x12\x16.google.protobuf.Empty\x1a\x16.google.protobuf.Empty\x121\n" +
 	"\x05Start\x12\x16.google.protobuf.Empty\x1a\x0e.StartResponse0\x01\x126\n" +
 	"\x04Stop\x12\x16.google.protobuf.Empty\x1a\x16.google.protobuf.Empty\x128\n" +
+	"\x06Delete\x12\x16.google.protobuf.Empty\x1a\x16.google.protobuf.Empty\x12;\n" +
+	"\vBootScripts\x12\x16.google.protobuf.Empty\x1a\x14.BootScriptsResponse\x128\n" +
 	"\x06RunGUI\x12\x16.google.protobuf.Empty\x1a\x16.google.protobuf.Empty\x12N\n" +
 	"\x15ChangeDisplayPassword\x12\x1d.ChangeDisplayPasswordRequest\x1a\x16.google.protobuf.Empty\x12M\n" +
 	"\x14GetDisplayConnection\x12\x16.google.protobuf.Empty\x1a\x1d.GetDisplayConnectionResponse\x12@\n" +
 	"\x0eCreateSnapshot\x12\x16.CreateSnapshotRequest\x1a\x16.google.protobuf.Empty\x12>\n" +
 	"\rApplySnapshot\x12\x15.ApplySnapshotRequest\x1a\x16.google.protobuf.Empty\x12@\n" +
 	"\x0eDeleteSnapshot\x12\x16.DeleteSnapshotRequest\x1a\x16.google.protobuf.Empty\x12?\n" +
-	"\rListSnapshots\x12\x16.google.protobuf.Empty\x1a\x16.ListSnapshotsResponse\x12:\n" +
-	"\bRegister\x12\x16.google.protobuf.Empty\x1a\x16.google.protobuf.Empty\x12<\n" +
-	"\n" +
-	"Unregister\x12\x16.google.protobuf.Empty\x1a\x16.google.protobuf.Empty\x12G\n" +
+	"\rListSnapshots\x12\x16.google.protobuf.Empty\x1a\x16.ListSnapshotsResponse\x12G\n" +
 	"\x11ForwardGuestAgent\x12\x16.google.protobuf.Empty\x1a\x1a.ForwardGuestAgentResponse\x12@\n" +
 	"\x0eGuestAgentConn\x12\x16.google.protobuf.Empty\x1a\x16.google.protobuf.Empty\x126\n" +
-	"\tSetConfig\x12\x11.SetConfigRequest\x1a\x16.google.protobuf.Empty\x120\n" +
-	"\aGetInfo\x12\x16.google.protobuf.Empty\x1a\r.InfoResponseB0Z.github.com/lima-vm/lima/v2/pkg/driver/externalb\x06proto3"
+	"\tConfigure\x12\x11.SetConfigRequest\x1a\x16.google.protobuf.Empty\x12-\n" +
+	"\x04Info\x12\x16.google.protobuf.Empty\x1a\r.InfoResponse\x129\n" +
+	"\n" +
+	"SSHAddress\x12\x16.google.protobuf.Empty\x1a\x13.SSHAddressResponseB0Z.github.com/lima-vm/lima/v2/pkg/driver/externalb\x06proto3"
 
 var (
 	file_pkg_driver_external_driver_proto_rawDescOnce sync.Once
@@ -533,62 +628,68 @@ func file_pkg_driver_external_driver_proto_rawDescGZIP() []byte {
 	return file_pkg_driver_external_driver_proto_rawDescData
 }
 
-var file_pkg_driver_external_driver_proto_msgTypes = make([]protoimpl.MessageInfo, 10)
+var file_pkg_driver_external_driver_proto_msgTypes = make([]protoimpl.MessageInfo, 13)
 var file_pkg_driver_external_driver_proto_goTypes = []any{
-	(*InfoResponse)(nil),                 // 0: InfoResponse
-	(*StartResponse)(nil),                // 1: StartResponse
-	(*SetConfigRequest)(nil),             // 2: SetConfigRequest
-	(*ChangeDisplayPasswordRequest)(nil), // 3: ChangeDisplayPasswordRequest
-	(*GetDisplayConnectionResponse)(nil), // 4: GetDisplayConnectionResponse
-	(*CreateSnapshotRequest)(nil),        // 5: CreateSnapshotRequest
-	(*ApplySnapshotRequest)(nil),         // 6: ApplySnapshotRequest
-	(*DeleteSnapshotRequest)(nil),        // 7: DeleteSnapshotRequest
-	(*ListSnapshotsResponse)(nil),        // 8: ListSnapshotsResponse
-	(*ForwardGuestAgentResponse)(nil),    // 9: ForwardGuestAgentResponse
-	(*emptypb.Empty)(nil),                // 10: google.protobuf.Empty
+	(*BootScriptsResponse)(nil),          // 0: BootScriptsResponse
+	(*SSHAddressResponse)(nil),           // 1: SSHAddressResponse
+	(*InfoResponse)(nil),                 // 2: InfoResponse
+	(*StartResponse)(nil),                // 3: StartResponse
+	(*SetConfigRequest)(nil),             // 4: SetConfigRequest
+	(*ChangeDisplayPasswordRequest)(nil), // 5: ChangeDisplayPasswordRequest
+	(*GetDisplayConnectionResponse)(nil), // 6: GetDisplayConnectionResponse
+	(*CreateSnapshotRequest)(nil),        // 7: CreateSnapshotRequest
+	(*ApplySnapshotRequest)(nil),         // 8: ApplySnapshotRequest
+	(*DeleteSnapshotRequest)(nil),        // 9: DeleteSnapshotRequest
+	(*ListSnapshotsResponse)(nil),        // 10: ListSnapshotsResponse
+	(*ForwardGuestAgentResponse)(nil),    // 11: ForwardGuestAgentResponse
+	nil,                                  // 12: BootScriptsResponse.ScriptsEntry
+	(*emptypb.Empty)(nil),                // 13: google.protobuf.Empty
 }
 var file_pkg_driver_external_driver_proto_depIdxs = []int32{
-	10, // 0: Driver.Validate:input_type -> google.protobuf.Empty
-	10, // 1: Driver.Initialize:input_type -> google.protobuf.Empty
-	10, // 2: Driver.CreateDisk:input_type -> google.protobuf.Empty
-	10, // 3: Driver.Start:input_type -> google.protobuf.Empty
-	10, // 4: Driver.Stop:input_type -> google.protobuf.Empty
-	10, // 5: Driver.RunGUI:input_type -> google.protobuf.Empty
-	3,  // 6: Driver.ChangeDisplayPassword:input_type -> ChangeDisplayPasswordRequest
-	10, // 7: Driver.GetDisplayConnection:input_type -> google.protobuf.Empty
-	5,  // 8: Driver.CreateSnapshot:input_type -> CreateSnapshotRequest
-	6,  // 9: Driver.ApplySnapshot:input_type -> ApplySnapshotRequest
-	7,  // 10: Driver.DeleteSnapshot:input_type -> DeleteSnapshotRequest
-	10, // 11: Driver.ListSnapshots:input_type -> google.protobuf.Empty
-	10, // 12: Driver.Register:input_type -> google.protobuf.Empty
-	10, // 13: Driver.Unregister:input_type -> google.protobuf.Empty
-	10, // 14: Driver.ForwardGuestAgent:input_type -> google.protobuf.Empty
-	10, // 15: Driver.GuestAgentConn:input_type -> google.protobuf.Empty
-	2,  // 16: Driver.SetConfig:input_type -> SetConfigRequest
-	10, // 17: Driver.GetInfo:input_type -> google.protobuf.Empty
-	10, // 18: Driver.Validate:output_type -> google.protobuf.Empty
-	10, // 19: Driver.Initialize:output_type -> google.protobuf.Empty
-	10, // 20: Driver.CreateDisk:output_type -> google.protobuf.Empty
-	1,  // 21: Driver.Start:output_type -> StartResponse
-	10, // 22: Driver.Stop:output_type -> google.protobuf.Empty
-	10, // 23: Driver.RunGUI:output_type -> google.protobuf.Empty
-	10, // 24: Driver.ChangeDisplayPassword:output_type -> google.protobuf.Empty
-	4,  // 25: Driver.GetDisplayConnection:output_type -> GetDisplayConnectionResponse
-	10, // 26: Driver.CreateSnapshot:output_type -> google.protobuf.Empty
-	10, // 27: Driver.ApplySnapshot:output_type -> google.protobuf.Empty
-	10, // 28: Driver.DeleteSnapshot:output_type -> google.protobuf.Empty
-	8,  // 29: Driver.ListSnapshots:output_type -> ListSnapshotsResponse
-	10, // 30: Driver.Register:output_type -> google.protobuf.Empty
-	10, // 31: Driver.Unregister:output_type -> google.protobuf.Empty
-	9,  // 32: Driver.ForwardGuestAgent:output_type -> ForwardGuestAgentResponse
-	10, // 33: Driver.GuestAgentConn:output_type -> google.protobuf.Empty
-	10, // 34: Driver.SetConfig:output_type -> google.protobuf.Empty
-	0,  // 35: Driver.GetInfo:output_type -> InfoResponse
-	18, // [18:36] is the sub-list for method output_type
-	0,  // [0:18] is the sub-list for method input_type
-	0,  // [0:0] is the sub-list for extension type_name
-	0,  // [0:0] is the sub-list for extension extendee
-	0,  // [0:0] is the sub-list for field type_name
+	12, // 0: BootScriptsResponse.scripts:type_name -> BootScriptsResponse.ScriptsEntry
+	13, // 1: Driver.Validate:input_type -> google.protobuf.Empty
+	13, // 2: Driver.Create:input_type -> google.protobuf.Empty
+	13, // 3: Driver.CreateDisk:input_type -> google.protobuf.Empty
+	13, // 4: Driver.Start:input_type -> google.protobuf.Empty
+	13, // 5: Driver.Stop:input_type -> google.protobuf.Empty
+	13, // 6: Driver.Delete:input_type -> google.protobuf.Empty
+	13, // 7: Driver.BootScripts:input_type -> google.protobuf.Empty
+	13, // 8: Driver.RunGUI:input_type -> google.protobuf.Empty
+	5,  // 9: Driver.ChangeDisplayPassword:input_type -> ChangeDisplayPasswordRequest
+	13, // 10: Driver.GetDisplayConnection:input_type -> google.protobuf.Empty
+	7,  // 11: Driver.CreateSnapshot:input_type -> CreateSnapshotRequest
+	8,  // 12: Driver.ApplySnapshot:input_type -> ApplySnapshotRequest
+	9,  // 13: Driver.DeleteSnapshot:input_type -> DeleteSnapshotRequest
+	13, // 14: Driver.ListSnapshots:input_type -> google.protobuf.Empty
+	13, // 15: Driver.ForwardGuestAgent:input_type -> google.protobuf.Empty
+	13, // 16: Driver.GuestAgentConn:input_type -> google.protobuf.Empty
+	4,  // 17: Driver.Configure:input_type -> SetConfigRequest
+	13, // 18: Driver.Info:input_type -> google.protobuf.Empty
+	13, // 19: Driver.SSHAddress:input_type -> google.protobuf.Empty
+	13, // 20: Driver.Validate:output_type -> google.protobuf.Empty
+	13, // 21: Driver.Create:output_type -> google.protobuf.Empty
+	13, // 22: Driver.CreateDisk:output_type -> google.protobuf.Empty
+	3,  // 23: Driver.Start:output_type -> StartResponse
+	13, // 24: Driver.Stop:output_type -> google.protobuf.Empty
+	13, // 25: Driver.Delete:output_type -> google.protobuf.Empty
+	0,  // 26: Driver.BootScripts:output_type -> BootScriptsResponse
+	13, // 27: Driver.RunGUI:output_type -> google.protobuf.Empty
+	13, // 28: Driver.ChangeDisplayPassword:output_type -> google.protobuf.Empty
+	6,  // 29: Driver.GetDisplayConnection:output_type -> GetDisplayConnectionResponse
+	13, // 30: Driver.CreateSnapshot:output_type -> google.protobuf.Empty
+	13, // 31: Driver.ApplySnapshot:output_type -> google.protobuf.Empty
+	13, // 32: Driver.DeleteSnapshot:output_type -> google.protobuf.Empty
+	10, // 33: Driver.ListSnapshots:output_type -> ListSnapshotsResponse
+	11, // 34: Driver.ForwardGuestAgent:output_type -> ForwardGuestAgentResponse
+	13, // 35: Driver.GuestAgentConn:output_type -> google.protobuf.Empty
+	13, // 36: Driver.Configure:output_type -> google.protobuf.Empty
+	2,  // 37: Driver.Info:output_type -> InfoResponse
+	1,  // 38: Driver.SSHAddress:output_type -> SSHAddressResponse
+	20, // [20:39] is the sub-list for method output_type
+	1,  // [1:20] is the sub-list for method input_type
+	1,  // [1:1] is the sub-list for extension type_name
+	1,  // [1:1] is the sub-list for extension extendee
+	0,  // [0:1] is the sub-list for field type_name
 }
 
 func init() { file_pkg_driver_external_driver_proto_init() }
@@ -602,7 +703,7 @@ func file_pkg_driver_external_driver_proto_init() {
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 			RawDescriptor: unsafe.Slice(unsafe.StringData(file_pkg_driver_external_driver_proto_rawDesc), len(file_pkg_driver_external_driver_proto_rawDesc)),
 			NumEnums:      0,
-			NumMessages:   10,
+			NumMessages:   13,
 			NumExtensions: 0,
 			NumServices:   1,
 		},
diff --git a/pkg/driver/external/driver.proto b/pkg/driver/external/driver.proto
index 48282c50..743cd491 100644
--- a/pkg/driver/external/driver.proto
+++ b/pkg/driver/external/driver.proto
@@ -6,10 +6,12 @@ option go_package = "github.com/lima-vm/lima/v2/pkg/driver/external";
 
 service Driver {
   rpc Validate(google.protobuf.Empty) returns (google.protobuf.Empty);
-  rpc Initialize(google.protobuf.Empty) returns (google.protobuf.Empty);
+  rpc Create(google.protobuf.Empty) returns (google.protobuf.Empty);
   rpc CreateDisk(google.protobuf.Empty) returns (google.protobuf.Empty);
   rpc Start(google.protobuf.Empty) returns (stream StartResponse);
   rpc Stop(google.protobuf.Empty) returns (google.protobuf.Empty);
+  rpc Delete(google.protobuf.Empty) returns (google.protobuf.Empty);
+  rpc BootScripts(google.protobuf.Empty) returns (BootScriptsResponse);
 
   rpc RunGUI(google.protobuf.Empty) returns (google.protobuf.Empty);
   rpc ChangeDisplayPassword(ChangeDisplayPasswordRequest) returns (google.protobuf.Empty);
@@ -20,15 +22,20 @@ service Driver {
   rpc DeleteSnapshot(DeleteSnapshotRequest) returns (google.protobuf.Empty);
   rpc ListSnapshots(google.protobuf.Empty) returns (ListSnapshotsResponse);
 
-  rpc Register(google.protobuf.Empty) returns (google.protobuf.Empty);
-  rpc Unregister(google.protobuf.Empty) returns (google.protobuf.Empty);
-
   rpc ForwardGuestAgent(google.protobuf.Empty) returns (ForwardGuestAgentResponse);
   rpc GuestAgentConn(google.protobuf.Empty) returns (google.protobuf.Empty);
 
-  rpc SetConfig(SetConfigRequest) returns (google.protobuf.Empty);
+  rpc Configure(SetConfigRequest) returns (google.protobuf.Empty);
+  rpc Info(google.protobuf.Empty) returns (InfoResponse);
+  rpc SSHAddress(google.protobuf.Empty) returns (SSHAddressResponse);
+}
+
+message BootScriptsResponse {
+  map<string, bytes> scripts = 1;
+}
 
-  rpc GetInfo(google.protobuf.Empty) returns (InfoResponse);
+message SSHAddressResponse {
+  string address = 1;
 }
 
 message InfoResponse{
diff --git a/pkg/driver/external/driver_grpc.pb.go b/pkg/driver/external/driver_grpc.pb.go
index 5e1dcde9..dd5deb6d 100644
--- a/pkg/driver/external/driver_grpc.pb.go
+++ b/pkg/driver/external/driver_grpc.pb.go
@@ -21,10 +21,12 @@ const _ = grpc.SupportPackageIsVersion9
 
 const (
 	Driver_Validate_FullMethodName              = "/Driver/Validate"
-	Driver_Initialize_FullMethodName            = "/Driver/Initialize"
+	Driver_Create_FullMethodName                = "/Driver/Create"
 	Driver_CreateDisk_FullMethodName            = "/Driver/CreateDisk"
 	Driver_Start_FullMethodName                 = "/Driver/Start"
 	Driver_Stop_FullMethodName                  = "/Driver/Stop"
+	Driver_Delete_FullMethodName                = "/Driver/Delete"
+	Driver_BootScripts_FullMethodName           = "/Driver/BootScripts"
 	Driver_RunGUI_FullMethodName                = "/Driver/RunGUI"
 	Driver_ChangeDisplayPassword_FullMethodName = "/Driver/ChangeDisplayPassword"
 	Driver_GetDisplayConnection_FullMethodName  = "/Driver/GetDisplayConnection"
@@ -32,12 +34,11 @@ const (
 	Driver_ApplySnapshot_FullMethodName         = "/Driver/ApplySnapshot"
 	Driver_DeleteSnapshot_FullMethodName        = "/Driver/DeleteSnapshot"
 	Driver_ListSnapshots_FullMethodName         = "/Driver/ListSnapshots"
-	Driver_Register_FullMethodName              = "/Driver/Register"
-	Driver_Unregister_FullMethodName            = "/Driver/Unregister"
 	Driver_ForwardGuestAgent_FullMethodName     = "/Driver/ForwardGuestAgent"
 	Driver_GuestAgentConn_FullMethodName        = "/Driver/GuestAgentConn"
-	Driver_SetConfig_FullMethodName             = "/Driver/SetConfig"
-	Driver_GetInfo_FullMethodName               = "/Driver/GetInfo"
+	Driver_Configure_FullMethodName             = "/Driver/Configure"
+	Driver_Info_FullMethodName                  = "/Driver/Info"
+	Driver_SSHAddress_FullMethodName            = "/Driver/SSHAddress"
 )
 
 // DriverClient is the client API for Driver service.
@@ -45,10 +46,12 @@ const (
 // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
 type DriverClient interface {
 	Validate(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error)
-	Initialize(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error)
+	Create(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error)
 	CreateDisk(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error)
 	Start(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (grpc.ServerStreamingClient[StartResponse], error)
 	Stop(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error)
+	Delete(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error)
+	BootScripts(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*BootScriptsResponse, error)
 	RunGUI(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error)
 	ChangeDisplayPassword(ctx context.Context, in *ChangeDisplayPasswordRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
 	GetDisplayConnection(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*GetDisplayConnectionResponse, error)
@@ -56,12 +59,11 @@ type DriverClient interface {
 	ApplySnapshot(ctx context.Context, in *ApplySnapshotRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
 	DeleteSnapshot(ctx context.Context, in *DeleteSnapshotRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
 	ListSnapshots(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*ListSnapshotsResponse, error)
-	Register(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error)
-	Unregister(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error)
 	ForwardGuestAgent(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*ForwardGuestAgentResponse, error)
 	GuestAgentConn(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error)
-	SetConfig(ctx context.Context, in *SetConfigRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
-	GetInfo(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*InfoResponse, error)
+	Configure(ctx context.Context, in *SetConfigRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
+	Info(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*InfoResponse, error)
+	SSHAddress(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*SSHAddressResponse, error)
 }
 
 type driverClient struct {
@@ -82,10 +84,10 @@ func (c *driverClient) Validate(ctx context.Context, in *emptypb.Empty, opts ...
 	return out, nil
 }
 
-func (c *driverClient) Initialize(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+func (c *driverClient) Create(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error) {
 	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(emptypb.Empty)
-	err := c.cc.Invoke(ctx, Driver_Initialize_FullMethodName, in, out, cOpts...)
+	err := c.cc.Invoke(ctx, Driver_Create_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -131,6 +133,26 @@ func (c *driverClient) Stop(ctx context.Context, in *emptypb.Empty, opts ...grpc
 	return out, nil
 }
 
+func (c *driverClient) Delete(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+	out := new(emptypb.Empty)
+	err := c.cc.Invoke(ctx, Driver_Delete_FullMethodName, in, out, cOpts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *driverClient) BootScripts(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*BootScriptsResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+	out := new(BootScriptsResponse)
+	err := c.cc.Invoke(ctx, Driver_BootScripts_FullMethodName, in, out, cOpts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
 func (c *driverClient) RunGUI(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error) {
 	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(emptypb.Empty)
@@ -201,60 +223,50 @@ func (c *driverClient) ListSnapshots(ctx context.Context, in *emptypb.Empty, opt
 	return out, nil
 }
 
-func (c *driverClient) Register(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+func (c *driverClient) ForwardGuestAgent(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*ForwardGuestAgentResponse, error) {
 	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
-	out := new(emptypb.Empty)
-	err := c.cc.Invoke(ctx, Driver_Register_FullMethodName, in, out, cOpts...)
+	out := new(ForwardGuestAgentResponse)
+	err := c.cc.Invoke(ctx, Driver_ForwardGuestAgent_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
 	return out, nil
 }
 
-func (c *driverClient) Unregister(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+func (c *driverClient) GuestAgentConn(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error) {
 	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(emptypb.Empty)
-	err := c.cc.Invoke(ctx, Driver_Unregister_FullMethodName, in, out, cOpts...)
-	if err != nil {
-		return nil, err
-	}
-	return out, nil
-}
-
-func (c *driverClient) ForwardGuestAgent(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*ForwardGuestAgentResponse, error) {
-	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
-	out := new(ForwardGuestAgentResponse)
-	err := c.cc.Invoke(ctx, Driver_ForwardGuestAgent_FullMethodName, in, out, cOpts...)
+	err := c.cc.Invoke(ctx, Driver_GuestAgentConn_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
 	return out, nil
 }
 
-func (c *driverClient) GuestAgentConn(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+func (c *driverClient) Configure(ctx context.Context, in *SetConfigRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
 	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(emptypb.Empty)
-	err := c.cc.Invoke(ctx, Driver_GuestAgentConn_FullMethodName, in, out, cOpts...)
+	err := c.cc.Invoke(ctx, Driver_Configure_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
 	return out, nil
 }
 
-func (c *driverClient) SetConfig(ctx context.Context, in *SetConfigRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+func (c *driverClient) Info(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*InfoResponse, error) {
 	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
-	out := new(emptypb.Empty)
-	err := c.cc.Invoke(ctx, Driver_SetConfig_FullMethodName, in, out, cOpts...)
+	out := new(InfoResponse)
+	err := c.cc.Invoke(ctx, Driver_Info_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
 	return out, nil
 }
 
-func (c *driverClient) GetInfo(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*InfoResponse, error) {
+func (c *driverClient) SSHAddress(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*SSHAddressResponse, error) {
 	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
-	out := new(InfoResponse)
-	err := c.cc.Invoke(ctx, Driver_GetInfo_FullMethodName, in, out, cOpts...)
+	out := new(SSHAddressResponse)
+	err := c.cc.Invoke(ctx, Driver_SSHAddress_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -266,10 +278,12 @@ func (c *driverClient) GetInfo(ctx context.Context, in *emptypb.Empty, opts ...g
 // for forward compatibility.
 type DriverServer interface {
 	Validate(context.Context, *emptypb.Empty) (*emptypb.Empty, error)
-	Initialize(context.Context, *emptypb.Empty) (*emptypb.Empty, error)
+	Create(context.Context, *emptypb.Empty) (*emptypb.Empty, error)
 	CreateDisk(context.Context, *emptypb.Empty) (*emptypb.Empty, error)
 	Start(*emptypb.Empty, grpc.ServerStreamingServer[StartResponse]) error
 	Stop(context.Context, *emptypb.Empty) (*emptypb.Empty, error)
+	Delete(context.Context, *emptypb.Empty) (*emptypb.Empty, error)
+	BootScripts(context.Context, *emptypb.Empty) (*BootScriptsResponse, error)
 	RunGUI(context.Context, *emptypb.Empty) (*emptypb.Empty, error)
 	ChangeDisplayPassword(context.Context, *ChangeDisplayPasswordRequest) (*emptypb.Empty, error)
 	GetDisplayConnection(context.Context, *emptypb.Empty) (*GetDisplayConnectionResponse, error)
@@ -277,12 +291,11 @@ type DriverServer interface {
 	ApplySnapshot(context.Context, *ApplySnapshotRequest) (*emptypb.Empty, error)
 	DeleteSnapshot(context.Context, *DeleteSnapshotRequest) (*emptypb.Empty, error)
 	ListSnapshots(context.Context, *emptypb.Empty) (*ListSnapshotsResponse, error)
-	Register(context.Context, *emptypb.Empty) (*emptypb.Empty, error)
-	Unregister(context.Context, *emptypb.Empty) (*emptypb.Empty, error)
 	ForwardGuestAgent(context.Context, *emptypb.Empty) (*ForwardGuestAgentResponse, error)
 	GuestAgentConn(context.Context, *emptypb.Empty) (*emptypb.Empty, error)
-	SetConfig(context.Context, *SetConfigRequest) (*emptypb.Empty, error)
-	GetInfo(context.Context, *emptypb.Empty) (*InfoResponse, error)
+	Configure(context.Context, *SetConfigRequest) (*emptypb.Empty, error)
+	Info(context.Context, *emptypb.Empty) (*InfoResponse, error)
+	SSHAddress(context.Context, *emptypb.Empty) (*SSHAddressResponse, error)
 	mustEmbedUnimplementedDriverServer()
 }
 
@@ -296,8 +309,8 @@ type UnimplementedDriverServer struct{}
 func (UnimplementedDriverServer) Validate(context.Context, *emptypb.Empty) (*emptypb.Empty, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method Validate not implemented")
 }
-func (UnimplementedDriverServer) Initialize(context.Context, *emptypb.Empty) (*emptypb.Empty, error) {
-	return nil, status.Errorf(codes.Unimplemented, "method Initialize not implemented")
+func (UnimplementedDriverServer) Create(context.Context, *emptypb.Empty) (*emptypb.Empty, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method Create not implemented")
 }
 func (UnimplementedDriverServer) CreateDisk(context.Context, *emptypb.Empty) (*emptypb.Empty, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method CreateDisk not implemented")
@@ -308,6 +321,12 @@ func (UnimplementedDriverServer) Start(*emptypb.Empty, grpc.ServerStreamingServe
 func (UnimplementedDriverServer) Stop(context.Context, *emptypb.Empty) (*emptypb.Empty, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method Stop not implemented")
 }
+func (UnimplementedDriverServer) Delete(context.Context, *emptypb.Empty) (*emptypb.Empty, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method Delete not implemented")
+}
+func (UnimplementedDriverServer) BootScripts(context.Context, *emptypb.Empty) (*BootScriptsResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method BootScripts not implemented")
+}
 func (UnimplementedDriverServer) RunGUI(context.Context, *emptypb.Empty) (*emptypb.Empty, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method RunGUI not implemented")
 }
@@ -329,23 +348,20 @@ func (UnimplementedDriverServer) DeleteSnapshot(context.Context, *DeleteSnapshot
 func (UnimplementedDriverServer) ListSnapshots(context.Context, *emptypb.Empty) (*ListSnapshotsResponse, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method ListSnapshots not implemented")
 }
-func (UnimplementedDriverServer) Register(context.Context, *emptypb.Empty) (*emptypb.Empty, error) {
-	return nil, status.Errorf(codes.Unimplemented, "method Register not implemented")
-}
-func (UnimplementedDriverServer) Unregister(context.Context, *emptypb.Empty) (*emptypb.Empty, error) {
-	return nil, status.Errorf(codes.Unimplemented, "method Unregister not implemented")
-}
 func (UnimplementedDriverServer) ForwardGuestAgent(context.Context, *emptypb.Empty) (*ForwardGuestAgentResponse, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method ForwardGuestAgent not implemented")
 }
 func (UnimplementedDriverServer) GuestAgentConn(context.Context, *emptypb.Empty) (*emptypb.Empty, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method GuestAgentConn not implemented")
 }
-func (UnimplementedDriverServer) SetConfig(context.Context, *SetConfigRequest) (*emptypb.Empty, error) {
-	return nil, status.Errorf(codes.Unimplemented, "method SetConfig not implemented")
+func (UnimplementedDriverServer) Configure(context.Context, *SetConfigRequest) (*emptypb.Empty, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method Configure not implemented")
+}
+func (UnimplementedDriverServer) Info(context.Context, *emptypb.Empty) (*InfoResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method Info not implemented")
 }
-func (UnimplementedDriverServer) GetInfo(context.Context, *emptypb.Empty) (*InfoResponse, error) {
-	return nil, status.Errorf(codes.Unimplemented, "method GetInfo not implemented")
+func (UnimplementedDriverServer) SSHAddress(context.Context, *emptypb.Empty) (*SSHAddressResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method SSHAddress not implemented")
 }
 func (UnimplementedDriverServer) mustEmbedUnimplementedDriverServer() {}
 func (UnimplementedDriverServer) testEmbeddedByValue()                {}
@@ -386,20 +402,20 @@ func _Driver_Validate_Handler(srv interface{}, ctx context.Context, dec func(int
 	return interceptor(ctx, in, info, handler)
 }
 
-func _Driver_Initialize_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+func _Driver_Create_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
 	in := new(emptypb.Empty)
 	if err := dec(in); err != nil {
 		return nil, err
 	}
 	if interceptor == nil {
-		return srv.(DriverServer).Initialize(ctx, in)
+		return srv.(DriverServer).Create(ctx, in)
 	}
 	info := &grpc.UnaryServerInfo{
 		Server:     srv,
-		FullMethod: Driver_Initialize_FullMethodName,
+		FullMethod: Driver_Create_FullMethodName,
 	}
 	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
-		return srv.(DriverServer).Initialize(ctx, req.(*emptypb.Empty))
+		return srv.(DriverServer).Create(ctx, req.(*emptypb.Empty))
 	}
 	return interceptor(ctx, in, info, handler)
 }
@@ -451,6 +467,42 @@ func _Driver_Stop_Handler(srv interface{}, ctx context.Context, dec func(interfa
 	return interceptor(ctx, in, info, handler)
 }
 
+func _Driver_Delete_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(emptypb.Empty)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(DriverServer).Delete(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: Driver_Delete_FullMethodName,
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(DriverServer).Delete(ctx, req.(*emptypb.Empty))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _Driver_BootScripts_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(emptypb.Empty)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(DriverServer).BootScripts(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: Driver_BootScripts_FullMethodName,
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(DriverServer).BootScripts(ctx, req.(*emptypb.Empty))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
 func _Driver_RunGUI_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
 	in := new(emptypb.Empty)
 	if err := dec(in); err != nil {
@@ -577,110 +629,92 @@ func _Driver_ListSnapshots_Handler(srv interface{}, ctx context.Context, dec fun
 	return interceptor(ctx, in, info, handler)
 }
 
-func _Driver_Register_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+func _Driver_ForwardGuestAgent_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
 	in := new(emptypb.Empty)
 	if err := dec(in); err != nil {
 		return nil, err
 	}
 	if interceptor == nil {
-		return srv.(DriverServer).Register(ctx, in)
+		return srv.(DriverServer).ForwardGuestAgent(ctx, in)
 	}
 	info := &grpc.UnaryServerInfo{
 		Server:     srv,
-		FullMethod: Driver_Register_FullMethodName,
+		FullMethod: Driver_ForwardGuestAgent_FullMethodName,
 	}
 	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
-		return srv.(DriverServer).Register(ctx, req.(*emptypb.Empty))
+		return srv.(DriverServer).ForwardGuestAgent(ctx, req.(*emptypb.Empty))
 	}
 	return interceptor(ctx, in, info, handler)
 }
 
-func _Driver_Unregister_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+func _Driver_GuestAgentConn_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
 	in := new(emptypb.Empty)
 	if err := dec(in); err != nil {
 		return nil, err
 	}
 	if interceptor == nil {
-		return srv.(DriverServer).Unregister(ctx, in)
+		return srv.(DriverServer).GuestAgentConn(ctx, in)
 	}
 	info := &grpc.UnaryServerInfo{
 		Server:     srv,
-		FullMethod: Driver_Unregister_FullMethodName,
+		FullMethod: Driver_GuestAgentConn_FullMethodName,
 	}
 	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
-		return srv.(DriverServer).Unregister(ctx, req.(*emptypb.Empty))
+		return srv.(DriverServer).GuestAgentConn(ctx, req.(*emptypb.Empty))
 	}
 	return interceptor(ctx, in, info, handler)
 }
 
-func _Driver_ForwardGuestAgent_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
-	in := new(emptypb.Empty)
+func _Driver_Configure_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(SetConfigRequest)
 	if err := dec(in); err != nil {
 		return nil, err
 	}
 	if interceptor == nil {
-		return srv.(DriverServer).ForwardGuestAgent(ctx, in)
+		return srv.(DriverServer).Configure(ctx, in)
 	}
 	info := &grpc.UnaryServerInfo{
 		Server:     srv,
-		FullMethod: Driver_ForwardGuestAgent_FullMethodName,
+		FullMethod: Driver_Configure_FullMethodName,
 	}
 	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
-		return srv.(DriverServer).ForwardGuestAgent(ctx, req.(*emptypb.Empty))
+		return srv.(DriverServer).Configure(ctx, req.(*SetConfigRequest))
 	}
 	return interceptor(ctx, in, info, handler)
 }
 
-func _Driver_GuestAgentConn_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+func _Driver_Info_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
 	in := new(emptypb.Empty)
 	if err := dec(in); err != nil {
 		return nil, err
 	}
 	if interceptor == nil {
-		return srv.(DriverServer).GuestAgentConn(ctx, in)
+		return srv.(DriverServer).Info(ctx, in)
 	}
 	info := &grpc.UnaryServerInfo{
 		Server:     srv,
-		FullMethod: Driver_GuestAgentConn_FullMethodName,
+		FullMethod: Driver_Info_FullMethodName,
 	}
 	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
-		return srv.(DriverServer).GuestAgentConn(ctx, req.(*emptypb.Empty))
+		return srv.(DriverServer).Info(ctx, req.(*emptypb.Empty))
 	}
 	return interceptor(ctx, in, info, handler)
 }
 
-func _Driver_SetConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
-	in := new(SetConfigRequest)
-	if err := dec(in); err != nil {
-		return nil, err
-	}
-	if interceptor == nil {
-		return srv.(DriverServer).SetConfig(ctx, in)
-	}
-	info := &grpc.UnaryServerInfo{
-		Server:     srv,
-		FullMethod: Driver_SetConfig_FullMethodName,
-	}
-	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
-		return srv.(DriverServer).SetConfig(ctx, req.(*SetConfigRequest))
-	}
-	return interceptor(ctx, in, info, handler)
-}
-
-func _Driver_GetInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+func _Driver_SSHAddress_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
 	in := new(emptypb.Empty)
 	if err := dec(in); err != nil {
 		return nil, err
 	}
 	if interceptor == nil {
-		return srv.(DriverServer).GetInfo(ctx, in)
+		return srv.(DriverServer).SSHAddress(ctx, in)
 	}
 	info := &grpc.UnaryServerInfo{
 		Server:     srv,
-		FullMethod: Driver_GetInfo_FullMethodName,
+		FullMethod: Driver_SSHAddress_FullMethodName,
 	}
 	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
-		return srv.(DriverServer).GetInfo(ctx, req.(*emptypb.Empty))
+		return srv.(DriverServer).SSHAddress(ctx, req.(*emptypb.Empty))
 	}
 	return interceptor(ctx, in, info, handler)
 }
@@ -697,8 +731,8 @@ var Driver_ServiceDesc = grpc.ServiceDesc{
 			Handler:    _Driver_Validate_Handler,
 		},
 		{
-			MethodName: "Initialize",
-			Handler:    _Driver_Initialize_Handler,
+			MethodName: "Create",
+			Handler:    _Driver_Create_Handler,
 		},
 		{
 			MethodName: "CreateDisk",
@@ -708,6 +742,14 @@ var Driver_ServiceDesc = grpc.ServiceDesc{
 			MethodName: "Stop",
 			Handler:    _Driver_Stop_Handler,
 		},
+		{
+			MethodName: "Delete",
+			Handler:    _Driver_Delete_Handler,
+		},
+		{
+			MethodName: "BootScripts",
+			Handler:    _Driver_BootScripts_Handler,
+		},
 		{
 			MethodName: "RunGUI",
 			Handler:    _Driver_RunGUI_Handler,
@@ -736,14 +778,6 @@ var Driver_ServiceDesc = grpc.ServiceDesc{
 			MethodName: "ListSnapshots",
 			Handler:    _Driver_ListSnapshots_Handler,
 		},
-		{
-			MethodName: "Register",
-			Handler:    _Driver_Register_Handler,
-		},
-		{
-			MethodName: "Unregister",
-			Handler:    _Driver_Unregister_Handler,
-		},
 		{
 			MethodName: "ForwardGuestAgent",
 			Handler:    _Driver_ForwardGuestAgent_Handler,
@@ -753,12 +787,16 @@ var Driver_ServiceDesc = grpc.ServiceDesc{
 			Handler:    _Driver_GuestAgentConn_Handler,
 		},
 		{
-			MethodName: "SetConfig",
-			Handler:    _Driver_SetConfig_Handler,
+			MethodName: "Configure",
+			Handler:    _Driver_Configure_Handler,
+		},
+		{
+			MethodName: "Info",
+			Handler:    _Driver_Info_Handler,
 		},
 		{
-			MethodName: "GetInfo",
-			Handler:    _Driver_GetInfo_Handler,
+			MethodName: "SSHAddress",
+			Handler:    _Driver_SSHAddress_Handler,
 		},
 	},
 	Streams: []grpc.StreamDesc{
diff --git a/pkg/driver/external/server/methods.go b/pkg/driver/external/server/methods.go
index 73cfafed..44fdcc54 100644
--- a/pkg/driver/external/server/methods.go
+++ b/pkg/driver/external/server/methods.go
@@ -6,6 +6,7 @@ package server
 import (
 	"context"
 	"encoding/json"
+	"maps"
 	"net"
 	"path/filepath"
 
@@ -16,8 +17,8 @@ import (
 
 	"github.com/lima-vm/lima/v2/pkg/bicopy"
 	pb "github.com/lima-vm/lima/v2/pkg/driver/external"
-	"github.com/lima-vm/lima/v2/pkg/store"
-	"github.com/lima-vm/lima/v2/pkg/store/filenames"
+	"github.com/lima-vm/lima/v2/pkg/limatype"
+	"github.com/lima-vm/lima/v2/pkg/limatype/filenames"
 )
 
 func (s *DriverServer) Start(_ *emptypb.Empty, stream pb.Driver_StartServer) error {
@@ -53,9 +54,9 @@ func (s *DriverServer) Start(_ *emptypb.Empty, stream pb.Driver_StartServer) err
 	}
 }
 
-func (s *DriverServer) SetConfig(_ context.Context, req *pb.SetConfigRequest) (*emptypb.Empty, error) {
+func (s *DriverServer) Configure(_ context.Context, req *pb.SetConfigRequest) (*emptypb.Empty, error) {
 	s.logger.Debugf("Received SetConfig request")
-	var inst store.Instance
+	var inst limatype.Instance
 
 	if err := inst.UnmarshalJSON(req.InstanceConfigJson); err != nil {
 		s.logger.Errorf("Failed to unmarshal InstanceConfigJson: %v", err)
@@ -102,7 +103,7 @@ func (s *DriverServer) GuestAgentConn(ctx context.Context, _ *emptypb.Empty) (*e
 	return &emptypb.Empty{}, nil
 }
 
-func (s *DriverServer) GetInfo(_ context.Context, _ *emptypb.Empty) (*pb.InfoResponse, error) {
+func (s *DriverServer) Info(_ context.Context, _ *emptypb.Empty) (*pb.InfoResponse, error) {
 	s.logger.Debug("Received GetInfo request")
 	info := s.driver.Info()
 
@@ -128,9 +129,9 @@ func (s *DriverServer) Validate(ctx context.Context, empty *emptypb.Empty) (*emp
 	return empty, nil
 }
 
-func (s *DriverServer) Initialize(ctx context.Context, empty *emptypb.Empty) (*emptypb.Empty, error) {
+func (s *DriverServer) Create(ctx context.Context, empty *emptypb.Empty) (*emptypb.Empty, error) {
 	s.logger.Debug("Received Initialize request")
-	err := s.driver.Initialize(ctx)
+	err := s.driver.Create(ctx)
 	if err != nil {
 		s.logger.Errorf("Initialization failed: %v", err)
 		return empty, err
@@ -172,6 +173,47 @@ func (s *DriverServer) RunGUI(_ context.Context, empty *emptypb.Empty) (*emptypb
 	return empty, nil
 }
 
+func (s *DriverServer) Delete(ctx context.Context, empty *emptypb.Empty) (*emptypb.Empty, error) {
+	s.logger.Debug("Received Delete request")
+	err := s.driver.Delete(ctx)
+	if err != nil {
+		s.logger.Errorf("Delete failed: %v", err)
+		return empty, err
+	}
+	s.logger.Debug("Delete succeeded")
+	return empty, nil
+}
+
+func (s *DriverServer) BootScripts(_ context.Context, _ *emptypb.Empty) (*pb.BootScriptsResponse, error) {
+	s.logger.Debug("Received BootScripts request")
+	scripts, err := s.driver.BootScripts()
+	if err != nil {
+		s.logger.Errorf("BootScripts failed: %v", err)
+		return nil, err
+	}
+
+	resp := &pb.BootScriptsResponse{
+		Scripts: make(map[string][]byte),
+	}
+
+	maps.Copy(resp.Scripts, scripts)
+
+	s.logger.Debugf("BootScripts succeeded with %d scripts", len(resp.Scripts))
+	return resp, nil
+}
+
+func (s *DriverServer) SSHAddress(ctx context.Context, _ *emptypb.Empty) (*pb.SSHAddressResponse, error) {
+	s.logger.Debug("Received SSHAddress request")
+	address, err := s.driver.SSHAddress(ctx)
+	if err != nil {
+		s.logger.Errorf("SSHAddress failed: %v", err)
+		return nil, err
+	}
+
+	s.logger.Debugf("SSHAddress succeeded with address: %s", address)
+	return &pb.SSHAddressResponse{Address: address}, nil
+}
+
 func (s *DriverServer) ChangeDisplayPassword(ctx context.Context, req *pb.ChangeDisplayPasswordRequest) (*emptypb.Empty, error) {
 	s.logger.Debug("Received ChangeDisplayPassword request")
 	err := s.driver.ChangeDisplayPassword(ctx, req.Password)
@@ -238,28 +280,6 @@ func (s *DriverServer) ListSnapshots(ctx context.Context, _ *emptypb.Empty) (*pb
 	return &pb.ListSnapshotsResponse{Snapshots: snapshots}, nil
 }
 
-func (s *DriverServer) Register(ctx context.Context, empty *emptypb.Empty) (*emptypb.Empty, error) {
-	s.logger.Debug("Received Register request")
-	err := s.driver.Register(ctx)
-	if err != nil {
-		s.logger.Errorf("Register failed: %v", err)
-		return empty, err
-	}
-	s.logger.Debug("Register succeeded")
-	return empty, nil
-}
-
-func (s *DriverServer) Unregister(ctx context.Context, empty *emptypb.Empty) (*emptypb.Empty, error) {
-	s.logger.Debug("Received Unregister request")
-	err := s.driver.Unregister(ctx)
-	if err != nil {
-		s.logger.Errorf("Unregister failed: %v", err)
-		return empty, err
-	}
-	s.logger.Debug("Unregister succeeded")
-	return empty, nil
-}
-
 func (s *DriverServer) ForwardGuestAgent(_ context.Context, _ *emptypb.Empty) (*pb.ForwardGuestAgentResponse, error) {
 	s.logger.Debug("Received ForwardGuestAgent request")
 	return &pb.ForwardGuestAgentResponse{ShouldForward: s.driver.ForwardGuestAgent()}, nil
diff --git a/pkg/driver/external/server/server.go b/pkg/driver/external/server/server.go
index a40d4b1b..2f5c37c9 100644
--- a/pkg/driver/external/server/server.go
+++ b/pkg/driver/external/server/server.go
@@ -8,6 +8,7 @@ import (
 	"context"
 	"encoding/json"
 	"errors"
+	"flag"
 	"fmt"
 	"net"
 	"os"
@@ -25,9 +26,10 @@ import (
 	"github.com/lima-vm/lima/v2/pkg/driver"
 	pb "github.com/lima-vm/lima/v2/pkg/driver/external"
 	"github.com/lima-vm/lima/v2/pkg/driver/external/client"
+	"github.com/lima-vm/lima/v2/pkg/limatype"
+	"github.com/lima-vm/lima/v2/pkg/limatype/dirnames"
+	"github.com/lima-vm/lima/v2/pkg/limatype/filenames"
 	"github.com/lima-vm/lima/v2/pkg/registry"
-	"github.com/lima-vm/lima/v2/pkg/store"
-	"github.com/lima-vm/lima/v2/pkg/store/filenames"
 )
 
 type DriverServer struct {
@@ -51,10 +53,22 @@ func (t *listenerTracker) Accept() (net.Conn, error) {
 }
 
 func Serve(ctx context.Context, driver driver.Driver) {
+	preConfiguredDriverAction := flag.Bool("pre-driver-action", false, "Run pre-driver action before starting the gRPC server")
+	inspectStatus := flag.Bool("inspect-status", false, "Inspect status of the driver")
+	flag.Parse()
+	if *preConfiguredDriverAction {
+		handlePreConfiguredDriverAction(driver)
+		return
+	}
+	if *inspectStatus {
+		handleInspectStatus(driver)
+		return
+	}
+
 	logger := logrus.New()
 	logger.SetLevel(logrus.DebugLevel)
 
-	socketPath := filepath.Join(os.TempDir(), fmt.Sprintf("lima-driver-%s-%d.sock", driver.Info().DriverName, os.Getpid()))
+	socketPath := filepath.Join(os.TempDir(), fmt.Sprintf("lima-driver-%s-%d.sock", driver.Info().Features.DriverName, os.Getpid()))
 
 	defer func() {
 		if err := os.Remove(socketPath); err != nil && !os.IsNotExist(err) {
@@ -115,7 +129,6 @@ func Serve(ctx context.Context, driver driver.Driver) {
 		logger.Info("Received shutdown signal, stopping server...")
 		closeShutdown()
 	}()
-
 	go func() {
 		timer := time.NewTimer(60 * time.Second)
 		defer timer.Stop()
@@ -133,7 +146,7 @@ func Serve(ctx context.Context, driver driver.Driver) {
 	}()
 
 	go func() {
-		logger.Infof("Starting external driver server for %s", driver.Info().DriverName)
+		logger.Infof("Starting external driver server for %s", driver.Info().Features.DriverName)
 		logger.Infof("Server starting on Unix socket: %s", socketPath)
 		if err := server.Serve(tListener); err != nil {
 			if errors.Is(err, grpc.ErrServerStopped) {
@@ -148,6 +161,56 @@ func Serve(ctx context.Context, driver driver.Driver) {
 	server.GracefulStop()
 }
 
+func handleInspectStatus(driver driver.Driver) {
+	decoder := json.NewDecoder(os.Stdin)
+	encoder := json.NewEncoder(os.Stdout)
+
+	var payload []byte
+	if err := decoder.Decode(&payload); err != nil {
+		fmt.Fprintf(os.Stderr, "Failed to decode instance payload from stdin: %v", err)
+	}
+
+	var inst limatype.Instance
+	if err := inst.UnmarshalJSON(payload); err != nil {
+		fmt.Fprintf(os.Stderr, "Failed to unmarshal instance: %v", err)
+	}
+
+	status := driver.InspectStatus(context.Background(), &inst)
+	inst.Status = status
+
+	resp, err := inst.MarshalJSON()
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "Failed to marshal instance response: %v", err)
+	}
+
+	if err := encoder.Encode(resp); err != nil {
+		fmt.Fprintf(os.Stderr, "Failed to encode instance response: %v", err)
+	}
+}
+
+func handlePreConfiguredDriverAction(driver driver.Driver) {
+	decoder := json.NewDecoder(os.Stdin)
+	encoder := json.NewEncoder(os.Stdout)
+
+	var payload limatype.PreConfiguredDriverPayload
+	if err := decoder.Decode(&payload); err != nil {
+		fmt.Fprintf(os.Stderr, "Failed to decode pre-configured driver payload from stdin: %v", err)
+	}
+
+	config := &payload.Config
+	if err := driver.AcceptConfig(config, payload.FilePath); err != nil {
+		fmt.Fprintf(os.Stderr, "Failed to accept config: %v", err)
+	}
+
+	if err := driver.FillConfig(config, payload.FilePath); err != nil {
+		fmt.Fprintf(os.Stderr, "Failed to fill config: %v", err)
+	}
+
+	if err := encoder.Encode(*config); err != nil {
+		fmt.Fprintf(os.Stderr, "Error encoding response: %v", err)
+	}
+}
+
 func Start(extDriver *registry.ExternalDriver, instName string) error {
 	extDriver.Logger.Debugf("Starting external driver at %s", extDriver.Path)
 	if instName == "" {
@@ -164,7 +227,7 @@ func Start(extDriver *registry.ExternalDriver, instName string) error {
 		return fmt.Errorf("failed to create stdout pipe for external driver: %w", err)
 	}
 
-	instanceDir, err := store.InstanceDir(extDriver.InstanceName)
+	instanceDir, err := dirnames.InstanceDir(extDriver.InstanceName)
 	if err != nil {
 		cancel()
 		return fmt.Errorf("failed to determine instance directory: %w", err)
diff --git a/pkg/driver/qemu/qemu.go b/pkg/driver/qemu/qemu.go
index 2138893e..858dad02 100644
--- a/pkg/driver/qemu/qemu.go
+++ b/pkg/driver/qemu/qemu.go
@@ -30,19 +30,20 @@ import (
 
 	"github.com/lima-vm/lima/v2/pkg/fileutils"
 	"github.com/lima-vm/lima/v2/pkg/iso9660util"
+	"github.com/lima-vm/lima/v2/pkg/limatype"
+	"github.com/lima-vm/lima/v2/pkg/limatype/filenames"
 	"github.com/lima-vm/lima/v2/pkg/limayaml"
 	"github.com/lima-vm/lima/v2/pkg/networks"
 	"github.com/lima-vm/lima/v2/pkg/networks/usernet"
 	"github.com/lima-vm/lima/v2/pkg/osutil"
 	"github.com/lima-vm/lima/v2/pkg/qemuimgutil"
 	"github.com/lima-vm/lima/v2/pkg/store"
-	"github.com/lima-vm/lima/v2/pkg/store/filenames"
 )
 
 type Config struct {
 	Name         string
 	InstanceDir  string
-	LimaYAML     *limayaml.LimaYAML
+	LimaYAML     *limatype.LimaYAML
 	SSHLocalPort int
 	SSHAddress   string
 	VirtioGA     bool
@@ -384,8 +385,8 @@ func adjustMemBytesDarwinARM64HVF(memBytes int64, accel string) int64 {
 }
 
 // qemuMachine returns string to use for -machine.
-func qemuMachine(arch limayaml.Arch) string {
-	if arch == limayaml.X8664 {
+func qemuMachine(arch limatype.Arch) string {
+	if arch == limatype.X8664 {
 		return "q35"
 	}
 	return "virt"
@@ -404,7 +405,7 @@ func audioDevice() string {
 	return "oss"
 }
 
-func defaultCPUType() limayaml.CPUType {
+func defaultCPUType() limatype.CPUType {
 	// x86_64 + TCG + max was previously unstable until 2021.
 	// https://bugzilla.redhat.com/show_bug.cgi?id=1999700
 	// https://bugs.launchpad.net/qemu/+bug/1748296
@@ -417,13 +418,13 @@ func defaultCPUType() limayaml.CPUType {
 		// TODO: remove this if "max" works with the latest qemu
 		defaultX8664 = "qemu64"
 	}
-	cpuType := map[limayaml.Arch]string{
-		limayaml.AARCH64: "max",
-		limayaml.ARMV7L:  "max",
-		limayaml.X8664:   defaultX8664,
-		limayaml.PPC64LE: "max",
-		limayaml.RISCV64: "max",
-		limayaml.S390X:   "max",
+	cpuType := map[limatype.Arch]string{
+		limatype.AARCH64: "max",
+		limatype.ARMV7L:  "max",
+		limatype.X8664:   defaultX8664,
+		limatype.PPC64LE: "max",
+		limatype.RISCV64: "max",
+		limatype.S390X:   "max",
 	}
 	for arch := range cpuType {
 		if limayaml.IsNativeArch(arch) && limayaml.IsAccelOS() {
@@ -431,7 +432,7 @@ func defaultCPUType() limayaml.CPUType {
 				cpuType[arch] = "host"
 			}
 		}
-		if arch == limayaml.X8664 && runtime.GOOS == "darwin" {
+		if arch == limatype.X8664 && runtime.GOOS == "darwin" {
 			// disable AVX-512, since it requires trapping instruction faults in guest
 			// Enterprise Linux requires either v2 (SSE4) or v3 (AVX2), but not yet v4.
 			cpuType[arch] += ",-avx512vl"
@@ -445,11 +446,11 @@ func defaultCPUType() limayaml.CPUType {
 	return cpuType
 }
 
-func resolveCPUType(y *limayaml.LimaYAML) string {
+func resolveCPUType(y *limatype.LimaYAML) string {
 	cpuType := defaultCPUType()
 	var overrideCPUType bool
 	for k, v := range y.VMOpts.QEMU.CPUType {
-		if !slices.Contains(limayaml.ArchTypes, *y.Arch) {
+		if !slices.Contains(limatype.ArchTypes, *y.Arch) {
 			logrus.Warnf("field `vmOpts.qemu.cpuType` uses unsupported arch %q", k)
 			continue
 		}
@@ -508,7 +509,7 @@ func Cmdline(ctx context.Context, cfg Config) (exe string, args []string, err er
 	memBytes = adjustMemBytesDarwinARM64HVF(memBytes, accel)
 	args = appendArgsIfNoConflict(args, "-m", strconv.Itoa(int(memBytes>>20)))
 
-	if *y.MountType == limayaml.VIRTIOFS {
+	if *y.MountType == limatype.VIRTIOFS {
 		args = appendArgsIfNoConflict(args, "-object",
 			fmt.Sprintf("memory-backend-file,id=virtiofs-shm,size=%s,mem-path=/dev/shm,share=on", strconv.Itoa(int(memBytes))))
 		args = appendArgsIfNoConflict(args, "-numa", "node,memdev=virtiofs-shm")
@@ -532,7 +533,7 @@ func Cmdline(ctx context.Context, cfg Config) (exe string, args []string, err er
 
 	// Machine
 	switch *y.Arch {
-	case limayaml.X8664:
+	case limatype.X8664:
 		switch accel {
 		case "tcg":
 			// use q35 machine with vmware io port disabled.
@@ -551,10 +552,10 @@ func Cmdline(ctx context.Context, cfg Config) (exe string, args []string, err er
 		default:
 			args = appendArgsIfNoConflict(args, "-machine", "q35,accel="+accel)
 		}
-	case limayaml.AARCH64:
+	case limatype.AARCH64:
 		machine := "virt,accel=" + accel
 		args = appendArgsIfNoConflict(args, "-machine", machine)
-	case limayaml.RISCV64:
+	case limatype.RISCV64:
 		// https://github.com/tianocore/edk2/blob/edk2-stable202408/OvmfPkg/RiscVVirt/README.md#test
 		// > Note: the `acpi=off` machine property is specified because Linux guest
 		// > support for ACPI (that is, the ACPI consumer side) is a work in progress.
@@ -562,13 +563,13 @@ func Cmdline(ctx context.Context, cfg Config) (exe string, args []string, err er
 		// > yourself.
 		machine := "virt,acpi=off,accel=" + accel
 		args = appendArgsIfNoConflict(args, "-machine", machine)
-	case limayaml.ARMV7L:
+	case limatype.ARMV7L:
 		machine := "virt,accel=" + accel
 		args = appendArgsIfNoConflict(args, "-machine", machine)
-	case limayaml.PPC64LE:
+	case limatype.PPC64LE:
 		machine := "pseries,accel=" + accel
 		args = appendArgsIfNoConflict(args, "-machine", machine)
-	case limayaml.S390X:
+	case limatype.S390X:
 		machine := "s390-ccw-virtio,accel=" + accel
 		args = appendArgsIfNoConflict(args, "-machine", machine)
 	}
@@ -579,11 +580,11 @@ func Cmdline(ctx context.Context, cfg Config) (exe string, args []string, err er
 
 	// Firmware
 	legacyBIOS := *y.Firmware.LegacyBIOS
-	if legacyBIOS && *y.Arch != limayaml.X8664 && *y.Arch != limayaml.ARMV7L {
+	if legacyBIOS && *y.Arch != limatype.X8664 && *y.Arch != limatype.ARMV7L {
 		logrus.Warnf("field `firmware.legacyBIOS` is not supported for architecture %q, ignoring", *y.Arch)
 		legacyBIOS = false
 	}
-	noFirmware := *y.Arch == limayaml.PPC64LE || *y.Arch == limayaml.S390X || legacyBIOS
+	noFirmware := *y.Arch == limatype.PPC64LE || *y.Arch == limatype.S390X || legacyBIOS
 	if !noFirmware {
 		var firmware string
 		firmwareInBios := runtime.GOOS == "windows"
@@ -595,7 +596,7 @@ func Cmdline(ctx context.Context, cfg Config) (exe string, args []string, err er
 				firmwareInBios = b
 			}
 		}
-		firmwareInBios = firmwareInBios && *y.Arch == limayaml.X8664
+		firmwareInBios = firmwareInBios && *y.Arch == limatype.X8664
 		downloadedFirmware := filepath.Join(cfg.InstanceDir, filenames.QemuEfiCodeFD)
 		firmwareWithVars := filepath.Join(cfg.InstanceDir, filenames.QemuEfiFullFD)
 		if firmwareInBios {
@@ -608,7 +609,7 @@ func Cmdline(ctx context.Context, cfg Config) (exe string, args []string, err er
 			loop:
 				for _, f := range y.Firmware.Images {
 					switch f.VMType {
-					case "", limayaml.QEMU:
+					case "", limatype.QEMU:
 						if f.Arch == *y.Arch {
 							if _, err = fileutils.DownloadFile(ctx, downloadedFirmware, f.File, true, "UEFI code "+f.Location, *y.Arch); err != nil {
 								logrus.WithError(err).Warnf("failed to download %q", f.Location)
@@ -768,7 +769,7 @@ func Cmdline(ctx context.Context, cfg Config) (exe string, args []string, err er
 		args = append(args, "-netdev", fmt.Sprintf("socket,id=net0,fd={{ fd_connect %q }}", qemuSock))
 	}
 	virtioNet := "virtio-net-pci"
-	if *y.Arch == limayaml.S390X {
+	if *y.Arch == limatype.S390X {
 		// virtio-net-pci does not work on EL, while it works on Ubuntu
 		// https://github.com/lima-vm/lima/pull/3319/files#r1986388345
 		virtioNet = "virtio-net-ccw"
@@ -854,7 +855,7 @@ func Cmdline(ctx context.Context, cfg Config) (exe string, args []string, err er
 	if *y.Video.Display != "none" {
 		switch *y.Arch {
 		// FIXME: use virtio-gpu on all the architectures
-		case limayaml.X8664, limayaml.RISCV64:
+		case limatype.X8664, limatype.RISCV64:
 			args = append(args, "-device", "virtio-vga")
 		default:
 			args = append(args, "-device", "virtio-gpu")
@@ -885,7 +886,7 @@ func Cmdline(ctx context.Context, cfg Config) (exe string, args []string, err er
 	// On ARM, the default serial is ttyAMA0, this PCI serial is ttyS0.
 	// https://gitlab.com/qemu-project/qemu/-/issues/1801#note_1494720586
 	switch *y.Arch {
-	case limayaml.AARCH64, limayaml.ARMV7L:
+	case limatype.AARCH64, limatype.ARMV7L:
 		serialpSock := filepath.Join(cfg.InstanceDir, filenames.SerialPCISock)
 		if err := os.RemoveAll(serialpSock); err != nil {
 			return "", nil, err
@@ -912,7 +913,7 @@ func Cmdline(ctx context.Context, cfg Config) (exe string, args []string, err er
 	args = append(args, "-chardev", fmt.Sprintf("socket,id=%s,path=%s,server=on,wait=off,logfile=%s", serialvChardev, serialvSock, serialvLog))
 	// max_ports=1 is required for https://github.com/lima-vm/lima/issues/1689 https://github.com/lima-vm/lima/issues/1691
 	serialvMaxPorts := 1
-	if *y.Arch == limayaml.S390X {
+	if *y.Arch == limatype.S390X {
 		serialvMaxPorts++ // needed to avoid `virtio-serial-bus: Out-of-range port id specified, max. allowed: 0`
 	}
 	args = append(args, "-device", fmt.Sprintf("virtio-serial-pci,id=virtio-serial0,max_ports=%d", serialvMaxPorts))
@@ -920,7 +921,7 @@ func Cmdline(ctx context.Context, cfg Config) (exe string, args []string, err er
 
 	// We also want to enable vsock here, but QEMU does not support vsock for macOS hosts
 
-	if *y.MountType == limayaml.NINEP || *y.MountType == limayaml.VIRTIOFS {
+	if *y.MountType == limatype.NINEP || *y.MountType == limatype.VIRTIOFS {
 		for i, f := range y.Mounts {
 			tag := fmt.Sprintf("mount%d", i)
 			if err := os.MkdirAll(f.Location, 0o755); err != nil {
@@ -928,7 +929,7 @@ func Cmdline(ctx context.Context, cfg Config) (exe string, args []string, err er
 			}
 
 			switch *y.MountType {
-			case limayaml.NINEP:
+			case limatype.NINEP:
 				options := "local"
 				options += fmt.Sprintf(",mount_tag=%s", tag)
 				options += fmt.Sprintf(",path=%s", f.Location)
@@ -937,7 +938,7 @@ func Cmdline(ctx context.Context, cfg Config) (exe string, args []string, err er
 					options += ",readonly"
 				}
 				args = append(args, "-virtfs", options)
-			case limayaml.VIRTIOFS:
+			case limatype.VIRTIOFS:
 				// Note that read-only mode is not supported on the QEMU/virtiofsd side yet:
 				// https://gitlab.com/virtio-fs/virtiofsd/-/issues/97
 				chardev := fmt.Sprintf("char-virtiofs-%d", i)
@@ -1067,11 +1068,11 @@ func VirtiofsdCmdline(cfg Config, mountIndex int) ([]string, error) {
 }
 
 // qemuArch returns the arch string used by qemu.
-func qemuArch(arch limayaml.Arch) string {
+func qemuArch(arch limatype.Arch) string {
 	switch arch {
-	case limayaml.ARMV7L:
+	case limatype.ARMV7L:
 		return "arm"
-	case limayaml.PPC64LE:
+	case limatype.PPC64LE:
 		return "ppc64"
 	default:
 		return arch
@@ -1079,14 +1080,14 @@ func qemuArch(arch limayaml.Arch) string {
 }
 
 // qemuEdk2 returns the arch string used by `/usr/local/share/qemu/edk2-*-code.fd`.
-func qemuEdk2Arch(arch limayaml.Arch) string {
-	if arch == limayaml.RISCV64 {
+func qemuEdk2Arch(arch limatype.Arch) string {
+	if arch == limatype.RISCV64 {
 		return "riscv"
 	}
 	return qemuArch(arch)
 }
 
-func Exe(arch limayaml.Arch) (exe string, args []string, err error) {
+func Exe(arch limatype.Arch) (exe string, args []string, err error) {
 	exeBase := "qemu-system-" + qemuArch(arch)
 	envK := "QEMU_SYSTEM_" + strings.ToUpper(qemuArch(arch))
 	if envV := os.Getenv(envK); envV != "" {
@@ -1106,7 +1107,7 @@ func Exe(arch limayaml.Arch) (exe string, args []string, err error) {
 	return exe, args, nil
 }
 
-func Accel(arch limayaml.Arch) string {
+func Accel(arch limatype.Arch) string {
 	if limayaml.IsNativeArch(arch) {
 		switch runtime.GOOS {
 		case "darwin":
@@ -1149,9 +1150,9 @@ func getQemuVersion(ctx context.Context, qemuExe string) (*semver.Version, error
 	return parseQemuVersion(stdout.String())
 }
 
-func getFirmware(qemuExe string, arch limayaml.Arch) (string, error) {
+func getFirmware(qemuExe string, arch limatype.Arch) (string, error) {
 	switch arch {
-	case limayaml.X8664, limayaml.AARCH64, limayaml.ARMV7L, limayaml.RISCV64:
+	case limatype.X8664, limatype.AARCH64, limatype.ARMV7L, limatype.RISCV64:
 	default:
 		return "", fmt.Errorf("unexpected architecture: %q", arch)
 	}
@@ -1174,7 +1175,7 @@ func getFirmware(qemuExe string, arch limayaml.Arch) (string, error) {
 	}
 
 	switch arch {
-	case limayaml.X8664:
+	case limatype.X8664:
 		// Archlinux package "edk2-ovmf"
 		// @see: https://archlinux.org/packages/extra/any/edk2-ovmf/files
 		candidates = append(candidates, "/usr/share/edk2/x64/OVMF_CODE.4m.fd")
@@ -1185,7 +1186,7 @@ func getFirmware(qemuExe string, arch limayaml.Arch) (string, error) {
 		candidates = append(candidates, "/usr/share/edk2/ovmf/OVMF_CODE.fd")
 		// openSUSE package "qemu-ovmf-x86_64"
 		candidates = append(candidates, "/usr/share/qemu/ovmf-x86_64.bin")
-	case limayaml.AARCH64:
+	case limatype.AARCH64:
 		// Archlinux package "edk2-aarch64"
 		// @see: https://archlinux.org/packages/extra/any/edk2-aarch64/files
 		candidates = append(candidates, "/usr/share/edk2/aarch64/QEMU_CODE.fd")
@@ -1194,14 +1195,14 @@ func getFirmware(qemuExe string, arch limayaml.Arch) (string, error) {
 		candidates = append(candidates, "/usr/share/AAVMF/AAVMF_CODE.fd")
 		// Debian package "qemu-efi-aarch64" (unpadded, backwards compatibility)
 		candidates = append(candidates, "/usr/share/qemu-efi-aarch64/QEMU_EFI.fd")
-	case limayaml.ARMV7L:
+	case limatype.ARMV7L:
 		// Archlinux package "edk2-arm"
 		// @see: https://archlinux.org/packages/extra/any/edk2-arm/files
 		candidates = append(candidates, "/usr/share/edk2/arm/QEMU_CODE.fd")
 		// Debian package "qemu-efi-arm"
 		// Fedora package "edk2-arm"
 		candidates = append(candidates, "/usr/share/AAVMF/AAVMF32_CODE.fd")
-	case limayaml.RISCV64:
+	case limatype.RISCV64:
 		// Debian package "qemu-efi-riscv64"
 		candidates = append(candidates, "/usr/share/qemu-efi-riscv64/RISCV_VIRT_CODE.fd")
 		// Fedora package "edk2-riscv64"
@@ -1216,17 +1217,17 @@ func getFirmware(qemuExe string, arch limayaml.Arch) (string, error) {
 		}
 	}
 
-	if arch == limayaml.X8664 {
+	if arch == limatype.X8664 {
 		return "", fmt.Errorf("could not find firmware for %q (hint: try setting `firmware.legacyBIOS` to `true`)", arch)
 	}
 	qemuArch := strings.TrimPrefix(filepath.Base(qemuExe), "qemu-system-")
 	return "", fmt.Errorf("could not find firmware for %q (hint: try copying the \"edk-%s-code.fd\" firmware to $HOME/.local/share/qemu/)", arch, qemuArch)
 }
 
-func getFirmwareVars(qemuExe string, arch limayaml.Arch) (string, error) {
+func getFirmwareVars(qemuExe string, arch limatype.Arch) (string, error) {
 	var targetArch string
 	switch arch {
-	case limayaml.X8664:
+	case limatype.X8664:
 		targetArch = "i386" // vars are unified between i386 and x86_64 and normally only former is bundled
 	default:
 		return "", fmt.Errorf("unexpected architecture: %q", arch)
diff --git a/pkg/driver/qemu/qemu_driver.go b/pkg/driver/qemu/qemu_driver.go
index 8e7f49e2..24ae8f2b 100644
--- a/pkg/driver/qemu/qemu_driver.go
+++ b/pkg/driver/qemu/qemu_driver.go
@@ -16,6 +16,7 @@ import (
 	"os/exec"
 	"path/filepath"
 	"runtime"
+	"slices"
 	"strconv"
 	"strings"
 	"text/template"
@@ -29,15 +30,17 @@ import (
 	"github.com/lima-vm/lima/v2/pkg/driver"
 	"github.com/lima-vm/lima/v2/pkg/driver/qemu/entitlementutil"
 	"github.com/lima-vm/lima/v2/pkg/executil"
+	"github.com/lima-vm/lima/v2/pkg/limatype"
+	"github.com/lima-vm/lima/v2/pkg/limatype/filenames"
 	"github.com/lima-vm/lima/v2/pkg/limayaml"
 	"github.com/lima-vm/lima/v2/pkg/networks/usernet"
 	"github.com/lima-vm/lima/v2/pkg/osutil"
-	"github.com/lima-vm/lima/v2/pkg/store"
-	"github.com/lima-vm/lima/v2/pkg/store/filenames"
+	"github.com/lima-vm/lima/v2/pkg/ptr"
+	"github.com/lima-vm/lima/v2/pkg/version/versionutil"
 )
 
 type LimaQemuDriver struct {
-	Instance     *store.Instance
+	Instance     *limatype.Instance
 	SSHLocalPort int
 	vSockPort    int
 	virtioPort   string
@@ -64,7 +67,7 @@ func New() *LimaQemuDriver {
 	}
 }
 
-func (l *LimaQemuDriver) Configure(inst *store.Instance) *driver.ConfiguredDriver {
+func (l *LimaQemuDriver) Configure(inst *limatype.Instance) *driver.ConfiguredDriver {
 	l.Instance = inst
 	l.SSHLocalPort = inst.SSHLocalPort
 
@@ -79,14 +82,155 @@ func (l *LimaQemuDriver) Validate(ctx context.Context) error {
 			return err
 		}
 	}
+	if err := l.validateMountType(); err != nil {
+		return err
+	}
+
+	return nil
+}
 
-	if *l.Instance.Config.MountType == limayaml.VIRTIOFS && runtime.GOOS != "linux" {
+// Helper method for mount type validation.
+func (l *LimaQemuDriver) validateMountType() error {
+	if l.Instance == nil || l.Instance.Config == nil {
+		return errors.New("instance configuration is not set")
+	}
+
+	cfg := l.Instance.Config
+
+	if cfg.MountType != nil && *cfg.MountType == limatype.VIRTIOFS && runtime.GOOS != "linux" {
 		return fmt.Errorf("field `mountType` must be %q or %q for QEMU driver on non-Linux, got %q",
-			limayaml.REVSSHFS, limayaml.NINEP, *l.Instance.Config.MountType)
+			limatype.REVSSHFS, limatype.NINEP, *cfg.MountType)
+	}
+	if cfg.MountTypesUnsupported != nil && cfg.MountType != nil && slices.Contains(cfg.MountTypesUnsupported, *cfg.MountType) {
+		return fmt.Errorf("mount type %q is explicitly unsupported", *cfg.MountType)
 	}
+	if runtime.GOOS == "windows" && cfg.MountType != nil && *cfg.MountType == limatype.NINEP {
+		return fmt.Errorf("mount type %q is not supported on Windows", limatype.NINEP)
+	}
+
 	return nil
 }
 
+func (l *LimaQemuDriver) FillConfig(cfg *limatype.LimaYAML, filePath string) error {
+	if cfg.VMType == nil {
+		cfg.VMType = ptr.Of(limatype.QEMU)
+	}
+
+	instDir := filepath.Dir(filePath)
+
+	if cfg.Video.VNC.Display == nil || *cfg.Video.VNC.Display == "" {
+		cfg.Video.VNC.Display = ptr.Of("127.0.0.1:0,to=9")
+	}
+
+	if cfg.VMOpts.QEMU.CPUType == nil {
+		cfg.VMOpts.QEMU.CPUType = limatype.CPUType{}
+	}
+
+	//nolint:staticcheck // Migration of top-level CPUTYPE if specified
+	if len(cfg.CPUType) > 0 {
+		logrus.Warn("The top-level `cpuType` field is deprecated and will be removed in a future release. Please migrate to `vmOpts.qemu.cpuType`.")
+		for arch, v := range cfg.CPUType {
+			if v == "" {
+				continue
+			}
+			if existing, ok := cfg.VMOpts.QEMU.CPUType[arch]; ok && existing != "" && existing != v {
+				logrus.Warnf("Conflicting cpuType for arch %q: top-level=%q, vmOpts.qemu=%q; using vmOpts.qemu value", arch, v, existing)
+				continue
+			}
+			cfg.VMOpts.QEMU.CPUType[arch] = v
+		}
+		cfg.CPUType = nil
+	}
+
+	mountTypesUnsupported := make(map[string]struct{})
+	for _, f := range cfg.MountTypesUnsupported {
+		mountTypesUnsupported[f] = struct{}{}
+	}
+
+	if runtime.GOOS == "windows" {
+		// QEMU for Windows does not support 9p
+		mountTypesUnsupported[limatype.NINEP] = struct{}{}
+	}
+
+	if cfg.MountType == nil || *cfg.MountType == "" || *cfg.MountType == "default" {
+		cfg.MountType = ptr.Of(limatype.NINEP)
+		if _, ok := mountTypesUnsupported[limatype.NINEP]; ok {
+			// Use REVSSHFS if the instance does not support 9p
+			cfg.MountType = ptr.Of(limatype.REVSSHFS)
+		} else if limayaml.IsExistingInstanceDir(instDir) && !versionutil.GreaterEqual(limayaml.ExistingLimaVersion(instDir), "1.0.0") {
+			// Use REVSSHFS if the instance was created with Lima prior to v1.0
+			cfg.MountType = ptr.Of(limatype.REVSSHFS)
+		}
+	}
+
+	for i := range cfg.Mounts {
+		mount := &cfg.Mounts[i]
+		if mount.Virtiofs.QueueSize == nil && *cfg.MountType == limatype.VIRTIOFS {
+			cfg.Mounts[i].Virtiofs.QueueSize = ptr.Of(limayaml.DefaultVirtiofsQueueSize)
+		}
+	}
+
+	if _, ok := mountTypesUnsupported[*cfg.MountType]; ok {
+		return fmt.Errorf("mount type %q is explicitly unsupported", *cfg.MountType)
+	}
+
+	return nil
+}
+
+func (l *LimaQemuDriver) AcceptConfig(cfg *limatype.LimaYAML, _ string) error {
+	if l.Instance == nil {
+		l.Instance = &limatype.Instance{}
+	}
+	l.Instance.Config = cfg
+
+	if err := l.Validate(context.Background()); err != nil {
+		return fmt.Errorf("config not supported by the QEMU driver: %w", err)
+	}
+
+	if cfg.VMOpts.QEMU.MinimumVersion != nil {
+		if _, err := semver.NewVersion(*cfg.VMOpts.QEMU.MinimumVersion); err != nil {
+			return fmt.Errorf("field `vmOpts.qemu.minimumVersion` must be a semvar value, got %q: %w", *cfg.VMOpts.QEMU.MinimumVersion, err)
+		}
+	}
+
+	if runtime.GOOS == "darwin" {
+		if cfg.Arch != nil && limayaml.IsNativeArch(*cfg.Arch) {
+			logrus.Debugf("ResolveVMType: resolved VMType %q (non-native arch=%q is specified in []*LimaYAML{o,y,d})", "qemu", *cfg.Arch)
+			return nil
+		}
+		if limayaml.ResolveArch(cfg.Arch) == limatype.X8664 && cfg.Firmware.LegacyBIOS != nil && *cfg.Firmware.LegacyBIOS {
+			logrus.Debugf("ResolveVMType: resolved VMType %q (firmware.legacyBIOS is specified in []*LimaYAML{o,y,d} on x86_64)", "qemu")
+			return nil
+		}
+		if cfg.MountType != nil && *cfg.MountType == limatype.NINEP {
+			logrus.Debugf("ResolveVMType: resolved VMType %q (mountType=%q is specified in []*LimaYAML{o,y,d})", "qemu", limatype.NINEP)
+			return nil
+		}
+		if cfg.Audio.Device != nil {
+			switch *cfg.Audio.Device {
+			case "", "none", "default", "vz":
+				// NOP
+			default:
+				logrus.Debugf("ResolveVMType: resolved VMType %q (audio.device=%q is specified in []*LimaYAML{o,y,d})", "qemu", *cfg.Audio.Device)
+				return nil
+			}
+		}
+		if cfg.Video.Display != nil {
+			display := *cfg.Video.Display
+			if display != "" && display != "none" && display != "default" && display != "vz" {
+				logrus.Debugf("ResolveVMType: resolved VMType %q (video.display=%q is specified in []*LimaYAML{o,y,d})", "qemu", display)
+				return nil
+			}
+		}
+	}
+
+	return nil
+}
+
+func (l *LimaQemuDriver) BootScripts() (map[string][]byte, error) {
+	return nil, nil
+}
+
 func (l *LimaQemuDriver) CreateDisk(ctx context.Context) error {
 	qCfg := Config{
 		Name:        l.Instance.Name,
@@ -118,7 +262,7 @@ func (l *LimaQemuDriver) Start(_ context.Context) (chan error, error) {
 	}
 
 	var vhostCmds []*exec.Cmd
-	if *l.Instance.Config.MountType == limayaml.VIRTIOFS {
+	if *l.Instance.Config.MountType == limatype.VIRTIOFS {
 		vhostExe, err := FindVirtiofsd(ctx, qExe)
 		if err != nil {
 			return nil, err
@@ -276,7 +420,7 @@ func (l *LimaQemuDriver) checkBinarySignature(ctx context.Context) error {
 		return err
 	}
 	// The codesign --xml option is only available on macOS Monterey and later
-	if !macOSProductVersion.LessThan(*semver.New("12.0.0")) {
+	if !macOSProductVersion.LessThan(*semver.New("12.0.0")) && l.Instance.Arch != "" {
 		qExe, _, err := Exe(l.Instance.Arch)
 		if err != nil {
 			return fmt.Errorf("failed to find the QEMU binary for the architecture %q: %w", l.Instance.Arch, err)
@@ -440,7 +584,7 @@ func (l *LimaQemuDriver) DeleteSnapshot(ctx context.Context, tag string) error {
 		InstanceDir: l.Instance.Dir,
 		LimaYAML:    l.Instance.Config,
 	}
-	return Del(ctx, qCfg, l.Instance.Status == store.StatusRunning, tag)
+	return Del(ctx, qCfg, l.Instance.Status == limatype.StatusRunning, tag)
 }
 
 func (l *LimaQemuDriver) CreateSnapshot(ctx context.Context, tag string) error {
@@ -449,7 +593,7 @@ func (l *LimaQemuDriver) CreateSnapshot(ctx context.Context, tag string) error {
 		InstanceDir: l.Instance.Dir,
 		LimaYAML:    l.Instance.Config,
 	}
-	return Save(ctx, qCfg, l.Instance.Status == store.StatusRunning, tag)
+	return Save(ctx, qCfg, l.Instance.Status == limatype.StatusRunning, tag)
 }
 
 func (l *LimaQemuDriver) ApplySnapshot(ctx context.Context, tag string) error {
@@ -458,7 +602,7 @@ func (l *LimaQemuDriver) ApplySnapshot(ctx context.Context, tag string) error {
 		InstanceDir: l.Instance.Dir,
 		LimaYAML:    l.Instance.Config,
 	}
-	return Load(ctx, qCfg, l.Instance.Status == store.StatusRunning, tag)
+	return Load(ctx, qCfg, l.Instance.Status == limatype.StatusRunning, tag)
 }
 
 func (l *LimaQemuDriver) ListSnapshots(ctx context.Context) (string, error) {
@@ -467,7 +611,7 @@ func (l *LimaQemuDriver) ListSnapshots(ctx context.Context) (string, error) {
 		InstanceDir: l.Instance.Dir,
 		LimaYAML:    l.Instance.Config,
 	}
-	return List(ctx, qCfg, l.Instance.Status == store.StatusRunning)
+	return List(ctx, qCfg, l.Instance.Status == limatype.StatusRunning)
 }
 
 func (l *LimaQemuDriver) GuestAgentConn(ctx context.Context) (net.Conn, string, error) {
@@ -533,14 +677,31 @@ func (l *LimaQemuDriver) Info() driver.Info {
 	if l.Instance != nil && l.Instance.Dir != "" {
 		info.InstanceDir = l.Instance.Dir
 	}
-	info.DriverName = "qemu"
-	info.CanRunGUI = false
 	info.VirtioPort = l.virtioPort
 	info.VsockPort = l.vSockPort
+
+	info.Features = driver.DriverFeatures{
+		DynamicSSHAddress:    false,
+		SkipSocketForwarding: false,
+		DriverName:           "qemu",
+		CanRunGUI:            false,
+	}
 	return info
 }
 
-func (l *LimaQemuDriver) Initialize(_ context.Context) error {
+func (l *LimaQemuDriver) SSHAddress(_ context.Context) (string, error) {
+	return "127.0.0.1", nil
+}
+
+func (l *LimaQemuDriver) InspectStatus(_ context.Context, _ *limatype.Instance) string {
+	return ""
+}
+
+func (l *LimaQemuDriver) Create(_ context.Context) error {
+	return nil
+}
+
+func (l *LimaQemuDriver) Delete(_ context.Context) error {
 	return nil
 }
 
diff --git a/pkg/cidata/cidata.TEMPLATE.d/boot/05-rosetta-volume.sh b/pkg/driver/vz/boot/05-rosetta-volume.sh
similarity index 96%
rename from pkg/cidata/cidata.TEMPLATE.d/boot/05-rosetta-volume.sh
rename to pkg/driver/vz/boot/05-rosetta-volume.sh
index 4a43ff3f..9fa517ae 100755
--- a/pkg/cidata/cidata.TEMPLATE.d/boot/05-rosetta-volume.sh
+++ b/pkg/driver/vz/boot/05-rosetta-volume.sh
@@ -27,7 +27,7 @@ if [ "$LIMA_CIDATA_ROSETTA_BINFMT" = "true" ]; then
 	[ ! -d "$(dirname "$binfmtd_conf")" ] || [ -f "$binfmtd_conf" ] || echo "$rosetta_binfmt" >"$binfmtd_conf"
 else
 	# unregister rosetta from binfmt_misc if it exists
-	[ ! -f "$binfmt_entry" ] || echo -1 | "$binfmt_entry"
+	[ ! -f "$binfmt_entry" ] || echo -1 >"$binfmt_entry"
 	# remove binfmt.d(5) configuration if it exists
 	[ ! -f "$binfmtd_conf" ] || rm "$binfmtd_conf"
 fi
diff --git a/pkg/driver/vz/disk.go b/pkg/driver/vz/disk.go
index 35f28f52..9cb81e06 100644
--- a/pkg/driver/vz/disk.go
+++ b/pkg/driver/vz/disk.go
@@ -14,11 +14,11 @@ import (
 
 	"github.com/lima-vm/lima/v2/pkg/imgutil/proxyimgutil"
 	"github.com/lima-vm/lima/v2/pkg/iso9660util"
-	"github.com/lima-vm/lima/v2/pkg/store"
-	"github.com/lima-vm/lima/v2/pkg/store/filenames"
+	"github.com/lima-vm/lima/v2/pkg/limatype"
+	"github.com/lima-vm/lima/v2/pkg/limatype/filenames"
 )
 
-func EnsureDisk(ctx context.Context, inst *store.Instance) error {
+func EnsureDisk(ctx context.Context, inst *limatype.Instance) error {
 	diffDisk := filepath.Join(inst.Dir, filenames.DiffDisk)
 	if _, err := os.Stat(diffDisk); err == nil || !errors.Is(err, os.ErrNotExist) {
 		// disk is already ensured
diff --git a/pkg/driver/vz/vm_darwin.go b/pkg/driver/vz/vm_darwin.go
index aec3a8a0..342f73fe 100644
--- a/pkg/driver/vz/vm_darwin.go
+++ b/pkg/driver/vz/vm_darwin.go
@@ -27,12 +27,13 @@ import (
 
 	"github.com/lima-vm/lima/v2/pkg/imgutil/proxyimgutil"
 	"github.com/lima-vm/lima/v2/pkg/iso9660util"
+	"github.com/lima-vm/lima/v2/pkg/limatype"
+	"github.com/lima-vm/lima/v2/pkg/limatype/filenames"
 	"github.com/lima-vm/lima/v2/pkg/limayaml"
 	"github.com/lima-vm/lima/v2/pkg/networks"
 	"github.com/lima-vm/lima/v2/pkg/networks/usernet"
 	"github.com/lima-vm/lima/v2/pkg/osutil"
 	"github.com/lima-vm/lima/v2/pkg/store"
-	"github.com/lima-vm/lima/v2/pkg/store/filenames"
 )
 
 // diskImageCachingMode is set to DiskImageCachingModeCached so as to avoid disk corruption on ARM:
@@ -51,7 +52,7 @@ type virtualMachineWrapper struct {
 // Hold all *os.File created via socketpair() so that they won't get garbage collected. f.FD() gets invalid if f gets garbage collected.
 var vmNetworkFiles = make([]*os.File, 1)
 
-func startVM(ctx context.Context, inst *store.Instance, sshLocalPort int) (*virtualMachineWrapper, chan error, error) {
+func startVM(ctx context.Context, inst *limatype.Instance, sshLocalPort int) (*virtualMachineWrapper, chan error, error) {
 	usernetClient, err := startUsernet(ctx, inst)
 	if err != nil {
 		return nil, nil, err
@@ -128,7 +129,7 @@ func startVM(ctx context.Context, inst *store.Instance, sshLocalPort int) (*virt
 	return wrapper, errCh, err
 }
 
-func startUsernet(ctx context.Context, inst *store.Instance) (*usernet.Client, error) {
+func startUsernet(ctx context.Context, inst *limatype.Instance) (*usernet.Client, error) {
 	if firstUsernetIndex := limayaml.FirstUsernetIndex(inst.Config); firstUsernetIndex != -1 {
 		nwName := inst.Config.Networks[firstUsernetIndex].Lima
 		return usernet.NewClientByName(nwName), nil
@@ -161,7 +162,7 @@ func startUsernet(ctx context.Context, inst *store.Instance) (*usernet.Client, e
 	return usernet.NewClient(endpointSock, subnetIP), err
 }
 
-func createVM(ctx context.Context, inst *store.Instance) (*vz.VirtualMachine, error) {
+func createVM(ctx context.Context, inst *limatype.Instance) (*vz.VirtualMachine, error) {
 	vmConfig, err := createInitialConfig(inst)
 	if err != nil {
 		return nil, err
@@ -207,7 +208,7 @@ func createVM(ctx context.Context, inst *store.Instance) (*vz.VirtualMachine, er
 	return vz.NewVirtualMachine(vmConfig)
 }
 
-func createInitialConfig(inst *store.Instance) (*vz.VirtualMachineConfiguration, error) {
+func createInitialConfig(inst *limatype.Instance) (*vz.VirtualMachineConfiguration, error) {
 	bootLoader, err := bootLoader(inst)
 	if err != nil {
 		return nil, err
@@ -229,7 +230,7 @@ func createInitialConfig(inst *store.Instance) (*vz.VirtualMachineConfiguration,
 	return vmConfig, nil
 }
 
-func attachPlatformConfig(inst *store.Instance, vmConfig *vz.VirtualMachineConfiguration) error {
+func attachPlatformConfig(inst *limatype.Instance, vmConfig *vz.VirtualMachineConfiguration) error {
 	machineIdentifier, err := getMachineIdentifier(inst)
 	if err != nil {
 		return err
@@ -264,7 +265,7 @@ func attachPlatformConfig(inst *store.Instance, vmConfig *vz.VirtualMachineConfi
 	return nil
 }
 
-func attachSerialPort(inst *store.Instance, config *vz.VirtualMachineConfiguration) error {
+func attachSerialPort(inst *limatype.Instance, config *vz.VirtualMachineConfiguration) error {
 	path := filepath.Join(inst.Dir, filenames.SerialVirtioLog)
 	serialPortAttachment, err := vz.NewFileSerialPortAttachment(path, false)
 	if err != nil {
@@ -302,7 +303,7 @@ func newVirtioNetworkDeviceConfiguration(attachment vz.NetworkDeviceAttachment,
 	return networkConfig, nil
 }
 
-func attachNetwork(ctx context.Context, inst *store.Instance, vmConfig *vz.VirtualMachineConfiguration) error {
+func attachNetwork(ctx context.Context, inst *limatype.Instance, vmConfig *vz.VirtualMachineConfiguration) error {
 	var configurations []*vz.VirtioNetworkDeviceConfiguration
 
 	// Configure default usernetwork with limayaml.MACAddress(inst.Dir) for eth0 interface
@@ -434,7 +435,7 @@ func validateDiskFormat(diskPath string) error {
 	return nil
 }
 
-func attachDisks(ctx context.Context, inst *store.Instance, vmConfig *vz.VirtualMachineConfiguration) error {
+func attachDisks(ctx context.Context, inst *limatype.Instance, vmConfig *vz.VirtualMachineConfiguration) error {
 	baseDiskPath := filepath.Join(inst.Dir, filenames.BaseDisk)
 	diffDiskPath := filepath.Join(inst.Dir, filenames.DiffDisk)
 	ciDataPath := filepath.Join(inst.Dir, filenames.CIDataISO)
@@ -523,7 +524,7 @@ func attachDisks(ctx context.Context, inst *store.Instance, vmConfig *vz.Virtual
 	return nil
 }
 
-func attachDisplay(inst *store.Instance, vmConfig *vz.VirtualMachineConfiguration) error {
+func attachDisplay(inst *limatype.Instance, vmConfig *vz.VirtualMachineConfiguration) error {
 	switch *inst.Config.Video.Display {
 	case "vz", "default":
 		graphicsDeviceConfiguration, err := vz.NewVirtioGraphicsDeviceConfiguration()
@@ -547,9 +548,9 @@ func attachDisplay(inst *store.Instance, vmConfig *vz.VirtualMachineConfiguratio
 	}
 }
 
-func attachFolderMounts(inst *store.Instance, vmConfig *vz.VirtualMachineConfiguration) error {
+func attachFolderMounts(inst *limatype.Instance, vmConfig *vz.VirtualMachineConfiguration) error {
 	var mounts []vz.DirectorySharingDeviceConfiguration
-	if *inst.Config.MountType == limayaml.VIRTIOFS {
+	if *inst.Config.MountType == limatype.VIRTIOFS {
 		for i, mount := range inst.Config.Mounts {
 			if _, err := os.Stat(mount.Location); errors.Is(err, os.ErrNotExist) {
 				err := os.MkdirAll(mount.Location, 0o750)
@@ -577,7 +578,7 @@ func attachFolderMounts(inst *store.Instance, vmConfig *vz.VirtualMachineConfigu
 		}
 	}
 
-	if *inst.Config.Rosetta.Enabled {
+	if *inst.Config.VMOpts.VZ.Rosetta.Enabled {
 		logrus.Info("Setting up Rosetta share")
 		directorySharingDeviceConfig, err := createRosettaDirectoryShareConfiguration()
 		if err != nil {
@@ -593,7 +594,7 @@ func attachFolderMounts(inst *store.Instance, vmConfig *vz.VirtualMachineConfigu
 	return nil
 }
 
-func attachAudio(inst *store.Instance, config *vz.VirtualMachineConfiguration) error {
+func attachAudio(inst *limatype.Instance, config *vz.VirtualMachineConfiguration) error {
 	switch *inst.Config.Audio.Device {
 	case "vz", "default":
 		outputStream, err := vz.NewVirtioSoundDeviceHostOutputStreamConfiguration()
@@ -616,7 +617,7 @@ func attachAudio(inst *store.Instance, config *vz.VirtualMachineConfiguration) e
 	}
 }
 
-func attachOtherDevices(_ *store.Instance, vmConfig *vz.VirtualMachineConfiguration) error {
+func attachOtherDevices(_ *limatype.Instance, vmConfig *vz.VirtualMachineConfiguration) error {
 	entropyConfig, err := vz.NewVirtioEntropyDeviceConfiguration()
 	if err != nil {
 		return err
@@ -690,7 +691,7 @@ func attachOtherDevices(_ *store.Instance, vmConfig *vz.VirtualMachineConfigurat
 	return nil
 }
 
-func getMachineIdentifier(inst *store.Instance) (*vz.GenericMachineIdentifier, error) {
+func getMachineIdentifier(inst *limatype.Instance) (*vz.GenericMachineIdentifier, error) {
 	identifier := filepath.Join(inst.Dir, filenames.VzIdentifier)
 	// Empty VzIdentifier can be created on cloning an instance.
 	if st, err := os.Stat(identifier); err != nil || (st != nil && st.Size() == 0) {
@@ -710,7 +711,7 @@ func getMachineIdentifier(inst *store.Instance) (*vz.GenericMachineIdentifier, e
 	return vz.NewGenericMachineIdentifierWithDataPath(identifier)
 }
 
-func bootLoader(inst *store.Instance) (vz.BootLoader, error) {
+func bootLoader(inst *limatype.Instance) (vz.BootLoader, error) {
 	linuxBootLoader, err := linuxBootLoader(inst)
 	if linuxBootLoader != nil {
 		return linuxBootLoader, nil
@@ -726,7 +727,7 @@ func bootLoader(inst *store.Instance) (vz.BootLoader, error) {
 	return vz.NewEFIBootLoader(vz.WithEFIVariableStore(efiVariableStore))
 }
 
-func linuxBootLoader(inst *store.Instance) (*vz.LinuxBootLoader, error) {
+func linuxBootLoader(inst *limatype.Instance) (*vz.LinuxBootLoader, error) {
 	kernel := filepath.Join(inst.Dir, filenames.Kernel)
 	kernelCmdline := filepath.Join(inst.Dir, filenames.KernelCmdline)
 	initrd := filepath.Join(inst.Dir, filenames.Initrd)
@@ -751,7 +752,7 @@ func linuxBootLoader(inst *store.Instance) (*vz.LinuxBootLoader, error) {
 	return vz.NewLinuxBootLoader(kernel, opt...)
 }
 
-func getEFI(inst *store.Instance) (*vz.EFIVariableStore, error) {
+func getEFI(inst *limatype.Instance) (*vz.EFIVariableStore, error) {
 	efi := filepath.Join(inst.Dir, filenames.VzEfi)
 	if _, err := os.Stat(efi); os.IsNotExist(err) {
 		return vz.NewEFIVariableStore(efi, vz.WithCreatingEFIVariableStore())
diff --git a/pkg/driver/vz/vz_driver_darwin.go b/pkg/driver/vz/vz_driver_darwin.go
index 17c70ca2..decfc10f 100644
--- a/pkg/driver/vz/vz_driver_darwin.go
+++ b/pkg/driver/vz/vz_driver_darwin.go
@@ -7,9 +7,11 @@ package vz
 
 import (
 	"context"
+	"embed"
 	"errors"
 	"fmt"
 	"net"
+	"os"
 	"path/filepath"
 	"runtime"
 	"time"
@@ -19,10 +21,12 @@ import (
 	"github.com/sirupsen/logrus"
 
 	"github.com/lima-vm/lima/v2/pkg/driver"
+	"github.com/lima-vm/lima/v2/pkg/limatype"
+	"github.com/lima-vm/lima/v2/pkg/limatype/filenames"
 	"github.com/lima-vm/lima/v2/pkg/limayaml"
 	"github.com/lima-vm/lima/v2/pkg/osutil"
+	"github.com/lima-vm/lima/v2/pkg/ptr"
 	"github.com/lima-vm/lima/v2/pkg/reflectutil"
-	"github.com/lima-vm/lima/v2/pkg/store"
 )
 
 var knownYamlProperties = []string{
@@ -64,12 +68,13 @@ var knownYamlProperties = []string{
 	"User",
 	"Video",
 	"VMType",
+	"VMOpts",
 }
 
 const Enabled = true
 
 type LimaVzDriver struct {
-	Instance *store.Instance
+	Instance *limatype.Instance
 
 	SSHLocalPort int
 	vSockPort    int
@@ -87,15 +92,126 @@ func New() *LimaVzDriver {
 	}
 }
 
-func (l *LimaVzDriver) Configure(inst *store.Instance) *driver.ConfiguredDriver {
+func (l *LimaVzDriver) Configure(inst *limatype.Instance) *driver.ConfiguredDriver {
 	l.Instance = inst
 	l.SSHLocalPort = inst.SSHLocalPort
 
+	if l.Instance.Config.MountType != nil {
+		mountTypesUnsupported := make(map[string]struct{})
+		for _, f := range l.Instance.Config.MountTypesUnsupported {
+			mountTypesUnsupported[f] = struct{}{}
+		}
+
+		if _, ok := mountTypesUnsupported[*l.Instance.Config.MountType]; ok {
+			// We cannot return an error here, but Validate() will return it.
+			logrus.Warnf("Unsupported mount type: %q", *l.Instance.Config.MountType)
+		}
+	}
+
 	return &driver.ConfiguredDriver{
 		Driver: l,
 	}
 }
 
+func (l *LimaVzDriver) FillConfig(cfg *limatype.LimaYAML, _ string) error {
+	if cfg.VMType == nil {
+		cfg.VMType = ptr.Of(limatype.VZ)
+	}
+
+	if cfg.MountType == nil {
+		cfg.MountType = ptr.Of(limatype.VIRTIOFS)
+	}
+
+	//nolint:staticcheck // Migration of top-level Rosetta if specified
+	if (cfg.VMOpts.VZ.Rosetta.Enabled == nil && cfg.VMOpts.VZ.Rosetta.BinFmt == nil) && (!isEmpty(cfg.Rosetta)) {
+		logrus.Debug("Migrating top-level Rosetta configuration to vmOpts.vz.rosetta")
+		cfg.VMOpts.VZ.Rosetta = cfg.Rosetta
+	}
+	//nolint:staticcheck // Warning about both top-level and vmOpts.vz.Rosetta being set
+	if (cfg.VMOpts.VZ.Rosetta.Enabled != nil && cfg.VMOpts.VZ.Rosetta.BinFmt != nil) && (!isEmpty(cfg.Rosetta)) {
+		logrus.Warn("Both top-level 'rosetta' and 'vmOpts.vz.rosetta' are configured. Using vmOpts.vz.rosetta. Top-level 'rosetta' is deprecated.")
+	}
+
+	if cfg.VMOpts.VZ.Rosetta.Enabled == nil {
+		cfg.VMOpts.VZ.Rosetta.Enabled = ptr.Of(false)
+	}
+	if cfg.VMOpts.VZ.Rosetta.BinFmt == nil {
+		cfg.VMOpts.VZ.Rosetta.BinFmt = ptr.Of(false)
+	}
+
+	return nil
+}
+
+func isEmpty(r limatype.Rosetta) bool {
+	return r.Enabled == nil && r.BinFmt == nil
+}
+
+//go:embed boot/*.sh
+var bootFS embed.FS
+
+func (l *LimaVzDriver) BootScripts() (map[string][]byte, error) {
+	scripts := make(map[string][]byte)
+
+	entries, err := bootFS.ReadDir("boot")
+	if err == nil {
+		for _, entry := range entries {
+			if entry.IsDir() {
+				continue
+			}
+
+			content, err := bootFS.ReadFile("boot/" + entry.Name())
+			if err != nil {
+				return nil, err
+			}
+
+			scripts[entry.Name()] = content
+		}
+	}
+
+	return scripts, nil
+}
+
+func (l *LimaVzDriver) AcceptConfig(cfg *limatype.LimaYAML, filePath string) error {
+	if dir, basename := filepath.Split(filePath); dir != "" && basename == filenames.LimaYAML && limayaml.IsExistingInstanceDir(dir) {
+		vzIdentifier := filepath.Join(dir, filenames.VzIdentifier) // since Lima v0.14
+		if _, err := os.Lstat(vzIdentifier); !errors.Is(err, os.ErrNotExist) {
+			logrus.Debugf("ResolveVMType: resolved VMType %q (existing instance, with %q)", "vz", vzIdentifier)
+		}
+	}
+
+	for i, nw := range cfg.Networks {
+		field := fmt.Sprintf("networks[%d]", i)
+		switch {
+		case nw.Lima != "":
+			if nw.VZNAT != nil && *nw.VZNAT {
+				return fmt.Errorf("field `%s.lima` and field `%s.vzNAT` are mutually exclusive", field, field)
+			}
+		case nw.Socket != "":
+			if nw.VZNAT != nil && *nw.VZNAT {
+				return fmt.Errorf("field `%s.socket` and field `%s.vzNAT` are mutually exclusive", field, field)
+			}
+		case nw.VZNAT != nil && *nw.VZNAT:
+			if nw.Lima != "" {
+				return fmt.Errorf("field `%s.vzNAT` and field `%s.lima` are mutually exclusive", field, field)
+			}
+			if nw.Socket != "" {
+				return fmt.Errorf("field `%s.vzNAT` and field `%s.socket` are mutually exclusive", field, field)
+			}
+		}
+	}
+
+	if l.Instance == nil {
+		l.Instance = &limatype.Instance{}
+	}
+	l.Instance.Config = cfg
+
+	if err := l.Validate(context.Background()); err != nil {
+		return fmt.Errorf("config not supported by the VZ driver: %w", err)
+	}
+
+	return nil
+}
+
 func (l *LimaVzDriver) Validate(_ context.Context) error {
 	macOSProductVersion, err := osutil.ProductVersion()
 	if err != nil {
@@ -109,21 +225,21 @@ func (l *LimaVzDriver) Validate(_ context.Context) error {
 			"Update macOS, or change vmType to \"qemu\" if the VM does not start up. (https://github.com/lima-vm/lima/issues/3334)",
 			*l.Instance.Config.VMType)
 	}
-	if *l.Instance.Config.MountType == limayaml.NINEP {
-		return fmt.Errorf("field `mountType` must be %q or %q for VZ driver , got %q", limayaml.REVSSHFS, limayaml.VIRTIOFS, *l.Instance.Config.MountType)
+	if l.Instance.Config.MountType != nil && *l.Instance.Config.MountType == limatype.NINEP {
+		return fmt.Errorf("field `mountType` must be %q or %q for VZ driver , got %q", limatype.REVSSHFS, limatype.VIRTIOFS, *l.Instance.Config.MountType)
 	}
 	if *l.Instance.Config.Firmware.LegacyBIOS {
 		logrus.Warnf("vmType %s: ignoring `firmware.legacyBIOS`", *l.Instance.Config.VMType)
 	}
 	for _, f := range l.Instance.Config.Firmware.Images {
 		switch f.VMType {
-		case "", limayaml.VZ:
+		case "", limatype.VZ:
 			if f.Arch == *l.Instance.Config.Arch {
 				return errors.New("`firmware.images` configuration is not supported for VZ driver")
 			}
 		}
 	}
-	if unknown := reflectutil.UnknownNonEmptyFields(l.Instance.Config, knownYamlProperties...); len(unknown) > 0 {
+	if unknown := reflectutil.UnknownNonEmptyFields(l.Instance.Config, knownYamlProperties...); l.Instance.Config.VMType != nil && len(unknown) > 0 {
 		logrus.Warnf("vmType %s: ignoring %+v", *l.Instance.Config.VMType, unknown)
 	}
 
@@ -175,7 +291,7 @@ func (l *LimaVzDriver) Validate(_ context.Context) error {
 	return nil
 }
 
-func (l *LimaVzDriver) Initialize(_ context.Context) error {
+func (l *LimaVzDriver) Create(_ context.Context) error {
 	_, err := getMachineIdentifier(l.Instance)
 	return err
 }
@@ -255,19 +371,39 @@ func (l *LimaVzDriver) GuestAgentConn(_ context.Context) (net.Conn, string, erro
 
 func (l *LimaVzDriver) Info() driver.Info {
 	var info driver.Info
-	if l.Instance != nil {
-		info.CanRunGUI = l.canRunGUI()
-	}
 
-	info.DriverName = "vz"
+	info.Features.DriverName = "vz"
 	info.VsockPort = l.vSockPort
 	info.VirtioPort = l.virtioPort
 	if l.Instance != nil {
 		info.InstanceDir = l.Instance.Dir
 	}
+
+	var guiFlag bool
+	if l.Instance != nil {
+		guiFlag = l.canRunGUI()
+	}
+	info.Features = driver.DriverFeatures{
+		DynamicSSHAddress:    false,
+		SkipSocketForwarding: false,
+		DriverName:           "vz",
+		CanRunGUI:            guiFlag,
+	}
 	return info
 }
 
+func (l *LimaVzDriver) SSHAddress(_ context.Context) (string, error) {
+	return "127.0.0.1", nil
+}
+
+func (l *LimaVzDriver) InspectStatus(_ context.Context, _ *limatype.Instance) string {
+	return ""
+}
+
+func (l *LimaVzDriver) Delete(_ context.Context) error {
+	return nil
+}
+
 func (l *LimaVzDriver) Register(_ context.Context) error {
 	return nil
 }
diff --git a/pkg/cidata/cidata.TEMPLATE.d/boot/02-wsl2-setup.sh b/pkg/driver/wsl2/boot/02-wsl2-setup.sh
similarity index 100%
rename from pkg/cidata/cidata.TEMPLATE.d/boot/02-wsl2-setup.sh
rename to pkg/driver/wsl2/boot/02-wsl2-setup.sh
diff --git a/pkg/driver/wsl2/fs.go b/pkg/driver/wsl2/fs.go
index ddbc0bbd..b247b29d 100644
--- a/pkg/driver/wsl2/fs.go
+++ b/pkg/driver/wsl2/fs.go
@@ -12,12 +12,12 @@ import (
 	"github.com/sirupsen/logrus"
 
 	"github.com/lima-vm/lima/v2/pkg/fileutils"
-	"github.com/lima-vm/lima/v2/pkg/store"
-	"github.com/lima-vm/lima/v2/pkg/store/filenames"
+	"github.com/lima-vm/lima/v2/pkg/limatype"
+	"github.com/lima-vm/lima/v2/pkg/limatype/filenames"
 )
 
 // EnsureFs downloads the root fs.
-func EnsureFs(ctx context.Context, inst *store.Instance) error {
+func EnsureFs(ctx context.Context, inst *limatype.Instance) error {
 	baseDisk := filepath.Join(inst.Dir, filenames.BaseDisk)
 	if _, err := os.Stat(baseDisk); errors.Is(err, os.ErrNotExist) {
 		var ensuredBaseDisk bool
diff --git a/pkg/driver/wsl2/vm_windows.go b/pkg/driver/wsl2/vm_windows.go
index 493a8380..63601270 100644
--- a/pkg/driver/wsl2/vm_windows.go
+++ b/pkg/driver/wsl2/vm_windows.go
@@ -10,14 +10,15 @@ import (
 	"os"
 	"os/exec"
 	"path/filepath"
+	"regexp"
 	"strconv"
 	"strings"
 
 	"github.com/sirupsen/logrus"
 
 	"github.com/lima-vm/lima/v2/pkg/executil"
-	"github.com/lima-vm/lima/v2/pkg/store"
-	"github.com/lima-vm/lima/v2/pkg/store/filenames"
+	"github.com/lima-vm/lima/v2/pkg/limatype"
+	"github.com/lima-vm/lima/v2/pkg/limatype/filenames"
 	"github.com/lima-vm/lima/v2/pkg/textutil"
 )
 
@@ -133,8 +134,8 @@ func provisionVM(ctx context.Context, instanceDir, instanceName, distroName stri
 		for {
 			<-ctx.Done()
 			logrus.Info("Context closed, stopping vm")
-			if status, err := store.GetWslStatus(instanceName); err == nil &&
-				status == store.StatusRunning {
+			if status, err := getWslStatus(instanceName); err == nil &&
+				status == limatype.StatusRunning {
 				_ = stopVM(ctx, distroName)
 			}
 		}
@@ -177,3 +178,54 @@ func unregisterVM(ctx context.Context, distroName string) error {
 	}
 	return nil
 }
+
+func getWslStatus(instName string) (string, error) {
+	distroName := "lima-" + instName
+	out, err := executil.RunUTF16leCommand([]string{
+		"wsl.exe",
+		"--list",
+		"--verbose",
+	})
+	if err != nil {
+		return "", fmt.Errorf("failed to run `wsl --list --verbose`, err: %w (out=%q)", err, out)
+	}
+
+	if out == "" {
+		return limatype.StatusBroken, fmt.Errorf("failed to read instance state for instance %q, try running `wsl --list --verbose` to debug, err: %w", instName, err)
+	}
+
+	// Check for edge cases first
+	if strings.Contains(out, "Windows Subsystem for Linux has no installed distributions.") {
+		if strings.Contains(out, "Wsl/WSL_E_DEFAULT_DISTRO_NOT_FOUND") {
+			return limatype.StatusBroken, fmt.Errorf(
+				"failed to read instance state for instance %q because no distro is installed,"+
+					"try running `wsl --install -d Ubuntu` and then re-running Lima", instName)
+		}
+		return limatype.StatusBroken, fmt.Errorf(
+			"failed to read instance state for instance %q because there is no WSL kernel installed,"+
+				"this usually happens when WSL was installed for another user, but never for your user."+
+				"Try running `wsl --install -d Ubuntu` and `wsl --update`, and then re-running Lima", instName)
+	}
+
+	var instState string
+	wslListColsRegex := regexp.MustCompile(`\s+`)
+	// wsl --list --verbose may have different headers depending on localization, just split by line
+	for _, rows := range strings.Split(strings.ReplaceAll(out, "\r\n", "\n"), "\n") {
+		cols := wslListColsRegex.Split(strings.TrimSpace(rows), -1)
+		nameIdx := 0
+		// '*' indicates default instance
+		if cols[0] == "*" {
+			nameIdx = 1
+		}
+		if cols[nameIdx] == distroName {
+			instState = cols[nameIdx+1]
+			break
+		}
+	}
+
+	if instState == "" {
+		return limatype.StatusUninitialized, nil
+	}
+
+	return instState, nil
+}
diff --git a/pkg/driver/wsl2/wsl_driver_windows.go b/pkg/driver/wsl2/wsl_driver_windows.go
index 930a2b84..ef49dd73 100644
--- a/pkg/driver/wsl2/wsl_driver_windows.go
+++ b/pkg/driver/wsl2/wsl_driver_windows.go
@@ -5,6 +5,7 @@ package wsl2
 
 import (
 	"context"
+	"embed"
 	"fmt"
 	"net"
 	"regexp"
@@ -15,9 +16,10 @@ import (
 
 	"github.com/lima-vm/lima/v2/pkg/driver"
 	"github.com/lima-vm/lima/v2/pkg/freeport"
+	"github.com/lima-vm/lima/v2/pkg/limatype"
 	"github.com/lima-vm/lima/v2/pkg/limayaml"
+	"github.com/lima-vm/lima/v2/pkg/ptr"
 	"github.com/lima-vm/lima/v2/pkg/reflectutil"
-	"github.com/lima-vm/lima/v2/pkg/store"
 	"github.com/lima-vm/lima/v2/pkg/windows"
 )
 
@@ -47,7 +49,7 @@ var knownYamlProperties = []string{
 const Enabled = true
 
 type LimaWslDriver struct {
-	Instance *store.Instance
+	Instance *limatype.Instance
 
 	SSHLocalPort int
 	vSockPort    int
@@ -68,7 +70,7 @@ func New() *LimaWslDriver {
 	}
 }
 
-func (l *LimaWslDriver) Configure(inst *store.Instance) *driver.ConfiguredDriver {
+func (l *LimaWslDriver) Configure(inst *limatype.Instance) *driver.ConfiguredDriver {
 	l.Instance = inst
 	l.SSHLocalPort = inst.SSHLocalPort
 
@@ -77,61 +79,162 @@ func (l *LimaWslDriver) Configure(inst *store.Instance) *driver.ConfiguredDriver
 	}
 }
 
+func (l *LimaWslDriver) AcceptConfig(cfg *limatype.LimaYAML, _ string) error {
+	if l.Instance == nil {
+		l.Instance = &limatype.Instance{}
+	}
+	l.Instance.Config = cfg
+
+	if err := l.Validate(context.Background()); err != nil {
+		return fmt.Errorf("config not supported by the WSL2 driver: %w", err)
+	}
+
+	return nil
+}
+
+func (l *LimaWslDriver) FillConfig(cfg *limatype.LimaYAML, _ string) error {
+	if cfg.VMType == nil {
+		cfg.VMType = ptr.Of(limatype.WSL2)
+	}
+	if cfg.MountType == nil {
+		cfg.MountType = ptr.Of(limatype.WSLMount)
+	}
+
+	return nil
+}
+
 func (l *LimaWslDriver) Validate(_ context.Context) error {
-	if *l.Instance.Config.MountType != limayaml.WSLMount {
-		return fmt.Errorf("field `mountType` must be %q for WSL2 driver, got %q", limayaml.WSLMount, *l.Instance.Config.MountType)
+	if l.Instance.Config.MountType != nil && *l.Instance.Config.MountType != limatype.WSLMount {
+		return fmt.Errorf("field `mountType` must be %q for WSL2 driver, got %q", limatype.WSLMount, *l.Instance.Config.MountType)
 	}
 	// TODO: revise this list for WSL2
-	if unknown := reflectutil.UnknownNonEmptyFields(l.Instance.Config, knownYamlProperties...); len(unknown) > 0 {
-		logrus.Warnf("Ignoring: vmType %s: %+v", *l.Instance.Config.VMType, unknown)
+	if l.Instance.Config.VMType != nil {
+		if unknown := reflectutil.UnknownNonEmptyFields(l.Instance.Config, knownYamlProperties...); len(unknown) > 0 {
+			logrus.Warnf("Ignoring: vmType %s: %+v", *l.Instance.Config.VMType, unknown)
+		}
 	}
 
 	if !limayaml.IsNativeArch(*l.Instance.Config.Arch) {
 		return fmt.Errorf("unsupported arch: %q", *l.Instance.Config.Arch)
 	}
 
-	// TODO: real filetype checks
-	tarFileRegex := regexp.MustCompile(`.*tar\.*`)
-	for i, image := range l.Instance.Config.Images {
-		if unknown := reflectutil.UnknownNonEmptyFields(image, "File"); len(unknown) > 0 {
-			logrus.Warnf("Ignoring: vmType %s: images[%d]: %+v", *l.Instance.Config.VMType, i, unknown)
+	if l.Instance.Config.VMType != nil {
+		if l.Instance.Config.Images != nil && l.Instance.Config.Arch != nil {
+			// TODO: real filetype checks
+			tarFileRegex := regexp.MustCompile(`.*tar\.*`)
+			for i, image := range l.Instance.Config.Images {
+				if unknown := reflectutil.UnknownNonEmptyFields(image, "File"); len(unknown) > 0 {
+					logrus.Warnf("Ignoring: vmType %s: images[%d]: %+v", *l.Instance.Config.VMType, i, unknown)
+				}
+				match := tarFileRegex.MatchString(image.Location)
+				if image.Arch == *l.Instance.Config.Arch && !match {
+					return fmt.Errorf("unsupported image type for vmType: %s, tarball root file system required: %q", *l.Instance.Config.VMType, image.Location)
+				}
+			}
 		}
-		match := tarFileRegex.MatchString(image.Location)
-		if image.Arch == *l.Instance.Config.Arch && !match {
-			return fmt.Errorf("unsupported image type for vmType: %s, tarball root file system required: %q", *l.Instance.Config.VMType, image.Location)
+
+		if l.Instance.Config.Mounts != nil {
+			for i, mount := range l.Instance.Config.Mounts {
+				if unknown := reflectutil.UnknownNonEmptyFields(mount); len(unknown) > 0 {
+					logrus.Warnf("Ignoring: vmType %s: mounts[%d]: %+v", *l.Instance.Config.VMType, i, unknown)
+				}
+			}
+		}
+
+		if l.Instance.Config.Networks != nil {
+			for i, network := range l.Instance.Config.Networks {
+				if unknown := reflectutil.UnknownNonEmptyFields(network); len(unknown) > 0 {
+					logrus.Warnf("Ignoring: vmType %s: networks[%d]: %+v", *l.Instance.Config.VMType, i, unknown)
+				}
+			}
+		}
+
+		if l.Instance.Config.Audio.Device != nil {
+			audioDevice := *l.Instance.Config.Audio.Device
+			if audioDevice != "" {
+				logrus.Warnf("Ignoring: vmType %s: `audio.device`: %+v", *l.Instance.Config.VMType, audioDevice)
+			}
 		}
 	}
 
-	for i, mount := range l.Instance.Config.Mounts {
-		if unknown := reflectutil.UnknownNonEmptyFields(mount); len(unknown) > 0 {
-			logrus.Warnf("Ignoring: vmType %s: mounts[%d]: %+v", *l.Instance.Config.VMType, i, unknown)
+	return nil
+}
+
+//go:embed boot/*.sh
+var bootFS embed.FS
+
+func (l *LimaWslDriver) BootScripts() (map[string][]byte, error) {
+	scripts := make(map[string][]byte)
+
+	entries, err := bootFS.ReadDir("boot")
+	if err != nil {
+		return scripts, err
+	}
+
+	for _, entry := range entries {
+		if entry.IsDir() {
+			continue
 		}
+
+		content, err := bootFS.ReadFile("boot/" + entry.Name())
+		if err != nil {
+			return nil, err
+		}
+
+		scripts[entry.Name()] = content
 	}
 
-	for i, network := range l.Instance.Config.Networks {
-		if unknown := reflectutil.UnknownNonEmptyFields(network); len(unknown) > 0 {
-			logrus.Warnf("Ignoring: vmType %s: networks[%d]: %+v", *l.Instance.Config.VMType, i, unknown)
+	return scripts, nil
+}
+
+func (l *LimaWslDriver) InspectStatus(ctx context.Context, inst *limatype.Instance) string {
+	status, err := getWslStatus(inst.Name)
+	if err != nil {
+		inst.Status = limatype.StatusBroken
+		inst.Errors = append(inst.Errors, err)
+	} else {
+		inst.Status = status
+	}
+
+	inst.SSHLocalPort = 22
+
+	if inst.Status == limatype.StatusRunning {
+		sshAddr, err := l.SSHAddress(ctx)
+		if err == nil {
+			inst.SSHAddress = sshAddr
+		} else {
+			inst.Errors = append(inst.Errors, err)
 		}
 	}
 
-	audioDevice := *l.Instance.Config.Audio.Device
-	if audioDevice != "" {
-		logrus.Warnf("Ignoring: vmType %s: `audio.device`: %+v", *l.Instance.Config.VMType, audioDevice)
+	return inst.Status
+}
+
+func (l *LimaWslDriver) Delete(ctx context.Context) error {
+	distroName := "lima-" + l.Instance.Name
+	status, err := getWslStatus(l.Instance.Name)
+	if err != nil {
+		return err
+	}
+	switch status {
+	case limatype.StatusRunning, limatype.StatusStopped, limatype.StatusBroken, limatype.StatusInstalling:
+		return unregisterVM(ctx, distroName)
 	}
 
+	logrus.Info("WSL VM is not running or does not exist, skipping deletion")
 	return nil
 }
 
 func (l *LimaWslDriver) Start(ctx context.Context) (chan error, error) {
 	logrus.Infof("Starting WSL VM")
-	status, err := store.GetWslStatus(l.Instance.Name)
+	status, err := getWslStatus(l.Instance.Name)
 	if err != nil {
 		return nil, err
 	}
 
 	distroName := "lima-" + l.Instance.Name
 
-	if status == store.StatusUninitialized {
+	if status == limatype.StatusUninitialized {
 		if err := EnsureFs(ctx, l.Instance); err != nil {
 			return nil, err
 		}
@@ -177,21 +280,6 @@ func (l *LimaWslDriver) Stop(ctx context.Context) error {
 	return stopVM(ctx, distroName)
 }
 
-func (l *LimaWslDriver) Unregister(ctx context.Context) error {
-	distroName := "lima-" + l.Instance.Name
-	status, err := store.GetWslStatus(l.Instance.Name)
-	if err != nil {
-		return err
-	}
-	switch status {
-	case store.StatusRunning, store.StatusStopped, store.StatusBroken, store.StatusInstalling:
-		return unregisterVM(ctx, distroName)
-	}
-
-	logrus.Info("VM not registered, skipping unregistration")
-	return nil
-}
-
 // GuestAgentConn returns the guest agent connection, or nil (if forwarded by ssh).
 // As of 08-01-2024, github.com/mdlayher/vsock does not natively support vsock on
 // Windows, so use the winio library to create the connection.
@@ -221,22 +309,27 @@ func (l *LimaWslDriver) Info() driver.Info {
 	if l.Instance != nil {
 		info.InstanceDir = l.Instance.Dir
 	}
-	info.DriverName = "wsl2"
-	info.CanRunGUI = l.canRunGUI()
 	info.VirtioPort = l.virtioPort
 	info.VsockPort = l.vSockPort
+
+	info.Features = driver.DriverFeatures{
+		DynamicSSHAddress:    true,
+		SkipSocketForwarding: true,
+		DriverName:           "wsl2",
+		CanRunGUI:            l.canRunGUI(),
+	}
 	return info
 }
 
-func (l *LimaWslDriver) Initialize(_ context.Context) error {
-	return nil
+func (l *LimaWslDriver) SSHAddress(_ context.Context) (string, error) {
+	return "127.0.0.1", nil
 }
 
-func (l *LimaWslDriver) CreateDisk(_ context.Context) error {
+func (l *LimaWslDriver) Create(_ context.Context) error {
 	return nil
 }
 
-func (l *LimaWslDriver) Register(_ context.Context) error {
+func (l *LimaWslDriver) CreateDisk(_ context.Context) error {
 	return nil
 }
 
diff --git a/pkg/driverutil/instance.go b/pkg/driverutil/instance.go
index 2d865a80..e861b1cf 100644
--- a/pkg/driverutil/instance.go
+++ b/pkg/driverutil/instance.go
@@ -10,12 +10,12 @@ import (
 
 	"github.com/lima-vm/lima/v2/pkg/driver"
 	"github.com/lima-vm/lima/v2/pkg/driver/external/server"
+	"github.com/lima-vm/lima/v2/pkg/limatype"
 	"github.com/lima-vm/lima/v2/pkg/registry"
-	"github.com/lima-vm/lima/v2/pkg/store"
 )
 
 // CreateConfiguredDriver creates a driver.ConfiguredDriver for the given instance.
-func CreateConfiguredDriver(inst *store.Instance, sshLocalPort int) (*driver.ConfiguredDriver, error) {
+func CreateConfiguredDriver(inst *limatype.Instance, sshLocalPort int) (*driver.ConfiguredDriver, error) {
 	limaDriver := inst.Config.VMType
 	extDriver, intDriver, exists := registry.Get(*limaDriver)
 	if !exists {
@@ -39,6 +39,6 @@ func CreateConfiguredDriver(inst *store.Instance, sshLocalPort int) (*driver.Con
 		return extDriver.Client.Configure(inst), nil
 	}
 
-	logrus.Debugf("Using internal driver %q", intDriver.Info().DriverName)
+	logrus.Debugf("Using internal driver %q", intDriver.Info().Features.DriverName)
 	return intDriver.Configure(inst), nil
 }
diff --git a/pkg/driverutil/vm.go b/pkg/driverutil/vm.go
new file mode 100644
index 00000000..4e542355
--- /dev/null
+++ b/pkg/driverutil/vm.go
@@ -0,0 +1,180 @@
+// SPDX-FileCopyrightText: Copyright The Lima Authors
+// SPDX-License-Identifier: Apache-2.0
+
+package driverutil
+
+import (
+	"bytes"
+	"context"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"os/exec"
+
+	"github.com/sirupsen/logrus"
+
+	"github.com/lima-vm/lima/v2/pkg/limatype"
+	"github.com/lima-vm/lima/v2/pkg/registry"
+)
+
+func ResolveVMType(ctx context.Context, y *limatype.LimaYAML, filePath string) error {
+	if y.VMType != nil && *y.VMType != "" {
+		if err := validateConfigAgainstDriver(ctx, y, filePath, *y.VMType); err != nil {
+			return err
+		}
+		logrus.Debugf("Using specified vmType %q for %q", *y.VMType, filePath)
+		return nil
+	}
+
+	// If VMType is not specified, we go with the default platform driver.
+	vmType := limatype.DefaultDriver()
+	return validateConfigAgainstDriver(ctx, y, filePath, vmType)
+}
+
+func validateConfigAgainstDriver(ctx context.Context, y *limatype.LimaYAML, filePath, vmType string) error {
+	extDriver, intDriver, exists := registry.Get(vmType)
+	if !exists {
+		return fmt.Errorf("vmType %q is not a registered driver", vmType)
+	}
+
+	if extDriver != nil {
+		return handlePreConfiguredDriverAction(ctx, y, extDriver.Path, filePath)
+	}
+
+	if err := intDriver.AcceptConfig(y, filePath); err != nil {
+		return err
+	}
+	if err := intDriver.FillConfig(y, filePath); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func handlePreConfiguredDriverAction(ctx context.Context, y *limatype.LimaYAML, extDriverPath, filePath string) error {
+	cmd := exec.CommandContext(ctx, extDriverPath, "--pre-driver-action")
+
+	var stderrBuf bytes.Buffer
+	cmd.Stderr = &stderrBuf
+
+	stdout, err := cmd.StdoutPipe()
+	if err != nil {
+		return fmt.Errorf("failed to get stdout pipe: %w", err)
+	}
+	stdin, err := cmd.StdinPipe()
+	if err != nil {
+		return fmt.Errorf("failed to get stdin pipe: %w", err)
+	}
+	if err := cmd.Start(); err != nil {
+		return fmt.Errorf("failed to start external driver: %w", err)
+	}
+
+	encoder := json.NewEncoder(stdin)
+	if err := encoder.Encode(limatype.PreConfiguredDriverPayload{
+		Config:   *y,
+		FilePath: filePath,
+	}); err != nil {
+		stdin.Close()
+		return fmt.Errorf("failed to encode pre-configured driver payload: %w", err)
+	}
+	stdin.Close()
+
+	decoder := json.NewDecoder(stdout)
+	var res limatype.LimaYAML
+	if err := decoder.Decode(&res); err != nil {
+		return fmt.Errorf("failed to decode pre-configured driver response: %w", err)
+	}
+
+	if err := cmd.Wait(); err != nil {
+		if stderrBuf.Len() > 0 {
+			return fmt.Errorf("pre-configured driver command failed: %w; stderr: %s", err, stderrBuf.String())
+		}
+		return fmt.Errorf("pre-configured driver command failed: %w", err)
+	}
+
+	if stderrBuf.Len() > 0 {
+		return fmt.Errorf("external driver stderr: %s", stderrBuf.String())
+	}
+
+	*y = res
+	logrus.Debugf("Pre-configured driver action completed successfully for %q", extDriverPath)
+	return nil
+}
+
+func InspectStatus(ctx context.Context, inst *limatype.Instance) (string, error) {
+	if inst == nil || inst.Config == nil || inst.Config.VMType == nil {
+		return "", errors.New("instance or its configuration is not properly initialized")
+	}
+
+	extDriver, intDriver, exists := registry.Get(*inst.Config.VMType)
+	if !exists {
+		return "", fmt.Errorf("unknown or unsupported VM type: %s", *inst.Config.VMType)
+	}
+
+	if extDriver != nil {
+		status, err := handleInspectStatusAction(ctx, inst, extDriver.Path)
+		if err != nil {
+			extDriver.Logger.Errorf("Failed to inspect status for instance %q: %v", inst.Name, err)
+			return "", err
+		}
+		extDriver.Logger.Debugf("Instance %q inspected successfully with status: %s", inst.Name, inst.Status)
+		return status, nil
+	}
+
+	return intDriver.InspectStatus(ctx, inst), nil
+}
+
+func handleInspectStatusAction(ctx context.Context, inst *limatype.Instance, extDriverPath string) (string, error) {
+	cmd := exec.CommandContext(ctx, extDriverPath, "--inspect-status")
+
+	var stderrBuf bytes.Buffer
+	cmd.Stderr = &stderrBuf
+
+	stdin, err := cmd.StdinPipe()
+	if err != nil {
+		return "", err
+	}
+	stdout, err := cmd.StdoutPipe()
+	if err != nil {
+		return "", err
+	}
+	if err := cmd.Start(); err != nil {
+		return "", err
+	}
+
+	encoder := json.NewEncoder(stdin)
+	payload, err := inst.MarshalJSON()
+	if err != nil {
+		return "", fmt.Errorf("failed to marshal instance config: %w", err)
+	}
+	if err := encoder.Encode(payload); err != nil {
+		return "", err
+	}
+	stdin.Close()
+
+	decoder := json.NewDecoder(stdout)
+	var response []byte
+	if err := decoder.Decode(&response); err != nil {
+		return "", err
+	}
+
+	var respInst limatype.Instance
+	if err := respInst.UnmarshalJSON(response); err != nil {
+		return "", fmt.Errorf("failed to unmarshal instance response: %w", err)
+	}
+
+	if err := cmd.Wait(); err != nil {
+		if stderrBuf.Len() > 0 {
+			return "", fmt.Errorf("inspect status command failed: %w; stderr: %s", err, stderrBuf.String())
+		}
+		return "", fmt.Errorf("inspect status command failed: %w", err)
+	}
+
+	if stderrBuf.Len() > 0 {
+		return "", fmt.Errorf("external driver stderr: %s", stderrBuf.String())
+	}
+
+	*inst = respInst
+	logrus.Debugf("Inspecting instance status action completed successfully for %q", extDriverPath)
+	return inst.Status, nil
+}
diff --git a/pkg/editutil/editutil.go b/pkg/editutil/editutil.go
index 462b791d..513b99f9 100644
--- a/pkg/editutil/editutil.go
+++ b/pkg/editutil/editutil.go
@@ -15,8 +15,8 @@ import (
 	"github.com/sirupsen/logrus"
 
 	"github.com/lima-vm/lima/v2/pkg/editutil/editorcmd"
-	"github.com/lima-vm/lima/v2/pkg/store/dirnames"
-	"github.com/lima-vm/lima/v2/pkg/store/filenames"
+	"github.com/lima-vm/lima/v2/pkg/limatype/dirnames"
+	"github.com/lima-vm/lima/v2/pkg/limatype/filenames"
 )
 
 func fileWarning(filename string) string {
diff --git a/pkg/fileutils/download.go b/pkg/fileutils/download.go
index 7bf3fea5..e6d47f2b 100644
--- a/pkg/fileutils/download.go
+++ b/pkg/fileutils/download.go
@@ -12,14 +12,14 @@ import (
 	"github.com/sirupsen/logrus"
 
 	"github.com/lima-vm/lima/v2/pkg/downloader"
-	"github.com/lima-vm/lima/v2/pkg/limayaml"
+	"github.com/lima-vm/lima/v2/pkg/limatype"
 )
 
 // ErrSkipped is returned when the downloader did not attempt to download the specified file.
 var ErrSkipped = errors.New("skipped to download")
 
 // DownloadFile downloads a file to the cache, optionally copying it to the destination. Returns path in cache.
-func DownloadFile(ctx context.Context, dest string, f limayaml.File, decompress bool, description string, expectedArch limayaml.Arch) (string, error) {
+func DownloadFile(ctx context.Context, dest string, f limatype.File, decompress bool, description string, expectedArch limatype.Arch) (string, error) {
 	if f.Arch != expectedArch {
 		return "", fmt.Errorf("%w: %q: unsupported arch: %q", ErrSkipped, f.Location, f.Arch)
 	}
@@ -47,7 +47,7 @@ func DownloadFile(ctx context.Context, dest string, f limayaml.File, decompress
 }
 
 // CachedFile checks if a file is in the cache, validating the digest if it is available. Returns path in cache.
-func CachedFile(f limayaml.File) (string, error) {
+func CachedFile(f limatype.File) (string, error) {
 	res, err := downloader.Cached(f.Location,
 		downloader.WithCache(),
 		downloader.WithExpectedDigest(f.Digest))
diff --git a/pkg/hostagent/hostagent.go b/pkg/hostagent/hostagent.go
index 4cb8f95c..6fd52c8a 100644
--- a/pkg/hostagent/hostagent.go
+++ b/pkg/hostagent/hostagent.go
@@ -36,18 +36,19 @@ import (
 	"github.com/lima-vm/lima/v2/pkg/hostagent/dns"
 	"github.com/lima-vm/lima/v2/pkg/hostagent/events"
 	"github.com/lima-vm/lima/v2/pkg/instance/hostname"
+	"github.com/lima-vm/lima/v2/pkg/limatype"
+	"github.com/lima-vm/lima/v2/pkg/limatype/filenames"
 	"github.com/lima-vm/lima/v2/pkg/limayaml"
 	"github.com/lima-vm/lima/v2/pkg/networks"
 	"github.com/lima-vm/lima/v2/pkg/osutil"
 	"github.com/lima-vm/lima/v2/pkg/portfwd"
 	"github.com/lima-vm/lima/v2/pkg/sshutil"
 	"github.com/lima-vm/lima/v2/pkg/store"
-	"github.com/lima-vm/lima/v2/pkg/store/filenames"
 	"github.com/lima-vm/lima/v2/pkg/version/versionutil"
 )
 
 type HostAgent struct {
-	instConfig        *limayaml.LimaYAML
+	instConfig        *limatype.LimaYAML
 	sshLocalPort      int
 	udpDNSLocalPort   int
 	tcpDNSLocalPort   int
@@ -76,6 +77,9 @@ type HostAgent struct {
 	guestAgentAliveChOnce sync.Once
 
 	showProgress bool // whether to show cloud-init progress
+
+	statusMu      sync.RWMutex
+	currentStatus events.Status
 }
 
 type options struct {
@@ -135,7 +139,7 @@ func New(ctx context.Context, instName string, stdout io.Writer, signalCh chan o
 	if err != nil {
 		return nil, err
 	}
-	if *inst.Config.VMType == limayaml.WSL2 {
+	if *inst.Config.VMType == limatype.WSL2 {
 		sshLocalPort = inst.SSHLocalPort
 	}
 
@@ -162,7 +166,7 @@ func New(ctx context.Context, instName string, stdout io.Writer, signalCh chan o
 	if err := cidata.GenerateCloudConfig(ctx, inst.Dir, instName, inst.Config); err != nil {
 		return nil, err
 	}
-	if err := cidata.GenerateISO9660(ctx, inst.Dir, instName, inst.Config, udpDNSLocalPort, tcpDNSLocalPort, o.guestAgentBinary, o.nerdctlArchive, vSockPort, virtioPort); err != nil {
+	if err := cidata.GenerateISO9660(ctx, limaDriver, inst.Dir, instName, inst.Config, udpDNSLocalPort, tcpDNSLocalPort, o.guestAgentBinary, o.nerdctlArchive, vSockPort, virtioPort); err != nil {
 		return nil, err
 	}
 
@@ -194,13 +198,13 @@ func New(ctx context.Context, instName string, stdout io.Writer, signalCh chan o
 	for _, rule := range inst.Config.PortForwards {
 		if rule.Ignore && rule.GuestPortRange[0] == 1 && rule.GuestPortRange[1] == 65535 {
 			switch rule.Proto {
-			case limayaml.ProtoTCP:
+			case limatype.ProtoTCP:
 				ignoreTCP = true
 				logrus.Info("TCP port forwarding is disabled (except for SSH)")
-			case limayaml.ProtoUDP:
+			case limatype.ProtoUDP:
 				ignoreUDP = true
 				logrus.Info("UDP port forwarding is disabled")
-			case limayaml.ProtoAny:
+			case limatype.ProtoAny:
 				ignoreTCP = true
 				ignoreUDP = true
 				logrus.Info("TCP (except for SSH) and UDP port forwarding is disabled")
@@ -209,16 +213,16 @@ func New(ctx context.Context, instName string, stdout io.Writer, signalCh chan o
 			break
 		}
 	}
-	rules := make([]limayaml.PortForward, 0, 3+len(inst.Config.PortForwards))
+	rules := make([]limatype.PortForward, 0, 3+len(inst.Config.PortForwards))
 	// Block ports 22 and sshLocalPort on all IPs
 	for _, port := range []int{sshGuestPort, sshLocalPort} {
-		rule := limayaml.PortForward{GuestIP: net.IPv4zero, GuestPort: port, Ignore: true}
+		rule := limatype.PortForward{GuestIP: net.IPv4zero, GuestPort: port, Ignore: true}
 		limayaml.FillPortForwardDefaults(&rule, inst.Dir, inst.Config.User, inst.Param)
 		rules = append(rules, rule)
 	}
 	rules = append(rules, inst.Config.PortForwards...)
 	// Default forwards for all non-privileged ports from "127.0.0.1" and "::1"
-	rule := limayaml.PortForward{}
+	rule := limatype.PortForward{}
 	limayaml.FillPortForwardDefaults(&rule, inst.Dir, inst.Config.User, inst.Param)
 	rules = append(rules, rule)
 
@@ -284,6 +288,11 @@ func determineSSHLocalPort(confLocalPort int, instName, limaVersion string) (int
 func (a *HostAgent) emitEvent(_ context.Context, ev events.Event) {
 	a.eventEncMu.Lock()
 	defer a.eventEncMu.Unlock()
+
+	a.statusMu.Lock()
+	a.currentStatus = ev.Status
+	a.statusMu.Unlock()
+
 	if ev.Time.IsZero() {
 		ev.Time = time.Now()
 	}
@@ -292,6 +301,17 @@ func (a *HostAgent) emitEvent(_ context.Context, ev events.Event) {
 	}
 }
 
+func (a *HostAgent) emitCloudInitProgressEvent(ctx context.Context, progress *events.CloudInitProgress) {
+	a.statusMu.RLock()
+	currentStatus := a.currentStatus
+	a.statusMu.RUnlock()
+
+	currentStatus.CloudInitProgress = progress
+
+	ev := events.Event{Status: currentStatus}
+	a.emitEvent(ctx, ev)
+}
+
 func generatePassword(length int) (string, error) {
 	// avoid any special symbols, to make it easier to copy/paste
 	return password.Generate(length, length/4, 0, false, false)
@@ -310,6 +330,9 @@ func (a *HostAgent) Run(ctx context.Context) error {
 
 	if limayaml.FirstUsernetIndex(a.instConfig) == -1 && *a.instConfig.HostResolver.Enabled {
 		hosts := a.instConfig.HostResolver.Hosts
+		if hosts == nil {
+			hosts = make(map[string]string)
+		}
 		hosts["host.lima.internal"] = networks.SlirpGateway
 		name := hostname.FromInstName(a.instName) // TODO: support customization
 		hosts[name] = networks.SlirpIPAddress
@@ -335,8 +358,8 @@ func (a *HostAgent) Run(ctx context.Context) error {
 	}
 
 	// WSL instance SSH address isn't known until after VM start
-	if *a.instConfig.VMType == limayaml.WSL2 {
-		sshAddr, err := store.GetSSHAddress(ctx, a.instName)
+	if a.driver.Info().Features.DynamicSSHAddress {
+		sshAddr, err := a.driver.SSHAddress(ctx)
 		if err != nil {
 			return err
 		}
@@ -387,7 +410,7 @@ func (a *HostAgent) Run(ctx context.Context) error {
 		logrus.Infof("VNC Password: `%s`", vncpwdfile)
 	}
 
-	if a.driver.Info().CanRunGUI {
+	if a.driver.Info().Features.CanRunGUI {
 		go func() {
 			err = a.startRoutinesAndWait(ctx, errCh)
 			if err != nil {
@@ -472,7 +495,7 @@ sudo chown -R "${USER}" /run/host-services`
 			errs = append(errs, fmt.Errorf("stdout=%q, stderr=%q: %w", stdout, stderr, err))
 		}
 	}
-	if *a.instConfig.MountType == limayaml.REVSSHFS && !*a.instConfig.Plain {
+	if *a.instConfig.MountType == limatype.REVSSHFS && !*a.instConfig.Plain {
 		mounts, err := a.setupMounts(ctx)
 		if err != nil {
 			errs = append(errs, err)
@@ -509,7 +532,10 @@ sudo chown -R "${USER}" /run/host-services`
 		if a.showProgress {
 			cloudInitDone := make(chan struct{})
 			go func() {
-				a.watchCloudInitProgress(ctx)
+				timeoutCtx, cancel := context.WithTimeout(ctx, 10*time.Minute)
+				defer cancel()
+
+				a.watchCloudInitProgress(timeoutCtx)
 				close(cloudInitDone)
 			}()
 
@@ -571,7 +597,7 @@ func (a *HostAgent) watchGuestAgentEvents(ctx context.Context) {
 	// TODO: use vSock (when QEMU for macOS gets support for vSock)
 
 	// Setup all socket forwards and defer their teardown
-	if *a.instConfig.VMType != limayaml.WSL2 {
+	if !(a.driver.Info().Features.DynamicSSHAddress) {
 		logrus.Debugf("Forwarding unix sockets")
 		for _, rule := range a.instConfig.PortForwards {
 			if rule.GuestSocket != "" {
@@ -816,15 +842,22 @@ func forwardSSH(ctx context.Context, sshConfig *ssh.SSHConfig, port int, local,
 }
 
 func (a *HostAgent) watchCloudInitProgress(ctx context.Context) {
+	exitReason := "Cloud-init monitoring completed successfully"
+	var cmd *exec.Cmd
+
+	defer func() {
+		a.emitCloudInitProgressEvent(context.Background(), &events.CloudInitProgress{
+			Active:    false,
+			Completed: true,
+			LogLine:   exitReason,
+		})
+		logrus.Debug("Cloud-init progress monitoring completed")
+	}()
+
 	logrus.Debug("Starting cloud-init progress monitoring")
 
-	a.emitEvent(ctx, events.Event{
-		Status: events.Status{
-			SSHLocalPort: a.sshLocalPort,
-			CloudInitProgress: &events.CloudInitProgress{
-				Active: true,
-			},
-		},
+	a.emitCloudInitProgressEvent(ctx, &events.CloudInitProgress{
+		Active: true,
 	})
 
 	maxRetries := 30
@@ -862,15 +895,17 @@ func (a *HostAgent) watchCloudInitProgress(ctx context.Context) {
 		"sudo", "tail", "-n", "+1", "-f", "/var/log/cloud-init-output.log",
 	)
 
-	cmd := exec.CommandContext(ctx, a.sshConfig.Binary(), args...)
+	cmd = exec.CommandContext(ctx, a.sshConfig.Binary(), args...)
 	stdout, err := cmd.StdoutPipe()
 	if err != nil {
 		logrus.WithError(err).Warn("Failed to create stdout pipe for cloud-init monitoring")
+		exitReason = "Failed to create stdout pipe for cloud-init monitoring"
 		return
 	}
 
 	if err := cmd.Start(); err != nil {
 		logrus.WithError(err).Warn("Failed to start cloud-init monitoring command")
+		exitReason = "Failed to start cloud-init monitoring command"
 		return
 	}
 
@@ -883,23 +918,37 @@ func (a *HostAgent) watchCloudInitProgress(ctx context.Context) {
 			continue
 		}
 
-		if strings.Contains(line, "Cloud-init") && strings.Contains(line, "finished") {
-			cloudInitFinished = true
+		if !cloudInitFinished {
+			if isCloudInitFinished(line) {
+				logrus.Debug("Cloud-init completion detected via log pattern")
+				cloudInitFinished = true
+			}
 		}
 
-		a.emitEvent(ctx, events.Event{
-			Status: events.Status{
-				SSHLocalPort: a.sshLocalPort,
-				CloudInitProgress: &events.CloudInitProgress{
-					Active:    !cloudInitFinished,
-					LogLine:   line,
-					Completed: cloudInitFinished,
-				},
-			},
+		a.emitCloudInitProgressEvent(ctx, &events.CloudInitProgress{
+			Active:    !cloudInitFinished,
+			LogLine:   line,
+			Completed: cloudInitFinished,
 		})
+
+		if cloudInitFinished {
+			logrus.Debug("Breaking from cloud-init monitoring loop - completion detected")
+			if cmd.Process != nil {
+				logrus.Debug("Killing cloud-init monitoring process after completion")
+				if err := cmd.Process.Kill(); err != nil {
+					logrus.WithError(err).Debug("Failed to kill cloud-init monitoring process")
+				}
+			}
+			break
+		}
 	}
 
 	if err := cmd.Wait(); err != nil {
+		if ctx.Err() == context.DeadlineExceeded {
+			logrus.Warn("Cloud-init monitoring timed out after 10 minutes")
+			exitReason = "Cloud-init monitoring timed out after 10 minutes"
+			return
+		}
 		logrus.WithError(err).Debug("SSH command finished (expected when cloud-init completes)")
 	}
 
@@ -918,36 +967,24 @@ func (a *HostAgent) watchCloudInitProgress(ctx context.Context) {
 			lines := strings.Split(string(finalOutput), "\n")
 			for _, line := range lines {
 				if strings.TrimSpace(line) != "" {
-					if strings.Contains(line, "Cloud-init") && strings.Contains(line, "finished") {
-						cloudInitFinished = true
+					if !cloudInitFinished {
+						cloudInitFinished = isCloudInitFinished(line)
 					}
 
-					a.emitEvent(ctx, events.Event{
-						Status: events.Status{
-							SSHLocalPort: a.sshLocalPort,
-							CloudInitProgress: &events.CloudInitProgress{
-								Active:    !cloudInitFinished,
-								LogLine:   line,
-								Completed: cloudInitFinished,
-							},
-						},
+					a.emitCloudInitProgressEvent(ctx, &events.CloudInitProgress{
+						Active:    !cloudInitFinished,
+						LogLine:   line,
+						Completed: cloudInitFinished,
 					})
 				}
 			}
 		}
 	}
+}
 
-	a.emitEvent(ctx, events.Event{
-		Status: events.Status{
-			SSHLocalPort: a.sshLocalPort,
-			CloudInitProgress: &events.CloudInitProgress{
-				Active:    false,
-				Completed: true,
-			},
-		},
-	})
-
-	logrus.Debug("Cloud-init progress monitoring completed")
+func isCloudInitFinished(line string) bool {
+	line = strings.ToLower(strings.TrimSpace(line))
+	return strings.Contains(line, "cloud-init") && strings.Contains(line, "finished")
 }
 
 func copyToHost(ctx context.Context, sshConfig *ssh.SSHConfig, port int, local, remote string) error {
diff --git a/pkg/hostagent/mount.go b/pkg/hostagent/mount.go
index 92ea6e40..1763e15b 100644
--- a/pkg/hostagent/mount.go
+++ b/pkg/hostagent/mount.go
@@ -14,7 +14,7 @@ import (
 	"github.com/sirupsen/logrus"
 
 	"github.com/lima-vm/lima/v2/pkg/ioutilx"
-	"github.com/lima-vm/lima/v2/pkg/limayaml"
+	"github.com/lima-vm/lima/v2/pkg/limatype"
 )
 
 type mount struct {
@@ -37,7 +37,7 @@ func (a *HostAgent) setupMounts(ctx context.Context) ([]*mount, error) {
 	return res, errors.Join(errs...)
 }
 
-func (a *HostAgent) setupMount(ctx context.Context, m limayaml.Mount) (*mount, error) {
+func (a *HostAgent) setupMount(ctx context.Context, m limatype.Mount) (*mount, error) {
 	if err := os.MkdirAll(m.Location, 0o755); err != nil {
 		return nil, err
 	}
diff --git a/pkg/hostagent/port.go b/pkg/hostagent/port.go
index 5a945c24..cf792386 100644
--- a/pkg/hostagent/port.go
+++ b/pkg/hostagent/port.go
@@ -11,22 +11,23 @@ import (
 	"github.com/sirupsen/logrus"
 
 	"github.com/lima-vm/lima/v2/pkg/guestagent/api"
+	"github.com/lima-vm/lima/v2/pkg/limatype"
 	"github.com/lima-vm/lima/v2/pkg/limayaml"
 )
 
 type portForwarder struct {
 	sshConfig   *ssh.SSHConfig
 	sshHostPort int
-	rules       []limayaml.PortForward
+	rules       []limatype.PortForward
 	ignore      bool
-	vmType      limayaml.VMType
+	vmType      limatype.VMType
 }
 
 const sshGuestPort = 22
 
 var IPv4loopback1 = limayaml.IPv4loopback1
 
-func newPortForwarder(sshConfig *ssh.SSHConfig, sshHostPort int, rules []limayaml.PortForward, ignore bool, vmType limayaml.VMType) *portForwarder {
+func newPortForwarder(sshConfig *ssh.SSHConfig, sshHostPort int, rules []limatype.PortForward, ignore bool, vmType limatype.VMType) *portForwarder {
 	return &portForwarder{
 		sshConfig:   sshConfig,
 		sshHostPort: sshHostPort,
@@ -36,7 +37,7 @@ func newPortForwarder(sshConfig *ssh.SSHConfig, sshHostPort int, rules []limayam
 	}
 }
 
-func hostAddress(rule limayaml.PortForward, guest *api.IPPort) string {
+func hostAddress(rule limatype.PortForward, guest *api.IPPort) string {
 	if rule.HostSocket != "" {
 		return rule.HostSocket
 	}
@@ -57,7 +58,7 @@ func (pf *portForwarder) forwardingAddresses(guest *api.IPPort) (hostAddr, guest
 			continue
 		}
 		switch rule.Proto {
-		case limayaml.ProtoTCP, limayaml.ProtoAny:
+		case limatype.ProtoTCP, limatype.ProtoAny:
 		default:
 			continue
 		}
diff --git a/pkg/hostagent/requirements.go b/pkg/hostagent/requirements.go
index 1c09b42b..148d6785 100644
--- a/pkg/hostagent/requirements.go
+++ b/pkg/hostagent/requirements.go
@@ -12,7 +12,7 @@ import (
 	"github.com/lima-vm/sshocker/pkg/ssh"
 	"github.com/sirupsen/logrus"
 
-	"github.com/lima-vm/lima/v2/pkg/limayaml"
+	"github.com/lima-vm/lima/v2/pkg/limatype"
 )
 
 func (a *HostAgent) waitForRequirements(label string, requirements []requirement) error {
@@ -149,7 +149,7 @@ it must not be created until the session reset is done.
 `,
 		})
 
-	if *a.instConfig.MountType == limayaml.REVSSHFS && len(a.instConfig.Mounts) > 0 {
+	if *a.instConfig.MountType == limatype.REVSSHFS && len(a.instConfig.Mounts) > 0 {
 		req = append(req, requirement{
 			description: "sshfs binary to be installed",
 			script: `#!/bin/bash
@@ -216,7 +216,7 @@ Also see "/var/log/cloud-init-output.log" in the guest.
 			})
 	}
 	for _, probe := range a.instConfig.Probes {
-		if probe.Mode == limayaml.ProbeModeReadiness {
+		if probe.Mode == limatype.ProbeModeReadiness {
 			req = append(req, requirement{
 				description: probe.Description,
 				script:      probe.Script,
diff --git a/pkg/instance/ansible.go b/pkg/instance/ansible.go
index c0f9b0f1..2468804a 100644
--- a/pkg/instance/ansible.go
+++ b/pkg/instance/ansible.go
@@ -13,14 +13,13 @@ import (
 	"github.com/goccy/go-yaml"
 	"github.com/sirupsen/logrus"
 
-	"github.com/lima-vm/lima/v2/pkg/limayaml"
-	"github.com/lima-vm/lima/v2/pkg/store"
-	"github.com/lima-vm/lima/v2/pkg/store/filenames"
+	"github.com/lima-vm/lima/v2/pkg/limatype"
+	"github.com/lima-vm/lima/v2/pkg/limatype/filenames"
 )
 
-func runAnsibleProvision(ctx context.Context, inst *store.Instance) error {
+func runAnsibleProvision(ctx context.Context, inst *limatype.Instance) error {
 	for _, f := range inst.Config.Provision {
-		if f.Mode == limayaml.ProvisionModeAnsible {
+		if f.Mode == limatype.ProvisionModeAnsible {
 			logrus.Infof("Waiting for ansible playbook %q", f.Playbook)
 			if err := runAnsiblePlaybook(ctx, inst, f.Playbook); err != nil {
 				return err
@@ -30,7 +29,7 @@ func runAnsibleProvision(ctx context.Context, inst *store.Instance) error {
 	return nil
 }
 
-func runAnsiblePlaybook(ctx context.Context, inst *store.Instance, playbook string) error {
+func runAnsiblePlaybook(ctx context.Context, inst *limatype.Instance, playbook string) error {
 	inventory, err := createAnsibleInventory(inst)
 	if err != nil {
 		return err
@@ -44,7 +43,7 @@ func runAnsiblePlaybook(ctx context.Context, inst *store.Instance, playbook stri
 	return cmd.Run()
 }
 
-func createAnsibleInventory(inst *store.Instance) (string, error) {
+func createAnsibleInventory(inst *limatype.Instance) (string, error) {
 	vars := map[string]any{
 		"ansible_connection":      "ssh",
 		"ansible_host":            inst.Hostname,
@@ -67,7 +66,7 @@ func createAnsibleInventory(inst *store.Instance) (string, error) {
 	return inventory, os.WriteFile(inventory, bytes, 0o644)
 }
 
-func getAnsibleEnvironment(inst *store.Instance) []string {
+func getAnsibleEnvironment(inst *limatype.Instance) []string {
 	env := os.Environ()
 	for key, val := range inst.Config.Param {
 		env = append(env, fmt.Sprintf("PARAM_%s=%s", key, val))
diff --git a/pkg/instance/clone.go b/pkg/instance/clone.go
index b2f3410b..f4d1a013 100644
--- a/pkg/instance/clone.go
+++ b/pkg/instance/clone.go
@@ -15,23 +15,25 @@ import (
 
 	continuityfs "github.com/containerd/continuity/fs"
 
+	"github.com/lima-vm/lima/v2/pkg/limatype"
+	"github.com/lima-vm/lima/v2/pkg/limatype/dirnames"
+	"github.com/lima-vm/lima/v2/pkg/limatype/filenames"
 	"github.com/lima-vm/lima/v2/pkg/osutil"
 	"github.com/lima-vm/lima/v2/pkg/store"
-	"github.com/lima-vm/lima/v2/pkg/store/filenames"
 )
 
-func Clone(ctx context.Context, oldInst *store.Instance, newInstName string) (*store.Instance, error) {
+func Clone(ctx context.Context, oldInst *limatype.Instance, newInstName string) (*limatype.Instance, error) {
 	if newInstName == "" {
 		return nil, errors.New("got empty instName")
 	}
 	if oldInst.Name == newInstName {
 		return nil, fmt.Errorf("new instance name %q must be different from %q", newInstName, oldInst.Name)
 	}
-	if oldInst.Status == store.StatusRunning {
+	if oldInst.Status == limatype.StatusRunning {
 		return nil, errors.New("cannot clone a running instance")
 	}
 
-	newInstDir, err := store.InstanceDir(newInstName)
+	newInstDir, err := dirnames.InstanceDir(newInstName)
 	if err != nil {
 		return nil, err
 	}
diff --git a/pkg/instance/create.go b/pkg/instance/create.go
index d9064632..08c5bd42 100644
--- a/pkg/instance/create.go
+++ b/pkg/instance/create.go
@@ -12,14 +12,16 @@ import (
 
 	"github.com/lima-vm/lima/v2/pkg/cidata"
 	"github.com/lima-vm/lima/v2/pkg/driverutil"
+	"github.com/lima-vm/lima/v2/pkg/limatype"
+	"github.com/lima-vm/lima/v2/pkg/limatype/dirnames"
+	"github.com/lima-vm/lima/v2/pkg/limatype/filenames"
 	"github.com/lima-vm/lima/v2/pkg/limayaml"
 	"github.com/lima-vm/lima/v2/pkg/osutil"
 	"github.com/lima-vm/lima/v2/pkg/store"
-	"github.com/lima-vm/lima/v2/pkg/store/filenames"
 	"github.com/lima-vm/lima/v2/pkg/version"
 )
 
-func Create(ctx context.Context, instName string, instConfig []byte, saveBrokenYAML bool) (*store.Instance, error) {
+func Create(ctx context.Context, instName string, instConfig []byte, saveBrokenYAML bool) (*limatype.Instance, error) {
 	if instName == "" {
 		return nil, errors.New("got empty instName")
 	}
@@ -27,7 +29,7 @@ func Create(ctx context.Context, instName string, instConfig []byte, saveBrokenY
 		return nil, errors.New("got empty instConfig")
 	}
 
-	instDir, err := store.InstanceDir(instName)
+	instDir, err := dirnames.InstanceDir(instName)
 	if err != nil {
 		return nil, err
 	}
@@ -47,6 +49,9 @@ func Create(ctx context.Context, instName string, instConfig []byte, saveBrokenY
 	if err != nil {
 		return nil, err
 	}
+	if err := driverutil.ResolveVMType(ctx, loadedInstConfig, filePath); err != nil {
+		return nil, fmt.Errorf("failed to resolve vm for %q: %w", filePath, err)
+	}
 	if err := limayaml.Validate(loadedInstConfig, true); err != nil {
 		if !saveBrokenYAML {
 			return nil, err
@@ -80,7 +85,7 @@ func Create(ctx context.Context, instName string, instConfig []byte, saveBrokenY
 		return nil, fmt.Errorf("failed to create driver instance: %w", err)
 	}
 
-	if err := limaDriver.Register(ctx); err != nil {
+	if err := limaDriver.Create(ctx); err != nil {
 		return nil, err
 	}
 
diff --git a/pkg/instance/delete.go b/pkg/instance/delete.go
index 7c53fdf1..df0954db 100644
--- a/pkg/instance/delete.go
+++ b/pkg/instance/delete.go
@@ -10,15 +10,15 @@ import (
 	"os"
 
 	"github.com/lima-vm/lima/v2/pkg/driverutil"
-	"github.com/lima-vm/lima/v2/pkg/store"
+	"github.com/lima-vm/lima/v2/pkg/limatype"
 )
 
-func Delete(ctx context.Context, inst *store.Instance, force bool) error {
+func Delete(ctx context.Context, inst *limatype.Instance, force bool) error {
 	if inst.Protected {
 		return errors.New("instance is protected to prohibit accidental removal (Hint: use `limactl unprotect`)")
 	}
-	if !force && inst.Status != store.StatusStopped {
-		return fmt.Errorf("expected status %q, got %q", store.StatusStopped, inst.Status)
+	if !force && inst.Status != limatype.StatusStopped {
+		return fmt.Errorf("expected status %q, got %q", limatype.StatusStopped, inst.Status)
 	}
 
 	StopForcibly(inst)
@@ -35,11 +35,11 @@ func Delete(ctx context.Context, inst *store.Instance, force bool) error {
 	return nil
 }
 
-func unregister(ctx context.Context, inst *store.Instance) error {
+func unregister(ctx context.Context, inst *limatype.Instance) error {
 	limaDriver, err := driverutil.CreateConfiguredDriver(inst, 0)
 	if err != nil {
 		return fmt.Errorf("failed to create driver instance: %w", err)
 	}
 
-	return limaDriver.Unregister(ctx)
+	return limaDriver.Delete(ctx)
 }
diff --git a/pkg/instance/restart.go b/pkg/instance/restart.go
index 0a5f8b98..52db7a4d 100644
--- a/pkg/instance/restart.go
+++ b/pkg/instance/restart.go
@@ -8,16 +8,15 @@ import (
 
 	"github.com/sirupsen/logrus"
 
+	"github.com/lima-vm/lima/v2/pkg/limatype"
 	networks "github.com/lima-vm/lima/v2/pkg/networks/reconcile"
-	"github.com/lima-vm/lima/v2/pkg/store"
 )
 
 const (
 	launchHostAgentForeground = false
-	showProgress              = false
 )
 
-func Restart(ctx context.Context, inst *store.Instance) error {
+func Restart(ctx context.Context, inst *limatype.Instance, showProgress bool) error {
 	if err := StopGracefully(ctx, inst, true); err != nil {
 		return err
 	}
@@ -33,7 +32,7 @@ func Restart(ctx context.Context, inst *store.Instance) error {
 	return nil
 }
 
-func RestartForcibly(ctx context.Context, inst *store.Instance) error {
+func RestartForcibly(ctx context.Context, inst *limatype.Instance, showProgress bool) error {
 	logrus.Info("Restarting the instance forcibly")
 	StopForcibly(inst)
 
diff --git a/pkg/instance/start.go b/pkg/instance/start.go
index 4f08219f..a44f3113 100644
--- a/pkg/instance/start.go
+++ b/pkg/instance/start.go
@@ -26,10 +26,10 @@ import (
 	"github.com/lima-vm/lima/v2/pkg/fileutils"
 	hostagentevents "github.com/lima-vm/lima/v2/pkg/hostagent/events"
 	"github.com/lima-vm/lima/v2/pkg/imgutil/proxyimgutil"
-	"github.com/lima-vm/lima/v2/pkg/limayaml"
+	"github.com/lima-vm/lima/v2/pkg/limatype"
+	"github.com/lima-vm/lima/v2/pkg/limatype/filenames"
 	"github.com/lima-vm/lima/v2/pkg/registry"
 	"github.com/lima-vm/lima/v2/pkg/store"
-	"github.com/lima-vm/lima/v2/pkg/store/filenames"
 	"github.com/lima-vm/lima/v2/pkg/usrlocalsharelima"
 )
 
@@ -40,7 +40,7 @@ const DefaultWatchHostAgentEventsTimeout = 10 * time.Minute
 // ensureNerdctlArchiveCache prefetches the nerdctl-full-VERSION-GOOS-GOARCH.tar.gz archive
 // into the cache before launching the hostagent process, so that we can show the progress in tty.
 // https://github.com/lima-vm/lima/issues/326
-func ensureNerdctlArchiveCache(ctx context.Context, y *limayaml.LimaYAML, created bool) (string, error) {
+func ensureNerdctlArchiveCache(ctx context.Context, y *limatype.LimaYAML, created bool) (string, error) {
 	if !*y.Containerd.System && !*y.Containerd.User {
 		// nerdctl archive is not needed
 		return "", nil
@@ -79,7 +79,7 @@ type Prepared struct {
 }
 
 // Prepare ensures the disk, the nerdctl archive, etc.
-func Prepare(ctx context.Context, inst *store.Instance) (*Prepared, error) {
+func Prepare(ctx context.Context, inst *limatype.Instance) (*Prepared, error) {
 	var guestAgent string
 	if !*inst.Config.Plain {
 		var err error
@@ -97,7 +97,7 @@ func Prepare(ctx context.Context, inst *store.Instance) (*Prepared, error) {
 		return nil, err
 	}
 
-	if err := limaDriver.Initialize(ctx); err != nil {
+	if err := limaDriver.Create(ctx); err != nil {
 		return nil, err
 	}
 
@@ -179,7 +179,7 @@ func Prepare(ctx context.Context, inst *store.Instance) (*Prepared, error) {
 // shut down again.
 //
 // Start calls Prepare by itself, so you do not need to call Prepare manually before calling Start.
-func Start(ctx context.Context, inst *store.Instance, limactl string, launchHostAgentForeground, showProgress bool) error {
+func Start(ctx context.Context, inst *limatype.Instance, limactl string, launchHostAgentForeground, showProgress bool) error {
 	haPIDPath := filepath.Join(inst.Dir, filenames.HostAgentPID)
 	if _, err := os.Stat(haPIDPath); !errors.Is(err, os.ErrNotExist) {
 		return fmt.Errorf("instance %q seems running (hint: remove %q if the instance is not actually running)", inst.Name, haPIDPath)
@@ -226,7 +226,7 @@ func Start(ctx context.Context, inst *store.Instance, limactl string, launchHost
 		"hostagent",
 		"--pidfile", haPIDPath,
 		"--socket", haSockPath)
-	if prepared.Driver.Info().CanRunGUI {
+	if prepared.Driver.Info().Features.CanRunGUI {
 		args = append(args, "--run-gui")
 	}
 	if prepared.GuestAgent != "" {
@@ -300,7 +300,7 @@ func waitHostAgentStart(_ context.Context, haPIDPath, haStderrPath string) error
 	}
 }
 
-func watchHostAgentEvents(ctx context.Context, inst *store.Instance, haStdoutPath, haStderrPath string, begin time.Time, showProgress bool) error {
+func watchHostAgentEvents(ctx context.Context, inst *limatype.Instance, haStdoutPath, haStderrPath string, begin time.Time, showProgress bool) error {
 	ctx, cancel := context.WithTimeout(ctx, watchHostAgentTimeout(ctx))
 	defer cancel()
 
@@ -332,9 +332,9 @@ func watchHostAgentEvents(ctx context.Context, inst *store.Instance, haStdoutPat
 
 			if progress.Completed {
 				cloudInitCompleted = true
+				logrus.Infof("Cloud-init progress monitoring done.")
 			}
 		}
-
 		if len(ev.Status.Errors) > 0 {
 			logrus.Errorf("%+v", ev.Status.Errors)
 		}
@@ -355,7 +355,6 @@ func watchHostAgentEvents(ctx context.Context, inst *store.Instance, haStdoutPat
 			}
 
 			if showProgress && !cloudInitCompleted {
-				logrus.Infof("VM is running, waiting for cloud-init to complete...")
 				return false
 			}
 
@@ -411,7 +410,7 @@ func LimactlShellCmd(instName string) string {
 	return shellCmd
 }
 
-func ShowMessage(inst *store.Instance) error {
+func ShowMessage(inst *limatype.Instance) error {
 	if inst.Message == "" {
 		return nil
 	}
@@ -438,7 +437,7 @@ func ShowMessage(inst *store.Instance) error {
 
 // prepareDiffDisk checks the disk size difference between inst.Disk and yaml.Disk.
 // If there is no diffDisk, return nil (the instance has not been initialized or started yet).
-func prepareDiffDisk(ctx context.Context, inst *store.Instance) error {
+func prepareDiffDisk(ctx context.Context, inst *limatype.Instance) error {
 	diffDisk := filepath.Join(inst.Dir, filenames.DiffDisk)
 
 	// Handle the instance initialization
diff --git a/pkg/instance/stop.go b/pkg/instance/stop.go
index b2fb6d04..0b677080 100644
--- a/pkg/instance/stop.go
+++ b/pkg/instance/stop.go
@@ -15,18 +15,19 @@ import (
 	"github.com/sirupsen/logrus"
 
 	hostagentevents "github.com/lima-vm/lima/v2/pkg/hostagent/events"
+	"github.com/lima-vm/lima/v2/pkg/limatype"
+	"github.com/lima-vm/lima/v2/pkg/limatype/filenames"
 	"github.com/lima-vm/lima/v2/pkg/osutil"
 	"github.com/lima-vm/lima/v2/pkg/store"
-	"github.com/lima-vm/lima/v2/pkg/store/filenames"
 )
 
-func StopGracefully(ctx context.Context, inst *store.Instance, isRestart bool) error {
-	if inst.Status != store.StatusRunning {
+func StopGracefully(ctx context.Context, inst *limatype.Instance, isRestart bool) error {
+	if inst.Status != limatype.StatusRunning {
 		if isRestart {
 			logrus.Warn("The instance is not running, continuing with the restart")
 			return nil
 		}
-		return fmt.Errorf("expected status %q, got %q (maybe use `limactl stop -f`?)", store.StatusRunning, inst.Status)
+		return fmt.Errorf("expected status %q, got %q (maybe use `limactl stop -f`?)", limatype.StatusRunning, inst.Status)
 	}
 
 	begin := time.Now() // used for logrus propagation
@@ -45,7 +46,7 @@ func StopGracefully(ctx context.Context, inst *store.Instance, isRestart bool) e
 	return waitForInstanceShutdown(ctx, inst)
 }
 
-func waitForHostAgentTermination(ctx context.Context, inst *store.Instance, begin time.Time) error {
+func waitForHostAgentTermination(ctx context.Context, inst *limatype.Instance, begin time.Time) error {
 	ctx, cancel := context.WithTimeout(ctx, 3*time.Minute+10*time.Second)
 	defer cancel()
 
@@ -75,7 +76,7 @@ func waitForHostAgentTermination(ctx context.Context, inst *store.Instance, begi
 	return nil
 }
 
-func waitForInstanceShutdown(ctx context.Context, inst *store.Instance) error {
+func waitForInstanceShutdown(ctx context.Context, inst *limatype.Instance) error {
 	ctx, cancel := context.WithTimeout(ctx, 3*time.Minute)
 	defer cancel()
 
@@ -90,7 +91,7 @@ func waitForInstanceShutdown(ctx context.Context, inst *store.Instance) error {
 				return fmt.Errorf("failed to inspect instance status: %w", err)
 			}
 
-			if updatedInst.Status == store.StatusStopped {
+			if updatedInst.Status == limatype.StatusStopped {
 				logrus.Infof("The instance %s has shut down", updatedInst.Name)
 				return nil
 			}
@@ -100,7 +101,7 @@ func waitForInstanceShutdown(ctx context.Context, inst *store.Instance) error {
 	}
 }
 
-func StopForcibly(inst *store.Instance) {
+func StopForcibly(inst *limatype.Instance) {
 	if inst.DriverPID > 0 {
 		logrus.Infof("Sending SIGKILL to the %s driver process %d", inst.VMType, inst.DriverPID)
 		if err := osutil.SysKill(inst.DriverPID, osutil.SigKill); err != nil {
diff --git a/pkg/limainfo/limainfo.go b/pkg/limainfo/limainfo.go
index dda29025..d9a42882 100644
--- a/pkg/limainfo/limainfo.go
+++ b/pkg/limainfo/limainfo.go
@@ -11,9 +11,10 @@ import (
 	"github.com/sirupsen/logrus"
 
 	"github.com/lima-vm/lima/v2/pkg/envutil"
+	"github.com/lima-vm/lima/v2/pkg/limatype"
+	"github.com/lima-vm/lima/v2/pkg/limatype/dirnames"
 	"github.com/lima-vm/lima/v2/pkg/limayaml"
 	"github.com/lima-vm/lima/v2/pkg/registry"
-	"github.com/lima-vm/lima/v2/pkg/store/dirnames"
 	"github.com/lima-vm/lima/v2/pkg/templatestore"
 	"github.com/lima-vm/lima/v2/pkg/usrlocalsharelima"
 	"github.com/lima-vm/lima/v2/pkg/version"
@@ -22,11 +23,11 @@ import (
 type LimaInfo struct {
 	Version         string                       `json:"version"`
 	Templates       []templatestore.Template     `json:"templates"`
-	DefaultTemplate *limayaml.LimaYAML           `json:"defaultTemplate"`
+	DefaultTemplate *limatype.LimaYAML           `json:"defaultTemplate"`
 	LimaHome        string                       `json:"limaHome"`
 	VMTypes         []string                     `json:"vmTypes"`     // since Lima v0.14.2
 	VMTypesEx       map[string]DriverExt         `json:"vmTypesEx"`   // since Lima v2.0.0
-	GuestAgents     map[limayaml.Arch]GuestAgent `json:"guestAgents"` // since Lima v1.1.0
+	GuestAgents     map[limatype.Arch]GuestAgent `json:"guestAgents"` // since Lima v1.1.0
 	ShellEnvBlock   []string                     `json:"shellEnvBlock"`
 }
 
@@ -69,7 +70,7 @@ func New(ctx context.Context) (*LimaInfo, error) {
 		DefaultTemplate: y,
 		VMTypes:         vmTypes,
 		VMTypesEx:       vmTypesEx,
-		GuestAgents:     make(map[limayaml.Arch]GuestAgent),
+		GuestAgents:     make(map[limatype.Arch]GuestAgent),
 		ShellEnvBlock:   envutil.GetDefaultBlockList(),
 	}
 	info.Templates, err = templatestore.Templates()
@@ -80,8 +81,8 @@ func New(ctx context.Context) (*LimaInfo, error) {
 	if err != nil {
 		return nil, err
 	}
-	for _, arch := range limayaml.ArchTypes {
-		bin, err := usrlocalsharelima.GuestAgentBinary(limayaml.LINUX, arch)
+	for _, arch := range limatype.ArchTypes {
+		bin, err := usrlocalsharelima.GuestAgentBinary(limatype.LINUX, arch)
 		if err != nil {
 			if errors.Is(err, fs.ErrNotExist) {
 				logrus.WithError(err).Debugf("Failed to resolve the guest agent binary for %q", arch)
diff --git a/pkg/limatmpl/embed.go b/pkg/limatmpl/embed.go
index 6c2a7b91..9ccbe564 100644
--- a/pkg/limatmpl/embed.go
+++ b/pkg/limatmpl/embed.go
@@ -18,9 +18,9 @@ import (
 	"github.com/coreos/go-semver/semver"
 	"github.com/sirupsen/logrus"
 
-	"github.com/lima-vm/lima/v2/pkg/limayaml"
-	"github.com/lima-vm/lima/v2/pkg/store/dirnames"
-	"github.com/lima-vm/lima/v2/pkg/store/filenames"
+	"github.com/lima-vm/lima/v2/pkg/limatype"
+	"github.com/lima-vm/lima/v2/pkg/limatype/dirnames"
+	"github.com/lima-vm/lima/v2/pkg/limatype/filenames"
 	"github.com/lima-vm/lima/v2/pkg/version/versionutil"
 	"github.com/lima-vm/lima/v2/pkg/yqutil"
 )
@@ -106,7 +106,7 @@ func (tmpl *Template) embedAllBases(ctx context.Context, embedAll, defaultBase b
 	return nil
 }
 
-func (tmpl *Template) embedBase(ctx context.Context, baseLocator limayaml.LocatorWithDigest, embedAll bool, seen map[string]bool) error {
+func (tmpl *Template) embedBase(ctx context.Context, baseLocator limatype.LocatorWithDigest, embedAll bool, seen map[string]bool) error {
 	logrus.Debugf("Embedding base %q in template %q", baseLocator.URL, tmpl.Locator)
 	if err := tmpl.Unmarshal(); err != nil {
 		return err
@@ -643,7 +643,7 @@ func (tmpl *Template) embedAllScripts(ctx context.Context, embedAll bool) error
 		}
 		newName := "script"
 		switch p.Mode {
-		case limayaml.ProvisionModeData:
+		case limatype.ProvisionModeData:
 			newName = "content"
 			if p.Content != nil {
 				continue
diff --git a/pkg/limatmpl/locator.go b/pkg/limatmpl/locator.go
index 7940e164..98e664af 100644
--- a/pkg/limatmpl/locator.go
+++ b/pkg/limatmpl/locator.go
@@ -20,6 +20,7 @@ import (
 
 	"github.com/lima-vm/lima/v2/pkg/identifiers"
 	"github.com/lima-vm/lima/v2/pkg/ioutilx"
+	"github.com/lima-vm/lima/v2/pkg/limatype"
 	"github.com/lima-vm/lima/v2/pkg/limayaml"
 	"github.com/lima-vm/lima/v2/pkg/templatestore"
 )
@@ -131,17 +132,17 @@ func Read(ctx context.Context, name, locator string) (*Template, error) {
 var imageURLRegex = regexp.MustCompile(`\.(img|qcow2|raw|iso)(\.(gz|xz|bz2|zstd))?$`)
 
 // Image architecture will be guessed based on the presence of arch keywords.
-var archKeywords = map[string]limayaml.Arch{
-	"aarch64": limayaml.AARCH64,
-	"amd64":   limayaml.X8664,
-	"arm64":   limayaml.AARCH64,
-	"armhf":   limayaml.ARMV7L,
-	"armv7l":  limayaml.ARMV7L,
-	"ppc64el": limayaml.PPC64LE,
-	"ppc64le": limayaml.PPC64LE,
-	"riscv64": limayaml.RISCV64,
-	"s390x":   limayaml.S390X,
-	"x86_64":  limayaml.X8664,
+var archKeywords = map[string]limatype.Arch{
+	"aarch64": limatype.AARCH64,
+	"amd64":   limatype.X8664,
+	"arm64":   limatype.AARCH64,
+	"armhf":   limatype.ARMV7L,
+	"armv7l":  limatype.ARMV7L,
+	"ppc64el": limatype.PPC64LE,
+	"ppc64le": limatype.PPC64LE,
+	"riscv64": limatype.RISCV64,
+	"s390x":   limatype.S390X,
+	"x86_64":  limatype.X8664,
 }
 
 // These generic tags will be stripped from an image name before turning it into an instance name.
@@ -174,7 +175,7 @@ func imageTemplate(tmpl *Template, locator string) bool {
 		return false
 	}
 
-	var imageArch limayaml.Arch
+	var imageArch limatype.Arch
 	for keyword, arch := range archKeywords {
 		pattern := fmt.Sprintf(`\b%s\b`, keyword)
 		if regexp.MustCompile(pattern).MatchString(locator) {
@@ -183,7 +184,7 @@ func imageTemplate(tmpl *Template, locator string) bool {
 		}
 	}
 	if imageArch == "" {
-		imageArch = limayaml.NewArch(runtime.GOARCH)
+		imageArch = limatype.NewArch(runtime.GOARCH)
 		logrus.Warnf("cannot determine image arch from URL %q; assuming %q", locator, imageArch)
 	}
 	template := `arch: %q
diff --git a/pkg/limatmpl/template.go b/pkg/limatmpl/template.go
index 1ca56a3c..5ac277f9 100644
--- a/pkg/limatmpl/template.go
+++ b/pkg/limatmpl/template.go
@@ -6,6 +6,7 @@ package limatmpl
 import (
 	"strings"
 
+	"github.com/lima-vm/lima/v2/pkg/limatype"
 	"github.com/lima-vm/lima/v2/pkg/limayaml"
 )
 
@@ -15,7 +16,7 @@ type Template struct {
 
 	// The following fields are only used when the template represents a YAML config file.
 	Name   string // instance name, may be inferred from locator
-	Config *limayaml.LimaYAML
+	Config *limatype.LimaYAML
 
 	expr strings.Builder // yq expression to update template
 }
@@ -33,7 +34,7 @@ func (tmpl *Template) ClearOnError(err error) error {
 // tmpl.Bytes is expected to set tmpl.Config back to nil.
 func (tmpl *Template) Unmarshal() error {
 	if tmpl.Config == nil {
-		tmpl.Config = &limayaml.LimaYAML{}
+		tmpl.Config = &limatype.LimaYAML{}
 		if err := limayaml.Unmarshal(tmpl.Bytes, tmpl.Config, tmpl.Locator); err != nil {
 			tmpl.Config = nil
 			return err
diff --git a/pkg/store/dirnames/dirnames.go b/pkg/limatype/dirnames/dirnames.go
similarity index 66%
rename from pkg/store/dirnames/dirnames.go
rename to pkg/limatype/dirnames/dirnames.go
index 43607f9a..670de227 100644
--- a/pkg/store/dirnames/dirnames.go
+++ b/pkg/limatype/dirnames/dirnames.go
@@ -8,8 +8,10 @@ import (
 	"fmt"
 	"os"
 	"path/filepath"
+	"strings"
 
-	"github.com/lima-vm/lima/v2/pkg/store/filenames"
+	"github.com/lima-vm/lima/v2/pkg/identifiers"
+	"github.com/lima-vm/lima/v2/pkg/limatype/filenames"
 )
 
 // DotLima is a directory that appears under the home directory.
@@ -74,3 +76,30 @@ func LimaTemplatesDir() (string, error) {
 	}
 	return filepath.Join(limaDir, filenames.TemplatesDir), nil
 }
+
+// InstanceDir returns the instance dir.
+// InstanceDir does not check whether the instance exists.
+func InstanceDir(name string) (string, error) {
+	if err := ValidateInstName(name); err != nil {
+		return "", err
+	}
+	limaDir, err := LimaDir()
+	if err != nil {
+		return "", err
+	}
+	dir := filepath.Join(limaDir, name)
+	return dir, nil
+}
+
+// ValidateInstName checks if the name is a valid instance name. For this it needs to
+// be a valid identifier, and not end in .yml or .yaml (case insensitively).
+func ValidateInstName(name string) error {
+	if err := identifiers.Validate(name); err != nil {
+		return fmt.Errorf("instance name %q is not a valid identifier: %w", name, err)
+	}
+	lower := strings.ToLower(name)
+	if strings.HasSuffix(lower, ".yml") || strings.HasSuffix(lower, ".yaml") {
+		return fmt.Errorf("instance name %q must not end with .yml or .yaml suffix", name)
+	}
+	return nil
+}
diff --git a/pkg/store/filenames/filenames.go b/pkg/limatype/filenames/filenames.go
similarity index 100%
rename from pkg/store/filenames/filenames.go
rename to pkg/limatype/filenames/filenames.go
diff --git a/pkg/limatype/lima_instance.go b/pkg/limatype/lima_instance.go
new file mode 100644
index 00000000..308dd21e
--- /dev/null
+++ b/pkg/limatype/lima_instance.go
@@ -0,0 +1,109 @@
+// SPDX-FileCopyrightText: Copyright The Lima Authors
+// SPDX-License-Identifier: Apache-2.0
+
+package limatype
+
+import (
+	"encoding/json"
+	"errors"
+	"os"
+	"path/filepath"
+
+	"github.com/lima-vm/lima/v2/pkg/limatype/filenames"
+)
+
+type Status = string
+
+const (
+	StatusUnknown       Status = ""
+	StatusUninitialized Status = "Uninitialized"
+	StatusInstalling    Status = "Installing"
+	StatusBroken        Status = "Broken"
+	StatusStopped       Status = "Stopped"
+	StatusRunning       Status = "Running"
+)
+
+type Instance struct {
+	Name string `json:"name"`
+	// Hostname, not HostName (corresponds to SSH's naming convention)
+	Hostname        string            `json:"hostname"`
+	Status          Status            `json:"status"`
+	Dir             string            `json:"dir"`
+	VMType          VMType            `json:"vmType"`
+	Arch            Arch              `json:"arch"`
+	CPUs            int               `json:"cpus,omitempty"`
+	Memory          int64             `json:"memory,omitempty"` // bytes
+	Disk            int64             `json:"disk,omitempty"`   // bytes
+	Message         string            `json:"message,omitempty"`
+	AdditionalDisks []Disk            `json:"additionalDisks,omitempty"`
+	Networks        []Network         `json:"network,omitempty"`
+	SSHLocalPort    int               `json:"sshLocalPort,omitempty"`
+	SSHConfigFile   string            `json:"sshConfigFile,omitempty"`
+	HostAgentPID    int               `json:"hostAgentPID,omitempty"`
+	DriverPID       int               `json:"driverPID,omitempty"`
+	Errors          []error           `json:"errors,omitempty"`
+	Config          *LimaYAML         `json:"config,omitempty"`
+	SSHAddress      string            `json:"sshAddress,omitempty"`
+	Protected       bool              `json:"protected"`
+	LimaVersion     string            `json:"limaVersion"`
+	Param           map[string]string `json:"param,omitempty"`
+}
+
+// Protect protects the instance to prohibit accidental removal.
+// Protect does not return an error even when the instance is already protected.
+func (inst *Instance) Protect() error {
+	protected := filepath.Join(inst.Dir, filenames.Protected)
+	// TODO: Do an equivalent of `chmod +a "everyone deny delete,delete_child,file_inherit,directory_inherit"`
+	// https://github.com/lima-vm/lima/issues/1595
+	if err := os.WriteFile(protected, nil, 0o400); err != nil {
+		return err
+	}
+	inst.Protected = true
+	return nil
+}
+
+// Unprotect unprotects the instance.
+// Unprotect does not return an error even when the instance is already unprotected.
+func (inst *Instance) Unprotect() error {
+	protected := filepath.Join(inst.Dir, filenames.Protected)
+	if err := os.RemoveAll(protected); err != nil {
+		return err
+	}
+	inst.Protected = false
+	return nil
+}
+
+func (inst *Instance) MarshalJSON() ([]byte, error) {
+	type Alias Instance
+	errorsAsStrings := make([]string, len(inst.Errors))
+	for i, err := range inst.Errors {
+		if err != nil {
+			errorsAsStrings[i] = err.Error()
+		}
+	}
+	return json.Marshal(&struct {
+		*Alias
+		Errors []string `json:"errors,omitempty"`
+	}{
+		Alias:  (*Alias)(inst),
+		Errors: errorsAsStrings,
+	})
+}
+
+func (inst *Instance) UnmarshalJSON(data []byte) error {
+	type Alias Instance
+	aux := &struct {
+		*Alias
+		Errors []string `json:"errors,omitempty"`
+	}{
+		Alias: (*Alias)(inst),
+	}
+	if err := json.Unmarshal(data, &aux); err != nil {
+		return err
+	}
+	inst.Errors = nil
+	for _, msg := range aux.Errors {
+		inst.Errors = append(inst.Errors, errors.New(msg))
+	}
+	return nil
+}
diff --git a/pkg/limayaml/limayaml.go b/pkg/limatype/lima_yaml.go
similarity index 88%
rename from pkg/limayaml/limayaml.go
rename to pkg/limatype/lima_yaml.go
index a21c4aa5..5c5106aa 100644
--- a/pkg/limayaml/limayaml.go
+++ b/pkg/limatype/lima_yaml.go
@@ -1,12 +1,15 @@
 // SPDX-FileCopyrightText: Copyright The Lima Authors
 // SPDX-License-Identifier: Apache-2.0
 
-package limayaml
+package limatype
 
 import (
 	"net"
+	"runtime"
 
 	"github.com/opencontainers/go-digest"
+	"github.com/sirupsen/logrus"
+	"golang.org/x/sys/cpu"
 )
 
 type LimaYAML struct {
@@ -46,13 +49,14 @@ type LimaYAML struct {
 	DNS          []net.IP          `yaml:"dns,omitempty" json:"dns,omitempty"`
 	HostResolver HostResolver      `yaml:"hostResolver,omitempty" json:"hostResolver,omitempty"`
 	// `useHostResolver` was deprecated in Lima v0.8.1, removed in Lima v0.14.0. Use `hostResolver.enabled` instead.
-	PropagateProxyEnv    *bool          `yaml:"propagateProxyEnv,omitempty" json:"propagateProxyEnv,omitempty" jsonschema:"nullable"`
-	CACertificates       CACertificates `yaml:"caCerts,omitempty" json:"caCerts,omitempty"`
-	Rosetta              Rosetta        `yaml:"rosetta,omitempty" json:"rosetta,omitempty"`
-	Plain                *bool          `yaml:"plain,omitempty" json:"plain,omitempty" jsonschema:"nullable"`
-	TimeZone             *string        `yaml:"timezone,omitempty" json:"timezone,omitempty" jsonschema:"nullable"`
-	NestedVirtualization *bool          `yaml:"nestedVirtualization,omitempty" json:"nestedVirtualization,omitempty" jsonschema:"nullable"`
-	User                 User           `yaml:"user,omitempty" json:"user,omitempty"`
+	PropagateProxyEnv *bool          `yaml:"propagateProxyEnv,omitempty" json:"propagateProxyEnv,omitempty" jsonschema:"nullable"`
+	CACertificates    CACertificates `yaml:"caCerts,omitempty" json:"caCerts,omitempty"`
+	// Deprecated: Use VMOpts.VZ.Rosetta instead.
+	Rosetta              Rosetta `yaml:"rosetta,omitempty" json:"rosetta,omitempty"`
+	Plain                *bool   `yaml:"plain,omitempty" json:"plain,omitempty" jsonschema:"nullable"`
+	TimeZone             *string `yaml:"timezone,omitempty" json:"timezone,omitempty" jsonschema:"nullable"`
+	NestedVirtualization *bool   `yaml:"nestedVirtualization,omitempty" json:"nestedVirtualization,omitempty" jsonschema:"nullable"`
+	User                 User    `yaml:"user,omitempty" json:"user,omitempty"`
 }
 
 type BaseTemplates []LocatorWithDigest
@@ -108,6 +112,7 @@ type User struct {
 
 type VMOpts struct {
 	QEMU QEMUOpts `yaml:"qemu,omitempty" json:"qemu,omitempty"`
+	VZ   VZOpts   `yaml:"vz,omitempty" json:"vz,omitempty"`
 }
 
 type QEMUOpts struct {
@@ -115,6 +120,10 @@ type QEMUOpts struct {
 	CPUType        CPUType `yaml:"cpuType,omitempty" json:"cpuType,omitempty" jsonschema:"nullable"`
 }
 
+type VZOpts struct {
+	Rosetta Rosetta `yaml:"rosetta,omitempty" json:"rosetta,omitempty"`
+}
+
 type Rosetta struct {
 	Enabled *bool `yaml:"enabled,omitempty" json:"enabled,omitempty" jsonschema:"nullable"`
 	BinFmt  *bool `yaml:"binfmt,omitempty" json:"binfmt,omitempty" jsonschema:"nullable"`
@@ -319,3 +328,70 @@ type CACertificates struct {
 	Files          []string `yaml:"files,omitempty" json:"files,omitempty" jsonschema:"nullable"`
 	Certs          []string `yaml:"certs,omitempty" json:"certs,omitempty" jsonschema:"nullable"`
 }
+
+type PreConfiguredDriverPayload struct {
+	Config   LimaYAML `json:"config"`
+	FilePath string   `json:"filePath"`
+}
+
+func NewOS(osname string) OS {
+	switch osname {
+	case "linux":
+		return LINUX
+	default:
+		logrus.Warnf("Unknown os: %s", osname)
+		return osname
+	}
+}
+
+func Goarm() int {
+	if runtime.GOOS != "linux" {
+		return 0
+	}
+	if runtime.GOARCH != "arm" {
+		return 0
+	}
+	if cpu.ARM.HasVFPv3 {
+		return 7
+	}
+	if cpu.ARM.HasVFP {
+		return 6
+	}
+	return 5 // default
+}
+
+func NewArch(arch string) Arch {
+	switch arch {
+	case "amd64":
+		return X8664
+	case "arm64":
+		return AARCH64
+	case "arm":
+		arm := Goarm()
+		if arm == 7 {
+			return ARMV7L
+		}
+		logrus.Warnf("Unknown arm: %d", arm)
+		return arch
+	case "ppc64le":
+		return PPC64LE
+	case "riscv64":
+		return RISCV64
+	case "s390x":
+		return S390X
+	default:
+		logrus.Warnf("Unknown arch: %s", arch)
+		return arch
+	}
+}
+
+func DefaultDriver() VMType {
+	switch runtime.GOOS {
+	case "darwin":
+		return VZ
+	case "windows":
+		return WSL2
+	default:
+		return QEMU
+	}
+}
diff --git a/pkg/limayaml/defaults.go b/pkg/limayaml/defaults.go
index 8a973128..104a85b1 100644
--- a/pkg/limayaml/defaults.go
+++ b/pkg/limayaml/defaults.go
@@ -22,24 +22,22 @@ import (
 	"sync"
 	"text/template"
 
-	"github.com/coreos/go-semver/semver"
 	"github.com/docker/go-units"
 	"github.com/goccy/go-yaml"
 	"github.com/pbnjay/memory"
 	"github.com/sirupsen/logrus"
-	"golang.org/x/sys/cpu"
 
 	"github.com/lima-vm/lima/v2/pkg/instance/hostname"
 	"github.com/lima-vm/lima/v2/pkg/ioutilx"
+	"github.com/lima-vm/lima/v2/pkg/limatype"
+	"github.com/lima-vm/lima/v2/pkg/limatype/dirnames"
+	"github.com/lima-vm/lima/v2/pkg/limatype/filenames"
 	"github.com/lima-vm/lima/v2/pkg/localpathutil"
 	. "github.com/lima-vm/lima/v2/pkg/must"
 	"github.com/lima-vm/lima/v2/pkg/networks"
 	"github.com/lima-vm/lima/v2/pkg/osutil"
 	"github.com/lima-vm/lima/v2/pkg/ptr"
-	"github.com/lima-vm/lima/v2/pkg/store/dirnames"
-	"github.com/lima-vm/lima/v2/pkg/store/filenames"
 	"github.com/lima-vm/lima/v2/pkg/version"
-	"github.com/lima-vm/lima/v2/pkg/version/versionutil"
 )
 
 const (
@@ -65,10 +63,10 @@ var (
 var defaultContainerdYAML []byte
 
 type ContainerdYAML struct {
-	Archives []File
+	Archives []limatype.File
 }
 
-func defaultContainerdArchives() []File {
+func defaultContainerdArchives() []limatype.File {
 	var containerd ContainerdYAML
 	err := yaml.UnmarshalWithOptions(defaultContainerdYAML, &containerd, yaml.Strict())
 	if err != nil {
@@ -78,8 +76,8 @@ func defaultContainerdArchives() []File {
 }
 
 // FirstUsernetIndex gets the index of first usernet network under l.Network[]. Returns -1 if no usernet network found.
-func FirstUsernetIndex(l *LimaYAML) int {
-	return slices.IndexFunc(l.Networks, func(network Network) bool { return networks.IsUsernet(network.Lima) })
+func FirstUsernetIndex(l *limatype.LimaYAML) int {
+	return slices.IndexFunc(l.Networks, func(network limatype.Network) bool { return networks.IsUsernet(network.Lima) })
 }
 
 func MACAddress(uniqueID string) string {
@@ -138,21 +136,10 @@ func defaultGuestInstallPrefix() string {
 //   - Networks are appended in d, y, o order
 //   - DNS are picked from the highest priority where DNS is not empty.
 //   - CACertificates Files and Certs are uniquely appended in d, y, o order
-func FillDefault(ctx context.Context, y, d, o *LimaYAML, filePath string, warn bool) {
+func FillDefault(ctx context.Context, y, d, o *limatype.LimaYAML, filePath string, warn bool) {
 	instDir := filepath.Dir(filePath)
 
-	// existingLimaVersion can be empty if the instance was created with Lima prior to v0.20,
-	var existingLimaVersion string
-	if !isExistingInstanceDir(instDir) {
-		existingLimaVersion = version.Version
-	} else {
-		limaVersionFile := filepath.Join(instDir, filenames.LimaVersion)
-		if b, err := os.ReadFile(limaVersionFile); err == nil {
-			existingLimaVersion = strings.TrimSpace(string(b))
-		} else if !errors.Is(err, os.ErrNotExist) {
-			logrus.WithError(err).Warnf("Failed to read %q", limaVersionFile)
-		}
-	}
+	existingLimaVersion := ExistingLimaVersion(instDir)
 
 	if y.User.Name == nil {
 		y.User.Name = d.User.Name
@@ -222,7 +209,7 @@ func FillDefault(ctx context.Context, y, d, o *LimaYAML, filePath string, warn b
 	if o.VMType != nil {
 		y.VMType = o.VMType
 	}
-	y.VMType = ptr.Of(ResolveVMType(y, d, o, filePath))
+
 	if y.OS == nil {
 		y.OS = d.OS
 	}
@@ -252,27 +239,6 @@ func FillDefault(ctx context.Context, y, d, o *LimaYAML, filePath string, warn b
 		}
 	}
 
-	if y.VMOpts.QEMU.CPUType == nil {
-		y.VMOpts.QEMU.CPUType = CPUType{}
-	}
-	// TODO: This check should be removed when we completely eliminate `CPUType` from limayaml.
-	if len(y.CPUType) > 0 {
-		if warn {
-			logrus.Warn("The top-level `cpuType` field is deprecated and will be removed in a future release. Please migrate to `vmOpts.qemu.cpuType`.")
-		}
-		for arch, v := range y.CPUType {
-			if v == "" {
-				continue
-			}
-			if existing, ok := y.VMOpts.QEMU.CPUType[arch]; ok && existing != "" && existing != v {
-				logrus.Warnf("Conflicting cpuType for arch %q: top-level=%q, vmOpts.qemu=%q; using vmOpts.qemu value", arch, v, existing)
-				continue
-			}
-			y.VMOpts.QEMU.CPUType[arch] = v
-		}
-		y.CPUType = nil
-	}
-
 	if y.CPUs == nil {
 		y.CPUs = d.CPUs
 	}
@@ -331,9 +297,6 @@ func FillDefault(ctx context.Context, y, d, o *LimaYAML, filePath string, warn b
 	if o.Video.VNC.Display != nil {
 		y.Video.VNC.Display = o.Video.VNC.Display
 	}
-	if (y.Video.VNC.Display == nil || *y.Video.VNC.Display == "") && *y.VMType == QEMU {
-		y.Video.VNC.Display = ptr.Of("127.0.0.1:0,to=9")
-	}
 
 	if y.Firmware.LegacyBIOS == nil {
 		y.Firmware.LegacyBIOS = d.Firmware.LegacyBIOS
@@ -424,12 +387,12 @@ func FillDefault(ctx context.Context, y, d, o *LimaYAML, filePath string, warn b
 	for i := range y.Provision {
 		provision := &y.Provision[i]
 		if provision.Mode == "" {
-			provision.Mode = ProvisionModeSystem
+			provision.Mode = limatype.ProvisionModeSystem
 		}
-		if provision.Mode == ProvisionModeDependency && provision.SkipDefaultDependencyResolution == nil {
+		if provision.Mode == limatype.ProvisionModeDependency && provision.SkipDefaultDependencyResolution == nil {
 			provision.SkipDefaultDependencyResolution = ptr.Of(false)
 		}
-		if provision.Mode == ProvisionModeData {
+		if provision.Mode == limatype.ProvisionModeData {
 			if provision.Content == nil {
 				provision.Content = ptr.Of("")
 			} else {
@@ -510,7 +473,7 @@ func FillDefault(ctx context.Context, y, d, o *LimaYAML, filePath string, warn b
 	}
 	if y.Containerd.User == nil {
 		switch *y.Arch {
-		case X8664, AARCH64:
+		case limatype.X8664, limatype.AARCH64:
 			y.Containerd.User = ptr.Of(true)
 		default:
 			y.Containerd.User = ptr.Of(false)
@@ -532,7 +495,7 @@ func FillDefault(ctx context.Context, y, d, o *LimaYAML, filePath string, warn b
 	for i := range y.Probes {
 		probe := &y.Probes[i]
 		if probe.Mode == "" {
-			probe.Mode = ProbeModeReadiness
+			probe.Mode = limatype.ProbeModeReadiness
 		}
 		if probe.Description == "" {
 			probe.Description = fmt.Sprintf("user probe %d/%d", i+1, len(y.Probes))
@@ -585,7 +548,7 @@ func FillDefault(ctx context.Context, y, d, o *LimaYAML, filePath string, warn b
 		y.PropagateProxyEnv = ptr.Of(true)
 	}
 
-	networks := make([]Network, 0, len(d.Networks)+len(y.Networks)+len(o.Networks))
+	networks := make([]limatype.Network, 0, len(d.Networks)+len(y.Networks)+len(o.Networks))
 	iface := make(map[string]int)
 	for _, nw := range slices.Concat(d.Networks, y.Networks, o.Networks) {
 		if i, ok := iface[nw.Interface]; ok {
@@ -632,15 +595,6 @@ func FillDefault(ctx context.Context, y, d, o *LimaYAML, filePath string, warn b
 	}
 
 	y.MountTypesUnsupported = slices.Concat(o.MountTypesUnsupported, y.MountTypesUnsupported, d.MountTypesUnsupported)
-	mountTypesUnsupported := make(map[string]struct{})
-	for _, f := range y.MountTypesUnsupported {
-		mountTypesUnsupported[f] = struct{}{}
-	}
-
-	if runtime.GOOS == "windows" {
-		// QEMU for Windows does not support 9p
-		mountTypesUnsupported[NINEP] = struct{}{}
-	}
 
 	// MountType has to be resolved before resolving Mounts
 	if y.MountType == nil {
@@ -649,28 +603,6 @@ func FillDefault(ctx context.Context, y, d, o *LimaYAML, filePath string, warn b
 	if o.MountType != nil {
 		y.MountType = o.MountType
 	}
-	if y.MountType == nil || *y.MountType == "" || *y.MountType == "default" {
-		switch *y.VMType {
-		case VZ:
-			y.MountType = ptr.Of(VIRTIOFS)
-		case QEMU:
-			y.MountType = ptr.Of(NINEP)
-			if _, ok := mountTypesUnsupported[NINEP]; ok {
-				// Use REVSSHFS if the instance does not support 9p
-				y.MountType = ptr.Of(REVSSHFS)
-			} else if isExistingInstanceDir(instDir) && !versionutil.GreaterEqual(existingLimaVersion, "1.0.0") {
-				// Use REVSSHFS if the instance was created with Lima prior to v1.0
-				y.MountType = ptr.Of(REVSSHFS)
-			}
-		default:
-			y.MountType = ptr.Of(REVSSHFS)
-		}
-	}
-
-	if _, ok := mountTypesUnsupported[*y.MountType]; ok {
-		// We cannot return an error here, but Validate() will return it.
-		logrus.Warnf("Unsupported mount type: %q", *y.MountType)
-	}
 
 	if y.MountInotify == nil {
 		y.MountInotify = d.MountInotify
@@ -684,7 +616,7 @@ func FillDefault(ctx context.Context, y, d, o *LimaYAML, filePath string, warn b
 
 	// Combine all mounts; highest priority entry determines writable status.
 	// Only works for exact matches; does not normalize case or resolve symlinks.
-	mounts := make([]Mount, 0, len(d.Mounts)+len(y.Mounts)+len(o.Mounts))
+	mounts := make([]limatype.Mount, 0, len(d.Mounts)+len(y.Mounts)+len(o.Mounts))
 	location := make(map[string]int)
 	for _, mount := range slices.Concat(d.Mounts, y.Mounts, o.Mounts) {
 		if out, err := executeHostTemplate(mount.Location, instDir, y.Param); err == nil {
@@ -757,9 +689,6 @@ func FillDefault(ctx context.Context, y, d, o *LimaYAML, filePath string, warn b
 		if mount.NineP.Msize == nil {
 			mounts[i].NineP.Msize = ptr.Of(Default9pMsize)
 		}
-		if mount.Virtiofs.QueueSize == nil && *y.VMType == QEMU && *y.MountType == VIRTIOFS {
-			mounts[i].Virtiofs.QueueSize = ptr.Of(DefaultVirtiofsQueueSize)
-		}
 		if mount.Writable == nil {
 			mount.Writable = ptr.Of(false)
 		}
@@ -821,28 +750,20 @@ func FillDefault(ctx context.Context, y, d, o *LimaYAML, filePath string, warn b
 	y.CACertificates.Files = unique(slices.Concat(d.CACertificates.Files, y.CACertificates.Files, o.CACertificates.Files))
 	y.CACertificates.Certs = unique(slices.Concat(d.CACertificates.Certs, y.CACertificates.Certs, o.CACertificates.Certs))
 
-	if runtime.GOOS == "darwin" && IsNativeArch(AARCH64) {
-		if y.Rosetta.Enabled == nil {
-			y.Rosetta.Enabled = d.Rosetta.Enabled
-		}
-		if o.Rosetta.Enabled != nil {
-			y.Rosetta.Enabled = o.Rosetta.Enabled
+	if runtime.GOOS == "darwin" && IsNativeArch(limatype.AARCH64) {
+		if y.VMOpts.VZ.Rosetta.Enabled == nil {
+			y.VMOpts.VZ.Rosetta.Enabled = d.VMOpts.VZ.Rosetta.Enabled
 		}
-		if y.Rosetta.Enabled == nil {
-			y.Rosetta.Enabled = ptr.Of(false)
+		if o.VMOpts.VZ.Rosetta.Enabled != nil {
+			y.VMOpts.VZ.Rosetta.Enabled = o.VMOpts.VZ.Rosetta.Enabled
 		}
-	} else {
-		y.Rosetta.Enabled = ptr.Of(false)
 	}
 
-	if y.Rosetta.BinFmt == nil {
-		y.Rosetta.BinFmt = d.Rosetta.BinFmt
-	}
-	if o.Rosetta.BinFmt != nil {
-		y.Rosetta.BinFmt = o.Rosetta.BinFmt
+	if y.VMOpts.VZ.Rosetta.BinFmt == nil {
+		y.VMOpts.VZ.Rosetta.BinFmt = d.VMOpts.VZ.Rosetta.BinFmt
 	}
-	if y.Rosetta.BinFmt == nil {
-		y.Rosetta.BinFmt = ptr.Of(false)
+	if o.VMOpts.VZ.Rosetta.BinFmt != nil {
+		y.VMOpts.VZ.Rosetta.BinFmt = o.VMOpts.VZ.Rosetta.BinFmt
 	}
 
 	if y.NestedVirtualization == nil {
@@ -868,7 +789,23 @@ func FillDefault(ctx context.Context, y, d, o *LimaYAML, filePath string, warn b
 	fixUpForPlainMode(y)
 }
 
-func fixUpForPlainMode(y *LimaYAML) {
+// ExistingLimaVersion returns empty if the instance was created with Lima prior to v0.20.
+func ExistingLimaVersion(instDir string) string {
+	if !IsExistingInstanceDir(instDir) {
+		return version.Version
+	}
+
+	limaVersionFile := filepath.Join(instDir, filenames.LimaVersion)
+	if b, err := os.ReadFile(limaVersionFile); err == nil {
+		return strings.TrimSpace(string(b))
+	} else if !errors.Is(err, os.ErrNotExist) {
+		logrus.WithError(err).Warnf("Failed to read %q", limaVersionFile)
+	}
+
+	return version.Version
+}
+
+func fixUpForPlainMode(y *limatype.LimaYAML) {
 	if !*y.Plain {
 		return
 	}
@@ -876,12 +813,12 @@ func fixUpForPlainMode(y *LimaYAML) {
 	y.PortForwards = nil
 	y.Containerd.System = ptr.Of(false)
 	y.Containerd.User = ptr.Of(false)
-	y.Rosetta.BinFmt = ptr.Of(false)
-	y.Rosetta.Enabled = ptr.Of(false)
+	y.VMOpts.VZ.Rosetta.BinFmt = ptr.Of(false)
+	y.VMOpts.VZ.Rosetta.Enabled = ptr.Of(false)
 	y.TimeZone = ptr.Of("")
 }
 
-func executeGuestTemplate(format, instDir string, user User, param map[string]string) (bytes.Buffer, error) {
+func executeGuestTemplate(format, instDir string, user limatype.User, param map[string]string) (bytes.Buffer, error) {
 	tmpl, err := template.New("").Parse(format)
 	if err == nil {
 		name := filepath.Base(instDir)
@@ -933,9 +870,9 @@ func executeHostTemplate(format, instDir string, param map[string]string) (bytes
 	return bytes.Buffer{}, err
 }
 
-func FillPortForwardDefaults(rule *PortForward, instDir string, user User, param map[string]string) {
+func FillPortForwardDefaults(rule *limatype.PortForward, instDir string, user limatype.User, param map[string]string) {
 	if rule.Proto == "" {
-		rule.Proto = ProtoTCP
+		rule.Proto = limatype.ProtoTCP
 	}
 	if rule.GuestIP == nil {
 		if rule.GuestIPMustBeZero {
@@ -983,7 +920,7 @@ func FillPortForwardDefaults(rule *PortForward, instDir string, user User, param
 	}
 }
 
-func FillCopyToHostDefaults(rule *CopyToHost, instDir string, user User, param map[string]string) {
+func FillCopyToHostDefaults(rule *limatype.CopyToHost, instDir string, user limatype.User, param map[string]string) {
 	if rule.GuestFile != "" {
 		if out, err := executeGuestTemplate(rule.GuestFile, instDir, user, param); err == nil {
 			rule.GuestFile = out.String()
@@ -1000,72 +937,7 @@ func FillCopyToHostDefaults(rule *CopyToHost, instDir string, user User, param m
 	}
 }
 
-func NewOS(osname string) OS {
-	switch osname {
-	case "linux":
-		return LINUX
-	default:
-		logrus.Warnf("Unknown os: %s", osname)
-		return osname
-	}
-}
-
-func goarm() int {
-	if runtime.GOOS != "linux" {
-		return 0
-	}
-	if runtime.GOARCH != "arm" {
-		return 0
-	}
-	if cpu.ARM.HasVFPv3 {
-		return 7
-	}
-	if cpu.ARM.HasVFP {
-		return 6
-	}
-	return 5 // default
-}
-
-func NewArch(arch string) Arch {
-	switch arch {
-	case "amd64":
-		return X8664
-	case "arm64":
-		return AARCH64
-	case "arm":
-		arm := goarm()
-		if arm == 7 {
-			return ARMV7L
-		}
-		logrus.Warnf("Unknown arm: %d", arm)
-		return arch
-	case "ppc64le":
-		return PPC64LE
-	case "riscv64":
-		return RISCV64
-	case "s390x":
-		return S390X
-	default:
-		logrus.Warnf("Unknown arch: %s", arch)
-		return arch
-	}
-}
-
-func NewVMType(driver string) VMType {
-	switch driver {
-	case "vz":
-		return VZ
-	case "qemu":
-		return QEMU
-	case "wsl2":
-		return WSL2
-	default:
-		logrus.Warnf("Unknown driver: %s", driver)
-		return driver
-	}
-}
-
-func isExistingInstanceDir(dir string) bool {
+func IsExistingInstanceDir(dir string) bool {
 	// existence of "lima.yaml" does not signify existence of the instance,
 	// because the file is created during the initialization of the instance.
 	for _, f := range []string{
@@ -1080,97 +952,16 @@ func isExistingInstanceDir(dir string) bool {
 	return false
 }
 
-func ResolveVMType(y, d, o *LimaYAML, filePath string) VMType {
-	// Check if the VMType is explicitly specified
-	for i, f := range []*LimaYAML{o, y, d} {
-		if f.VMType != nil && *f.VMType != "" && *f.VMType != "default" {
-			logrus.Debugf("ResolveVMType: resolved VMType %q (explicitly specified in []*LimaYAML{o,y,d}[%d])", *f.VMType, i)
-			return NewVMType(*f.VMType)
-		}
-	}
-
-	// If this is an existing instance, guess the VMType from the contents of the instance directory.
-	if dir, basename := filepath.Split(filePath); dir != "" && basename == filenames.LimaYAML && isExistingInstanceDir(dir) {
-		if runtime.GOOS == "darwin" {
-			vzIdentifier := filepath.Join(dir, filenames.VzIdentifier) // since Lima v0.14
-			if _, err := os.Lstat(vzIdentifier); !errors.Is(err, os.ErrNotExist) {
-				logrus.Debugf("ResolveVMType: resolved VMType %q (existing instance, with %q)", VZ, vzIdentifier)
-				return VZ
-			}
-			logrus.Debugf("ResolveVMType: resolved VMType %q (existing instance, without %q)", QEMU, vzIdentifier)
-			return QEMU
-		}
-		logrus.Debugf("ResolveVMType: resolved VMType %q (existing instance)", QEMU)
-		return QEMU
-	}
-
-	// Resolve the best type, depending on GOOS
-	switch runtime.GOOS {
-	case "darwin":
-		macOSProductVersion, err := osutil.ProductVersion()
-		if err != nil {
-			logrus.WithError(err).Warn("Failed to get macOS product version")
-			logrus.Debugf("ResolveVMType: resolved VMType %q (default for unknown version of macOS)", QEMU)
-			return QEMU
-		}
-		// Virtualization.framework in macOS prior to 13.5 could not boot Linux kernel v6.2 on Intel
-		// https://github.com/lima-vm/lima/issues/1577
-		if macOSProductVersion.LessThan(*semver.New("13.5.0")) {
-			logrus.Debugf("ResolveVMType: resolved VMType %q (default for macOS prior to 13.5)", QEMU)
-			return QEMU
-		}
-		// Use QEMU if the config depends on QEMU
-		for i, f := range []*LimaYAML{o, y, d} {
-			if f.Arch != nil && !IsNativeArch(*f.Arch) {
-				logrus.Debugf("ResolveVMType: resolved VMType %q (non-native arch=%q is specified in []*LimaYAML{o,y,d}[%d])", QEMU, *f.Arch, i)
-				return QEMU
-			}
-			if ResolveArch(f.Arch) == X8664 && f.Firmware.LegacyBIOS != nil && *f.Firmware.LegacyBIOS {
-				logrus.Debugf("ResolveVMType: resolved VMType %q (firmware.legacyBIOS is specified in []*LimaYAML{o,y,d}[%d], on x86_64)", QEMU, i)
-				return QEMU
-			}
-			if f.MountType != nil && *f.MountType == NINEP {
-				logrus.Debugf("ResolveVMType: resolved VMType %q (mountType=%q is specified in []*LimaYAML{o,y,d}[%d])", QEMU, NINEP, i)
-				return QEMU
-			}
-			if f.Audio.Device != nil {
-				switch *f.Audio.Device {
-				case "", "none", "default", "vz":
-					// NOP
-				default:
-					logrus.Debugf("ResolveVMType: resolved VMType %q (audio.device=%q is specified in []*LimaYAML{o,y,d}[%d])", QEMU, *f.Audio.Device, i)
-					return QEMU
-				}
-			}
-			if f.Video.Display != nil {
-				switch *f.Video.Display {
-				case "", "none", "default", "vz":
-					// NOP
-				default:
-					logrus.Debugf("ResolveVMType: resolved VMType %q (video.display=%q is specified in []*LimaYAML{o,y,d}[%d])", QEMU, *f.Video.Display, i)
-					return QEMU
-				}
-			}
-		}
-		// Use VZ if the config is compatible with VZ
-		logrus.Debugf("ResolveVMType: resolved VMType %q (default for macOS 13.5 and later)", VZ)
-		return VZ
-	default:
-		logrus.Debugf("ResolveVMType: resolved VMType %q (default for GOOS=%q)", QEMU, runtime.GOOS)
-		return QEMU
-	}
-}
-
-func ResolveOS(s *string) OS {
+func ResolveOS(s *string) limatype.OS {
 	if s == nil || *s == "" || *s == "default" {
-		return NewOS("linux")
+		return limatype.NewOS("linux")
 	}
 	return *s
 }
 
-func ResolveArch(s *string) Arch {
+func ResolveArch(s *string) limatype.Arch {
 	if s == nil || *s == "" || *s == "default" {
-		return NewArch(runtime.GOARCH)
+		return limatype.NewArch(runtime.GOARCH)
 	}
 	return *s
 }
@@ -1225,13 +1016,13 @@ func HasHostCPU() bool {
 	return false
 }
 
-func IsNativeArch(arch Arch) bool {
-	nativeX8664 := arch == X8664 && runtime.GOARCH == "amd64"
-	nativeAARCH64 := arch == AARCH64 && runtime.GOARCH == "arm64"
-	nativeARMV7L := arch == ARMV7L && runtime.GOARCH == "arm" && goarm() == 7
-	nativePPC64LE := arch == PPC64LE && runtime.GOARCH == "ppc64le"
-	nativeRISCV64 := arch == RISCV64 && runtime.GOARCH == "riscv64"
-	nativeS390X := arch == S390X && runtime.GOARCH == "s390x"
+func IsNativeArch(arch limatype.Arch) bool {
+	nativeX8664 := arch == limatype.X8664 && runtime.GOARCH == "amd64"
+	nativeAARCH64 := arch == limatype.AARCH64 && runtime.GOARCH == "arm64"
+	nativeARMV7L := arch == limatype.ARMV7L && runtime.GOARCH == "arm" && limatype.Goarm() == 7
+	nativePPC64LE := arch == limatype.PPC64LE && runtime.GOARCH == "ppc64le"
+	nativeRISCV64 := arch == limatype.RISCV64 && runtime.GOARCH == "riscv64"
+	nativeS390X := arch == limatype.S390X && runtime.GOARCH == "s390x"
 	return nativeX8664 || nativeAARCH64 || nativeARMV7L || nativePPC64LE || nativeRISCV64 || nativeS390X
 }
 
diff --git a/pkg/limayaml/load.go b/pkg/limayaml/load.go
index 6436d585..78ecce98 100644
--- a/pkg/limayaml/load.go
+++ b/pkg/limayaml/load.go
@@ -12,26 +12,27 @@ import (
 
 	"github.com/sirupsen/logrus"
 
-	"github.com/lima-vm/lima/v2/pkg/store/dirnames"
-	"github.com/lima-vm/lima/v2/pkg/store/filenames"
+	"github.com/lima-vm/lima/v2/pkg/limatype"
+	"github.com/lima-vm/lima/v2/pkg/limatype/dirnames"
+	"github.com/lima-vm/lima/v2/pkg/limatype/filenames"
 )
 
 // Load loads the yaml and fulfills unspecified fields with the default values.
 //
 // Load does not validate. Use Validate for validation.
-func Load(ctx context.Context, b []byte, filePath string) (*LimaYAML, error) {
+func Load(ctx context.Context, b []byte, filePath string) (*limatype.LimaYAML, error) {
 	return load(ctx, b, filePath, false)
 }
 
 // LoadWithWarnings will call FillDefaults with warnings enabled (e.g. when
 // the username is not valid on Linux and must be replaced by "Lima").
 // It is called when creating or editing an instance.
-func LoadWithWarnings(ctx context.Context, b []byte, filePath string) (*LimaYAML, error) {
+func LoadWithWarnings(ctx context.Context, b []byte, filePath string) (*limatype.LimaYAML, error) {
 	return load(ctx, b, filePath, true)
 }
 
-func load(ctx context.Context, b []byte, filePath string, warn bool) (*LimaYAML, error) {
-	var y, d, o LimaYAML
+func load(ctx context.Context, b []byte, filePath string, warn bool) (*limatype.LimaYAML, error) {
+	var y, d, o limatype.LimaYAML
 
 	if err := Unmarshal(b, &y, fmt.Sprintf("main file %q", filePath)); err != nil {
 		return nil, err
@@ -69,5 +70,6 @@ func load(ctx context.Context, b []byte, filePath string, warn bool) (*LimaYAML,
 	}
 
 	FillDefault(ctx, &y, &d, &o, filePath, warn)
+
 	return &y, nil
 }
diff --git a/pkg/limayaml/marshal.go b/pkg/limayaml/marshal.go
index efa85530..a43821f3 100644
--- a/pkg/limayaml/marshal.go
+++ b/pkg/limayaml/marshal.go
@@ -9,6 +9,7 @@ import (
 	"github.com/goccy/go-yaml"
 	"github.com/sirupsen/logrus"
 
+	"github.com/lima-vm/lima/v2/pkg/limatype"
 	"github.com/lima-vm/lima/v2/pkg/yqutil"
 )
 
@@ -18,7 +19,7 @@ const (
 )
 
 // Marshal the struct as a YAML document, optionally as a stream.
-func Marshal(y *LimaYAML, stream bool) ([]byte, error) {
+func Marshal(y *limatype.LimaYAML, stream bool) ([]byte, error) {
 	b, err := yaml.Marshal(y)
 	if err != nil {
 		return nil, err
@@ -30,40 +31,40 @@ func Marshal(y *LimaYAML, stream bool) ([]byte, error) {
 	return b, nil
 }
 
-func unmarshalDisk(dst *Disk, b []byte) error {
+func unmarshalDisk(dst *limatype.Disk, b []byte) error {
 	var s string
 	if err := yaml.Unmarshal(b, &s); err == nil {
-		*dst = Disk{Name: s}
+		*dst = limatype.Disk{Name: s}
 		return nil
 	}
 	return yaml.Unmarshal(b, dst)
 }
 
 // unmarshalBaseTemplates unmarshalls `base` which is either a string or a list of Locators.
-func unmarshalBaseTemplates(dst *BaseTemplates, b []byte) error {
+func unmarshalBaseTemplates(dst *limatype.BaseTemplates, b []byte) error {
 	var s string
 	if err := yaml.Unmarshal(b, &s); err == nil {
-		*dst = BaseTemplates{LocatorWithDigest{URL: s}}
+		*dst = limatype.BaseTemplates{limatype.LocatorWithDigest{URL: s}}
 		return nil
 	}
-	return yaml.UnmarshalWithOptions(b, dst, yaml.CustomUnmarshaler[LocatorWithDigest](unmarshalLocatorWithDigest))
+	return yaml.UnmarshalWithOptions(b, dst, yaml.CustomUnmarshaler[limatype.LocatorWithDigest](unmarshalLocatorWithDigest))
 }
 
 // unmarshalLocator unmarshalls a locator which is either a string or a Locator struct.
-func unmarshalLocatorWithDigest(dst *LocatorWithDigest, b []byte) error {
+func unmarshalLocatorWithDigest(dst *limatype.LocatorWithDigest, b []byte) error {
 	var s string
 	if err := yaml.Unmarshal(b, &s); err == nil {
-		*dst = LocatorWithDigest{URL: s}
+		*dst = limatype.LocatorWithDigest{URL: s}
 		return nil
 	}
 	return yaml.Unmarshal(b, dst)
 }
 
-func Unmarshal(data []byte, y *LimaYAML, comment string) error {
+func Unmarshal(data []byte, y *limatype.LimaYAML, comment string) error {
 	opts := []yaml.DecodeOption{
-		yaml.CustomUnmarshaler[BaseTemplates](unmarshalBaseTemplates),
-		yaml.CustomUnmarshaler[Disk](unmarshalDisk),
-		yaml.CustomUnmarshaler[LocatorWithDigest](unmarshalLocatorWithDigest),
+		yaml.CustomUnmarshaler[limatype.BaseTemplates](unmarshalBaseTemplates),
+		yaml.CustomUnmarshaler[limatype.Disk](unmarshalDisk),
+		yaml.CustomUnmarshaler[limatype.LocatorWithDigest](unmarshalLocatorWithDigest),
 	}
 	if err := yaml.UnmarshalWithOptions(data, y, opts...); err != nil {
 		return fmt.Errorf("failed to unmarshal YAML (%s): %w", comment, err)
@@ -75,7 +76,7 @@ func Unmarshal(data []byte, y *LimaYAML, comment string) error {
 	}
 	// Finally log a warning if the YAML file violates the "strict" rules
 	opts = append(opts, yaml.Strict())
-	var ignore LimaYAML
+	var ignore limatype.LimaYAML
 	if err := yaml.UnmarshalWithOptions(data, &ignore, opts...); err != nil {
 		logrus.WithField("comment", comment).WithError(err).Warn("Non-strict YAML detected; please check for typos")
 	}
diff --git a/pkg/limayaml/validate.go b/pkg/limayaml/validate.go
index 2aa750d4..ab67baa1 100644
--- a/pkg/limayaml/validate.go
+++ b/pkg/limayaml/validate.go
@@ -18,11 +18,12 @@ import (
 	"strings"
 	"unicode"
 
-	"github.com/coreos/go-semver/semver"
 	"github.com/docker/go-units"
 	"github.com/sirupsen/logrus"
 
+	"github.com/lima-vm/lima/v2/pkg/driverutil"
 	"github.com/lima-vm/lima/v2/pkg/identifiers"
+	"github.com/lima-vm/lima/v2/pkg/limatype"
 	"github.com/lima-vm/lima/v2/pkg/localpathutil"
 	"github.com/lima-vm/lima/v2/pkg/networks"
 	"github.com/lima-vm/lima/v2/pkg/osutil"
@@ -30,7 +31,7 @@ import (
 	"github.com/lima-vm/lima/v2/pkg/version/versionutil"
 )
 
-func Validate(y *LimaYAML, warn bool) error {
+func Validate(y *limatype.LimaYAML, warn bool) error {
 	var errs error
 
 	if len(y.Base) > 0 {
@@ -47,30 +48,14 @@ func Validate(y *LimaYAML, warn bool) error {
 			errs = errors.Join(errs, fmt.Errorf("template requires Lima version %q; this is only %q", *y.MinimumLimaVersion, version.Version))
 		}
 	}
-	if y.VMOpts.QEMU.MinimumVersion != nil {
-		if _, err := semver.NewVersion(*y.VMOpts.QEMU.MinimumVersion); err != nil {
-			errs = errors.Join(errs, fmt.Errorf("field `vmOpts.qemu.minimumVersion` must be a semvar value, got %q: %w", *y.VMOpts.QEMU.MinimumVersion, err))
-		}
-	}
+
 	switch *y.OS {
-	case LINUX:
+	case limatype.LINUX:
 	default:
-		errs = errors.Join(errs, fmt.Errorf("field `os` must be %q; got %q", LINUX, *y.OS))
+		errs = errors.Join(errs, fmt.Errorf("field `os` must be %q; got %q", limatype.LINUX, *y.OS))
 	}
-	if !slices.Contains(ArchTypes, *y.Arch) {
-		errs = errors.Join(errs, fmt.Errorf("field `arch` must be one of %v; got %q", ArchTypes, *y.Arch))
-	}
-	switch *y.VMType {
-	case QEMU:
-		// NOP
-	case WSL2:
-		// NOP
-	case VZ:
-		if !IsNativeArch(*y.Arch) {
-			errs = errors.Join(errs, fmt.Errorf("field `arch` must be %q for VZ; got %q", NewArch(runtime.GOARCH), *y.Arch))
-		}
-	default:
-		errs = errors.Join(errs, fmt.Errorf("field `vmType` must be %q, %q, %q; got %q", QEMU, VZ, WSL2, *y.VMType))
+	if !slices.Contains(limatype.ArchTypes, *y.Arch) {
+		errs = errors.Join(errs, fmt.Errorf("field `arch` must be one of %v; got %q", limatype.ArchTypes, *y.Arch))
 	}
 
 	if len(y.Images) == 0 {
@@ -164,14 +149,16 @@ func Validate(y *LimaYAML, warn bool) error {
 		}
 	}
 
-	switch *y.MountType {
-	case REVSSHFS, NINEP, VIRTIOFS, WSLMount:
-	default:
-		errs = errors.Join(errs, fmt.Errorf("field `mountType` must be %q or %q or %q, or %q, got %q", REVSSHFS, NINEP, VIRTIOFS, WSLMount, *y.MountType))
-	}
+	if y.MountType != nil {
+		switch *y.MountType {
+		case limatype.REVSSHFS, limatype.NINEP, limatype.VIRTIOFS, limatype.WSLMount:
+		default:
+			errs = errors.Join(errs, fmt.Errorf("field `mountType` must be %q or %q or %q, or %q, got %q", limatype.REVSSHFS, limatype.NINEP, limatype.VIRTIOFS, limatype.WSLMount, *y.MountType))
+		}
 
-	if slices.Contains(y.MountTypesUnsupported, *y.MountType) {
-		errs = errors.Join(errs, fmt.Errorf("field `mountType` must not be one of %v (`mountTypesUnsupported`), got %q", y.MountTypesUnsupported, *y.MountType))
+		if slices.Contains(y.MountTypesUnsupported, *y.MountType) {
+			errs = errors.Join(errs, fmt.Errorf("field `mountType` must not be one of %v (`mountTypesUnsupported`), got %q", y.MountTypesUnsupported, *y.MountType))
+		}
 	}
 
 	if warn && runtime.GOOS != "linux" {
@@ -194,55 +181,55 @@ func Validate(y *LimaYAML, warn bool) error {
 			}
 		}
 		switch p.Mode {
-		case ProvisionModeSystem, ProvisionModeUser, ProvisionModeBoot, ProvisionModeData, ProvisionModeDependency, ProvisionModeAnsible:
+		case limatype.ProvisionModeSystem, limatype.ProvisionModeUser, limatype.ProvisionModeBoot, limatype.ProvisionModeData, limatype.ProvisionModeDependency, limatype.ProvisionModeAnsible:
 		default:
 			errs = errors.Join(errs, fmt.Errorf("field `provision[%d].mode` must one of %q, %q, %q, %q, %q, or %q",
-				i, ProvisionModeSystem, ProvisionModeUser, ProvisionModeBoot, ProvisionModeData, ProvisionModeDependency, ProvisionModeAnsible))
+				i, limatype.ProvisionModeSystem, limatype.ProvisionModeUser, limatype.ProvisionModeBoot, limatype.ProvisionModeData, limatype.ProvisionModeDependency, limatype.ProvisionModeAnsible))
 		}
-		if p.Mode != ProvisionModeDependency && p.SkipDefaultDependencyResolution != nil {
+		if p.Mode != limatype.ProvisionModeDependency && p.SkipDefaultDependencyResolution != nil {
 			errs = errors.Join(errs, fmt.Errorf("field `provision[%d].mode` cannot set skipDefaultDependencyResolution, only valid on scripts of type %q",
-				i, ProvisionModeDependency))
+				i, limatype.ProvisionModeDependency))
 		}
 
 		// This can lead to fatal Panic if p.Path is nil, better to return an error here
-		if p.Mode == ProvisionModeData {
+		if p.Mode == limatype.ProvisionModeData {
 			if p.Path == nil {
-				errs = errors.Join(errs, fmt.Errorf("field `provision[%d].path` must not be empty when mode is %q", i, ProvisionModeData))
+				errs = errors.Join(errs, fmt.Errorf("field `provision[%d].path` must not be empty when mode is %q", i, limatype.ProvisionModeData))
 				return errs
 			}
 			if !path.IsAbs(*p.Path) {
 				errs = errors.Join(errs, fmt.Errorf("field `provision[%d].path` must be an absolute path", i))
 			}
 			if p.Content == nil {
-				errs = errors.Join(errs, fmt.Errorf("field `provision[%d].content` must not be empty when mode is %q", i, ProvisionModeData))
+				errs = errors.Join(errs, fmt.Errorf("field `provision[%d].content` must not be empty when mode is %q", i, limatype.ProvisionModeData))
 			}
 			// FillDefaults makes sure that p.Permissions is not nil
 			if _, err := strconv.ParseInt(*p.Permissions, 8, 64); err != nil {
 				errs = errors.Join(errs, fmt.Errorf("field `provision[%d].permissions` must be an octal number: %w", i, err))
 			}
 		} else {
-			if p.Script == "" && p.Mode != ProvisionModeAnsible {
+			if p.Script == "" && p.Mode != limatype.ProvisionModeAnsible {
 				errs = errors.Join(errs, fmt.Errorf("field `provision[%d].script` must not be empty", i))
 			}
 			if p.Content != nil {
-				errs = errors.Join(errs, fmt.Errorf("field `provision[%d].content` can only be set when mode is %q", i, ProvisionModeData))
+				errs = errors.Join(errs, fmt.Errorf("field `provision[%d].content` can only be set when mode is %q", i, limatype.ProvisionModeData))
 			}
 			if p.Overwrite != nil {
-				errs = errors.Join(errs, fmt.Errorf("field `provision[%d].overwrite` can only be set when mode is %q", i, ProvisionModeData))
+				errs = errors.Join(errs, fmt.Errorf("field `provision[%d].overwrite` can only be set when mode is %q", i, limatype.ProvisionModeData))
 			}
 			if p.Owner != nil {
-				errs = errors.Join(errs, fmt.Errorf("field `provision[%d].owner` can only be set when mode is %q", i, ProvisionModeData))
+				errs = errors.Join(errs, fmt.Errorf("field `provision[%d].owner` can only be set when mode is %q", i, limatype.ProvisionModeData))
 			}
 			if p.Path != nil {
-				errs = errors.Join(errs, fmt.Errorf("field `provision[%d].path` can only be set when mode is %q", i, ProvisionModeData))
+				errs = errors.Join(errs, fmt.Errorf("field `provision[%d].path` can only be set when mode is %q", i, limatype.ProvisionModeData))
 			}
 			if p.Permissions != nil {
-				errs = errors.Join(errs, fmt.Errorf("field `provision[%d].permissions` can only be set when mode is %q", i, ProvisionModeData))
+				errs = errors.Join(errs, fmt.Errorf("field `provision[%d].permissions` can only be set when mode is %q", i, limatype.ProvisionModeData))
 			}
 		}
 		if p.Playbook != "" {
-			if p.Mode != ProvisionModeAnsible {
-				errs = errors.Join(errs, fmt.Errorf("field `provision[%d].playbook can only be set when mode is %q", i, ProvisionModeAnsible))
+			if p.Mode != limatype.ProvisionModeAnsible {
+				errs = errors.Join(errs, fmt.Errorf("field `provision[%d].playbook can only be set when mode is %q", i, limatype.ProvisionModeAnsible))
 			}
 			if p.Script != "" {
 				errs = errors.Join(errs, fmt.Errorf("field `provision[%d].script must be empty if playbook is set", i))
@@ -251,7 +238,7 @@ func Validate(y *LimaYAML, warn bool) error {
 			if _, err := os.Stat(playbook); err != nil {
 				errs = errors.Join(errs, fmt.Errorf("field `provision[%d].playbook` refers to an inaccessible path: %q: %w", i, playbook, err))
 			}
-			logrus.Warnf("provision mode %q is deprecated, use `ansible-playbook %q` instead", ProvisionModeAnsible, playbook)
+			logrus.Warnf("provision mode %q is deprecated, use `ansible-playbook %q` instead", limatype.ProvisionModeAnsible, playbook)
 		}
 		if strings.Contains(p.Script, "LIMA_CIDATA") {
 			logrus.Warn("provisioning scripts should not reference the LIMA_CIDATA variables")
@@ -282,9 +269,9 @@ func Validate(y *LimaYAML, warn bool) error {
 			errs = errors.Join(errs, fmt.Errorf("field `probe[%d].script` must start with a '#!' line", i))
 		}
 		switch p.Mode {
-		case ProbeModeReadiness:
+		case limatype.ProbeModeReadiness:
 		default:
-			errs = errors.Join(errs, fmt.Errorf("field `probe[%d].mode` can only be %q", i, ProbeModeReadiness))
+			errs = errors.Join(errs, fmt.Errorf("field `probe[%d].mode` can only be %q", i, limatype.ProbeModeReadiness))
 		}
 	}
 	for i, rule := range y.PortForwards {
@@ -355,9 +342,9 @@ func Validate(y *LimaYAML, warn bool) error {
 				field, osutil.UnixPathMax, len(rule.HostSocket)))
 		}
 		switch rule.Proto {
-		case ProtoTCP, ProtoUDP, ProtoAny:
+		case limatype.ProtoTCP, limatype.ProtoUDP, limatype.ProtoAny:
 		default:
-			errs = errors.Join(errs, fmt.Errorf("field `%s.proto` must be %q, %q, or %q", field, ProtoTCP, ProtoUDP, ProtoAny))
+			errs = errors.Join(errs, fmt.Errorf("field `%s.proto` must be %q, %q, or %q", field, limatype.ProtoTCP, limatype.ProtoUDP, limatype.ProtoAny))
 		}
 		if rule.Reverse && rule.GuestSocket == "" {
 			errs = errors.Join(errs, fmt.Errorf("field `%s.reverse` must be %t", field, false))
@@ -412,7 +399,7 @@ func Validate(y *LimaYAML, warn bool) error {
 	return errs
 }
 
-func validateFileObject(f File, fieldName string) error {
+func validateFileObject(f limatype.File, fieldName string) error {
 	var errs error
 	if !strings.Contains(f.Location, "://") {
 		if _, err := localpathutil.Expand(f.Location); err != nil {
@@ -420,8 +407,8 @@ func validateFileObject(f File, fieldName string) error {
 		}
 		// f.Location does NOT need to be accessible, so we do NOT check os.Stat(f.Location)
 	}
-	if !slices.Contains(ArchTypes, f.Arch) {
-		errs = errors.Join(errs, fmt.Errorf("field `arch` must be one of %v; got %q", ArchTypes, f.Arch))
+	if !slices.Contains(limatype.ArchTypes, f.Arch) {
+		errs = errors.Join(errs, fmt.Errorf("field `arch` must be one of %v; got %q", limatype.ArchTypes, f.Arch))
 	}
 	if f.Digest != "" {
 		if err := f.Digest.Validate(); err != nil {
@@ -431,7 +418,7 @@ func validateFileObject(f File, fieldName string) error {
 	return errs
 }
 
-func validateNetwork(y *LimaYAML) error {
+func validateNetwork(y *limatype.LimaYAML) error {
 	var errs error
 	interfaceName := make(map[string]int)
 	for i, nw := range y.Networks {
@@ -455,28 +442,12 @@ func validateNetwork(y *LimaYAML) error {
 			if nw.Socket != "" {
 				errs = errors.Join(errs, fmt.Errorf("field `%s.lima` and field `%s.socket` are mutually exclusive", field, field))
 			}
-			if nw.VZNAT != nil && *nw.VZNAT {
-				errs = errors.Join(errs, fmt.Errorf("field `%s.lima` and field `%s.vzNAT` are mutually exclusive", field, field))
-			}
 		case nw.Socket != "":
-			if nw.VZNAT != nil && *nw.VZNAT {
-				errs = errors.Join(errs, fmt.Errorf("field `%s.socket` and field `%s.vzNAT` are mutually exclusive", field, field))
-			}
 			if fi, err := os.Stat(nw.Socket); err != nil && !errors.Is(err, os.ErrNotExist) {
 				errs = errors.Join(errs, err)
 			} else if err == nil && fi.Mode()&os.ModeSocket == 0 {
 				errs = errors.Join(errs, fmt.Errorf("field `%s.socket` %q points to a non-socket file", field, nw.Socket))
 			}
-		case nw.VZNAT != nil && *nw.VZNAT:
-			if y.VMType == nil || *y.VMType != VZ {
-				errs = errors.Join(errs, fmt.Errorf("field `%s.vzNAT` requires `vmType` to be %q", field, VZ))
-			}
-			if nw.Lima != "" {
-				errs = errors.Join(errs, fmt.Errorf("field `%s.vzNAT` and field `%s.lima` are mutually exclusive", field, field))
-			}
-			if nw.Socket != "" {
-				errs = errors.Join(errs, fmt.Errorf("field `%s.vzNAT` and field `%s.socket` are mutually exclusive", field, field))
-			}
 		default:
 			errs = errors.Join(errs, fmt.Errorf("field `%s.lima` or  field `%s.socket must be set", field, field))
 		}
@@ -510,7 +481,7 @@ func validateNetwork(y *LimaYAML) error {
 
 // validateParamIsUsed checks if the keys in the `param` field are used in any script, probe, copyToHost, or portForward.
 // It should be called before the `y` parameter is passed to FillDefault() that execute template.
-func validateParamIsUsed(y *LimaYAML) error {
+func validateParamIsUsed(y *limatype.LimaYAML) error {
 	for key := range y.Param {
 		re, err := regexp.Compile(`{{[^}]*\.Param\.` + key + `[^}]*}}|\bPARAM_` + key + `\b`)
 		if err != nil {
@@ -586,12 +557,12 @@ func validatePort(field string, port int) error {
 	return nil
 }
 
-func warnExperimental(y *LimaYAML) {
-	if *y.MountType == VIRTIOFS && runtime.GOOS == "linux" {
+func warnExperimental(y *limatype.LimaYAML) {
+	if *y.MountType == limatype.VIRTIOFS && runtime.GOOS == "linux" {
 		logrus.Warn("`mountType: virtiofs` on Linux is experimental")
 	}
 	switch *y.Arch {
-	case RISCV64, ARMV7L, S390X, PPC64LE:
+	case limatype.RISCV64, limatype.ARMV7L, limatype.S390X, limatype.PPC64LE:
 		logrus.Warnf("`arch: %s ` is experimental", *y.Arch)
 	}
 	if y.Video.Display != nil && strings.Contains(*y.Video.Display, "vnc") {
@@ -608,36 +579,40 @@ func warnExperimental(y *LimaYAML) {
 // ValidateAgainstLatestConfig validates the values between the latest YAML and the updated(New) YAML.
 // This validates configuration rules that disallow certain changes, such as shrinking the disk.
 func ValidateAgainstLatestConfig(ctx context.Context, yNew, yLatest []byte) error {
-	var n LimaYAML
+	var n limatype.LimaYAML
+	var errs error
 
 	// Load the latest YAML and fill in defaults
 	l, err := LoadWithWarnings(ctx, yLatest, "")
 	if err != nil {
-		return err
+		errs = errors.Join(errs, err)
+	}
+	if err := driverutil.ResolveVMType(ctx, l, ""); err != nil {
+		errs = errors.Join(errs, fmt.Errorf("failed to resolve vm for %q: %w", "", err))
 	}
 	if err := Unmarshal(yNew, &n, "Unmarshal new YAML bytes"); err != nil {
-		return err
+		errs = errors.Join(errs, err)
 	}
 
 	// Handle editing the template without a disk value
 	if n.Disk == nil || l.Disk == nil {
-		return nil
+		return errs
 	}
 
 	// Disk value must be provided, as it is required when creating an instance.
 	nDisk, err := units.RAMInBytes(*n.Disk)
 	if err != nil {
-		return err
+		errs = errors.Join(errs, err)
 	}
 	lDisk, err := units.RAMInBytes(*l.Disk)
 	if err != nil {
-		return err
+		errs = errors.Join(errs, err)
 	}
 
 	// Reject shrinking disk
 	if nDisk < lDisk {
-		return fmt.Errorf("field `disk`: shrinking the disk (%v --> %v) is not supported", *l.Disk, *n.Disk)
+		errs = errors.Join(errs, fmt.Errorf("field `disk`: shrinking the disk (%v --> %v) is not supported", *l.Disk, *n.Disk))
 	}
 
-	return nil
+	return errs
 }
diff --git a/pkg/networks/commands.go b/pkg/networks/commands.go
index 98dbb107..abe34650 100644
--- a/pkg/networks/commands.go
+++ b/pkg/networks/commands.go
@@ -10,8 +10,8 @@ import (
 	"os/exec"
 	"path/filepath"
 
+	"github.com/lima-vm/lima/v2/pkg/limatype/dirnames"
 	"github.com/lima-vm/lima/v2/pkg/osutil"
-	"github.com/lima-vm/lima/v2/pkg/store/dirnames"
 )
 
 const (
diff --git a/pkg/networks/config.go b/pkg/networks/config.go
index 34b5f226..6cefe46b 100644
--- a/pkg/networks/config.go
+++ b/pkg/networks/config.go
@@ -15,8 +15,8 @@ import (
 	"github.com/goccy/go-yaml"
 	"github.com/sirupsen/logrus"
 
-	"github.com/lima-vm/lima/v2/pkg/store/dirnames"
-	"github.com/lima-vm/lima/v2/pkg/store/filenames"
+	"github.com/lima-vm/lima/v2/pkg/limatype/dirnames"
+	"github.com/lima-vm/lima/v2/pkg/limatype/filenames"
 	"github.com/lima-vm/lima/v2/pkg/textutil"
 )
 
diff --git a/pkg/networks/reconcile/reconcile.go b/pkg/networks/reconcile/reconcile.go
index c3575571..02076b0b 100644
--- a/pkg/networks/reconcile/reconcile.go
+++ b/pkg/networks/reconcile/reconcile.go
@@ -16,11 +16,12 @@ import (
 
 	"github.com/sirupsen/logrus"
 
+	"github.com/lima-vm/lima/v2/pkg/limatype"
+	"github.com/lima-vm/lima/v2/pkg/limatype/dirnames"
 	"github.com/lima-vm/lima/v2/pkg/networks"
 	"github.com/lima-vm/lima/v2/pkg/networks/usernet"
 	"github.com/lima-vm/lima/v2/pkg/osutil"
 	"github.com/lima-vm/lima/v2/pkg/store"
-	"github.com/lima-vm/lima/v2/pkg/store/dirnames"
 )
 
 func Reconcile(ctx context.Context, newInst string) error {
@@ -39,7 +40,7 @@ func Reconcile(ctx context.Context, newInst string) error {
 			return err
 		}
 		// newInst is about to be started, so its networks should be running
-		if instance.Status != store.StatusRunning && instName != newInst {
+		if instance.Status != limatype.StatusRunning && instName != newInst {
 			continue
 		}
 		for _, nw := range instance.Networks {
diff --git a/pkg/networks/usernet/client.go b/pkg/networks/usernet/client.go
index 53333c2f..f556c0a0 100644
--- a/pkg/networks/usernet/client.go
+++ b/pkg/networks/usernet/client.go
@@ -18,9 +18,9 @@ import (
 	"github.com/containers/gvisor-tap-vsock/pkg/types"
 
 	"github.com/lima-vm/lima/v2/pkg/httpclientutil"
+	"github.com/lima-vm/lima/v2/pkg/limatype"
 	"github.com/lima-vm/lima/v2/pkg/limayaml"
 	"github.com/lima-vm/lima/v2/pkg/networks/usernet/dnshosts"
-	"github.com/lima-vm/lima/v2/pkg/store"
 )
 
 type Client struct {
@@ -32,7 +32,7 @@ type Client struct {
 	subnet   net.IP
 }
 
-func (c *Client) ConfigureDriver(ctx context.Context, inst *store.Instance, sshLocalPort int) error {
+func (c *Client) ConfigureDriver(ctx context.Context, inst *limatype.Instance, sshLocalPort int) error {
 	macAddress := limayaml.MACAddress(inst.Dir)
 	ipAddress, err := c.ResolveIPAddress(ctx, macAddress)
 	if err != nil {
diff --git a/pkg/networks/usernet/config.go b/pkg/networks/usernet/config.go
index e8304dee..35904494 100644
--- a/pkg/networks/usernet/config.go
+++ b/pkg/networks/usernet/config.go
@@ -10,9 +10,9 @@ import (
 
 	"github.com/apparentlymart/go-cidr/cidr"
 
+	"github.com/lima-vm/lima/v2/pkg/limatype/dirnames"
 	"github.com/lima-vm/lima/v2/pkg/networks"
 	"github.com/lima-vm/lima/v2/pkg/osutil"
-	"github.com/lima-vm/lima/v2/pkg/store/dirnames"
 )
 
 type SockType = string
diff --git a/pkg/networks/usernet/recoincile.go b/pkg/networks/usernet/recoincile.go
index 1a8709ad..6f62bd49 100644
--- a/pkg/networks/usernet/recoincile.go
+++ b/pkg/networks/usernet/recoincile.go
@@ -19,9 +19,9 @@ import (
 	"github.com/sirupsen/logrus"
 
 	"github.com/lima-vm/lima/v2/pkg/executil"
+	"github.com/lima-vm/lima/v2/pkg/limatype/dirnames"
 	"github.com/lima-vm/lima/v2/pkg/lockutil"
 	"github.com/lima-vm/lima/v2/pkg/store"
-	"github.com/lima-vm/lima/v2/pkg/store/dirnames"
 )
 
 // Start starts a instance a usernet network with the given name.
diff --git a/pkg/portfwd/forward.go b/pkg/portfwd/forward.go
index 1dc44dfa..d20d9a7e 100644
--- a/pkg/portfwd/forward.go
+++ b/pkg/portfwd/forward.go
@@ -12,19 +12,20 @@ import (
 
 	"github.com/lima-vm/lima/v2/pkg/guestagent/api"
 	guestagentclient "github.com/lima-vm/lima/v2/pkg/guestagent/api/client"
+	"github.com/lima-vm/lima/v2/pkg/limatype"
 	"github.com/lima-vm/lima/v2/pkg/limayaml"
 )
 
 var IPv4loopback1 = limayaml.IPv4loopback1
 
 type Forwarder struct {
-	rules             []limayaml.PortForward
+	rules             []limatype.PortForward
 	ignoreTCP         bool
 	ignoreUDP         bool
 	closableListeners *ClosableListeners
 }
 
-func NewPortForwarder(rules []limayaml.PortForward, ignoreTCP, ignoreUDP bool) *Forwarder {
+func NewPortForwarder(rules []limatype.PortForward, ignoreTCP, ignoreUDP bool) *Forwarder {
 	return &Forwarder{
 		rules:             rules,
 		ignoreTCP:         ignoreTCP,
@@ -64,7 +65,7 @@ func (fw *Forwarder) forwardingAddresses(guest *api.IPPort) (hostAddr, guestAddr
 		if rule.GuestSocket != "" {
 			continue
 		}
-		if rule.Proto != limayaml.ProtoAny && rule.Proto != guest.Protocol {
+		if rule.Proto != limatype.ProtoAny && rule.Proto != guest.Protocol {
 			continue
 		}
 		if guest.Port < int32(rule.GuestPortRange[0]) || guest.Port > int32(rule.GuestPortRange[1]) {
@@ -91,7 +92,7 @@ func (fw *Forwarder) forwardingAddresses(guest *api.IPPort) (hostAddr, guestAddr
 	return "", guest.HostString()
 }
 
-func hostAddress(rule limayaml.PortForward, guest *api.IPPort) string {
+func hostAddress(rule limatype.PortForward, guest *api.IPPort) string {
 	if rule.HostSocket != "" {
 		return rule.HostSocket
 	}
diff --git a/pkg/registry/registry.go b/pkg/registry/registry.go
index 7ea99543..d69146c5 100644
--- a/pkg/registry/registry.go
+++ b/pkg/registry/registry.go
@@ -195,7 +195,7 @@ func isExecutable(mode os.FileMode) bool {
 }
 
 func Register(driver driver.Driver) {
-	name := driver.Info().DriverName
+	name := driver.Info().Features.DriverName
 	if _, exists := internalDrivers[name]; exists {
 		return
 	}
diff --git a/pkg/snapshot/snapshot.go b/pkg/snapshot/snapshot.go
index 530363f8..e76caa6e 100644
--- a/pkg/snapshot/snapshot.go
+++ b/pkg/snapshot/snapshot.go
@@ -8,10 +8,10 @@ import (
 	"fmt"
 
 	"github.com/lima-vm/lima/v2/pkg/driverutil"
-	"github.com/lima-vm/lima/v2/pkg/store"
+	"github.com/lima-vm/lima/v2/pkg/limatype"
 )
 
-func Del(ctx context.Context, inst *store.Instance, tag string) error {
+func Del(ctx context.Context, inst *limatype.Instance, tag string) error {
 	limaDriver, err := driverutil.CreateConfiguredDriver(inst, 0)
 	if err != nil {
 		return fmt.Errorf("failed to create driver instance: %w", err)
@@ -20,7 +20,7 @@ func Del(ctx context.Context, inst *store.Instance, tag string) error {
 	return limaDriver.DeleteSnapshot(ctx, tag)
 }
 
-func Save(ctx context.Context, inst *store.Instance, tag string) error {
+func Save(ctx context.Context, inst *limatype.Instance, tag string) error {
 	limaDriver, err := driverutil.CreateConfiguredDriver(inst, 0)
 	if err != nil {
 		return fmt.Errorf("failed to create driver instance: %w", err)
@@ -28,7 +28,7 @@ func Save(ctx context.Context, inst *store.Instance, tag string) error {
 	return limaDriver.CreateSnapshot(ctx, tag)
 }
 
-func Load(ctx context.Context, inst *store.Instance, tag string) error {
+func Load(ctx context.Context, inst *limatype.Instance, tag string) error {
 	limaDriver, err := driverutil.CreateConfiguredDriver(inst, 0)
 	if err != nil {
 		return fmt.Errorf("failed to create driver instance: %w", err)
@@ -36,7 +36,7 @@ func Load(ctx context.Context, inst *store.Instance, tag string) error {
 	return limaDriver.ApplySnapshot(ctx, tag)
 }
 
-func List(ctx context.Context, inst *store.Instance) (string, error) {
+func List(ctx context.Context, inst *limatype.Instance) (string, error) {
 	limaDriver, err := driverutil.CreateConfiguredDriver(inst, 0)
 	if err != nil {
 		return "", fmt.Errorf("failed to create driver instance: %w", err)
diff --git a/pkg/sshutil/sshutil.go b/pkg/sshutil/sshutil.go
index 103da564..b3d8e8c2 100644
--- a/pkg/sshutil/sshutil.go
+++ b/pkg/sshutil/sshutil.go
@@ -26,10 +26,10 @@ import (
 	"golang.org/x/sys/cpu"
 
 	"github.com/lima-vm/lima/v2/pkg/ioutilx"
+	"github.com/lima-vm/lima/v2/pkg/limatype/dirnames"
+	"github.com/lima-vm/lima/v2/pkg/limatype/filenames"
 	"github.com/lima-vm/lima/v2/pkg/lockutil"
 	"github.com/lima-vm/lima/v2/pkg/osutil"
-	"github.com/lima-vm/lima/v2/pkg/store/dirnames"
-	"github.com/lima-vm/lima/v2/pkg/store/filenames"
 )
 
 // Environment variable that allows configuring the command (alias) to execute
diff --git a/pkg/store/disk.go b/pkg/store/disk.go
index 5df18fbc..20a69f91 100644
--- a/pkg/store/disk.go
+++ b/pkg/store/disk.go
@@ -12,7 +12,7 @@ import (
 
 	"github.com/lima-vm/go-qcow2reader"
 
-	"github.com/lima-vm/lima/v2/pkg/store/filenames"
+	"github.com/lima-vm/lima/v2/pkg/limatype/filenames"
 )
 
 type Disk struct {
diff --git a/pkg/store/instance.go b/pkg/store/instance.go
index 04e6b49c..038bb16b 100644
--- a/pkg/store/instance.go
+++ b/pkg/store/instance.go
@@ -5,7 +5,6 @@ package store
 
 import (
 	"context"
-	"encoding/json"
 	"errors"
 	"fmt"
 	"io"
@@ -22,64 +21,27 @@ import (
 	"github.com/docker/go-units"
 	"github.com/sirupsen/logrus"
 
+	"github.com/lima-vm/lima/v2/pkg/driverutil"
 	hostagentclient "github.com/lima-vm/lima/v2/pkg/hostagent/api/client"
 	"github.com/lima-vm/lima/v2/pkg/instance/hostname"
-	"github.com/lima-vm/lima/v2/pkg/limayaml"
-	"github.com/lima-vm/lima/v2/pkg/store/dirnames"
-	"github.com/lima-vm/lima/v2/pkg/store/filenames"
+	"github.com/lima-vm/lima/v2/pkg/limatype"
+	"github.com/lima-vm/lima/v2/pkg/limatype/dirnames"
+	"github.com/lima-vm/lima/v2/pkg/limatype/filenames"
 	"github.com/lima-vm/lima/v2/pkg/textutil"
 	"github.com/lima-vm/lima/v2/pkg/version/versionutil"
 )
 
-type Status = string
-
-const (
-	StatusUnknown       Status = ""
-	StatusUninitialized Status = "Uninitialized"
-	StatusInstalling    Status = "Installing"
-	StatusBroken        Status = "Broken"
-	StatusStopped       Status = "Stopped"
-	StatusRunning       Status = "Running"
-)
-
-type Instance struct {
-	Name string `json:"name"`
-	// Hostname, not HostName (corresponds to SSH's naming convention)
-	Hostname        string             `json:"hostname"`
-	Status          Status             `json:"status"`
-	Dir             string             `json:"dir"`
-	VMType          limayaml.VMType    `json:"vmType"`
-	Arch            limayaml.Arch      `json:"arch"`
-	CPUType         string             `json:"cpuType"`
-	CPUs            int                `json:"cpus,omitempty"`
-	Memory          int64              `json:"memory,omitempty"` // bytes
-	Disk            int64              `json:"disk,omitempty"`   // bytes
-	Message         string             `json:"message,omitempty"`
-	AdditionalDisks []limayaml.Disk    `json:"additionalDisks,omitempty"`
-	Networks        []limayaml.Network `json:"network,omitempty"`
-	SSHLocalPort    int                `json:"sshLocalPort,omitempty"`
-	SSHConfigFile   string             `json:"sshConfigFile,omitempty"`
-	HostAgentPID    int                `json:"hostAgentPID,omitempty"`
-	DriverPID       int                `json:"driverPID,omitempty"`
-	Errors          []error            `json:"errors,omitempty"`
-	Config          *limayaml.LimaYAML `json:"config,omitempty"`
-	SSHAddress      string             `json:"sshAddress,omitempty"`
-	Protected       bool               `json:"protected"`
-	LimaVersion     string             `json:"limaVersion"`
-	Param           map[string]string  `json:"param,omitempty"`
-}
-
 // Inspect returns err only when the instance does not exist (os.ErrNotExist).
 // Other errors are returned as *Instance.Errors.
-func Inspect(ctx context.Context, instName string) (*Instance, error) {
-	inst := &Instance{
+func Inspect(ctx context.Context, instName string) (*limatype.Instance, error) {
+	inst := &limatype.Instance{
 		Name: instName,
 		// TODO: support customizing hostname
 		Hostname: hostname.FromInstName(instName),
-		Status:   StatusUnknown,
+		Status:   limatype.StatusUnknown,
 	}
 	// InstanceDir validates the instName but does not check whether the instance exists
-	instDir, err := InstanceDir(instName)
+	instDir, err := dirnames.InstanceDir(instName)
 	if err != nil {
 		return nil, err
 	}
@@ -97,13 +59,12 @@ func Inspect(ctx context.Context, instName string) (*Instance, error) {
 	inst.Config = y
 	inst.Arch = *y.Arch
 	inst.VMType = *y.VMType
-	inst.CPUType = y.VMOpts.QEMU.CPUType[*y.Arch]
 	inst.SSHAddress = "127.0.0.1"
 	inst.SSHLocalPort = *y.SSH.LocalPort // maybe 0
 	inst.SSHConfigFile = filepath.Join(instDir, filenames.SSHConfig)
 	inst.HostAgentPID, err = ReadPIDFile(filepath.Join(instDir, filenames.HostAgentPID))
 	if err != nil {
-		inst.Status = StatusBroken
+		inst.Status = limatype.StatusBroken
 		inst.Errors = append(inst.Errors, err)
 	}
 
@@ -111,14 +72,14 @@ func Inspect(ctx context.Context, instName string) (*Instance, error) {
 		haSock := filepath.Join(instDir, filenames.HostAgentSock)
 		haClient, err := hostagentclient.NewHostAgentClient(haSock)
 		if err != nil {
-			inst.Status = StatusBroken
+			inst.Status = limatype.StatusBroken
 			inst.Errors = append(inst.Errors, fmt.Errorf("failed to connect to %q: %w", haSock, err))
 		} else {
 			ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
 			defer cancel()
 			info, err := haClient.Info(ctx)
 			if err != nil {
-				inst.Status = StatusBroken
+				inst.Status = limatype.StatusBroken
 				inst.Errors = append(inst.Errors, fmt.Errorf("failed to get Info from %q: %w", haSock, err))
 			} else {
 				inst.SSHLocalPort = info.SSHLocalPort
@@ -139,7 +100,7 @@ func Inspect(ctx context.Context, instName string) (*Instance, error) {
 	inst.Networks = y.Networks
 
 	// 0 out values since not configurable on WSL2
-	if inst.VMType == limayaml.WSL2 {
+	if inst.VMType == limatype.WSL2 {
 		inst.Memory = 0
 		inst.CPUs = 0
 		inst.Disk = 0
@@ -155,18 +116,18 @@ func Inspect(ctx context.Context, instName string) (*Instance, error) {
 	tmpl, err := template.New("format").Parse(y.Message)
 	if err != nil {
 		inst.Errors = append(inst.Errors, fmt.Errorf("message %q is not a valid template: %w", y.Message, err))
-		inst.Status = StatusBroken
+		inst.Status = limatype.StatusBroken
 	} else {
 		data, err := AddGlobalFields(inst)
 		if err != nil {
 			inst.Errors = append(inst.Errors, fmt.Errorf("cannot add global fields to instance data: %w", err))
-			inst.Status = StatusBroken
+			inst.Status = limatype.StatusBroken
 		} else {
 			var message strings.Builder
 			err = tmpl.Execute(&message, data)
 			if err != nil {
 				inst.Errors = append(inst.Errors, fmt.Errorf("cannot execute template %q: %w", y.Message, err))
-				inst.Status = StatusBroken
+				inst.Status = limatype.StatusBroken
 			} else {
 				inst.Message = message.String()
 			}
@@ -186,26 +147,42 @@ func Inspect(ctx context.Context, instName string) (*Instance, error) {
 	return inst, nil
 }
 
-func inspectStatusWithPIDFiles(instDir string, inst *Instance, y *limayaml.LimaYAML) {
+func inspectStatus(ctx context.Context, instDir string, inst *limatype.Instance, y *limatype.LimaYAML) {
+	status, err := driverutil.InspectStatus(ctx, inst)
+	if err != nil {
+		inst.Status = limatype.StatusBroken
+		inst.Errors = append(inst.Errors, fmt.Errorf("failed to inspect status: %w", err))
+		return
+	}
+
+	if status == "" {
+		inspectStatusWithPIDFiles(instDir, inst, y)
+		return
+	}
+
+	inst.Status = status
+}
+
+func inspectStatusWithPIDFiles(instDir string, inst *limatype.Instance, y *limatype.LimaYAML) {
 	var err error
 	inst.DriverPID, err = ReadPIDFile(filepath.Join(instDir, filenames.PIDFile(*y.VMType)))
 	if err != nil {
-		inst.Status = StatusBroken
+		inst.Status = limatype.StatusBroken
 		inst.Errors = append(inst.Errors, err)
 	}
 
-	if inst.Status == StatusUnknown {
+	if inst.Status == limatype.StatusUnknown {
 		switch {
 		case inst.HostAgentPID > 0 && inst.DriverPID > 0:
-			inst.Status = StatusRunning
+			inst.Status = limatype.StatusRunning
 		case inst.HostAgentPID == 0 && inst.DriverPID == 0:
-			inst.Status = StatusStopped
+			inst.Status = limatype.StatusStopped
 		case inst.HostAgentPID > 0 && inst.DriverPID == 0:
 			inst.Errors = append(inst.Errors, errors.New("host agent is running but driver is not"))
-			inst.Status = StatusBroken
+			inst.Status = limatype.StatusBroken
 		default:
 			inst.Errors = append(inst.Errors, fmt.Errorf("%s driver is running but host agent is not", inst.VMType))
-			inst.Status = StatusBroken
+			inst.Status = limatype.StatusBroken
 		}
 	}
 }
@@ -248,7 +225,7 @@ func ReadPIDFile(path string) (int, error) {
 }
 
 type FormatData struct {
-	Instance
+	limatype.Instance
 	HostOS       string
 	HostArch     string
 	LimaHome     string
@@ -260,13 +237,13 @@ var FormatHelp = "\n" +
 	textutil.IndentString(2,
 		strings.Join(textutil.FuncHelp, "\n")+"\n")
 
-func AddGlobalFields(inst *Instance) (FormatData, error) {
+func AddGlobalFields(inst *limatype.Instance) (FormatData, error) {
 	var data FormatData
 	data.Instance = *inst
 	// Add HostOS
 	data.HostOS = runtime.GOOS
 	// Add HostArch
-	data.HostArch = limayaml.NewArch(runtime.GOARCH)
+	data.HostArch = limatype.NewArch(runtime.GOARCH)
 	// Add IdentityFile
 	configDir, err := dirnames.LimaConfigDir()
 	if err != nil {
@@ -288,7 +265,7 @@ type PrintOptions struct {
 
 // PrintInstances prints instances in a requested format to a given io.Writer.
 // Supported formats are "json", "yaml", "table", or a go template.
-func PrintInstances(w io.Writer, instances []*Instance, format string, options *PrintOptions) error {
+func PrintInstances(w io.Writer, instances []*limatype.Instance, format string, options *PrintOptions) error {
 	switch format {
 	case "json":
 		format = "{{json .}}"
@@ -322,7 +299,7 @@ func PrintInstances(w io.Writer, instances []*Instance, format string, options *
 			columns++ // VMTYPE
 		}
 		// only hide arch if it is the same as the host arch
-		goarch := limayaml.NewArch(runtime.GOARCH)
+		goarch := limatype.NewArch(runtime.GOARCH)
 		// can we still fit the remaining columns (6)
 		if width == 0 || (columns+6)*columnWidth > width && !all {
 			hideArch = len(archs) == 1 && instances[0].Arch == goarch
@@ -415,62 +392,3 @@ func PrintInstances(w io.Writer, instances []*Instance, format string, options *
 	}
 	return nil
 }
-
-// Protect protects the instance to prohibit accidental removal.
-// Protect does not return an error even when the instance is already protected.
-func (inst *Instance) Protect() error {
-	protected := filepath.Join(inst.Dir, filenames.Protected)
-	// TODO: Do an equivalent of `chmod +a "everyone deny delete,delete_child,file_inherit,directory_inherit"`
-	// https://github.com/lima-vm/lima/issues/1595
-	if err := os.WriteFile(protected, nil, 0o400); err != nil {
-		return err
-	}
-	inst.Protected = true
-	return nil
-}
-
-// Unprotect unprotects the instance.
-// Unprotect does not return an error even when the instance is already unprotected.
-func (inst *Instance) Unprotect() error {
-	protected := filepath.Join(inst.Dir, filenames.Protected)
-	if err := os.RemoveAll(protected); err != nil {
-		return err
-	}
-	inst.Protected = false
-	return nil
-}
-
-func (inst *Instance) MarshalJSON() ([]byte, error) {
-	type Alias Instance
-	errorsAsStrings := make([]string, len(inst.Errors))
-	for i, err := range inst.Errors {
-		if err != nil {
-			errorsAsStrings[i] = err.Error()
-		}
-	}
-	return json.Marshal(&struct {
-		*Alias
-		Errors []string `json:"errors,omitempty"`
-	}{
-		Alias:  (*Alias)(inst),
-		Errors: errorsAsStrings,
-	})
-}
-
-func (inst *Instance) UnmarshalJSON(data []byte) error {
-	type Alias Instance
-	aux := &struct {
-		*Alias
-		Errors []string `json:"errors,omitempty"`
-	}{
-		Alias: (*Alias)(inst),
-	}
-	if err := json.Unmarshal(data, &aux); err != nil {
-		return err
-	}
-	inst.Errors = nil
-	for _, msg := range aux.Errors {
-		inst.Errors = append(inst.Errors, errors.New(msg))
-	}
-	return nil
-}
diff --git a/pkg/store/instance_unix.go b/pkg/store/instance_unix.go
deleted file mode 100644
index 71aaa3df..00000000
--- a/pkg/store/instance_unix.go
+++ /dev/null
@@ -1,20 +0,0 @@
-//go:build !windows
-
-// SPDX-FileCopyrightText: Copyright The Lima Authors
-// SPDX-License-Identifier: Apache-2.0
-
-package store
-
-import (
-	"context"
-
-	"github.com/lima-vm/lima/v2/pkg/limayaml"
-)
-
-func inspectStatus(_ context.Context, instDir string, inst *Instance, y *limayaml.LimaYAML) {
-	inspectStatusWithPIDFiles(instDir, inst, y)
-}
-
-func GetSSHAddress(_ context.Context, _ string) (string, error) {
-	return "127.0.0.1", nil
-}
diff --git a/pkg/store/instance_windows.go b/pkg/store/instance_windows.go
deleted file mode 100644
index b9e963ba..00000000
--- a/pkg/store/instance_windows.go
+++ /dev/null
@@ -1,149 +0,0 @@
-// SPDX-FileCopyrightText: Copyright The Lima Authors
-// SPDX-License-Identifier: Apache-2.0
-
-package store
-
-import (
-	"context"
-	"fmt"
-	"os/exec"
-	"regexp"
-	"strings"
-
-	"github.com/lima-vm/lima/v2/pkg/executil"
-	"github.com/lima-vm/lima/v2/pkg/limayaml"
-)
-
-func inspectStatus(ctx context.Context, instDir string, inst *Instance, y *limayaml.LimaYAML) {
-	if inst.VMType == limayaml.WSL2 {
-		status, err := GetWslStatus(inst.Name)
-		if err != nil {
-			inst.Status = StatusBroken
-			inst.Errors = append(inst.Errors, err)
-		} else {
-			inst.Status = status
-		}
-
-		inst.SSHLocalPort = 22
-
-		if inst.Status == StatusRunning {
-			sshAddr, err := GetSSHAddress(ctx, inst.Name)
-			if err == nil {
-				inst.SSHAddress = sshAddr
-			} else {
-				inst.Errors = append(inst.Errors, err)
-			}
-		}
-	} else {
-		inspectStatusWithPIDFiles(instDir, inst, y)
-	}
-}
-
-// GetWslStatus runs `wsl --list --verbose` and parses its output.
-// There are several possible outputs, all listed with their whitespace preserved output below.
-//
-// (1) Expected output if at least one distro is installed:
-// PS > wsl --list --verbose
-//
-//	NAME      STATE           VERSION
-//
-// * Ubuntu    Stopped         2
-//
-// (2) Expected output when no distros are installed, but WSL is configured properly:
-// PS > wsl --list --verbose
-// Windows Subsystem for Linux has no installed distributions.
-//
-// Use 'wsl.exe --list --online' to list available distributions
-// and 'wsl.exe --install <Distro>' to install.
-//
-// Distributions can also be installed by visiting the Microsoft Store:
-// https://aka.ms/wslstore
-// Error code: Wsl/WSL_E_DEFAULT_DISTRO_NOT_FOUND
-//
-// (3) Expected output when no distros are installed, and WSL2 has no kernel installed:
-//
-// PS > wsl --list --verbose
-// Windows Subsystem for Linux has no installed distributions.
-// Distributions can be installed by visiting the Microsoft Store:
-// https://aka.ms/wslstore
-func GetWslStatus(instName string) (string, error) {
-	distroName := "lima-" + instName
-	out, err := executil.RunUTF16leCommand([]string{
-		"wsl.exe",
-		"--list",
-		"--verbose",
-	})
-	if err != nil {
-		return "", fmt.Errorf("failed to run `wsl --list --verbose`, err: %w (out=%q)", err, out)
-	}
-
-	if out == "" {
-		return StatusBroken, fmt.Errorf("failed to read instance state for instance %q, try running `wsl --list --verbose` to debug, err: %w", instName, err)
-	}
-
-	// Check for edge cases first
-	if strings.Contains(out, "Windows Subsystem for Linux has no installed distributions.") {
-		if strings.Contains(out, "Wsl/WSL_E_DEFAULT_DISTRO_NOT_FOUND") {
-			return StatusBroken, fmt.Errorf(
-				"failed to read instance state for instance %q because no distro is installed,"+
-					"try running `wsl --install -d Ubuntu` and then re-running Lima", instName)
-		}
-		return StatusBroken, fmt.Errorf(
-			"failed to read instance state for instance %q because there is no WSL kernel installed,"+
-				"this usually happens when WSL was installed for another user, but never for your user."+
-				"Try running `wsl --install -d Ubuntu` and `wsl --update`, and then re-running Lima", instName)
-	}
-
-	var instState string
-	wslListColsRegex := regexp.MustCompile(`\s+`)
-	// wsl --list --verbose may have different headers depending on localization, just split by line
-	for _, rows := range strings.Split(strings.ReplaceAll(out, "\r\n", "\n"), "\n") {
-		cols := wslListColsRegex.Split(strings.TrimSpace(rows), -1)
-		nameIdx := 0
-		// '*' indicates default instance
-		if cols[0] == "*" {
-			nameIdx = 1
-		}
-		if cols[nameIdx] == distroName {
-			instState = cols[nameIdx+1]
-			break
-		}
-	}
-
-	if instState == "" {
-		return StatusUninitialized, nil
-	}
-
-	return instState, nil
-}
-
-// GetSSHAddress runs a hostname command to get the IP from inside of a wsl2 VM.
-//
-// Expected output (whitespace preserved, [] for optional):
-// PS > wsl -d <distroName> bash -c hostname -I | cut -d' ' -f1
-// 168.1.1.1 [10.0.0.1]
-// But busybox hostname does not implement --all-ip-addresses:
-// hostname: unrecognized option: I
-func GetSSHAddress(ctx context.Context, instName string) (string, error) {
-	distroName := "lima-" + instName
-	// Ubuntu
-	cmd := exec.CommandContext(ctx, "wsl.exe", "-d", distroName, "bash", "-c", `hostname -I | cut -d ' ' -f1`)
-	out, err := cmd.CombinedOutput()
-	if err == nil {
-		return strings.TrimSpace(string(out)), nil
-	}
-	// Alpine
-	cmd = exec.CommandContext(ctx, "wsl.exe", "-d", distroName, "sh", "-c", `ip route get 1 | awk '{gsub("^.*src ",""); print $1; exit}'`)
-	out, err = cmd.CombinedOutput()
-	if err == nil {
-		return strings.TrimSpace(string(out)), nil
-	}
-	// fallback
-	cmd = exec.CommandContext(ctx, "wsl.exe", "-d", distroName, "hostname", "-i")
-	out, err = cmd.CombinedOutput()
-	if err != nil || strings.HasPrefix(string(out), "127.") {
-		return "", fmt.Errorf("failed to get hostname for instance %q, err: %w (out=%q)", instName, err, string(out))
-	}
-
-	return strings.TrimSpace(string(out)), nil
-}
diff --git a/pkg/store/store.go b/pkg/store/store.go
index 7a4f27c1..93249bae 100644
--- a/pkg/store/store.go
+++ b/pkg/store/store.go
@@ -11,10 +11,12 @@ import (
 	"path/filepath"
 	"strings"
 
+	"github.com/lima-vm/lima/v2/pkg/driverutil"
 	"github.com/lima-vm/lima/v2/pkg/identifiers"
+	"github.com/lima-vm/lima/v2/pkg/limatype"
+	"github.com/lima-vm/lima/v2/pkg/limatype/dirnames"
+	"github.com/lima-vm/lima/v2/pkg/limatype/filenames"
 	"github.com/lima-vm/lima/v2/pkg/limayaml"
-	"github.com/lima-vm/lima/v2/pkg/store/dirnames"
-	"github.com/lima-vm/lima/v2/pkg/store/filenames"
 )
 
 // Directory returns the LimaDir.
@@ -47,19 +49,6 @@ func Validate() error {
 	return nil
 }
 
-// ValidateInstName checks if the name is a valid instance name. For this it needs to
-// be a valid identifier, and not end in .yml or .yaml (case insensitively).
-func ValidateInstName(name string) error {
-	if err := identifiers.Validate(name); err != nil {
-		return fmt.Errorf("instance name %q is not a valid identifier: %w", name, err)
-	}
-	lower := strings.ToLower(name)
-	if strings.HasSuffix(lower, ".yml") || strings.HasSuffix(lower, ".yaml") {
-		return fmt.Errorf("instance name %q must not end with .yml or .yaml suffix", name)
-	}
-	return nil
-}
-
 // Instances returns the names of the instances under LimaDir.
 func Instances() ([]string, error) {
 	limaDir, err := dirnames.LimaDir()
@@ -105,20 +94,6 @@ func Disks() ([]string, error) {
 	return names, nil
 }
 
-// InstanceDir returns the instance dir.
-// InstanceDir does not check whether the instance exists.
-func InstanceDir(name string) (string, error) {
-	if err := ValidateInstName(name); err != nil {
-		return "", err
-	}
-	limaDir, err := dirnames.LimaDir()
-	if err != nil {
-		return "", err
-	}
-	dir := filepath.Join(limaDir, name)
-	return dir, nil
-}
-
 func DiskDir(name string) (string, error) {
 	if err := identifiers.Validate(name); err != nil {
 		return "", err
@@ -132,7 +107,7 @@ func DiskDir(name string) (string, error) {
 }
 
 // LoadYAMLByFilePath loads and validates the yaml.
-func LoadYAMLByFilePath(ctx context.Context, filePath string) (*limayaml.LimaYAML, error) {
+func LoadYAMLByFilePath(ctx context.Context, filePath string) (*limatype.LimaYAML, error) {
 	// We need to use the absolute path because it may be used to determine hostSocket locations.
 	absPath, err := filepath.Abs(filePath)
 	if err != nil {
@@ -146,6 +121,9 @@ func LoadYAMLByFilePath(ctx context.Context, filePath string) (*limayaml.LimaYAM
 	if err != nil {
 		return nil, err
 	}
+	if err := driverutil.ResolveVMType(ctx, y, filePath); err != nil {
+		return nil, fmt.Errorf("failed to resolve vm for %q: %w", filePath, err)
+	}
 	if err := limayaml.Validate(y, false); err != nil {
 		return nil, err
 	}
diff --git a/pkg/usrlocalsharelima/usrlocalsharelima.go b/pkg/usrlocalsharelima/usrlocalsharelima.go
index e22052ce..917fe576 100644
--- a/pkg/usrlocalsharelima/usrlocalsharelima.go
+++ b/pkg/usrlocalsharelima/usrlocalsharelima.go
@@ -16,7 +16,7 @@ import (
 	"github.com/sirupsen/logrus"
 
 	"github.com/lima-vm/lima/v2/pkg/debugutil"
-	"github.com/lima-vm/lima/v2/pkg/limayaml"
+	"github.com/lima-vm/lima/v2/pkg/limatype"
 )
 
 // executableViaArgs0 returns the absolute path to the executable used to start this process.
@@ -69,8 +69,8 @@ func Dir() (string, error) {
 		}
 	}
 
-	ostype := limayaml.NewOS("linux")
-	arch := limayaml.NewArch(runtime.GOARCH)
+	ostype := limatype.NewOS("linux")
+	arch := limatype.NewArch(runtime.GOARCH)
 	if arch == "" {
 		return "", fmt.Errorf("failed to get arch for %q", runtime.GOARCH)
 	}
@@ -122,7 +122,7 @@ func Dir() (string, error) {
 }
 
 // GuestAgentBinary returns the absolute path of the guest agent binary, possibly with ".gz" suffix.
-func GuestAgentBinary(ostype limayaml.OS, arch limayaml.Arch) (string, error) {
+func GuestAgentBinary(ostype limatype.OS, arch limatype.Arch) (string, error) {
 	if ostype == "" {
 		return "", errors.New("os must be set")
 	}
diff --git a/templates/default.yaml b/templates/default.yaml
index 2aa5c407..2b9196e7 100644
--- a/templates/default.yaml
+++ b/templates/default.yaml
@@ -329,6 +329,15 @@ vmOpts:
     #   armv7l:  "max" # (or "host" when running on armv7l host)
     #   riscv64: "max" # (or "host" when running on riscv64 host)
     #   x86_64:  "max" # (or "host" when running on x86_64 host; additional options are appended on Intel Mac)
+  vz:
+    rosetta:
+      # Enable Rosetta inside the VM; needs `vmType: vz`
+      # Hint: try `softwareupdate --install-rosetta` if Lima gets stuck at `Installing rosetta...`
+      # 🟢 Builtin default: false
+      enabled: null
+      # Register rosetta to /proc/sys/fs/binfmt_misc
+      # 🟢 Builtin default: false
+      binfmt: null
 
 # OS: "Linux".
 # 🟢 Builtin default: "Linux"
@@ -337,13 +346,9 @@ os: null
 # DEPRECATED: Use vmOpts.qemu.cpuType instead. See the vmOpts.qemu.cpuType section above for configuration.
 cpuType:
 
+# DEPRECATED: Use vmOpts.vz.rosetta instead. See the vmOpts.qemu.cpuType section above for configuration.
 rosetta:
-  # Enable Rosetta inside the VM; needs `vmType: vz`
-  # Hint: try `softwareupdate --install-rosetta` if Lima gets stuck at `Installing rosetta...`
-  # 🟢 Builtin default: false
   enabled: null
-  # Register rosetta to /proc/sys/fs/binfmt_misc
-  # 🟢 Builtin default: false
   binfmt: null
 
 # Specify the timezone name (as used by the zoneinfo database). Specify the empty string
diff --git a/website/content/en/docs/community/governance.md b/website/content/en/docs/community/governance.md
index 549d2963..bdcd2e8b 100644
--- a/website/content/en/docs/community/governance.md
+++ b/website/content/en/docs/community/governance.md
@@ -26,6 +26,11 @@ Maintainers consist of two roles:
   A Reviewer is considered as a candidate to become a Committer.
   Reviewers are not recognized as Maintainers in <https://github.com/cncf/foundation/blob/main/project-maintainers.csv>.
 
+Access control is enforced via GitHub team membership:
+* All committers are members of the `@lima-vm/committers` team and have the `Maintain` role on all repos.
+* All reviewers are members of the `@lima-vm/reviewers` team and have the `Triage` role on all repos.
+* The `@lima-vm/maintainers` team includes both committers and reviewers, but doesn't grant any extra access.
+
 See also the [Contributing](../contributing) page.
 
 ### Current maintainers
diff --git a/website/content/en/docs/config/environment-variables.md b/website/content/en/docs/config/environment-variables.md
index dcaa128c..064917e9 100644
--- a/website/content/en/docs/config/environment-variables.md
+++ b/website/content/en/docs/config/environment-variables.md
@@ -57,6 +57,45 @@ This page documents the environment variables used in Lima.
   lima
   ```
 
+### `LIMA_SHELLENV_ALLOW`
+
+- **Description**: Specifies a comma-separated list of environment variable patterns to allow when propagating environment variables to the Lima instance with `--preserve-env`. When set, **only** variables matching these patterns will be passed through, completely overriding the default block list behavior. This feature only applies to Lima v2.0.0 or later.
+- **Default**: unset (when using `--preserve-env`, all variables are propagated except those matching the block list patterns)
+- **Usage**:
+  ```sh
+  export LIMA_SHELLENV_ALLOW="FPATH,XAUTHORITY,CUSTOM_*"
+  limactl shell --preserve-env default
+  ```
+- **Behavior**:
+  - **Without `--preserve-env`**: No environment variables are propagated (regardless of this setting)
+  - **With `--preserve-env` and `LIMA_SHELLENV_ALLOW` unset**: All variables are propagated except those in the block list
+  - **With `--preserve-env` and `LIMA_SHELLENV_ALLOW` set**: Only variables matching the allow patterns are propagated (block list is ignored)
+- **Note**: Patterns support wildcards using `*` at the end (e.g., `CUSTOM_*` matches `CUSTOM_VAR`, `CUSTOM_PATH`, etc.).
+
+### `LIMA_SHELLENV_BLOCK`
+
+- **Description**: Specifies a comma-separated list of environment variable patterns to block when propagating environment variables to the Lima instance with `--preserve-env`. Can either replace the default block list or extend it by prefixing with `+`. This feature only applies to Lima v2.0.0 or later.
+- **Default**: A predefined list of system and shell-specific variables that should not be propagated:
+  - Shell variables: `BASH*`, `SHELL`, `SHLVL`, `ZSH*`, `ZDOTDIR`, `FPATH`
+  - System paths: `PATH`, `PWD`, `OLDPWD`, `TMPDIR`
+  - User/system info: `HOME`, `USER`, `LOGNAME`, `UID`, `GID`, `EUID`, `GROUP`, `HOSTNAME`
+  - Display/terminal: `DISPLAY`, `TERM`, `TERMINFO`, `XAUTHORITY`, `XDG_*`
+  - SSH/security: `SSH_*`
+  - Dynamic linker: `DYLD_*`, `LD_*`
+  - Internal variables: `_*` (variables starting with underscore)
+  
+  See [`GetDefaultBlockList()`](https://github.com/lima-vm/lima/blob/master/pkg/envutil/envutil.go#L133) for the complete list.
+- **Usage**:
+  ```sh
+  # Replace default block list entirely (not recommended)
+  export LIMA_SHELLENV_BLOCK="SECRET_*,PRIVATE_*"
+  
+  # Extend default block list (recommended)
+  export LIMA_SHELLENV_BLOCK="+SECRET_*,PRIVATE_*"
+  limactl shell --preserve-env default
+  ```
+- **Note**: Patterns support wildcards using `*` at the end (e.g., `SSH_*` matches `SSH_AUTH_SOCK`, `SSH_AGENT_PID`). Use the `+` prefix to add to the default block list rather than replacing it entirely. This variable only affects the `--preserve-env` flag behavior.
+
 ### `LIMACTL`
 
 - **Description**: Specifies the path to the `limactl` binary.



Test patch:
diff --git a/pkg/envutil/envutil_test.go b/pkg/envutil/envutil_test.go
index b6fb37ea..3530288f 100644
--- a/pkg/envutil/envutil_test.go
+++ b/pkg/envutil/envutil_test.go
@@ -207,7 +207,7 @@ func TestGetDefaultBlockList(t *testing.T) {
 
 	assert.DeepEqual(t, blocklist, defaultBlockList)
 
-	expectedItems := []string{"PATH", "HOME", "SSH_*"}
+	expectedItems := []string{"PATH", "HOME", "SSH_*", "USER"}
 	for _, item := range expectedItems {
 		found := slices.Contains(blocklist, item)
 		assert.Assert(t, found, "Expected builtin blocklist to contain %q", item)
diff --git a/pkg/limatmpl/embed_test.go b/pkg/limatmpl/embed_test.go
index 02e28ab1..f6c3b57f 100644
--- a/pkg/limatmpl/embed_test.go
+++ b/pkg/limatmpl/embed_test.go
@@ -14,6 +14,7 @@ import (
 	"gotest.tools/v3/assert"
 	"gotest.tools/v3/assert/cmp"
 
+	"github.com/lima-vm/lima/v2/pkg/limatype"
 	"github.com/lima-vm/lima/v2/pkg/limayaml"
 )
 
@@ -472,7 +473,7 @@ func RunEmbedTest(t *testing.T, tc embedTestCase) {
 	err = tmpl.Unmarshal()
 	assert.NilError(t, err, tc.description)
 
-	var expected limayaml.LimaYAML
+	var expected limatype.LimaYAML
 	err = limayaml.Unmarshal([]byte(tc.expected), &expected, "expected")
 	assert.NilError(t, err, tc.description)
 
diff --git a/pkg/limatmpl/locator_test.go b/pkg/limatmpl/locator_test.go
index b3cf8bda..b611cf3b 100644
--- a/pkg/limatmpl/locator_test.go
+++ b/pkg/limatmpl/locator_test.go
@@ -11,7 +11,7 @@ import (
 	"gotest.tools/v3/assert"
 
 	"github.com/lima-vm/lima/v2/pkg/limatmpl"
-	"github.com/lima-vm/lima/v2/pkg/limayaml"
+	"github.com/lima-vm/lima/v2/pkg/limatype"
 )
 
 func TestInstNameFromImageURL(t *testing.T) {
@@ -48,7 +48,7 @@ func TestInstNameFromImageURL(t *testing.T) {
 		assert.Equal(t, name, "my-arch-aarch64")
 	})
 	t.Run("removes native arch", func(t *testing.T) {
-		arch := limayaml.NewArch(runtime.GOARCH)
+		arch := limatype.NewArch(runtime.GOARCH)
 		image := fmt.Sprintf("linux_cloudimg.base-%s.qcow2.gz", arch)
 		name := limatmpl.InstNameFromImageURL(image, arch)
 		assert.Equal(t, name, "linux")
diff --git a/pkg/limayaml/defaults_test.go b/pkg/limayaml/defaults_test.go
index 72031382..f0b2c644 100644
--- a/pkg/limayaml/defaults_test.go
+++ b/pkg/limayaml/defaults_test.go
@@ -20,43 +20,42 @@ import (
 	"gotest.tools/v3/assert"
 
 	"github.com/lima-vm/lima/v2/pkg/ioutilx"
+	"github.com/lima-vm/lima/v2/pkg/limatype"
+	"github.com/lima-vm/lima/v2/pkg/limatype/dirnames"
+	"github.com/lima-vm/lima/v2/pkg/limatype/filenames"
 	"github.com/lima-vm/lima/v2/pkg/osutil"
 	"github.com/lima-vm/lima/v2/pkg/ptr"
-	"github.com/lima-vm/lima/v2/pkg/store/dirnames"
-	"github.com/lima-vm/lima/v2/pkg/store/filenames"
 )
 
 func TestFillDefault(t *testing.T) {
 	logrus.SetLevel(logrus.DebugLevel)
-	var d, y, o LimaYAML
-
-	defaultVMType := ResolveVMType(&y, &d, &o, "")
+	var d, y, o limatype.LimaYAML
 
 	opts := []cmp.Option{
 		// Consider nil slices and empty slices to be identical
 		cmpopts.EquateEmpty(),
 	}
 
-	var arch Arch
+	var arch limatype.Arch
 	switch runtime.GOARCH {
 	case "amd64":
-		arch = X8664
+		arch = limatype.X8664
 	case "arm64":
-		arch = AARCH64
+		arch = limatype.AARCH64
 	case "arm":
 		if runtime.GOOS != "linux" {
 			t.Skipf("unsupported GOOS: %s", runtime.GOOS)
 		}
-		if arm := goarm(); arm < 7 {
+		if arm := limatype.Goarm(); arm < 7 {
 			t.Skipf("unsupported GOARM: %d", arm)
 		}
-		arch = ARMV7L
+		arch = limatype.ARMV7L
 	case "ppc64le":
-		arch = PPC64LE
+		arch = limatype.PPC64LE
 	case "riscv64":
-		arch = RISCV64
+		arch = limatype.RISCV64
 	case "s390x":
-		arch = S390X
+		arch = limatype.S390X
 	default:
 		t.Skipf("unknown GOARCH: %s", runtime.GOARCH)
 	}
@@ -75,21 +74,20 @@ func TestFillDefault(t *testing.T) {
 	filePath := filepath.Join(instDir, filenames.LimaYAML)
 
 	// Builtin default values
-	builtin := LimaYAML{
-		VMType:             &defaultVMType,
-		OS:                 ptr.Of(LINUX),
+	builtin := limatype.LimaYAML{
+		OS:                 ptr.Of(limatype.LINUX),
 		Arch:               ptr.Of(arch),
 		CPUs:               ptr.Of(defaultCPUs()),
 		Memory:             ptr.Of(defaultMemoryAsString()),
 		Disk:               ptr.Of(defaultDiskSizeAsString()),
 		GuestInstallPrefix: ptr.Of(defaultGuestInstallPrefix()),
 		UpgradePackages:    ptr.Of(false),
-		Containerd: Containerd{
+		Containerd: limatype.Containerd{
 			System:   ptr.Of(false),
 			User:     ptr.Of(true),
 			Archives: defaultContainerdArchives(),
 		},
-		SSH: SSH{
+		SSH: limatype.SSH{
 			LocalPort:         ptr.Of(0),
 			LoadDotSSHPubKeys: ptr.Of(false),
 			ForwardAgent:      ptr.Of(false),
@@ -97,29 +95,26 @@ func TestFillDefault(t *testing.T) {
 			ForwardX11Trusted: ptr.Of(false),
 		},
 		TimeZone: ptr.Of(hostTimeZone()),
-		Firmware: Firmware{
+		Firmware: limatype.Firmware{
 			LegacyBIOS: ptr.Of(false),
 		},
-		Audio: Audio{
+		Audio: limatype.Audio{
 			Device: ptr.Of(""),
 		},
-		Video: Video{
+		Video: limatype.Video{
 			Display: ptr.Of("none"),
-			VNC: VNCOptions{
-				Display: ptr.Of("127.0.0.1:0,to=9"),
-			},
 		},
-		HostResolver: HostResolver{
+		HostResolver: limatype.HostResolver{
 			Enabled: ptr.Of(true),
 			IPv6:    ptr.Of(false),
 		},
 		PropagateProxyEnv: ptr.Of(true),
-		CACertificates: CACertificates{
+		CACertificates: limatype.CACertificates{
 			RemoveDefaults: ptr.Of(false),
 		},
 		NestedVirtualization: ptr.Of(false),
 		Plain:                ptr.Of(false),
-		User: User{
+		User: limatype.User{
 			Name:    ptr.Of(user.Username),
 			Comment: ptr.Of(user.Name),
 			Home:    ptr.Of(user.HomeDir),
@@ -128,12 +123,12 @@ func TestFillDefault(t *testing.T) {
 		},
 	}
 
-	defaultPortForward := PortForward{
+	defaultPortForward := limatype.PortForward{
 		GuestIP:        IPv4loopback1,
 		GuestPortRange: [2]int{1, 65535},
 		HostIP:         IPv4loopback1,
 		HostPortRange:  [2]int{1, 65535},
-		Proto:          ProtoTCP,
+		Proto:          limatype.ProtoTCP,
 		Reverse:        false,
 	}
 
@@ -142,31 +137,31 @@ func TestFillDefault(t *testing.T) {
 
 	// All these slices and maps are empty in "builtin". Add minimal entries here to see that
 	// their values are retained and defaults for their fields are applied correctly.
-	y = LimaYAML{
-		HostResolver: HostResolver{
+	y = limatype.LimaYAML{
+		HostResolver: limatype.HostResolver{
 			Hosts: map[string]string{
 				"MY.Host": "host.lima.internal",
 			},
 		},
-		Mounts: []Mount{
+		Mounts: []limatype.Mount{
 			//nolint:usetesting // We need the OS temp directory name here; it is not used to create temp files for testing
 			{Location: filepath.Clean(os.TempDir())},
 			{Location: filepath.Clean("{{.Dir}}/{{.Param.ONE}}"), MountPoint: ptr.Of("/mnt/{{.Param.ONE}}")},
 		},
-		MountType: ptr.Of(NINEP),
-		Provision: []Provision{
+		MountType: ptr.Of(limatype.NINEP),
+		Provision: []limatype.Provision{
 			{Script: "#!/bin/true # {{.Param.ONE}}"},
 		},
-		Probes: []Probe{
+		Probes: []limatype.Probe{
 			{Script: "#!/bin/false # {{.Param.ONE}}"},
 		},
-		Networks: []Network{
+		Networks: []limatype.Network{
 			{Lima: "shared"},
 		},
 		DNS: []net.IP{
 			net.ParseIP("1.0.1.0"),
 		},
-		PortForwards: []PortForward{
+		PortForwards: []limatype.PortForward{
 			{},
 			{GuestPort: 80},
 			{GuestPort: 8080, HostPort: 8888},
@@ -175,7 +170,7 @@ func TestFillDefault(t *testing.T) {
 				HostSocket:  "{{.Home}} | {{.Dir}} | {{.Name}} | {{.UID}} | {{.User}} | {{.Param.ONE}}",
 			},
 		},
-		CopyToHost: []CopyToHost{
+		CopyToHost: []limatype.CopyToHost{
 			{
 				GuestFile: "{{.Home}} | {{.UID}} | {{.User}} | {{.Param.ONE}}",
 				HostFile:  "{{.Home}} | {{.Dir}} | {{.Name}} | {{.UID}} | {{.User}} | {{.Param.ONE}}",
@@ -187,38 +182,18 @@ func TestFillDefault(t *testing.T) {
 		Param: map[string]string{
 			"ONE": "Eins",
 		},
-		CACertificates: CACertificates{
+		CACertificates: limatype.CACertificates{
 			Files: []string{"ca.crt"},
 			Certs: []string{
 				"-----BEGIN CERTIFICATE-----\nYOUR-ORGS-TRUSTED-CA-CERT\n-----END CERTIFICATE-----\n",
 			},
 		},
 		TimeZone: ptr.Of("Antarctica/Troll"),
-		Firmware: Firmware{
-			LegacyBIOS: ptr.Of(false),
-			Images: []FileWithVMType{
-				{
-					File: File{
-						Location: "https://gitlab.com/kraxel/qemu/-/raw/704f7cad5105246822686f65765ab92045f71a3b/pc-bios/edk2-aarch64-code.fd.bz2",
-						Arch:     AARCH64,
-						Digest:   "sha256:a5fc228623891297f2d82e22ea56ec57cde93fea5ec01abf543e4ed5cacaf277",
-					},
-					VMType: QEMU,
-				},
-				{
-					File: File{
-						Location: "https://github.com/AkihiroSuda/qemu/raw/704f7cad5105246822686f65765ab92045f71a3b/pc-bios/edk2-aarch64-code.fd.bz2",
-						Arch:     AARCH64,
-						Digest:   "sha256:a5fc228623891297f2d82e22ea56ec57cde93fea5ec01abf543e4ed5cacaf277",
-					},
-					VMType: QEMU,
-				},
-			},
-		},
 	}
 
 	expect := builtin
-	expect.VMType = ptr.Of(QEMU) // due to NINEP
+	// VMType should remain nil when not explicitly set (will be resolved by ValidateVMType later)
+	expect.VMType = nil
 	expect.HostResolver.Hosts = map[string]string{
 		"MY.Host": "host.lima.internal",
 	}
@@ -253,16 +228,16 @@ func TestFillDefault(t *testing.T) {
 	expect.Mounts[1].NineP.Cache = ptr.Of(Default9pCacheForRO)
 	expect.Mounts[1].Virtiofs.QueueSize = nil
 
-	expect.MountType = ptr.Of(NINEP)
+	expect.MountType = ptr.Of(limatype.NINEP)
 
 	expect.MountInotify = ptr.Of(false)
 
 	expect.Provision = slices.Clone(y.Provision)
-	expect.Provision[0].Mode = ProvisionModeSystem
+	expect.Provision[0].Mode = limatype.ProvisionModeSystem
 	expect.Provision[0].Script = "#!/bin/true # Eins"
 
 	expect.Probes = slices.Clone(y.Probes)
-	expect.Probes[0].Mode = ProbeModeReadiness
+	expect.Probes[0].Mode = limatype.ProbeModeReadiness
 	expect.Probes[0].Description = "user probe 1/1"
 	expect.Probes[0].Script = "#!/bin/false # Eins"
 
@@ -272,13 +247,13 @@ func TestFillDefault(t *testing.T) {
 	expect.Networks[0].Metric = ptr.Of(uint32(100))
 
 	expect.DNS = slices.Clone(y.DNS)
-	expect.PortForwards = []PortForward{
+	expect.PortForwards = []limatype.PortForward{
 		defaultPortForward,
 		defaultPortForward,
 		defaultPortForward,
 		defaultPortForward,
 	}
-	expect.CopyToHost = []CopyToHost{
+	expect.CopyToHost = []limatype.CopyToHost{
 		{},
 	}
 
@@ -303,7 +278,7 @@ func TestFillDefault(t *testing.T) {
 
 	expect.Param = y.Param
 
-	expect.CACertificates = CACertificates{
+	expect.CACertificates = limatype.CACertificates{
 		RemoveDefaults: ptr.Of(false),
 		Files:          []string{"ca.crt"},
 		Certs: []string{
@@ -312,17 +287,16 @@ func TestFillDefault(t *testing.T) {
 	}
 
 	expect.TimeZone = y.TimeZone
-	expect.Firmware = y.Firmware
-	expect.Firmware.Images = slices.Clone(y.Firmware.Images)
-
-	expect.Rosetta = Rosetta{
-		Enabled: ptr.Of(false),
-		BinFmt:  ptr.Of(false),
+	// Set firmware expectations to match what FillDefault actually does
+	// FillDefault uses the builtin default values, which include LegacyBIOS: ptr.Of(false)
+	expect.Firmware = limatype.Firmware{
+		LegacyBIOS: ptr.Of(false), // This matches what FillDefault actually sets
+		Images:     nil,
 	}
 
 	expect.NestedVirtualization = ptr.Of(false)
 
-	FillDefault(t.Context(), &y, &LimaYAML{}, &LimaYAML{}, filePath, false)
+	FillDefault(t.Context(), &y, &limatype.LimaYAML{}, &limatype.LimaYAML{}, filePath, false)
 	assert.DeepEqual(t, &y, &expect, opts...)
 
 	filledDefaults := y
@@ -334,26 +308,26 @@ func TestFillDefault(t *testing.T) {
 
 	// Calling filepath.Abs() to add a drive letter on Windows
 	varLog, _ := filepath.Abs("/var/log")
-	d = LimaYAML{
-		VMType: ptr.Of("vz"),
+	d = limatype.LimaYAML{
+		// Remove driver-specific VMType from defaults test
 		OS:     ptr.Of("unknown"),
 		Arch:   ptr.Of("unknown"),
 		CPUs:   ptr.Of(7),
 		Memory: ptr.Of("5GiB"),
 		Disk:   ptr.Of("105GiB"),
-		AdditionalDisks: []Disk{
+		AdditionalDisks: []limatype.Disk{
 			{Name: "data"},
 		},
 		GuestInstallPrefix: ptr.Of("/opt"),
 		UpgradePackages:    ptr.Of(true),
-		Containerd: Containerd{
+		Containerd: limatype.Containerd{
 			System: ptr.Of(true),
 			User:   ptr.Of(false),
-			Archives: []File{
+			Archives: []limatype.File{
 				{Location: "/tmp/nerdctl.tgz"},
 			},
 		},
-		SSH: SSH{
+		SSH: limatype.SSH{
 			LocalPort:         ptr.Of(888),
 			LoadDotSSHPubKeys: ptr.Of(false),
 			ForwardAgent:      ptr.Of(true),
@@ -361,27 +335,18 @@ func TestFillDefault(t *testing.T) {
 			ForwardX11Trusted: ptr.Of(false),
 		},
 		TimeZone: ptr.Of("Zulu"),
-		Firmware: Firmware{
+		Firmware: limatype.Firmware{
 			LegacyBIOS: ptr.Of(true),
-			Images: []FileWithVMType{
-				{
-					File: File{
-						Location: "/dummy",
-						Arch:     X8664,
-					},
-				},
-			},
+			// Remove driver-specific firmware images from defaults
 		},
-		Audio: Audio{
+		Audio: limatype.Audio{
 			Device: ptr.Of("coreaudio"),
 		},
-		Video: Video{
+		Video: limatype.Video{
 			Display: ptr.Of("cocoa"),
-			VNC: VNCOptions{
-				Display: ptr.Of("none"),
-			},
+			// Remove driver-specific VNC configuration
 		},
-		HostResolver: HostResolver{
+		HostResolver: limatype.HostResolver{
 			Enabled: ptr.Of(false),
 			IPv6:    ptr.Of(true),
 			Hosts: map[string]string{
@@ -390,26 +355,26 @@ func TestFillDefault(t *testing.T) {
 		},
 		PropagateProxyEnv: ptr.Of(false),
 
-		Mounts: []Mount{
+		Mounts: []limatype.Mount{
 			{
 				Location: varLog,
 				Writable: ptr.Of(false),
 			},
 		},
-		Provision: []Provision{
+		Provision: []limatype.Provision{
 			{
 				Script: "#!/bin/true",
-				Mode:   ProvisionModeUser,
+				Mode:   limatype.ProvisionModeUser,
 			},
 		},
-		Probes: []Probe{
+		Probes: []limatype.Probe{
 			{
 				Script:      "#!/bin/false",
-				Mode:        ProbeModeReadiness,
+				Mode:        limatype.ProbeModeReadiness,
 				Description: "User Probe",
 			},
 		},
-		Networks: []Network{
+		Networks: []limatype.Network{
 			{
 				MACAddress: "11:22:33:44:55:66",
 				Interface:  "def0",
@@ -419,16 +384,16 @@ func TestFillDefault(t *testing.T) {
 		DNS: []net.IP{
 			net.ParseIP("1.1.1.1"),
 		},
-		PortForwards: []PortForward{{
+		PortForwards: []limatype.PortForward{{
 			GuestIP:        IPv4loopback1,
 			GuestPort:      80,
 			GuestPortRange: [2]int{80, 80},
 			HostIP:         IPv4loopback1,
 			HostPort:       80,
 			HostPortRange:  [2]int{80, 80},
-			Proto:          ProtoTCP,
+			Proto:          limatype.ProtoTCP,
 		}},
-		CopyToHost: []CopyToHost{{}},
+		CopyToHost: []limatype.CopyToHost{{}},
 		Env: map[string]string{
 			"ONE": "one",
 			"TWO": "two",
@@ -437,18 +402,14 @@ func TestFillDefault(t *testing.T) {
 			"ONE": "one",
 			"TWO": "two",
 		},
-		CACertificates: CACertificates{
+		CACertificates: limatype.CACertificates{
 			RemoveDefaults: ptr.Of(true),
 			Certs: []string{
 				"-----BEGIN CERTIFICATE-----\nYOUR-ORGS-TRUSTED-CA-CERT\n-----END CERTIFICATE-----\n",
 			},
 		},
-		Rosetta: Rosetta{
-			Enabled: ptr.Of(true),
-			BinFmt:  ptr.Of(true),
-		},
 		NestedVirtualization: ptr.Of(true),
-		User: User{
+		User: limatype.User{
 			Name:    ptr.Of("xxx"),
 			Comment: ptr.Of("Foo Bar"),
 			Home:    ptr.Of("/tmp"),
@@ -458,6 +419,8 @@ func TestFillDefault(t *testing.T) {
 	}
 
 	expect = d
+	// VMType should remain nil when not explicitly set
+	expect.VMType = nil
 	// Also verify that archive arch is filled in
 	expect.Containerd.Archives = slices.Clone(d.Containerd.Archives)
 	expect.Containerd.Archives[0].Arch = *d.Arch
@@ -480,88 +443,78 @@ func TestFillDefault(t *testing.T) {
 	expect.HostResolver.Hosts = map[string]string{
 		"default": d.HostResolver.Hosts["default"],
 	}
-	expect.MountType = ptr.Of(VIRTIOFS)
+	// Remove driver-specific mount type from defaults test
+	expect.MountType = nil
 	expect.MountInotify = ptr.Of(false)
 	expect.CACertificates.RemoveDefaults = ptr.Of(true)
 	expect.CACertificates.Certs = []string{
 		"-----BEGIN CERTIFICATE-----\nYOUR-ORGS-TRUSTED-CA-CERT\n-----END CERTIFICATE-----\n",
 	}
 
-	if runtime.GOOS == "darwin" && IsNativeArch(AARCH64) {
-		expect.Rosetta = Rosetta{
-			Enabled: ptr.Of(true),
-			BinFmt:  ptr.Of(true),
-		}
-	} else {
-		expect.Rosetta = Rosetta{
-			Enabled: ptr.Of(false),
-			BinFmt:  ptr.Of(true),
-		}
-	}
 	expect.Plain = ptr.Of(false)
 
-	y = LimaYAML{}
-	FillDefault(t.Context(), &y, &d, &LimaYAML{}, filePath, false)
+	y = limatype.LimaYAML{}
+	FillDefault(t.Context(), &y, &d, &limatype.LimaYAML{}, filePath, false)
 	assert.DeepEqual(t, &y, &expect, opts...)
 
-	dExpect := expect
+	dExpected := expect
 
 	// ------------------------------------------------------------------------------------
 	// User-provided defaults should not override user-provided config values
 
 	y = filledDefaults
 	y.DNS = []net.IP{net.ParseIP("8.8.8.8")}
-	y.AdditionalDisks = []Disk{{Name: "overridden"}}
+	y.AdditionalDisks = []limatype.Disk{{Name: "overridden"}}
 	y.User.Home = ptr.Of("/root")
 
 	expect = y
 
-	expect.Provision = slices.Concat(y.Provision, dExpect.Provision)
-	expect.Probes = slices.Concat(y.Probes, dExpect.Probes)
-	expect.PortForwards = slices.Concat(y.PortForwards, dExpect.PortForwards)
-	expect.CopyToHost = slices.Concat(y.CopyToHost, dExpect.CopyToHost)
-	expect.Containerd.Archives = slices.Concat(y.Containerd.Archives, dExpect.Containerd.Archives)
+	expect.Provision = slices.Concat(y.Provision, dExpected.Provision)
+	expect.Probes = slices.Concat(y.Probes, dExpected.Probes)
+	expect.PortForwards = slices.Concat(y.PortForwards, dExpected.PortForwards)
+	expect.CopyToHost = slices.Concat(y.CopyToHost, dExpected.CopyToHost)
+	expect.Containerd.Archives = slices.Concat(y.Containerd.Archives, dExpected.Containerd.Archives)
 	expect.Containerd.Archives[2].Arch = *expect.Arch
-	expect.AdditionalDisks = slices.Concat(y.AdditionalDisks, dExpect.AdditionalDisks)
-	expect.Firmware.Images = slices.Concat(y.Firmware.Images, dExpect.Firmware.Images)
+	expect.AdditionalDisks = slices.Concat(y.AdditionalDisks, dExpected.AdditionalDisks)
+	expect.Firmware.Images = slices.Concat(y.Firmware.Images, dExpected.Firmware.Images)
 
 	// Mounts and Networks start with lowest priority first, so higher priority entries can overwrite
-	expect.Mounts = slices.Concat(dExpect.Mounts, y.Mounts)
-	expect.Networks = slices.Concat(dExpect.Networks, y.Networks)
+	expect.Mounts = slices.Concat(dExpected.Mounts, y.Mounts)
+	expect.Networks = slices.Concat(dExpected.Networks, y.Networks)
 
-	expect.HostResolver.Hosts["default"] = dExpect.HostResolver.Hosts["default"]
+	expect.HostResolver.Hosts["default"] = dExpected.HostResolver.Hosts["default"]
 
-	// dExpect.DNS will be ignored, and not appended to y.DNS
+	// dExpected.DNS will be ignored, and not appended to y.DNS
 
-	// "TWO" does not exist in filledDefaults.Env, so is set from dExpect.Env
-	expect.Env["TWO"] = dExpect.Env["TWO"]
+	// "TWO" does not exist in filledDefaults.Env, so is set from dExpected.Env
+	expect.Env["TWO"] = dExpected.Env["TWO"]
 
-	expect.Param["TWO"] = dExpect.Param["TWO"]
+	expect.Param["TWO"] = dExpected.Param["TWO"]
 
-	t.Logf("d.vmType=%q, y.vmType=%q, expect.vmType=%q", *d.VMType, *y.VMType, *expect.VMType)
+	t.Logf("d.vmType=%v, y.vmType=%v, expect.vmType=%v", d.VMType, y.VMType, expect.VMType)
 
-	FillDefault(t.Context(), &y, &d, &LimaYAML{}, filePath, false)
+	FillDefault(t.Context(), &y, &d, &limatype.LimaYAML{}, filePath, false)
 	assert.DeepEqual(t, &y, &expect, opts...)
 
 	// ------------------------------------------------------------------------------------
 	// User-provided overrides should override user-provided config settings
 
-	o = LimaYAML{
-		VMType: ptr.Of("qemu"),
-		OS:     ptr.Of(LINUX),
+	o = limatype.LimaYAML{
+		// Remove driver-specific VMType from override test
+		OS:     ptr.Of(limatype.LINUX),
 		Arch:   ptr.Of(arch),
 		CPUs:   ptr.Of(12),
 		Memory: ptr.Of("7GiB"),
 		Disk:   ptr.Of("117GiB"),
-		AdditionalDisks: []Disk{
+		AdditionalDisks: []limatype.Disk{
 			{Name: "test"},
 		},
 		GuestInstallPrefix: ptr.Of("/usr"),
 		UpgradePackages:    ptr.Of(true),
-		Containerd: Containerd{
+		Containerd: limatype.Containerd{
 			System: ptr.Of(true),
 			User:   ptr.Of(false),
-			Archives: []File{
+			Archives: []limatype.File{
 				{
 					Arch:     arch,
 					Location: "/tmp/nerdctl.tgz",
@@ -569,7 +522,7 @@ func TestFillDefault(t *testing.T) {
 				},
 			},
 		},
-		SSH: SSH{
+		SSH: limatype.SSH{
 			LocalPort:         ptr.Of(4433),
 			LoadDotSSHPubKeys: ptr.Of(true),
 			ForwardAgent:      ptr.Of(true),
@@ -577,19 +530,17 @@ func TestFillDefault(t *testing.T) {
 			ForwardX11Trusted: ptr.Of(false),
 		},
 		TimeZone: ptr.Of("Universal"),
-		Firmware: Firmware{
+		Firmware: limatype.Firmware{
 			LegacyBIOS: ptr.Of(true),
 		},
-		Audio: Audio{
+		Audio: limatype.Audio{
 			Device: ptr.Of("coreaudio"),
 		},
-		Video: Video{
+		Video: limatype.Video{
 			Display: ptr.Of("cocoa"),
-			VNC: VNCOptions{
-				Display: ptr.Of("none"),
-			},
+			// Remove driver-specific VNC configuration
 		},
-		HostResolver: HostResolver{
+		HostResolver: limatype.HostResolver{
 			Enabled: ptr.Of(false),
 			IPv6:    ptr.Of(false),
 			Hosts: map[string]string{
@@ -598,40 +549,40 @@ func TestFillDefault(t *testing.T) {
 		},
 		PropagateProxyEnv: ptr.Of(false),
 
-		Mounts: []Mount{
+		Mounts: []limatype.Mount{
 			{
 				Location: varLog,
 				Writable: ptr.Of(true),
-				SSHFS: SSHFS{
+				SSHFS: limatype.SSHFS{
 					Cache:          ptr.Of(false),
 					FollowSymlinks: ptr.Of(true),
 				},
-				NineP: NineP{
+				NineP: limatype.NineP{
 					SecurityModel:   ptr.Of("mapped-file"),
 					ProtocolVersion: ptr.Of("9p2000"),
 					Msize:           ptr.Of("8KiB"),
 					Cache:           ptr.Of("none"),
 				},
-				Virtiofs: Virtiofs{
+				Virtiofs: limatype.Virtiofs{
 					QueueSize: ptr.Of(2048),
 				},
 			},
 		},
 		MountInotify: ptr.Of(true),
-		Provision: []Provision{
+		Provision: []limatype.Provision{
 			{
 				Script: "#!/bin/true",
-				Mode:   ProvisionModeSystem,
+				Mode:   limatype.ProvisionModeSystem,
 			},
 		},
-		Probes: []Probe{
+		Probes: []limatype.Probe{
 			{
 				Script:      "#!/bin/false",
-				Mode:        ProbeModeReadiness,
+				Mode:        limatype.ProbeModeReadiness,
 				Description: "Another Probe",
 			},
 		},
-		Networks: []Network{
+		Networks: []limatype.Network{
 			{
 				Lima:       "shared",
 				MACAddress: "10:20:30:40:50:60",
@@ -646,16 +597,16 @@ func TestFillDefault(t *testing.T) {
 		DNS: []net.IP{
 			net.ParseIP("2.2.2.2"),
 		},
-		PortForwards: []PortForward{{
+		PortForwards: []limatype.PortForward{{
 			GuestIP:        IPv4loopback1,
 			GuestPort:      88,
 			GuestPortRange: [2]int{88, 88},
 			HostIP:         IPv4loopback1,
 			HostPort:       8080,
 			HostPortRange:  [2]int{8080, 8080},
-			Proto:          ProtoTCP,
+			Proto:          limatype.ProtoTCP,
 		}},
-		CopyToHost: []CopyToHost{{}},
+		CopyToHost: []limatype.CopyToHost{{}},
 		Env: map[string]string{
 			"TWO":   "deux",
 			"THREE": "trois",
@@ -664,15 +615,11 @@ func TestFillDefault(t *testing.T) {
 			"TWO":   "deux",
 			"THREE": "trois",
 		},
-		CACertificates: CACertificates{
+		CACertificates: limatype.CACertificates{
 			RemoveDefaults: ptr.Of(true),
 		},
-		Rosetta: Rosetta{
-			Enabled: ptr.Of(false),
-			BinFmt:  ptr.Of(false),
-		},
 		NestedVirtualization: ptr.Of(false),
-		User: User{
+		User: limatype.User{
 			Name:    ptr.Of("foo"),
 			Comment: ptr.Of("foo bar baz"),
 			Home:    ptr.Of("/override"),
@@ -685,20 +632,20 @@ func TestFillDefault(t *testing.T) {
 
 	expect = o
 
-	expect.Provision = slices.Concat(o.Provision, y.Provision, dExpect.Provision)
-	expect.Probes = slices.Concat(o.Probes, y.Probes, dExpect.Probes)
-	expect.PortForwards = slices.Concat(o.PortForwards, y.PortForwards, dExpect.PortForwards)
-	expect.CopyToHost = slices.Concat(o.CopyToHost, y.CopyToHost, dExpect.CopyToHost)
-	expect.Containerd.Archives = slices.Concat(o.Containerd.Archives, y.Containerd.Archives, dExpect.Containerd.Archives)
+	expect.Provision = slices.Concat(o.Provision, y.Provision, dExpected.Provision)
+	expect.Probes = slices.Concat(o.Probes, y.Probes, dExpected.Probes)
+	expect.PortForwards = slices.Concat(o.PortForwards, y.PortForwards, dExpected.PortForwards)
+	expect.CopyToHost = slices.Concat(o.CopyToHost, y.CopyToHost, dExpected.CopyToHost)
+	expect.Containerd.Archives = slices.Concat(o.Containerd.Archives, y.Containerd.Archives, dExpected.Containerd.Archives)
 	expect.Containerd.Archives[3].Arch = *expect.Arch
-	expect.AdditionalDisks = slices.Concat(o.AdditionalDisks, y.AdditionalDisks, dExpect.AdditionalDisks)
-	expect.Firmware.Images = slices.Concat(o.Firmware.Images, y.Firmware.Images, dExpect.Firmware.Images)
+	expect.AdditionalDisks = slices.Concat(o.AdditionalDisks, y.AdditionalDisks, dExpected.AdditionalDisks)
+	expect.Firmware.Images = slices.Concat(o.Firmware.Images, y.Firmware.Images, dExpected.Firmware.Images)
 
-	expect.HostResolver.Hosts["default"] = dExpect.HostResolver.Hosts["default"]
-	expect.HostResolver.Hosts["MY.Host"] = dExpect.HostResolver.Hosts["host.lima.internal"]
+	expect.HostResolver.Hosts["default"] = dExpected.HostResolver.Hosts["default"]
+	expect.HostResolver.Hosts["MY.Host"] = dExpected.HostResolver.Hosts["host.lima.internal"]
 
-	// o.Mounts just makes dExpect.Mounts[0] writable because the Location matches
-	expect.Mounts = slices.Concat(dExpect.Mounts, y.Mounts)
+	// o.Mounts just makes dExpected.Mounts[0] writable because the Location matches
+	expect.Mounts = slices.Concat(dExpected.Mounts, y.Mounts)
 	expect.Mounts[0].Writable = ptr.Of(true)
 	expect.Mounts[0].SSHFS.Cache = ptr.Of(false)
 	expect.Mounts[0].SSHFS.FollowSymlinks = ptr.Of(true)
@@ -708,11 +655,11 @@ func TestFillDefault(t *testing.T) {
 	expect.Mounts[0].NineP.Cache = ptr.Of("none")
 	expect.Mounts[0].Virtiofs.QueueSize = ptr.Of(2048)
 
-	expect.MountType = ptr.Of(NINEP)
+	expect.MountType = ptr.Of(limatype.NINEP)
 	expect.MountInotify = ptr.Of(true)
 
-	// o.Networks[1] is overriding the dExpect.Networks[0].Lima entry for the "def0" interface
-	expect.Networks = slices.Concat(dExpect.Networks, y.Networks, []Network{o.Networks[0]})
+	// o.Networks[1] is overriding the dExpected.Networks[0].Lima entry for the "def0" interface
+	expect.Networks = slices.Concat(dExpected.Networks, y.Networks, []limatype.Network{o.Networks[0]})
 	expect.Networks[0].Lima = o.Networks[1].Lima
 
 	// Only highest prio DNS are retained
@@ -729,10 +676,6 @@ func TestFillDefault(t *testing.T) {
 		"-----BEGIN CERTIFICATE-----\nYOUR-ORGS-TRUSTED-CA-CERT\n-----END CERTIFICATE-----\n",
 	}
 
-	expect.Rosetta = Rosetta{
-		Enabled: ptr.Of(false),
-		BinFmt:  ptr.Of(false),
-	}
 	expect.Plain = ptr.Of(false)
 
 	expect.NestedVirtualization = ptr.Of(false)
diff --git a/pkg/limayaml/limayaml_test.go b/pkg/limayaml/limayaml_test.go
index 2fbeebf0..97fd8bae 100644
--- a/pkg/limayaml/limayaml_test.go
+++ b/pkg/limayaml/limayaml_test.go
@@ -12,6 +12,8 @@ import (
 	"testing"
 
 	"gotest.tools/v3/assert"
+
+	"github.com/lima-vm/lima/v2/pkg/limatype"
 )
 
 func dumpJSON(t *testing.T, d any) string {
@@ -23,7 +25,7 @@ func dumpJSON(t *testing.T, d any) string {
 const emptyYAML = "{}\n"
 
 func TestEmptyYAML(t *testing.T) {
-	var y LimaYAML
+	var y limatype.LimaYAML
 	t.Log(dumpJSON(t, y))
 	b, err := Marshal(&y, false)
 	assert.NilError(t, err)
@@ -43,7 +45,7 @@ func TestDefaultYAML(t *testing.T) {
 		assert.NilError(t, err)
 	}
 
-	var y LimaYAML
+	var y limatype.LimaYAML
 	err = Unmarshal(content, &y, "")
 	assert.NilError(t, err)
 	y.Images = nil                // remove default images
diff --git a/pkg/limayaml/marshal_test.go b/pkg/limayaml/marshal_test.go
index 7e778ae0..4ad8f3ab 100644
--- a/pkg/limayaml/marshal_test.go
+++ b/pkg/limayaml/marshal_test.go
@@ -8,17 +8,18 @@ import (
 
 	"gotest.tools/v3/assert"
 
+	"github.com/lima-vm/lima/v2/pkg/limatype"
 	"github.com/lima-vm/lima/v2/pkg/ptr"
 )
 
 func TestMarshalEmpty(t *testing.T) {
-	_, err := Marshal(&LimaYAML{}, false)
+	_, err := Marshal(&limatype.LimaYAML{}, false)
 	assert.NilError(t, err)
 }
 
 func TestMarshalTilde(t *testing.T) {
-	y := LimaYAML{
-		Mounts: []Mount{
+	y := limatype.LimaYAML{
+		Mounts: []limatype.Mount{
 			{Location: "~", Writable: ptr.Of(false)},
 			{Location: "/tmp/lima", Writable: ptr.Of(true)},
 			{Location: "null"},
diff --git a/pkg/limayaml/validate_test.go b/pkg/limayaml/validate_test.go
index 7cb1755c..0eca03df 100644
--- a/pkg/limayaml/validate_test.go
+++ b/pkg/limayaml/validate_test.go
@@ -4,11 +4,12 @@
 package limayaml
 
 import (
-	"errors"
+	"fmt"
 	"testing"
 
 	"gotest.tools/v3/assert"
 
+	"github.com/lima-vm/lima/v2/pkg/limatype"
 	"github.com/lima-vm/lima/v2/pkg/version"
 )
 
@@ -312,7 +313,6 @@ func TestValidateMultipleErrors(t *testing.T) {
 	yamlWithMultipleErrors := `
 os: windows
 arch: unsupported_arch
-vmType: invalid_type
 portForwards:
   - guestPort: 22
     hostPort: 2222
@@ -328,10 +328,10 @@ provision:
 	y, err := Load(t.Context(), []byte(yamlWithMultipleErrors), "multiple-errors.yaml")
 	assert.NilError(t, err)
 	err = Validate(y, false)
+	t.Logf("Validation errors: %v", err)
 
 	assert.Error(t, err, "field `os` must be \"Linux\"; got \"windows\"\n"+
 		"field `arch` must be one of [x86_64 aarch64 armv7l ppc64le riscv64 s390x]; got \"unsupported_arch\"\n"+
-		"field `vmType` must be \"qemu\", \"vz\", \"wsl2\"; got \"invalid_type\"\n"+
 		"field `images` must be set\n"+
 		"field `provision[0].mode` must one of \"system\", \"user\", \"boot\", \"data\", \"dependency\", or \"ansible\"\n"+
 		"field `provision[1].path` must not be empty when mode is \"data\"")
@@ -342,49 +342,51 @@ func TestValidateAgainstLatestConfig(t *testing.T) {
 		name    string
 		yNew    string
 		yLatest string
-		wantErr error
+		wantErr string
 	}{
 		{
 			name:    "Valid disk size unchanged",
 			yNew:    `disk: 100GiB`,
 			yLatest: `disk: 100GiB`,
+			wantErr: fmt.Sprintf("failed to resolve vm for \"\": vmType %q is not a registered driver", limatype.DefaultDriver()),
 		},
 		{
 			name:    "Valid disk size increased",
 			yNew:    `disk: 200GiB`,
 			yLatest: `disk: 100GiB`,
+			wantErr: fmt.Sprintf("failed to resolve vm for \"\": vmType %q is not a registered driver", limatype.DefaultDriver()),
 		},
 		{
 			name:    "No disk field in both YAMLs",
 			yNew:    ``,
 			yLatest: ``,
+			wantErr: fmt.Sprintf("failed to resolve vm for \"\": vmType %q is not a registered driver", limatype.DefaultDriver()),
 		},
 		{
 			name:    "No disk field in new YAMLs",
 			yNew:    ``,
 			yLatest: `disk: 100GiB`,
+			wantErr: fmt.Sprintf("failed to resolve vm for \"\": vmType %q is not a registered driver", limatype.DefaultDriver()),
 		},
 		{
 			name:    "No disk field in latest YAMLs",
 			yNew:    `disk: 100GiB`,
 			yLatest: ``,
+			wantErr: fmt.Sprintf("failed to resolve vm for \"\": vmType %q is not a registered driver", limatype.DefaultDriver()),
 		},
 		{
 			name:    "Disk size shrunk",
 			yNew:    `disk: 50GiB`,
 			yLatest: `disk: 100GiB`,
-			wantErr: errors.New("field `disk`: shrinking the disk (100GiB --> 50GiB) is not supported"),
+			wantErr: fmt.Sprintf("failed to resolve vm for \"\": vmType %q is not a registered driver\n", limatype.DefaultDriver()) +
+				"field `disk`: shrinking the disk (100GiB --> 50GiB) is not supported",
 		},
 	}
 
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
 			err := ValidateAgainstLatestConfig(t.Context(), []byte(tt.yNew), []byte(tt.yLatest))
-			if tt.wantErr == nil {
-				assert.NilError(t, err)
-			} else {
-				assert.Error(t, err, tt.wantErr.Error())
-			}
+			assert.Error(t, err, tt.wantErr)
 		})
 	}
 }
diff --git a/pkg/networks/commands_test.go b/pkg/networks/commands_test.go
index 2f9877e6..4d5667d9 100644
--- a/pkg/networks/commands_test.go
+++ b/pkg/networks/commands_test.go
@@ -10,7 +10,7 @@ import (
 
 	"gotest.tools/v3/assert"
 
-	"github.com/lima-vm/lima/v2/pkg/store/dirnames"
+	"github.com/lima-vm/lima/v2/pkg/limatype/dirnames"
 )
 
 func TestCheck(t *testing.T) {
diff --git a/pkg/registry/registry_test.go b/pkg/registry/registry_test.go
index c872e62d..d75565af 100644
--- a/pkg/registry/registry_test.go
+++ b/pkg/registry/registry_test.go
@@ -14,7 +14,7 @@ import (
 	"gotest.tools/v3/assert"
 
 	"github.com/lima-vm/lima/v2/pkg/driver"
-	"github.com/lima-vm/lima/v2/pkg/store"
+	"github.com/lima-vm/lima/v2/pkg/limatype"
 )
 
 type mockDriver struct {
@@ -28,7 +28,8 @@ func newMockDriver(name string) *mockDriver {
 var _ driver.Driver = (*mockDriver)(nil)
 
 func (m *mockDriver) Validate(_ context.Context) error                           { return nil }
-func (m *mockDriver) Initialize(_ context.Context) error                         { return nil }
+func (m *mockDriver) Create(_ context.Context) error                             { return nil }
+func (m *mockDriver) Delete(_ context.Context) error                             { return nil }
 func (m *mockDriver) CreateDisk(_ context.Context) error                         { return nil }
 func (m *mockDriver) Start(_ context.Context) (chan error, error)                { return nil, nil }
 func (m *mockDriver) Stop(_ context.Context) error                               { return nil }
@@ -43,8 +44,15 @@ func (m *mockDriver) Register(_ context.Context) error
 func (m *mockDriver) Unregister(_ context.Context) error                         { return nil }
 func (m *mockDriver) ForwardGuestAgent() bool                                    { return false }
 func (m *mockDriver) GuestAgentConn(_ context.Context) (net.Conn, string, error) { return nil, "", nil }
-func (m *mockDriver) Info() driver.Info                                          { return driver.Info{DriverName: m.Name} }
-func (m *mockDriver) Configure(_ *store.Instance) *driver.ConfiguredDriver       { return nil }
+func (m *mockDriver) Info() driver.Info {
+	return driver.Info{Features: driver.DriverFeatures{DriverName: m.Name}}
+}
+func (m *mockDriver) Configure(_ *limatype.Instance) *driver.ConfiguredDriver      { return nil }
+func (m *mockDriver) AcceptConfig(_ *limatype.LimaYAML, _ string) error            { return nil }
+func (m *mockDriver) FillConfig(_ *limatype.LimaYAML, _ string) error              { return nil }
+func (m *mockDriver) InspectStatus(_ context.Context, _ *limatype.Instance) string { return "" }
+func (m *mockDriver) SSHAddress(_ context.Context) (string, error)                 { return "", nil }
+func (m *mockDriver) BootScripts() (map[string][]byte, error)                      { return nil, nil }
 
 func TestRegister(t *testing.T) {
 	BackupRegistry(t)
@@ -72,7 +80,7 @@ func TestRegister(t *testing.T) {
 	assert.Equal(t, exists, true)
 	assert.Assert(t, extDriver == nil)
 	assert.Assert(t, intDriver != nil)
-	assert.Equal(t, intDriver.Info().DriverName, "test-driver")
+	assert.Equal(t, intDriver.Info().Features.DriverName, "test-driver")
 
 	vmTypes := List()
 	assert.Equal(t, vmTypes["test-driver-2"], Internal)
diff --git a/pkg/store/fuzz_test.go b/pkg/store/fuzz_test.go
index 62b007d5..7f1083ee 100644
--- a/pkg/store/fuzz_test.go
+++ b/pkg/store/fuzz_test.go
@@ -10,7 +10,7 @@ import (
 
 	"gotest.tools/v3/assert"
 
-	"github.com/lima-vm/lima/v2/pkg/store/filenames"
+	"github.com/lima-vm/lima/v2/pkg/limatype/filenames"
 )
 
 func FuzzLoadYAMLByFilePath(f *testing.F) {
diff --git a/pkg/store/instance_test.go b/pkg/store/instance_test.go
index 55b7e57b..28ffcb7a 100644
--- a/pkg/store/instance_test.go
+++ b/pkg/store/instance_test.go
@@ -13,20 +13,20 @@ import (
 
 	"gotest.tools/v3/assert"
 
-	"github.com/lima-vm/lima/v2/pkg/limayaml"
+	"github.com/lima-vm/lima/v2/pkg/limatype"
 )
 
 const separator = string(filepath.Separator)
 
 var (
-	vmtype = limayaml.QEMU
-	goarch = limayaml.NewArch(runtime.GOARCH)
+	vmtype = limatype.QEMU
+	goarch = limatype.NewArch(runtime.GOARCH)
 	space  = strings.Repeat(" ", len(goarch)-4)
 )
 
-var instance = Instance{
+var instance = limatype.Instance{
 	Name:       "foo",
-	Status:     StatusStopped,
+	Status:     limatype.StatusStopped,
 	VMType:     vmtype,
 	Arch:       goarch,
 	Dir:        "dir",
@@ -68,7 +68,7 @@ var tableTwo = "NAME    STATUS     SSH            VMTYPE    ARCH       CPUS    M
 
 func TestPrintInstanceTable(t *testing.T) {
 	var buf bytes.Buffer
-	instances := []*Instance{&instance}
+	instances := []*limatype.Instance{&instance}
 	err := PrintInstances(&buf, instances, "table", nil)
 	assert.NilError(t, err)
 	assert.Equal(t, table, buf.String())
@@ -78,7 +78,7 @@ func TestPrintInstanceTableEmu(t *testing.T) {
 	var buf bytes.Buffer
 	instance1 := instance
 	instance1.Arch = "unknown"
-	instances := []*Instance{&instance1}
+	instances := []*limatype.Instance{&instance1}
 	err := PrintInstances(&buf, instances, "table", nil)
 	assert.NilError(t, err)
 	assert.Equal(t, tableEmu, buf.String())
@@ -90,7 +90,7 @@ func TestPrintInstanceTableHome(t *testing.T) {
 	assert.NilError(t, err)
 	instance1 := instance
 	instance1.Dir = filepath.Join(homeDir, "dir")
-	instances := []*Instance{&instance1}
+	instances := []*limatype.Instance{&instance1}
 	err = PrintInstances(&buf, instances, "table", nil)
 	assert.NilError(t, err)
 	assert.Equal(t, tableHome, buf.String())
@@ -98,7 +98,7 @@ func TestPrintInstanceTableHome(t *testing.T) {
 
 func TestPrintInstanceTable60(t *testing.T) {
 	var buf bytes.Buffer
-	instances := []*Instance{&instance}
+	instances := []*limatype.Instance{&instance}
 	options := PrintOptions{TerminalWidth: 60}
 	err := PrintInstances(&buf, instances, "table", &options)
 	assert.NilError(t, err)
@@ -107,7 +107,7 @@ func TestPrintInstanceTable60(t *testing.T) {
 
 func TestPrintInstanceTable80SameArch(t *testing.T) {
 	var buf bytes.Buffer
-	instances := []*Instance{&instance}
+	instances := []*limatype.Instance{&instance}
 	options := PrintOptions{TerminalWidth: 80}
 	err := PrintInstances(&buf, instances, "table", &options)
 	assert.NilError(t, err)
@@ -117,8 +117,8 @@ func TestPrintInstanceTable80SameArch(t *testing.T) {
 func TestPrintInstanceTable80DiffArch(t *testing.T) {
 	var buf bytes.Buffer
 	instance1 := instance
-	instance1.Arch = limayaml.NewArch("unknown")
-	instances := []*Instance{&instance1}
+	instance1.Arch = limatype.NewArch("unknown")
+	instances := []*limatype.Instance{&instance1}
 	options := PrintOptions{TerminalWidth: 80}
 	err := PrintInstances(&buf, instances, "table", &options)
 	assert.NilError(t, err)
@@ -127,7 +127,7 @@ func TestPrintInstanceTable80DiffArch(t *testing.T) {
 
 func TestPrintInstanceTable100(t *testing.T) {
 	var buf bytes.Buffer
-	instances := []*Instance{&instance}
+	instances := []*limatype.Instance{&instance}
 	options := PrintOptions{TerminalWidth: 100}
 	err := PrintInstances(&buf, instances, "table", &options)
 	assert.NilError(t, err)
@@ -136,7 +136,7 @@ func TestPrintInstanceTable100(t *testing.T) {
 
 func TestPrintInstanceTableAll(t *testing.T) {
 	var buf bytes.Buffer
-	instances := []*Instance{&instance}
+	instances := []*limatype.Instance{&instance}
 	options := PrintOptions{TerminalWidth: 40, AllFields: true}
 	err := PrintInstances(&buf, instances, "table", &options)
 	assert.NilError(t, err)
@@ -147,13 +147,13 @@ func TestPrintInstanceTableTwo(t *testing.T) {
 	var buf bytes.Buffer
 	instance1 := instance
 	instance1.Name = "foo"
-	instance1.VMType = limayaml.QEMU
-	instance1.Arch = limayaml.X8664
+	instance1.VMType = limatype.QEMU
+	instance1.Arch = limatype.X8664
 	instance2 := instance
 	instance2.Name = "bar"
-	instance2.VMType = limayaml.VZ
-	instance2.Arch = limayaml.AARCH64
-	instances := []*Instance{&instance1, &instance2}
+	instance2.VMType = limatype.VZ
+	instance2.Arch = limatype.AARCH64
+	instances := []*limatype.Instance{&instance1, &instance2}
 	options := PrintOptions{TerminalWidth: 80}
 	err := PrintInstances(&buf, instances, "table", &options)
 	assert.NilError(t, err)
diff --git a/pkg/store/store_test.go b/pkg/store/store_test.go
index 4dfe1eea..9879bf25 100644
--- a/pkg/store/store_test.go
+++ b/pkg/store/store_test.go
@@ -7,6 +7,8 @@ import (
 	"testing"
 
 	"gotest.tools/v3/assert"
+
+	"github.com/lima-vm/lima/v2/pkg/limatype/dirnames"
 )
 
 func TestValidateInstName(t *testing.T) {
@@ -22,7 +24,7 @@ func TestValidateInstName(t *testing.T) {
 	}
 	for _, arg := range instNames {
 		t.Run(arg, func(t *testing.T) {
-			err := ValidateInstName(arg)
+			err := dirnames.ValidateInstName(arg)
 			assert.NilError(t, err)
 		})
 	}
@@ -43,7 +45,7 @@ func TestValidateInstName(t *testing.T) {
 	}
 	for _, arg := range invalidIdentifiers {
 		t.Run(arg, func(t *testing.T) {
-			err := ValidateInstName(arg)
+			err := dirnames.ValidateInstName(arg)
 			assert.ErrorContains(t, err, "not a valid identifier")
 		})
 	}
@@ -54,7 +56,7 @@ func TestValidateInstName(t *testing.T) {
 	}
 	for _, arg := range yamlNames {
 		t.Run(arg, func(t *testing.T) {
-			err := ValidateInstName(arg)
+			err := dirnames.ValidateInstName(arg)
 			assert.ErrorContains(t, err, "must not end with .y")
 		})
 	}
diff --git a/pkg/templatestore/templatestore.go b/pkg/templatestore/templatestore.go
index a61eeb2c..9591a59a 100644
--- a/pkg/templatestore/templatestore.go
+++ b/pkg/templatestore/templatestore.go
@@ -14,7 +14,7 @@ import (
 	"strings"
 	"unicode"
 
-	"github.com/lima-vm/lima/v2/pkg/store/dirnames"
+	"github.com/lima-vm/lima/v2/pkg/limatype/dirnames"
 	"github.com/lima-vm/lima/v2/pkg/usrlocalsharelima"
 )