Skip to content

Latest commit

 

History

History
153 lines (118 loc) · 6.31 KB

File metadata and controls

153 lines (118 loc) · 6.31 KB

fskit-s3 logo

fskit-s3

Mount an S3 bucket or a WebDAV share as a folder on your Mac. Open it in Finder, browse it, read and write files — like any other drive, except the bytes live on the remote. No kernel extension, no macFUSE, no security downgrade.

It's built on FSKit, Apple's built-in framework for filesystems that run as normal apps (macOS 15.4+, tested on 26). The name says S3, but the design isn't tied to it: S3 and WebDAV work today, and SFTP and other services are on the roadmap.

Quick start

1. Install the app. macOS loads the filesystem from a host app, so you install and run that app once:

xcodegen generate && open fskit-s3.xcodeproj   # pick your team, then Build & Run

(Code signing needs a paid Apple Developer account — see CONTRIBUTING.md.)

2. Enable the extension. The app appears as a ☁ item in your menu bar. On first launch it checks whether the filesystem extension is turned on and, if not, opens a window that links you to the right settings pane: System Settings ▸ Login Items & Extensions ▸ File System Extensions. Turn it on there.

3. Mount a bucket or share. From the ☁ menu, pick New Connection…, choose a Type, and fill in the form — for S3 the endpoint, bucket, region, and access keys; for WebDAV the server URL, username, and password (plus an optional folder to mount instead of the whole share). The secret is saved to your Keychain.

The menu then mounts and unmounts each connection. That's it — the storage shows up as a folder you can open in Finder.

Mounting from the command line

There's no custom CLI — a connection is just the system mount tool. The first path is the connection config (it doesn't need to exist on disk); the second is the folder to mount it at.

# With the secret in your Keychain (recommended). Store it once, keyed by the
# connection name, then mount without repeating it:
security add-generic-password -U -s dev.lucsoft.fskit-s3 -a photos -w 's3cr3t'
mount -F -t fskit-s3 \
  "/s3/photos?bucket=my-bucket&access_key_id=AKIA…&region=us-east-1" \
  ~/fskit-s3/photos

# Or pass the secret inline — no setup, but insecure (it's visible to `ps`/`mount`):
mount -F -t fskit-s3 -o secret=s3cr3t \
  "/s3/photos?bucket=my-bucket&access_key_id=AKIA…&region=us-east-1" \
  ~/fskit-s3/photos

# Unmount either of them:
umount ~/fskit-s3/photos

A WebDAV share works the same way — a different source path, the same secret rules (the password takes the secret access key's place):

security add-generic-password -U -s dev.lucsoft.fskit-s3 -a cloud -w 'pa55word'
mount -F -t fskit-s3 \
  "/webdav/cloud?endpoint=https://cloud.example.com/remote.php/dav/files/me&username=me" \
  ~/fskit-s3/cloud
umount ~/fskit-s3/cloud
All the options you can pass

The source path is /<type>/<name>?<key>=<value>&…, where <type> is s3 or webdav (plus /memory for the built-in demo, which takes no config). <name> is a label for the connection (it's also the Keychain account the secret is stored under) — use letters, numbers, and . - _ only, no spaces or slashes. The query carries the rest of the config; values can't contain ? & = #.

/s3/<name>?…

Key Required Meaning
bucket yes The bucket name.
access_key_id yes Your access key ID.
region no Bucket region (e.g. us-east-1). Leave off for most S3-compatible stores.
endpoint no Custom endpoint URL for an S3-compatible store (MinIO, Cloudflare R2, RustFS, …). Omit for AWS.
session_token no Session token, if you're using temporary (STS) credentials.

/webdav/<name>?…

Key Required Meaning
endpoint yes The share URL, e.g. https://cloud.example.com/remote.php/dav/files/me. Unlike S3 there's no default host, so this can't be omitted.
username yes The account username (auth is HTTP Basic).
root no A sub-directory of the share to mount instead of the whole thing, e.g. /photos.

The secret — the S3 secret access key, or the WebDAV password — is never part of the path (it would show up in ps and mount). It comes from your Keychain, keyed by <name>; if it isn't there, pass it as a mount option instead:

Option Meaning
-o secret=<key> The secret access key or password, passed inline. Convenient, but insecure — visible to ps/mount. Prefer the Keychain.

For the full story on how the secret travels (including the unsigned-dev-build caveat), see mounting by hand.

How it works

flowchart TD
    apps["Finder / Photos / any app"] -->|POSIX VFS| fskitd["fskitd (FSKit)"]
    fskitd -->|Objective-C| ext["ext — FSKit extension"]
    ext -->|"async StorageBackend trait"| core["core::StorageBackend"]
    backend["backend — OpenDAL"] -.implements.-> core
    backend --> s3[("S3")]
    backend --> dav[("WebDAV")]
    backend -.->|feature flag| more[("SFTP / …")]
Loading

Your Mac talks to the extension the way it talks to any disk — list a folder, read a file, write a file. The extension turns those requests into object-storage operations. Everything above the storage layer is generic, so adding a new service is a small, contained change.

The whole project is written in Rust, using Apache OpenDAL for the storage side (~40 services behind one interface). For the full design, rationale, and contributor guide, see CONTRIBUTING.md and CLAUDE.md.

Status

  • Browse and read files (list + read)
  • Write files (create / write / truncate / rename / remove)
  • SwiftUI menu-bar app — manage connections, Keychain secrets, mounting
  • Verified end-to-end against a real S3 bucket on a signed build
  • WebDAV shares (Nextcloud, ownCloud, Synology, mod_dav)
  • Mount via s3:// URL scheme
  • Graceful handling of flaky networks (timeouts, disconnects)
  • More backends — SFTP
  • Local cache layer so hot files stay on disk
  • Hosting a Photos library (needs a different FSKit flavor — see CLAUDE.md)

License

MIT