README.md

Eternal Terminal Android #

A modern Android client for Eternal Terminal with Material You theming, automatic reconnection, and secure authentication.

Features #

  • Host Management: Save and organize your Eternal Terminal connections
  • Material You Design: Dynamic colors and modern Android UI
  • SSH Authentication: Password and private key support
  • Automatic Reconnection: ET's signature feature - stays connected even when your network changes
  • Terminal Input: Full keyboard support including special keys, arrow keys, and control sequences
  • Biometric Security: Optional fingerprint/face unlock per host
  • Background Connections: Maintain connections in the background with foreground service

Architecture #

This is a pure Kotlin Android app implementing the Eternal Terminal protocol directly:

  • Protocol Layer: Pure Kotlin implementation of ET protocol (SSH handshake, TCP connection, XSalsa20+Poly1305 crypto, reconnection)
  • UI Layer: Jetpack Compose with Material You theming
  • Data Layer: Room database for host configurations and settings
  • Security: Android Keystore for private keys, BiometricPrompt for authentication

Why Pure Kotlin? #

The original plan was to wrap the ET C++ client via JNI, but this proved extremely complex because:

  1. ET is designed as a standalone binary, not a library
  2. Heavy dependencies (protobuf, libsodium, openssl, boost)
  3. Executes SSH as a subprocess for authentication
  4. Complex build system with vcpkg cross-compilation

Instead, we implemented the ET wire protocol directly in Kotlin, which provides:

  • Better Android integration
  • Easier maintenance
  • Smaller APK size
  • Full control over the connection lifecycle

Building #

Prerequisites #

  • Nix package manager with flakes enabled
  • direnv (optional but recommended)

Setup #

  1. Clone the repository:
git clone <repository-url>
cd et-android
  1. Enter the development shell:
nix develop
# Or if using direnv:
direnv allow
  1. Build the project:
./gradlew build
  1. Install debug APK:
./gradlew installDebug

Project Structure #

et-android/
├── app/
│   ├── src/main/
│   │   ├── java/io/et/android/
│   │   │   ├── protocol/          # ET protocol implementation
│   │   │   │   ├── EtProtocol.kt  # Main connection handler
│   │   │   │   ├── EtCrypto.kt    # XSalsa20 + Poly1305 encryption
│   │   │   │   ├── SshSetup.kt    # SSH handshake
│   │   │   │   └── TerminalInputHandler.kt  # Keyboard input
│   │   │   ├── data/
│   │   │   │   ├── db/            # Room database
│   │   │   │   └── model/         # Host, SshKey entities
│   │   │   ├── ui/
│   │   │   │   ├── screens/       # Compose screens
│   │   │   │   └── theme/         # Material You theme
│   │   │   ├── viewmodel/         # Hilt ViewModels
│   │   │   ├── di/                # Dependency injection
│   │   │   ├── service/           # Background connection service
│   │   │   ├── MainActivity.kt
│   │   │   └── EtAndroidApp.kt
│   │   ├── proto/                 # ET protobuf definitions
│   │   └── res/                   # Android resources
│   └── build.gradle.kts
├── identity-server/               # Passkey.pub identity and public-key directory service
│   └── src/main/kotlin/io/et/identity/
├── flake.nix                      # Nix development environment
├── .envrc                         # direnv configuration
└── PROJECT_PLAN.md               # Detailed project plan

Protocol Implementation #

The ET protocol implementation follows the official specification:

  1. SSH Handshake: Uses JSch to SSH into the host and run etterminal to get credentials
  2. TCP Connection: Connects to port 2022 (default)
  3. Encryption: XSalsa20 stream cipher + Poly1305 MAC using Bouncy Castle
  4. Message Framing: Length-prefixed protobuf messages
  5. Reconnection: Sequence number tracking and catchup buffers

Development Status #

Completed (MVP) #

In Progress #

Planned #

Passkey.pub Identity Server #

The identity-server module is a self-hostable passkey-backed SSH public-key directory. Production deployments should set:

ET_IDENTITY_RP_ID=identity.example.com
ET_IDENTITY_ORIGINS=https://identity.example.com
ET_IDENTITY_ANDROID_SHA256_CERT_FINGERPRINTS=<release signing certificate SHA-256>
ET_IDENTITY_DB=/var/lib/passkeypub/et-identity.sqlite

Use HTTPS for every non-local deployment. The Android client rejects non-HTTPS identity URLs except localhost development URLs, stores identity sessions in encrypted preferences, and excludes those preferences from backup.

Contributing #

This is an open-source project. Contributions are welcome!

License #

MIT/Apache 2.0 (TBD)