P4-4: Linux provider — FUSE (no OS standard exists) #47

Open
opened 2026-09-10 18:09:50 +00:00 by Cordy · 0 comments
Owner

Depends on P4-3.

The situation

Linux has no equivalent of Cloud Filter or File Provider. There is no desktop-wide
placeholder mechanism, no standard hydration callback, and no file-manager convention for
"cloud file". OpenCloud is writing openvfs from scratch in 2026 for exactly this reason.

So this is not "port the pattern" — it is "build the mechanism". Weigh that before committing.

Options

A. FUSE mount. Present the Cairn as a FUSE filesystem; content fetched on read().
Recommended. github.com/hanwen/go-fuse is mature and pure Go. Gives genuine files-on-demand
with no per-file-manager work.

Costs: the sync folder becomes a mount point, not a normal directory — different from the
Windows and macOS models. Requires the fuse kernel module (present on mainstream distros,
absent in some containers and hardened kernels). Unmounting cleanly on crash needs care, or the
user is left with a hung mount that ls blocks on forever.

B. Do not ship it on Linux. Keep full sync on Linux and document the difference honestly.

Costs: feature inconsistency across platforms.

C. Wait for something standard. There is no credible candidate. Not a plan.

Recommendation: A, with B as the fallback if FUSE proves fragile in the beta. Decide
before writing code, and record the decision here.

Note the honest asymmetry: Linux is where Cairn's self-hosting audience lives, but almost
certainly not where the 2 TB-server-256 GB-laptop problem is most acute — that is a corporate
Windows and macOS fleet problem. Shipping A on Windows and macOS and B on Linux is a defensible
outcome, not a failure.

If FUSE

  • Mount at the configured path; refuse to start if the path is non-empty and not our mount.
  • read() triggers a ranged GET; cache hydrated content on local disk so a second read is free.
  • Honour pin state: PinAlways files are kept in the cache and never evicted.
  • Unmount cleanly on exit, and detect a stale mount on start. A hung FUSE mount is worse
    than no feature — every ls in that directory blocks, and users blame the app for freezing
    their file manager.
  • Ship a systemd user unit so the mount survives correctly across sessions.

Steps

  • Decide A or B; record the decision and the reasoning here.
  • If A: implement PlaceholderFS over go-fuse.
  • Implement the local content cache with pin-aware eviction (shared with P4-5).
  • Handle stale-mount detection and clean unmount, including after a crash.
  • Test on the arm64 Pi and on an x86 desktop distro.
  • Test the failure path: kill the process mid-read, confirm the mount is recoverable
    without a reboot.
  • Commit: git commit -s -m "feat(vfs): Linux FUSE placeholder provider"

Acceptance criteria

  • Files appear with correct metadata and no local content until read.
  • Reading one file fetches exactly that file.
  • Killing the process leaves no hung mount.
  • A missing fuse module degrades to full sync with a clear message, rather than failing to
    start.
  • internal/sync is unchanged.
Depends on P4-3. ## The situation **Linux has no equivalent of Cloud Filter or File Provider.** There is no desktop-wide placeholder mechanism, no standard hydration callback, and no file-manager convention for "cloud file". OpenCloud is writing `openvfs` from scratch in 2026 for exactly this reason. So this is not "port the pattern" — it is "build the mechanism". Weigh that before committing. ## Options **A. FUSE mount.** Present the Cairn as a FUSE filesystem; content fetched on `read()`. Recommended. `github.com/hanwen/go-fuse` is mature and pure Go. Gives genuine files-on-demand with no per-file-manager work. *Costs:* the sync folder becomes a mount point, not a normal directory — different from the Windows and macOS models. Requires the `fuse` kernel module (present on mainstream distros, absent in some containers and hardened kernels). Unmounting cleanly on crash needs care, or the user is left with a hung mount that `ls` blocks on forever. **B. Do not ship it on Linux.** Keep full sync on Linux and document the difference honestly. *Costs:* feature inconsistency across platforms. **C. Wait for something standard.** There is no credible candidate. Not a plan. **Recommendation: A**, with **B as the fallback** if FUSE proves fragile in the beta. Decide before writing code, and record the decision here. Note the honest asymmetry: Linux is where Cairn's *self-hosting* audience lives, but almost certainly not where the 2 TB-server-256 GB-laptop problem is most acute — that is a corporate Windows and macOS fleet problem. Shipping A on Windows and macOS and B on Linux is a defensible outcome, not a failure. ## If FUSE - Mount at the configured path; refuse to start if the path is non-empty and not our mount. - `read()` triggers a ranged GET; cache hydrated content on local disk so a second read is free. - Honour pin state: `PinAlways` files are kept in the cache and never evicted. - **Unmount cleanly on exit, and detect a stale mount on start.** A hung FUSE mount is worse than no feature — every `ls` in that directory blocks, and users blame the app for freezing their file manager. - Ship a systemd user unit so the mount survives correctly across sessions. ## Steps - [ ] Decide A or B; record the decision and the reasoning here. - [ ] If A: implement `PlaceholderFS` over `go-fuse`. - [ ] Implement the local content cache with pin-aware eviction (shared with P4-5). - [ ] Handle stale-mount detection and clean unmount, including after a crash. - [ ] Test on the arm64 Pi and on an x86 desktop distro. - [ ] Test the failure path: kill the process mid-read, confirm the mount is recoverable without a reboot. - [ ] Commit: `git commit -s -m "feat(vfs): Linux FUSE placeholder provider"` ## Acceptance criteria - Files appear with correct metadata and no local content until read. - Reading one file fetches exactly that file. - Killing the process leaves no hung mount. - A missing `fuse` module degrades to full sync with a clear message, rather than failing to start. - `internal/sync` is unchanged.
Sign in to join this conversation.
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: Cordy/cairn-desktop#47
No description provided.