Declare intent
Define a VM through the same versioned API used by the web UI and CLI.
Private infrastructure. Public contract.
A modern control plane and host agent for operating VMs on Linux + KVM—built in Rust, driven by an enforced API, and designed to recover from the failures real systems have.
01 / THE PLATFORM
nextVIRT separates intent from execution. The control plane records what should exist; host agents report what does. Reconciliation closes the gap without pretending failures never happen.
Define a VM through the same versioned API used by the web UI and CLI.
Long-running work is recorded before side effects begin, with progress and ownership.
The host agent owns VM processes, disks, networking, and local safety.
Observed state wins. Inventory and events show what happened—not what was hoped for.
The REST API is the product. The UI, CLI, and control plane are all clients, with build-time guardrails that catch undocumented routes and boundary violations.
Both processes assume they can be interrupted at any point. State is written first, effects are idempotent, and startup reconciliation leaves a record instead of a mystery.
Every important transition becomes a task or event. Operate visually, automate through the API, or compose fleet-wide workflows with native Nushell pipelines.
02 / ARCHITECTURE
A single control plane owns inventory, placement, policy, tasks, and the public API. A focused agent on each host owns execution. The boundary between them is explicit, versioned, and testable.
03 / INSIDE THE STACK
nextVIRT is a composed systems stack, not a wrapper around shell scripts. The control plane, agent, clients, contracts, and operator surfaces share typed models while keeping execution boundaries deliberately narrow.
RUNTIME
Each VM is a detached process on a Linux host. Direct-kernel guests run on Cloud Hypervisor v48 through its HTTP-over-Unix-socket API; firmware guests run on QEMU through QMP for UEFI, graphics, and VNC. Both sit behind the same host-side hypervisor interface.
CONTROL PLANE
Rust 1.97, Tokio, and Axum power the API, task engine, scheduler, poller, event feed, and embedded product UI. Utoipa generates the OpenAPI document; the committed snapshot is checked against registered routes so contract drift fails the build.
STATE + STORAGE
Inventory and operations live in SQLite through SQLx, with WAL mode, foreign keys, embedded migrations, and a single database-owning crate. Local, shared, and object-backed disks keep their native semantics; NuFinder provides the content-addressed volume path for backups and recoverable object storage.
NETWORKING
Host-local networks use Linux bridges, deterministic tap names, and bridge VLAN filtering. Cluster overlays use OVN logical switches and routers over Geneve, with OVS port identity and distributed ACL machinery at the edge.
TRUST + SAFETY
Browser sessions use CSRF protection and capability-based authorization. Automation uses scoped API keys. Control-plane-to-agent traffic can use rustls mTLS with host UUIDs as identities, avoiding trust in addresses that can move.
OPERATOR SURFACES
The React UI, Rust client, and embedded Nushell environment all consume the same API. The UI is baked into the control-plane binary; the shell turns API resources into native dates, durations, filesizes, records, and streams instead of flattening them to text.
NUFINDER VOLUME ENGINE
nfd, the NuFinder daemon, is built into nextVIRT’s storage path. The host agent drives it as an isolated sidecar, turning VM disks and memory snapshots into immutable manifests of verified 4 MiB chunks. The result is incremental-forever backup, fleet-wide deduplication, and recovery from any host that can reach the volume.
WRITE + RECOVERY PATH
Only changed chunks upload. Identical blocks—even across VMs—resolve to the same immutable object.
SHA-256 content addressing, per-chunk verification, signed manifests, journal replay, and scheduled scrub.
Versioned bindings, leases, retention, garbage collection, and S3 Object Lock separate recovery from deletion.
Pin a manifest, expose it through NBD, and start the VM on another host with a local COW write layer.
The backup, retention, scrub, manifest pinning, and NBD-backed recovery paths are implemented and exercised against real MinIO and KVM hosts. Production scheduling and storage hardening remain active engineering.
THE ENGINEERING GATE
Formatting, Clippy with warnings denied, the complete Rust test suite, documentation links, TypeScript, generated API types, and Vitest all run as one gate. The workspace denies unsafe Rust, and boundary scanners reject SQL, process spawning, host paths, or HTTP clients in the wrong layer.
cargo fmt --checkPASScargo clippy -D warningsPASScargo test --workspace896 testsopenapi snapshot + api typesLOCKEDtsc + vitestPASS04 / OPERATIONS
The nextVIRT shell embeds Nushell, so resources are typed records—not strings to scrape. Filter them, transform them, and pipe them directly into safe mutations.
# Find quiet running VMs, then stop them as one bounded operation.
production〉 show vms --power running | where cpu_percent < 5 | select name host power cpu_percent memory
name host power cpu_percent memory
alpine-1 kvm-1 running 1.4% 512 MiB
build-runner kvm-2 running 3.1% 8.0 GiB
web-edge kvm-2 running 0.8% 2.0 GiB
production〉 $in | vm stop --no-wait --parallel 8 | task wait
✓ 3 tasks succeeded in 4.8sec
# Records carry native dates and filesizes—not preformatted strings.
production〉 show hosts | select name state memory_available last_seen
name state memory_available last_seen
kvm-1 online 38.2 GiB 2026-09-18 15:42:08
kvm-2 online 51.7 GiB 2026-09-18 15:42:09
kvm-3 online 91.4 GiB 2026-09-18 15:42:07
production〉 watch events | where ($in.data.kind == "host.offline") | first 1
◉ waiting for the next matching structured event…
# Filesize literals make capacity checks readable and exact.
production〉 show datastores | where ($it.available < 10gb) | select name kind available
name kind available
local-fast zfs 7.8 GiB
production〉 show datastores | datastore refresh | select name available
✓ capacity refreshed across 3 datastores
# Inventory is data; provisioning uses the same names operators see.
production〉 show networks | select name bridge vlan
name bridge vlan
management br0 —
production br0 100
storage br1 240
production〉 network add tenant-42 --host kvm-1 --bridge br0 --vlan 142
✓ network tenant-42 registered on kvm-1
# Snapshot before a risky change; recovery remains an auditable task.
production〉 vm snapshot create web-01 before-upgrade --live | task wait
✓ snapshot before-upgrade created
production〉 show events --all | where kind =~ "vm.error" | first 3
time kind resource
15:47:12 vm.error web-01
production〉 vm snapshot revert web-01 before-upgrade --yes | task wait
✓ web-01 restored to before-upgrade
# Fan in every active task, keep only failures, emit machine-readable JSON.
production〉 show tasks --state running --all |
task wait --parallel 8 --timeout 30min |
where state != "succeeded" | select id kind target error | to json
[]
# The same pipeline runs non-interactively from CI or a timer.
$ nextvirt -c 'show tasks --state failed --all | to json'
✓ exit 0 · valid JSON on stdout
# Provision with real filesize values, wait, then return the created VM.
production〉 vm create web-02 --image alpine --datastore tank --network prod --memory 4gb |
task wait | task result | select name power host memory
name power host memory
web-02 running kvm-2 4.0 GB
# 4gb is decimal; 4gib is binary. The distinction survives the API.
✓ VM created and task result resolved
# Evacuate kvm-1 one VM at a time with live migration.
production〉 show vms --host kvm-1 |
vm migrate --to kvm-2 --live --parallel 1 | task wait
target kind state elapsed
api-01 vm_migrate succeeded 8.2sec
worker-03 vm_migrate succeeded 11.7sec
metrics-01 vm_migrate succeeded 6.9sec
✓ kvm-1 evacuated without parallel migration pressure
# A mutation returns a task; task result returns the resource it created.
production〉 vm clone web-01 web-02 | task wait | task result
name power cpus memory source
web-02 stopped 4 8.0 GB web-01
production〉 show vm web-02 | get disks | select name size datastore
✓ clone inventory and disks are immediately pipeable
# Preview every request a destructive pipeline would send.
production〉 show vms --power stopped | vm remove --dry-run |
select method path target.name
method path target.name
DELETE /api/v1/vms/4b1… old-runner
DELETE /api/v1/vms/91e… test-db
DRY RUN · no requests sent · server state not inspected
# Query several control planes without changing the active context.
production〉 [lab prod] | each {|ctx| context with $ctx { show hosts } } | flatten |
select context name state
context name state
lab kvm-lab-1 online
prod kvm-1 online
prod kvm-2 online
✓ active context restored to production
# Serialize upgrades across online hosts and wait for every task.
production〉 show hosts | where state == "online" |
host upgrade --parallel 1 | task wait
✓ 3 hosts upgraded sequentially
# Scoped maintenance and an HA-safe planned restart.
production〉 show hosts --cluster edge | host maintenance enter
production〉 host expect-restart kvm-1 --within 10min
◉ HA fencing suppressed for the declared restart window
# Diagnostics are records, so healthy checks disappear from the view.
production〉 doctor | where status != "ok"
check status detail
notifications warn WebSocket reconnecting
# The binary reports its compiled feature surface—including omissions.
production〉 version features | where not available
filesystem false restricted operator profile
# Switch from the operator projection to canonical OpenAPI field names.
production〉 show hosts --api | first | columns
[id, name, address, state, maintenance, capacity, last_seen_at, …]
# Formatting is the final pipeline stage, not a separate command path.
production〉 show vms | select name host power | sort-by name | to json
[{"name":"api-01","host":"kvm-2","power":"running"}, …]
✓ same records · machine-readable output
NATIVE VALUES dates · durations · filesizes · records · tables
PIPELINE TOOLS where · sort-by · select · get · group-by · first · length · to json
EXECUTION interactive REPL · nextvirt -c · scripts on stdin
05 / ENGINEERING STATUS
nextVIRT is under active engineering. Core behavior is tested through contract checks, deterministic agent simulations, and real KVM infrastructure—not only mocked happy paths.