A Checkpoint Is Not a Safety Net

We have git for code history. For everything else an agent leaves behind, we have a version ID and a shrug.

Checkpointing feels like safety, and that's what makes it dangerous.

The wrong default is completely reasonable: give an agent a real computer, take snapshots constantly, and restore when it breaks something. On Sprites, that part is real. A Sprite is a persistent Linux computer, and it can checkpoint and restore its writable filesystem state on command. The capture is fast, the restore primitive exists, and the CLI gives you a version ID. Then you have to pick one.

RecallMEM is a small local-first memory app I maintain. I run it inside a Sprite when I want agents to work somewhere that is not my laptop. But before I let an agent loose on it, I always make sure to make a checkpoint. Good instinct, right? Then the agent can go full YOLO, and it starts doing agent things: clones, installs, starts a dev server, edits a config, runs something, edits it again. And then... something breaks. Well, guess what? I can just roll back to a previous checkpoint.

I list the checkpoints. The IDs are real enough, but the moment behind each one is not:

ID   CREATED             COMMENT
v1   2026-06-01 19:11
v2   2026-06-01 19:18
v3   2026-06-01 19:27

So Restore is right here! The primitive works, right? The problem is, I had no damn idea which one of those to land on. Was v3 before the bad config or after it? Was the app healthy when v2 was taken? If I pick wrong, what am I about to overwrite? A restore button is not a safety net. The net is knowing which state was good, and a version ID doesn't tell you that.

What The Snapshot Knows

The snapshot itself is the hard, valuable part. Sprites checkpoints capture the writable filesystem overlay: files, directories, installed packages, config, dotfiles, and on-disk databases added on top of the base image. They do not capture the base image itself, the running processes, memory, or open network connections. Restoring replaces the writable overlay with the checkpointed state and restarts the environment. That is a very useful primitive, but it is not the same thing as history.

Sprites checkpoints are copy-on-write. A new checkpoint stores the blocks that changed, not a fresh copy of the whole disk, and the platform describes live checkpoint creation as taking about 300 ms. Fast enough that you can take them often without building your workflow around the cost of taking them. So the mechanism isn't where this breaks down. Where it breaks down is that a perfect snapshot can still be useless to a tired human.

Sprites already lets you add a checkpoint comment, and you should. sprite checkpoint create --comment "before auth refactor" is much better than a naked v2. But a comment is still only the thing you remembered to type at the moment you typed it. It does not tell you what files changed, whether the app was healthy, which verification passed, or what the agent had just done. So the problem was never "add restore." Restore exists. The problem is that the machine state got captured and the meaning only partly did.

Git Solved The Wrong Half

We already solved a version of this for source code. A commit has a message, a diff, and a parent. You can stand in front of a hundred commits and usually know roughly where you are, because each one carries a little story about why it exists. No one freezes in front of git log the way I froze in front of that checkpoint list.

But an agent working on a real computer changes far more than the files git tracks. It installs packages. It writes generated files git never sees. It leaves databases on disk. It starts processes while it works. It mutates the environment around the repo, and that environment is often the thing you actually need to roll back. Git is code history. Checkpoints are environment state. Agents make the gap between those two painfully obvious.

For most of the last twenty years, that gap was tolerable. The environment was something you set up once, by hand, and then mostly stopped touching. You did not need a readable timeline of your machine because your machine barely moved. Hand that machine to an agent that installs, writes, and breaks things on its own initiative, and the environment stops being a backdrop. It becomes part of the program.

A checkpoint is the raw material for environment history. It just needs the part that made git usable: context attached to the point in time.

Three Cheap Signals

So I attached the story in the least glamorous way possible.

I wrapped the checkpoint call in a small CLI. It does not inspect the snapshot. It never diffs the checkpoint itself. It can't look at a capture and tell you "this installed Postgres." What it does is grab three things that happen to be lying around when you run it, then records them in Workbench keyed by the checkpoint ID:

One line, in practice:

node scripts/workbench.mjs checkpoint "before auth refactor" --verify "npm test"

Or, after linking the repo-local command once:

workbench checkpoint "before auth refactor" --verify "npm test"

Do not use npx workbench. There is already an unrelated package with that name on npm, and npx may download and run that instead of this repo's command. Ask me how I found out, except don't, because the answer is "before publishing the blog post, thankfully."

Verification does not have to be a test suite. A smoke test, a curl against a health endpoint, whatever that project already uses to know it's alive. The point is recording a yes or no next to the moment, not proving correctness.

Now the same restore decision has more shape:

10:02  v1  clean clone, verify passing
10:11  v2  before auth refactor
10:18  v3  after package install, verify failing
10:27  v4  working version, app healthy

I pick v4 because I can see it was the good one. The question stopped being "which ID?" and became "which moment?"

One rule matters here: the snapshot is sacred, the context is best-effort. If the git read fails, file context is skipped. If the Workbench event write fails, the tool warns and moves on. Either way, the checkpoint still exists. The capture is the thing you can't afford to lose, so the convenience layer does not get to make it fragile.

Where The Label Lies

This is still not truth. It's a label, and the label comes from git while the snapshot comes from the writable filesystem overlay. Those do not always agree, and when they disagree, the label lies a little.

A file changes on the machine that git doesn't track: build output, a written .env.local, runtime data, a database file. It's in the snapshot. It's absent from the changed-files list. The capture has it; the story never mentions it.

Or the reverse, which got me first. I'd already committed my work, so git diff HEAD came back empty. The checkpoint got no file context at all, while the environment still had plenty of state worth capturing. The most complete capture, the emptiest label, for the dumbest reason.

That's git being honest about what it is. Git can tell you about tracked code. It cannot tell you what happened to the computer around that code. So the useful part of this wrapper is not that it solves environment history, because it doesn't. The useful part is that it turns an opaque restore decision into a readable one most of the time, which is already a large upgrade over staring at a version list and pretending memory is a rollback strategy.

The Version That Reads The Snapshot

The real fix is to stop leaning on git and read the snapshots themselves.

Sprites mounts the last five checkpoints read-only at /.sprite/checkpoints/, so the next version of this idea is sitting right there: compare a checkpoint mount against the current filesystem, or compare two checkpoint mounts, and derive the story from the bytes that actually changed. That is how you catch the files git never saw. That is how you stop pretending a source diff is an environment diff.

That is a real project, not an afternoon of shelling out. It has all the annoying parts you would expect: redaction, noisy generated files, package caches, databases, secrets, and deciding which changes are meaningful enough to show a human. But that is the right problem, because it derives the story from the same layer the checkpoint captured.

The bigger point is not my little command. It is the mental model: agents need real computers, and real computers need readable history. Git gave us readable history for source files. Sprites gives us fast restore points for the environment around those files. The missing layer is the thing between them. Sprites makes restore possible. Workbench makes restore legible.

If you want to feel the exact moment I'm talking about, it takes ten minutes. Spin up a Sprite, make a checkpoint, break something on purpose, and restore it. Restoring is the easy part. The pause comes a second later, when you look at the checkpoint list and can't tell which version was the good one.

The capture was never the hard part. That problem is solved, and solved well. The hard part is making a captured moment legible enough that a tired human, at 10:27, can land on the right one without guessing. Code got that treatment a long time ago. The computer underneath it is still waiting.


Questions about this post? Ask the terminal on my homepage — it knows this whole site.

Chris Dabatos - Staff DevRel Engineer

Chris Dabatos

Staff DevRel Engineer @ Fly.io, AI Engineer, and Technical Storyteller based in Las Vegas. He builds things with AI and writes about what breaks.

Sections
Now playing
Intro
0:00 / 0:00