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.
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.
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…®ion=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…®ion=us-east-1" \
~/fskit-s3/photos
# Unmount either of them:
umount ~/fskit-s3/photosA 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/cloudAll 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.
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 / …")]
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.
- 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)
MIT
