mesa mount --layout).
Mounting a layout from the SDK
Declare each repository withrepo(...), map absolute paths to declarations, and call mesa.fs({ layout }) to build a definition — the one value that carries a layout and everything done with it. This example mounts an application repository at /workspace with two read-only skills repositories nested inside its tree:
mode is the repository’s access mode. It is required on every declaration and has no default: "rw" allows writes, "ro" rejects them with EROFS. The mount mints a least-privilege access token scoped by name to exactly the layout’s repositories — read-only when every declaration is "ro".
Composing a layout from a repository query
A layout is a plain value, so the repository set does not have to be hard-coded. Query repositories first — for example with a tag filter onrepos.list — and map the result into declarations. Calling mesa.fs({ layout }) prepares the layout and returns a definition whose mount() opens it (see Running the mount elsewhere for the definition’s other operations):
.agents/skills, named after the repository. Two things to keep in mind when the set is dynamic: repos.list is paginated, so follow next_cursor when has_more is set before building the layout, and a repository can appear only once per layout, so deduplicate if your queries can overlap.
The layout file
A layout serializes to the JSON format thatmesa mount --layout reads. The document is a pure path map: every top-level key is an absolute mount path, and each value is one repository declaration or an array of them. The file does not include organization configuration (see Organization scope).
layout.json
definition.layout() returns exactly this form. Serialize it with JSON.stringify(...) in TypeScript or json.dumps(...) in Python.
A path’s value is one declaration or an array, and the two mean different things:
- Single declaration — the repository’s contents appear directly at the path (
/workspace/README.mdismy-app’sREADME.md). - Array — each repository appears in its own child directory under the path, named after the repository;
aliasoverrides the directory name.
Declaration fields
When neither
at nor branchedFrom is set, the mount checks out the repository’s default bookmark.
Structural rules:
- Top-level keys must be absolute (
/-prefixed);subPathskeys must be relative. /itself cannot be a mount path.- Path components cannot be
.or... - A repository can appear only once per layout.
- Two declarations cannot expand to the same path.
Mounting a layout with the CLI
Pass the file tomesa mount:
~/.local/share/mesa/mnt/workspace.
The file is validated while arguments are parsed — a missing file, invalid JSON, or a rule violation fails immediately, before any mount work.
Organization scope
The layout file does not include organization configuration. Every repository in the layout must belong to the same organization; cross-org layouts are not supported.Running the mount elsewhere
A common shape: your backend composes the layout and holds the private key, while the mount runs in a sandbox that should only ever see a scoped, short-lived access token. Callingmesa.fs({ layout, authors, ttl }) validates the raw Layout and bundles it with the operations that flow needs: layout() returns an independent plain-data snapshot, and token() mints the layout’s least-privilege access token with the definition’s ttl.
await fsDefinition.mount() in TypeScript, async with fs_definition.mount() as fs: in Python. Private-key clients pass authors when building the definition. mesa.fs(...) validates the layout against the structural rules above before returning, so a structurally invalid layout fails immediately. Repository names are not resolved until mount time, so a layout naming a nonexistent repository still produces a definition and token, then fails when the mount resolves it.
ttl is the token lifetime in seconds. Private-key clients default to 15 minutes and allow up to four hours. There is no refresh: once the token expires, the mount stops authenticating.
Repository boundaries
Nesting changes where repositories appear, not how they behave. Each declaration stays its own repository with its own history, and the deepest mount owns each subtree:- Writes route to the repository that owns the path: a write under a nested mount lands in the nested repository, never in the repository it sits inside.
- Renames cannot cross repositories —
renameacross a boundary returnsEXDEV. Tools likemvfall back to copy-and-delete, which writes to both repositories. - The directory at a nested mount’s path cannot itself be renamed or removed.
- Each repository mount enforces its own
mode: a read-write repository can nest read-only ones, and only the read-only subtrees reject writes withEROFS. - Intermediate directories a layout introduces (for example
/tools/internalon the way to/tools/internal/cli) are read-only scaffolding.
mesa mount flags, see the CLI reference.
