diff --git a/.gitea/workflows/deploy-prod.yml b/.gitea/workflows/deploy-prod.yml index 55ea082..4af41a7 100644 --- a/.gitea/workflows/deploy-prod.yml +++ b/.gitea/workflows/deploy-prod.yml @@ -25,7 +25,17 @@ jobs: fi docker compose build --no-cache backend frontend echo "✓ Backend + Frontend gebaut (Frontend: npm run build im Dockerfile)" - docker compose up -d --wait - curl -sf http://localhost:8004/api/health && echo "✓ PROD API /api/health OK" + if ! docker compose up -d --wait; then + echo "✗ compose up --wait fehlgeschlagen — Backend-Logs:" + docker compose logs backend --tail 150 || true + docker compose ps || true + exit 1 + fi + if ! curl -sf http://localhost:8004/api/health; then + echo "✗ PROD API nicht erreichbar — Backend-Logs:" + docker compose logs backend --tail 150 || true + exit 1 + fi + echo "✓ PROD API /api/health OK" curl -sf http://localhost:3004/api/health && echo "✓ PROD Frontend-Proxy /api/health OK" echo "=== Kairo PROD Deploy complete ===" diff --git a/docs/DEPLOYMENT.md b/docs/DEPLOYMENT.md index 2912438..99da214 100644 --- a/docs/DEPLOYMENT.md +++ b/docs/DEPLOYMENT.md @@ -75,9 +75,24 @@ Aktuell keine Medien-Speicherung. Bei Bedarf: NAS-Mount + `docker-compose.overri --- -## Dev-Datenbank wechseln +## Datenbank-Passwort (Dev & Prod) -PostgreSQL im Compose-Stack initialisiert User/Passwort **nur beim ersten Start** des Volumes (`dev-kairo-db-data`). +PostgreSQL im Compose-Stack initialisiert User/Passwort **nur beim ersten Start** des Volumes. +Wenn `DB_PASSWORD` in `.env` nicht mehr zum Volume passt, scheitert das Backend mit +`password authentication failed for user "kairo_user"` — der Container bleibt `unhealthy`. + +**Prod (Daten behalten):** Passwort in der laufenden DB an `.env` anpassen: + +```bash +cd /home/lars/docker/kairo +set -a && source .env && set +a +docker compose exec -T postgres psql -U kairo_user -d kairo \ + -c "ALTER USER kairo_user WITH PASSWORD '${DB_PASSWORD}';" +docker compose restart backend +curl -sf http://localhost:8004/api/health +``` + +**Dev (Volume neu, inkl. Testdaten):** Volume löschen und neu starten (`dev-kairo-db-data`). Wenn du `DB_NAME`, `DB_USER` oder `DB_PASSWORD` in `.env` änderst, muss das Volume neu angelegt werden: ```bash diff --git a/docs/sprints/Sprint0_AP0_3_Completion_Report_v0.3.md b/docs/sprints/Sprint0_AP0_3_Completion_Report_v0.3.md new file mode 100644 index 0000000..462321a --- /dev/null +++ b/docs/sprints/Sprint0_AP0_3_Completion_Report_v0.3.md @@ -0,0 +1,322 @@ +# AP0.3 – Abschlussbericht Capability / Rights Registry & Entitlements Snapshot + +**Status:** abgeschlossen +**Stand:** 2026-07-04 (final inkl. Prod-Deploy-Nacharbeit) +**Branch:** `develop` · Dev-Deploy und Test Suite grün · **Prod verifiziert** (Schema `004`, Health ok) + +--- + +## 1. Scope und Einordnung + +AP0.3 liefert die **minimale Capability-/Rights-Grundlage** und den **Entitlements-Snapshot** für Sprint 0. Auth, TenantContext und Sessions stammen aus AP0.2; AP0.3 ergänzt Registry, DB-Sync, zentrale Capability-Prüfung und `/api/me/entitlements`. + +| Anforderung (AP0.3) | Status | +|---------------------|--------| +| Migration Capabilities + Role-Grants | ✓ | +| Runtime Registry + Modul-Registrierungen | ✓ | +| Startup-Sync Registry → DB | ✓ | +| TenantContext.capabilities | ✓ | +| `require_capability()` Dependency | ✓ | +| `GET /api/me/entitlements` | ✓ | +| Beispiel-Endpoint mit Capability-Gate | ✓ | +| Tests (Registry, Sync, Auflösung, Enforce/Probe) | ✓ | +| Trennung Auth / Capability / Feature | ✓ | + +**Bewusst nicht in AP0.3:** Feature-Limits, Billing, Usage-Zähler, Prompt Registry, produktive Admin-UI, Governance/Object-ACL. + +--- + +## 2. Umgesetzte Dateien + +| Datei | Zweck | +|-------|--------| +| `backend/migrations/004_capabilities_registry.sql` | Tabellen `capabilities`, `role_capability_grants` | +| `backend/rights_registry.py` | In-Memory-Registry, Validierung, DB-Sync, Grant-Lookup | +| `backend/rights_registrations/__init__.py` | Side-Effect-Import aller Modul-Registrierungen | +| `backend/rights_registrations/platform.py` | Platform-Capabilities | +| `backend/rights_registrations/tenant_ops.py` | Tenant-/Actor-Capabilities | +| `backend/capabilities.py` | Auflösung, `check_capability`, `require_capability` | +| `backend/entitlements.py` | Entitlements-Snapshot-Builder | +| `backend/sync_rights_registry.py` | CLI-Sync beim Container-Start | +| `backend/tenant_context.py` | `TenantContext.capabilities` | +| `backend/routers/me.py` | `/entitlements`, `/admin/demo` | +| `backend/entrypoint.sh` | Rights-Sync nach Seeds | +| `backend/version.py` | `0.3.0-ap0.3`, Schema `004` | +| `backend/tests/test_rights_registry.py` | Registry + Sync | +| `backend/tests/test_capabilities.py` | Grants, enforce/probe, Demo-Endpoint | +| `backend/tests/test_entitlements.py` | Snapshot-Form | +| `backend/tests/conftest.py` | Registry-Sync in Test-Session | +| `frontend/src/App.jsx` | Header AP0.3; `/api/me/context` zeigt Capabilities | +| `README.md` | API-Doku AP0.3 | +| `.gitea/workflows/deploy-prod.yml` | Backend-Logs bei `--wait`-Fehler (Parität zu Dev) | +| `docs/DEPLOYMENT.md` | Abschnitt DB-Passwort Dev/Prod inkl. `ALTER USER`-Fix | + +**Wesentliche Commits:** `910b1d7` (Implementierung), `eaa0e25` (pytest-Fix), `4069cb0` (GUI-Sprint-Label), `39e8084` (Abschlussbericht v0.2). + +**Nacharbeit (noch auf `develop`):** `deploy-prod.yml`, `DEPLOYMENT.md`, dieser Bericht v0.3. + +--- + +## 3. Neue Migrationen + +**`004_capabilities_registry.sql`** + +- **`capabilities`** — `capability_key`, `module`, `description`, `is_active` +- **`role_capability_grants`** — `role_scope` (`portal` \| `tenant`), `role_code`, `capability_key` + +Kein Feature-/Limit-Schema (Vorbereitung AP0.4). + +--- + +## 4. Registry-Struktur + +``` +backend/ +├── rights_registry.py +├── rights_registrations/ +│ ├── __init__.py +│ ├── platform.py # admin, context, entitlements +│ └── tenant_ops.py # tenant.manage, actor.manage +└── sync_rights_registry.py +``` + +**Modell:** `@dataclass(frozen=True) CapabilityRegistration` mit `key`, `module`, `description`, `default_grants`. + +**Startup:** `entrypoint.sh` → Migrationen → Seeds → `python sync_rights_registry.py` → Uvicorn (`SKIP_RIGHTS_SYNC=1` zum Überspringen). + +--- + +## 5. Endpoints + +| Endpoint | Methode | Auth / Gate | Status | +|----------|---------|-------------|--------| +| `/api/me/context` | GET | Session | geändert — `capabilities[]` | +| `/api/me/entitlements` | GET | Session | neu | +| `/api/me/admin/demo` | GET | Session + `kairo.admin.access` | neu (Beispiel `require_capability`) | + +OpenAPI (Dev): `/api/docs` + +--- + +## 6. Capability-Auflösungslogik + +``` +Session (portal_role, active_tenant_id) + → tenant_role aus Membership (optional) + → load_grants_for_roles(portal_role, tenant_role) + → Union Portal-Grants + Tenant-Grants + → TenantContext.capabilities (frozenset) +``` + +**`require_capability(key)`:** prüft Mitgliedschaft in `ctx.capabilities`. + +| Modus | Env | Verhalten | +|-------|-----|-----------| +| probe | `CAPABILITY_ENFORCE=probe` (Default) | Zugriff erlaubt; Verweigerung → Audit `capability.denied` | +| enforce | `CAPABILITY_ENFORCE=enforce` | HTTP 403 ohne Grant | + +--- + +## 7. Initiale Capabilities + +| Capability | Portal admin | Portal user | Tenant owner | Tenant admin | Tenant member | +|------------|:------------:|:-----------:|:------------:|:------------:|:-------------:| +| `kairo.admin.access` | ✓ | | | | | +| `kairo.tenant.manage` | ✓ | | ✓ | ✓ | | +| `kairo.actor.manage` | ✓ | | ✓ | ✓ | | +| `kairo.context.read` | ✓ | ✓ | ✓ | ✓ | ✓ | +| `kairo.entitlements.read` | ✓ | ✓ | ✓ | ✓ | ✓ | + +--- + +## 8. Tests und Verifikation + +### pytest (CI, Dev-Container) + +```bash +docker compose -f docker-compose.dev-env.yml exec backend python -m pytest tests -ra -vv +``` + +| Testdatei | Abdeckung | Status | +|-----------|-----------|--------| +| `test_rights_registry.py` | 5 Capabilities, Sync idempotent | ✓ | +| `test_capabilities.py` | Admin/Member enforce, Owner-Grants, probe | ✓ | +| `test_entitlements.py` | Snapshot-Struktur | ✓ | +| `test_migrations.py` | Migration 004 | ✓ | +| Gesamt Test Suite (Gitea) | nach Push `develop` | ✓ | + +### Manuell (Dev-GUI) + +| Prüfung | Ergebnis | +|---------|----------| +| Login `lars@stommer.com` | ✓ | +| `/api/me/context` → `capabilities` sichtbar | ✓ | +| API Health → `"schema": "004"` | ✓ | +| Header Sprint-Anzeige AP0.3 | ✓ | + +**Hinweis:** Entitlements und Admin-Demo sind in der GUI noch nicht als eigene Karten — prüfbar über Swagger (`/api/docs`) oder DevTools. + +### Production (nach Nacharbeit 2026-07-04) + +| Prüfung | Ergebnis | +|---------|----------| +| `curl http://localhost:8004/api/health` | ✓ — `0.3.0-ap0.3`, Schema `004`, `db: ok` | +| `curl http://localhost:3004/api/health` | ✓ | +| Migration 004 auf Prod-Volume | ✓ (nach DB-Reconnect) | +| Rights-Sync beim Start | ✓ | +| Bootstrap-Admin (`.env`) | unverändert nutzbar | + +--- + +## 9. Prod-Deploy-Nacharbeit (Gitea `deploy-prod` #7154) + +### Symptom + +Push auf `main` → Workflow `deploy-prod` schlug fehl: + +``` +dependency failed to start: container kairo-api is unhealthy +``` + +Backend-Logs (Pi): + +``` +[FAIL] ... FATAL: password authentication failed for user "kairo_user" +``` + +### Ursache + +**Kein AP0.3-Codefehler.** PostgreSQL initialisiert User/Passwort nur beim **ersten Start** des Volumes (`kairo-db-data`). Das Passwort in `/home/lars/docker/kairo/.env` wich vom in der DB gespeicherten Passwort ab — identisches Muster wie beim Dev-Deploy (#7100). + +Der Entrypoint bricht bei DB-Auth-Fehlern fail-fast ab (seit AP0.2/`f8b3ae3`); Uvicorn startet nicht → Healthcheck scheitert nach wenigen Sekunden. + +### Behebung (Prod, ohne Datenverlust) + +```bash +cd /home/lars/docker/kairo +set -a && source .env && set +a +docker compose exec -T postgres psql -U kairo_user -d kairo \ + -c "ALTER USER kairo_user WITH PASSWORD '${DB_PASSWORD}';" +docker compose restart backend +docker compose up -d --wait +``` + +Danach: alle Container `healthy`, API und Frontend-Proxy antworten. + +### Prävention / Doku + +| Maßnahme | Datei | +|----------|-------| +| Prod-Fix ohne Volume-Löschung dokumentiert | `docs/DEPLOYMENT.md` § Datenbank-Passwort | +| Backend-Logs bei `compose up --wait`-Fehler | `.gitea/workflows/deploy-prod.yml` (wie Dev) | + +**Wichtig:** `docker compose down -v` auf Prod löscht alle Anwendungsdaten. Bei Passwort-Wechsel in `.env` bevorzugt `ALTER USER` verwenden. + +--- + +## 10. Deployment & Betrieb + +| | Development | Production | +|---|-------------|------------| +| Verzeichnis | `/home/lars/docker/kairo-dev` | `/home/lars/docker/kairo` | +| Ports | 3097 / 8097 | 3004 / 8004 | +| Postgres | eigener Container + Volume | eigener Container + Volume (`kairo-db-data`) | +| Dev-Seeds | ja | nein | +| Admin | `lars@stommer.com` (Seed) | `KAIRO_BOOTSTRAP_*` in `.env` | +| Schema nach AP0.3 | `004` | `004` | + +**Startup-Reihenfolge:** Entrypoint → Schema-Migrationen → Data-Seeds → Rights-Sync → Uvicorn. + +--- + +## 11. Übernommene Muster aus Shinkan + +| Muster | Kairo-Umsetzung | +|--------|-----------------| +| Registry-first | `rights_registry.py` + `rights_registrations/` | +| `register_capability()` mit Validierung | Pflichtfelder `key`, `module`, `description` | +| Frozen Dataclass-Definitionen | `CapabilityRegistration` | +| Startup-Sync Registry → DB | `sync_rights_registry_to_db()` | +| Modul-Ownership | `platform`, `tenant` | +| Default Grants in Code | `default_grants` pro Capability | +| `require_capability` Dependency | `capabilities.py` | +| `/api/me/entitlements` Snapshot | `entitlements.py` | +| Probe vs. Enforce | `CAPABILITY_ENFORCE` | + +**Nicht übernommen:** `club_id`, Vereinsrollen, Übungs-/Trainingsrechte, Club-Feature-Kontingente. + +--- + +## 12. Übernommene Muster aus Mitai + +| Muster | Kairo-Umsetzung | +|--------|-----------------| +| Entitlements als zentrale Schicht | `build_entitlements_snapshot()` | +| Probe-vs-Enforce-Denken | Default `probe`, optional `enforce` | +| Feature-Registry vorbereitet | `features: {}` im Snapshot | + +**Nicht übernommen:** Tier/Subscription, Usage-Zähler, Billing, Legacy `check_feature_access`. + +--- + +## 13. Bewusst nicht übernommen + +- Shinkan-/Mitai-Domänenbegriffe (`club`, `profile`, Tier) +- Feature-Limits, Billing, Coupons +- Prompt Registry, Vorhaben-/Projektlogik +- MCP, produktive Admin-UI (nur Debug-JSON + Swagger) +- Capabilities nur in SQL pflegen +- Governance/Object-ACL + +--- + +## 14. Abweichungen von Designprinzipien + +| Thema | Abweichung | Begründung | +|-------|------------|------------| +| Entitlements-Struktur | Account- + Tenant-Block | Kairo Hybrid laut Family Entitlement Model | +| Feature-Registry | leeres Objekt | AP0.4 | +| Grant-Overrides | Sync nur additive Grants | Sprint-0-Minimal | +| `linked_feature_id` | nicht modelliert | Keine Limits in AP0.3 | +| Frontend | kein Entitlements-Panel | Minimal-UI; AP0.4/UX optional | + +**Eingehalten:** zentrale Capability-Prüfung; Portal- vs. Tenant-Rolle getrennt; TenantContext als Auflösungsschicht. + +--- + +## 15. Offene Entscheidungen + +1. **Prod `CAPABILITY_ENFORCE=enforce`** — wann umstellen? +2. **Grant-Overrides in DB** vs. reiner Code-Kanon +3. **Prod-pytest-Cleanup** (`*@example.com`) — aus AP0.2 offen +4. **Frontend:** Entitlements-Karte + Admin-Demo-Button +5. **Feature-Registry** — AP0.4 + +--- + +## 16. Empfehlung für AP0.4 + +Laut Foundation **AP0.4 – Prompt, Feature, Config Registry**: + +- Feature Registry (Metadaten, ohne Billing-Limits in Sprint 0) +- PromptTemplate / PromptVersion / Placeholder +- ConfigurationEntry +- Prompt-Rendering mit Placeholder-Validation +- `features`-Block in Entitlements mit Registry-Sync + +Optional vor AP0.4: Prod-Cleanup-Seed; GUI-Entitlements; `CAPABILITY_ENFORCE=enforce` in Prod nach Kalibrierung. + +--- + +## Referenzen + +- `docs/sprints/Jinkendo_Kairo_04_Sprint0_Foundation_v0.3.md` § AP0.3, §9 +- `docs/reference/design-principles/shinkan/RIGHTS_REGISTRY_DESIGN_PRINCIPLES.md` +- `docs/reference/design-principles/shinkan/CAPABILITY_ENTITLEMENT_DESIGN_PRINCIPLES.md` +- `docs/reference/design-principles/alignment/FAMILY_ENTITLEMENT_MODEL.md` +- `docs/DEPLOYMENT.md` — DB-Passwort Dev/Prod +- Vorgänger: `docs/sprints/Sprint0_AP0_2_Completion_Report_v0.2.md`, `Sprint0_AP0_3_Completion_Report_v0.2.md` + +--- + +*Abgeschlossen im Rahmen Sprint 0 – AP0.3. Ersetzt `Sprint0_AP0_3_Completion_Report_v0.2.md`.*