Can Tauri build a multi-pane SFTP client?
Contents
Why I looked into this
Most Mac SFTP clients are annoying to use. I wanted to open multiple directories in separate panes at the same time, but many tools just do not support that.
On the same server, I often jump between fixed deep paths such as /var/www/production/assets/2024/01/ and /var/www/staging/assets/. Re-navigating every time is a pain.
People kept saying “build it yourself” and “Tauri can do it”, so I checked whether that was actually true.
Conclusion
Yes, Tauri can absolutely build this.
- There are already real-world examples such as AeroFTP, Jexpe, and T-Shell
- Rust has solid SFTP crates
- It can be built as a native macOS app
Useful Rust crates
| Crate | Notes |
|---|---|
| russh-sftp | Pure Rust, async/tokio support. My pick |
| ssh2 | libssh2 wrapper. Stable |
| remotefs-ssh | Higher-level API. Simple to use |
russh-sftp feels the most modern and fits Tauri’s tokio-based architecture well.
Design approach
Let existing file managers handle local files
This app should be remote-only. Local file operations can stay in Finder or QSpace.
- No local file browser
- Upload via drag-and-drop from Finder
- Download by dragging from the app to Finder
Simple division of responsibility.
Multiple panes, multiple paths
You can open multiple panes on the same server. Share the connection and vary only the path.
┌─────────────────────────────────────────┐
│ [+] New pane [Connection settings] │
├──────────────┬──────────────┬───────────┤
│ Server A │ Server A │ Server B │
│ /var/www │ /home/user │ /opt/app │
├──────────────┼──────────────┼───────────┤
│ css/ │ .bashrc │ logs/ │
│ js/ │ .zshrc │ conf/ │
│ index.html │ │ │
└──────────────┴──────────────┴───────────┘
With drag-and-drop between panes, same-server copy/move operations should work without going through the local machine.
Technical points
Tauri IPC
Expose Rust functions to the frontend with #[tauri::command].
#[tauri::command(async)]
async fn list_directory(
state: State<'_, SftpManager>,
pane_id: String,
path: String,
) -> Result<Vec<FileEntry>, String> {
// Fetch directory entries
}
Call it from the frontend with invoke:
const files = await invoke('list_directory', {
paneId: 'left',
path: '/var/www'
});
Drag-and-drop support
Tauri supports file drops out of the box:
tauri::Builder::default()
.on_file_drop_event(|event| {
if let tauri::FileDropEvent::Dropped(paths) = event.event {
// File paths dropped from Finder
}
})
State management
Keep per-pane SFTP sessions in a HashMap:
struct SftpManager {
sessions: HashMap<String, SftpSession>, // pane_id -> session
}
If multiple panes connect to the same server, you can also keep one SSH connection and open multiple SFTP channels on top of it.
Async processing
russh-sftp is tokio-based, so large directory listings and transfers will not block the UI. Progress updates can also be emitted in real time with window.emit().
Caveats
IPC overhead
Tauri IPC is JSON serialization. If there are too many files, it becomes expensive.
Mitigation: page results in chunks of 50 to 100 entries.
macOS sandbox
If you enable the App Sandbox, network permissions need to be configured. Tauri handles most of it, but Entitlements.plist still needs to be checked.
SSH key auth
- ssh-agent integration: use
ssh2::Agentto talk to the system ssh-agent - Direct key-file loading:
ssh_keycan read PEM/OpenSSH formats
Estimated development time
- If you already know Rust: 2 to 4 weeks for the core features
- If you are learning Rust at the same time: 4 to 8 weeks
The frontend can be React or Svelte, whichever you prefer.
Projects worth looking at
- Jexpe - Tauri + Next.js SSH/SFTP client
- russh examples - SFTP implementation examples
Wrap-up
A multi-pane SFTP client in Tauri is technically very doable. There are existing implementations and the crates are already there.
If I get some time, I want to build it.
CyberDuck that can open multiple windows would be fine too, honestly.