NotariumDokumentation
Dokumentationsversion: latest
DE

Produktion

Auf dieser Seite geht es darum, eine Instanz in den Produktivbetrieb zu bringen: wie du sie richtig hinter einen Reverse-Proxy stellst, was die Single-Instance-Invariante bedeutet und wie du Backups davon erstellst. Notarium wird als ein einzelner Container ausgerollt — die Produktionskonfiguration läuft auf die drei folgenden Punkte hinaus.

Reverse-Proxy und Forwarded-Header

Vor die Anwendung stellst du einen Reverse-Proxy (nginx, Caddy, Traefik) — er terminiert TLS und leitet an den Port von Notarium weiter. Die zentrale Anforderung: Der Proxy muss die Header mit der externen Adresse weiterreichen.

Der häufigste Deployment-Fehler

Der Reverse-Proxy muss X-Forwarded-Host senden (oder Host unverändert lassen) und X-Forwarded-Proto: https setzen. Andernfalls werden Cookie-authentifizierte Mutationen aus der Oberfläche als Cross-Origin abgelehnt — das Symptom lautet „Ich sehe alles, kann aber nichts speichern.“ Das Weiterleiten von X-Forwarded-Proto ist außerdem nötig, damit das Session-Cookie das Secure-Flag erhält.

Der Grund dafür: Die Origin-Prüfung bei Mutationen gleicht die Herkunft der Anfrage mit der Adresse ab, die der Browser sieht — und diese Adresse kommt in einem Forwarded-Header an. Agenten-Aufrufe über ein Bearer-PAT sind von der Prüfung ausgenommen (sie tragen kein Cookie — also gibt es auch keine CSRF-Angriffsfläche). Der Proxy seinerseits muss die Forwarded-Header mit seinen eigenen Werten überschreiben, statt die des Clients unbesehen durchzureichen.

Wenn du die OAuth-Autorisierung für Agenten aktivierst, setze hinter dem Proxy PUBLIC_BASE_URL (zum Beispiel https://notes.example.com) — eine stabile externe Adresse für die OAuth-Metadaten. Ohne sie wird die Adresse aus den Forwarded-Headern abgeleitet. Siehe Konfiguration.

Vertrauen in die Client-IP

Eine von der Adresse unabhängige Achse ist die echte IP des Clients. Auf sie werden zwei Limits angerechnet: Anmeldeversuche und die Zulassung neuer OAuth-Clients. Hinter einem Proxy treffen alle Anfragen von derselben Adresse ein; ohne ausdrückliche Konfiguration würden diese Limits also pro Proxy gezählt — das heißt für alle gemeinsam.

Der Regler dafür ist TRUST_PROXY: eine kommagetrennte Liste der IPs/CIDRs der unmittelbar vorgelagerten Proxys.

# .env — trage die Adresse deines eigenen Proxy-Containers oder Hosts ein
TRUST_PROXY=172.18.0.0/16

Der sichere Standard ist, die Variable gar nicht zu setzen: Dann hat X-Forwarded-For überhaupt keinen Einfluss auf die Limits, und kein Header kann eine fremde IP vortäuschen. Setze sie nur, wenn du die Adresse deines Proxys sicher kennst, und halte die Liste eng gefasst.

Trage hier nicht „alle“ ein

Boolesche Werte, Hop-Zähler, benannte Bereiche und Bereiche über alle Adressen (/0) werden beim Start abgelehnt. Allen Adressen zu vertrauen hieße: Jeder Client könnte sich per Header selbst eine IP zuweisen und am Login-Limit vorbeilaufen.

Auf das Weiterleiten von X-Forwarded-Host und X-Forwarded-Proto hat diese Einstellung keinen Einfluss — das sind unabhängige Achsen, und der Kontrakt aus dem vorigen Abschnitt bleibt unverändert.

Die Single-Instance-Invariante

Notarium ist für einen einzelnen Prozess gebaut. Zwei Teile des Authentifizierungszustands leben im Arbeitsspeicher des Prozesses:

  • Login-Rate-Limit — die Zähler für Anmeldeversuche;
  • die Registry der SSE-Sockets — der Mechanismus, über den ein Zugriffsentzug offene Live-Verbindungen sofort kappt.

Hinter einem Load Balancer mit mehreren Instanzen und ohne gemeinsamen Speicher brechen diese Mechanismen zusammen: Ein Angreifer vervielfacht das Limit über die Instanzen hinweg, und ein Zugriffsentzug auf einer Instanz schließt keine SSE-Verbindung, die auf einer anderen offen gehalten wird.

Mehrere Instanzen hinter einem Load Balancer

Beide Zustandsteile leben im Arbeitsspeicher des Prozesses, daher funktionieren diese Mechanismen hinter einem Load Balancer mit mehreren Instanzen und ohne gemeinsamen Speicher nicht. Das Verlagern der Metadaten-Datenbank nach Postgres gibt dir gemeinsamen Zustand, aber das allein reicht für horizontale Skalierung nicht aus. Bleib bei einer einzelnen Instanz.

Backups

Das kanonische Backup ist ein eingebauter Befehl des Images und kein Kopieren der Dateien von außen: notarium backup packt ein verifiziertes ZIP und gibt es als Stream aus, während der Dienst einfach weiterläuft.

docker compose exec -T notarium backup > notarium-$(date -u +%Y%m%dT%H%M%SZ).zip
docker compose exec -T notarium backup verify < notarium-20260731.zip

Die Verifizierung gehört in den regulären Job und ist keine einmalige Geste: Sie verändert nichts und findet Beschädigungen, bevor du das Archiv wirklich brauchst. Für den Job selbst reicht eine einfache Umleitung nicht — übernimm die sichere Veröffentlichung (temporäre Datei → Flush auf die Platte → atomarer Hardlink) aus dem Runbook: Backup und Wiederherstellung. Dort steht auch, wie du in ein frisches Volume wiederherstellst und wo der Befehl an seine Grenzen stößt.

Kopiere meta.db nicht im laufenden Betrieb

cp /data/meta.db oder das Kopieren des Volumes im laufenden Betrieb ist kein Backup: Die Metadaten-Datenbank arbeitet im WAL-Modus, committete Zeilen können noch in meta.db-wal liegen, und einzeln kopierte Dateien sind kein Abbild eines einzigen Zeitpunkts.

Was im Backup landet und warum:

WasRolle
/data/spacesDeine Markdown-Dateien — die Quelle der Wahrheit.
/data/meta.dbDie Metadaten-Datenbank (Verlauf, Benutzer, Zugriffe) — aus Dateien nicht wiederherstellbar.
/data/jobsArtefakte und Uploads aus Import- und Export-Jobs.
/data/engineDie abgeleiteten Indizes der Engine. Nicht im Archiv: Sie werden aus den Dateien neu aufgebaut.

Wenn die Metadaten-Datenbank nach Postgres verlagert wurde oder die Notizen außerhalb des Datenverzeichnisses liegen, bricht der eingebaute Befehl mit einem Fehler ab, statt ein unvollständiges Archiv zu erzeugen — sichere die Datenbank und die eingehängten Verzeichnisse dann mit den Standardwerkzeugen deines Anbieters. Was die Metadaten-Datenbank genau enthält, steht auf der Seite Datenbank.