Skip to content

feat(ssh): native SSH support — PTY terminal + SFTP filesystem#276

Open
dcieslak19973 wants to merge 26 commits into
crynta:mainfrom
dcieslak19973:main
Open

feat(ssh): native SSH support — PTY terminal + SFTP filesystem#276
dcieslak19973 wants to merge 26 commits into
crynta:mainfrom
dcieslak19973:main

Conversation

@dcieslak19973
Copy link
Copy Markdown

Summary

  • Adds native SSH support using russh + russh-sftp (pure Rust, no OpenSSH dependency)
  • SSH profiles stored persistently with key file or SSH agent auth
  • TOFU (Trust On First Use) host key fingerprint verification
  • Full PTY terminal over SSH wired to the existing xterm.js frontend
  • All filesystem commands (read, write, stat, create, rename, delete, search, grep) branch on SSH via SFTP
  • WorkspaceEnvSelector shows SSH profiles alongside WSL distros, works cross-platform
  • SSH settings tab for managing profiles (name, host, port, user, auth method, key path)
  • Auto-reconnects last SSH profile on app startup

New dependencies

  • russh = "0.45" — SSH client
  • russh-sftp = "2" — SFTP over russh sessions
  • uuid = "1" — profile IDs
  • shellexpand = "3"~ expansion in key paths

Architecture

A new WorkspaceEnv::Ssh { profile_id } variant flows through all PTY and FS commands. Each command branches early on SSH, delegating to SFTP wrappers or an SSH exec channel. Local paths use the existing spawn_blocking path unchanged.

Test plan

  • Add an SSH profile in Settings → SSH
  • Connect via the workspace selector — TOFU dialog appears on first connect
  • Terminal opens and is interactive (shell, ls, vim, etc.)
  • File explorer shows remote filesystem, files open in editor
  • Restart app — auto-reconnects to last SSH profile

🤖 Generated with Claude Code

@dcieslak19973 dcieslak19973 requested a review from crynta as a code owner May 16, 2026 00:33
Copilot AI review requested due to automatic review settings May 16, 2026 00:33
dcieslak19973 and others added 22 commits May 16, 2026 10:39
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…RUD, search, grep

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ands

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…de tests

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… mod, profile cmd paths

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fix russh API paths for Windows (keys::key::PublicKey, keys::load_secret_key)
- Fix profile_id serde rename to camelCase for frontend compat
- Fix pty flush: use interval timer so output isn't stuck in pending buffer
- Add ssh_home command to fetch remote home dir via exec
- Auto-reconnect last SSH profile on app startup, persisted via lastSshProfileId pref
- Connect SSH before fetching home dir in switchWorkspace

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… fix)

Credentials were previously transmitted before the user confirmed the host
fingerprint, enabling a MITM attacker to capture them on first connect.

Fix: after connecting, check whether this is a first-time host connection.
If the fingerprint is unknown, immediately disconnect and return
TOFU_REQUIRED:<fingerprint> — no credentials are sent. The frontend shows
the confirmation dialog; once the user accepts, ssh_fingerprint_save
persists the fingerprint and the caller retries ssh_connect.

Also adds ssh_fingerprint_save as a proper Tauri command (previously
fingerprint persistence was done silently server-side with no user consent).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant