# 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][1p-fr]. - Drittanbieter-Apps können 1Password-Schlüssel auf Android nicht einbinden. [1p-fr]: https://www.1password.community/developers-69/feature-request-android-and-ssh-19991 **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-emulator` Engine (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. ```bash # 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: ```bash ./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 `configChanges` ab), 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.