mountx
Write a filesystem in JavaScript, mount it as a real folder.
Write an object with the same methods as `node:fs/promises` and mountx turns it into a real folder on the machine — so `ls`, `cat`, your editor and every other program can use it. There is no special API to learn: `node:fs/promises` already is the interface.
import { mount } from "mountx/auto";
import { createMemoryDriver } from "mountx/drivers/memory";
// A driver is any object with stat(), readdir() and open().
const driver = createMemoryDriver();
// FUSE on Linux (no root needed), NFSv3 on macOS.
await using mounted = await mount(driver, "/mnt/point");
mounted.transport; // "fuse" | "nfs"
// $ echo hi > /mnt/point/hello.txt
// $ cat /mnt/point/hello.txt
Features
No API to learn
A driver is a subset of
node:fs/promises—stat,readdir,open. Node's ownfs/promisesis a valid driver, unchanged.A real folder
Every program on the machine sees an ordinary path. Shell tools, editors, build systems and agents need to know nothing about it.
One API, two transports
FUSE where FUSE works, NFSv3 everywhere else.
mountx/autoprobes the host, picks one, and hands back that transport's own mount object.No root on Linux
Unprivileged mounting through
fusermount3, the setuid helper FUSE already ships. Serving never needs privileges on either transport.Pure JavaScript
Zero runtime dependencies, both protocols implemented from the specs. The one native piece is a ~7 KB helper that only unprivileged FUSE mounts load.
Develop without mounting
createLoopback(driver)runs your driver likefs/promisesin-process — no kernel, no privileges, every platform. Where drivers get written and tested.Errors pass through
Throw what
node:fsthrows.fsError("ENOENT")builds an error byte-identical to Node's, and it reaches the kernel untranslated.Capabilities, never faked
What your driver supports is declared or inferred from its shape. Anything unsupported answers
ENOSYS/ENOTSUPinstead of pretending.Kernel caching you control
Attribute and entry timeouts are worth 8–15× on a real workload, and
notifyInvalInode()drops a cache the moment your storage changes.