Isolation & limits

Security model

Each FlashVM runs behind a hardware virtualization boundary in its own process, with only the rights it needs.

The FlashVMM security model is a layered isolation design that puts every FlashVM behind a hardware virtualization boundary (KVM on Linux, Hypervisor.framework and Virtualization.framework on macOS, Windows Hypervisor Platform on Windows), in its own flashvm process, reachable only through an operating-system-protected local socket or pipe, on a host whose admission and updates are verified.

The guest is never trusted to behave. Its CPU, memory, disk and network are held by the host (see resource enforcement), what it may reach is decided outside it (egress policy), and the keys it uses never enter it (secret proxy). This page covers the boundaries around the VMM itself: process isolation, privilege drop, the control-socket trust model, host admission and signed updates.

Key facts

Runs onLinux (KVM, x86_64 and arm64) · macOS (Hypervisor.framework and Virtualization.framework, Apple silicon) · Windows (Windows Hypervisor Platform, x64 and arm64)
InterfacesCLI flash · MCP flash-mcp · REST per-VM API under /v1/flashvm, host API under /v1/host
Isolation unitOne flashvm process per FlashVM, one API address per VM
VM control socketUnix socket mode 0600 (Linux, macOS); per-VM named pipe \\.\pipe\flashvm-<id> (Windows)
Linux process hardeningAll capabilities dropped (bounding, ambient, effective, permitted, inheritable) · no_new_privs · non-dumpable · RLIMIT_CORE 0 · descriptor ceiling 16384 · per-thread seccomp allowlist enforced on every vCPU and device thread
Host admissionPOST /v1/host/admissions before every create; report or enforce
Update signatureEd25519 over the release manifest, trust anchor compiled into the binary; SHA-256 per artifact; anti-rollback

How it works

Hardware boundary and one process per VM

Each FlashVM runs on the host's native hypervisor: Linux KVM; on macOS the HVF engine (Hypervisor.framework) or the VZ engine (Virtualization.framework); on Windows the Windows Hypervisor Platform. Every VM is served by its own flashvm process with its own API address, and no shared daemon sits between flash and a VM. A fault in one VM's device emulation is contained to that VM's process.

Privilege drop before the guest runs (Linux)

The Linux flashvm process does everything that needs privilege first, then gives it up before any guest-facing thread exists:

  1. Binds its frame socket and REST socket (mode 0600) and pre-opens what a restore needs, such as the userfaultfd device.
  2. Optionally enters a private PID namespace, a private empty network namespace, and a pivot_root jail holding only /dev/kvm, /dev/null, the read-only bundle and a writable artifacts directory.
  3. Optionally becomes an unprivileged user: setgroups(0), then setgid, then setuid, each fatal on failure, then verified with getresuid and getgroups.
  4. Drops the bounding and ambient capability sets, then zeroes effective, permitted and inheritable with capset.
  5. Sets PR_SET_NO_NEW_PRIVS, PR_SET_DUMPABLE=0 (no same-user ptrace or /proc/<pid>/mem read of guest RAM), RLIMIT_CORE=0, and lowers RLIMIT_NOFILE to at most 16384.
  6. Enforces a per-thread seccomp allowlist on every vCPU, pacer and network thread: a call outside the list stops that thread alone, so one VM ends and the host carries on.
  7. Builds the async runtime last, so every thread that serves or runs the VM is created by an already-dropped thread and inherits the empty capability set.

Linux capabilities are per thread; building the runtime after the drop is what makes the drop true for the vCPU, device and API threads, not only for the main thread.

Per-OS process boundary

Each OS uses its native mechanisms: Linux the sequence above; macOS a Developer ID signed flashvm under the hardened runtime, where only the VMM carries the com.apple.security.hypervisor and com.apple.security.virtualization entitlements and flash, flash-mcp and the daemons sign with none; Windows a per-VM process placed in its own Job Object, which also carries its hard CPU-rate cap.

Host trust model

Access to a FlashVM is access to its control address, and that address is protected by the operating system:

  • Linux and macOS: each VM's API socket is created mode 0600, owned by the user who launched it. With --run-as, the sockets are handed to that user and group before the drop.
  • Windows: each VM listens on its own named pipe, created under the launching account's security descriptor.
  • Daemons (flashhostd, flashpoold, flashgpud, flashnetd) read the caller's identity from the connection: SO_PEERCRED on Linux, getpeereid on macOS, the client token on Windows. Writes need root (or Administrator/LocalSystem), the daemon's own account, or membership in the socket's operator group. Windows daemon pipes carry an explicit DACL (full control for LocalSystem, Administrators and the daemon account; read and write for authenticated users) and reject remote clients.

Why no socket credential layer

A token or signed handshake on the local socket would add nothing to the boundary. The credential would have to live in a file, and any process able to read that file already holds the filesystem rights that reach the socket. FlashVMM therefore uses the boundary the host already enforces and audits: file modes and ownership on Linux and macOS, security descriptors on Windows. The effective policy is inspectable with the OS's own tools (ls -la on the registry directory). Network access to a host is a separate path with its own strong credential: the flashhostd listener is mutual TLS, and every caller must present a certificate signed by the configured CA.

Host admission as a boundary

Before any VM is created, flash run, restore, clone, fork, rewind, pool spares and POST /v1/host/flashvms reserve room through flashhostd's fit check. An ordinary user reserves only in its own registry, at most 64 reservations at a time; changing the host policy or creating and removing VMs through the host needs operator rights. Under enforce, a VM that does not fit is refused with the dimension and rule that decided it, so no tenant can overcommit the machine past its policy. See host daemon.

Signed updates as a boundary

flash self-update trusts exactly one key, compiled into the binary at build time; no file, flag or environment variable changes it. The release manifest's Ed25519 signature is verified before any field is parsed. A manifest older than the newest one this machine accepted is refused (anti-rollback), an artifact older than the running version is refused (never downgrade), and every artifact's SHA-256 and size come from inside the signed manifest and are checked before it is written. Paths are channel-relative, so a manifest cannot point the downloader at another host or name an install path. Linux replaces each binary with an atomic rename(); macOS installs the signed, notarized .pkg; Windows installs the .msi. See updates.

Interfaces

CLI

ls -la "$XDG_RUNTIME_DIR/flashvm"        # the VM sockets and who may open them
flash host policy set --admission enforce --memory-headroom-pct 10
flash host admissions                    # room reserved for VMs about to start
flash host ca create --out ./ca
flash host cert issue --ca ./ca --name host1 --ip 10.0.0.5 --out ./certs
flash host listen --addr 0.0.0.0:7443 --cert ./certs/host1.pem --key ./certs/host1.key --ca ./ca/ca.pem
flash self-update

MCP

flash_host(method: "GET", path: "/v1/host/policy")
flash_host(method: "POST", path: "/v1/host/admissions", body: {"id": "job1", "vcpus": 2, "mem_mib": 1024})

REST

POST /v1/host/admissions {"id": "job1", "vcpus": 2, "mem_mib": 1024}
PUT  /v1/host/policy {"admission": "enforce", "memory_headroom_pct": 10}
PUT  /v1/host/listener {"addr": "0.0.0.0:7443", "cert": "...", "key": "...", "ca": "..."}

flashvm process options (Linux)

--run-as USER  --run-as-group GROUP  --pid-namespace  --net-namespace (with --tap-fd)  --jail-root DIR
FLASHVM_SECCOMP = enforce (default) | errno | log   per-thread syscall allowlist on vCPU, pacer and RX threads

Contract an agent must know: POST /v1/host/admissions returns 409 host_full with refusal: {dimension, rule, need, free, message} when a VM does not fit under enforce; 403 forbidden when an unprivileged caller names another user's registry or writes the policy; 429 too_many_reservations past 64 per user or 4096 in all. PUT /v1/host/listener returns 422 listener_refused unless cert, key and CA are all given.

Guarantees

  • Hardware isolation per VM. Every FlashVM runs on the host hypervisor in its own flashvm process.
  • Privilege is gone before the guest runs. On Linux every thread that runs or serves a VM starts with an empty capability set, no new privileges and no core dumps.
  • The OS decides who reaches a VM. Owner-only sockets on Linux and macOS, security descriptors on Windows, mutual TLS across the network.
  • Every create passes admission. Room is reserved before a VM exists, and an enforcing host refuses with the exact reason.
  • Every release installs Ed25519-signed. Ed25519 verified before parsing, SHA-256 per artifact, anti-rollback and never-downgrade.

What it does

Related