Zum Artikel springen
HB CodeDocsApp herunterladen

HandbuchMitwirken

Server, Datenbankrollen und Betrieb

Der Konto- und Relay-Dienst, die PostgreSQL-Rolleninvariante, die Konfiguration, die ein Entwicklungsserver braucht, und was über HTTP-Health hinaus zu prüfen ist.

Gegen den Quellcode geprüft am 5. Oktober 2026

Auf dieser Seite

Der Server besitzt Konto- und Relay-Zuständigkeiten

Die Actix-Anwendung initialisiert beim Start gemeinsame Dienste: Datenbank-Pools, Kontoauthentifizierung, Verbindungsverwaltung sowie konfigurierte Anbieter- und Push-Integrationen. Der DeviceConnectionManager wird von den HTTP-Workern des Prozesses gemeinsam genutzt. Seine Live-Sockets, Verbindungsgenerationen und ausstehenden RPC-Einträge sind Prozesszustand. Auch der Relay-Session-Speicher hinter den Resume-Tokens liegt im Arbeitsspeicher, mit einer TTL von 24 Stunden und einem Aufräumlauf alle 5 Minuten. Jeder RPC und jeder Heartbeat-Frame erneuert einen Session-Eintrag. Redis ist für den gemeinsamen Anmeldezustand, die Ratenbegrenzung und Guthabenreservierungen für bezahlte Anfragen erforderlich, und der Prozess beendet sich beim Start ohne Redis. Redis macht ein Register von Live-Sockets nicht zwischen Serverprozessen übertragbar.

Beide Geräte und ihre ausstehende Anfrage müssen diesen Connection-Manager erreichen, deshalb läuft in Produktion ein Relay-Prozess pro Region mit explizitem Verbindungsabbau. Das Kapitel zum Geräte-Routing erklärt, warum eine Round-Robin-Replik das Routing zerstören würde.

PostgreSQL-Rollen sind eine Start-Invariante

Der Server beendet sich beim Start, wenn seine zwei Datenbank-Logins nicht getrennt und durch Zeilensicherheit begrenzt sind
Der Server beendet sich beim Start, wenn seine zwei Datenbank-Logins nicht getrennt und durch Zeilensicherheit begrenzt sindIn PostgreSQL besitzt die Migrationsrolle die Tabellen, und 27 davon müssen Zeilensicherheit aktiviert und erzwungen haben. Der Server verbindet sich über zwei Logins, das System-Login hbcode und das Tenant-Login authenticated. Beim Start prüft er, dass keines der Logins Superuser ist, Zeilensicherheit umgeht, Rollen erbt oder Tabellen besitzt, dass keines das andere ist oder Mitglied des anderen oder des Tabellenbesitzers, und dass das Tenant-Login ohne Benutzer-ID aus 16 Tenant-Tabellen keine Zeilen liest. Jeder Fehler beendet den Prozess, bevor er auf Anfragen lauscht. Jeder Serverprozess öffnet einen eigenen System-Pool mit 8 und einen Tenant-Pool mit 16 Verbindungen, die seine HTTP-Worker teilen.
PostgreSQL
Tabellenbesitzerplantocode_migrator · SUPERUSER
27 TabellenZeilensicherheit aktiviert und erzwungen
System-Loginhbcode
Tenant-Loginauthenticated
Mitglied des Besitzers
dasselbe Login oder Mitglied des anderen
liest ohne Benutzer-ID 0 Zeilen aus 16 Tabellen
beide: NOSUPERUSER · NOBYPASSRLS · NOINHERIT · besitzen keine Tabellen
Jeder Serverprozess
System-Pool 8
Tenant-Pool 16
für alle seine HTTP-Worker
Eine durchgestrichene Linie, eine Tabelle ohne FORCE oder eine sichtbare Zeile: create_dual_pools schlägt fehl, und der Prozess endet, bevor er auf Anfragen lauscht.
Die Produktion öffnet außerdem ACCOUNT_CONTROL_DATABASE_URL, mit eigener Least-Privilege-Prüfung, und METRICS_DATABASE_URL.
  • Laufzeit-LoginsDATABASE_SYSTEM_URL · DATABASE_USER_URL

    assert_runtime_boundary prüft die Principals, als die sich diese URLs verbinden, nicht die URLs. Schon vor dem Verbinden lehnt create_dual_pools zwei identische URLs ab.

  • Tenant-Kontextapp.current_user_id

    Mit set_config für eine Transaktion gesetzt, sodass eine Verbindung im Pool ihn am Ende der Transaktion vergisst. Zeilensicherheits-Policies, die für jedes Login geschrieben sind, entscheiden, was es anfassen darf.

  • PoolsDB_POOL_SYSTEM_MAX · DB_POOL_USER_MAX

    Einmal pro Serverprozess geöffnet und von allen seinen HTTP-Workern geteilt. Jeder weitere Prozess, auch die zweite Farbe während einer Blue-Green-Umschaltung, öffnet eigene.

create_dual_pools öffnet beide Pools und führt assert_runtime_boundary für die Principals aus, als die sie tatsächlich verbunden sind, sodass eine URL mit der falschen Rolle an denselben Prüfungen scheitert.

Standard­wertSystem-PoolTenant-Pool
Maximale Verbin­dungen816
Minimale Verbin­dungen24
application_namehbcode-systemhbcode-user
Statement-Timeout30 Sekunden8 Sekunden
Idle-in-Transaction-Timeout60 Sekunden10 Sekunden
Lock-Timeout5 Sekunden2 Sekunden

Beide Pools nutzen 2 Sekunden Acquire-Timeout, 60 Sekunden Leerlauf-Timeout und eine maximale Verbindungslebensdauer von 1.800 Sekunden. Jede Verbindung setzt die Zeitzone auf UTC. Ein fehlgeschlagener Verbindungsaufbau wird bis zu fünfmal versucht, und die Wartezeit wächst mit jedem Versuch um eine Sekunde. Jeder Serverprozess öffnet eigene Pools, die seine HTTP-Worker teilen. Weitere Prozesse, auch die zweite Farbe einer Blue-Green-Umschaltung, vervielfachen die Verbindungen und brauchen zuerst eine Prüfung der Datenbankkapazität.

Lokalen Server konfigurieren

  1. Kopieren Sie server/.env.example nach server/.env und konfigurieren Sie eine lokale Datenbank, eine Redis-URL, Einstellungen zur Verschlüsselung von JWT- und Refresh-Tokens sowie Auth0-Werte.
  2. Legen Sie getrennte Migrations- und Runtime-Principals gegen eine lokale Entwicklungsdatenbank an.
  3. Wenden Sie server/scripts/run-migrations.sh mit der benötigten Migrationsumgebung an. server/README.md beschreibt die Rollenbereitstellung und die Erhaltungsrichtlinie.
Aus dem Repository-Root lesen und ausführen
cp server/.env.example server/.env
# Configure the local services and database roles first.
pnpm server:check
pnpm dev:server

Der Migrations-Runner verarbeitet infrastructure/ansible/playbooks/plantocode/migration-execution-plan.yml, denselben geordneten Plan, den die Bereitstellung nutzt. Die Migrationsrolle muss ein Superuser sein, der keiner von postgres, hbcode oder authenticated ist und in DATABASE_RUNTIME_ROLES fehlt. Als execution once markierte Dateien werden per sha256 in plantocode_internal.migration_execution_ledger gesperrt, und der Runner bricht ab, wenn eine Laufzeitrolle dieses Ledger erreichen kann. Eine Prüfsummenabweichung, eine once-Datei ohne einzelne Transaktion oder eine ungeplante .sql-Datei bricht die Ausführung ab. PRESERVE_EXISTING_DATABASE_IDENTITIES=true wird nur für die expand-Phase akzeptiert. Ersetzen Sie den Runner nicht durch sqlx migrate run.

ZweckEinstellungen und wann sie erforderlich sind
Kern­diensteDATABASE_SYSTEM_URL, DATABASE_USER_URL, REDIS_URL. Zentrale Datenbank- und gemeinsam genutzte Anmeldedienste.
Konto-AnmeldungAUTH0_DOMAIN, AUTH0_API_AUDIENCE, AUTH0_ALLOWED_CLIENT_IDS. Die konfigurierte Vertrauensgrenze für Konto-Logins.
TokensJWT_SECRET, REFRESH_TOKEN_ENCRYPTION_KEY. Ausstellen von App-Anmeldedaten und Speichern von Refresh-Material.
AnbieterProvider-API-Schlüssel. Nur die entsprechenden konfigurierten Provider-Routen.
Integra­tionenStripe-, APNs-, FCM-, E-Mail- und weitere Integrationseinstellungen. Ihre jeweiligen Konto- oder Benachrichtigungsfunktionen.
Weitere Daten­bankenMETRICS_DATABASE_URL, ACCOUNT_CONTROL_DATABASE_URL. In der Entwicklung optional. Die Produktion startet ohne beide nicht, und jede muss auf eine eigene PostgreSQL-Datenbank zeigen.
Service-HealthSERVICE_HEALTH_REGION, SERVICE_HEALTH_RELEASE. Erforderlich, wenn METRICS_DATABASE_URL gesetzt ist. Die Region ist eu oder us. Das Release ist der SHA-256 der bereitgestellten Binärdatei als 64 Hex-Zeichen.
VorschauenWORKSPACE_PREVIEW_HMAC_SECRET. In der Entwicklung optional, in der Produktion erforderlich. Mindestens 32 Zeichen.
DeploymentPLANTOCODE_DEPLOYMENT_TOKEN. Der geschützte Endpunkt /health/deployment und seine Aktion /health/deployment/drain-device-links. Ohne das Token antworten beide mit 503. Ein falsches Token erhält 401.

Verhalten jenseits des HTTP-Health-Checks verifizieren

GET /health prüft den grundlegenden Serverzustand. Das geschützte /health/deployment unterstützt die Deployment-Inspektion. Verifizieren Sie anschließend einen Konto-Login, zwei Geräteregistrierungen, einen schreibgeschützten Desktop-RPC und eine kontrollierte Outbox-Operation. Ein fehlerfreier HTTP-Listener allein testet weder die Trennung von Datenbankrollen noch native Callbacks, Provider-Konfigurationen oder das Relay-Routing.

Eine nützliche Relay-Spur folgt der Anfrage-ID, dem Quell-Client, dem Ziel-Desktop, der Methode und der Verbindungsgeneration. Der Weiterleitungscode extrahiert den W3C-Trace-Kontext und injiziert den Kindkontext in den nächsten Hop. Bei mehrdeutiger Zustellung prüfen Sie den remote_rpc_idempotency-Anspruch des Desktops und seine chat_operation_ledger-Zeilen, bevor Sie schließen, ob Arbeit gelaufen ist.

Ein Release wird auf dem US-Canary fertig, bevor es in der EU beginnt. Die zwei Regionen laufen daher eine Zeit lang mit verschiedenen Releases
Ein Release wird auf dem US-Canary fertig, bevor es in der EU beginnt. Die zwei Regionen laufen daher eine Zeit lang mit verschiedenen ReleasesZwei Host-Spuren auf einer nicht maßstäblichen Zeitachse. Die Spur interserver-us durchläuft fünf Schritte in fester Reihenfolge: das Datenbank-Backup, api-expand, api-binary-deploy, die Soak-Phase und api-migrate. Danach bedient sie das neue Release. Während all dem bedient die Spur hetzner-primary das vorherige Release. Ein Betreiber prüft das Canary-Ergebnis. Erst dann durchläuft hetzner-primary dieselben fünf Schritte in derselben Reihenfolge.
interserver-usUSA · Canary
hetzner-primaryEU
die Regionen laufen mit verschiedenen Releases
Backup
Expand
Binary
Soak
Migrate
bedient das neue Release
bedient das vorherige Release
Canary-Ergebnis geprüft
dieselben fünf Schritte in derselben Reihenfolge
nicht maßstäblich
Es gibt kein allgemeines Rollback. Ein Zurück nach api-migrate braucht ein Wartungsfenster, das passende Backup aus dem ersten Schritt und das archivierte Release, das dazu passt.
  • Backup/usr/local/bin/backup_postgresql.sh

    Läuft auf jedem Host zuerst. Seine geprüfte Ausgabe ist der einzige Weg zurück nach einer Vorwärtsmigration.

  • Expandsite-app.yml --tags api-expand

    Wendet den abwärtskompatiblen Teil des Migrationsplans an. Die Binärdatei, die noch läuft, arbeitet auf dem erweiterten Schema weiter.

  • Binärdatei und Soak-Phasesite-app.yml --tags api-binary-deploy

    Aktiviert die neue Binärdatei mit den Blue-Green-Diensten. Der Betreiber prüft Health und Logs und wartet das Ende der Soak-Phase ab.

  • Migratesite-app.yml --tags api-migrate

    Schreibt beide Release-Farben fest und wendet den Contract-Teil des Plans an. Nach diesem Schritt gibt es keine allgemeine Rollback-Aktion.

  • Ein Host pro Lauf--limit interserver-us · --limit hetzner-primary

    Jede Aktion, die die Produktion ändert, zielt auf genau einen Host im Inventar. Die EU-Abfolge beginnt erst, wenn das Canary-Ergebnis geprüft ist.

Die Produktion hat zwei Hosts im Ansible-Inventar: interserver-us in den USA und hetzner-primary in der EU. Jeder betreibt den Server als zwei Dienste, plantocode-blue auf Port 8080 und plantocode-green auf Port 8081. Jede Aktion, die die Produktion ändert, läuft über site-app.yml mit genau einem Host in --limit. infrastructure/ansible/operations.md ist das Runbook. Inventar und Geheimnisse sind Betreiberkonfiguration.

Schritt auf einem HostAktion und Ergebnis
Backupbackup_postgresql.sh in /usr/local/bin. Ein geprüftes Datenbank-Backup, das der Betreiber aufbewahrt.
Expand--tags api-expand. Die abwärtskompatible Phase des Migrationsplans ist angewendet und bestätigt.
Binary--tags api-binary-deploy. Die neue Binärdatei ist aktiv. Der Betreiber prüft Health und Logs und schließt die Soak-Phase ab.
Migrate--tags api-migrate. Beide Release-Farben sind festgeschrieben, und die Contract-Phase ist angewendet.

Das Runbook verbietet drei Abkürzungen. Kopieren Sie nie eine Release-SQL-Datei auf einen Server, um psql von Hand auszuführen. Führen Sie nie eine Anwendungs- oder Datenbankänderung ohne genau ein --limit aus. FLUSHALL in Redis ist keine routinemäßige Wiederherstellung und braucht die ausdrückliche Freigabe des Betreibers.