Not implemented. The project is Linux-only. macOS (FSKit) was investigated as a backend and is not pursued. The platform-specific code was prototyped and then removed after an Apple 26.x FSKit OS bug blocked enabling a third-party file-system extension. There is no
fs-fskit/platformcrate, noFskitOps/MacBackend/Capabilitytype, andgit lazy-mountships for Linux only (see the README). This note records what a macOS port would need and the design choices that were considered, so the work can be picked up later. Nothing described here is present-tense built.
The intended backend was an FSKit file-system extension. On macOS versions without usable FSKit, an isolated macFUSE backend could be offered as a separate, explicitly chosen backend — never substituted silently by changing semantics.
The shipped filesystem surface is impl Filesystem for TransparentFs in
crates/fuse/src/mount.rs. It is FUSE-specific,
not a cross-platform engine abstraction. A macOS port would need to bridge FSKit
(or macFUSE) callbacks onto the same projection/overlay/journal that
TransparentFs drives — the projection, overlay, and durable change journal all
live in crates/worktree and are backend-agnostic.
What a macOS port would need
The notes below are speculative design — "would need to" / "was considered",
not status. For the concrete build/sign/install/registration runbook and the
exact OS-level blocker that stopped the prototype, see
macos-fskit-ondevice.md.
FSKit extension (or isolated macFUSE)
A Swift FSUnaryFileSystem/FSVolume adapter would translate FSKit callbacks
(lookup, getattr, enumerate, read, readlink, plus the write callbacks
create, write, truncate, remove, rename, symlink) onto the projection.
Writes would route through the same overlay + durable ChangeJournal
(crates/worktree) that the FUSE path uses; there should be no macOS-only write
semantics. The FSKit-vs-macFUSE choice would be an explicit backend selection —
macFUSE only ever chosen deliberately, never by silently changing behavior.
Runtime capability detection + diagnostics
A host probe would check the macOS version (third-party FSKit needs ≥ 15.4),
whether the system extension is installed and approved, and whether macFUSE is
present, then select a backend or none. When no backend is available, mount and
git lazy-mount doctor should emit concrete, ordered install/approval steps. (The
shipped doctor reports only mountpoint / mounted / show_toplevel —
crates/cli/src/main.rs cmd_doctor — and has no
FSKit fields.)
APFS case-sensitivity and Unicode normalization
This is the most macOS-specific concern. Both APFS variants are normalization-insensitive, so NFC/NFD-equivalent names fold together on either, and a case-insensitive volume also folds case. A collision-aware resolver would need to:
- return every directory entry's exact recorded bytes on
enumerate, and report sets of names that fold together rather than silently merging a directory; - on
lookup, fuzzy-resolve (NFC↔NFD, case-insensitive) to the single matching entry's exact bytes, and surface a path-collision error when two distinct Git entries fold together rather than picking one; - on
create/symlink, reject a new name that would collide with a sibling.
A case-only rename (a.txt → A.txt) on a case-insensitive volume targets a
name that "already exists" by the volume's comparison; the bridge would need to
recognize the folding-only rename and perform it, preserving identity via the
inode table. The shipped projection already keeps raw byte-exact paths
(crates/core RepoPath), which is the foundation this would build on.
Resource forks, Finder metadata, xattrs, file flags
A macOS port needs an explicit, documented policy table:
.DS_Store/._*→ ignored (never reach a staged tree or commit);- xattrs (resource forks, Finder info, quarantine) and BSD file flags → overlay-only.
xattrs / resource forks / file flags have no Git commit channel at all, so they
are structurally never committed regardless. The shipped overlay does not
implement this classification today — note that the FUSE build does not implement
any xattr ops (they fall through to ENOSYS), so this is greenfield work.
File coordination (NSFileCoordinator)
To cooperate with coordinated readers/writers (Finder, document-based apps), the
adapter would wrap each callback in an NSFileCoordinator intent so coordinated
writes to a path are mutually exclusive and no coordinated read overlaps an
in-flight write. This was considered as a per-path coordinator model; it was not
built.
System-extension lifecycle, signing, recovery
The Swift FSKit module would need to build, sign (com.apple.developer.fskit.fsmodule
entitlement), install, and register with FSKit, then survive the
activation/approval flow on Apple hardware with a Developer identity. After an
extension restart, mounts would recover by replaying the durable journal; the
kernel re-issues lookup, so inode identity is rebuilt on demand and numbers are
never reused. The shipped mount lifecycle is a detached hidden __serve child
plus a monotonic MountGeneration counter
(crates/core/src/ids.rs) — there is no daemon and
no daemon state machine.
This is where the prototype died. Both Apple's official Passthrough FSKit
sample and the prototype extension built, signed, installed, and registered, but
neither could be enabled in System Settings on macOS 26.4.1 — an Apple OS-level
bug. The full runbook and findings are in
macos-fskit-ondevice.md. Because the OS itself
blocked enabling the extension, the macOS backend code was retired and the project
is Linux-only.
Data root
If revived, macOS state would live under the same XDG-style data directory the CLI
already computes: $XDG_DATA_HOME/git-lazy-mount, else
$HOME/.local/share/git-lazy-mount (data_dir in
crates/cli/src/main.rs). The earlier prototype's
~/Library/Application Support placement is not what the shipped CLI uses.