# BSN Server-Architektur — Split-Plan

**Stand:** 18.06.2026 | **Autor:** Basti (Chef vom Dienst)  
**Status:** Planungsphase — noch nicht umgesetzt

---

## Ziel

Das BSN-Chatsystem von einem Monolithen auf zwei spezialisierte Dienste aufteilen:

| Server | Rolle | Optimiert für |
|---|---|---|
| **Server A** (aktuell, 4C/8GB) | Web-Frontend + Datenbank | I/O, HTTP-Requests |
| **Server B** (lokal, 24C/32GB) | Sprachverarbeitung | CPU, parallele Jobs |

## Ist-Analyse

### Aktueller Monolith (Port 5002)
```
app.py (Flask) — EIN Prozess, alles drin:
├── WhatsApp Webhook          (I/O-leicht)
├── Telegram Webhook          (I/O-leicht)
├── Public Frontend           (I/O-leicht)
├── Admin Dashboard           (I/O-leicht)
├── faster-whisper Transkription  (CPU-schwer, 30–60s, 1 Kern)
├── Piper TTS                 (CPU-mittel, 1–3s)
├── ffmpeg Video-Thumbnails   (CPU-mittel, 2–5s)
├── DeepSeek LLM-Calls        (Netzwerk, kein CPU-Problem)
└── SQLite DB                 (I/O)
```

### Ressourcen-Profil pro Aufgabe

| Aufgabe | CPU | RAM | Dauer | Parallelfähig |
|---|---|---|---|---|
| WhatsApp-Request | <1% | 5 MB | 50ms | ✅ Beliebig |
| Frontend-Rendering | 2% | 10 MB | 100ms | ✅ Beliebig |
| **Whisper Transkription** | 100% (1 Kern) | 1,5 GB | 30–60s | ⚠️ 2–3 gleichzeitig |
| **Piper TTS** | 50% (1 Kern) | 200 MB | 1–3s | ✅ 4–5 |
| **ffmpeg Thumbnail** | 80% (1 Kern) | 100 MB | 2–5s | ⚠️ 2–3 |
| LLM API-Call | 0% | 20 MB | 2–5s | ✅ Beliebig |

### Kritischer Pfad: SPIEL Essen Messe
Während der Messe (4 Tage, ~10h/Tag) ist mit **Spitzen von 20–50 parallelen Requests** zu rechnen.  
Der aktuelle 4-Kern-Server würde bei 3–4 gleichzeitigen Transkriptionen **alle Kerne blockieren** — Web-Requests würden timeouten.

---

## Ziel-Architektur

```
┌─────────────────────────────────────────────────────┐
│              Server A — "Web & Data"                 │
│              4 Kerne, 8 GB RAM                       │
│              Port 5002 (intern)                      │
│                                                      │
│  Flask (app.py), schlank:                            │
│  ├── WhatsApp/Telegram Webhook                       │
│  ├── Public Frontend + Admin                         │
│  ├── SQLite (bsn_intake.db)                          │
│  ├── DeepSeek LLM-Calls                              │
│  └── HTTP-Client → Server B                          │
│                                                      │
│  NUTZT: whisperclient.py (sendet Audio an Server B)  │
└──────────────────────┬──────────────────────────────┘
                       │
          HTTP (lokal, <1ms Latenz)
          POST /transcribe   {audio_file} → {text}
          POST /speak        {text}       → {audio_file}
          POST /thumbnail    {video_file} → {thumbnail}
                       │
┌──────────────────────▼──────────────────────────────┐
│            Server B — "Language Processor"           │
│            24 Kerne, 32 GB RAM (lokal)               │
│            Port 5003 (intern)                        │
│                                                      │
│  Flask/FastAPI (language_server.py):                 │
│  ├── POST /transcribe                                │
│  │   └── faster-whisper 'base' model                 │
│  ├── POST /speak                                     │
│  │   └── Piper TTS (de_DE-thorsten-medium)           │
│  ├── POST /thumbnail                                 │
│  │   └── ffmpeg -ss 1 -vframes 1                     │
│  └── GET /health                                     │
│                                                      │
│  Worker-Pool: 8–10 parallele Transkriptionen         │
│  RAM: 1,5 GB pro Whisper-Instanz ≈ 15 GB nutzbar     │
└─────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────┐
│          Server C — "Fallback & Test"                │
│          24 Kerne, 32 GB RAM (lokal)                 │
│                                                      │
│  Optional:                                           │
│  ├── Redundanz: Failover wenn Server B ausfällt      │
│  ├── Testumgebung: Staging für neue Features         │
│  └── Proxmox-Host: mehrere isolierte VMs             │
└─────────────────────────────────────────────────────┘
```

---

## API-Spezifikation (Server A ↔ Server B)

### `POST /transcribe`
```
Request:  multipart/form-data
  file:    audio.ogg (WhatsApp voice memo)

Response: 200 JSON
{
  "ok": true,
  "text": "Transkribierter deutscher Text...",
  "duration_seconds": 12.5,
  "processing_time_ms": 3420
}

Fehler:   { "ok": false, "error": "..." }
```

### `POST /speak`
```
Request:  JSON
{
  "text": "Hallo Daniel, hier ist deine Antwort.",
  "voice": "de_DE-thorsten-medium"     // optional, default
}

Response: 200 audio/wav
  (binary WAV data)

Fehler:   { "ok": false, "error": "..." }
```

### `POST /thumbnail`
```
Request:  multipart/form-data
  file:    video.mp4

Response: 200 image/jpeg
  (binary JPEG, 320px wide)

Fehler:   { "ok": false, "error": "..." }
```

### `GET /health`
```
Response: 200 JSON
{
  "ok": true,
  "workers_busy": 2,
  "workers_available": 8,
  "uptime_seconds": 86400
}
```

---

## Dateigrößen & Netzwerk

| Medientyp | Typische Größe | Übertragung (1 Gbit/s lokal) |
|---|---|---|
| WhatsApp Sprachnachricht | 50–500 KB | < 5 ms |
| WhatsApp Audio (2 Min.) | 2–5 MB | ~40 ms |
| WhatsApp Video (1 Min.) | 5–50 MB | ~400 ms |
| WAV-Audio (TTS Output) | 100–500 KB | < 5 ms |
| JPEG Thumbnail | 10–50 KB | < 1 ms |

**Fazit:** Dateigrößen sind kein Bottleneck. Selbst 50-MB-Videos gehen in <500ms über das lokale Netz.

---

## Migrationsplan (6 Schritte)

### Phase 1: Language Server extrahieren (2h)
1. `language_server.py` schreiben — Flask-App mit `/transcribe`, `/speak`, `/thumbnail`, `/health`
2. Whisper/Piper/ffmpeg-Code aus `app.py` in `language_server.py` verschieben
3. Auf Server B deployen und testen
4. healthcheck einrichten

### Phase 2: Web Server umbauen (1h)
5. `whisperclient.py` schreiben — HTTP-Client für Language Server
6. In `app.py`: direkte Funktionsaufrufe ersetzen durch `whisperclient.transcribe(audio_path)`
7. Fallback: wenn Language Server nicht erreichbar → Submission als "Transkription ausstehend" markieren

### Phase 3: Testen (1h)
8. WhatsApp-Sprachnachricht senden → Transkription via Server B verifizieren
9. TTS via Server B verifizieren
10. Video-Thumbnail via Server B verifizieren
11. Lasttest: 10 parallele Sprachnachrichten

### Phase 4: Proxmox (Daniel)
12. Proxmox auf Server B installieren
13. VM mit 12 Kernen + 16 GB RAM für Language Server
14. SSH-Zugang für Basti

### Phase 5: Redundanz (optional)
15. Language Server auch auf Server C deployen
16. Web Server mit Failover: wenn B nicht antwortet → C anfragen

### Phase 6: Monitoring
17. Health-Checks via cron (alle 60s)
18. Alert an Telegram wenn Language Server down

---

## Voraussetzungen

### Was Daniel bereitstellen muss
- [ ] Server B: Proxmox installiert, VM bereit
- [ ] Server B: SSH-Zugang für Basti
- [ ] Server B: Linux (Debian/Ubuntu), Python 3.11+
- [ ] Lokales Netzwerk: Server A kann Server B erreichen (ping, HTTP)

### Was auf Server B installiert wird
```bash
# System
apt install python3 python3-pip python3-venv ffmpeg espeak-ng

# Python
pip install faster-whisper flask requests

# Piper TTS (pre-built binary)
# Voices: de_DE-thorsten-medium.onnx (~61 MB)
```

---

## Risiken & Fallbacks

| Risiko | Eintritts-WSK | Fallback |
|---|---|---|
| Server B nicht erreichbar | Niedrig (lokales Netz) | Submission markiert als "Transkription ausstehend", retry in 30s |
| Whisper-Modell zu langsam | Niedrig (24 Kerne) | Kleinere `tiny`-Variante laden |
| RAM voll (zu viele parallele Jobs) | Mittel (unter Last) | Worker-Pool begrenzen, Queue |
| Piper TTS fehlende Library | Niedrig | LD_LIBRARY_PATH + ESPEAK_DATA_PATH gesetzt |

---

## Kosten-Nutzen

**Vor dem Split (aktuell):**
- 4-Kern-Server: Web + Transkription + TTS = alles in einem
- SPIEL Essen: 3 parallele Transkriptionen = alle Kerne blockiert = Web timeouts

**Nach dem Split:**
- 4-Kern-Server: NUR Web = ~100 gleichzeitige Requests möglich
- 24-Kern-Server: NUR Sprache = 8–10 parallele Transkriptionen
- Web-Server bleibt IMMER responsiv, egal wie viele Sprachnachrichten reinkommen

---

## Nächste Schritte

1. Daniel installiert Proxmox auf Server B
2. Daniel gibt SSH-Zugang
3. Basti deployt `language_server.py`
4. Basti baut `app.py` um
5. Integrationstest
6. Produktiv setzen
