Cheap, branch-backed repo views for parallel coding agents.
wafers lets many agents work against one large Git checkout without paying
for many clones or worktrees. It creates lightweight writable views with
fuse-overlayfs: the base repo stays read-only, and each agent gets its own
empty upperdir for changes.
When an agent is done, wafers commits the full wafer view onto a normal local
Git branch in the base repo. From there, you can inspect, diff, merge, or push
the branch with regular Git.
base repo checkout
|
| lowerdir
v
wafer mount /tmp/agent-1 ---> commit ---> refs/heads/agents/agent-1
wafer mount /tmp/agent-2 ---> commit ---> refs/heads/agents/agent-2
wafer mount /tmp/agent-3 ---> commit ---> refs/heads/agents/agent-3
This is not a sandbox. It is filesystem fanout machinery. Pair it with a real sandbox/container boundary if you need security isolation.
Large-agent fanout has an annoying local filesystem problem:
- full clones are expensive for large repos
- worktrees are cheaper, but still materialize a full working tree
- sharing one checkout between agents is a fast path to conflicts
wafers uses overlay filesystems for the shape agents usually need: many
mostly-read views with small independent write sets.
This is early, Linux-only, and intentionally narrow. It can:
- check host support for
fuse-overlayfs - create named wafer views at chosen mountpoints
- create or attach one local branch per wafer
- hide
.gitinside wafer views so agents do not run Git there by accident - commit the wafer view onto the wafer branch
- commit only selected paths when scratch files should remain uncommitted
- list and remove wafers
Not implemented yet:
- remote push helpers
- partial staging /
git-add - submodule or Git LFS special handling
- non-Linux support
Host requirements:
- Linux
gitfuse-overlayfsfusermount3- accessible
/dev/fuse
Check the host:
wafers doctorInstall with Go:
go install github.com/satmihir/wafers/cmd/wafers@latestMake sure Go's binary directory is on your PATH. It is usually:
$(go env GOPATH)/binFrom a local checkout, build directly:
go build -o wafers ./cmd/wafersThe module currently targets the latest Go line used by this project.
From inside an existing Git repo:
wafers doctor
wafers add agent-1 --at /tmp/agent-1 --branch agents/agent-1This records the current HEAD, creates local branch agents/agent-1 at that
commit if needed, mounts the wafer at /tmp/agent-1, and hides .git in the
wafer view.
Work inside the wafer:
cd /tmp/agent-1
printf 'hello\n' >new-file.txtCommit the wafer view:
wafers git-commit agent-1 -m "agent changes"Inspect or push from the base repo:
git log --oneline --decorate --graph --all
git show --stat agents/agent-1
git push origin agents/agent-1Clean up the mounted view:
cd /tmp
wafers rm agent-1 --forcewafers rm removes wafer state and the mount. It keeps the local branch and
commits.
wafers add <name> --at <mountpoint> --branch <branch> [--from <repo>]
wafers git-commit <name> -m <message> [-- <paths>...]
wafers git-diff <name>
wafers ls
wafers rm <name> [--force]
wafers doctor
wafers skill
wafers help [command]
wafers version
Run wafers help <command> for command-specific examples and notes.
Creates a wafer for a local branch.
wafers add agent-1 --from /src/repo --at /tmp/agent-1 --branch agents/agent-1Rules:
--fromdefaults to the current directory.- the base repo worktree must be clean, except for ignored files.
--branchmust be a valid branch name.- if the branch does not exist, wafers creates it at the current base
HEAD. - if the branch already exists, it must descend from the current base
HEAD, and wafers replays its changes into the mounted wafer view. - a branch can be owned by only one wafer at a time.
- the mountpoint is created if missing, but must be empty.
.gitis hidden in the wafer view with overlay whiteout state.
Commits the entire mounted wafer view.
wafers git-commit agent-1 -m "fix parser"To commit only selected paths, pass paths after --:
wafers git-commit agent-1 -m "fix parser" -- parser.go go.modInternally, this is similar to:
git add -A
git commitbut it uses a wafer-private index and Git plumbing. The base repo worktree and
base repo index are not touched. The wafer branch is advanced atomically; if
the branch moved outside wafers, the command refuses to overwrite it. Path
arguments are relative to the wafer root; absolute paths and parent traversal
are refused.
Shows committed wafer branch changes.
wafers git-diff agent-1This is equivalent to diffing the wafer branch against the base commit recorded when the wafer was created. The wafer does not need to be mounted. Uncommitted edits in the wafer mount are not included.
Unmounts and removes wafer state.
wafers rm agent-1If the wafer has changes, removal is refused unless you pass:
wafers rm agent-1 --forceIf unmounting fails with Device or resource busy, make sure no shell or
process has the wafer mountpoint as its current working directory.
Checks the Linux/FUSE prerequisites and reports whether Git can resolve an
author identity for commits. Missing user.name or user.email is a warning,
not a platform failure, but wafers git-commit needs an identity from Git config
or GIT_AUTHOR_NAME/GIT_AUTHOR_EMAIL.
Prints a generic agent skill/instructions file for using wafers correctly.
wafers skill > SKILL.mdInstall that SKILL.md wherever your agent runner loads skills or project
instructions. The generated skill tells agents to work inside wafer mountpoints,
commit through wafers git-commit, push branches from the base repo, and avoid
editing the base checkout.
The demo/ directory contains a Linux/FUSE setup for trying the project even
from a non-Linux development machine with Docker.
Automated demo:
docker compose -f demo/compose.yaml up --build --abort-on-container-exitInteractive shell:
docker compose -f demo/compose.yaml run --rm wafers-demo bashSee:
demo/README.mddemo/MANUAL_TEST.md
Run the default test suite:
go test ./...Run the Linux/FUSE integration tests on a host with fuse-overlayfs and
/dev/fuse:
WAFERS_INTEGRATION=1 go test ./internal/cli -run TestIntegration -count=1Run the full Docker demo:
docker compose -f demo/compose.yaml up --build --abort-on-container-exitThe default suite uses temp Git repos but does not require FUSE. Real mount behavior is covered by the gated integration tests and Docker demo.
- State lives under
$XDG_STATE_HOME/wafers, or~/.local/state/waferswhenXDG_STATE_HOMEis unset. - Each wafer has its own upperdir, workdir, metadata file, and private Git index.
- The base repo worktree and index are not modified by wafer lifecycle or commit commands.
- Git objects and branch refs are written through the base repo's Git database.
.gitis hidden in wafer views to discourage direct Git usage inside them.- Mountpoints should live outside other Git repos so Git cannot discover a
parent
.git. wafersis not a security boundary.
