Blog / Why our Mac mount doesn't need a kernel extension

Why our Mac mount doesn't need a kernel extension

The first version of the Swarmfile mount on macOS worked the way most FUSE-based tools do: a kernel extension. It worked, and it was also the single biggest first-run risk on the platform. It's worth explaining both what that cost, and what replacing it actually involved.

What a kernel extension used to cost you#

Loading a third-party kernel extension on Apple Silicon isn't a permission dialog; it's a detour through Recovery Mode. Install the extension, then reboot into Recovery, lower the security setting from "Full Security" to "Reduced Security," reboot back, and only then does macOS let the extension load, with its own approval prompt along the way. That's a lot to ask of someone who just wants to mount a drive, and it was flagged internally as the highest-severity risk to a smooth first run on the platform, the same reason LucidLink, among others, moved off a kext.

What replaced it: FUSE-T, and what it actually is#

The mount now runs on FUSE-T, and it's worth being precise about what that is rather than waving at "some userspace FUSE thing." FUSE-T's helper (go-nfsv4) implements the FUSE wire protocol in userspace and, from there, negotiates an actual NFS mount with the kernel. macOS's own mount command reports it as a real NFS mount (fuse-t:/... on ... (nfs, nodev, nosuid, mounted by admin)), not a special case. No kernel extension is loaded at any point.

On the Rust side, we specifically did not use the fuser crate that most Rust FUSE projects reach for. Its macOS build script only knows how to find the old macFUSE/osxfuse libraries and fails outright without them, with no fuse3 fallback. Talking to FUSE-T instead meant writing a direct FFI binding to its C API by hand: a few hundred lines binding straight to fuse_session_new and fuse_reply_*, gated behind its own build feature so it only compiles in where it's needed.

The tradeoff: what you give up by not having a kext#

Losing the kernel extension isn't free. macOS's Finder relies on AppleDouble ._name sidecar files to carry resource forks and Finder metadata for anything that can't store them natively, and by default that's exactly what happens on an NFS mount too, which would have doubled the file count, the lock count, and the history of every single file on the drive.

Our answer is to keep those sidecars where they're made. Swarmfile treats every ._name file, and .DS_Store, as local to the Mac that wrote it: it's never uploaded, never versioned, and never counted against anyone's lock or history. Finder tags, comments and other extended attributes keep working on that Mac, and cp -p, ditto, rsync and the Finder all behave normally. The honest cost is that those attributes don't travel: a teammate, or the same Mac after a fresh install, won't see them. The filesystem compatibility reference lists exactly what is and isn't carried across machines.

Staying honest when the helper hangs, not just when it dies#

A kext-free mount still needs to survive its userspace helper misbehaving, and "misbehaving" turns out to have two different shapes that need two different detectors. If the go-nfsv4 helper process dies outright, that's the easy case: a kqueue watcher catches the process exit immediately and remounts in-process, backing off 1, 2, then 4 seconds between attempts.

The harder case is a helper that's still alive but has quietly stopped listening. Nothing crashed, so the process-exit watcher never fires, but because the mount is negotiated as hard,nointr (deliberately, so a transient blip doesn't turn into read errors), any command touching the drive just hangs, uninterruptibly, in a way not even kill -9 on the calling process can clear. That needed a second, independent watchdog that tracks how long it's actually been since the helper last responded to anything and, once that goes quiet for too long, kills the stuck helper itself, which then hands off cleanly to the same force-unmount-drain-remount sequence the death detector uses. We found this specific failure mode in the field, not in a design review, which is part of why it's a separate detector rather than an assumption baked into the first one.

A legacy macFUSE build still ships alongside FUSE-T, for machines that already have it installed via IT policy or for anyone who's measured a preference for it. It's a fallback, not a deprecated path we're trying to steer people off.