
# The `mountx` CLI

**One command that mounts something real, and prints every request the kernel makes.**

```sh
npx mountx                       # an in-memory tree at ~/mountx
npx mountx /tmp/scratch -t nfs   # somewhere else, over a named transport
npx mountx --help
```

No root on either platform (unprivileged FUSE wants `fusermount3`; macOS NFS wants a mountpoint you own), and nothing to clean up but the mountpoint.

::note
It is a **demonstration and a test bench, not a mount tool.** What it serves is always the bundled [memory driver](/guide/drivers/built-in#memory)'s tree, which exists for exactly as long as the process does. The library is what mounts a real driver.
::

## What it does

Seeds an in-memory filesystem with a file or two, wraps it in a request logger, and mounts it through [`mountx/auto`](/transports/auto) — so it picks FUSE on Linux and NFSv3 on macOS. Then it narrates: every `lookup`, `open`, `read` and `write` the kernel sends, as it happens.

That log is the point. It is the fastest way to see how much traffic a `ls -l` really is, what the [attribute cache](/guide/tuning#caching-this-is-where-the-speed-is) absorbs, and how many FUSE requests one syscall becomes.

The metadata polls — `lstat`, `stat`, `statfs` — are the exception, and `--verbose` puts them back. There is one `lstat` per listed entry and a desktop session asks `statfs` on a timer, so by default they are logged only when they _fail_, where an `ENOENT` is usually the whole story of the command that caused it. Turn them on when the question is how much traffic there really is.

## Options

| flag                                        |                                                          | default                             |
| ------------------------------------------- | -------------------------------------------------------- | ----------------------------------- |
| `[mountpoint]` or `-m, --mountpoint <path>` | where to mount                                           | `~/mountx`, or `$MOUNTX_MOUNTPOINT` |
| `-t, --transport <name>`                    | `auto`, `fuse` or `nfs`                                  | `auto`                              |
| `-q, --quiet`                               | do not log filesystem requests                           | off                                 |
| `-v, --verbose`                             | log the metadata polls too (`lstat`, `stat`, `statfs`)   | off                                 |
| `-r, --read-only`                           | mount read-only                                          | off                                 |
| `--empty`                                   | start with an empty tree instead of the seeded one       | off                                 |
| `--allow-other`                             | let other users see the mount (FUSE; implied under root) | off                                 |
| `-h, --help`                                | usage                                                    |                                     |

A leading `~` in the mountpoint is expanded the way a shell would. Ctrl-C unmounts and exits.

## Using it

**From another terminal** — never from the one serving it, and never from the serving process, for [the reason in Troubleshooting](/guide/troubleshooting#dont-use-your-own-mount-from-the-serving-process):

```sh
ls -l ~/mountx
head ~/mountx/README.md
echo 'written from outside' > ~/mountx/note.txt
cat ~/mountx/note.txt
```

Every one of those shows up in the first terminal as the requests it actually became.

## If it dies without unmounting

A run that was killed rather than Ctrl-C'd leaves the mountpoint in the mount table, answering `ENOTCONN` — and that is enough for a later `mount()` to refuse to [stack](/guide/mounting#no-mount-stacking) on it. The CLI clears a stale entry at that exact path on the next run, and prints the command to do it by hand if the route needs privileges it does not have:

```sh
fusermount3 -u ~/mountx      # FUSE, unprivileged
sudo umount -l ~/mountx      # FUSE, mounted as root
umount -f ~/mountx           # NFS on macOS
sudo umount -l ~/mountx      # NFS on Linux
```

There is one case it hands back rather than handling: a stale NFS mount **on Linux**, where `umount(8)` is neither setuid nor a BSD, so clearing it needs a `sudo` the CLI will not run for you. The two it does handle are unprivileged for unrelated reasons — `fusermount3 -u` is setuid, and macOS lets the user who mounted a filesystem unmount it, the same rule that lets [NFS mount there without root](/transports/nfs#where-it-mounts).

## From source

Working in the repository rather than on the published package:

```sh
pnpm mountx          # node src/cli/index.ts
pnpm mountx --help
```

Source: [`src/cli/index.ts`](https://github.com/pithings/mountx/blob/main/src/cli/index.ts).
