Expand description
Copies of folders that cost nothing to make, saved on disk and mounted as real
folders any program can work in: FUSE on Linux, and on macOS an NFS server on
localhost that macOS’s own client mounts, so nothing needs installing. This is the
library behind the sandfs command, for a program that wants those copies without
anyone installing or running sandfs.
Workspaces live in a Home, a folder of their saved changes. Each is over a
source, read lazily and never written to: a folder on disk, a commit of a git
repository (a remote one is fetched a file at a time, as files are read), or a
frozen copy of a plain folder. Every command of sandfs is a method of Home
that returns what it found:
Home::mountmakes a workspace over a source and mounts it at a folder.Home::forkcopies a workspace, with its changes so far and none of its source, mounted or kept as a snapshot; a copy of a git clone can also be a real folder of cloned files, as it is by default on macOS.Home::changeslists the files a workspace added, modified or deleted, against its source or another workspace, and withChangesOptions::copy_towrites them into a folder.Home::updatemoves a workspace onto a newer version of its source, keeping its own changes, even while it is mounted.Home::workspaces,Home::unmount,Home::removeandHome::cleanlist, unmount and delete them, and free downloaded space.Home::serve_mcphands a workspace to an agent as MCP file tools, andHome::shellruns a command confined to a mount by the operating system’s sandbox.
// First thing in `main`: sandfs runs this program again to serve mounts.
sandfs::handle_relaunch(&[]);
let home = sandfs::Home::locate()?;
// A folder, mounted at /tmp/docs; /srv/docs itself is never written to.
let docs = home.mount("/srv/docs", "/tmp/docs".as_ref(), &Default::default())?;
// A copy of it, mounted at /tmp/attempt, which any program can work in.
let attempt = home.fork(&docs.name, Some("/tmp/attempt".as_ref()), &Default::default())?;
std::fs::write("/tmp/attempt/notes.md", "draft\n")?;
for change in home.changes(&attempt.name, &Default::default())?.changes {
println!("{:?} {}", change.kind, change.path); // Added notes.md
}
home.remove(&attempt.name, true)?;examples/attempts.rs in the repository runs a whole round trip in a temporary
folder: two copies of a folder, changed two ways, compared, and one written back.
Graft, which gives each task its own copy of a
GitHub repository, is a program built on this crate.
§The one line in main
A mount is served by one background process per home, which outlives the program
that asked for it. That process is this program, started again, so
handle_relaunch must be the first thing in main: in those runs it serves the
mounts and exits. A program with commands of its own passes a word none of them
uses, such as &["sandfs"].
§Before you build on it
- sandfs is pre-1.0: a minor release (0.9 to 0.10) may change this API, and the
changelog says how. Depend on one minor version, such as
sandfs = "0.9". - Every file operation in a mount goes through the mount’s process, and on macOS each write is also a round trip over localhost before it returns, so write-heavy work runs slower there than in a plain folder: compiling ripgrep from scratch took about 18 s in a mount against 13 s outside one on the same Mac (October 2026).
- Mounting needs the default
mountfeature, and on Linux FUSE 3 (fusermount3). Without it, and on Windows, everything but mounting works.
sandfs-core is the in-memory engine underneath: sources, forks and changes, with
nothing on disk.
Structs§
- Change
- One changed file.
- Changes
- What
Home::changesfound. - Changes
Options - What
Home::changeslists, and where it copies them. - Cleaned
- What
Home::cleandid with one local copy of a remote repository. - Cross
Device Home::forkrefused to freeze a plain folder, sincefolderis on another disk than the copy, and a file is cloned only within one disk. A folder on another disk than the home is copied onto its own disk, into.sandfs/copiesat that disk’s top, so this is a folder mounted inside the one forked, or a disk whose top sandfs can’t write. SetForkOptions::allow_copyto copy the files instead, or mount the folder itself withHome::mount, which shows its later changes too. Find it witherr.downcast_ref::<sandfs::CrossDevice>().- Fork
Options - How
Home::forkmakes a fork. - Forked
- A fork
Home::forkmade. - Home
- A folder of workspaces: each one’s source, saved changes and mount.
- Mount
Options - How
Home::mountmakes a workspace. - Mounted
- A workspace
Home::mountmounted. - Serve
Options - How
Home::serve_mcpmakes a workspace over a source. - Trimmed
- What
Home::trimlet go of. - Updated
- What
crate::Home::updatedid. - Workspace
Info - A workspace, as
Home::workspaceslists it.
Enums§
- Change
Kind - How a file differs from the mounted base.
- Clean
Outcome - See
Cleaned.
Functions§
- handle_
relaunch - Call this first thing in
mainof a program that uses sandfs, before anything else reads the arguments.