Files
2026-09-16 06:26:16 +02:00

9.5 KiB

Betrieb, Provider und Zustellgarantien

Start und Upgrade

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:

{"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:

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, Gotify Nachrichten, Discord Webhooks.

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 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.

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.