rebreak-monorepo/ops/CUTOVER_PLAN.md
chahinebrini d1b71e76b2 chore(cutover): prepare backend/-Layout for Hetzner-Pipeline-Cutover
Phase-1-Vorbereitung für den Rebreak-Cutover (apps/rebreak Nuxt → backend
standalone Nitro). Alle Änderungen sind lokal verifiziert (build = 9.66 MB
gzipped 3.08 MB, node .output/server/index.mjs startet ohne ERR_MODULE_NOT_FOUND
auf :3000). Kein Push, kein Server-Eingriff in dieser Session.

Inhalt:

- backend/nitro.config.ts: 8 zusätzliche runtimeConfig-Keys (cartesia*, eleven*,
  supabaseUrl/AnonKey/ServiceKey, public.supabase.{url,key}). Schließt den
  Auth-500-Cascade vom 2026-05-06 (server/utils/auth.ts:32 liest
  config.public.supabase ?? config.supabase — beide Pfade jetzt deklariert).

- .npmrc (NEU, root-level): node-linker=hoisted für Prisma 7 transitive
  @prisma/client-runtime-utils (siehe feedback_backend_runtime_config.md).

- backend/start-staging.sh: Pfad korrigiert von /srv/rebreak-monorepo/...
  → /srv/rebreak/backend/.output-staging/server/index.mjs. infisical run
  wrapper (kein NUXT_*-Mapping mehr — runtimeConfig liest process.env.X
  direkt). IMAP-Services entfernt (sind Mo's Scope, separat in ecosystem).

- scripts/deploy.sh (NEU): adaptiert von /srv/rebreak/scripts/deploy.sh
  für backend/-Layout. APP_DIR=backend, pnpm --filter rebreak-backend build,
  .output → .output-staging atomic-move bleibt erhalten, pm2 restart
  --update-env zieht neue Infisical-Secrets.

- scripts/deploy-webhook/server.mjs (NEU): 1:1-Kopie vom Server, damit
  ecosystem.config.js auf die Repo-Version zeigen kann.

- ecosystem.config.js (NEU, root-level): rebreak-staging zeigt auf
  backend/start-staging.sh, rebreak-webhook zeigt auf scripts/deploy-webhook.
  rebreak-prod + dns-* sind kommentiert (folgen in späterer Phase).

- ops/CUTOVER_PLAN.md: Plan-Doku vom 2026-05-06 (yesterday's work).

- .gitignore: .claude/ und xgit ergänzt (lokale Agent-State, nicht versioniert).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 00:35:50 +02:00

24 KiB

Rebreak Cutover-Plan: apps/rebreak/ (Nuxt) → backend/ (Standalone Nitro)

Verantwortlich: Backyard-Scope (Hetzner-Pipeline + Cutover-Architektur) Erstellt: 2026-05-06 Status: PLAN — nicht ausgeführt. Alle destruktiven Schritte sind ⚠️-markiert und bedürfen explizitem User-Approval. Server: ssh rebreak-server (49.13.55.22, rebreak-prod-01, CX23, 4 GB RAM) Repo: git@github.com:RaynisDev/rebreak.git


1. Status quo (Stand 2026-05-06, post-rollback)

1.1 Server /srv/rebreak/ (live, Backyard's Layout)

/srv/rebreak/
├── apps/
│   ├── rebreak/                       ← Nuxt-App (LIVE)
│   │   ├── nuxt.config.ts             ← runtimeConfig (vollständig)
│   │   ├── start-staging.sh           ← Infisical login + jq-eval + NUXT_*-Mapping
│   │   ├── server/api/...             ← API-Routes
│   │   ├── .output-staging/           ← gebauter Output (pm2 fährt das)
│   │   ├── imap-proxy/                ← Mo's Scope
│   │   └── imap-idle/                 ← Mo's Scope
│   └── rebreak-native/                ← React-Native-App
├── scripts/
│   ├── deploy.sh                      ← cd apps/rebreak + pnpm --filter @trucko/rebreak build
│   └── deploy-webhook/server.mjs      ← GitHub HMAC validator → spawnt deploy.sh
├── ecosystem.config.js                ← pm2 zeigt auf apps/rebreak/start-staging.sh
└── ops/nginx/                         ← Source-tracking only (nicht live)
  • Git-HEAD: 6eca0b5 (rolled back, Backyard's working state)
  • Branch: main
  • Remote-main: 6eca0b5 (force-pushed back to safe state)
  • pm2: rebreak-staging online, rebreak-imap-staging online, rebreak-idle-staging online, rebreak-webhook online
  • Smoke-Tests: https://staging.rebreak.org/ → 200, /api/auth/me → 401 (correct).

1.2 Mac ~/mono/rebreak-monorepo/ (Cutover-Target)

~/mono/rebreak-monorepo/
├── backend/                           ← Standalone Nitro (NEU)
│   ├── nitro.config.ts                ← runtimeConfig UNVOLLSTÄNDIG (siehe §3)
│   ├── start-staging.sh               ← infisical run wrapper, zeigt auf /srv/rebreak-monorepo/backend/.output/server/index.mjs
│   ├── package.json                   ← name: rebreak-backend
│   ├── prisma/                        ← Prisma 7
│   ├── server/api/...
│   └── tsconfig.json
├── apps/rebreak-native/
├── ops/nginx/                         ← gleiche Source-Tracking-Configs wie auf Server
├── pnpm-workspace.yaml                ← packages: apps/* + backend
├── package.json                       ← name: rebreak-monorepo
└── xgit
  • Git-HEAD: 5dfbe88 (mit cutover-llm-toggle-prep-Arbeit, llmProvider-Toggle-Code)
  • Branch: main (Mac), preserved as cutover-llm-toggle-prep auf Remote
  • Kein .npmrc — siehe §3 Risiko Prisma 7 transitive deps

1.3 Was kaputt war (Incident 2026-05-06)

User force-pushte aus dem neuen Mac-Repo (backend/-Layout) zu RaynisDev/rebreak.git → Server-Webhook triggerte deploy.shcd /srv/rebreak/apps/rebreak schlug fehl (Pfad existiert nicht im neuen Layout) bzw. anderer build-pfad lief → schließlich crashte Auth-Middleware mit HTTP 500: Cannot read properties of undefined (reading 'url'), weil backend/nitro.config.ts.runtimeConfig keine supabase-Section hat und die @nuxtjs/supabase-Module-Config (die das im alten Setup auto-injected) im Standalone-Nitro nicht mehr existiert. Cascade: alle authentifizierten Endpoints kaputt.

Rollback durchgeführt vor dieser Session: RaynisDev/rebreak.git main = 6eca0b5, Server-HEAD = 6eca0b5, pm2 läuft wieder, HTTP 200 + 401.


2. Pipeline-Diagramm

2.1 Pipeline AKTUELL (Backyard-Layout, läuft)

 Mac (trucko-monorepo)         GitHub                       Hetzner-Server (49.13.55.22)
 ────────────────────         ───────                       ─────────────────────────────
 ./xgit "msg"        ─push──▶ RaynisDev/rebreak.git ─hook─▶ rebreak-webhook (pm2, :9000)
                                                            │  HMAC-validate
                                                            ▼
                                                            scripts/deploy.sh
                                                            │  cd /srv/rebreak
                                                            │  git fetch && reset --hard origin/main
                                                            │  pnpm install --frozen-lockfile
                                                            │  cd apps/rebreak
                                                            │  pnpm --filter @trucko/rebreak build
                                                            │  cp -r .output .output-staging-new
                                                            │  rm -rf .output-staging
                                                            │  mv .output-staging-new .output-staging
                                                            ▼
                                                            pm2 restart rebreak-staging
                                                            │  → bash apps/rebreak/start-staging.sh
                                                            │     → infisical login (universal-auth)
                                                            │     → infisical secrets --env=staging --output=json
                                                            │     → eval/export VAR=val + NUXT_*-Mapping
                                                            │     → pm2 start imap-staging + idle-staging
                                                            │     → exec node apps/rebreak/.output-staging/server/index.mjs
                                                            ▼
                                                            nginx: staging.rebreak.org → 127.0.0.1:3016

2.2 Pipeline ZIEL (nach Cutover, neues Layout)

 Mac (rebreak-monorepo)        GitHub                       Hetzner-Server
 ────────────────────         ───────                       ──────────────
 ./xgit "msg"        ─push──▶ RaynisDev/rebreak.git ─hook─▶ rebreak-webhook
                                                            ▼
                                                            scripts/deploy.sh   ← MUSS GEÄNDERT WERDEN
                                                            │  cd /srv/rebreak
                                                            │  git fetch && reset --hard origin/main
                                                            │  pnpm install --frozen-lockfile
                                                            │  cd backend                                  ← NEU
                                                            │  pnpm --filter rebreak-backend build         ← NEU
                                                            │  cp -r .output .output-staging-new           ← NEU (relativ zu backend/)
                                                            │  rm -rf .output-staging
                                                            │  mv .output-staging-new .output-staging
                                                            ▼
                                                            pm2 restart rebreak-staging
                                                            │  → bash backend/start-staging.sh             ← NEU
                                                            │     → infisical login + run --env=staging
                                                            │     → exec node backend/.output-staging/server/index.mjs
                                                            ▼
                                                            nginx: staging.rebreak.org → 127.0.0.1:3016

Wichtigste Pfad-Änderungen:

Bereich Vorher (live) Nachher (Cutover)
App-Dir /srv/rebreak/apps/rebreak/ /srv/rebreak/backend/
Build-Filter pnpm --filter @trucko/rebreak build pnpm --filter rebreak-backend build
Build-Output apps/rebreak/.output-staging/server/index.mjs backend/.output-staging/server/index.mjs
Start-Script apps/rebreak/start-staging.sh backend/start-staging.sh
ecosystem.config.js zeigt auf apps/rebreak zeigt auf backend
start-staging.sh-Pattern jq+eval+NUXT_*-Mapping infisical run -- node …/.output/server/index.mjs (kein Mapping nötig — Nitro liest process.env.X direkt via runtimeConfig-Defaults)

Achtung: backend/start-staging.sh aktuell zeigt auf /srv/rebreak-monorepo/backend/... — falscher Pfad, muss /srv/rebreak/backend/... sein (siehe §4 Schritt 6).


3. Fehlende runtimeConfig-Keys

Quelle 1: /srv/rebreak/apps/rebreak/nuxt.config.ts (live, vollständig) Quelle 2: grep config.X in ~/mono/rebreak-monorepo/backend/server/ Ziel: backend/nitro.config.ts.runtimeConfig

3.1 Keys die im AKTUELLEN backend/nitro.config.ts schon existieren (16 total)

databaseUrl, adminSecret, openrouterApiKey, deepgramApiKey, googleApiKey, googleAiApiKey, azureTtsKey, azureTtsRegion, openaiApiKey, stripeSecretKey, stripeWebhookSecret, resendApiKey, encryptionKey, lyraBotUserId, rebreakBotUserId, groqApiKey, cronSecret + public.{stripePublishableKey, appUrl, apiBase}

3.2 Keys die FEHLEN (Cutover-Blocker)

Key Quelle (Mac backend code) Wofür Default
cartesiaApiKey server/api/coach/speak-cartesia.post.ts:22 Cartesia TTS API process.env.CARTESIA_API_KEY ?? ""
cartesiaVoiceId server/api/coach/speak-cartesia.post.ts:24 Cartesia voice override process.env.CARTESIA_VOICE_ID ?? ""
elevenlabsApiKey server/api/coach/speak-elevenlabs.post.ts:26,34 ElevenLabs TTS API process.env.ELEVENLABS_API_KEY ?? ""
elevenlabsVoiceId server/api/coach/speak-elevenlabs.post.ts:28 ElevenLabs voice ID process.env.ELEVENLABS_VOICE_ID ?? ""

Diese 4 Keys sind in der live nuxt.config.ts ebenfalls nicht deklariert — die TTS-Routes für Cartesia/ElevenLabs sind NEU in der backend/-Welt (existieren als untracked files: backend/server/api/coach/speak-cartesia.post.ts

  • speak-elevenlabs.post.ts). Cutover-Risiko: gering (alte Routes existierten gar nicht), aber für Vollständigkeit muss nitro.config.ts diese Keys deklarieren, sonst config.cartesiaApiKey === undefined zur Laufzeit.

3.3 Supabase-Keys (Hauptbruchstelle des Incidents)

⚠️ Im aktuellen backend/nitro.config.ts fehlen supabase.url / supabase.key / supabase.serviceKey komplett.

Im alten Nuxt-Layout setzt @nuxtjs/supabase automatisch config.public.supabase = { url, key } aus dem supabase: { ... }-Block. Im standalone-Nitro gibt es kein Modul → muss explizit deklariert werden.

Backend-Code-Aufrufer (siehe server/utils/auth.ts:30 etc.) lesen Supabase typischerweise via @supabase/supabase-js-Client, der direkt process.env.SUPABASE_URL / SUPABASE_SERVICE_ROLE_KEY liest. Trotzdem: für Konsistenz mit Frontend/native und falls irgendein File config.public.supabase.url liest, müssen wir public.supabase.{url, key} und top-level supabaseServiceKey ergänzen.

→ siehe backend/nitro.config.ts.cutover-draft für vollständige Liste.

3.4 Verifikation des Cutover-Drafts

Nach Cutover:

ssh rebreak-server "tr '\\0' '\\n' < /proc/$(ss -tlnp | grep :3016 | grep -oE 'pid=[0-9]+' | cut -d= -f2)/environ | grep -E 'API_KEY|SUPABASE|JWT|SECRET' | sort"

Liste muss alle erwarteten Werte enthalten. Wenn process.env.X leer ist, ist Infisical-Wrapper kaputt — siehe feedback_infisical_secrets.md Verify-Trick.


4. Cutover-Sequence (sequentiell abarbeiten, jeden Step bestätigen lassen)

Annahme: Mac-Repo ist auf einem Pre-Cutover-Branch (z.B. cutover-llm-toggle-prep), main lokal = 5dfbe88. Server main = 6eca0b5. Webhook ist online.

Phase A — Vorbereitung (nicht-destruktiv, lokal auf Mac)

Step 1. Aktiviere backend/nitro.config.ts.cutover-draft → nach Review der Datei: mv backend/nitro.config.ts.cutover-draft backend/nitro.config.ts.

Step 2. Aktiviere .npmrc.cutover-draftmv .npmrc.cutover-draft .npmrc (Repo-root-level, sorgt für node-linker=hoisted).

Step 3. Korrigiere Pfad in backend/start-staging.sh Zeile 31:

  • VORHER: /srv/rebreak-monorepo/backend/.output/server/index.mjs
  • NACHHER: /srv/rebreak/backend/.output-staging/server/index.mjs (analog zur prod-pattern: gebauten output nach .output-staging movt deploy.sh).

Step 4. Lokaler Build-Test: cd backend && pnpm install && pnpm build. Muss .output/server/index.mjs produzieren.

Step 5. Smoke-Test lokal: node backend/.output/server/index.mjs mit allen ENV-Vars manuell gesetzt → Health-Check (Port 3016).

Phase B — Server-Vorbereitung (read-only, kein Eingriff in laufendes System)

Step 6. SSH-Vergleich: ssh rebreak-server "ls /srv/rebreak/". Verifiziere dass backend/-Pfad NICHT existiert (sonst stale Files vom letzten Force-Push-Versuch).

Step 7. ⚠️ Falls stale backend/ existiert: ssh rebreak-server "ls -la /srv/rebreak/backend/ 2>&1" Output an User eskalieren. NICHT selbst löschen.

Step 8. Infisical-Secrets Audit: User soll bestätigen dass alle in §3.2 + §3.3 genannten Keys (CARTESIA_API_KEY, ELEVENLABS_API_KEY, SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY etc.) im Infisical-staging-Env existieren. NICHT selbst Infisical-CLI ausführen (Token-Scope-Risiko).

Phase C — Pipeline-Anpassung (auf Mac, dann xgit)

Step 9. Edit scripts/deploy.sh (Mac-Repo erstellen, Server hat noch alte Version):

  • APP_DIR="${REPO_ROOT}/apps/rebreak"APP_DIR="${REPO_ROOT}/backend"
  • pnpm --filter @trucko/rebreak buildpnpm --filter rebreak-backend build

Step 10. Edit ecosystem.config.js (falls im neuen Repo gemirrort wird — derzeit ist es nur auf Server):

  • script: \${APP_DIR}/start-staging.sh`` (APP_DIR jetzt = backend)
  • cwd: APP_DIR analog
  • Webhook-Eintrag script: \${REPO_ROOT}/scripts/deploy-webhook/server.mjs`` bleibt (existiert weiter unter scripts/).

Klärung an User: Liegt scripts/deploy.sh und ecosystem.config.js im neuen Mac-Repo? Falls nein, müssen sie aus /srv/rebreak/ rüberkopiert + angepasst werden. Wenn der Webhook auf dem Server diese Files NICHT aus dem Repo zieht (sondern lokal vorhält), dann werden Step 9+10 zu manuellen Server-Edits — siehe Step 14 ⚠️.

Phase D — Deploy (DESTRUKTIV)

Step 11. ⚠️ Sichere Backyard-State auf Server: ssh rebreak-server "cd /srv/rebreak && git tag -a backyard-pre-cutover -m 'Pre-cutover safe-state 6eca0b5' 6eca0b5" — User-OK einholen (read-only-tag, aber zur Sicherheit).

Step 12. ⚠️ Force-push neuen Layout-State: aus ~/mono/rebreak-monorepo mit ./xgit "cutover: backend/-Layout aktiviert".

  • Webhook-Triggert deploy.sh AUTOMATISCH.
  • Risiko: alte deploy.sh ist auf apps/rebreak/-Pfad fixiert, wird also cd: apps/rebreak: No such file werfen → build crash → kein restart, alter .output-staging läuft weiter.
  • Mitigation: Der CRASH ist gewollt — er verhindert dass kaputter Code gestartet wird. Server bleibt auf laufenden rebreak-staging (alter index.mjs) live.

Step 13. ⚠️ Manueller Server-Edit für scripts/deploy.sh UND ecosystem.config.js:

  • NICHT von dieser Agent-Session ausgeführt — User macht das selbst.
  • Alternativen je nach User-Wahl: (a) ssh + sed-Edit der Server-Files (b) deploy.sh wird Teil des Repos und der webhook-deploy-flow zieht es aus dem Repo (cleaner, aber selbst-referentielles Bootstrap-Problem: alter deploy.sh muss erst die neue deploy.sh ans richtige scripts/-Pfad kopieren).
  • Empfehlung: (a) für ersten Cutover, danach (b) als follow-up commit.

Step 14. ⚠️ Trigger Re-Deploy nach Server-deploy.sh-Edit: ssh rebreak-server "bash /srv/rebreak/scripts/deploy.sh" ODER neuer xgit-trivial-commit (z.B. README touch). User-Wahl.

Step 15. ⚠️ Manueller pm2-restart falls deploy.sh letzten restart noch nicht getriggert hat: ssh rebreak-server "pm2 restart rebreak-staging --update-env". Das --update-env ist wichtig damit neue Infisical-secrets gezogen werden.

Phase E — Verifikation (read-only)

Step 16. ssh rebreak-server "pm2 logs rebreak-staging --nostream --lines 50" — keine 500er, kein crash-loop.

Step 17. Endpoint-Tests (siehe §6 Test-Checklist).

Step 18. Wenn 30 Minuten stable: cleanup ssh rebreak-server "rm -rf /srv/rebreak/apps/rebreak/.output-staging /srv/rebreak/apps/rebreak/.output". An User eskalieren — rm -rf ist destruktiv.


5. Rollback-Plan (Cutover scheitert, <5 min)

5.1 Schneller Rollback (Webhook re-triggert alter HEAD)

# Aus Mac-Repo:
git checkout main
git reset --hard 6eca0b5    # Backyard's safe state
git push --force origin main  # ⚠️ destructiv, User-OK

# Dann auf Server:
ssh rebreak-server "cd /srv/rebreak && git reset --hard 6eca0b5"  # ⚠️
ssh rebreak-server "bash /srv/rebreak/scripts/deploy.sh"           # alter deploy.sh, alter Pfad
ssh rebreak-server "pm2 restart rebreak-staging --update-env"

5.2 Falls Server scripts/deploy.sh schon im Cutover modifiziert wurde

User muss manuell die alte Version aus git show 6eca0b5:scripts/deploy.sh > /srv/rebreak/scripts/deploy.sh schreiben (read-only-Mac, dann scp), bevor Re-Deploy klappt.

5.3 Falls .output-staging bereits gelöscht wurde

ssh rebreak-server "cd /srv/rebreak/apps/rebreak && pnpm --filter @trucko/rebreak build && cp -r .output .output-staging"

Vorher User-OK einholen (Build dauert 90-180s, zwischenzeitlich keine Auth möglich).

5.4 Smoke-Test nach Rollback

curl -s -o /dev/null -w '%{http_code}\n' https://staging.rebreak.org/
curl -s -o /dev/null -w '%{http_code}\n' https://staging.rebreak.org/api/auth/me
# Erwartung: 200 + 401

6. Test-Checklist nach Cutover

Pflicht (alle müssen pass):

  • GET https://staging.rebreak.org/ → 200
  • GET https://staging.rebreak.org/api/auth/me → 401 (unauthenticated, NICHT 500)
  • GET https://staging.rebreak.org/api/auth/me mit gültigem JWT → 200 + user-data
  • POST https://staging.rebreak.org/api/coach/sos-session mit JWT → 200 + sessionId
  • GET https://staging.rebreak.org/api/coach/sos-stream?session=<id> → SSE stream openend
  • POST /api/coach/speak-google mit JWT → audio/mpeg blob
  • POST /api/coach/speak-deepgram mit JWT → audio blob
  • POST /api/coach/speak-gemini mit JWT → audio blob
  • POST /api/coach/speak-cartesia mit JWT → audio blob (NEU)
  • POST /api/coach/speak-elevenlabs mit JWT → audio blob (NEU)
  • POST /api/coach/transcribe mit audio + JWT → text
  • GET /api/community/posts mit JWT → posts-array
  • GET /api/dns/profile mit JWT → profile-config (achtet auf config.public.appUrl.includes("staging"))
  • GET /api/lyra/welcome-back mit JWT → message
  • POST /api/stripe/checkout mit JWT → session-url

Best-effort (admin/cron):

  • GET /api/admin/stats?secret=<adminSecret> → 200
  • POST /api/cron/lyra-post?secret=<cronSecret> → 200/204

Process-Health:

  • pm2 logs rebreak-staging --nostream --lines 100 — keine 500er, keine crash-restarts
  • pm2 jlist | jq '.[] | select(.name=="rebreak-staging") | .pm2_env.restart_time' → 0 increments über 5 min
  • tr '\0' '\n' < /proc/<node-pid>/environ | grep -E 'API_KEY|SUPABASE' → alle keys present

iOS/Android-App-Test (User-Side):

  • App-Login → User-Profile lädt
  • SOS-Session öffnen → Lyra streamt + spricht (TtsProvider beide testen)
  • Community-Tab → posts laden
  • DNS-Profile-Sync läuft

7. Risiken + Open Questions

Risiken

  • R1 (high): scripts/deploy.sh und ecosystem.config.js sind auf dem Server, NICHT im Repo. Cutover-Bootstrap ist selbstreferentiell — entweder manueller Server-Edit (Step 13) ODER Repo-Migration der Files. Diese Sequenz hat ein narrow window in dem ein altes deploy.sh gegen neue Repo-Struktur baut → fail.
  • R2 (medium): node-linker=hoisted in .npmrc muss BEFORE first pnpm install aktiv sein, sonst landet @prisma/client-runtime-utils in einem nested node_modules/.pnpm/... und ESM-Loader findet's nicht. Step 2 muss vor Step 4 passieren. Strikte Order.
  • R3 (medium): start-staging.sh im neuen Backend nutzt infisical run -- node ... — beim alten gab es eval von secrets-JSON. Falls irgendeine ENV-Var-Mapping-Magie (NUXT_*-Prefix) gebraucht wird, muss sie nachgezogen werden. Audit nötig: existiert ein Code-Pfad der process.env.NUXT_X direkt liest und nicht via useRuntimeConfig?
  • R4 (low): PWA-Manifest + i18n-locales aus nuxt.config.ts haben kein Äquivalent in standalone Nitro. Falls App-Frontend (rebreak-native) auf SSR-i18n-Endpoints angewiesen ist → kaputt. Native-App-Test im Detail.
  • R5 (low): nitro.config.externals.inline: [/^(?!@supabase\/supabase-js)/] ist eine Anti-pattern-Regex (matched fast alles). Möglicherweise produziert sie unerwartet riesige .output-bundles. Code-Review im Cutover-PR sinnvoll.

Open Questions an User

  1. Sollen scripts/deploy.sh + ecosystem.config.js Teil des Cutover-Commits werden (im Repo unter scripts/ und ecosystem.config.js Root)? Das vermeidet manuelle Server-Edits und ist Backyard-konform. Bootstrap-Frage: einmaliger manueller git pull && cp scripts/deploy.sh /srv/rebreak/scripts/ first-time, danach automatisch.
  2. backend/start-staging.sh Pfad: soll auf /srv/rebreak/backend/.output-staging/server/index.mjs zeigen (analog Backyard's apps/rebreak-Pattern, mit deploy.sh-.output → .output-staging move) ODER direkt /srv/rebreak/backend/.output/server/index.mjs (ohne staging-suffix-Move)? Erste Variante = Konsistenz mit Backyard. Zweite = simpler Cutover.
  3. Webhook-Build-Filter: soll der Filter pnpm --filter rebreak-backend build den Frontend-Native-App-Build IGNORIEREN (bisher: nur Backend baut)? Die rebreak-native-App buildet via Capacitor/Metro, nicht via pnpm — also ist --filter rebreak-backend korrekt restriktiv. Bestätigen.
  4. Gibt es eine Test-Staging-Umgebung wo wir den Cutover proben können bevor wir die echte staging.rebreak.org nehmen? Andernfalls ist DiGA-User-Test-Window (TestFlight) während Cutover potentiell down.
  5. Cartesia/ElevenLabs-Secrets in Infisical: existieren CARTESIA_API_KEY und ELEVENLABS_API_KEY schon im staging-Env? Falls nein, sind die neuen TTS-Routes nach Cutover broken (404-degradation, nicht 500 — if (!key) return error).

8. Operations-Cheatsheet (post-Cutover)

# Logs live
ssh rebreak-server "pm2 logs rebreak-staging --lines 100"

# Manueller Re-Deploy (nur bei broken webhook)
ssh rebreak-server "bash /srv/rebreak/scripts/deploy.sh"

# pm2-status
ssh rebreak-server "pm2 list"

# Verify .output-staging is fresh
ssh rebreak-server "stat -c '%y' /srv/rebreak/backend/.output-staging/server/index.mjs"

# Verify config-key present in built bundle
ssh rebreak-server "grep -l 'cartesiaApiKey' /srv/rebreak/backend/.output-staging/server/chunks/*.mjs"

# Check ENV-Vars in running node-process
ssh rebreak-server "tr '\\0' '\\n' < /proc/\$(ss -tlnp | grep :3016 | grep -oE 'pid=[0-9]+' | cut -d= -f2)/environ | grep -E 'CARTESIA|ELEVENLABS|SUPABASE' | sort"

# nginx reload (NUR wenn nginx-conf changed; wir machen das nicht in diesem Cutover)
ssh rebreak-server "nginx -t && systemctl reload nginx"

KEIN-GO-Liste (gilt während Cutover und danach):

  • pm2 delete rebreak-staging (nur restart)
  • git reset --hard direkt auf Server (deploy.sh macht das offiziell)
  • rm -rf auf /srv/rebreak/... ohne explizites User-OK
  • DNS-Änderungen
  • Infisical-Token-Rotation
  • pnpm install parallel zum Webhook-Deploy (OOM auf 4 GB)