A native macOS SFTP file manager in the spirit of Cyberduck/FileZilla, built with SwiftUI and Citadel (Swift NIO SSH) — no Xcode project required, just Swift Package Manager.
This project is entirely vibe coded (initially made using Fable 5) and not a representation of my own work.
- Bookmarks sidebar — save servers (host, port, username, starting path),
double-click to connect, optional custom emoji icon per bookmark. Stored in
~/Library/Application Support/CloudLink/bookmarks.json. - Authentication — password or OpenSSH private key (Ed25519 and RSA, encrypted keys supported). Passwords and passphrases are kept in the macOS Keychain, never on disk.
- Remote browser — Finder-matching file icons (via NSWorkspace, so third-party document icons like PhpStorm's .php icon appear), sortable columns (name, size, modified, permissions), editable path bar, hidden-file toggle, folder-first ordering, double-click to open folders or download files.
- Transfers — chunked uploads/downloads with live progress bars and cancellation, capped at 3 concurrent transfers. Recursive folder download/upload. Drag files from Finder onto the file list to upload.
- File management — new folder, rename, recursive delete (with confirmation), copy remote path.
- Edit in external editor (⌘K) — opens the selected file via a local working copy; every save is detected and uploaded back automatically. Handles both in-place saves and atomic (rename-over) saves. The editor is configurable in Settings (⌘,): automatic (VS Code, then system default), the system default per file type, or any installed app.
swift build # debug build
Scripts/package.sh # release build → dist/CloudLink.app
open dist/CloudLink.appThe command-line toolchain has no XCTest, so verification is built into the binary. This checks the path/permission helpers locally, then connects to Rebex's public demo SFTP server and exercises auth, listing, download with progress, and error handling:
.build/release/CloudLink --smoke-testScripts/smoke-rw.sh additionally spins up a disposable sshd on
127.0.0.1:2222 and runs the read-write pass: private-key auth, chunked
upload, byte-for-byte download round-trip, rename, and recursive delete,
plus the file-watcher checks behind ⌘K editing.
Sources/CloudLink/
main.swift entry point (app vs --smoke-test)
CloudLinkApp.swift App scene, menu commands
ContentView.swift split view, sidebar, connect + password prompt flows
BrowserView.swift remote file table, toolbar, context menus
TransfersView.swift transfer queue panel
BookmarkEditorView.swift
SessionViewModel.swift @MainActor state: connection, listing, transfers,
edit sessions (⌘K save-and-upload loop)
EditingSupport.swift FileWatcher, external editor launcher, ⌘K command
SettingsView.swift editor preference pane (⌘,)
SFTPConnection.swift Citadel wrapper: connect/list/transfer/delete
BookmarkStore.swift JSON persistence
KeychainStore.swift Security.framework wrapper
Models.swift SiteBookmark, RemoteItem, TransferItem
Resources/ Info.plist, AppIcon.icns
Scripts/package.sh release build + .app assembly + ad-hoc codesign
- Host keys are accepted on first use without a known_hosts check
(
hostKeyValidator: .acceptAnything()). - One connection at a time (no tabs / dual-pane).
- Symlinked folders open fine, but symlinks are skipped during recursive folder downloads.
0 comments
log in to comment.