| .zcode/plans | ||
| app | ||
| gradle | ||
| .gitignore | ||
| build.gradle.kts | ||
| gradle.properties | ||
| gradlew | ||
| README.md | ||
| settings.gradle.kts | ||
Mobile SSH Manager
Android-App (Kotlin + Jetpack Compose) zum Verwalten von SSH-Verbindungen und zum interaktiven Arbeiten auf Servern über ein eingebautes Terminal.
Stand / 1Password-Hinweis (bitte lesen)
Der ursprüngliche Wunsch war 1Password SSH Key Support. Das ist auf Android aktuell technisch nicht möglich:
- Der 1Password SSH Agent ist Desktop-only (macOS, Windows, Linux).
- Es gibt keine Kotlin/Java-SDK und keinen Android-Agent, nur Go/JS/Python-SDKs (Desktop/Server) und einen offenen Feature-Request für Android.
- Drittanbieter-Apps können 1Password-Schlüssel auf Android nicht einbinden.
Stattdessen unterstützt diese App den realistischen Workflow: Private Keys werden als Datei importiert (Storage Access Framework) und AES-GCM-verschlüsselt via Android Keystore gespeichert. Der praktische Weg mit 1Password ist also: Key am Desktop aus 1Password exportieren → aufs Handy übertragen → in der App importieren. Kein 1Password-Bezug im UI, weil er ehrlicherweise nicht eingehalten werden könnte.
Funktionen
- Hosts anlegen/bearbeiten/löschen (Name, Host, Port, User, Auth-Art)
- Authentifizierung per Passwort oder per importiertem Private Key
- Keys importieren (OpenSSH-/PEM-/PKCS8-/Ed25519-Formate via sshj + Bouncy Castle)
- Verbindung testen direkt aus dem Host-Editor
- Interaktives Terminal: Compose-Canvas-Renderer auf der Termux-
terminal-emulatorEngine (VT100/xterm, ANSI-Farben, Bold/Italic/Underline, Cursor, Alt-Buffer) - Tastatur-Erweiterungsleiste (ESC, Tab, Backspace, Pfeile)
- Dunkles, terminal-orientiertes Theme
Sicherheit
- Sensitive DB-Felder (Passwörter, private Keys) werden mit einem AES-GCM-Schlüssel aus dem Android Keystore ver- und entschlüsselt.
- Klartext steht nur transient im Arbeitsspeicher (während Editieren/Verbinden).
- Host-Verifikation ist aktuell promiscuous (MVP) — Known-Hosts-Speicher ist ein todo.
Architektur
core/crypto KeystoreCrypto (AES-GCM, Android Keystore)
data Room (Host, SshKey, Group) + DAOs + Repositories + DI
domain/model AuthType
ssh SshConnectionManager (sshj), SshKeyParser, SshSupport (Bouncy Castle)
terminal SshTerminalBridge (sshj <-> TerminalEmulator), TerminalTextStyle, KeyEncoder
terminal/ui TerminalCanvas (Compose-Renderer), TerminalViewModel, TerminalScreen
com.termux.terminal.TerminalBufferAccess Adapter auf package-private Buffer-Felder
ui theme, nav, hostlist, hosteditor, keylist, keyimport, components
Terminal-Pipeline
SSH-Server ──► sshj Shell.getInputStream()
│ (Reader-Thread)
▼
TerminalEmulator.append(bytes) ← aktualisiert TerminalBuffer
│
▼ (Revision-Signal)
TerminalCanvas (Compose) liest über TerminalBufferAccess
│
Tastatur ──► KeyEncoder ──► SshTerminalBridge.send() ──► sshj Shell.getOutputStream()
Die Termux-Engine wird direkt (ohne TerminalSession, das lokale Prozesse startet)
genutzt. Da TerminalBuffer.mLines/TerminalRow.mText package-private sind, greift
TerminalBufferAccess als Klasse im com.termux.terminal-Package darauf zu.
Stack
| Teil | Technologie |
|---|---|
| Sprache | Kotlin 2.0.21 (K2) |
| UI | Jetpack Compose (BOM 2024.10), Material 3 |
| DI | Hilt |
| DB | Room 2.6 |
| SSH | sshj 0.39 |
| Krypto | Bouncy Castle 1.78 (Key-Parsing) + Android Keystore (Feldverschlüsselung) |
| Terminal-Engine | Termux terminal-emulator 0.118.3 (via JitPack) |
| Build | AGP 8.7.3, Gradle 8.11.1, JDK 17 target / JDK 21 zum Bauen |
| SDK | compileSdk 34, minSdk 26 |
Bauen
Voraussetzung: Android SDK mit platforms;android-34 und build-tools sowie JDK 17/21.
# Mit dem Gradle-Wrapper (JDK 21 empfohlen):
JAVA_HOME=/pfad/zum/jdk-21 ./gradlew assembleDebug
# APK liegt dann hier:
app/build/outputs/apk/debug/app-debug.apk
Installieren auf einem verbundenen Gerät/Emulator:
./gradlew installDebug
# oder
adb install app/build/outputs/apk/debug/app-debug.apk
Bekannte Einschränkungen / Todos
- Passphrase-geschützte Keys: Beim Import wird die Passphrase geprüft, für das Login müssen Keys aktuell unverschlüsselt gespeichert werden (MVP).
- Known-Hosts: Host-Keys werden noch nicht verifiziert.
- Hintergrund-Persistenz: Sessions überleben Konfigurationswechsel (die Activity fängt
configChangesab), aber für echtes Background-Keeping ist ein Foreground-Service nötig (Folgearchitektur). - Terminal-Fidelity: Ziel ist robuste Shell/
vim/htop-Nutzung; exotische Features (Sixel, Maus-Reporting, Unicode-Width-Polierung) sind nicht Teil des MVP. - Echtes 1Password: erst möglich, wenn 1Password Android-Support anbietet.