---
title: FUSE
icon: hard-drive
description: Set up FUSE support for MIRAGE in Python.
---

## Prerequisites

- Python 3.11+
- [uv](https://docs.astral.sh/uv/) package manager (optional)

## System FUSE

Install the OS-level FUSE kernel extension first:

- [macOS FUSE Setup](/home/setup/macos), macFUSE + kernel extension + Apple Silicon recovery mode steps.
- [Linux FUSE Setup](/home/setup/linux), `fuse3` install and `/etc/fuse.conf`.

## Install Mirage with the FUSE Extra

```bash
pip install "mirage-ai[fuse]"
```

Or with uv:

```bash
uv add "mirage-ai[fuse]"
```

## Verify

```python
from mirage import Mount, MountMode, Workspace
from mirage.resource.ram import RAMResource

with Workspace(
    {"/data": Mount(RAMResource(), mode=MountMode.WRITE, fuse=True)}) as ws:
    print("mountpoints:", ws.fuse_mountpoints)
```

If the printed path exists under `/tmp/mirage-*`, FUSE is wired up correctly.

The `Workspace` constructor blocks until every `fuse` mount is live, so the
mountpoint is ready to read as soon as the `with` block is entered, no sleep
needed. (In TypeScript, where mounts are async, you `await ws.fuseReady()`
instead.)

## Per-mount FUSE

FUSE is configured **per mount**. Each mount whose `fuse` is set is exposed at
its own mountpoint, showing only that mount's subtree. The value is either
`true` (mount at a fresh temp directory) or a path string (mount there,
creating the directory if missing):

```yaml
mode: WRITE
mounts:
  /data:
    resource: ram
    fuse: /tmp/data-repo   # explicit path
  /s3:
    resource: s3
    fuse: true             # temp directory
  /logs:
    resource: disk
    # no fuse key, not FUSE-exposed
```

`ws.fuse_mountpoints` returns a `{prefix: path}` map of the live mountpoints.

## Size semantics for API-backed files

Some resources (Linear, Trello, Slack, ...) cannot report a file's size
without fetching its content, so `stat` returns an unknown size. Over the
FUSE mount these files behave like Linux `/proc` files: they stat as **0
bytes until first open**, and become fully readable the moment anything opens
them. Mirage mounts with `direct_io` (the kernel reads to EOF regardless of
the reported size) and `attr_timeout=0` (post-open `fstat` returns the real
size of the now-fetched content, kept warm in a 30-second cache).

What that means per tool:

| Tools | Behavior |
| --- | --- |
| `cat`, `grep`, `head`, `cp`, `md5sum`, `sed`, `sort` | correct content, always |
| `wc -c`, `tail -c` | correct (they fstat after open, which serves the real size) |
| `ls -l`, `du`, `find -size`, `test -s` | report 0 until the file has been opened recently |
| `tar`, `rsync`, `scp` | see 0 at stat time and copy empty content, exactly like `tar` over `/proc`; read the file first or use `cat`/`cp` based flows |

Mirage never reports a fake size and never fetches content during `stat`:
returning real sizes eagerly would fire one API call per file on every
`ls -l`.

<Warning>
  **macOS allows only one in-process FUSE mount.** macFUSE registers a
  process-global signal source, so a second simultaneous mount in the same
  process fails with `fuse: cannot register signal source`. Multiple per-mount
  FUSE mounts work on Linux; on macOS, enable `fuse` on a single mount per
  workspace (or run additional mounts in separate processes).
</Warning>
