Skip to main content

Crate sandfs

Crate sandfs 

Source
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::mount makes a workspace over a source and mounts it at a folder.
  • Home::fork copies 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::changes lists the files a workspace added, modified or deleted, against its source or another workspace, and with ChangesOptions::copy_to writes them into a folder.
  • Home::update moves a workspace onto a newer version of its source, keeping its own changes, even while it is mounted.
  • Home::workspaces, Home::unmount, Home::remove and Home::clean list, unmount and delete them, and free downloaded space.
  • Home::serve_mcp hands a workspace to an agent as MCP file tools, and Home::shell runs 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 mount feature, 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::changes found.
ChangesOptions
What Home::changes lists, and where it copies them.
Cleaned
What Home::clean did with one local copy of a remote repository.
CrossDevice
Home::fork refused to freeze a plain folder, since folder is 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/copies at that disk’s top, so this is a folder mounted inside the one forked, or a disk whose top sandfs can’t write. Set ForkOptions::allow_copy to copy the files instead, or mount the folder itself with Home::mount, which shows its later changes too. Find it with err.downcast_ref::<sandfs::CrossDevice>().
ForkOptions
How Home::fork makes a fork.
Forked
A fork Home::fork made.
Home
A folder of workspaces: each one’s source, saved changes and mount.
MountOptions
How Home::mount makes a workspace.
Mounted
A workspace Home::mount mounted.
ServeOptions
How Home::serve_mcp makes a workspace over a source.
Trimmed
What Home::trim let go of.
Updated
What crate::Home::update did.
WorkspaceInfo
A workspace, as Home::workspaces lists it.

Enums§

ChangeKind
How a file differs from the mounted base.
CleanOutcome
See Cleaned.

Functions§

handle_relaunch
Call this first thing in main of a program that uses sandfs, before anything else reads the arguments.