P4-3: macOS provider — File Provider extension #46

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

Depends on P4-2. Requires the Mac runner (#19) and Apple Developer membership (#17).

Goal

Implement vfs.PlaceholderFS on macOS using a File Provider extension — the same mechanism
iCloud Drive and OneDrive use on the Mac.

The structural problem, and it is a big one

A File Provider extension is a separate process, written in Swift or Objective-C, that
macOS launches on demand — not a library you link into the Go application.

That means this task is not "call some APIs from Go". It is:

  1. A Swift/ObjC extension target bundled inside the .app.
  2. An IPC channel between the extension and the Go daemon (XPC, or a local socket), because
    the extension must ask the daemon to fetch content and the daemon must tell the extension
    what exists.
  3. Both sides signed, sandboxed, and entitled correctly.

This is the largest single task in the whole programme. Budget accordingly, and do not
discover it halfway through P4-2. If any part of phase 4 slips, expect it to be this one.

Constraints

  • Extensions have hard memory limits (tens of MB). Stream content; never buffer a whole
    file. A 4 GB file must hydrate without the extension being killed.
  • Entitlements: File Provider capability, App Groups for shared container access between
    app and extension. These must be present on the provisioning profile from the Apple Developer
    account — check this early, because provisioning problems surface late and confusingly.
  • The API has churned. NSFileProviderReplicatedExtension (macOS 11+) is the current
    model and differs substantially from the older one. Target the replicated extension; expect
    further change and budget a compatibility pass per macOS release.
  • The domain must be registered with NSFileProviderManager, and cleanly removed on
    uninstall or sign-out. An orphaned domain leaves a folder in Finder that does nothing.

Where the files live

A File Provider domain does not live at an arbitrary path the user picks. macOS places it
under the provider's own container. This differs from the Windows model, where the sync root is
a normal folder.

This affects the setup wizard (P3-4) — on macOS the "choose a folder" step becomes "your
Cairn appears in Finder's sidebar" when files-on-demand is enabled. Update the wizard copy and
flow rather than pretending the platforms behave alike.

Steps

  • Create the extension target; get an empty domain appearing in Finder before anything else.
  • Establish IPC between extension and Go daemon; define the message set.
  • Implement enumeration from the daemon's state DB.
  • Implement content fetch, streaming, with progress and cancellation.
  • Implement pinning via NSFileProviderManager materialisation APIs.
  • Sign both app and extension (inside-out, per P3-10); confirm notarisation still passes.
  • Handle domain removal on uninstall and sign-out.
  • Test on a real Mac: 1,000 placeholders visible in Finder, open one, exactly one hydrates.
  • Test a 4 GB hydration without the extension being killed.
  • Commit: git commit -s -m "feat(vfs): macOS File Provider placeholder provider"

Acceptance criteria

  • Cairn appears in the Finder sidebar with dataless files.
  • Opening one file hydrates exactly that file.
  • A multi-GB file hydrates without exceeding the extension's memory limit.
  • App and extension are signed and notarised together.
  • Removing the account removes the domain cleanly.
  • internal/sync is unchanged.
Depends on P4-2. Requires the Mac runner (#19) and Apple Developer membership (#17). ## Goal Implement `vfs.PlaceholderFS` on macOS using a **File Provider extension** — the same mechanism iCloud Drive and OneDrive use on the Mac. ## The structural problem, and it is a big one A File Provider extension is **a separate process**, written in Swift or Objective-C, that macOS launches on demand — not a library you link into the Go application. That means this task is not "call some APIs from Go". It is: 1. A Swift/ObjC extension target bundled inside the `.app`. 2. An **IPC channel** between the extension and the Go daemon (XPC, or a local socket), because the extension must ask the daemon to fetch content and the daemon must tell the extension what exists. 3. Both sides signed, sandboxed, and entitled correctly. **This is the largest single task in the whole programme.** Budget accordingly, and do not discover it halfway through P4-2. If any part of phase 4 slips, expect it to be this one. ## Constraints - **Extensions have hard memory limits** (tens of MB). Stream content; never buffer a whole file. A 4 GB file must hydrate without the extension being killed. - **Entitlements:** File Provider capability, App Groups for shared container access between app and extension. These must be present on the provisioning profile from the Apple Developer account — check this early, because provisioning problems surface late and confusingly. - **The API has churned.** `NSFileProviderReplicatedExtension` (macOS 11+) is the current model and differs substantially from the older one. Target the replicated extension; expect further change and budget a compatibility pass per macOS release. - **The domain must be registered** with `NSFileProviderManager`, and cleanly removed on uninstall or sign-out. An orphaned domain leaves a folder in Finder that does nothing. ## Where the files live A File Provider domain does **not** live at an arbitrary path the user picks. macOS places it under the provider's own container. This differs from the Windows model, where the sync root is a normal folder. **This affects the setup wizard (P3-4)** — on macOS the "choose a folder" step becomes "your Cairn appears in Finder's sidebar" when files-on-demand is enabled. Update the wizard copy and flow rather than pretending the platforms behave alike. ## Steps - [ ] Create the extension target; get an empty domain appearing in Finder before anything else. - [ ] Establish IPC between extension and Go daemon; define the message set. - [ ] Implement enumeration from the daemon's state DB. - [ ] Implement content fetch, streaming, with progress and cancellation. - [ ] Implement pinning via `NSFileProviderManager` materialisation APIs. - [ ] Sign both app and extension (inside-out, per P3-10); confirm notarisation still passes. - [ ] Handle domain removal on uninstall and sign-out. - [ ] Test on a real Mac: 1,000 placeholders visible in Finder, open one, exactly one hydrates. - [ ] Test a 4 GB hydration without the extension being killed. - [ ] Commit: `git commit -s -m "feat(vfs): macOS File Provider placeholder provider"` ## Acceptance criteria - Cairn appears in the Finder sidebar with dataless files. - Opening one file hydrates exactly that file. - A multi-GB file hydrates without exceeding the extension's memory limit. - App and extension are signed and notarised together. - Removing the account removes the domain cleanly. - `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#46
No description provided.