# Zentraler Push-Server für FEDEO

Dieser Entwurf beschreibt einen zentral betriebenen FEDEO-Push-Server, über den selbst gehostete FEDEO-Instanzen Push Notifications zustellen können. Ziel ist, dass Selfhost-Betreiber keine eigenen Apple-, Google- oder Web-Push-Zugangsdaten verwalten müssen und FEDEO trotzdem mandantenfähig, datensparsam und zuverlässig benachrichtigen kann.

## Zielbild

FEDEO erhält einen zentralen Zustelldienst unter einer FEDEO-kontrollierten Domain, zum Beispiel `https://push.fedeo.cloud`. Jede selbst gehostete Instanz registriert sich dort einmalig als Push-Client. Danach sendet die Instanz nur noch neutrale Zustellaufträge an den zentralen Server. Der zentrale Server übernimmt:

- Verwaltung öffentlicher Push-Konfigurationen für Web, iOS und Android
- Annahme signierter Zustellaufträge von Selfhost-Instanzen
- Zustellung über Web Push, APNs und FCM
- Retry, Rate Limits, Fehlerklassifizierung und Deaktivierung ungültiger Geräte
- technische Zustellmetriken ohne fachliche Inhaltsdaten

Die fachliche Entscheidung, wer welche Benachrichtigung erhält, bleibt in der jeweiligen FEDEO-Instanz. Der zentrale Server ist nur Transport- und Zustellkomponente.

## Grundprinzipien

- **Datenminimierung:** Der zentrale Server speichert keine FEDEO-Nutzerdaten, keine Mandantendaten und möglichst keine lesbaren Nachrichtentexte.
- **Instanzhoheit:** Selfhost-Instanzen behalten Nutzerverwaltung, Benachrichtigungseinstellungen und Eventlogik.
- **Zentrale Provider-Schlüssel:** APNs-, FCM- und VAPID-Schlüssel liegen ausschließlich im zentralen Push-Server.
- **Signierte Aufträge:** Jede Instanz authentifiziert sich per rotierbarem Instanzschlüssel.
- **Mandantentrennung:** Instanzen und deren Geräte werden strikt getrennt, auch wenn sie denselben zentralen Dienst nutzen.
- **Ausfallsicherheit:** Der lokale FEDEO-Betrieb darf nicht blockieren, wenn der Push-Server temporär nicht erreichbar ist.

## Architektur

```text
FEDEO Web / Mobile App
  |
  | registriert Gerät bei lokaler Selfhost-Instanz
  v
Selfhost-FEDEO-Instanz
  |
  | signierte API-Aufrufe
  v
Zentraler FEDEO Push-Server
  |
  +-- PostgreSQL für Instanzen, Geräte, Zustellaufträge, Audit-Metadaten
  +-- Redis oder Queue für Retry und Backpressure
  +-- Worker für Web Push, APNs und FCM
  +-- Provider-Schlüsselverwaltung
  |
  +--> Web Push Endpoints
  +--> Apple Push Notification service
  +--> Firebase Cloud Messaging
```

### Komponenten

| Komponente | Aufgabe |
| --- | --- |
| Selfhost-FEDEO-Backend | entscheidet Empfänger, erzeugt Notification Items, übermittelt Push-Aufträge |
| Zentrale Push API | registriert Instanzen und Geräte, nimmt Push-Aufträge an |
| Push Worker | sendet an Web Push, APNs und FCM, verarbeitet Provider-Fehler |
| Geräte-Registry | speichert technische Push Tokens oder Web-Push-Subscriptions |
| Zustelljournal | speichert technische Statuswerte, keine fachliche Historie |
| Admin-Konsole | zeigt Instanzstatus, Fehlerquoten, Rate Limits und Schlüsselrotation |

## Rollenverteilung

### Lokale FEDEO-Instanz

Die Selfhost-Instanz bleibt führend für:

- Nutzer und Mandanten
- Benachrichtigungseinstellungen
- Event-Typen und Kanäle
- fachliche Nachrichtentexte
- In-App-Benachrichtigungen
- lokale Audit- und Lesestatusdaten

Sie speichert weiterhin `notifications_items` und entscheidet, ob für ein Ereignis der Kanal `push` aktiv ist. Statt selbst über `web-push` zu senden, ruft sie den zentralen Push-Server auf.

### Zentraler Push-Server

Der zentrale Server ist zuständig für:

- öffentliche Push-Konfiguration für Clients
- Entgegennahme von Geräte-Registrierungen
- Mapping von lokalen Geräte-IDs auf Provider Tokens
- Zustellung an externe Push-Netze
- technische Fehlerbehandlung
- Missbrauchsschutz, Quotas und Sperren

Er kennt keine FEDEO-Berechtigungen. Wenn eine lokale Instanz einen gültig signierten Push-Auftrag sendet, wird dieser technisch zugestellt.

## Datenschutzmodell

Für die erste Ausbaustufe sollte der Push-Server Push-Inhalte nur transitär verarbeiten und nicht dauerhaft speichern. Dauerhaft gespeichert werden:

- Instanz-ID
- Geräte-ID
- Plattform: `web`, `ios`, `android`
- Provider-Endpunkt oder Provider-Token, verschlüsselt gespeichert
- Zeitpunkte: erstellt, zuletzt gesehen, deaktiviert
- Zustellstatus pro Auftrag: angenommen, gesendet, fehlgeschlagen
- technische Fehlercodes

Nicht dauerhaft gespeichert werden:

- Name, E-Mail oder lokale Nutzer-ID
- Mandantenname
- fachliche Objekt-IDs, sofern nicht zwingend notwendig
- vollständige Nachrichtentexte

Für Benachrichtigungen mit sensiblen Inhalten empfiehlt sich ein Payload-Modell mit generischem Text:

```json
{
  "title": "Neue FEDEO-Benachrichtigung",
  "body": "Öffne FEDEO, um die Details anzusehen.",
  "data": {
    "notificationId": "lokale-notification-id",
    "instanceBaseUrl": "https://app.example.com"
  }
}
```

Die lokale App lädt Details anschließend authentifiziert von der Selfhost-Instanz.

## Instanzregistrierung

Jede Selfhost-Instanz erhält beim Setup eine zentrale Instanzidentität.

### Ablauf

1. Betreiber öffnet in FEDEO den Bereich `Administration > Push Notifications`.
2. FEDEO erzeugt eine lokale Instanz-ID, falls noch keine existiert.
3. Betreiber meldet die Instanz am zentralen Push-Portal an oder nutzt einen geführten Aktivierungscode.
4. Der zentrale Server erzeugt einen Client-Schlüssel.
5. Die Selfhost-Instanz speichert `FEDEO_PUSH_INSTANCE_ID` und `FEDEO_PUSH_CLIENT_SECRET`.
6. Die Instanz ruft regelmäßig einen Healthcheck auf.

### Benötigte Umgebungsvariablen

```env
FEDEO_PUSH_MODE=central
FEDEO_PUSH_GATEWAY_URL=https://push.fedeo.cloud
FEDEO_PUSH_INSTANCE_ID=inst_...
FEDEO_PUSH_CLIENT_SECRET=...
FEDEO_PUSH_PAYLOAD_MODE=minimal
```

`FEDEO_PUSH_MODE=local` bleibt für Entwickler und Betreiber möglich, die eigene VAPID-Schlüssel nutzen möchten.

## Geräte-Registrierung

Die lokale FEDEO-Instanz sollte weiterhin der erste Ansprechpartner für den Client bleiben. Dadurch kann sie Authentifizierung und Mandantenzuordnung prüfen.

### Web Push

1. FEDEO Web fragt lokal `/api/notifications/push/config` ab.
2. Die lokale Instanz liefert bei `central` die öffentliche VAPID-Konfiguration des zentralen Servers.
3. Der Browser erzeugt eine Web-Push-Subscription.
4. FEDEO Web sendet die Subscription an die lokale Instanz.
5. Die lokale Instanz registriert die Subscription beim zentralen Push-Server.
6. Der zentrale Server gibt eine `centralDeviceId` zurück.
7. Die lokale Instanz speichert diese ID in `notification_push_subscriptions.meta`.

### Mobile Push

Für iOS und Android registriert die FEDEO-App das Gerät über die native Push-Schicht und sendet den Provider Token an die lokale Instanz. Die lokale Instanz leitet ihn an den zentralen Push-Server weiter. Der zentrale Server speichert den Token verschlüsselt und liefert eine `centralDeviceId` zurück.

## API-Entwurf

Alle Instanz-API-Aufrufe nutzen HTTPS und eine HMAC-Signatur oder kurze JWTs mit rotierbarem Secret.

### `GET /v1/public-config`

Liefert öffentliche Push-Parameter.

```json
{
  "webPushPublicKey": "...",
  "iosBundleId": "cloud.fedeo.app",
  "androidSenderId": "..."
}
```

### `POST /v1/instances/heartbeat`

Meldet Status und Version der Selfhost-Instanz.

```json
{
  "instanceId": "inst_123",
  "fedeoVersion": "2026.05.22",
  "baseUrl": "https://app.example.com",
  "capabilities": ["web_push", "minimal_payload"]
}
```

### `POST /v1/devices`

Registriert oder aktualisiert ein Gerät.

```json
{
  "localDeviceId": "sub_456",
  "platform": "web",
  "subscription": {
    "endpoint": "https://...",
    "keys": {
      "p256dh": "...",
      "auth": "..."
    }
  },
  "meta": {
    "browser": "Firefox",
    "os": "macOS"
  }
}
```

Antwort:

```json
{
  "centralDeviceId": "dev_789",
  "status": "active"
}
```

### `DELETE /v1/devices/{centralDeviceId}`

Deaktiviert ein Gerät.

### `POST /v1/push`

Nimmt einen Zustellauftrag entgegen.

```json
{
  "idempotencyKey": "notification-item-id:dev_789",
  "devices": ["dev_789"],
  "priority": "normal",
  "ttlSeconds": 3600,
  "collapseKey": "communication.message.new",
  "notification": {
    "title": "Neue FEDEO-Benachrichtigung",
    "body": "Öffne FEDEO, um die Details anzusehen."
  },
  "data": {
    "notificationId": "lokale-notification-id",
    "link": "/communication"
  }
}
```

Antwort:

```json
{
  "accepted": 1,
  "rejected": 0,
  "deliveryJobId": "job_abc"
}
```

### `GET /v1/push/{deliveryJobId}`

Liefert technische Zustellinformationen für Debugging und Support.

```json
{
  "status": "completed",
  "sent": 1,
  "failed": 0,
  "disabledDevices": []
}
```

## Lokale Backend-Anpassung

Der bestehende `NotificationService` kann um einen Provider ergänzt werden:

```text
NotificationService
  |
  +-- InAppDeliveryProvider
  +-- EmailDeliveryProvider
  +-- LocalWebPushDeliveryProvider
  +-- CentralPushDeliveryProvider
```

`CentralPushDeliveryProvider` nutzt die lokalen Einträge aus `notification_push_subscriptions`, sucht aktive Geräte mit `centralDeviceId` und sendet Zustellaufträge an den zentralen Push-Server. Ohne `centralDeviceId` versucht er eine Nachregistrierung oder markiert den lokalen Eintrag als fehlerhaft.

Für Kompatibilität sollte die lokale Tabelle erweitert oder die vorhandene `meta`-Spalte genutzt werden:

```json
{
  "centralDeviceId": "dev_789",
  "platform": "web",
  "registeredVia": "central",
  "lastGatewaySyncAt": "2026-05-22T10:00:00Z"
}
```

## Zustellverhalten

### Idempotenz

Jede lokale Instanz sendet pro Notification Item und Gerät einen stabilen `idempotencyKey`. Der zentrale Server verarbeitet denselben Key innerhalb eines konfigurierbaren Fensters nur einmal.

### Retry

Der zentrale Server unterscheidet:

- temporäre Fehler: Netzwerk, Provider-Timeout, Rate Limit
- dauerhafte Fehler: ungültiges Token, abgelaufene Web-Push-Subscription
- Konfigurationsfehler: gesperrte Instanz, ungültige Signatur, Quota überschritten

Temporäre Fehler werden mit exponentiellem Backoff wiederholt. Dauerhafte Gerätefehler deaktivieren das zentrale Gerät und werden an die lokale Instanz zurückgemeldet.

### Rückmeldung an lokale Instanzen

Für die erste Ausbaustufe reicht Pull-basiertes Debugging über `GET /v1/push/{deliveryJobId}`. Später kann ein Webhook ergänzt werden:

```text
POST <selfhost-base-url>/api/notifications/push/gateway-callback
```

Damit kann die lokale Instanz ungültige Geräte automatisch deaktivieren.

## Sicherheit

### Authentifizierung

Empfohlen ist ein HMAC-Schema:

```text
X-Fedeo-Instance-Id: inst_123
X-Fedeo-Timestamp: 2026-05-22T10:00:00Z
X-Fedeo-Signature: hmac-sha256(...)
```

Signiert werden Methode, Pfad, Timestamp, Body-Hash und Instanz-ID. Requests mit zu altem Timestamp werden abgelehnt.

### Secret-Rotation

Der zentrale Server sollte pro Instanz zwei aktive Secrets unterstützen:

- `currentSecret`
- `nextSecret`

Die Selfhost-Instanz kann dadurch ohne Ausfall rotieren. Nach erfolgreichem Wechsel wird das alte Secret deaktiviert.

### Missbrauchsschutz

- Rate Limit pro Instanz
- Tageskontingent pro Instanz
- Maximalgröße für Payloads
- Blocklist für kompromittierte Instanzen
- Audit-Log für Admin-Aktionen
- strikte Validierung von Links, damit Pushes nicht zu fremden Domains leiten

## Betriebsmodell

### Deployment

Der zentrale Push-Server sollte als eigener Dienst betrieben werden:

- `push-api`: HTTP API für Instanzen und Admin-Konsole
- `push-worker`: Queue-Verarbeitung und Provider-Zustellung
- `postgres`: Instanzen, Geräte, Jobs, Audit
- `redis`: Queue, Rate Limits, kurzlebige Idempotenzdaten
- `traefik` oder anderer Reverse Proxy mit TLS

### Monitoring

Wichtige Metriken:

- angenommene Push-Aufträge pro Instanz
- Zustellquote pro Plattform
- Provider-Latenz
- Fehlerrate nach Fehlerklasse
- deaktivierte Geräte
- Queue-Länge und Retry-Alter
- Signaturfehler und Rate-Limit-Treffer

### Verfügbarkeit

Wenn der zentrale Push-Server nicht erreichbar ist, markiert die lokale Instanz Push-Zustellungen als temporär fehlgeschlagen und versucht sie später erneut. In-App-Benachrichtigungen bleiben davon unberührt.

## Migrationspfad

### Phase 1: Web Push Gateway

- zentrale VAPID-Schlüssel bereitstellen
- Selfhost-Instanzen gegen Push-Server authentifizieren
- Web-Push-Subscriptions zentral registrieren
- bestehendes lokales Web-Push-Sending optional durch Gateway ersetzen
- minimale Payloads verwenden

### Phase 2: Mobile Push

- iOS und Android Tokens registrieren
- APNs und FCM im zentralen Worker anbinden
- Deep Links zur passenden Selfhost-Instanz öffnen
- Token-Rotation und App-Neuinstallation robust behandeln

### Phase 3: Rückmeldungen und Admin

- Zustellcallbacks an Selfhost-Instanzen
- Admin-Konsole für Instanzen, Sperren, Quotas und Fehleranalyse
- Secret-Rotation per Oberfläche
- Export technischer Logs für Supportfälle

### Phase 4: Erweiterte Kanäle

- mandantenweite Push-Richtlinien
- gebündelte Pushes
- stille Pushes für Synchronisation
- optional Webhook-Zustellung für Integrationen

## Offene Entscheidungen

- Sollen Push-Inhalte standardmäßig immer minimal sein oder darf eine Instanz explizit lesbare Titel und Texte erlauben?
- Wird die Instanzregistrierung über ein zentrales FEDEO-Konto oder über offline erzeugte Aktivierungscodes erfolgen?
- Soll der zentrale Push-Server Bestandteil eines größeren FEDEO-Cloud-Portals werden?
- Wie lange sollen technische Zustelllogs maximal aufbewahrt werden?
- Welche Quotas gelten für kostenlose, lizenzierte und interne Instanzen?

## Empfehlung für den ersten Schnitt

Für den ersten produktionsnahen Stand sollte FEDEO nur Web Push über den zentralen Server abwickeln. Das passt direkt zum vorhandenen Benachrichtigungssystem und reduziert den Betreiberaufwand für Selfhost-Instanzen deutlich. Die lokale Instanz bleibt fachlich führend, der zentrale Server sieht nur technische Geräte und minimale Zustellaufträge.

Die wichtigste Architekturentscheidung ist dabei, den zentralen Server nicht als Benachrichtigungslogik zu bauen, sondern als schlanken Push-Transport. Dadurch bleibt Selfhosting souverän, während FEDEO trotzdem die komplizierten Provider-Schlüssel und mobilen Push-Integrationen zentral beherrschen kann.
