Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.mesa.dev/llms.txt

Use this file to discover all available pages before exploring further.

Mesa’s virtual filesystem lets tools interact with repository files directly — no full clone required. Instead of pulling an entire repo to disk, Mesa fetches data on demand and presents it through a standard filesystem interface. Reads and writes go through Mesa’s API transparently, so any tool that works with files can work with Mesa repositories. There are two ways to use the virtual filesystem: OS-level virtualization (FUSE mount) and app-level virtualization (just-bash SDK). Both give you the same underlying Mesa filesystem — the difference is how it’s exposed to your tools.

OS-level virtualization (FUSE)

OS-level virtualization uses FUSE to mount repositories as real directories on the host. Every process on the machine — editors, language servers, build systems, agents — sees standard files and directories at a mount path like ~/.local/share/mesa/mnt/my-org/my-repo. Use OS-level virtualization when:
  • Your agent needs to install dependencies (npm install, pip install, etc.) or run arbitrary code
  • You’re working inside a sandboxed environment (Docker container, Daytona workspace, lightweight VM)
  • You need multiple processes to access the filesystem concurrently through native file paths
  • Your tooling requires a real mount point (e.g., build systems, compilers, language servers)
See CLI Mounts for setup and usage.

App-level virtualization (just-bash)

App-level virtualization runs entirely in-process via the @mesadev/sdk package. It integrates with Vercel’s just-bash library to let your agent execute shell commands (ls, cat, grep, cp, etc.) against Mesa repositories — no mount, no FUSE, no sandbox required. Use app-level virtualization when:
  • You have a TypeScript agent running in your own backend and don’t want to manage a sandbox
  • Your agent only needs to read/write files and run shell commands (no dependency installs or arbitrary binaries)
  • You want the simplest possible setup — just npm install @mesadev/sdk and go
  • You’re building with frameworks like Vercel AI SDK, Mastra, or Langchain and want to add a bash tool
See Application Mount for setup and usage.

Choosing between the two

OS-level (FUSE)App-level (just-bash)
SetupRequires FUSE + sandbox/containernpm install only
Install dependenciesYesNo
Run arbitrary binariesYesNo (bash builtins + optional Python/JS)
Multi-process accessYesSingle process
EnvironmentAny (Docker, VMs, local)Node.js backend
Best forFull dev environments, CI, sandbox agentsLightweight agents, multi-tenant backends
Both modes support read and write operations and use the same caching and prefetching under the hood.

Writing to a mount

When you mount — through either interface — you start in observe mode. Reads reflect the revision you mounted (typically a bookmark like main, or a specific change), and the first write through the mount forks a new change on top of that base rather than modifying it in place. Before your first write:
@  qzvqqupx   main       ← mounted here
○  ovknlmro
◆  root
After your first write:
@  zkxwvnoj              ← new change — writes land here
○  qzvqqupx   main       ← unchanged; bookmark did not move
○  ovknlmro
◆  root
Only the first write forks; subsequent writes land in that same newly-created change. The mounted bookmark is never moved implicitly. This is intentional — it keeps main (or whatever you mounted) safe from being accidentally rewritten by agent writes until you’re ready to accept the modifications. To make a bookmark point to your new writes, you must explicitly signal your intent with one of two paths:

Move the bookmark after writing (typical)

Let the fork happen, then move the bookmark onto the new change once you’re ready to publish the writes. The fork is only exposed through the mount, so grab the current change id from fs.change.list before moving the bookmark:
const fs = await mesa.fs.mount({
  repos: [{ name: "my-repo", bookmark: "main" }],
  mode: "rw",
});

// Do some writes...

// After your first write, the mount is pinned to the forked change.
const current = await fs.change.current({ repo: "my-repo" });

// Move the bookmark onto the new change to "accept" the writes.
await mesa.bookmarks.move({
  repo: "my-repo",
  bookmark: "main",
  change_id: current.changeId,
});
This is the normal flow for a “write, inspect, then publish” pattern — especially when an agent is producing the writes and a human (or a later step) decides when the bookmark should advance.

Enter edit mode on a specific change (situational)

Switch the mounted repo into edit mode on a specific change. Writes directly edit that change, and any bookmark pointing at it will always track latest writes in that change.
const fs = await mesa.fs.mount({
  repos: [{ name: "my-repo", bookmark: "main" }],
  mode: "rw",
});

// Switch to the change you want to edit directly (does not create a new change).
await fs.change.edit({ repo: "my-repo", bookmark: "main" });

// Do some writes...
Use this when you explicitly want in-place edits — for example, resuming work on a WIP change where every write should advance the bookmark without a separate “publish” step. See Versioning for the full change and bookmark model.