arcbox-vm-driver 0.10.0

The VM driver port: the vocabulary of a VM on this host — spec, driver/handle traits, capabilities, guest-network port — that VMM adapters implement and orchestrators consume.
Documentation

arcbox-vm-driver

The VM driver port: the vocabulary of "a VM on this host" — the serializable VmSpec, the VmDriver/VmHandle traits, the capability traits, and the GuestNetwork port — that VMM adapters (Firecracker, Virtualization.framework, the in-process HV engine, Cloud Hypervisor) implement and orchestrators consume. No arcbox-* dependency: it sits below every adapter, and only a composition root names both sides.

The mandatory surface

VmDriver boots a VmSpec into a Box<dyn VmHandle> (or restores one from a CheckpointImage). A handle's mandatory verbs are:

Verb What it is
id() / record() identify — the VmRecord is durable and stable for the handle's life
state() / events() observe — Running / Quiesced / Exited(status); Exited is broadcast once; neither blocks
shutdown(mode) stop — Kill always succeeds; Graceful { timeout } asks the guest and kills at the deadline

Dropping a handle kills the VM unless a Detach released it.

Capabilities

Everything else is optional and reached through an Option<&dyn Cap> accessor; None means "this driver cannot" or "this VM was not built with the device", never an error at call time. DriverCapabilities says which accessors a driver's handles can return Some for, and the contract checks that the flags and the accessors agree.

Capability Accessor Does
Vsock handle.vsock() dial a guest vsock port → VsockConn { fd, mode }
VsockListen handle.vsock_listener() accept guest-initiated vsock connections
Checkpoint handle.checkpoint() capture to disk; Resume or HoldQuiesced afterwards
Adopt / Detach driver.adopt() / handle.detach() VMs that outlive the process (external VMMs)
Prepare driver.prepare() spawn the VMM ahead of a boot: pid journaled and READY listener bound before the guest starts; boot is exactly prepare-then-boot
Staging prepared.staging() bring the kernel, the disks and a checkpoint into the area this VM's VMM can reach, and name each as its spec must; unstage_disk takes a disk back out, so it outlives the VM
Balloon handle.balloon() set/read the memory balloon
Console handle.console() read guest console output
DebugSnapshot handle.debug() driver-specific JSON for post-mortems

VmDriver::id_budget(&isolation) is the one thing a driver knows about ids that the port's own VmId rule does not: the longest id its layout leaves room for, or None when it imposes no limit. Some(0) is a real answer — a chroot base deep enough to spend the whole AF_UNIX path budget leaves room for no id at all.

net::GuestNetwork is the second port: reserve a lease, activate it into a NicSpec, adopt one whose guest outlived the process that activated it (re-establishing the host state that died with that process), ask what the guest sees under the mode it was activated in, quarantine/release, and the NetworkReconcile cleanup-token protocol. That protocol answers with Error::Unavailable (come back later) and Error::PreconditionFailed (that token names no pending generation); those two classes are the contract callers turn into their wire codes, so an adapter must not flatten them into Error::Network.

Usage

let driver: Arc<dyn VmDriver> = root.pick_driver();          // composition root
let vm = driver.boot(spec, &runtime_dir).await?;              // Box<dyn VmHandle>
let mut events = vm.events();
if let Some(vsock) = vm.vsock() {
    let conn = vsock.dial(AGENT_PORT).await?;                 // conn.mode picks the transport
}
if let Some(cp) = vm.checkpoint() {
    let image = cp.checkpoint(&dir, CheckpointOptions::default()).await?;
}
vm.shutdown(ShutdownMode::Graceful { timeout: Duration::from_secs(5) }).await?;

Running the contract against an adapter

Enable the testkit feature in the adapter's dev-dependencies, implement ContractHarness (driver, a spec asking for every device the driver claims, fresh runtime dirs, the agent port to dial, and how to wait for guest readiness), and instantiate the macro in a test target:

use arcbox_vm_driver::driver_contract;
driver_contract!(fc, FcHarness::new());   // → mod fc { #[tokio::test] async fn ... }

FakeHarness is the reference implementation, and tests/fake_contract.rs runs the same checks against FakeDriver, both fully claimed and with no capabilities claimed. FakeDriver and FakeNetwork are what runtime unit tests build their real actors and state machines on — no hypervisor needed.

Design: docs/architecture/vm-stack-redesign.md (D-VM1, D-VM7, D-VM9).