# Add localhost NFS mounts for macOS
## Current Baseline
- `crates/quarry-fuse` owns the current mount implementation.
- `FuseProjection` is the reusable store-backed projection layer:
- path normalization
- directory inference and persisted empty directories
- stable inode lookup through `QuarryStore`
- open handle buffers
- read-only enforcement
- Markdown whole-file reconciliation through `write_block_markdown`
- raw byte writes through `put_document`
- storage event invalidation generation
- The Linux-only FUSE adapter lives inside `crates/quarry-fuse/src/lib.rs` under `linux_mount` and implements `fuse3::raw::Filesystem`.
- `quarry mount` currently opens the store directly and runs `mount_library_with_shutdown`; with `--serve-addr`, it runs REST and FUSE in the same process.
- The older server-managed mounts plan exists in `docs/superpowers/plans/2026-05-28-cli-api-daemon-and-server-mounts.md`, but the implementation is not present. Do not require server-managed mounts for this issue.
## Requirements
- R1. Support a macOS-friendly localhost mount path using NFSv3.
- R2. Preserve the existing Linux FUSE behavior and CLI compatibility.
- R3. Use loopback-only serving by default. The NFS listener must not expose Quarry Libraries on non-loopback interfaces unless explicitly requested in a future issue.
- R4. Support read-only NFS mounts before writable NFS mounts.
- R5. Reuse Quarry's existing stable inode/path identity model rather than inventing a second identity system.
- R6. Keep NFS as a projection over committed Quarry state, not a separate source of truth.
- R7. Preserve Markdown semantics: Markdown writes must reconcile through the existing whole-file writer and must not silently degrade block documents to raw bytes.
- R8. Preserve raw document semantics: non-Markdown bytes round-trip exactly.
- R9. Document macOS prerequisites, mount command behavior, unmount command, and known limitations.
- R10. Defer SMB support. Record it as a future option, not part of this implementation.
## Proposed User-Facing Shape
Add an explicit protocol selector:
quarry mount <library> <mountpoint> --protocol fuse
quarry mount <library> <mountpoint> --protocol nfs
Recommended defaults:
- Linux: default to `fuse` to preserve current behavior.
- macOS: default to `nfs` once the NFS adapter is stable.
- Other platforms: return a clear unsupported error unless NFS support is verified there.
Read-only mode should work consistently:
quarry mount notes /Volumes/quarry-notes --protocol nfs --read-only
Manual NFS mount command shape for macOS spike:
mount_nfs -o nolocks,vers=3,tcp,rsize=131072,actimeo=0,port=<port>,mountport=<port> 127.0.0.1:/notes /Volumes/quarry-notes
Exact options should be validated during the spike. `actimeo=0` or similarly short caching may be needed for Quarry's committed-state visibility model. `nolocks` is likely acceptable for phase one because Quarry does not expose POSIX-perfect locking today.
## Technical Approach
1. Spike `nfsserve` against a minimal in-memory filesystem.
Goals:
- Build on current Rust stable.
- Serve on localhost with a non-privileged port.
- Mount successfully on macOS with `mount_nfs`.
- Exercise `ls`, `cat`, `find`, `rg`, `cp`, editor write, rename, and unmount.
- Confirm the crate can represent stable file handles using Quarry's inode table.
- Confirm read/write/commit call ordering from macOS for common workflows.
Exit criteria:
- If `nfsserve` cannot support macOS mounting or needed write semantics, stop and document the blocker before building Quarry integration.
- If it works for read-only but write semantics are ambiguous, ship/readiness-plan read-only NFS first.
2. Introduce a protocol-neutral projection boundary if needed.
Preferred low-risk path:
- Keep `FuseProjection` behavior intact.
- Extract or rename shared logic only when the NFS adapter needs it.
- Avoid large refactors before the NFS spike proves viability.
Possible target shape:
crates/quarry-mount/
shared projection model and tests
crates/quarry-fuse/
Linux FUSE adapter
crates/quarry-nfs/
localhost NFSv3 adapter
Do not force this crate split if a smaller first PR can reuse code safely.
3. Implement read-only NFS projection.
Cover:
- root export
- file handle to path resolution
- getattr/stat
- lookup
- readdir/readdirplus equivalent
- read
- stable file and directory identities
- directory inference from document paths
- persisted empty directories if exposed in read-only listings
Tests:
- Unit tests for handle/path mapping without kernel mount.
- Existing projection tests should continue passing.
- Add a manual macOS smoke script or documented checklist.
4. Add CLI integration for NFS read-only.
Behavior:
- `quarry mount notes /Volumes/quarry-notes --protocol nfs --read-only` starts a local NFS server.
- The CLI runs the macOS `mount_nfs` command when on macOS.
- Foreground command waits for Ctrl-C and unmounts where possible.
- If mounting fails, print the exact manual mount command and server port for debugging.
Keep existing FUSE command behavior unchanged on Linux.
5. Add writable NFS only after read-only is stable.
Key design problem:
- FUSE currently publishes on `flush`/`release` from a per-open handle buffer.
- NFSv3 write flows do not provide the same close event.
Candidate strategies to evaluate during the spike:
- Buffer writes by NFS file handle and publish on `COMMIT`.
- Publish after a short debounce window following the last write.
- For temp-file editor save patterns, rely on create/write/commit/rename and route rename-over-target through the existing Markdown reconciler.
- Keep writable NFS behind an explicit opt-in until editor behavior is verified.
Required tests before considering writable complete:
- Create/write/read raw file.
- Create/write/read Markdown file.
- Truncate existing Markdown file and write new content.
- Atomic-save rename over existing Markdown preserves target document id, block ids, and review anchors.
- Concurrent canonical edit plus NFS Markdown write converges with conflict review items, matching FUSE expectations.
- Invalid UTF-8 into Markdown returns an error and does not replace the canonical document.
- Raw binary bytes round-trip exactly.
6. Wire storage invalidation/caching carefully.
- Keep store event subscription behavior.
- Choose conservative NFS attribute caching for phase one.
- Document any visibility delay if caching cannot be fully disabled.
- Ensure REST/Git/FUSE/NFS writes are visible across surfaces under the same process model.
7. Update docs.
- `README.md`: add NFS/macOS mount summary and verification command.
- `docs/operations/fuse.md`: clarify FUSE remains Linux-only.
- Add `docs/operations/nfs.md` or rename to a broader `docs/operations/mounts.md`.
- Document macOS mount command, unmount command, troubleshooting, and limitations.
8. Verification matrix.
Automated where possible:
- `cargo test -p quarry-fuse`
- new NFS adapter unit tests
- projection/shared tests
- `cargo test --workspace`
- `cargo check --workspace --all-targets`
Manual macOS smoke:
- initialize a Quarry root
- put Markdown and raw files
- mount read-only over NFS
- `ls`, `cat`, `find`, `rg`
- unmount cleanly
- remount and confirm stable identities/contents
Manual writable smoke before writable release:
- `cp` a Markdown file into the mount
- edit with `vim` or another temp-rename editor
- truncate and rewrite a Markdown file
- copy a binary file
- verify REST reads see NFS writes
- verify NFS reads see REST writes after invalidation/caching window
## Non-Goals
- SMB support.
- Windows mount support.
- NFSv4 support.
- Network exposure beyond localhost.
- POSIX-perfect filesystem semantics.
- Hard links, symlinks, device files, or executable build-artifact workflows.
- Server-managed mount lifecycle unless that separate plan is revived.
## Research Notes
- `nfsserve` is the best current Rust lead for an embedded NFSv3 server:
- https://github.com/huggingface/nfsserve
- It was built as a FUSE alternative for user-mode mounts.
- It has macOS mount examples.
- It is described by its maintainers as incomplete but functional, so start with a spike.
- macOS `mount_nfs` supports explicit NFS version, TCP, port/mountport, lock, and cache controls:
- https://leopard-adc.pepas.com/documentation/Darwin/Reference/ManPages/man8/mount_nfs.8.html
- SMB should be deferred because a robust embedded SMB server is a much larger project. Samba VFS integration is possible but introduces a system dependency and a broad C module surface:
- https://wiki.samba.org/index.php/Writing_a_Samba_VFS_Module
- Samba's macOS compatibility guidance uses `fruit` and `streams_xattr`, which is useful later if SMB becomes a product priority:
- https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/8/html/deploying_different_types_of_servers/assembly_using-samba-as-a-server
Motivation
Quarry currently exposes Library contents as ordinary files through Linux FUSE only. That works for Linux users, but FUSE is a poor default on macOS because it requires extra kernel/filesystem software and has a higher support burden. The user-facing goal is to let macOS users mount a Quarry Library with built-in OS tooling while preserving Quarry's existing guarantees: committed-state projection, stable identities, read-only mode, auto-commit writable mode, and Markdown reconciliation.
Desired UX:
or, if we decide to make protocol selection automatic:
Recommended direction from research:
Why NFS first:
inodestable, which maps well to NFS file handles.FuseProjectionis already close to a protocol-neutral store-backed filesystem projection.nfsserveis a Rust NFSv3 server built for this exact class of problem: a localhost user-mode filesystem mount that avoids FUSE driver friction. Its README includes macOS mount examples and calls out FUSE pain on Mac/Windows as motivation: https://github.com/huggingface/nfsserveWhy not SMB first:
fruit/streams_xattr, reinforcing that SMB polish pulls toward Samba configuration rather than a self-contained Quarry binary: https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/8/html/deploying_different_types_of_servers/assembly_using-samba-as-a-server_deploying-different-types-of-servers#configuring-samba-for-macos-clients_assembly_using-samba-as-a-serverImportant semantic risk:
The current FUSE writable path stages writes per open file handle and publishes on
flush/release. NFSv3 is more stateless: writes arrive as RPCs, withCOMMITbut no true close. The first implementation should be read-only, then add writable behavior only after the NFS write/commit semantics are proven against Quarry's Markdown reconciliation model.Implementation Plan