Skip to main content

Prerequisites

  • Node.js 20+
  • pnpm (recommended, npm and yarn also work, but the FUSE binding’s install script needs extra configuration with pnpm).

System FUSE

Install the OS-level FUSE kernel support first:

Install the Node Binding

The Node package (@struktoai/mirage-node) ships FUSE support via an optional peer dependency on @zkochan/fuse-native, the pnpm author’s actively-maintained fork that works on Node 20+ and supports both macOS and Linux (thanks to @zkochan).
pnpm add @struktoai/mirage-node @zkochan/fuse-native
Non-FUSE users don’t need @zkochan/fuse-native, the base @struktoai/mirage-node package works without it. Only install the binding when you want a real mountpoint.

Allow pnpm to run the install script

@zkochan/fuse-native compiles native code on install. pnpm blocks install scripts by default, allow this one explicitly in pnpm-workspace.yaml:
onlyBuiltDependencies:
  - '@zkochan/fuse-native'
Or, if you’re not using a pnpm workspace, in package.json:
{
  "pnpm": {
    "onlyBuiltDependencies": ["@zkochan/fuse-native"]
  }
}
macFUSE 4 ships libfuse.2.dylib instead of the legacy libosxfuse.2.dylib that @zkochan/fuse-native was built against. Create a one-time symlink:
sudo ln -sf /usr/local/lib/libfuse.2.dylib /usr/local/lib/libosxfuse.2.dylib
pnpm rebuild @zkochan/fuse-native

Verify

import { MountMode, RAMResource, Workspace } from '@struktoai/mirage-node'

const ws = new Workspace(
  { '/data': new RAMResource() },
  { mode: MountMode.WRITE, fuseMounts: { '/data': true } },
)

await ws.execute('echo hello | tee /data/x.txt')
console.log('mountpoints:', ws.fuseMountpoints)

// Any tool (ls, cat, external subprocess) can now see x.txt under the mountpoint.
await ws.close() // auto-unmounts
If ws.fuseMountpoints prints a /tmp/mirage-fuse-XXXXXX path and no error is thrown, the binding is wired up correctly.

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):
mode: WRITE
mounts:
  /data:
    resource: ram
    fuse: /tmp/data-repo   # explicit path
  /s3:
    resource: s3
    fuse: true             # temp directory
Programmatically, pass fuseMounts: { '/data': '/tmp/data-repo', '/s3': true }. ws.fuseMountpoints returns a { prefix: path } map of the live mountpoints.
FUSE on Node has a runtime constraint worth knowing, fs-monkey can’t patch ESM node:fs imports. See TypeScript Limitations for details and workarounds.
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. Multiple per-mount FUSE mounts work on Linux; on macOS, enable fuse on a single mount per workspace (or run extra mounts in separate processes).