# Scoreboard-Deployment

Der statische Export bleibt unter
`$WEBROOT/kernel-panic/`. Der einzige dynamische Teil ist der
separate Score-Dienst:

- Compose: `$COMPOSE_DIR/`
- Container: `kernel-panic-score-api`
- Netzwerk: nur das interne `kernel_panic_backend`, kein Host-Port und keine
  Internetroute; der Webserver hängt zusätzlich in diesem Netz
- Daten: `data/scores.db` (UID/GID 10001, Modus 700/600)
- Reverse Proxy: ausschließlich `/kernel-panic/api/scores` und
  `/kernel-panic/api/health`

## Schutzgrenzen

- Runtime ohne Root, alle Linux-Capabilities entfernt
- read-only Container-Dateisystem; nur das SQLite-Volume ist beschreibbar
- 64 MiB RAM, 0,25 CPU, 40 Prozesse
- Same-Origin-Pflicht für POST, 8-KiB-Bodylimit und zweistufiges Rate-Limit
- parametrisierte SQL-Abfragen, serverseitig validierter Fragenkatalog und
  serverseitige Neuberechnung von XP, Integrität und Score
- keine Konten, E-Mail-Adressen oder IP-Adressen in SQLite
- keine öffentliche Admin- oder Löschroute

Die öffentliche Datei `scoreboard-backup.sqlite` ist davon vollständig
getrennt. Sie enthält ausschließlich erfundene Treasure-Hunt-Daten und wird
nur statisch ausgeliefert.

## Betrieb

```bash
docker compose build --pull
docker compose up -d
docker compose ps
docker exec kernel-panic-score-api python -I -c \
  'import urllib.request; print(urllib.request.urlopen("http://127.0.0.1:8080/health").read().decode())'
```

Vor Nginx-Restart immer `nginx -t`, danach öffentliche Health- und
GET/POST-Smokes ausführen. Rollback: vorherige Nginx-Konfiguration
wiederherstellen und den Score-Compose-Stack stoppen; das Spiel selbst bleibt
statisch nutzbar.
