Files
Nexus/docs/cicd-target-architecture.md
T
Hartmut 4a5edeef3e
CI / Unit Tests (pull_request) Successful in 5m46s
CI / Lint (pull_request) Failing after 3m49s
CI / E2E Tests (pull_request) Has been skipped
CI / Fresh-Linux Docker Deploy (pull_request) Has been skipped
CI / Assistant Split Regression (pull_request) Failing after 35s
CI / Architecture Guardrails (pull_request) Failing after 2m14s
CI / Typecheck (pull_request) Successful in 4m22s
CI / Build (pull_request) Has been skipped
CI / Release Images (pull_request) Has been skipped
rename(phase 1): CapaKraken → Nexus across code, UI, docs, CI
- @capakraken/* → @nexus/* across 12 packages (root + 11 workspaces),
  1551 import lines migrated via codemod
- User-visible brand strings renamed (emails, page titles, PWA
  manifest, mobile header, MFA backup-codes header, tooltips, signin
  page, invite page, weekly digest, install prompt)
- TOTP issuer "CapaKraken" → "Nexus" (existing secrets still valid;
  re-enrollment relabels them in users' authenticator apps)
- Function rename: assertCapaKrakenDbTarget → assertNexusDbTarget
- LocalStorage migration shim in apps/web/src/app/layout.tsx copies
  capakraken_* → nexus_* on first load (guarded by nexus_migrated_v1
  sentinel; runs once per browser, then never again)
- Service-worker cache name capakraken-v2 → nexus-v2 with one-time
  caches.delete('capakraken-v2') from the same shim
- Email-domain fixtures @capakraken.{dev,app} → @nexus.{dev,app} in
  seed data, e2e specs, SMTP default fallback
- Dockerfile.dev / Dockerfile.prod / all .github/workflows/*.yml
  pnpm --filter @capakraken/* → @nexus/*
- README, CLAUDE.md, LEARNINGS.md, all docs/*.md, .env.example,
  tooling/deploy/.env.production.example brand sweep

Phase 1 deliberately leaves untouched (handled in Phase 3 cutover):
- PostgreSQL DB name "capakraken" and POSTGRES_USER "capakraken"
- Volume names capakraken_pgdata etc.
- Compose project name "capakraken" / "capakraken-prod"
- db-target-guard default expectedDatabase
- env-var CAPAKRAKEN_EXPECTED_DB_NAME
- Container DNS names in docker-compose.ci.yml

Quality gates green: pnpm typecheck (7/7), pnpm test:unit (7/7),
pnpm lint (0 errors), check:exports/imports/architecture all pass.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 15:10:44 +02:00

4.9 KiB

CI/CD Target Architecture

Goal

This document describes the canonical release path for Nexus.

The release model is now:

  1. PRs are validated by CI before merge.
  2. Every push to main publishes immutable app and migrator images.
  3. Staging and production promote the exact same sha-<commit> tag.
  4. The host deploys only from images and runtime env files.
  5. A deployment is successful only after GET /api/ready passes.

Canonical Flow

1. Pull Request Validation

The main ci.yml workflow remains the merge gate for:

  • architecture guardrails
  • typecheck
  • lint
  • unit tests
  • build
  • E2E

2. Automatic Image Release

release-image.yml now runs automatically on every push to main and can still be started manually for rebuilds or tag overrides.

It publishes two images from Dockerfile.prod:

  • ghcr.io/<owner>/<repo>-app:sha-<commit>
  • ghcr.io/<owner>/<repo>-migrator:sha-<commit>

3. Staging Promotion

deploy-staging.yml copies the canonical deploy bundle to the staging host:

GitHub Actions also writes a short-lived deploy.env containing APP_IMAGE, MIGRATOR_IMAGE, and the host port.

4. Host-Side Deployment

On the target host, deploy-compose.sh:

  1. loads .env.production and deploy.env
  2. validates the rendered compose file
  3. pulls the immutable app and migrator images
  4. starts PostgreSQL and Redis
  5. runs Prisma migrations through the dedicated migrator image
  6. starts the new app container
  7. waits for GET /api/ready

The host does not build application code from Git anymore.

5. Production Promotion

deploy-prod.yml repeats the exact staging flow with the same image tag after staging acceptance.

That keeps staging and production on the same artifact instead of rebuilding.

Required Infrastructure

Minimum

  • GitHub repository with Actions enabled
  • GHCR or another container registry
  • one Linux host with Docker Engine and Docker Compose v2
  • PostgreSQL
  • Redis
  • SSH access from GitHub Actions to the host
  • reverse proxy or load balancer in front of the app
  • separate staging and production hosts
  • GitHub Environments for staging and production
  • required approval for the production environment
  • monitoring on /api/health and /api/ready
  • PostgreSQL backup and restore drills

Runtime Configuration

The canonical host-side inputs are:

.env.production holds long-lived runtime configuration and secrets. The example file is tooling/deploy/.env.production.example.

deploy.env is short-lived deployment metadata. The example file is tooling/deploy/deploy.env.example.

Important invariants:

  • RATE_LIMIT_BACKEND=redis should stay explicit in release environments
  • runtime AI, SMTP, and anonymization secrets belong to the host or platform secret layer
  • admin settings are for verification and legacy-secret cleanup, not for secret rotation

Database Policy

Release environments must run migrations through the migrator image, which executes:

pnpm --filter @nexus/db db:migrate:deploy

db:push remains a local-development tool, not a production rollout mechanism.

Rollback Model

Rollback is image-based:

  1. choose the previous healthy sha-<commit> tag
  2. redeploy staging or production with that tag
  3. confirm GET /api/ready

This assumes schema changes follow backwards-compatible expand-and-contract rollout rules.

Production Update Summary

The standard production update is:

  1. merge to main after CI is green
  2. let release-image.yml publish sha-<commit> images
  3. deploy that tag to staging through deploy-staging.yml
  4. validate staging
  5. promote the same tag through deploy-prod.yml

The important property is artifact identity: staging and production run the same image, not two separate builds.