Plan Android-App (notes/android.md)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TiXsPUqw7oeomZ8wZrQW5q
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
764ad50b8e
commit
a8f392edba
@@ -0,0 +1,112 @@
|
|||||||
|
# Android-App (Plan, zurückgestellt)
|
||||||
|
|
||||||
|
Stand 2026-10-02. Noch nichts davon ist umgesetzt; das hier ist die
|
||||||
|
Entscheidungsgrundlage für später.
|
||||||
|
|
||||||
|
## Anforderungen
|
||||||
|
|
||||||
|
- PGP clientseitig: signieren, verifizieren, ver-/entschlüsseln
|
||||||
|
- private Schlüssel lokal auf dem Gerät speichern
|
||||||
|
- Push-Benachrichtigungen
|
||||||
|
- Kamera-Zugriff
|
||||||
|
|
||||||
|
## Entscheidung: Capacitor
|
||||||
|
|
||||||
|
Der bestehende React-Code wird mit Capacitor als APK gebündelt. Eine native
|
||||||
|
Kotlin-App ist nicht nötig; nativer Code beschränkt sich auf (fertige oder
|
||||||
|
kleine eigene) Plugins.
|
||||||
|
|
||||||
|
### Verworfen: PWA / Trusted Web Activity
|
||||||
|
|
||||||
|
Wäre fast ohne Umbau machbar (Manifest, Service Worker,
|
||||||
|
`/.well-known/assetlinks.json`, Bubblewrap), scheitert aber am PGP-Ziel:
|
||||||
|
Bei TWA/PWA liefert der Server den Code bei jedem Start neu aus. Wer den
|
||||||
|
Server kontrolliert (Angreifer, kompromittiertes PROD, Anordnung), kann
|
||||||
|
JavaScript nachschieben, das den privaten Schlüssel ausliest. Clientseitige
|
||||||
|
Kryptographie soll gerade das Vertrauen in den Server überflüssig machen.
|
||||||
|
|
||||||
|
Mit Capacitor steckt der Code signiert im APK; Updates laufen über den
|
||||||
|
App-Signaturschlüssel, nicht über einen PROD-Deploy.
|
||||||
|
|
||||||
|
### Verworfen: native App (Kotlin/Compose)
|
||||||
|
|
||||||
|
Kompletter Neubau des Frontends ohne Mehrwert für die Anforderungen; passt
|
||||||
|
außerdem schlecht zum bewusst rohen Design, das in der Web-Version schon
|
||||||
|
existiert.
|
||||||
|
|
||||||
|
## Umbau Frontend
|
||||||
|
|
||||||
|
- **API-Basis-URL:** `frontend/src/api.ts` ruft fest `/api…` auf. Das wird
|
||||||
|
konfigurierbar (z. B. `import.meta.env.VITE_API_BASE`), weil die App unter
|
||||||
|
`https://localhost` läuft, nicht auf der Domain.
|
||||||
|
- **Absolute URLs:** Bild-URLs aus der API und geteilte Links
|
||||||
|
(`/entry/123`) müssen die Domain enthalten.
|
||||||
|
- **Deep Links:** Android App Links für `https://<domain>/entry/…`.
|
||||||
|
- **Routing:** `createBrowserRouter` funktioniert unter Capacitor weiter.
|
||||||
|
|
||||||
|
## Umbau Backend
|
||||||
|
|
||||||
|
- **Bearer-Token:** Der Session-Cookie (`auth.go`, `HttpOnly`,
|
||||||
|
`SameSite=Lax`) kommt im WebView cross-origin nicht zuverlässig an.
|
||||||
|
Zweiter Login-Modus: Token im JSON-Body zurückgeben, Client schickt
|
||||||
|
`Authorization: Bearer …`, `getSession` akzeptiert beides. Die Web-Version
|
||||||
|
bleibt beim Cookie.
|
||||||
|
- **CORS:** Middleware für `Access-Control-Allow-Origin` mit den App-Origins
|
||||||
|
(`https://localhost`, `capacitor://localhost`) inkl. Preflight.
|
||||||
|
- **`checkOrigin`** (`main.go`): App-Origins zulassen bzw. Requests mit
|
||||||
|
Bearer-Token ausnehmen. Ohne Cookie gibt es dort kein CSRF-Risiko.
|
||||||
|
|
||||||
|
## PGP
|
||||||
|
|
||||||
|
- **Bibliothek:** OpenPGP.js im Bundle, kein nativer Code. Kommt mit der
|
||||||
|
CSP `default-src 'self'` aus.
|
||||||
|
- **Web-Version:** Derselbe Code kann dort laufen, aber mit der schwächeren
|
||||||
|
Garantie (servergelieferter Code, siehe oben). Sollte in der UI kenntlich
|
||||||
|
sein.
|
||||||
|
- **Schlüsselablage:** Der Android Keystore kann PGP-Schlüssel nicht direkt
|
||||||
|
halten (keine Curve25519/PGP-Formate, gibt Schlüssel nie heraus). Daher:
|
||||||
|
1. Im Keystore liegt ein hardwaregestützter AES-Schlüssel, optional an
|
||||||
|
Biometrie/PIN gebunden.
|
||||||
|
2. Mit ihm wird der PGP-Private-Key verschlüsselt.
|
||||||
|
3. Das Ergebnis liegt im App-Speicher.
|
||||||
|
Dafür gibt es Capacitor-Plugins (Secure Storage, Biometrie); notfalls
|
||||||
|
ein eigenes Plugin, ca. 100 Zeilen Kotlin. IndexedDB allein ist die
|
||||||
|
schwächere Alternative (nicht hardwaregebunden, XSS-lesbar).
|
||||||
|
- **Optional OpenKeychain:** Über dessen OpenPGP-API (Intents) kann die App
|
||||||
|
Signieren/Entschlüsseln delegieren und hält selbst nie einen Schlüssel.
|
||||||
|
Eigenes Capacitor-Plugin; nur als Zusatzoption, nicht als einziger Weg.
|
||||||
|
|
||||||
|
### Vorab klären (betrifft das Backend)
|
||||||
|
|
||||||
|
Signiert der Client Beiträge, muss der Server das abbilden:
|
||||||
|
|
||||||
|
- Tabelle mit öffentlichen Schlüsseln pro Account (Upload, Wechsel,
|
||||||
|
Widerruf)
|
||||||
|
- Signaturfeld an `entry`
|
||||||
|
- API liefert Signatur und Schlüssel mit, damit andere Clients
|
||||||
|
verifizieren können
|
||||||
|
|
||||||
|
Kann unabhängig von der App vorbereitet werden.
|
||||||
|
|
||||||
|
## Push
|
||||||
|
|
||||||
|
- Firebase Cloud Messaging über das Capacitor-Push-Plugin.
|
||||||
|
- Backend: Tabelle für Device-Tokens pro Account, FCM-Sender.
|
||||||
|
- Inhalt nur „es gibt etwas Neues“ oder PGP-verschlüsselt, sonst liest
|
||||||
|
Google mit.
|
||||||
|
|
||||||
|
## Kamera
|
||||||
|
|
||||||
|
Capacitor-Kamera-Plugin; alternativ reicht `<input type="file" capture>`
|
||||||
|
ganz ohne Plugin.
|
||||||
|
|
||||||
|
## Reihenfolge (Vorschlag)
|
||||||
|
|
||||||
|
1. Backend: Bearer-Token, CORS, `checkOrigin`
|
||||||
|
2. Frontend: API-Basis-URL, absolute URLs
|
||||||
|
3. Capacitor-Projekt, APK-Build, App Links
|
||||||
|
4. Kamera
|
||||||
|
5. Push (FCM, Device-Tokens)
|
||||||
|
6. PGP: Schlüsseltabelle/Signaturfeld im Backend, OpenPGP.js,
|
||||||
|
Keystore-gestützte Ablage
|
||||||
|
7. optional OpenKeychain-Plugin
|
||||||
@@ -12,3 +12,4 @@
|
|||||||
- Feed ohne JS (früherer SSR-Stand: Tag `ssr-archiv`)
|
- Feed ohne JS (früherer SSR-Stand: Tag `ssr-archiv`)
|
||||||
- robots.txt / Sitemap
|
- robots.txt / Sitemap
|
||||||
- Ja/Nein Auswertung
|
- Ja/Nein Auswertung
|
||||||
|
- Android-App mit clientseitigem PGP, Push, Kamera (Plan: `notes/android.md`)
|
||||||
|
|||||||
Reference in New Issue
Block a user