> ## 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.

# fs.mount()

> Mount one or more repositories as a Mesa virtual filesystem.

`mesa.fs.mount(...)` is an async context manager that yields a `MesaFileSystem`.

```python theme={null}
from mesa_sdk import Mesa

mesa = Mesa(org="acme")

async with mesa.fs.mount(repos=["app"]) as fs:
    data = await fs.read("/acme/app/README.md")
    print(data.decode())
```

Under the hood, `mount()` signs a single short-lived, repo-scoped access token (JWT) locally from your API key and connects to the VCS backend. The token's repo scope is encoded as full `org/repo` names, so signing does not resolve repository ids over the network. The mount uses that one token for its whole lifetime: there is no background refresh and no credential hot-swap, so once the token expires the mount stops authenticating. Choose a `ttl` that covers the mount's work. No server-side credential is created or revoked.

## Options

<ParamField path="repos" type="Sequence[str | RepoConfig]" required>
  Repositories to mount. Each entry can be a bare repo name such as `app`, a matching `org/repo` slug such as `acme/app`, or a `RepoConfig` for bookmark/change pinning and read-only control. The sequence must be non-empty.
</ParamField>

<ParamField path="ttl" type="int | None">
  Lifetime of the mount, in seconds. The mount mints one access token with this TTL and uses it until it expires; there is no refresh. Defaults to `3600` (1 hour). Maximum `86400` (24 hours). A value outside `1..86400` raises `InvalidOptionsError`.
</ParamField>

<ParamField path="disk_cache" type="DiskCacheConfig | None">
  Optional on-disk cache. When omitted, the mount uses in-memory caching only.
</ParamField>

## RepoConfig

Pin a repository to a bookmark or change at mount time.

```python theme={null}
from mesa_sdk import RepoConfig

async with mesa.fs.mount(
    repos=[
        RepoConfig(name="app", bookmark="main"),
        RepoConfig(name="docs", change_id="zyxwvutsrqponmlkzyxwvutsrqponmlk"),
    ]
) as fs:
    ...
```

<ParamField path="RepoConfig.name" type="str" required>
  Repository name.
</ParamField>

<ParamField path="RepoConfig.bookmark" type="str | None">
  Bookmark to check out.
</ParamField>

<ParamField path="RepoConfig.change_id" type="str | None">
  Change ID to check out.
</ParamField>

<ParamField path="RepoConfig.read_only" type="bool">
  When `True`, the mesa daemon rejects writes to this repo with an `OSError` whose `errno` is `errno.EROFS`, so a single mount can mix writable and read-only repos.
</ParamField>

## DiskCacheConfig

```python theme={null}
from mesa_sdk import DiskCacheConfig

cache = DiskCacheConfig(path="/tmp/mesa-cache", max_size_bytes=500_000_000)
```

<ParamField path="DiskCacheConfig.path" type="str" required>
  Directory for the on-disk cache.
</ParamField>

<ParamField path="DiskCacheConfig.max_size_bytes" type="int | None">
  Optional cache size cap. When omitted, the native extension auto-sizes the budget against system resources.
</ParamField>

## Paths

Mounted filesystem paths include the organization and repository name:

```text theme={null}
/<org>/<repo>/README.md
/acme/app/src/main.py
```

Passing `repos=["acme/app"]` is accepted only when `acme` matches the resolved client organization. Cross-org mounts are rejected with `InvalidOptionsError`.

## Response

Yields a `MesaFileSystem`.

## MesaFileSystem

The yielded `fs` object exposes async file I/O, metadata, traversal, mutation, Bash, and mounted-repo version-control helpers.

```python theme={null}
async with mesa.fs.mount(repos=["app"]) as fs:
    await fs.mkdir("/acme/app/src", recursive=True)
    await fs.write("/acme/app/src/main.py", b"print('hello')\n")
    data = await fs.read("/acme/app/src/main.py")
```

### Byte I/O

<ParamField path="read(path)" type="async -> bytes">
  Read a file as bytes.
</ParamField>

<ParamField path="write(path, content)" type="async -> None">
  Replace file contents, creating the file if missing. Parent directories must already exist.
</ParamField>

<ParamField path="append(path, content)" type="async -> None">
  Append bytes to a file, creating it if missing.
</ParamField>

<ParamField path="exists(path)" type="async -> bool">
  Return whether a path exists. Follows symlinks.
</ParamField>

### Metadata and traversal

<ParamField path="stat(path)" type="async -> FsStat">
  Return metadata for a path, following symlinks.
</ParamField>

<ParamField path="lstat(path)" type="async -> FsStat">
  Return metadata for a path without following symlinks.
</ParamField>

<ParamField path="readdir(path)" type="async -> list[str]">
  Return entry names in a directory. Sort client-side if you need deterministic ordering.
</ParamField>

<ParamField path="realpath(path)" type="async -> str">
  Resolve symlinks and `..` segments to a canonical path.
</ParamField>

<ParamField path="readlink(path)" type="async -> str">
  Return the target of a symlink.
</ParamField>

<ParamField path="resolve_path(base, path)" type="str">
  Join and normalize a path against a base path without touching the filesystem.
</ParamField>

### Mutations

<ParamField path="mkdir(path, recursive=None)" type="async -> None">
  Create a directory. With `recursive=True`, create missing parents and do nothing when the path already exists as a directory.
</ParamField>

<ParamField path="rm(path, recursive=None, force=None)" type="async -> None">
  Remove a file or directory. Use `recursive=True` for non-empty directories and `force=True` to ignore missing paths.
</ParamField>

<ParamField path="cp(src, dest, recursive=None)" type="async -> None">
  Copy a file or directory. Use `recursive=True` for directories.
</ParamField>

<ParamField path="mv(src, dest)" type="async -> None">
  Move or rename a file or directory.
</ParamField>

<ParamField path="chmod(path, mode)" type="async -> None">
  Set permission bits, such as `0o755`.
</ParamField>

<ParamField path="symlink(target, link)" type="async -> None">
  Create a symlink. Relative targets are stored verbatim and resolve against the parent of `link` at read time.
</ParamField>

<ParamField path="utimes(path, atime_ms, mtime_ms)" type="async -> None">
  Set access and modification times. Values are milliseconds since the Unix epoch, not seconds.
</ParamField>

<ParamField path="link(existing, new)" type="None">
  Hard links are not supported and this method raises `NotImplementedError`.
</ParamField>

### Subscriptions

MesaFS reads and writes are realtime by default. Use subscriptions only when your process needs an event stream that identifies which paths changed, such as to refetch data and rerender a frontend.

<ParamField path="subscribe(handler)" type="MesaFileSystemSubscription">
  Subscribe to filesystem invalidation events. The handler is called after the changed state is visible through this filesystem instance.
</ParamField>

```python theme={null}
async def on_change(event):
    if not event.recursive:
        content = await fs.read(event.path)
        print(event.path, content.decode())

subscription = fs.subscribe(on_change)
await subscription.unsubscribe()
```

<ParamField path="handler" type="Callable[[WatchEvent], None | Awaitable[None]]">
  Callback invoked for each filesystem invalidation.
</ParamField>

<ResponseField name="WatchEvent.path" type="str">
  Absolute MesaFS path that changed, such as `/acme/app/src/index.py`.
</ResponseField>

<ResponseField name="WatchEvent.recursive" type="bool">
  Whether descendants of `path` may have changed. Refresh any cached directory or subtree state below `path` when this is `True`.
</ResponseField>

<ResponseField name="MesaFileSystemSubscription.unsubscribe()" type="async -> None">
  Stop receiving events and close the underlying watcher.
</ResponseField>

### FsStat

`stat(...)` and `lstat(...)` return `FsStat`.

<ResponseField name="is_file" type="bool">
  Whether the path is a regular file.
</ResponseField>

<ResponseField name="is_directory" type="bool">
  Whether the path is a directory.
</ResponseField>

<ResponseField name="is_symbolic_link" type="bool">
  Whether the path is a symlink. This is `False` from `stat(...)` when the target exists because `stat` follows symlinks.
</ResponseField>

<ResponseField name="mode" type="int">
  POSIX mode bits.
</ResponseField>

<ResponseField name="size" type="int">
  Size in bytes.
</ResponseField>

<ResponseField name="mtime_ms" type="float">
  Modification time in milliseconds since the Unix epoch.
</ResponseField>

### Related filesystem methods

| Method namespace    | Reference                                              |
| ------------------- | ------------------------------------------------------ |
| `fs.bash(...)`      | [fs.bash()](/content/reference/py/fs-bash)             |
| `fs.changes`        | [fs.changes](/content/reference/py/fs-changes)         |
| `fs.bookmarks`      | [fs.bookmarks](/content/reference/py/fs-bookmarks)     |
| `fs.subscribe(...)` | [Advanced Realtime](/content/mesafs/advanced/realtime) |

## Errors

Raises `InvalidOptionsError` for an empty repo list, a `ttl` outside `1..86400`, invalid `mode`, or a mismatched org prefix. Token signing or VCS connection failures can raise `ApiError` subclasses or connection errors.

## Multiprocessing

MesaFS is not fork-safe. If you use `multiprocessing`, set the start method to `spawn` or `forkserver` before creating Mesa objects.

```python theme={null}
import multiprocessing

multiprocessing.set_start_method("spawn")
```
