172 lines
9.5 KiB
Markdown
172 lines
9.5 KiB
Markdown
# Betrieb, Provider und Zustellgarantien
|
|
|
|
## Start und Upgrade
|
|
|
|
```sh
|
|
go run ./cmd/gateway -config ./data/config.json
|
|
```
|
|
|
|
Die Konfiguration bleibt JSON-Schema 1. Der Server legt `outbox.db` neben der
|
|
Konfiguration an. Ein anderer Pfad lässt sich mit `-outbox /pfad/outbox.db` setzen.
|
|
SQLite arbeitet ohne CGO. Das Verzeichnis muss schreibbar sein; dort entstehen
|
|
auch `outbox.db-wal` und `outbox.db-shm`.
|
|
|
|
**Verhaltensänderung:** HTTP-Eingänge antworten nach persistenter Annahme mit
|
|
**202**, bevor ein Ziel kontaktiert wird:
|
|
|
|
```json
|
|
{"ok":true,"receipt":{"id":"...","duplicate":false}}
|
|
```
|
|
|
|
Die Zustellhistorie zeigt den späteren Status. Speicherfehler führen zu 503,
|
|
ungültige Nachrichten/fehlende Regeln zu 400. Vorschauen versenden und speichern
|
|
keine Zustellungen. Verarbeitete Dry-Run-Aufträge erscheinen als `dry_run`.
|
|
|
|
## Deduplizierung und Retry
|
|
|
|
HTTP-Sender sollten pro logischer Nachricht einen stabilen Schlüssel setzen:
|
|
|
|
```sh
|
|
curl -X POST http://localhost:8080/in/webhook/default \
|
|
-H 'Authorization: Bearer replace-me' \
|
|
-H 'Idempotency-Key: monitoring-event-123' \
|
|
-H 'Content-Type: application/json' \
|
|
-d '{"title":"Server","message":"Dienst ausgefallen"}'
|
|
```
|
|
|
|
- Der Schlüssel gilt für Quelle und Kanal, maximal 256 Bytes. Derselbe Schlüssel
|
|
und Inhalt liefern dieselbe Referenz mit `duplicate:true`. Anderer Inhalt mit
|
|
demselben Schlüssel ergibt 409. Empfangszeitpunkte zählen nicht zum Inhalt.
|
|
- Ohne Schlüssel ist jede HTTP-Anfrage eine neue Nachricht. Gleicher Text allein
|
|
löst keine Deduplizierung aus: gleiche Meldungen können neue Ereignisse sein.
|
|
- Alle passenden Regeln werden in einer Transaktion angenommen. Wenn eine kein
|
|
gültiges Payload erzeugt, wird keine Teilmenge gespeichert.
|
|
- Jeder Ausgang hat einen eigenen Auftrag. Erfolgreiche Aufträge werden nicht
|
|
wegen des Fehlers eines anderen Ziels wiederholt.
|
|
- HTTP-Verbindungsfehler, 408/425/429/5xx sowie temporäre SMTP-4xx-Fehler werden
|
|
wiederholt: maximal acht Versuche, zunächst etwa fünf Sekunden Wartezeit,
|
|
danach exponentiell mit Zufallsanteil. `Retry-After` wird bis 24 Stunden beachtet.
|
|
- Nach dauerhaften Fehlern oder acht Fehlversuchen bleibt der Auftrag `dead`.
|
|
**Erneut versuchen** startet einen neuen Versuchslauf desselben Auftrags mit
|
|
ursprünglichem Ziel/Inhalt. Die vorherigen Versuche bleiben in der Historie.
|
|
- Nach einem Absturz wird ein verwaister Versand nach Ablauf seiner
|
|
zehnminütigen Reservierung wieder verfügbar. Regulärer Shutdown beendet
|
|
laufende Netzwerkaufrufe und speichert deren Ergebnisse.
|
|
|
|
HTTP-Ausgänge senden `Idempotency-Key` und `X-Notify-Gateway-Delivery` mit der
|
|
stabilen Delivery-ID. SMTP verwendet eine stabile `Message-ID`. Fremde Provider
|
|
müssen diese Merkmale selbst zur Deduplizierung unterstützen. Ein Absturz nach
|
|
erfolgreichem Versand, aber vor dem lokalen Erfolgs-Commit, kann daher eine
|
|
Doppelzustellung verursachen. Es gibt keine Exactly-once-Garantie.
|
|
|
|
Ein Worker arbeitet sequenziell. Ein langsames Ziel verzögert andere Aufträge;
|
|
Provider-Timeouts begrenzen die Dauer. Durchsatztests und konfigurierbare
|
|
Parallelität für größere Installationen stehen noch aus.
|
|
|
|
## Ausgangsprovider
|
|
|
|
Unter **Ausgänge** Verbindungen anlegen und unter **Zuordnungen** auswählen.
|
|
Neue Verbindungen verwenden `live:false`; Divera247 hat weiterhin `dry_run:true`.
|
|
Die Live-Einstellung wird beim Annehmen gespeichert. Das spätere Abschalten
|
|
eines Ausgangs hält bereits angenommene Live-Aufträge nicht an.
|
|
|
|
| Provider | Einstellungen |
|
|
|---|---|
|
|
| Discord | `https://discord.com/api/webhooks/ID/TOKEN`; Titel und Meldung, keine automatischen Mentions, maximal 2000 UTF-16-Codeeinheiten |
|
|
| HTTP-Webhook | Vollständige HTTP(S)-URL, optionaler Bearer-Token; POST mit gemappten Nachrichtenfeldern |
|
|
| ntfy | Server-Basis-URL, Topic, optionaler Bearer-Token; JSON-Publish, positive Priorität auf maximal 5 begrenzt |
|
|
| Gotify | Vollständiger `/message`-Endpunkt und App-Token; `X-Gotify-Key`, Titel/Meldung/Priorität |
|
|
| SMTP | Host, Port, `tls` (typisch 465) oder `starttls` (typisch 587), optional Benutzer/Passwort, reine Absender-/Empfängeradressen |
|
|
|
|
SMTP verlangt eine gültige Zertifikatskette. `AUTH PLAIN` wird ausschließlich
|
|
nach TLS verwendet. Mail-Inhalte sind UTF-8-Plain-Text als Base64-MIME; keine Anhänge.
|
|
|
|
Quellen: [ntfy JSON-Publish](https://docs.ntfy.sh/publish/#publish-as-json),
|
|
[Gotify Nachrichten](https://gotify.net/docs/pushmsg),
|
|
[Discord Webhooks](https://docs.discord.com/developers/resources/webhook#execute-webhook).
|
|
|
|
## IMAP-Eingang
|
|
|
|
Unter **Eingänge** einen Mail-Eingang einrichten: eindeutige ID, TLS-Adresse
|
|
wie `imap.example.org:993`, Benutzer, Passwort, Ordner und Zuordnungskanal.
|
|
Regeln verwenden `source: "mail"` und diesen Kanal.
|
|
|
|
- Passwort- oder App-Passwort-Anmeldung; OAuth2 ist noch nicht implementiert.
|
|
- Der Ordner wird schreibgeschützt geöffnet; Nachrichten werden weder gelöscht
|
|
noch als gelesen markiert. Checkpoints liegen in SQLite.
|
|
- Beim ersten erfolgreichen Abruf wird standardmäßig der aktuelle UID-Stand
|
|
übernommen. **Vorhandene Nachrichten importieren** muss vor diesem ersten
|
|
Abruf gesetzt sein, wenn alte Nachrichten verarbeitet werden sollen.
|
|
- UIDVALIDITY-Wechsel setzt den Cursor zurück. Der neue UID-Namensraum wird
|
|
erneut verarbeitet; alte Inhalte können dadurch erneut auftreten. Wechsel von
|
|
Adresse/Benutzer/Ordner/Eingangs-ID erzeugt einen neuen Checkpoint und wendet
|
|
die Erstimport-Einstellung erneut an.
|
|
- Ein Abruf verarbeitet bis zu 100 neue Nachrichten. Checkpoints werden erst
|
|
nach Outbox-Annahme oder bewusster Filterung fortgeschrieben.
|
|
- Maximal 1 MiB Rohmail und dekodierter Text. Plain-Text-MIME wird verwendet;
|
|
Anhänge werden ignoriert. HTML-only, fehlerhafte/zu große Nachrichten oder ein
|
|
fehlendes Mapping blockieren den Checkpoint. Die Zustellhistorie zeigt den
|
|
Abruffehler. Nachricht manuell verschieben oder eine passende Regel ergänzen.
|
|
- Absender-/Empfängerfilter vergleichen Adressen ohne Beachtung der
|
|
Großschreibung. Header beweisen keine Absenderidentität; diese Prüfung gehört
|
|
zusätzlich auf den empfangenden Mailserver.
|
|
- Eigene Gateway-Mails und `Auto-Submitted`-Nachrichten werden übersprungen,
|
|
um Rückkopplungen zu vermeiden. HTTP-Rückläufer mit Gateway-Delivery-Header
|
|
werden ebenfalls abgelehnt.
|
|
|
|
## Discord-Eingang
|
|
|
|
1. Discord-Anwendung anlegen, Public Key und Application-ID übernehmen.
|
|
2. Unter **Eingänge** Command-Name, erlaubte Guild-IDs und Channel-IDs setzen.
|
|
3. Slash-Command entsprechend [discord-command.example.json](discord-command.example.json)
|
|
bei der Anwendung registrieren; der Name muss zur Konfiguration passen.
|
|
4. Als Interactions Endpoint die öffentliche HTTPS-Adresse `/in/discord` setzen.
|
|
5. Regel mit `source: "discord"` und Channel-ID als Kanal erstellen.
|
|
|
|
PING und Commands werden mit Ed25519 geprüft; Zeitstempel dürfen maximal fünf
|
|
Minuten abweichen. Application-ID und beide Allowlists müssen passen.
|
|
`message` ist erforderlich, `title` und `priority` optional. Die private Antwort
|
|
bestätigt die Speicherung, nicht den Versand. Es erfolgt kein Mithören beliebiger
|
|
Channel-Nachrichten und keine automatische Registrierung externer Anwendungen.
|
|
Siehe [Discord Interactions](https://docs.discord.com/developers/interactions/receiving-and-responding).
|
|
|
|
## Historie, Metriken und Sicherheit
|
|
|
|
- **Zustellhistorie**: Seiten zu 50 Aufträgen, Status, Referenz, Fehlerklasse und
|
|
letzte 200 Versuchsereignisse pro Auftrag. Keine Bodies oder Zugangsdaten.
|
|
- `/healthz`: Prozess erreichbar. `/readyz`: SQLite erreichbar; keine Aussage zur
|
|
Erreichbarkeit aller Provider.
|
|
- `/metrics`: Prometheus-Gauge `notify_gateway_deliveries{state="..."}`.
|
|
Zugriff mit `Authorization: Bearer ...`, wenn `GATEWAY_METRICS_TOKEN` gesetzt ist;
|
|
sonst nur mit Admin-Session.
|
|
- Schreibende Admin-APIs/Formulare benötigen einen Token aus `GET /ui/api/csrf`
|
|
(`X-CSRF-Token` bzw. `csrf_token`). Die WebUI erledigt das automatisch.
|
|
Zusätzlich werden fremde Origins abgelehnt.
|
|
- Neue Passwort-Hashes verwenden bcrypt. SHA-256-Hashes werden beim erfolgreichen
|
|
Login migriert; alte Passwörter über 72 Bytes bleiben bis zum Wechsel im
|
|
Legacy-Format. Neue Passwörter: 10 bis 72 Bytes. Wechsel widerruft alle Sessions.
|
|
- Login-Limit: zehn Versuche pro Minute und direkter Client-IP. Proxy-Header
|
|
werden nicht vertraut; hinter einem Proxy eigenes Rate-Limit verwenden und
|
|
den originalen Host erhalten.
|
|
- Request-Limits: 1 MiB für Eingänge, 2 MiB für Admin-Aufrufe. CSP erlaubt das
|
|
eingebettete UI-Skript per Hash; Admin-Antworten verwenden `no-store`.
|
|
- Bearer-Token, SMTP-/IMAP-Passwort und Divera-Accesskey akzeptieren `env:VARIABLE`.
|
|
Fehlende Variablen führen zu Fehlern. Outbox-Snapshots behalten die Referenz;
|
|
ein später geänderter Variablenwert wird beim Versand verwendet.
|
|
|
|
Konfiguration und Outbox enthalten sensible Daten; direkte Secrets und Inhalte
|
|
werden nicht verschlüsselt. POSIX-Dateimodus ist `0600`, unter Windows gelten
|
|
zusätzlich die geerbten ACLs. Verzeichniszugriff und Backups entsprechend schützen.
|
|
HTTPS über einen Reverse Proxy bleibt erforderlich.
|
|
|
|
## Backup und Aufbewahrung
|
|
|
|
Für ein konsistentes einfaches Backup Gateway regulär stoppen und das ganze
|
|
Datenverzeichnis einschließlich Konfiguration, Datenbank und gegebenenfalls
|
|
WAL/SHM-Dateien sichern. Nur die laufende `outbox.db` zu kopieren genügt nicht.
|
|
Wiederherstellung bei gestopptem Gateway, anschließend starten.
|
|
|
|
Historie und Deduplizierungsbelege werden unbegrenzt aufbewahrt; automatische
|
|
Bereinigung fehlt noch. Größe überwachen. Tabellen nicht manuell löschen:
|
|
Belege und Checkpoints sind für Wiederanlauf und Deduplizierung nötig.
|