Isolation & limits
Resource enforcement
Every resource a VM uses has its own control, set at creation and changeable live.
FlashVMM resource enforcement is the host-side control layer that holds each FlashVM to its own CPU budget, memory ceiling, disk budget, disk and network rates and run length, using the native hypervisor (KVM, Hypervisor.framework, Windows Hypervisor Platform), virtio devices and per-thread CPU accounting, with every control set at creation and readable over REST and MCP.
Every control lives outside the guest, so root inside the VM cannot see or widen it. Every cap is opt-in and exact: omitted means uncapped, a passed value is enforced, and flash never invents one. Most controls change on a running VM with flash set or a PATCH of the matching resource. The safety story around the VMM process is on security model; how the host as a whole decides room is on host daemon.
Key facts
| Runs on | Linux (KVM) · macOS (HVF engine; NIC caps on the VZ engine) · Windows (WHP) |
|---|---|
| Interfaces | CLI flash run, flash set, flash balloon, flash mem · MCP flash_cli, flash_resource, flash_api · REST /v1/flashvm/cpu, /memory, /disk, /nics, /limits |
| CPU cap unit | millicpu across all vCPUs (cpu_millis); throttles when below vcpus × 1000 |
| CPU pacer interval | 5 ms (Linux and macOS) |
| Boot grace | 8000 ms default; the cap engages at the guest's Start or when grace elapses, whichever is first |
| Network caps per NIC | egress Mbit/s, egress packets/s, ingress Mbit/s |
| Run-length limits | max_wall_s, max_cpu_s; 0 = none |
How it works
CPU budget
cpu_millis is host CPU per wall-second summed over every vCPU thread. Boot runs uncapped; the cap engages when the guest reports Start or when the boot grace elapses, and GET /v1/flashvm/cpu reports cap_engaged from the enforcer's own flag. Each OS uses its native mechanism:
- Linux: a pacer thread signals every vCPU thread with
tgkillevery 5 ms. The handler is installed withoutSA_RESTART, soKVM_RUNreturnsEINTRand the thread's burn is checked againstCLOCK_THREAD_CPUTIME_ID. A guest that never exits voluntarily is still paced, and the mechanism needs no cgroups and no guest cooperation. - macOS (HVF): the same budget, field for field: a pacer forces every vCPU out with
hv_vcpus_exitevery 5 ms while a throttled VM runs, and each thread sleeps while the VM is over budget. The accounting epoch resets about every second, so idle time cannot bank unbounded burst. - Windows: the
flashvmprocess sits in a Job Object with a hard CPU-rate cap. WHP bills guest time to each vCPU's host thread, so the process-wide cap holds the VM tocpu_milliswith no vCPU exits at all.
CPU templates
--cpu-template fixes the CPUID the guest sees, either host-raw or a named template. A snapshot records its template and is checked against it at restore, so a captured VM only lands on silicon that can run it. Snapshotting requires a chosen template. See snapshots.
Memory ceiling
Guest RAM is mapped into the hypervisor at creation (KVM memory slots, hv_vm_map, WHvMapGpaRange), and that mapping is the hard ceiling. Two live levers work inside it: the balloon (flash balloon --target-pages) asks the guest to hand pages back, and a VM created with --mem-region-mib carries a virtio-mem region whose live target is set with flash mem --target-mib. The host's reclaim uses the balloon under pressure. See memory.
Disk budget
--disk-mib is one budget for the VM's whole host-disk footprint: the copy-on-write root overlay and the shared persistent volume. It is charged by real allocated blocks, never logical size, so sparse files and truncate cannot evade it. Every write first reserves its worst case under one lock, then settles to the real growth, so concurrent writes from different vCPU threads stay inside the quota. At the limit the guest sees ENOSPC or a block I/O error, exactly a full disk.
Disk and network rates
Disk throughput (disk_bps) and operations (disk_iops) are separate token buckets in the block device path, including the io_uring batched path on Linux. Each NIC has three independent buckets: egress bandwidth (mbps), egress packet rate (pps) and ingress bandwidth (ingress-mbps). A separate packet-rate bucket stops a flood of small packets from riding under a bandwidth-only cap.
Run-length limits
max_wall_s counts seconds since the VM was created (a pool spare: since its claim); max_cpu_s counts CPU seconds of the whole VMM process, every vCPU and device thread together, plus guest CPU spent in the engine's own process on the macOS VZ engine. When one is reached the VM stops itself as on DELETE, and GET /v1/flashvm/limits says which. On a live VM a limit may be set or lowered, never raised or removed: the creator decides how long a VM may run, and anything that can reach its socket may only shorten it. See run limits.
Interfaces
CLI
flash run --bundle ./bundle --vcpus 2 --mem-mib 1024 \
--cpu-millis 500 --cpu-template host-raw \
--disk-mib 4096 --disk-bps 52428800 --disk-iops 2000 \
--nic tap=tap0,mbps=100,pps=20000,ingress-mbps=100 \
--max-wall-s 3600 --max-cpu-s 1800
flash set job1 --cpu-millis 250 --disk-iops 500 --max-wall-s 600
flash set job1 --nic 0 --mbps 50 --pps 10000 --ingress-mbps 50
flash balloon job1 --target-pages 65536
flash mem job1 --target-mib 512
MCP
flash_resource(vm_id: "job1", resource: "cpu")
flash_api(vm_id: "job1", method: "PATCH", path: "/v1/flashvm/limits", body: {"max_wall_s": 600})
flash_cli(args: ["set", "job1", "--cpu-millis", "250"])
REST
GET /v1/flashvm/cpu
PATCH /v1/flashvm/cpu {"cpu_millis": 250}
PATCH /v1/flashvm/disk {"disk_mib": 8192, "disk_bps": 52428800, "disk_iops": 2000}
PATCH /v1/flashvm/nics/0 {"mbps": 50, "pps": 10000, "ingress_mbps": 50}
PATCH /v1/flashvm/memory/balloon {"target_pages": 65536}
PATCH /v1/flashvm/limits {"max_wall_s": 600}
Contract an agent must know: PATCH /v1/flashvm/memory returns 422 memory_immutable and names the balloon as the way to hand memory back; flash mem beyond the region's size returns 422 mem_exceeds_region, and on a VM without a region 422 mem_not_attached; raising or removing a set run limit returns 422 invalid_limits; a PATCH with nothing to change returns 422 nothing_to_change.
Guarantees
- Host-side, every time. Each control is enforced by the VMM or the OS, outside the guest.
- Exact caps. A passed value is enforced; an omitted one is uncapped; nothing is chosen for you.
- A spinning guest is still paced. The CPU budget holds without the guest leaving the hypervisor voluntarily.
- Real bytes, not sizes. Disk is charged by allocated blocks, with concurrent writes reserved before they land.
- Limits tighten in flight. A live run limit can be set or lowered, so a VM always stops by its creator's deadline.