ADR-0014: Hosting, Deployment & Auth

Status
Proposed
Datum
2026-05-15
Decider
Malte (mit Claude)
Verwandt
ADR-0013 (Web-UI), ADR-0012 (Observability)
Tags
infra, security, deployment

Kontext

mvest braucht eine öffentlich erreichbare Web-UI für die Konsumation der Agent-Outputs. Die Domain mvest.malte.io steht zur Verfügung (DNS zeigt bereits auf den Server 148.251.126.27). Der Server hostet bereits andere nginx-basierte Anwendungen. Da Trades nicht ausgeführt werden, ist die Angriffsfläche begrenzt, aber Portfolio-Hypothesen, Watchlists und Empfehlungs-Historie sind nicht öffentlich gedacht.

Entscheidungstreiber

Hosting-Entscheidung

Domain & TLS

Reverse-Proxy

nginx als Reverse-Proxy. Pfad-Aufteilung:

Code-Location & Service-User

Prozess-Management

Deployment

Auth-Entscheidung

Auth erfolgt in der Webapp selbst — kein nginx-Auth-Layer, keine externen IdPs in v1.

Verworfen: nginx Basic Auth

Browser-Popup, schlechte UX.

Keine User-Selbstverwaltung möglich.

User-Management außerhalb der App.

Verworfen: OAuth (GitHub) in v1

Externe Abhängigkeit für ein One-User-Tool.

Mehr Code, mehr Failure-Modes.

Bleibt als sinnvolles v2-Upgrade.

Gewählt: Eigene App-Auth mit Argon2id

FastAPI-eigenes Auth-System; minimal aber sicher implementiert.

Volle Kontrolle, keine externe Abhängigkeit.

Pfad zu Multi-User offen.

Wenig Code, gut auditierbar.

Eigener Code muss korrekt sein (Mitigation: kleine Surface, Standard-Library für Hashing).

Auth-Bausteine

Storage-Format

data/users.yaml (gitignored, chmod 600)

users:
  - username: malte
    password: "klartext-passwort"
    is_active: true
    created_at: 2026-05-15T10:30:00Z
    last_login_at: null

Sessions — kein Schema, In-Memory

SessionData(BaseModel):
    session_id: str            # 32 bytes urlsafe random
    username: str
    created_at: datetime
    expires_at: datetime
    last_used_at: datetime

# Storage: dict[session_id, SessionData], in-memory only

Warum YAML statt SQLite für Users? Für v1 ist die User-Anzahl winzig (1), Änderungen sind selten. Eine Textdatei ist trivial zu inspizieren, manuell zu editieren und zu sichern. Sessions hingegen sind kurzlebig und volatil — in-memory ist hier angemessen. Migration zu SQLite ist möglich, wenn Multi-User in v2 kommt.

Initial-User-Setup

Beim ersten Boot prüft die App, ob die users-Tabelle leer ist. Wenn ja: liest sie die Env-Vars MVEST_INITIAL_USER und MVEST_INITIAL_PASSWORD, legt den User an und logged in. Anschließend werden die Env-Vars bei jedem weiteren Boot ignoriert (Bedingung: users ist nicht leer).

Sicherheit: Initial-Passwort steht im Klartext in .env. Nach erstem erfolgreichem Login sollte das Passwort über die Settings-Seite in der Web-UI geändert und anschließend die beiden Env-Vars aus .env entfernt werden.

Multi-User-Vorbereitung

Implementierungsstatus (Stand 2026-05-15)

KomponenteStatus
DNS mvest.malte.io✓ live
nginx Site-Config✓ live
TLS via Let's Encrypt✓ live (bis 2026-08-13, Auto-Renewal aktiv)
HTTP→HTTPS Redirect✓ live
/adr/ als Static-Serving✓ live
FastAPI-App + Auth-Implementierung⏳ ausstehend (siehe ADR-0013)
systemd-Unit mvest.service⏳ ausstehend (folgt mit App)

Konsequenzen

Positiv

Negativ