# Pamietnik — Architekturdokumentation (arc42) ## 1. Einführung und Ziele **Pamietnik** ist ein persönliches Journal 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; ListPublic, ListByUser │ └── users.go ListUsers, DeleteUser ├── auth/ │ └── auth.go HashPassword, VerifyPassword, Login, Register, GetSession, Logout └── api/ ├── router.go chi Routing, Middleware-Gruppen ├── middleware.go RequireAuth, requireAdmin (Session Cookie → Context) ├── ingest.go HandleSingleTrackpoint, HandleBatchTrackpoints ├── query.go HandleListDays, HandleListTrackpoints, Stops, Suggestions ├── webui.go Web UI: Feed, Register, Days, Admin-Handlers ├── journal.go Journal Entry Endpoints (inkl. visibility + hashtags) └── response.go writeJSON, writeError helpers ``` ### Ebene 2 — Android-Pakete ``` app/app/src/main/kotlin/de/jacek/pamietnik/ ├── 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). --- ## 7b. Datenbankschema ```mermaid erDiagram users { TEXT user_id PK TEXT username UK TEXT password_hash BOOLEAN is_admin TIMESTAMPTZ created_at } sessions { TEXT session_id PK TEXT user_id FK TIMESTAMPTZ created_at TIMESTAMPTZ expires_at } devices { TEXT device_id PK TEXT user_id FK TIMESTAMPTZ created_at } trackpoints { BIGSERIAL id PK TEXT event_id TEXT device_id FK TEXT trip_id TIMESTAMPTZ ts DOUBLE lat DOUBLE lon TEXT source TEXT note } stops { TEXT stop_id PK TEXT device_id FK TEXT trip_id TIMESTAMPTZ start_ts TIMESTAMPTZ end_ts DOUBLE center_lat DOUBLE center_lon INT duration_s TEXT place_label } suggestions { TEXT suggestion_id PK TEXT stop_id FK TEXT type TEXT title TEXT text TIMESTAMPTZ created_at TIMESTAMPTZ dismissed_at } journal_entries { TEXT entry_id PK TEXT user_id FK DATE entry_date TIME entry_time TEXT title TEXT description DOUBLE lat DOUBLE lon TEXT visibility TEXT[] hashtags TIMESTAMPTZ created_at } journal_images { TEXT image_id PK TEXT entry_id FK TEXT filename TEXT original_name TEXT mime_type BIGINT size_bytes TIMESTAMPTZ created_at } users ||--o{ sessions : has users ||--o{ devices : owns users ||--o{ journal_entries : writes devices ||--o{ trackpoints : records stops ||--o{ suggestions : generates journal_entries ||--o{ journal_images : contains ``` **Wichtige Felder:** | Tabelle | Feld | Bedeutung | |---------|------|-----------| | `users` | `is_admin` | Admin-Flag für Zugang zum Admin-Bereich | | `journal_entries` | `visibility` | `public` = im öffentlichen Feed sichtbar; `private` = nur für Autor | | `journal_entries` | `hashtags` | Kommagetrennte Tags als `TEXT[]`-Array | | `trackpoints` | `(device_id, event_id)` | UNIQUE-Constraint für Idempotenz | --- ## 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 ### Implementierungsstatus ```mermaid flowchart TD subgraph Impl[Implementiert] A[REST API Ingest\nPOST /v1/trackpoints] B[Session Auth\nArgon2id + Cookie] C[Tagesabfragen\nGET /v1/days etc.] D[Web UI\nGo Templates SSR] E[SPA Webapp\nMapLibre + WebComponents] F[Schema Init\ngo:embed + IF NOT EXISTS] G[Device-Registrierung\nEnsureDevice] end subgraph Backlog[Backlog / TBD] H[Stop Detection\nT061] I[Geocoding Adapter\nT063] J[Suggestions Engine\nT062] K[Android Auth\nAPI-Key / JWT T050] L[OpenAPI Spec\nT070] M[Hashtag-Support\nT073-T087] end subgraph Risiko[Sicherheit / Production] N[CSRF-Schutz\nkritisch] O[TLS / HTTPS\nhoch] P[Rate-Limiting\nmittel] Q[Secrets via .env\nkritisch] R[Pagination\nhoch] S[Security-Header\nnginx mittel] end style Impl fill:#d4edda,stroke:#28a745 style Backlog fill:#fff3cd,stroke:#ffc107 style Risiko fill:#f8d7da,stroke:#dc3545 ``` ### Security-Risiken (Architekt-Review) ```mermaid quadrantChart title Risiken — Schwere vs. Aufwand x-axis Geringer Aufwand --> Hoher Aufwand y-axis Geringes Risiko --> Hohes Risiko quadrant-1 Sofort beheben quadrant-2 Planen quadrant-3 Beobachten quadrant-4 Nice to have CSRF-Schutz: [0.2, 0.9] Secrets via .env: [0.1, 0.85] TLS/HTTPS: [0.5, 0.8] Rate-Limiting Login: [0.3, 0.75] Pagination: [0.4, 0.6] Android Auth: [0.7, 0.65] Security-Header nginx: [0.15, 0.5] Error-Disclosure: [0.2, 0.5] EnsureDevice N+1: [0.35, 0.35] DB Pool Config: [0.2, 0.4] Stop Detection: [0.75, 0.5] Geocoding: [0.8, 0.4] OpenAPI Spec: [0.5, 0.2] Hashtags: [0.65, 0.3] ``` ### Produktionsreife ```mermaid flowchart LR DEV[Development\n✓ Lauffähig] MVP[MVP\nFehlend: CSRF, TLS\nSecrets, Rate-Limit] PROD[Production Ready\n+ Stop Detection\n+ Geocoding\n+ Monitoring\n+ Backup] DEV -->|Kritische Security-Fixes| MVP MVP -->|Business-Logik\n+ Observability| PROD style DEV fill:#d4edda,stroke:#28a745 style MVP fill:#fff3cd,stroke:#ffc107 style PROD fill:#cce5ff,stroke:#004085 ``` ### Offene Entscheidungen | Thema | Optionen | Auswirkung | |-------|----------|-----------| | `timestamp`-Format | epochMillis vs RFC3339 | Android + API Kompatibilität | | Android Upload Auth | Session Cookie / API-Key / JWT | Security-Architektur | | Payload | JSON vs Protobuf | Bandbreite auf Mobile | | Batch-Limits | max Items / max Bytes | Denial-of-Service Schutz | | Retention Policy | Löschen nach X Tagen | Storage-Kosten | | Stop-Parameter | Mindestdauer, Radius | Qualität der Vorschläge | | Geocoding Provider | Nominatim public / self-hosted | Datenschutz, Verfügbarkeit |