
# Quick Start

From install to a real folder in about a minute.

## Install

:pm-install{name="mountx"}

::warning
**Pre-1.0 API.** Expect breaking changes, and note that a mounted driver is reachable by every program on the machine.
::

## Step 0 — see one without writing one

The package ships a CLI: a seeded in-memory filesystem, mounted for real, with every request the kernel makes printed as it happens. No root on either platform.

```sh
npx mountx                       # mounts ~/mountx
npx mountx /tmp/scratch -t nfs   # somewhere else, over a named transport
npx mountx --help
```

Then poke at it **from another terminal** — `ls -l ~/mountx`, `cat ~/mountx/README.md` — and watch the requests scroll past. Full flags in [The `mountx` CLI](/guide/cli).

## Step 1 — no mounting at all

The fastest way to see mountx work is without touching the operating system. Wrap a driver in `createLoopback()` and use it like `fs/promises`, right inside your program:

```ts
import { createLoopback } from "mountx";
import { createMemoryDriver } from "mountx/drivers/memory";

const fs = createLoopback(createMemoryDriver());

await fs.mkdir("/notes");
await fs.writeFile("/notes/hello.txt", "hi");

const entries = await fs.readdir("/notes", { withFileTypes: true });
console.log(entries.map((entry) => entry.name)); // [ "hello.txt" ]

const bytes = await fs.readFile("/notes/hello.txt"); // Uint8Array
console.log(new TextDecoder().decode(bytes)); // "hi"
```

`createLoopback` does the same tidying-up a real mount does: it normalizes paths, answers `ENOSYS` for methods your driver does not have, and resolves what your driver supports. Nothing is mounted, no root is needed, and it works on every platform — including Windows. This is where drivers get developed and tested.

## Step 2 — mount it for real

Same driver, now as a folder anyone on the machine can open:

```ts [serve.ts]
import { mount } from "mountx/auto";
import { createMemoryDriver } from "mountx/drivers/memory";

await using mounted = await mount(createMemoryDriver(), "/mnt/point");

mounted.transport; // "fuse" | "nfs" — which one this host got

// /mnt/point is a real folder now. Keep the process alive.
await new Promise(() => {});
```

Run it:

```sh
node serve.ts
```

Then, **from another terminal** (this matters — see [below](#dont-use-it-from-the-same-process)):

```sh
echo hi > /mnt/point/hello.txt
cat /mnt/point/hello.txt
ls -l /mnt/point
```

Press Ctrl-C in the first terminal and the folder disappears. `await using` unmounts when the block ends; you can also call `await mounted.unmount()` yourself.

`mount()` from [`mountx/auto`](/transports) picks the transport this host can use — FUSE on Linux, NFSv3 on macOS — so that is the same code everywhere.

## Step 3 — serve a real folder

The second bundled driver hands requests to a real directory, resolving every path component itself so nothing outside the root can be reached:

```ts
import { mount } from "mountx/auto";
import { createNodeFsDriver } from "mountx/drivers/node-fs";

await using mounted = await mount(createNodeFsDriver("/home/me/data"), "/mnt/point");
```

Useful on its own, and the easiest thing to build on: wrap it and you can add caching, logging, filtering or transformation to an ordinary directory.

## Do I need root?

Serving never needs privileges, on either transport. Only _attaching_ the mount to a directory does, and how much depends on the host:

| host                          | transport | root?                                   |
| ----------------------------- | --------- | --------------------------------------- |
| Linux with `fusermount3`      | FUSE      | **no**                                  |
| Linux without it              | FUSE      | yes                                     |
| macOS                         | NFSv3     | **no**, but you must own the mountpoint |
| Linux (NFS pinned explicitly) | NFSv3     | yes                                     |

On Linux, mounting is a syscall Node cannot make, so mountx asks `fusermount3` — the setuid helper that ships with FUSE — to make it and hand the connection back. It is already installed on most desktop Linux; otherwise `apt install fuse3` / `dnf install fuse3`. As root, mountx skips the helper and uses `mount(8)` directly.

On macOS you get NFSv3, and `/sbin/mount_nfs` is not setuid — macOS is a BSD, and a BSD lets an ordinary user mount onto a directory that user owns. So no `sudo`, as long as the mountpoint is yours.

Two things an unprivileged FUSE mount cannot do: `allowOther` additionally needs `user_allow_other` in `/etc/fuse.conf`, and forcing down a mount whose driver has stopped answering is weaker, because there is no `umount -f` without root.

## Don't use it from the same process

A synchronous `fs` call against your own mountpoint, from the process serving it, deadlocks. Enough concurrent _async_ ones wedge it too. Put the client in another process — a shell, a test child process, anything. Full explanation in [Troubleshooting](/guide/troubleshooting#dont-use-your-own-mount-from-the-serving-process).

## Where to go next

- [Writing a driver](/guide/drivers/custom) — a complete filesystem in about 60 lines.
- [Mounting](/guide/mounting) — the mount object, lifecycle, and unmounting properly.
- [Tuning](/guide/tuning) — the cache settings that are worth 8–15×.
- [Troubleshooting](/guide/troubleshooting) — stale mounts, wedges, and how to recover.
