# 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.sh` → `cd /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-draft` → `mv .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 build` → `pnpm --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=` → 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=` → 200 - [ ] `POST /api/cron/lyra-post?secret=` → 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//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)