技術 約3分で読めます

TauriでマルチペインSFTPクライアントは作れるのか調べた

きっかけ

MacのSFTPクライアント、使いづらいソフトしかない。複数のディレクトリを別ペインで同時に開きたいのに、それができない。

同じサーバーでも /var/www/production/assets/2024/01//var/www/staging/assets/ みたいに、深い階層の決まった場所を行き来することが多い。毎回ナビゲートするのがダルい。

「自分で作れ」「Tauriでいけるだろ」と言われたので、本当にできるのか調べてみた。

結論

Tauriで十分作れる。

  • 既に実績あり(AeroFTP、Jexpe、T-Shellなど)
  • RustのSFTPクレートも充実している
  • macOSネイティブアプリとしてビルド可能

使えるRustクレート

クレート特徴
russh-sftpPure Rust、async/tokio対応。おすすめ
ssh2libssh2のラッパー。安定している
remotefs-ssh高レベルAPI。シンプルに使える

russh-sftp が最も現代的で、Tauriのtokioベースのアーキテクチャと相性がいい。

設計思想

ローカルは既存ファイラーに任せる

このアプリはリモート専用。ローカル側のファイル操作はFinderやQSpaceに任せる。

  • ローカルファイルシステムのブラウジング機能は不要
  • FinderからのD&Dでアップロード
  • アプリからFinderへのD&Dでダウンロード

シンプルな役割分担。

複数ペインで複数パス

同一サーバーで複数ペインを開ける。接続は共有してパスだけ別。

┌─────────────────────────────────────────┐
│  [+] 新規ペイン   [接続設定]             │
├──────────────┬──────────────┬───────────┤
│ サーバーA    │ サーバーA    │ サーバーB │
│ /var/www     │ /home/user   │ /opt/app  │
├──────────────┼──────────────┼───────────┤
│ css/         │ .bashrc      │ logs/     │
│ js/          │ .zshrc       │ conf/     │
│ index.html   │              │           │
└──────────────┴──────────────┴───────────┘

ペイン間のD&Dで、同一サーバー内ならローカルを経由せず直接コピー/移動もできるはず。

技術的なポイント

Tauri IPC

#[tauri::command] でRust関数をフロントエンドに公開する。

#[tauri::command(async)]
async fn list_directory(
    state: State<'_, SftpManager>,
    pane_id: String,
    path: String,
) -> Result<Vec<FileEntry>, String> {
    // ディレクトリ一覧を取得
}

フロントエンドからは invoke で呼び出す。

const files = await invoke('list_directory', {
  paneId: 'left',
  path: '/var/www'
});

D&D対応

Tauriは標準でファイルドロップをサポートしている。

tauri::Builder::default()
    .on_file_drop_event(|event| {
        if let tauri::FileDropEvent::Dropped(paths) = event.event {
            // Finderからドロップされたファイルパス
        }
    })

状態管理

ペインごとのSFTPセッションを HashMap で管理する。

struct SftpManager {
    sessions: HashMap<String, SftpSession>,  // pane_id → session
}

同一サーバーへの複数ペインなら、SSH接続は1本でSFTPチャネルだけ複数開く設計もできる。

非同期処理

russh-sftp はtokioベースなので、大量のファイルリストやファイル転送でもUIがブロックされない。進捗表示も window.emit() でリアルタイムに更新できる。

注意点

IPCのオーバーヘッド

TauriのIPCはJSONシリアライズ。ファイル数が多いとオーバーヘッドになる。

対策: ページネーションで50〜100件ずつ取得する。

macOSサンドボックス

App Sandboxを有効にする場合、ネットワーク権限の設定が必要。Tauriがほぼ自動で設定してくれるが、Entitlements.plist の確認は必要。

SSH鍵認証

  • ssh-agent連携: ssh2::Agent でシステムのssh-agentを使える
  • 鍵ファイル直接指定: ssh_key クレートでPEM/OpenSSH形式を読める

開発期間の目安

  • Rust経験者: 2〜4週間でコア機能
  • Rust学習しながら: 4〜8週間

フロントエンドはReactでもSvelteでも好みで。

参考になりそうなプロジェクト

  • Jexpe - Tauri + Next.jsのSSH/SFTPクライアント
  • russh examples - SFTPクライアントの実装例

まとめ

TauriでマルチペインのSFTPクライアント、技術的には十分実現可能。既存の実装例もあるし、クレートも揃っている。

時間ができたら作ってみたい。

CyberDuckが複数開けるだけでもまあいいんだが……