
# Auto

**One `mount()` that picks a transport, kept off the root export so importing `mountx` for the driver types does not drag two protocol stacks in with it.**

```ts
import { mount, probeTransports, liveMounts, unmountAll } from "mountx/auto";
```

## `mount(driver, mountpoint, options?)`

```ts
function mount(
  driver: FsDriver,
  mountpoint: string,
  options?: AutoMountOptions,
): Promise<AutoMount>;
```

Serves `driver` at `mountpoint` over whatever this host can mount, and resolves once the mountpoint is **usable**. The result is the transport's own mount object with a `transport` property defined on it — tagged, not wrapped — so `await using`, `unmount()` and every transport-specific member work exactly as they do when the transport is imported directly.

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

Throws when the probe finds nothing usable, with a message naming what _each_ transport is missing.

## `AutoMountOptions`

Deliberately **not** the union of both transports' option types: they have same-named options with genuinely different shapes (`onError` hands FUSE a request and NFS an RPC call), and a merged type would either lie about that or collapse to something unusable.

So the top level is the options that mean the same thing in both, and anything transport-specific goes in `fuse: {…}` or `nfs: {…}` — applied _after_ the shared ones, and therefore winning.

```ts
interface AutoMountOptions {
  /** "auto" (default), or a name — which skips the probe entirely. */
  transport?: "auto" | "fuse" | "nfs";
  /** Mount read-only. */
  readOnly?: boolean;
  /** Unmount on SIGINT/SIGTERM. Default true. */
  signals?: boolean;
  /** Milliseconds an unmount may spend before it is forced. Default 10_000. */
  unmountTimeout?: number;
  /** Report the driver's own ino values instead of synthesising them. */
  useDriverIno?: boolean;
  /** Called for errors raised while answering a request. */
  onError?: (error: unknown) => void;
  /** Called for transport-level failures, and for a forced teardown. */
  onTransportError?: (error: unknown) => void;
  /** FUSE-only options. Applied after the shared ones. */
  fuse?: MountOptions;
  /** NFS-only options. Applied after the shared ones. */
  nfs?: MountNfsOptions;
}
```

A block for the transport you did not get is simply ignored — which is what makes one call portable.

See [`MountOptions`](/transports/fuse#mountoptions) and [`MountNfsOptions`](/transports/nfs#mountnfsoptions) for the two nested blocks.

## `AutoMount`

```ts
type AutoMount =
  (Mount & { readonly transport: "fuse" }) | (NfsMount & { readonly transport: "nfs" });
```

A discriminated union. Narrowing on the tag reaches the full transport type, with no cast:

```ts
if (mounted.transport === "fuse") {
  mounted.session; // FuseSession
  mounted.fd; // the /dev/fuse descriptor
  mounted.closed; // Promise<void>, never rejects
  mounted.notifyInvalInode(2n);
  mounted.notifyInvalEntry(1n, "hello.txt");
} else {
  mounted.server; // NfsServer
  mounted.port; // number
}
```

Both share `mountpoint`, `source`, `active`, `unmount()` and `[Symbol.asyncDispose]`.

## `probeTransports(platform?)`

```ts
function probeTransports(platform?: NodeJS.Platform): Promise<AutoProbe>;
```

```ts
interface AutoProbe {
  platform: NodeJS.Platform;
  chosen: Transport | undefined; // what mount() would use
  preference: readonly Transport[]; // the list `chosen` was picked from
  fuse: TransportProbe; // { usable: boolean; reason: string | undefined }
  nfs: TransportProbe;
  reason: string | undefined; // when nothing can mount, naming both
}
```

Cheap enough to call before deciding whether to offer a mount at all, and specific enough to print. It reads facts in order of cheapness — the platform, then `/dev/fuse`, then (only when unprivileged) the `fusermount3` helper and the native addon — and it loads no protocol codec on either branch.

The preference order is `["fuse", "nfs"]` on Linux and `["nfs", "fuse"]` elsewhere. Linux is the only host where both can work, so it is the only host whose order decides anything.

::note
`platform` exists to be overridden in tests — it is how the darwin and win32 answers are checked from any host. Leave it alone otherwise.
::

## `liveMounts()` / `unmountAll()`

```ts
function liveMounts(): Promise<AutoMount[]>;
function unmountAll(): Promise<unknown[]>;
```

Every live mount in this process, on both transports, tagged; and unmount them all — never rejects, returning whatever went wrong. Only transports that were actually used are asked, so neither loads anything that was not already loaded.

## Re-exported types

`Transport`, `TransportProbe`, `AutoProbe`, `AutoMountOptions`, `AutoMount`, plus `Mount`/`MountOptions` from the FUSE transport and `NfsMount`/`MountNfsOptions` from the NFS one — so a caller needs one import either way.

## Three things `auto` deliberately does not do

- **No fallback after a failure.** The probe decides once, from host facts. If the chosen transport then fails to mount, that error is what you get — silently mounting the _other_ one would hand back a filesystem with different semantics than the error you never saw.
- **No probing when you name a transport.** `transport: "fuse"` calls the FUSE transport directly, whose own errors are more specific than anything the chooser could say.
- **No wrapping.** The result is the transport's own mount object with a `transport` property defined on it. Narrowing on that tag reaches `session`, `fd` and the `notifyInval*` pair for [FUSE](/transports/fuse), or `server` and `port` for [NFS](/transports/nfs) — and `await using` works either way.

And one it does do quietly: **it loads nothing it does not use.** Both transports arrive through `await import()`, so choosing one never pulls in the other's codec.

## Next

- [FUSE](/transports/fuse) — what a Linux host gets, in detail.
- [NFSv3](/transports/nfs) — what macOS gets, and serving without mounting at all.
- [Mounting](/guide/mounting) — the lifecycle and the unmount rules, in context.
