11 KiB
Normale Rhein-Main-Staging-Daten für demo-pro-crm
Der Seed baut den Staging-Tenant als synthetischen SHK-Betrieb mit 50 Mitarbeitern
(17 im Büro, 33 im Baustellen-/Kundendienst), 783 Kontakten, 537 Projekten und den
dazugehörigen operativen Datensätzen auf. Die 33 Feldmitarbeiter arbeiten in 15
Zweier-Kolonnen und einer Dreier-Kolonne. Das Büro besteht aus Geschäftsführung
(1), Vertriebsleitung (1), Vertrieb (3), Backoffice (4), Marketing (1) und
Projekt-/Bauleitung (7). Der Seed ist bewusst nicht an SEED_ON_BOOT gekoppelt.
Die fachlichen Datensätze werden dabei wie normale Betriebsdaten gespeichert
(isDemoData=false). Der technische Slug bindet den Reset an den richtigen Tenant;
deterministische IDs und interne Import-Metadaten halten die Herkunft nachvollziehbar.
Kontakte, Projekte, Aufgaben und Dateien müssen deshalb ohne demo=show oder einen
Demo-Schalter sichtbar sein.
Der Staging-Deploy setzt auf dem Web-Resource zusätzlich
SEED_DEMO_PRO_MANAGED=true. Dadurch überspringt der bestehende Boot-Demo-Seed
demo-pro-crm vollständig: Er verändert dort weder Mitgliedschaften noch Plan,
ProductCore, Buchungen, Projekte oder Automationen. Starter und Business bleiben im
bisherigen Boot-Seed. Das Flag darf nicht auf Production gesetzt werden und muss auf
Staging aktiv bleiben, damit spätere Deploys den hier verifizierten Pro-Datensatz nicht
wieder mit Legacy-Fixtures vermischen.
Bedienung
pnpm seed:demo-pro --dry-run
SENTRY_ENVIRONMENT=staging \
SEED_DEMO_PRO_ALLOWED=true \
SEED_DEMO_PRO_EXPECTED_TENANT_ID='<exakte-staging-tenant-id>' \
SEED_DEMO_PRO_EXPECTED_DATABASE='<exakter-staging-datenbankname>' \
SEED_DEMO_PRO_EXPECTED_DATABASE_HOST='<exakter-host:port>' \
SEED_DEMO_PRO_EXPECTED_STORAGE_BUCKET='hivegrid-staging-media' \
SEED_DEMO_PRO_EXPECTED_STORAGE_ENDPOINT='<exakter-staging-s3-endpoint>' \
SEED_DEMO_PRO_WORKERS_DRAINED=true \
SEED_DEMO_PRO_OUTBOUND_SANDBOX=true \
SEED_DEMO_USER_PASSWORD='<starkes-zufaelliges-secret-aus-dem-secret-store>' \
pnpm seed:staging-data --apply --confirm=demo-pro-crm
SENTRY_ENVIRONMENT=staging pnpm seed:demo-pro --verify
SENTRY_ENVIRONMENT=staging \
SEED_DEMO_PRO_ALLOWED=true \
SEED_DEMO_PRO_EXPECTED_TENANT_ID='<exakte-staging-tenant-id>' \
SEED_DEMO_PRO_EXPECTED_DATABASE='<exakter-staging-datenbankname>' \
SEED_DEMO_PRO_EXPECTED_DATABASE_HOST='<exakter-host:port>' \
SEED_DEMO_PRO_EXPECTED_STORAGE_BUCKET='hivegrid-staging-media' \
SEED_DEMO_PRO_EXPECTED_STORAGE_ENDPOINT='<exakter-staging-s3-endpoint>' \
SEED_DEMO_PRO_WORKERS_DRAINED=true \
SEED_DEMO_PRO_OUTBOUND_SANDBOX=true \
pnpm seed:staging-data --cleanup --confirm=demo-pro-crm
apply und cleanup verweigern jeden anderen Tenant sowie jede Umgebung außer
Staging. Zusätzlich müssen Tenant-ID, Datenbankname, Datenbank-Host mit Port,
S3-Provider, Bucket und Endpoint mit den im Staging-Secret-Store hinterlegten
Bindings exakt übereinstimmen. Lokaler Storage und fremde S3-Buckets werden vor
der ersten Mutation abgelehnt. Der
Apply-Lauf wartet standardmäßig bis zu 300 Sekunden auf die
Search-Outbox. SEED_DEMO_PRO_WAIT_MS ist nur für lokale Integrationsprüfungen
gedacht; auf Staging bleibt der Standard bestehen.
Vor dem ersten Staging-Apply muss der neue Web-/Worker-Stand vollständig deployed und
gesund sein. Erst dieser Stand versteht die Search-Batch-Aktionen und hält den
Legacy-Boot-Seed über SEED_DEMO_PRO_MANAGED=true dauerhaft aus dem Pro-Tenant heraus.
Nach dem Outbox-Drain pollt der Lauf alle registrierten Meilisearch-Indizes mit
Tenant-Filter. Der Tenant wird erst aktiviert, wenn CRM (976), Projekte (537),
Aufgaben (1.684), Dateien (917) und Nutzer (50) vollständig sichtbar und alle
anderen Tenant-Slices leer sind. Für die vollständig über die Search-API abrufbaren
Slices bis 1.000 Dokumente wird zusätzlich die exakte ID-Menge geprüft. Der
Aufgaben-Slice überschreitet Meilisearchs standardmäßige maxTotalHits-Grenze und
wird deshalb fail-closed über die exakte Tenant-Facette und den vollständigen Count
verifiziert.
Damit sich die geseedeten Demo-Konten anschließend auch anmelden können, muss auf
der Staging-Web-Ressource STAGING_DEMO_LOGIN_ENABLED=true gesetzt sein (wirkt nur
zusammen mit SENTRY_ENVIRONMENT=staging). Der Login-Schalter ist bewusst vom
Seeding getrennt und trägt kein SEED_-Präfix: Der RLS-Deploy-Vertrag weist jeden
SEED_*-Schlüssel auf Runtime-Ressourcen ab, weshalb der Login sonst nicht
freischaltbar wäre.
Das gemeinsame Staging-Login-Passwort wird ausschließlich zur Laufzeit aus dem
Secret-Store gelesen, muss mindestens 20 Zeichen lang und ausreichend zufällig
sein und darf weder in Git noch in Deployment-Logs landen. Nur für den vom Owner
explizit verlangten gemeinsamen Staging-Zugang akzeptiert der CLI exakt den
vereinbarten Testwert, wenn zusätzlich
SEED_DEMO_PRO_ALLOW_SHARED_STAGING_PASSWORD=true gesetzt ist. Das Flag gehört
nicht in die Coolify-Runtime-Umgebung und erlaubt keinen anderen schwachen Wert.
Der Seed nutzt in beiden Fällen denselben Argon2id-Hasher wie die App.
Vor dem Setzen von SEED_DEMO_PRO_WORKERS_DRAINED=true müssen Outbound- und
Automations-Worker in Coolify pausiert und ihre aktiven Queue-Jobs vollständig
abgearbeitet sein. SEED_DEMO_PRO_OUTBOUND_SANDBOX=true ist eine operative
Nichtzustellbarkeits-Bestätigung und darf nur in einer der folgenden Lagen gesetzt
werden:
- Mail, SMS, Webhooks und Push zeigen nachweislich auf nicht zustellbare Test-Sinks.
- Fail-closed Isolation: Alle BullMQ-Queues außer
outbox.search-indexsind global pausiert, für jede pausierte Queue istactive=0bestätigt und die Coolify-Appsworker-mail,worker-import,worker-webhookundworker-pluginsind gestoppt.worker-generalbleibt ausschließlich für DB-Outbox und Search-Acks aktiv; seine übrigen Queues bleiben ebenfalls global pausiert.
Die Isolation muss vor dem Apply erneut geprüft und bis zum erfolgreichen
--verify gehalten werden. Der DB-Outbox-Reset kann bereits an BullMQ übergebene
DONE-Jobs nicht zurückholen. Danach werden zuerst die pausierten Queues fortgesetzt
und anschließend die vier Worker-Apps wieder gestartet. Der Search-Pfad bleibt für
beide Search-Ack-Phasen durchgehend verfügbar.
Der Reset erhält Tenant, Subscription, Pro-Entitlements und globale Kataloge. Er ersetzt die fachlichen Projekt-, CRM-, Lead-, Aufgaben-, Datei-, Kalender-, Buchungs- und Search-Daten sowie die Tenant-Mitgliedschaften. Die 50 synthetischen Nutzer bleiben globale User-Zeilen und werden beim nächsten Lauf sicher aktualisiert. Die sieben SHK-Template-Rollen werden ebenfalls aus dem exakten Berechtigungskatalog neu aufgebaut, damit keine alte manuelle Zusatzberechtigung auf neue Testnutzer übergeht. Anschließend invalidiert der Lauf die Permission-Caches aller 50 Nutzer über den gemeinsamen Cache-Bus, bevor der Tenant wieder aktiviert werden kann.
Während apply und cleanup hält der Prozess eine DB-seitige Lease und setzt den
Tenant in Wartung. Ein Heartbeat erneuert die Lease während aller Seed-,
Outbox- und Search-Phasen; verliert der Prozess den Besitz, bricht er fail-closed
ab. Nur ein vollständig verifizierter Apply-Lauf aktiviert den Tenant wieder.
Nach Abbruch oder Fehler bleibt er absichtlich deaktiviert; zur Recovery denselben
Apply-Befehl erneut ausführen. cleanup hinterlässt den leeren Tenant ebenfalls
deaktiviert.
Datenschutz und Zustellsicherheit
- Sämtliche E-Mail-Adressen liegen unter
.example.test. - Sämtliche Telefonnummern liegen im offiziellen Frankfurter Drama-Number-Block
+49 69 90009 000–999. - Orte und PLZ sind real; Straßen und Hausnummern sind synthetisch.
- Die 917 Datei-Datensätze besitzen deterministisch erzeugte, minimale PDF-Objekte
unter
tenants/{tenantId}/files/{fileId}/original. Größe und SHA-256-Prüfsumme stammen aus den exakten Bytes. Da die PDFs ausschließlich im vertrauenswürdigen Seed-Prozess entstehen und nie Nutzereingaben oder Remote-Inhalte enthalten, werden sie alscleangespeichert; der AV-Gate für untrusted Uploads bleibt unverändert fail-closed. Vor jedem Reset löscht der Lauf den gesamten tenant-eigenenfiles/-Prefix, einschließlich verwaister Objekte aus älteren Katalogen oder abgebrochenen Läufen, in begrenzten Seiten von höchstens 1.000 Keys. Die Abschlussprüfung verlangt die exakte erwartete Key-Menge und lehnt auch zusätzliche Objekte im verwalteten Prefix ab. - Direkte Dokumentbezüge werden nur bei eindeutiger Fachsemantik gesetzt: 81
Rechnungen verweisen eins-zu-eins auf das gleichnamige Rechnungs-PDF und 13
Abnahmen auf das Abnahmeprotokoll ihres Projekts. Checklisten-Fotos bleiben leer,
weil der Katalog keine Bilddateien erzeugt. Plantafelzeilen erhalten nur dann
calendarEventId/synced, wenn Projekt, Mitarbeiter und Zeitfenster exakt mit einem Kalendereintrag übereinstimmen; die getrennten Besprechungstermine des aktuellen Katalogs werden nicht als Montageeinsatz ausgegeben und deshalb nicht künstlich verknüpft. - Der Seed erzeugt keine E-Mails, SMS, Webhooks oder Push-Nachrichten.
- Buchungsseiten bleiben unveröffentlicht, damit der öffentliche Staging-Tenant keine realen Terminanfragen oder Folge-E-Mails annimmt.
Markt- und Geodaten
Die Verteilungen basieren auf folgenden Quellen:
- ZVSHK Konjunktur Frühjahr 2026: Bestandsanteil und Auftragsreichweite.
- VDS Kosten eines neuen Bads: Preis- und Laufzeitkorridore.
- ifeu Wärmepumpenkosten: Wärmepumpen-Retrofit-Korridore.
- Bundesnetzagentur Drama Numbers: nicht zugeteilte Rufnummern.
- Geoportal Hessen, INSPIRE-Adressen: amtliche Straßennamen unter Datenlizenz Deutschland Zero 2.0.
- BfDI Kurzposition Testdaten: Vorrang synthetischer Testdaten.
Der Geodaten-Check läuft getrennt vom Seed:
pnpm seed:demo-pro:geodata
Am 13.07.2026 wurden 92.178 amtliche hessische Straßennamen geprüft. Die 60
generierten Straßenmuster hatten keine Kollision. Snapshot-Hash:
3678fccbbf62820aecc0a9c7c253e960123e2131a555aa7d4a3b0b2464a1fce3.
Da sich amtliche Daten ändern, ist der Check vor einem neuen Seed-Katalog erneut
auszuführen.
Verifikation
--verify prüft die vereinbarten Zielmengen einschließlich 17 Büro- und 33
Feldmitarbeitern, die exakte Identitäts-, Rollen- und Dateimatrix in PostgreSQL,
alle registrierten Meilisearch-Indizes sowie jedes
der 917 realen S3-Objekte bytegenau gegen Größe und SHA-256. Dazu gehören drei
Buchungsseiten, 35 Verfügbarkeitsregeln und 79 verknüpfte Buchungstermine. Ein erfolgreicher
Apply-Lauf erzeugt außerdem 4.164 permission-aware Search-Projektionen und die
entsprechenden, auf maximal 250 Dokumente gebündelten Outbox-Ereignisse für
Meilisearch. Dadurch entstehen statt tausender Einzeljobs nur wenige Dutzend
tenant-isolierte Batch-Jobs.
Zusätzlich schlägt die Verifikation fehl, sobald irgendein aktuelles tenant-bezogenes
Modell mit isDemoData noch markierte Datensätze enthält, der sichtbare Tenant-Name
nicht RheinMain Gebäudetechnik GmbH lautet oder die SHK-Rollen vom exakten
Berechtigungskatalog abweichen.