19 KiB
Tau (and Pi) Pocket on smolvm — Tutorial
A self-contained guide to running Tau, the minimalist terminal coding agent, inside a smolvm sandbox, and bringing in your existing /teacher-mode skills.
Scope. The substrate is smolvm. The primary agent is Tau. The door is left open for Pi (Tau's design inspiration) and for other smolvm-bundled agents —
smolvm hermes,smolvm claude,smolvm codex,smolvm openclaw— but only one agent lives in a sandbox at a time.What this replaces. The earlier "Decision Summary" document in this folder was mostly a paraphrase of
smolvm --helpplus a hand-rolled snapshot model. The CLI already does snapshots (smolvm sandbox snapshot ...) and already ships first-class launchers for every popular agent. This tutorial uses those primitives directly. No custom substrate, no bespoke snapshot file format, no extra "Phase 1/2/3" rollout.
0. Prerequisites
Verify the host can run smolvm:
smolvm doctor
You want Backend: firecracker (or qemu / libkrun), Result: OK, and kvm/network-permissions to show pass. The four warnings shown on this machine (nft-table:ip:smolvm not created yet, swap enabled, thp enabled) are non-fatal — they self-resolve or are host-tuning, not blocking.
Confirm the smolvm build you have actually exposes the commands used here:
smolvm --help # should list: claude codex hermes pi openclaw sandbox ...
smolvm sandbox --help # should list: create exec snapshot file ...
smolvm pack --help # OPTIONAL — read §6 before relying on this
Caveat verified on this host: this build of smolvm does not expose
smolvm machine ...orsmolvm pack ...(onlysandbox,browser, and the agent launchers). Everything below is written for thesandboxfamily, which is what you have. The pack/machine flow is described in §6 for reference; if your build has it, use it.
1. The minimal mental model
You only need four smolvm primitives and three Tau facts:
| smolvm | What it does |
|---|---|
sandbox create |
Allocate a fresh Linux VM (alpine/ubuntu) with a name |
sandbox exec |
Run a command inside it (auto-starts the VM with --start) |
sandbox snapshot create / restore / list |
Save and rewind VM state — this is the "snapshot model" |
sandbox file upload / download |
Copy files in and out (sandbox must be running) |
| Tau | Where it lives |
|---|---|
| Sessions | ~/.tau/sessions/*.jsonl (append-only) |
| Custom providers/models | ~/.tau/catalog.toml |
| Project skills / instructions | AGENTS.md, .tau/, .agents/ (loaded from cwd up) |
| Skills shipped with the repo | tau_coding/skills/ (read-only) |
Pi keeps its config under ~/.pi/agent/ with skills at ~/.pi/agent/skills/, sessions configurable via PI_CODING_AGENT_SESSION_DIR or settings.json. The two agents do not share state directories.
2. Build the base sandbox
Pick a name — tau-pocket is the convention used in the rest of this guide.
# Alpine is small and fast for a coding-agent sandbox
smolvm sandbox create --name tau-pocket --os alpine --network nat
smolvm sandbox list # status: created
Install Tau inside the sandbox (one exec call chains everything):
smolvm sandbox exec --start tau-pocket -- sh -lc '
apk add --no-cache python3 py3-pip git curl bash
pip install --break-system-packages uv
uv tool install tau-ai
uv tool dir # so you can see where tau-ai was installed
mkdir -p /workspace && cd /workspace && git init -q
'
Now, /.local/bin is not in the PATH, so we have to add it by hand:
smolvm sandbox exec tau-pocket
No, we edit /etc/profile with vi /etc/profile and add './local/bin' at the end of the PATH line:
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/.local/bin"
We save the file and exit the VM with CTRL+d.
Sanity check before snapshotting:
smolvm sandbox exec tau-pocket -- tau --help | head -5
If the Tau version banner appears, the base is good. If not, re-run the apk / uv / uv tool install chain and read the error.
Snapshot the base so every later session can roll back to a known-good image:
smolvm sandbox snapshot create tau-pocket --snapshot-id tau-base
smolvm sandbox snapshot list
tau-base is now your anchor. If a later experiment breaks the sandbox, restore brings it back without re-installing anything.
3. Run a Tau session
The simplest pattern: keep /workspace as the working directory and let Tau read its AGENTS.md (project instructions) from there.
# Drop an AGENTS.md describing the project before starting
smolvm sandbox file upload tau-pocket ./AGENTS.md /workspace/AGENTS.md
Then either run a one-shot, non-interactive prompt:
smolvm sandbox exec --name tau-pocket -- sh -lc 'cd /workspace && tau -p "summarize the project"'
Or open a real interactive shell inside the sandbox:
smolvm sandbox shell tau-pocket
# you are now root in the sandbox
cd /workspace
tau # launches the Textual TUI
Stop the sandbox when you're done (state is preserved on the underlying disk):
smolvm sandbox stop tau-pocket
smolvm sandbox start tau-pocket # resume later
Filesystem note.
/tmp,/run, and/dev/shmare tmpfs and do not survive a stop+start. Write anything that must outlive a restart under/workspaceor~/.tau/. Package installs,/etc/edits, and~/.tau/itself are on the persistent overlay.
4. Snapshotting the learning timeline
Every time you reach a milestone worth keeping — "added the Git teacher skills", "wired compaction", "experimented with self-modification" — capture it:
# inside the sandbox
cd /workspace
git add -A && git commit -m "m1: add basic Git teacher skills" -q
tau -p "/exit"
# back on the host
smolvm sandbox snapshot create tau-pocket --snapshot-id m1-git-skills
The snapshot stores the full sandbox state — installed packages, ~/.tau/, /workspace, the works. The git commit is inside the snapshot, which is the "Git overlay" the old design document described, except you don't need to design it; the snapshot mechanism already handles it.
To rewind a failed experiment:
smolvm sandbox snapshot restore m1-git-skills
restore takes a snapshot id, not a sandbox name, which is the part that trips people up. You can list available snapshots with smolvm sandbox snapshot list (optionally --json for a machine-readable view).
A lightweight textual changelog is still useful as a human-readable index — drop it in /workspace so it travels with every snapshot:
# inside the sandbox
cat >> /workspace/CHANGELOG.md <<'EOF'
## m1-git-skills
- Snapshot: m1-git-skills (2026-08-06)
- Git: a1b2c3d "add basic Git teacher skills"
- Notes: oriented on Tau architecture; added teacher-mode skills.
EOF
git add CHANGELOG.md && git commit -m "changelog: m1" -q
Repeat per milestone. The host-side record is smolvm sandbox snapshot list; the in-sandbox record is CHANGELOG.md. Pick the one that answers your question: "what state is on disk?" → host list. "why does this state matter?" → changelog.
5. Importing /teacher-mode and an existing Tau configuration
The assumption is that /teacher-mode (and possibly a fully-configured ~/.tau) already lives on the host you're importing from. There are two paths — pick one.
5a. sandbox file upload (one file or one directory at a time)
# upload a tarball (recommended for directories; smolvm file upload
# treats the source as a single file path)
tar czf /tmp/teacher-mode.tgz -C /path/to/parent teacher-mode
smolvm sandbox file upload tau-pocket /tmp/teacher-mode.tgz /workspace/teacher-mode.tgz
# then inside the sandbox
smolvm sandbox exec --name tau-pocket -- sh -lc '
cd /workspace
tar xzf teacher-mode.tgz && rm teacher-mode.tgz
ls teacher-mode/ # confirm structure
'
Place /teacher-mode under /workspace/ (or under ~/.tau/ — see 5b) so it travels with every subsequent snapshot. The default place for project-scoped resources in Tau is .tau/ and .agents/ relative to the cwd; /workspace/teacher-mode is a clean convention that mirrors the source layout.
For a custom ~/.tau (a catalog.toml you've tuned, existing sessions you want to keep, a private skill set, etc.), archive and inject it the same way, but extract to the home directory:
# from the source host
tar czf /tmp/dot-tau.tgz -C ~ .tau
# upload + extract
smolvm sandbox file upload tau-pocket /tmp/dot-tau.tgz /tmp/dot-tau.tgz
smolvm sandbox exec --name tau-pocket -- sh -lc '
rm -rf ~/.tau && mkdir -p ~/.tau && tar xzf /tmp/dot-tau.tgz -C ~
ls -la ~/.tau/
'
Watch for:
sandbox file uploadrequires the sandbox to be running. If it isn't, the error is non-obvious — start it first or usesandbox exec --start ...for the extract step instead.~/.tauis on the persistent overlay, so once you've imported it, it survives stop/start and is captured by every snapshot.
5b. Mount-on-start (cleaner, when you iterate often)
If you'll be editing /teacher-mode on the host and want the sandbox to see changes live, pass a host path when creating the sandbox (or update an existing one):
# at create time
smolvm sandbox create --name tau-pocket --os alpine \
--mount /home/pikaos/teacher-mode:/workspace/teacher-mode \
--writable-mounts
# or update an existing one
smolvm sandbox update --name tau-pocket \
-v /home/pikaos/teacher-mode:/workspace/teacher-mode \
--writable-mounts
With --writable-mounts the host directory is read-write inside the guest. Without it, the mount is read-only — that's usually what you want for a stable skills directory.
Same approach for ~/.tau:
smolvm sandbox update --name tau-pocket \
-v /home/pikaos/.tau:/root/.tau \
--writable-mounts
Once mounted, restart the sandbox and verify:
smolvm sandbox stop tau-pocket
smolvm sandbox start tau-pocket
smolvm sandbox exec --name tau-pocket -- ls /workspace/teacher-mode
smolvm sandbox exec --name tau-pocket -- ls /root/.tau
Snapshot semantics with mounts. Anything in the mounted directory is not part of the sandbox disk — it lives on the host. A snapshot still captures the sandbox state, but the mount is re-attached on next start, so the effective content is the union of the snapshot and the current host directory. Plan accordingly: if
/teacher-modeis the source of truth and you want a particular revision frozen inside a snapshot, copy it in (file uploadorcp -a) rather than mounting it.
5c. Verify Tau actually picks up the skills
smolvm sandbox exec --name tau-pocket -- sh -lc '
tau -p "list the skills you can see and which file each came from"
'
Or, if Tau has a /skills slash command in your build:
smolvm sandbox exec --name tau-pocket -- tau -p "/skills"
If the skills aren't visible, the most common cause is the cwd: Tau reads .tau/ and .agents/ from the current working directory upward. Launch Tau from /workspace (or from wherever the skills directory sits) and they will be picked up.
Snapshot once /teacher-mode is wired in and you have a working setup:
smolvm sandbox snapshot create tau-pocket --snapshot-id teacher-mode-ready
6. Exporting the machine
This is the part that needs a clear-eyed note. "Export the machine" can mean three different things, and the right tool depends on which you want.
6a. Export the state (cheapest, most common)
This is what you usually want — the configured sandbox, with Tau installed and /teacher-mode in place, portable enough to bring to another host running smolvm.
# locate the snapshot on disk
smolvm sandbox snapshot list --json
# the snapshot record includes an id you can use to find the file;
# on Linux the snapshot storage lives under ~/.smolvm/sandboxes/...
# copy the whole directory tree (or a single snapshot id) somewhere portable:
cp -a ~/.smolvm/sandboxes/<id-or-vm-dir> /path/to/backup/
# or, if your build exposes `pack` (this host does NOT):
smolvm pack create --from-vm tau-pocket -o tau-pocket.smolmachine
The cp -a route is verified; the pack route is documented for builds that have it. On any host that has the same smolvm version and the same architecture, you can recreate the sandbox from the copied tree by pointing the new host at the snapshot id, or by using sandbox snapshot restore once you've registered the copied state.
Architecture caveat. smolvm snapshots are tied to the guest architecture (the kernel, virtio layout, and the userland). A
firecrackersnapshot onlinux/amd64does not move tolinux/arm64or to macOS Apple Silicon. The substrate is portable across compatible hosts, not across architectures.
6b. Export the workspace (lighter, often enough)
If what you actually want to share is the project state (code, skills, changelog, sessions), export just the persistent bits — no need to ship the full VM:
# from inside the sandbox
smolvm sandbox exec --name tau-pocket -- sh -lc '
tar czf /tmp/pocket-export.tgz \
/workspace /root/.tau /etc/profile.d 2>/dev/null
'
smolvm sandbox download tau-pocket /tmp/pocket-export.tgz ./pocket-export.tgz
# on a fresh host: create a new sandbox and import
smolvm sandbox create --name tau-pocket --os alpine
smolvm sandbox file upload tau-pocket ./pocket-export.tgz /tmp/pocket-export.tgz
smolvm sandbox exec --start --name tau-pocket -- sh -lc '
apk add --no-cache python3 py3-pip git && pip install --break-system-packages uv && uv tool install tau-ai
tar xzf /tmp/pocket-export.tgz -C /
cd /workspace && tau
'
This is what you want when the receiving host also has smolvm but a different kernel build, or when you only care about project state, not the installed system packages.
6c. Export the image (heaviest, most portable across builds)
If your smolvm build has pack, you can produce a single self-contained binary that boots the configured state on any compatible host:
# only if `smolvm pack` is available:
smolvm pack create --from-vm tau-pocket -o tau-pocket.smolmachine
./tau-pocket.smolmachine run -- tau -p "hello"
This host does not have pack; treat 6a/6b as the working path until your build exposes it. If you upgrade smolvm specifically for this, verify with smolvm pack --help first.
7. Switching the agent to Pi (or another bundled agent)
The substrate is the constant; the agent is pluggable. Two ways to switch.
7a. Launch a different agent in a parallel sandbox (recommended)
# keep tau-pocket running for Tau work
smolvm sandbox create --name pi-pocket --os alpine --network nat
smolvm sandbox exec --start --name pi-pocket -- sh -lc '
apk add --no-cache nodejs npm git bash
npm i -g @mariozechner/pi-coding-agent
mkdir -p /workspace && cd /workspace && git init -q
'
smolvm sandbox exec --name pi-pocket -- pi --help | head -5
smolvm sandbox snapshot create pi-pocket --snapshot-id pi-base
Pi keeps its config in ~/.pi/agent/ (skills at ~/.pi/agent/skills/), so the import dance in §5 is the same shape but with ~/.pi as the target. Two sandboxes, two agents, one mental model.
7b. Use the bundled launchers for one-off sessions
smolvm ships a launcher per popular agent. These are pre-baked images with the CLI already installed — useful for quick interactive sessions, not for "the pocket" (which is a configured, skill-laden workspace).
smolvm hermes start --name hermes-pocket # if you want Hermes for a session
smolvm claude start --name claude-pocket
smolvm codex start --name codex-pocket
smolvm pi start --name pi-quick
These are the same sandbox family under the hood; the launchers just pre-fill the install step. Once the launcher has created a sandbox, you can sandbox exec / sandbox snapshot it like any other.
Why not pin one agent forever? The substrate is the constant. Tau is the primary, Pi is the alternative. Each gets its own sandbox, its own snapshot chain, and its own import of
/teacher-mode(the skills are written in plain Markdown and travel verbatim; the only difference is where the agent looks for them —~/.tauvs~/.pi/agent).
8. The full "do it once" recipe
For a clean machine, this is the whole thing top to bottom:
# 0. one-time check
smolvm doctor
# 1. create + install tau
smolvm sandbox create --name tau-pocket --os alpine --network nat
smolvm sandbox exec --start --name tau-pocket -- sh -lc '
apk add --no-cache python3 py3-pip git curl bash
pip install --break-system-packages uv
uv tool install tau-ai
mkdir -p /workspace && cd /workspace && git init -q
'
# 2. import /teacher-mode + ~/.tau
smolvm sandbox file upload tau-pocket /path/to/teacher-mode.tgz /workspace/teacher-mode.tgz
smolvm sandbox exec --name tau-pocket -- sh -lc '
cd /workspace && tar xzf teacher-mode.tgz && rm teacher-mode.tgz
'
# 3. snapshot the base
smolvm sandbox snapshot create tau-pocket --snapshot-id tau-base
# 4. iterate: session -> snapshot per milestone -> restore on rollback
smolvm sandbox shell tau-pocket # run tau inside
smolvm sandbox snapshot create tau-pocket --snapshot-id m1
smolvm sandbox snapshot restore m1 # if m2 broke
smolvm sandbox snapshot list # the timeline
That's the project. The earlier "Decision Summary" tried to design all of this from scratch; the CLI was already there.
9. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
sandbox file upload fails with no clear reason |
sandbox not running | smolvm sandbox start tau-pocket first, or use sandbox exec --start |
Tau doesn't see /teacher-mode skills |
wrong cwd when launching | cd /workspace before running tau; Tau reads .tau/ and .agents/ from cwd upward |
Packages missing after stop + start |
written under /tmp, /run, or /dev/shm |
rewrite under /workspace or ~/.tau/ — those are persistent |
sandbox snapshot restore says "not found" |
passed the sandbox name instead of the snapshot id | use sandbox snapshot list to get the id, then restore <id> |
| Two sandboxes both want to mount the same host dir | one-shot conflict | unmount the host dir from one of them, or use file upload instead |
smolvm pack / smolvm machine not present |
this build of smolvm | use §6a/§6b (snapshot tree copy + workspace tarball) |
smolvm doctor shows swap / thp warnings |
host tuning, not blocking | sudo swapoff -a and echo never > /sys/kernel/mm/transparent_hugepage/enabled if you want clean output |
10. What this tutorial deliberately does not do
- No custom substrate. smolvm is the substrate. Adding a second sandbox technology re-introduces the problem smolvm already solves.
- No custom snapshot file format.
smolvm sandbox snapshot createis the snapshot model. The earlier doc's Phase 1/2 ("Build the Base Machine", "Snapshot & Changelog Wiring") is two CLI calls. - No hand-rolled changelog daemon.
smolvm sandbox snapshot listis the host-side log.CHANGELOG.mdinside/workspaceis the why-we-care log. Both are optional, both are trivial. - No agent lock-in. Tau is the primary; Pi, Hermes, Claude, Codex, and OpenClaw are all reachable from the same smolvm install. The pocket is the workspace, not the agent.
- No architecture-cross-portability claim. smolvm snapshots are architecture-bound. A
linux/amd64snapshot is not alinux/arm64or macOS binary. The substrate is portable across compatible hosts, not all hosts.