Dokumentiere zentralen Push-Server
KI-AGENT: Entwirft den zentralen Push-Server für Selfhost-Instanzen, beschreibt API, Sicherheit, Datenschutz und Migrationspfad.
This commit is contained in:
@@ -6,3 +6,4 @@ Diese Dokumentation unterstützt dich bei der täglichen Nutzung von FEDEO.
|
||||
|
||||
- [Bedienung](./bedienung/README.md)
|
||||
- [Kommunikationslösung auf Basis des Matrix-Standards](./kommunikationslösung-matrix.md)
|
||||
- [Zentraler Push-Server für Selfhost-Instanzen](./zentraler-push-server.md)
|
||||
|
||||
429
docs/zentraler-push-server.md
Normal file
429
docs/zentraler-push-server.md
Normal file
@@ -0,0 +1,429 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user