State

Fork, checkpoints and diff internals

Copy-on-write children of a running VM, checkpoints that store only what differs, and a file-level diff from inside the guest.

FlashVMM fork and checkpoints are copy-on-write operations on a running FlashVM: a fork captures the VM and starts up to 64 children that continue from the same instant, a checkpoint is the same capture kept under the VM as a numbered diff against a base, and flash diff compares guest file manifests on the host.

All three are built from the snapshot and clone operations of the snapshot format and the page tracking of the memory model, so children and checkpoints cost the pages that differ rather than a copy of the VM. A child is running in 36 ms. An agent uses this to try several fixes from one prepared state, undo a failed step with flash rewind, and see exactly which files a command changed.

Key facts

Runs onLinux (KVM) · macOS (Hypervisor.framework) · Windows (WHP)
InterfacesCLI flash fork, checkpoint, checkpoints, history, rewind, diff · MCP flash_cli, flash_exec, flash_diff · REST POST /v1/flashvm/snapshots with fork, then PUT /v1/flashvm per child
Fork time36 ms (macOS), 38 ms (Windows x86), child running
Children per fork1 to 64
Checkpoints kept20 unpinned by default (--keep up to 1000), plus up to 50 pinned
Rebase triggerA checkpoint diff of at least 64 MiB and an eighth of the VM's memory
Diff limits20,000 entries per side; files up to 64 MiB hashed; text diffs up to 1 MiB per file

How it works

Live fork

  1. flash fork PARENT reads the parent's shape, caps, egress policy and secret proxies from its API.
  2. It sends POST /v1/flashvm/snapshots {"fork": true}. All-zero RAM is never written, so the capture is as large as what the guest used. The parent keeps running.
  3. It runs one flash clone per child from that capture. Each child maps RAM copy-on-write and pages it in as it touches it, so N children cost the pages each one writes. Each child gets its own identity, inherits the parent's egress policy, and takes its own NIC from --nic (with pool=ID, one per child from a flashnetd NIC pool).
  4. Each child holds a link to the capture (<registry>/<child>.fork.snap); flash rm on the last child removes it.

Forkable VMs on Linux

A VM started with --forkable keeps its RAM in a MAP_SHARED file. Its first fork capture freezes that file in place as the base and remaps the parent privately over it, so the fork copies no RAM; every later capture is a diff of the pages dirtied since that base or since the newest rebase.

Checkpoints, rewind and branch

A checkpoint is a fork capture that is kept under the VM instead of handed to children, numbered, labeled, and recorded with the exit code of the command it was taken for. flash run --checkpoints takes one before every flash exec and every MCP flash_exec, and implies --forkable. When a diff grows to the rebase trigger, the next checkpoint is written self-contained as the new base. flash rewind VM N rebuilds the VM under the same name from checkpoint N, with its memory, disk and running processes as they were; later checkpoints are kept, so a rewind can be undone. flash fork VM --from N branches children from any checkpoint while the VM carries on.

File-level diff

flash diff A B takes two VMs, or a VM and a snapshot file. On each side the guest lists its own files with BusyBox over exec (type, mode, size, inode, link target), and the host compares the listings. Only files the listings cannot settle (same path and size, or a possible rename) are hashed with sha256sum in the guest, so a diff never hashes a whole root. The result lists added, changed, deleted and renamed files with line diffs for text; /proc, /sys, /dev, /run, /var/log, /var/cache and shell history are left out unless --all. flash exec --json --changes returns the same shape for one command.

Interfaces

CLI

flash run --bundle ./bundle --id work --vcpus 2 --mem-mib 1024 --cpu-template host-raw --checkpoints --entry 'sleep 3600' --detach
flash exec work -- sh -c 'cd /work && sh setup.sh'
flash checkpoint work --label ready --pin
flash history work
flash fork work --from 1 --count 2
flash rewind work 1
flash diff work-fork1 work-fork2 --patch
flash exec work --json --changes -- sh -c 'make'

MCP

flash_exec(vm_id: "work", argv: ["sh", "-c", "cd /work && sh setup.sh"], checkpoint: true)
flash_cli(args: ["history", "work"])
flash_cli(args: ["fork", "work", "--from", "1", "--count", "2"])
flash_cli(args: ["rewind", "work", "1"])
flash_diff(a: "work-fork1", b: "work-fork2")
flash_exec(vm_id: "work", argv: ["sh", "-c", "make"], changes: true)

REST

POST /v1/flashvm/snapshots  {"path": "...", "fork": true}                  → 202 {"operation": "..."}
POST /v1/flashvm/snapshots  {"path": "...", "fork": true, "rebase": true}
PUT  /v1/flashvm            {"source": {"type": "snapshot", "path": "..."}, "allow_identity_mismatch": true, "diff_base": "...", ...}   (each child's socket)
POST /v1/flashvm/guest/execs {"argv": ["sh", "-c", "make"]}

Checkpoints, history, rewind and branch are composed by flash from these routes and keep their record under the VM in the registry. Contract: rebase without fork returns 422 invalid_body; a VM started with --disable-dirty-tracking takes full captures. A forked or rewound VM starts with no execs, since execs belong to the VMM that ran them.

Guarantees

  • Children pay for their own writes. RAM is shared copy-on-write from one capture.
  • Every child is its own machine. New identity, own NIC and reseeded randomness, with the parent's limits and egress policy.
  • Undo is undoable. Rewind keeps every checkpoint, later ones included.
  • Exact file changes. Diffs come from the guest's own listing and hashes, in the shape agents parse.

What it does

Related