An Android terminal for SSH and Mosh, built on libghostty.
Abyssh connects your Android phone to remote machines over SSH, Tailscale SSH, or Mosh, and renders the session with the same terminal engine as Ghostty. It is a fork of jossephus/chuchu.
Connections
- SSH with a password, a key, or a key with a passphrase; Tailscale SSH; and Mosh, which keeps a session alive across network changes and sleep
- A local shell on the phone
- Host key checks on first connect, a warning when a known key or its type changes, and a list of trusted keys where you can forget one
- A command per host that runs after each connect or reconnect
- tmux and zmx session lists, so you can reattach to a session, end one, or choose which one to attach to each time you connect
- tmux control mode, which shows each tmux window as a tab with its own scrollback, search, and selection, and draws every pane of a split window (needs tmux 3.2 or later)
Terminal
- Ghostty's terminal emulation, with scrollback, mouse support, and images through the Kitty graphics protocol
- 463 color themes from Ghostty, with separate light and dark choices, and a pure black option for OLED screens
- Fira Code, Geist Mono, Hack, and JetBrains Mono, with Nerd Font symbols
- Search through the scrollback, and tap links in the output to open or copy them
- Fling to scroll the scrollback, and scroll by touch in
lessandman - Mouse and touchpad support: scroll with the wheel, select by dragging
- A configurable row of extra keys above the keyboard, and custom actions
- Several sessions at once in tabs
Files and keys
- An SFTP file browser that streams transfers of any size, resumes them after a failure, and creates and renames folders
- Host groups, favourites, and sorting by last connected
- Quick connect from
user@host:port, and import of hosts from anssh_configfile - Ed25519 and RSA key generation, and import of existing keys
- Encrypted export and import of hosts and keys
Privacy
- Saved passwords and private keys are encrypted with a key that the Android Keystore protects
- An optional app lock that uses your fingerprint or the phone's screen lock
- An option that hides the app's contents from screenshots and the recent apps list
- No telemetry: see the privacy policy. To report a vulnerability, see the security policy
Download the signed APK (abyssh-<version>-arm64-v8a.apk) from the
latest release, or build it
from source as described below. Abyssh needs Android 7.0 or later. Release builds
include the native library only for 64-bit ARM phones (arm64-v8a), which covers
nearly all current devices.
You need:
- mise, which installs the pinned JDK 21, Zig 0.17.0, actionlint, and rumdl
- The Android SDK, with
ANDROID_HOMEset and an NDK installed
mise install
# Build the native library. Point ANDROID_NDK_HOME at one installed NDK version.
export ANDROID_NDK_HOME="$ANDROID_HOME/ndk/<version>"
mise run native # every ABI
mise run native -Dtarget=aarch64-linux-android # 64-bit ARM only, faster
mise run install # build the debug APK, install it on a connected phone, and open itThe native build copies libabyssh_jni.so into android/app/src/main/jniLibs/. Run
it again whenever you change Zig code. Without it the app still builds and opens, but
connections, key generation, and the terminal don't work.
Other tasks (mise tasks ls lists them all):
| Task | What it does |
|---|---|
mise run build |
Build the debug APK |
mise run check |
Every check that needs no device: formatting, Detekt, Android Lint, unit tests, compiling the device tests, the debug build, and lints of the GitHub workflows and Markdown. If only Markdown changed since origin/main, just the Markdown lint |
mise run format |
Format the Kotlin sources and Gradle scripts |
mise run detekt |
Detekt static analysis |
mise run check:native |
Build the 64-bit ARM native library and check that every Kotlin bridge method has a native export |
mise run test |
Unit tests only |
mise run test:device |
Tests on a connected phone or emulator |
| Path | Contents |
|---|---|
android/ |
The Kotlin and Jetpack Compose app, built with Gradle |
zig-src/ |
The native library: the terminal engine, SSH, Mosh, the local shell, and backup encryption, built with Zig |
.github/workflows/ |
CI checks and releases |
assets/ |
The logo used in this README |
The native library links libghostty-vt for terminal emulation, libssh2 and OpenSSL for SSH, and a Zig Mosh client.
Pushing a tag such as v1.2.3 waits for CI to pass on the tagged commit, which must be
on main, then builds a signed APK and app bundle and publishes them in a release with
generated notes as abyssh-1.2.3-arm64-v8a.apk and abyssh-1.2.3.aab.
mise run release creates the next tag from the Conventional Commits since the last
one. Before 1.0, a breaking change bumps the minor version and anything else bumps the
patch version; from 1.0 on, breaking changes bump major and feat bumps minor. Pass
major, minor, or patch to override it. The tag stays local until you push it with
git push origin v<version>.
Signing reads the KEYSTORE_BASE64, KEYSTORE_PASSWORD, KEY_ALIAS, and
KEY_PASSWORD secrets of the release environment, which only main and v* tags
can use. The workflow decodes the keystore into the runner's temporary directory and
deletes it after the build.
To sign a release build locally, keep the keystore outside the repository and point the build at it:
cd android
KEYSTORE_PATH="$HOME/path/to/release.jks" KEYSTORE_PASSWORD=… KEY_ALIAS=… KEY_PASSWORD=… \
./gradlew assembleReleaseWithout these variables the release build is unsigned; with only some of them it fails.
MIT; see LICENSE. Abyssh is based on chuchu by jossephus and its contributors.
