Security
The Matryoshka model: how mvm isolates untrusted code
mvm runs untrusted Linux workloads in microVMs. This page explains the five trust layers, the CI-enforced security claims, and which claims hold for each backend.
mvmβs job is to let you run untrusted code β third-party software, AI-generated scripts, CI runners, sandbox workloads β and trust the isolation. This page explains the security model in one diagram and one matrix.
What you get by default
Section titled βWhat you get by defaultβFor the ordinary command β mvmctl machine run --image ... -- ... β mvm starts
from a deny-by-default posture. The exact enforcement tier is printed by
mvmctl doctor and in launch receipts, but the user-visible contract is:
- A real microVM boundary: each workload gets its own guest memory, virtual devices, and Linux kernel. A container is never silently substituted.
- A private filesystem: the image and the workloadβs ephemeral writable layer are visible inside the guest. Host directories are absent unless you explicitly mount them; mounts are a deliberate part of the launch plan.
- No network by default: there is no egress until you enable networking and
admit destinations with
--allow-host. Inbound access is not implied by an outbound rule. - No raw host secrets in the guest: secret-aware egress substitutes credentials at the host boundary, so the workload receives only the placeholder or response it is authorized to use.
- An auditable launch decision: the image, resource limits, mounts, network posture, admission profile, and backend are captured in the signed execution plan before boot.
These defaults are intentionally stronger than βa VM with a public network.β They make the safe path the shortest path while keeping explicit escape hatches visible in the command line and the receipt.
What mvm defends
Section titled βWhat mvm defendsβmvm is designed to bound an untrusted guest that tries to escape to the host, read another workloadβs state, use an undeclared host mount, reach private host services, or obtain a credential that was not released for its destination. The guest-to-host control channel is host-brokered and typed; it is not SSH and does not require an inbound guest listener.
What mvm does not defend
Section titled βWhat mvm does not defendβmvm trusts the host operating system, the selected hypervisor, and the release or source artifacts used to build it. A compromised host or hypervisor is outside this boundary. A workload can also misuse a destination, mount, secret, or capability that its operator explicitly allowed. Multi-tenant sharing inside one guest and hardware-backed remote attestation are not default guarantees.
Your responsibility
Section titled βYour responsibilityβUse the strictest profile that fits the workload, keep networking disabled when it is unnecessary, allowlist only the destinations required, prefer read-only mounts, pin production images by digest, set resource and lifetime limits, and keep the host patched. The security posture is visible and enforceable, but it cannot infer whether an explicitly granted capability is safe for your code.
The five trust layers
Section titled βThe five trust layersβββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ L5 β Workload (your untrusted code) ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€β L4 β Guest agent (parses host messages, launches code) ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€β L3 β Guest kernel (Linux, ephemeral, isolated) ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€β L2 β VMM (Firecracker, Rust, seccomp-jailed) ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€β L1 β Host + hypervisor (KVM / HVF) ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββEach layer trusts only the layer below it. An attacker has to break through every boundary above to reach the host. A failure in any one layer is bounded β the layer below still enforces its own contract.
This pattern (sometimes called the matryoshka model after the nested Russian dolls) is the same defense-in-depth used across the production microVM / hardened-isolation ecosystem. mvmβs adaptation is that L5 is enforced inside the guest β even a guest-kernel compromise doesnβt give arbitrary access to other in-guest services. See ADR-001 for the full decision record.
The claims
Section titled βThe claimsβmvm makes fifteen CI-enforced security claims, plus three preview claims whose witnesses run but whose guarantee is narrower. Each one is backed by a test or a continuous-integration check that fails the build if the claim ceases to hold. The claims that defend a nesting layer are what the layer model is about; the rest defend the supply chain and the admission path, which decide what is allowed to run inside those layers at all.
| # | Claim | Defends layer | How itβs enforced |
|---|---|---|---|
| 1 | No host-fs access from a guest beyond explicit shares | L2 / L5 | Per-service uid + seccomp standard default + setpriv bounding-set drop; user-volume allow-list defaulting to read-only |
| 2 | No guest binary can elevate to uid 0 | L2 / L4 | setpriv --no-new-privs in launch path; /etc/{passwd,group} are read-only bind-mounts |
| 3 | A tampered rootfs ext4 fails to boot | L3 | dm-verity sidecar + roothash on cmdline + a verity-aware initramfs owning the boot pivot |
| 4 | A production-safe run cannot invoke DevOnly guest-agent verbs | L4 | Runtime profile + signed VerbGrant intersection; grant and conformance tests enforce the full DevOnly set |
| 5 | Vsock framing, supervisor-config JSON, and the datapath ingress are fuzzed | L2 / L4 | cargo-fuzz targets over the hostβguest messages, the supervisor config parser, and the userspace datapath ingress; deny_unknown_fields on every type |
| 6 | Pre-built dev image is hash-verified | supply chain | SHA-256 manifest streamed through the download; a mismatch rejects and deletes it |
| 7 | Cargo deps are audited on every PR | supply chain | cargo-deny + cargo-audit jobs; reproducibility double-build |
| 8 | Every workload runs from a signed, audited ExecutionPlan | admission | Ed25519 host-signer keypair; validity window + nonce replay-store; chain-signed admission entries |
| 9 | Every published bundle is content-addressed and key_id-pinned | supply chain | A rejection ladder at fetch and at admit time: unknown key, tampered manifest, key_id mismatch, unsafe path, pin drift |
| 10 | No untrusted workload reaches the network unless policy admits it | data containment | Policy defaults to deny-all; the workload guest has no NIC, so egress leaves only over vsock to a host endpoint that authorizes it |
| 11 | Every application-dependency volume is sealed and audited | supply chain (app layer) | A hash-locked volume carrying an SBOM, a CVE scan, and a hash-chained manifest; admission refuses a tampered volume |
| 12 | Every broker service is bound to a signed plan binding | admission | Binding-gated dispatch, enforced before the handler runs, with a rejection ladder for unbound and out-of-profile calls |
| 13 | No raw secret value crosses the broker channel | data containment | Destination-bound, time-bound signed credentials only; raw secret bytes never leave the supervisorβs address space |
| 14 | Every OCI image admission records provenance in the audit log | supply chain | A provenance entry carries registry, repo, resolved digest, layer digests, and trust verdict; production refuses a mutable reference |
| 15 | A sealed production microVM has no shell, no DevOnly verbs, no PTY | L4 | Only the dev /init serves a console; console capture is write-only with no host input; the host gate refuses console on a sealed image |
Three further claims β 16 (egress substitution keeps a raw secret off the guest), 17 (workload stdin is grant-gated and secret-scanned), and 18 (workload resource bounding) β are preview. Their witnesses run in CI, but each carries a limits note in ADR-001 that has to be read before treating it as enforced. See CI-enforced security claims.
L1 (host + hypervisor) doesnβt carry its own claim β the host is trusted by definition. If your host is compromised, every layer falls. Locking down the host (firewall, package hygiene, full-disk encryption) is your responsibility.
Intent-bound admission profiles
Section titled βIntent-bound admission profilesβEvery workload also goes through a signed admission step before boot. mvmctl machine run synthesizes an ExecutionPlan, signs it with the host key, checks its validity window and replay nonce, then emits a chain-signed audit entry.
The plan now carries an admission_profile: a compact record of the workloadβs declared intent and the controls selected for that intent:
- intent, for example
vm:boot,code:execute, oragent:web-research - seccomp tier selected for the run
- network, filesystem, egress, and tool policy refs
- secret-release posture (
none, plan-bound, or attestation-bound) - audit taxonomy and required labels
This does not add a second seccomp implementation or new execution capability inside the sandbox. Runtime syscall filtering still comes from mvm-security and the guest seccomp.json manifest. The admission profile records the selected tier in the signed plan so the audit chain can prove which security posture the workload was admitted under.
Per-backend tier matrix
Section titled βPer-backend tier matrixβmvm runs on multiple backends. Not all backends carry every claim. The tier you actually get depends on which backend mvm picks for your run.
The columns below are the nesting layers, so this matrix covers the claims that defend a layer. The supply-chain and admission claims (6, 7, 8, 9, 11, 12, 14) are backend-independent β they gate what is allowed to run before any backend is chosen, and hold identically across all of them.
| Backend | L1 | L2 | L3 | L4 | L5 | Tier |
|---|---|---|---|---|---|---|
| Firecracker (Linux + KVM) | β | β | β | β | β | Tier 1 β full ADR-001. Every layer-defending claim holds. |
| HVF (macOS 26+ Apple Silicon β auto-default) | β | β | β οΈ | β | β | Tier 2 β claim 3 (verified boot) partial; Hypervisor.framework, vsock-only egress (no guest NIC). The macOS-26 auto-default. |
| libkrun (Linux KVM, macOS Apple Silicon HVF) | β | β | β οΈ | β | β | Tier 2 β same as HVF. |
| QEMU (Linux KVM/TCG) | β | β οΈ | β οΈ | β | β | Tier 2 β claim 3 partial; QEMUβs larger device model raises L2 audit cost. Deliberately outside claim 10βs egress enforcement, because it carries no untrusted multi-tenant workload. Dev/test only (--hypervisor qemu; the no-/dev/kvm path via TCG software emulation). Never selected by mvmd. |
| BrowserWasi (browser tier) | β | β | β | β | β | Claim-free β no numbered claims. The browserβs sandbox/process isolation is the only boundary; there is no hypervisor, no guest kernel, no vsock, and no verified boot. |
β = layer fully enforced. β οΈ = layer partial (named exception). β = layer collapsed (claim does not apply). β = not applicable.
No container fallback
Section titled βNo container fallbackβOn every default and production path, mvm has no Tier 3 and no container/Docker fallback. A shared-kernel container is not a microVM: its isolation comes from the host kernelβs namespace and cgroup machinery, which is shared with the host. In 2024β2025 the container ecosystem produced multiple CVEs (Leaky Vessels, NVIDIAScape, runc race conditions, Docker Desktop priv-esc, runc masked-path) that yielded host escape from inside a container β none of which matter inside a microVM, where the guest kernel is isolated by hardware. If a host has no microVM-capable backend, mvm does not silently drop to a weaker boundary; it fails closed.
There is no carve-out. ADR-034 has been retired and the Docker dev-tier backend removed (Plan 329). A host without a usable microVM backend fails closed; mvm does not offer a shared-kernel container path on any default, production, or explicitly selected runtime path.
Browser-tier WASI backend
Section titled βBrowser-tier WASI backendβThe BrowserWasi backend runs workloads inside the browserβs own WebAssembly engine. It has no hypervisor boundary and runs the guest workload directly as a WASI module. This makes it a claim-free tier: it cannot assert any of the numbered security claims because there is no hardware isolation.
The browser-tier backend:
- Runs the workload as a WASI Preview 1 module inside the browserβs WebAssembly engine
- Has no Linux kernel, no initramfs, no vsock, and no virtual devices
- Enforces isolation through the WASI capability model (preopened directories, host imports)
- Mediates network egress through browser-native
fetch()via themvm:egresshost import - Is never auto-selected and only available through explicit
--hypervisor browser-wasm - Is for demos, playgrounds, and browser-local development only
- Does not apply to production workloads
When a browser-tier backend is selected, the same admission, policy, and audit semantics that apply to host backends are enforced inside the browserβs own WebAssembly engine.
Choosing a tier
Section titled βChoosing a tierβ- Production / untrusted code β Tier 1. Linux + KVM + Firecracker. No exceptions.
- macOS dev or CI on Apple Silicon β Tier 2 (HVF or libkrun). Verified boot is the open item.
- Linux dev/test without
/dev/kvmβ Tier 2 QEMU (--hypervisor qemu, TCG software emulation). A real microVM, slower; dev/test only. - macOS Intel / native Windows β unsupported for local microVM isolation today (no container fallback on any default path β only the explicitly selected ADR-034 dev tier). WSL2 with nested
/dev/kvmis the supported Windows-adjacent libkrun workload path; a Hyper-V managed Linux builder remains future backend work.
mvmctl doctor reports your current tier on the running host.
Whatβs not promised
Section titled βWhatβs not promisedβADR-001 names three explicit non-goals so we donβt accidentally commit to defending against them:
- A malicious host. mvm trusts the host with the hypervisor and the build keys. If your laptop or your server is compromised, every layer falls.
- Multi-tenant guests. One guest = one workload. Sharing a single guest VM between mutually-distrusting tenants is out of scope.
- Hardware-backed key attestation (TPM/SEV/etc.) is out of scope for v1.
If your threat model needs any of those, mvm is not the right tool today. ADR-001 documents these limits explicitly.
See also
Section titled βSee alsoβ- ADR-001 (full decision record)
- Plan 25 (microVM hardening β the implementation sequence for claims 1β7)
- Plan 53 (cross-platform roadmap β backend tier discipline)
- βYour container is not a sandboxβ (emirb, 2026) β the post that crystallized the matryoshka framing in the broader microVM ecosystem.