From 9775a22473a69ae457e0252d0ccf213546d9d005 Mon Sep 17 00:00:00 2001 From: "Christoph K." Date: Mon, 6 Apr 2026 10:20:01 +0200 Subject: [PATCH] Add arc42 architecture documentation in doc/architecture.md Co-Authored-By: Claude Sonnet 4.6 --- doc/architecture.md | 554 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 554 insertions(+) create mode 100644 doc/architecture.md diff --git a/doc/architecture.md b/doc/architecture.md new file mode 100644 index 0000000..51b04e7 --- /dev/null +++ b/doc/architecture.md @@ -0,0 +1,554 @@ +# Pamietnik — Architekturdokumentation (arc42) + +## 1. Einführung und Ziele + +**Pamietnik** (Codename RALPH) ist ein persönliches Lebens- und Reisejournal bestehend aus drei Komponenten: einer Android-App, einer Web-App und einem Go-Backend-Server. + +### Fachliches Zielbild + +- Android-App loggt GPS-Standortdaten im Hintergrund, cached offline, lädt bevorzugt per HTTP zum Server hoch +- Manuelles Hinzufügen von Punkten (ohne GPS) mit optionaler Notiz und Hashtags +- Server erkennt aus Logdaten „Stops" (längere Aufenthalte) und leitet Vorschläge ab +- Standortinformationen (Reverse-Geocoding) über kostenlose API (Nominatim/OSM) +- Web-Ansicht nach Login: Tagesübersicht und Tagesdetail mit Karte, Stops, Vorschlägen +- Datei-Export aus der App (Android SAF) +- Nutzung erfordert Account und Login + +### Qualitätsziele + +| Priorität | Ziel | Maßnahme | +|-----------|------|----------| +| 1 | Datensicherheit | Argon2id Passwort-Hashing, Session-Auth, HTTPS | +| 2 | Offline-Fähigkeit | Room + WorkManager auf Android, kein RAM-only Queue | +| 3 | Idempotenz | `event_id` (UUID) als Unique Key pro Device | +| 4 | Erweiterbarkeit | Geocoding-Provider über Config austauschbar | +| 5 | Datenschutz | Background-Location Disclosure (Google Play) | + +### Stakeholder + +| Rolle | Erwartung | +|-------|-----------| +| Mobiler Nutzer | Automatisches Tracking, Offline-Betrieb, Export | +| Web-Nutzer | Login, Tagesübersicht mit Karte, Stops und Vorschläge | + +--- + +## 2. Randbedingungen + +| Art | Bedingung | +|-----|-----------| +| Technisch | Android: Kotlin + Jetpack Compose (DEC-CLIENT-01) | +| Technisch | Backend: Go, PostgreSQL (DEC-DB-01) | +| Technisch | Webapp: Vanilla TypeScript, keine Frameworks | +| Technisch | Server-Schnittstelle: HTTP/REST, kein MQTT (DEC-API-01) | +| Technisch | Karten: OpenStreetMap + MapLibre, konfigurierbare Tile-Quelle (DEC-MAP-01) | +| Technisch | API-Spec: OpenAPI 3.1 als `openapi.yaml` (DEC-OPENAPI-01) | +| Organisatorisch | Keine kommerziell lizenzierten Bibliotheken oder Schriften | +| Organisatorisch | Geocoding über kostenlose API, Policy-konform (DEC-GEO-01) | +| Organisatorisch | Alle Anforderungen in Markdown; Diagramme in Mermaid (DEC-DOC-01) | + +--- + +## 3. Kontextsicht + +### Fachlicher Kontext + +| Kommunikationspartner | Interaktion | +|-----------------------|-------------| +| Benutzer mobil | Start/Stop Trip, Status, Export, manuelle Punkte, Hashtags | +| Benutzer web | Login, Tagesübersicht, Tagesdetail, Stops, Vorschläge, Tag-Filter | +| Android OS / Location Services | Liefert GPS-Standortupdates | +| Webserver | Nimmt Trackpoints entgegen, berechnet Stops, stellt Website bereit | +| Geocoding-Dienst (Nominatim) | Liefert Adress-/Place-Infos zu Koordinaten | +| Dateisystem via SAF | Export-Ziel für Logs/Trips | + +### Technischer Kontext + +```mermaid +flowchart LR + UserM[Nutzer mobil] + UserW[Nutzer Web] + App[Android App\nKotlin + Compose] + OS[Android Location Services] + Webapp[Webapp\nTS + nginx :9050] + API[Go Backend\nREST + Web UI] + DB[(PostgreSQL)] + GEO[Nominatim\nReverse-Geocoding] + SAF[Android SAF\nDatei-Export] + + UserM -->|Start/Stop\nManuelle Punkte\nHashtags| App + OS -->|GPS Updates| App + App -->|HTTPS REST JSON\nPOST /v1/trackpoints:batch| API + UserW -->|HTTPS Browser| Webapp + Webapp -->|/v1/* proxy_pass| API + API --> DB + API -->|HTTPS cached| GEO + App -->|ACTION_CREATE_DOCUMENT| SAF +``` + +| Schnittstelle | Protokoll | Auth | +|---------------|-----------|------| +| Android → Backend | HTTPS REST JSON | Session Cookie (TBD: API-Key/JWT) | +| Browser → Backend | HTTPS | Session Cookie | +| Backend → Nominatim | HTTPS | — (Rate-Limit, gecached) | +| App → Dateisystem | Android SAF | OS-Permission | + +--- + +## 4. Lösungsstrategie + +| Entscheidung | ID | Begründung | +|---|---|---| +| Android: Kotlin + Jetpack Compose | DEC-CLIENT-01 | Android „Kotlin-first", moderne UI | +| Go Single Binary als Backend | — | Geringe Ressourcen, einfaches Deployment | +| Distroless Docker Image | — | Minimale Angriffsfläche | +| Schema via `go:embed` + `CREATE TABLE IF NOT EXISTS` | DEC-SCHEMA-01 | Keine Migrations-Infrastruktur, idempotenter Start | +| Room (SQLite) auf Android | DEC-DB-01 | Offline-First Persistenz | +| PostgreSQL im Backend | DEC-DB-01 | JSONB, UUID-Funktionen, zuverlässig | +| Session Cookie statt JWT (Web) | DEC-AUTH-01 | Serverseitige Invalidierung, einfacheres Threat Model | +| Argon2id für Passwort-Hashing | REQ-AUTH-01 | GPU-resistent, State-of-the-art | +| MapLibre + OpenStreetMap | DEC-MAP-01 | Open Source, keine Lizenzkosten, konfigurierbare Tiles | +| Nominatim als Geocoding-Provider | DEC-GEO-01 | Kostenlos, OSM-basiert, Provider austauschbar | +| Vanilla Web Components | — | Kein Framework-Overhead, langlebig, keine Lizenzkonflikte | +| OpenAPI 3.1 als API-Spec | DEC-OPENAPI-01 | Maschinenlesbar, Validierbar in CI | + +--- + +## 5. Bausteinsicht + +### Ebene 1 — Systeme + +```mermaid +flowchart TB + subgraph Mobile[Android App — app/] + UI[Compose UI\nScreens + ViewModels] + Domain[Domain / Use-Cases] + LocalDB[(Room SQLite\nLocal DB + Queue)] + Worker[WorkManager\nUpload Worker] + Service[Foreground Service\nGPS Tracking] + Export[Export SAF] + end + + subgraph Server[Go Backend — backend/] + Router[chi Router\n+ Middleware] + IngestAPI[Ingest API\nPOST /v1/trackpoints] + QueryAPI[Query API\nGET /v1/*] + WebUI[Web UI\nGo Templates SSR] + Auth[Auth Store\nArgon2id + Sessions] + Stores[DB Stores\nTrackpoints / Stops\nSuggestions / Journal] + StopEngine[Stop Detection\n+ Suggestions] + GeoAdapter[Geocoding Adapter\n+ Cache + RateLimit] + DB[(PostgreSQL)] + end + + subgraph Web[Webapp — webapp/] + SPA[Vanilla TS SPA\nWeb Components] + Map[TrackMap\nMapLibre GL] + Nginx[nginx\nProxy + Static Files] + end + + Mobile -->|HTTPS Batch| IngestAPI + SPA -->|HTTPS /v1/*| Nginx + Nginx -->|proxy_pass| Router + Router --> IngestAPI + Router --> QueryAPI + Router --> WebUI + Router --> Auth + IngestAPI --> Stores + QueryAPI --> Stores + WebUI --> Stores + Auth --> DB + Stores --> DB + StopEngine --> GeoAdapter + GeoAdapter -->|HTTPS| GEO[Nominatim] +``` + +### Ebene 2 — Backend-Pakete + +``` +backend/ +├── cmd/ +│ ├── server/ Einstiegspunkt: Pool → InitSchema → Router → HTTP Server +│ └── createuser/ CLI: User mit Argon2id-Hash anlegen +└── internal/ + ├── domain/ Shared Types: Trackpoint, Stop, Suggestion, Session, User + ├── db/ + │ ├── schema.sql Eingebettetes Schema (go:embed, IF NOT EXISTS) + │ ├── db.go NewPool + InitSchema + │ ├── trackpoints.go UpsertBatch, ListByDate, ListDays, EnsureDevice + │ ├── stops.go ListByDate + │ ├── suggestions.go ListByDate + │ └── journal.go CRUD Journal Entries + Images + ├── auth/ + │ └── auth.go HashPassword, VerifyPassword, Login, GetSession, Logout + └── api/ + ├── router.go chi Routing, Middleware-Gruppen + ├── middleware.go RequireAuth (Session Cookie → Context) + ├── ingest.go HandleSingleTrackpoint, HandleBatchTrackpoints + ├── query.go HandleListDays, HandleListTrackpoints, Stops, Suggestions + ├── webui.go Server-side rendered Web UI (Go Templates) + ├── journal.go Journal Entry Endpoints + └── response.go writeJSON, writeError helpers +``` + +### Ebene 2 — Android-Pakete + +``` +app/app/src/main/kotlin/de/jacek/reisejournal/ +├── domain/ Trackpoint Domain Model +├── data/ Room Entities, DAOs, lokale DB +├── service/ Background Location Foreground Service +├── worker/ WorkManager Upload Worker +└── ui/ + ├── home/ HomeScreen (Compose) + HomeViewModel + ├── navigation/ NavGraph + └── theme/ Compose Theme +``` + +--- + +## 6. Laufzeitsicht + +### R1 — Trip starten und Trackpoints senden + +```mermaid +sequenceDiagram + participant App as Android App + participant Room as Room DB + participant Worker as WorkManager + participant API as Go Backend + participant DB as PostgreSQL + + App->>Room: Trackpoint persistieren (source=gps, pending) + Worker->>Room: Pending Batch laden (NetworkConnected) + Worker->>API: POST /v1/trackpoints:batch + Cookie + API->>DB: EnsureDevice (INSERT ... ON CONFLICT DO NOTHING) + API->>DB: UpsertBatch (ON CONFLICT device+event_id DO NOTHING) + API-->>Worker: {accepted_ids, rejected: []} + Worker->>Room: Accepted als "sent" markieren +``` + +### R2 — Offline → Online Retry (REQ-SYNC-02, REQ-SYNC-04) + +```mermaid +sequenceDiagram + participant Worker as WorkManager + participant Room as Room DB + participant API as Go Backend + + Worker->>API: POST /v1/trackpoints:batch + API--xWorker: Netzwerkfehler / Timeout + Worker->>Room: Pending belassen, retryCount++ + Note over Worker: Exponential Backoff\nConstraint: NetworkConnected + Worker->>API: POST /v1/trackpoints:batch (gleiche event_ids) + API-->>Worker: {accepted_ids} (Duplikate via ON CONFLICT ignoriert) + Worker->>Room: Accepted als "sent" markieren +``` + +### R3 — Web Login und Tagesansicht (REQ-WEB-01, REQ-WEB-02) + +```mermaid +sequenceDiagram + participant B as Browser + participant Nginx as nginx :9050 + participant API as Go Backend + participant DB as PostgreSQL + + B->>Nginx: POST /login (username, password) + Nginx->>API: proxy_pass + API->>DB: User laden, Argon2id verify + API-->>B: Set-Cookie: session=..., 303 /days + B->>Nginx: GET /v1/days?from=&to= + Cookie + Nginx->>API: proxy_pass + API->>DB: trackpoints JOIN devices WHERE user_id = $1 + API-->>B: [{date, count, first_ts, last_ts}] + B->>Nginx: GET /v1/trackpoints?date=YYYY-MM-DD + Cookie + API->>DB: trackpoints WHERE date = $2 AND user_id = $1 + API-->>B: [Trackpoint...] +``` + +### R4 — Manuellen Punkt hinzufügen (REQ-MAN-01 bis REQ-MAN-05) + +```mermaid +sequenceDiagram + participant U as Nutzer + participant VM as ViewModel + participant Room as Room DB + participant Worker as WorkManager + participant API as Go Backend + + U->>VM: Eingabe lat/lon/timestamp/note/tags + VM-->>U: Validierung as-the-user-types + VM->>Room: Trackpoint (source=manual, tags=[...], pending) + Worker->>Room: Fetch pending + Worker->>API: POST /v1/trackpoints:batch + API-->>Worker: Ack + Worker->>Room: Mark sent +``` + +### R5 — Stop-Erkennung und Geocoding (REQ-SUG-01, REQ-GEO-01) + +```mermaid +sequenceDiagram + participant API as Go Backend + participant SE as Stop Engine + participant GEO as Geocoding Adapter + participant NOM as Nominatim + participant DB as PostgreSQL + + API->>SE: Trigger nach Batch-Ingest (async) + SE->>DB: Letzte Trackpoints lesen + SE->>SE: Stop erkennen (minDuration, radiusMeters) + SE->>GEO: Reverse-Geocode Stop-Koordinate + alt Cache Hit + GEO-->>SE: Place Info (gecached) + else Cache Miss + GEO->>NOM: HTTPS Reverse-Geocode + NOM-->>GEO: Place Info + GEO-->>SE: Place Info (jetzt gecached) + end + SE->>DB: Stop + Suggestion speichern +``` + +### R6 — Schema-Initialisierung beim Server-Start + +```mermaid +sequenceDiagram + participant S as cmd/server + participant DB as PostgreSQL + + S->>DB: pgxpool.New + Ping + S->>DB: schema.sql ausführen (go:embed)\nCREATE TABLE IF NOT EXISTS ... + Note over DB: Idempotent — bestehende\nTabellen bleiben erhalten + S->>S: Stores + Router aufbauen + S->>S: HTTP Server starten :8080 +``` + +--- + +## 7. Verteilungssicht + +```mermaid +flowchart LR + Phone[Android Device] + Browser[Web Browser] + + subgraph Docker[Docker Compose] + Nginx[webapp\nnginx:alpine\n:9050] + API[api\ndistroless\n:8080 intern] + PG[postgres\npostgres:16-alpine\ninternal] + end + + GEO[Nominatim\nexternal HTTPS] + + Phone -->|HTTPS| Nginx + Browser -->|HTTPS| Nginx + Nginx -->|/v1/* /login /logout\nproxy_pass| API + Nginx -->|/ static SPA| Nginx + API --> PG + API -->|cached Rate-Limited| GEO +``` + +**Docker Compose Services:** + +| Service | Image | Port extern | Funktion | +|---------|-------|-------------|----------| +| `postgres` | postgres:16-alpine | — (intern) | Datenhaltung | +| `api` | golang:1.25 → distroless | — (intern) | REST API + Web UI | +| `webapp` | node:22 → nginx:alpine | **9050** | SPA + API-Proxy | + +**Start:** +```bash +docker-compose up --build +``` +Schema wird beim API-Start automatisch initialisiert (keine separate Migration nötig). + +--- + +## 8. Querschnittskonzepte + +### Authentifizierung & Sessions (REQ-AUTH-01, REQ-AUTH-02, DEC-AUTH-01) + +- Passwörter: Argon2id (`$argon2id$$`) +- Sessions in PostgreSQL (`sessions`-Tabelle): sofortige Invalidierung bei Logout +- Cookie: `HttpOnly`, `SameSite=Lax`, 24h Lebensdauer +- Android Upload-Auth: aktuell Session Cookie — **TBD: API-Key oder JWT** (DEC-AUTH-01) + +### Idempotenz (REQ-SYNC-04) + +- Jeder Trackpoint trägt eine client-generierte `event_id` (UUID empfohlen) +- Unique Constraint `(device_id, event_id)` in PostgreSQL +- `ON CONFLICT DO NOTHING` → doppelter Upload = 200 OK, kein Fehler +- Batch-Response enthält `accepted_ids` und `rejected[]` für Queue-Steuerung + +### Offline-First (REQ-SYNC-01 bis REQ-SYNC-03) + +- Jeder Punkt wird zuerst in Room (SQLite) persistiert +- Upload via WorkManager: `NetworkConnected`-Constraint, Exponential Backoff +- `source`-Feld unterscheidet `"gps"` und `"manual"` + +### Device-Registrierung + +- `EnsureDevice()` beim ersten Ingest: `INSERT ... ON CONFLICT DO NOTHING` +- Verknüpft `device_id` mit authentifiziertem `user_id` +- Query-Endpoints filtern über `devices.user_id` + +### Hashtags (REQ-TAG-01 bis REQ-TAG-06) + +- Feld `tags: string[]` im Trackpoint-Schema +- Eingabe-Vorschläge aus vorhandenen Tags des Nutzers +- Server: `GET /v1/tags` (Häufigkeit), `GET /v1/trackpoints?tag=...` +- Webapp: Tag-Filter in Tagesdetail, Tag-Übersicht + +### Geocoding (REQ-GEO-01 bis REQ-GEO-03) + +- Nur ereignisbasiert (pro Stop), niemals bulk/periodisch +- Cache vor jedem Provider-Call +- Provider über Config austauschbar (kein Hardcode) +- Nominatim: User-Agent korrekt setzen (Policy-Pflicht) + +### Karten (DEC-MAP-01, REQ-MAP-01, REQ-MAP-02) + +- Android: MapLibre SDK +- Webapp: MapLibre GL JS (open source, lizenzfrei) +- Tile-Quelle konfigurierbar (`tileUrl`-Property auf ``) + +--- + +## 9. Produktanforderungen (aus README) + +### 3.1 Background-Logging + +| ID | Anforderung | +|----|------------| +| REQ-LOC-01 | App MUSS Standort im Vordergrund und Hintergrund erfassen | +| REQ-LOC-02 | COARSE (optional FINE) im Manifest + Runtime; App funktioniert mit „approximate" | +| REQ-LOC-03 | Background-Location korrekt angefordert | +| REQ-LOC-04 | `foregroundServiceType="location"` wenn Foreground Service | + +### 3.2 Offline-First + +| ID | Anforderung | +|----|------------| +| REQ-SYNC-01 | Trackpoints zuerst lokal persistieren (kein RAM-Queue) | +| REQ-SYNC-02 | Upload bevorzugt; bei Fehler cachen und automatisch nachladen | +| REQ-SYNC-03 | WorkManager mit `NetworkConnected` + Retry/Backoff | +| REQ-SYNC-04 | Keine Duplikate bei Retries; Idempotenz via `event_id` | + +### 3.3 Manuelle Punkte + +| ID | Anforderung | +|----|------------| +| REQ-MAN-01 | Manueller Punkt: lat, lon, timestamp (Pflicht); Name/Notiz (optional) | +| REQ-MAN-02 | Lokal in Room speichern | +| REQ-MAN-03 | In Upload-Flow integriert (inkl. `event_id`) | +| REQ-MAN-04 | Validierung as-the-user-types; verständliche Fehlermeldungen | +| REQ-MAN-05 | Vorschlag „aktueller Zeitpunkt / aktuelle Position" (editierbar) | + +### 3.4 Export + +| ID | Anforderung | +|----|------------| +| REQ-EXP-01 | Export in Datei möglich | +| REQ-EXP-02 | Via SAF `ACTION_CREATE_DOCUMENT`; Nutzer wählt Speicherort | + +### 3.5 Server Website + +| ID | Anforderung | +|----|------------| +| REQ-WEB-01 | Website nur nach Login zugänglich | +| REQ-WEB-02 | Pro Tag Punkte anzeigen (Liste + Detailansicht) | +| REQ-WEB-03 | Daten nutzerspezifisch (ein Nutzer sieht nur eigene Daten) | +| REQ-WEB-04 | Kartenansicht (Punkte/Stops pro Tag) | + +### 3.6 Security / Auth + +| ID | Anforderung | +|----|------------| +| REQ-AUTH-01 | Passwörter NICHT im Klartext; Argon2id | +| REQ-AUTH-02 | Session Cookie; serverseitig verwaltbar (Logout/Expire) | +| REQ-AUTH-03 | Auth auch für Web-Query-Endpoints | + +### 3.7 Stops / Vorschläge + +| ID | Anforderung | +|----|------------| +| REQ-SUG-01 | Stop-Erkennung (Mindestdauer, Radius konfigurierbar) | +| REQ-SUG-02 | Vorschläge aus Stops ableiten | +| REQ-SUG-03 | Pro Nutzer/Device/Trip zuordenbar; in Website sichtbar | + +### 3.8 Geocoding + +| ID | Anforderung | +|----|------------| +| REQ-GEO-01 | Kostenloser Dienst (Nominatim); Caching + Rate-Limit | +| REQ-GEO-02 | Provider austauschbar via Config (ohne App-Update) | +| REQ-GEO-03 | Kein Bulk/periodisches Geocoding; ereignisbasiert (pro Stop) | + +### 3.9 Karten + +| ID | Anforderung | +|----|------------| +| REQ-MAP-01 | OpenStreetMap-Basis; MapLibre als Renderer | +| REQ-MAP-02 | Tile-Quelle konfigurierbar | + +### 3.10 OpenAPI + +| ID | Anforderung | +|----|------------| +| REQ-OPENAPI-01 | API als OpenAPI 3.1 dokumentiert | +| REQ-OPENAPI-02 | `openapi.yaml` im Repository versioniert | +| REQ-OPENAPI-03 | Alle Endpoints, Schemas, Fehler, Security-Schemes | +| REQ-OPENAPI-04 | CookieAuth via `type: apiKey, in: cookie` | + +### 3.11 Google Play / Datenschutz + +| ID | Anforderung | +|----|------------| +| REQ-PRIV-01 | Background-Location Disclosure im UI; korrekte Deklaration | + +### 3.13 Hashtags + +| ID | Anforderung | +|----|------------| +| REQ-TAG-01 | Trackpoints mit Hashtags versehen (`#restaurant`, ...) | +| REQ-TAG-02 | Tags lokal in Room + beim Upload übertragen (`tags: string[]`) | +| REQ-TAG-03 | Server persistiert Tags, abfragbar pro Trackpoint | +| REQ-TAG-04 | Webapp: Filter nach Hashtag | +| REQ-TAG-05 | Eingabe-Vorschläge aus vorhandenen Tags des Nutzers | +| REQ-TAG-06 | Webapp: Tag-Übersicht mit Häufigkeit | + +--- + +## 10. Architekturentscheidungen + +| ID | Entscheidung | Alternativen | Status | +|----|-------------|--------------|--------| +| DEC-CLIENT-01 | Android (Kotlin + Compose) | iOS, Flutter | Entschieden | +| DEC-LOC-01 | Background Location Logging | Nur Vordergrund | Entschieden | +| DEC-API-01 | HTTP/REST + JSON | MQTT, gRPC, Protobuf | Entschieden (Protobuf offen) | +| DEC-DB-01 | Room (Android) + PostgreSQL (Server) | SQLite Server, MySQL | Entschieden | +| DEC-WEB-01 | Website mit Login (SSR + SPA) | nur API | Entschieden | +| DEC-AUTH-01 | Session Cookie für Web; JWT optional | JWT only | Session entschieden | +| DEC-MAP-01 | MapLibre + OSM, konfigurierbare Tiles | Google Maps | Entschieden | +| DEC-GEO-01 | Nominatim, Provider austauschbar | Google Places API | Entschieden | +| DEC-OPENAPI-01 | OpenAPI 3.1 YAML | kein Spec | Entschieden | +| DEC-SCHEMA-01 | `go:embed` schema.sql, IF NOT EXISTS | golang-migrate | Entschieden | + +--- + +## 11. Risiken und offene Punkte + +| Risiko | Schwere | Status | +|--------|---------|--------| +| Android Upload Auth: Session Cookie suboptimal für Mobile | Mittel | **TBD** — API-Key oder JWT (T050) | +| `timestamp`-Format: epochMillis vs RFC3339 | Niedrig | **Offen** | +| Payload: JSON vs Protobuf | Niedrig | **Offen** | +| Batch-Limits (max Items, max Bytes) | Niedrig | **Offen** | +| Kein Rate-Limiting auf Ingest-Endpoints | Mittel | Backlog (T029) | +| Stop Detection nicht implementiert | Mittel | Backlog (T061) | +| Geocoding Adapter + Cache nicht implementiert | Mittel | Backlog (T063) | +| Kein HTTPS/TLS im nginx (nur HTTP intern) | Hoch | Für Produktion: TLS-Termination via Reverse Proxy nötig | +| Retention Policy (wie lange Trackpoints gespeichert) | Niedrig | **Offen** | +| Stop Detection Parameter (Mindestdauer, Radius) | Niedrig | **Offen** | +| Geocoding Provider: Nominatim public vs self-hosted | Niedrig | **Offen** |