Files
nexus/phases/deployment.md
T
reviewer 4ad0f9e493
CI - Build & Test / Backend (.NET) (push) Failing after 25s
CI - Build & Test / Frontend (Vue/TS) (push) Successful in 17s
CI - Build & Test / Security Check (push) Successful in 2s
refactor: SOLID architecture — backend service layer + frontend V2 components
## Backend — Service Layer & Repository Refactoring

### Neue Services (21 neue Dateien)

**Interfaces & Implementierungen:**
- `IOpenClawGatewayClient` — Interface für OpenClawGatewayClient (DIP-Fix: DashboardController hing an konkreter Klasse)
- `IAgentConfigService` / `AgentConfigService` — Agent-Config-File-I/O aus AgentsController extrahiert
- `IProjectService` / `ProjectService` — Projekt-CRUD + Activity-Logging (SRP)
- `ITaskService` / `TaskService` — Task-State-Machine, Approve/Reject, Dashboard-Operationen (eliminiert Duplikation zwischen TasksController und DashboardController)
- `IDashboardService` / `DashboardService` — Queue-Aggregation, Priority-Normalisierung, Gateway-Delegation
- `IOperationsService` / `OperationsService` — Metriken-Berechnung aus OperationsController
- `ITeamService` / `TeamService` — IDENTITY.md-Lesen aus TeamController
- `IMemoryService` / `MemoryService` — File-I/O aus MemoryController
- `IIncidentService` / `IncidentService` — File-Parsing (Regex-Source-Generatoren) aus IncidentsController
- `IDocService` / `DocService` — Directory-Scan aus DocsController
- `ICalendarService` / `CalendarService` — Gateway-HTTP-Calls + Fallback-Daten aus CalendarController

### Repository-Fixes

**IUserRepository / UserRepository:**
- `SaveChangesAsync` entfernt (leaky abstraction — Caller sollten nie SaveChanges steuern)
- `RevokeTokenAsync(tokenHash)` — atomares Token-Revoke inkl. SaveChanges
- `RevokeFamilyAsync(familyId)` — Batch-Revoke einer Token-Familie inkl. SaveChanges
- `RemoveExpiredTokensAsync` speichert jetzt selbst (war vorher dependent auf nachfolgenden Save)

### AuthService-Fixes
- `GetUserAsync`: unnötiges `Task.Run` entfernt → direkt `_users.GetByIdAsync().AsTask()`
- `RevokeAsync`: delegiert jetzt an `IUserRepository.RevokeTokenAsync`
- `RefreshAsync`: Token-Reuse-Detection delegiert an `IUserRepository.RevokeFamilyAsync`

### Bug-Fix
- `OpenClawGatewayClient.ReadAgentGoalAsync`: pre-existing `CS1656` behoben (`reader` war `using`-Variable und wurde neu zugewiesen — in `reader2` umbenannt)

### Controller (16 Stück — alle slim)
Alle Controller reduziert auf: Input validieren → Service aufrufen → HTTP-Result zurückgeben.
Kein Business-Logic, kein File-I/O, keine direkte Repository-Nutzung (außer AgentsController für Activity-Log).

**Program.cs — neue Registrierungen:**
- `AddHttpClient<IOpenClawGatewayClient, OpenClawGatewayClient>` (war vorher konkrete Klasse)
- Scoped: IDashboardService, IProjectService, ITaskService, IOperationsService, ITeamService, ICalendarService
- Singleton: IAgentConfigService, IMemoryService, IIncidentService, IDocService

---

## Frontend — Dashboard V2 Components

**AgentDetailModal.vue, IrisChat.vue, TaskStrip.vue:**
- V2 Design-System: Dark Space Theme, Glass-Panels, Gradient-Akzente
- Stores (agents, chat, tasks) nutzen Service + Mapper-Pattern
- NexusLayout, FlowBoard, Topbar — Layoutfixes für fullHeight-Route-Meta

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-14 08:34:58 +02:00

8.2 KiB

Deployment

Letzte Aktualisierung: 2026-06-13 Status: CD v3 (Auto + Manual) Live-URL: https://nexus.noveria.net

CD-Philosophie (v3)

  • CI läuft automatisch bei jedem Push → darf nie brechen
  • CD auto + manuell: Automaticher Deploy nach CI-Success auf main (patch default), manueller Deploy mit voller Kontrolle via workflow_dispatch
  • Loop-Schutz: Version-Bump-Commits enthalten [skip ci] — kein Re-Trigger der CI, kein Infinite-Loop
  • Main-Deploys duerfen VERSION bumpen und einen Git-Tag setzen
  • Nicht-Main-Deploys (anderer git_ref) deployen read-only und mutieren Git nicht
  • Rollback als eigener Workflow, manuell triggerbar
  • Database-Backup als eigener Workflow, manuell triggerbar (optionaler Nightly-Schedule)

Workflows

Deploy (.gitea/workflows/deploy.yaml)

Trigger:

  • Automatisch: Nach erfolgreicher CI (workflow_run auf CI - Build & Test) → Default-Parameter: patch bump, all services, main ref
  • Manuell: Via Gitea Actions → workflow_dispatch

Loop-Schutz:

  • Version-Bump-Commits enthalten [skip ci] → Gitea startet keine neue CI
  • Auto-Deploy prüft zusätzlich github.event.workflow_run.head_commit.message auf [skip ci]
  • Beide Mechanismen zusammen verhindern Endlosschleife: CI → Deploy → Bump → CI …

Inputs (nur bei workflow_dispatch):

Input Typ Default Beschreibung
version_bump choice (patch/minor/major) patch Version-Bump-Typ
service string (all) Einzelner Service oder alle
no_cache boolean false Docker-Build-Cache deaktivieren
git_ref string main Branch/Tag/Commit zum Deployen

Ablauf:

  1. Job-Level-Guard: Auto-Deploys fuer [skip ci]-Commits werden gar nicht gestartet
  2. Checkout des gewählten Git-Refs
  3. Wenn git_ref = main: Version-Bump + Git-Tag + Push
  4. Wenn git_ref != main: VERSION nur lesen, kein Push, kein Tag
  5. Safe Secret Handling: .env wird aus Secret-Umgebungsvariablen in /tmp/nexus-deploy-env geschrieben (mode 600), NICHT im Workspace
  6. Code-Sync zum Host-Deploy-Pfad
  7. docker compose build && up -d --wait --force-recreate
  8. .env-Tempfile wird mit shred gelöscht
  9. Health-Check (exponentieller Backoff, 6 Versuche)
  10. Smoke-Test (/dashboard, /health, /api/v1/operations/snapshot erwartet 401)
  11. Bei Fehler: Reviewer-Handoff-Meldung mit Job-URL

Backup (.gitea/workflows/backup.yaml)

Trigger: Manuell via Gitea Actions → workflow_dispatch (optional: Nightly-Schedule via Cron)

Inputs:

Input Typ Default Beschreibung
keep_on_host boolean false Backup auch auf Host-Pfad kopieren
host_backup_path string /opt/openclaw/backups Host-Zielpfad

Ablauf:

  1. Backup-ID generieren (Timestamp-basiert)
  2. docker exec nexus-postgres-1 pg_dumpall -U nexus → gzip
  3. Upload als Gitea-Artifact (90 Tage Retention, bereits komprimiert)
  4. Optional: Kopie auf Host-Pfad via Docker-Volume-Mount
  5. Integritäts-Check: gzip-Test + SQL-Header-Validierung
  6. Backup-Summary mit Restore-Befehl

Restore (manuell auf dem Host):

# Aus Gitea-Artifact herunterladen oder von Host-Pfad:
zcat nexus-backup-YYYY-MM-DDTHHMMSSZ.sql.gz | docker exec -i nexus-postgres-1 psql -U nexus -d postgres
# Danach Stack neu starten:
cd /opt/openclaw/data/openclaw/workspace/nexus
docker compose up -d --wait

Nightly-Schedule aktivieren: In backup.yaml die Zeilen auskommentieren:

schedule:
  - cron: '0 3 * * *'  # Jede Nacht um 03:00 UTC

Rollback (.gitea/workflows/rollback.yaml)

Trigger: Manuell via Gitea Actions → workflow_dispatch

Inputs:

Input Typ Beschreibung
target_tag string Git-Tag zum Zurückrollen (z.B. v0.2.49)
confirm string Muss exakt ROLLBACK sein (Safety-Gate)

Ablauf:

  1. Safety-Gate: Bestätigungstext muss ROLLBACK sein
  2. Checkout des Target-Tags
  3. Tag-Validierung (existiert? welcher Commit?)
  4. Safe Secret Handling (gleiches Tempfile-Pattern)
  5. Code-Sync des alten Stands zum Host
  6. docker compose build --no-cache && up -d --wait --force-recreate
  7. Health-Check + Smoke-Test (/dashboard, /health, /api/v1/operations/snapshot erwartet 401)
  8. Bei Fehler: Reviewer-Handoff mit manueller Rollback-Anleitung

DB-Migration bei Rollback: Die API führt MigrateAsync beim Start aus. Wenn die Migrationen des Rollback-Tags ein Prefix der aktuellen DB sind (Normalfall), läuft EF Core sie als No-Op. Wenn ein Rollback-Tag vor einer destruktiven Migration liegt, ist manuelles DB-Intervention nötig — ein Edge Case, der DevOps signalisiert wird.

Secrets und Konfiguration

Secrets in Gitea

Folgende Secrets sind in Gitea (Repo → Settings → Actions → Secrets) konfiguriert:

Secret Verwendung
ENV_POSTGRES_PASSWORD PostgreSQL-Passwort
ENV_JWT_KEY JWT-Signing-Key (min. 32 Bytes)
ENV_OWNER_PASSWORD Owner-Account-Passwort
ENV_OPENCLAW_TOKEN OpenClaw Gateway Token

Safe Secret Handling (v3)

Vorher (unsicher): Secrets wurden via ${{ secrets.X }} direkt in eine Datei im Workspace interpoliert, die dann zum Host synct wurde. Das .env lag potenziell lesbar im Workspace und auf dem Host-Dateisystem.

Jetzt (sicher):

  1. Secrets werden als Step-Environment aus Gitea Secrets bezogen und erst dann in /tmp/nexus-deploy-env (mode 600) geschrieben
  2. Die Temp-Datei wird via docker run -v als read-only ins Compose-Environment gemountet
  3. Nach Deploy/Rollback wird die Datei mit shred -u gelöscht
  4. Das .env erscheint nie im Workspace oder auf dem Host-Deploy-Pfad

Build-Anleitung (lokal oder in CI)

Die folgenden Befehle sind auf dem Build-System auszuführen. Vor dem Build müssen die Secrets in .env gesetzt sein.

# 1. Backend veröffentlichen
cd backend
dotnet publish -c Release -o dist

# 2. Frontend bauen (pnpm preferred)
cd ../frontend
pnpm install
pnpm build
# └─ Output: frontend/dist/ (statisch auslieferbar)

# 3. Docker-Stack starten (wenn compose verwendet wird)
cd ..
docker compose up -d --build

Die Container holen sich ihre Umgebungsvariablen aus der .env im Projektstamm.
Stelle sicher, dass .env existiert und alle ***-Platzhalter ersetzt sind.

Deployment-Plan

  1. .env-Datei auf dem VPS anlegen (alle Secrets generieren/setzen)
  2. Backup vor produktiven Infrastrukturarbeiten
  3. Docker-Stack auf dem VPS deployen
  4. Datenbankmigration läuft automatisch beim Start (via MigrateAsync)
  5. Nginx Proxy Manager und nexus.noveria.net verbinden
  6. HTTPS, Header, Cookies und externe Erreichbarkeit validieren

Abgeschlossene Deployment-Arbeit

  • Produktions-.env mit starken, getrennten Secrets angelegt (2026-06-08)
  • Datenbankmigration und kompletter Stack per Docker-Compose deployt
  • Nexus auf dem VPS deployt (Docker Compose)
  • Nginx mit Let's Encrypt SSL fuer nexus.noveria.net konfiguriert
  • HTTPS, Security-Header (HSTS, X-Content-Type-Options, X-Frame-Options), Cookies validiert
  • Externe Erreichbarkeit bestaetigt (2026-06-09)
  • CI/CD entkoppelt — Deploy darf automatisch (v3) oder manuell (2026-06-13)
  • Automatischer Deploy nach CI-Success auf main mit Loop-Schutz via [skip ci] (2026-06-13)
  • Safe Secret Handling: Tempfile in /tmp statt Workspace-Datei (2026-06-13)
  • Rollback-Workflow implementiert mit Safety-Gate (2026-06-13)
  • Main-Deploys koennen Version-Bump + Git-Tag automatisch setzen; Non-Main-Deploys bleiben read-only (2026-06-13)
  • Reviewer-Handoff bei Deploy/Rollback-Fehlern (2026-06-13)
  • Database-Backup-Workflow mit pg_dumpall + Gitea-Artifact (2026-06-13)

Verifizierung (2026-06-09)

  • https://nexus.noveria.net → 200 OK, SPA geladen
  • /health → Healthy
  • /dashboard, /login → SPA-Routing korrekt
  • /api/v1/operations/snapshot → 401 Unauthorized (Auth-Schutz aktiv)
  • Let's Encrypt TLS-Zertifikat aktiv
  • Nginx-Proxy → 127.0.0.1:18880

Offene Arbeit

  • Docker-Socket-Risiko im CD-Workflow final adressieren (kommt spaeter)
  • Docker-Logs und Container-Health-Monitoring einrichten
  • Restore-Drill fuer Backup/Recovery einmal realistisch durchspielen und dokumentieren
  • Direkt-Pushes auf main waehrend eines Main-Deploys organisatorisch vermeiden oder spaeter technisch haerter absichern