Skip to content

Repository files navigation

Shellwave app icon
~/Shellwave

An SSH client for Android
Built because every mobile SSH client makes you choose between convenient and trustworthy, and that's a stupid choice.
Requires Android 12 (API 31) or later. GPLv3.

Latest release Release build status Android 12 or later Licence

Screenshots

A session tailing a coloured log, with three session tabs above it The host list with a quick-connect field 16-colour, 256-colour and 24-bit ramps rendered in a session Per-host settings: tmux reattach, profile and colour scheme overrides, key bar, ProxyJump

Unfolded, the session list and the terminal share the screen; collapsing the split gives the terminal the full width.

Session list and terminal side by side on an unfolded device The terminal filling the full width of an unfolded device

Features

  • Dropped connections reconnect on their own, without re-prompting for a password; optional tmux-backed sessions reattach with scrollback intact.
  • Host key verification on first connect, with the fingerprint shown. A changed key blocks the connection; there is no accept-all mode, and a background run never answers a prompt for you.
  • Credentials encrypted with the Android Keystore, hardware-backed where available, with an optional biometric unlock per credential.
  • Key enrolment: generate a key, install it in the host's authorized_keys, replace the saved credential. The private key never leaves the device.
  • Password, private key (imported or generated on device), and keyboard-interactive auth.
  • Local and remote port forwarding, SOCKS5, ProxyJump chaining, ~/.ssh/config import.
  • Saved scripts, run from the app, a widget, an app shortcut, a Quick Settings tile, or another app.
  • SFTP upload and download.
  • Per-host terminal profile, colour scheme and key bar layout, each overriding a global default.
  • 256-colour and truecolour output, wide characters, alternate screen.
  • No analytics, no telemetry, no crash reporting. It talks to the hosts you give it, and nothing else.

Install

Every release ships a signed APK, built by GitHub Actions from the tag - an unsigned build never reaches the release page:

Download the latest release

Note

Forwarded ports and the SOCKS5 proxy bind to loopback unless you say otherwise. Binding wider is a choice you have to make on purpose, which is rather the point. The coffee shop Wi-Fi does not need a route into your homelab.

Building

Two product flavours, identical but for one dependency:

./gradlew assembleFossRelease   # no proprietary dependencies - the F-Droid build
./gradlew assemblePlayRelease   # adds Play Billing, for the optional one-time tip

SupporterBilling is an interface in :app's main source set with one implementation per flavour, so only play links com.android.billingclient. On foss the Support section of Settings does not exist.

Debug builds, unit tests and lint:

./gradlew assembleFossDebug
./gradlew testFossDebugUnitTest
./gradlew lintFossDebug

The Room migration and Keystore tests are instrumented, so they want a real device or emulator - which is to say the two things you least want quietly broken are the two that take the most effort to check.

Terminal engine

Writing a VT parser from scratch is a fine way to lose a year, so this one is borrowed.

:terminal-core contains twelve files from termux-app's terminal-emulator module, ten of them byte-identical to upstream. TerminalRenderer.kt in :app is a Kotlin port of upstream's TerminalRenderer.java. The package is left as com.termux.terminal so the vendored files stay a mergeable diff.

terminal-core/VENDORING.md records the upstream commit, the file list and the edits made. The engine's pty/JNI subprocess code is not used; sessions are remote shells over SSH.

Third-party attribution lives in NOTICE, which a Gradle task copies into res/raw for the in-app licence screen.

Licence

GPLv3 - see LICENSE. The vendored terminal engine is GPLv3 from termux-app; NOTICE has the details.