Retired — historical record. The macOS FSKit backend and its crates (
fs-fskit,glm-fskit-ffi,glm-daemon, theFskitOpsengine) were removed; git-lazy-mount is Linux-only. This page is kept as a runbook for a possible future revisit — the signing runbook and the Apple-bug blocker analysis below are the hard-won, still-valuable parts.
How to build, sign, install, and register the macOS FSKit backend on real Apple hardware, plus the OS-level blocker that stopped it. Validated on macOS 26.4.1 (25E253), Xcode 26.5 SDK, Apple silicon.
Result summary
The de-risk approach was to validate Apple's official Passthrough FSKit sample
on-device before finishing our own. Both Apple's sample and our GitLazyMount
extension hit the same wall:
| Step | Apple sample | Our extension |
|---|---|---|
| Build (Release, automatic signing) | ✅ | ✅ |
| Sign (team + FSKit entitlement) | ✅ | ✅ |
| Install to /Applications + launch | ✅ | ✅ |
pluginkit registration (com.apple.fskit.fsmodule) |
✅ | ✅ |
| Enable (System Settings → File System Extensions) | ❌ | ❌ |
mount -t … |
❌ | ❌ |
Blocker: a confirmed upstream Apple bug, not our code and not SIP. The
System Settings enablement toggle for File System Extensions is inert for
every third-party FSKit module on macOS 26. Clicking it produces zero system
response, so /Library/Filesystems/<name>.fs is never created and mount
fails with Module … is disabled!. This reproduces on Apple's own
FSKitSample. The root cause is documented in
andrewgazelka/loaf#1:
fskitd rejects the client connection (Hello FSClient! entitlement no →
Failed to start instance … extensionKit Code=2 … RBSRequestErrorDomain Code=5)
on 26.1 / 26.2 (and our 26.4.1). Others tried the same things we did, and more:
Developer ID signing, notarization, hardened runtime, FSKit entitlements,
library-validation disabled, embedded dylib, manual plist enablement, even an
fskitd restart. None of it worked. Apple DTS confirmed (Jul 2025) it awaits
"more fixes."
- SIP is not the issue. FSKit is a user-space app-extension model and is
designed to run with SIP enabled; no SIP change is ever required. The
Operation not permitted while System Integrity Protection is engagedwe saw was only fromlaunchctl kickstartof a system daemon (always SIP-blocked). The SIP-compatible restart issudo killall fskit, which still does not fix this bug. - A reboot will not fix it either. An
fskitdrestart was already tried and reproduces on Apple's sample. Re-validate when Apple ships an FSKit fix.
Signing runbook (the hard-won part)
No SIP changes, no notarization, no Developer ID, no OSSystemExtension. Just a
plain app extension provisioned by Xcode automatic signing. The blockers, in the
order they bite:
- Program License Agreement. Automatic signing fails with "Unable to process request – PLA Update available" until the account holder accepts the updated agreement at https://developer.apple.com/account.
- The team ID is the cert's
OU, not the name's parenthetical. A keychain identityApple Development: … (698SEVL7YQ)hasOU=HESNS6JK33, andHESNS6JK33is the team (confirmed by the Xcode accountteamIDand the embedded.provisionprofile). Passing the wrong team gives you "No Account for Team". - CLI
xcodebuildworks with the correct team (-allowProvisioningUpdates). No GUI build required once the team/PLA/cert are right. A wrong team is what produced the misleading "No Account" errors. - Duplicate "Apple Development" certs make Xcode sign the app and the appex
with different identities, which gives you "Embedded binary is not signed with the same
certificate as the parent app." Delete the orphaned one
(
security delete-identity -Z <sha1>). - Apple's sample ships an ad-hoc override on the app target only
(
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"); remove it so app + appex both real-sign. - Build Release, not Debug. The Debug "debug dylib" indirection
(
*.debug.dylib,__preview.dylib) is a poor fit for a system-validated extension.
Working invocation
xcodebuild -project GitLazyMount.xcodeproj -scheme GitLazyMount -configuration Release \
-derivedDataPath ./DerivedData DEVELOPMENT_TEAM=HESNS6JK33 -allowProvisioningUpdates build
cp -R DerivedData/Build/Products/Release/GitLazyMount.app /Applications/
open /Applications/GitLazyMount.app
pluginkit -mAv -p com.apple.fskit.fsmodule | grep gitlazymount # → "+ …GitLazyMountFS"
(In the retired prototype, crates/fs-fskit/extension/build.sh automated the
Rust staticlib + xcodegen + this build. That crate and script no longer exist.)
Architecture note: sandbox vs. git
FSKit extensions are sandboxed (com.apple.security.app-sandbox is mandatory).
In the retired prototype, glm-fskit-ffi opened the workspace and ran git
in-process, which the sandbox restricts. A production design would have proxied
FSKit callbacks to a glm-daemon over XPC/IPC so git ran outside the sandbox;
the prototype's engine split (FskitOps vs. daemon) was meant to support this.
The in-process FFI was the validation shim that proved build/sign/registration
end-to-end before the work was retired.