Giving AI an undo button
You can undo a typo, a deleted email or a bad crop. An AI agent working on a real computer can’t undo anything. When it drops the wrong table or spends forty steps going the wrong way, the only fix is to throw the machine away and lose everything it built. smol machines gives the agent’s whole computer an undo button.
Why agents can’t undo today
Undo in a text editor works because the editor holds everything you changed. An agent’s work is
spread across a whole computer: a half-built repo, a running dev server, a database, a logged-in
browser, files in /tmp. The usual tools each cover only part of that.
| Tool | What it can undo | What it loses |
|---|---|---|
| git | Files in one repo | Running programs, databases, installed packages, everything outside the repo |
| Restart the container | Everything, by going back to the image | All the work done since the image was built |
| VM memory snapshot | Memory | Disks, unless you save them some other way |
| smol machines checkpoint | Memory, running programs and disks, together | Nothing on the machine |
Memory and disk have to be saved at the same moment. If you roll back one without the other, a running program comes back expecting files that are no longer there, or no longer the same.
Save, then undo
Start the machine with --branchable and save a checkpoint before anything risky. The
machine only stops for a moment while the save begins, then keeps running while its memory is
written out. On a MacBook, saving a small Alpine machine took 2.7 seconds, and the machine was
paused for 71 milliseconds of that.
smolvm machine start --name agent --branchable
# save before anything risky
smolvm machine checkpoint --name agent --store ./history -o ./before-migrate.checkpoint To undo, create a machine from that checkpoint. Its programs carry on from exactly where they were, and its files match what they expect. The broken machine stays around until you delete it, so you can still look at what went wrong.
undo# the migration went wrong: undo it
smolvm machine stop --name agent
smolvm machine create --name agent-undo --from ./before-migrate.checkpoint
smolvm machine start --name agent-undo Restoring is fast enough to do every time something fails. On our Linux test host, restoring a machine with 2 GiB of memory took about a quarter of a second, because it reads its disk from the checkpoint instead of copying it, and the machine was running again about two seconds later.
In an agent loop, that means saving before each step and undoing any step that fails, instead of starting over:
an agent loop with undowhile read -r step; do
smolvm machine checkpoint --name agent --store ./history -o "./before-$step.checkpoint"
if ! smolvm machine exec --name agent -- ./do-step.sh "$step"; then
# undo: back to the moment before this step, processes and all
smolvm machine stop --name agent
smolvm machine delete --name agent -f
smolvm machine create --name agent --from "./before-$step.checkpoint"
smolvm machine start --name agent --branchable
fi
done < plan.txt Going back further
One step of undo is rarely enough, because the mistake is often ten steps back. So every checkpoint remembers the one before it. Save a few times and you have a history. Go back to an old point and keep working, and the history branches instead of being overwritten, the way checking out an old git commit and committing on top of it does.
workersmolvm machine checkpoint --name worker --store ./ckpt -o ./ckpt-3.checkpoint
~0 daa66d13daa5 worker (this checkpoint) ~1 3c6ef3623c6e worker ~2 9e3779b19e37 worker
smolvm machine checkpoint-log ./ckpt-3.checkpoint
# ~0 c4c23a7c6d3a 2026-09-28T03:01:05Z worker 401 MiB data (this checkpoint)
# ~1 6b3e4f1fde48 2026-09-28T03:00:55Z worker 401 MiB data
# ~2 58a6bb95baca 2026-09-28T03:00:45Z worker 401 MiB data
# go back two steps (quote ~2 so zsh doesn't expand it)
smolvm machine create --name rewind --from ./ckpt-3.checkpoint --at '~2' A history is only useful if cleaning up old checkpoints doesn’t break it. With most snapshot tools,
each save depends on the one before it, so deleting one breaks every later save. A smol machines
checkpoint saved with --store doesn’t depend on older checkpoint files. The newest one
can restore its whole history on its own, and data that didn’t change between saves is stored only
once. Three saves of the same machine took 98 MB, against 52 MB for one.
Delete files on either side below and see what still restores:
Diff chain snapshot per point
base.memABCDEFGHfull snapshotdiff-1.memC'G'pages dirtied since basediff-2.memE'H'pages dirtied since diff-1rootfs.ext4disk state: yours to copy and keep consistent with each point
- ✓ point 1 base.mem
- ✓ point 2 rebase base.mem → diff-1.mem
- ✓ point 3 rebase base.mem → diff-1.mem → diff-2.mem
Stored checkpoints RAM + disk, retained history
./ckpt-1.checkpoint/ABCDEFGHindex + links./ckpt-2.checkpoint/ABC'DEFG'Hindex + links · retains #1./ckpt-3.checkpoint/ABC'DE'FG'H'index + links · retains #1, #2
objects/ - ✓ point 1 ./ckpt-3.checkpoint --at '~2'
- ✓ point 2 ./ckpt-3.checkpoint --at '~1'
- ✓ point 3 ./ckpt-3.checkpoint
base.mem, then ./ckpt-1.checkpoint/ and ./ckpt-2.checkpoint/.Trying two paths at once
Sometimes you don’t want to undo. You want to try two next steps from the same moment and keep whichever works. That’s a branch. The machine pauses for a moment, and each new copy starts from exactly that point and only stores what it changes afterwards. On a MacBook the pause was about 80 milliseconds for a machine with 1 GiB of memory.
branchsmolvm machine branch --from agent --name try-a
smolvm machine branch --from agent --name try-b
# run a different next step in each, keep the one that works Try it: branch the machine while it runs, then write from each side.
The source machine is running. Branch it while it works.
- machines
- 1
- pages held
- 32
- as full copies
- 32
- still shared
- 0
private page (written after the branch) read through the shared base
Each copy is a new machine with its own name, hostname, machine id and ports, so the copies don’t clash, and it records which machine it came from.
How this compares with Firecracker
Most microVM platforms are built on Firecracker. Firecracker can pause a VM and save its memory, and leaves disks, history and copies for you to build. In practice every team on Firecracker ends up writing its own undo on top.
| Firecracker | smol machines | |
|---|---|---|
| Saves | Memory and devices | Memory, devices and disks |
| Go back to an older point | Replay the base and every change after it, in order | --at ~N on any later checkpoint |
| Delete older saves | Later saves stop restoring | The newest save still restores everything |
| Copy a running VM | Save it, then restore it N times | machine branch, and the original keeps running |
| Copies get their own identity | Up to you, inside the guest | New name, hostname, machine id and ports |
| Hosts | Linux | Linux and macOS |
Firecracker is cheaper when you save very often. It writes only the memory that changed since the last save, while smol machines still reads all of memory and skips writing the parts that didn’t change. If you save every few seconds, Firecracker’s approach costs less.
A few limits to know about. A checkpoint restores on a host with the same CPU architecture, and a
checkpoint from an Apple Silicon Mac also restores on arm64 Linux (smolvm 1.23.0 and later). History
keeps 32 points by default (--history N changes that), and copies of copies can go 32
levels deep. Windows doesn’t support checkpoints or branching yet. Everything else here works in
smolvm 1.19.1 and later.
Get the runtime on GitHub, or read how checkpoint history works.