Files
pamietnik/doc/architecture.md
Christoph K. 86627f94b1
Some checks failed
Deploy to NAS / deploy (push) Failing after 26s
Add public feed, admin area, self-registration, visibility & hashtags
- Public feed (/) with infinite scroll via Intersection Observer
- Self-registration (/register)
- Admin area (/admin/entries, /admin/users) with user management
- journal_entries: visibility (public/private) + hashtags fields
- users: is_admin flag
- DB schema updated (recreate DB to apply)
- CI: run go test via docker run (golang:1.25-alpine) — fixes 'go not found'

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 20:53:31 +02:00

24 KiB

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

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

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

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)

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)

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)

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)

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

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

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:

docker-compose up --build

Schema wird beim API-Start automatisch initialisiert (keine separate Migration nötig).


7b. Datenbankschema

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$<saltHex>$<hashHex>)
  • 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 <track-map>)

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

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)

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

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