An open-source, type-safe SSH client written in Kotlin with Compose Multiplatform. Runs on Android (APK) and Linux desktop (package / runnable JAR) from one shared codebase.
- One shared Kotlin codebase (
commonMain) for Android + desktop JVM - SSH2 via sshj — password and private-key (OpenSSH format) auth
- Strict host-key checking against a
known_hostsfile (TOFU warning: disable only if you accept MITM risk) - Secrets stored encrypted: Android Keystore / EncryptedSharedPreferences on Android, AES-256-GCM with a
0600-permission key file on desktop - Type-safe domain model:
sealed interface SshState, immutableConnectionProfilewith validated init
- Cleartext traffic disabled app-wide on Android (
network_security_config),allowBackup=false - Secrets never live in
ConnectionProfile; they are referenced by ID and kept in the encrypted store - PTY sessions run on a coroutine scope that is cancelled with the session (no leaked threads)
The desktop secure store protects secrets at rest on a multi-user machine, but any process running as your user can read
~/.osssh/key.bin. Prefer passphrase-protected private keys.
Requirements: JDK 17+, Gradle 8.10+ (or generate a wrapper once with gradle wrapper), Android SDK (for APK builds).
# Android debug APK -> composeApp/build/outputs/apk/debug/
gradle :composeApp:assembleDebug
# Signed release APK — create a keystore and configure signing first
gradle :composeApp:assembleRelease
# Linux desktop: run from sources
gradle :composeApp:run
# Linux desktop: native package (deb, requires fakeroot+dpkg) -> composeApp/build/compose/binaries/main/deb/
gradle :composeApp:packageReleaseDebCI: every push to GitHub builds the debug APK and the .deb and uploads them as workflow artifacts.
- Full VT100/xterm terminal emulation (current UI is a basic line-oriented shell)
-
ssh-agent-style key management UI - SFTP file browser pane
- Jump-host (ProxyCommand) support
- Connection profiles persisted across restarts (currently in-memory)
MIT — see LICENSE.