Files
Onyscribe-Admin/README.md
RubenRWU 7d8c226da2 Onyscribe Admin: Initialer Stand
Admin-Plattform für die Tenants von Onyscribe: Editor mit Live-Vorschau (echter Core in Sandbox), Versionen, KI-Agent (Cortecs),
OIDC-Abgleich über die Login-Developer-API, Deploy-Engine (Mittwald, gesperrt ohne DEPLOY_LIVE, Freigabe pro Tenant),
Core-Vertrag (Schema/Manifest), Core-Releases mit Kompatibilitätsprüfung, Canary-Rollout und Rollback, Zustandsabgleich.
Runbook: docs/RUNBOOK_PARALLELTEST.md

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-15 15:30:26 +02:00

142 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Onyscribe Admin
Separat gehostete Admin-Plattform für die Tenants von **Onyscribe** (Repo `transkript-platform`): Tenants anlegen und bearbeiten,
Live-Vorschau mit dem echten Core, versionieren, Secrets/OIDC/Domain pflegen, deployen, mit KI-Agent (Cortecs).
## Stand
Alles, was **ohne Änderung am Core und ohne echte Deployments** möglich ist, ist umgesetzt:
| Bereich | Stand |
|---|---|
| Tenants (7 importiert, Hash-identisch zu Git) | ✔ Import, Seed, Anlegen/Klonen, Status, Ressourcen, Notizen, Archivieren/Löschen |
| Entwurf → Diff → Version | ✔ unveränderliche Snapshots, Rollback als Entwurf, Hook-Code-Freigabe |
| Editor | ✔ Marke/Farben, Absender, PDF-Layout, Prompt, Dateien, Flags/STT, Hook-Code, tenant.json |
| Live-Vorschau | ✔ Web-UI + PDF mit dem echten Core in Sandbox (kein Secret, CPU-/Datei-Limits), Validierung |
| Prompt-Test | ✔ echter LLM-Lauf (braucht Cortecs-Key), finaler Prompt inkl. Hooks |
| KI-Agent | ✔ echter Tool-Loop (Messages- oder Chat-API gegen Cortecs), Workspace-Entwurf, Diff, Freigabe; ✔ Demo-Modus ohne Key; ⚠ gegen Live-Cortecs noch **ungetestet** (kein Key) |
| Login (OIDC) | ✔ Code + Tests (Mock-IdP); ⚠ gegen login.onyva.dev noch **ungetestet** |
| Secrets | ✔ Fernet-verschlüsselt, Import aus SOPS (`--with-secrets`) |
| OIDC-Clients | ✔ **automatischer Abgleich bei jedem Deploy** über die Login-Developer-API: Client anlegen, Redirect-URIs, auth/totp, Freigaben; Secret wird sofort verschlüsselt gespeichert; Dry-Run liest nur. Gegen Fake-Login getestet, gegen login.onyva.dev **ungetestet** (kein Key) |
| Domain | ✔ Subdomain/eigene Domain, DNS-Check |
| Deploy-Engine | ✔ Port von `infra/deploy.py` (PATCH, Guard, Ingress, Offboard, Abbruch bei Fehler), per Fake-HTTP getestet; **Schreibzugriffe gesperrt** (`DEPLOY_LIVE`) |
| Bundle-Endpoint | ✔ `GET /api/tenants/<id>/bundle` (Token); Gegenstück im Core: Branch `admin-platform-integration` (`core/bundle.py`) |
| **Core-Vertrag** | ✔ Schema/Manifest/Build-Info aus dem Core gelesen (`app/services/contract.py`); Formulare („Funktion & STT“ + automatisch alle neuen Optionen), Einstellungen und Container-Env werden daraus **erzeugt**; Pflicht-Secrets laut Schema |
| **Core-Releases** (Menü „Core“) | ✔ Release-Eingang für die Core-CI, Kompatibilitätsprüfung **aller** Tenants mit dem Core des Admins (Validierung + Fingerabdrücke von PDF/Web/Prompt gegen Baseline), Freigabe, Dry-Run, Canary-Rollout mit Abbruch, Health-Verifikation (Core-SHA, Bundle-Version), Pins pro Tenant, Rollback (Release/Tenant) |
| Zustandsabgleich | ✔ „Zustand prüfen“ im Dashboard und „Läuft?“ pro Tenant: fragt `/health` ab und zeigt Abweichungen |
**Bewusst offen (braucht Core- bzw. Produktionsentscheidung):**
1. Core-Branch `admin-platform-integration` (Vertrag, Bundle-Loader, `/health`) reviewen und mergen/deployen. Erst danach `core_supports_bundle` aktivieren (Einstellungen) – bis dahin sind Deploys von Tenants mit Quelle „Admin“ gesperrt.
2. `DEPLOY_LIVE=true` + `MITTWALD_API_TOKEN` + Projekt-/Stack-ID, sobald echte Deployments gewollt sind.
3. Cortecs-Key hinterlegen → Agent-Modell auswählen (Einstellungen → „Modelle von Cortecs laden“) und gegen Live testen.
4. OIDC-Client für die Admin-Plattform in login.onyva.dev anlegen, `AUTH_ENABLED=true`.
4b. Admin-Key im Login-Dashboard erzeugen → `ONYVA_LOGIN_API_KEY` setzen; mit „Verbindung testen“ (Einstellungen) prüfen; zum Schreiben `OIDC_SYNC_LIVE=true`.
5. Datenbank-Migrationen (Alembic) vor dem ersten produktiven Schemawechsel; Tabellen entstehen derzeit per `create_all`.
## Starten
```bash
# lokal (SQLite, kein Docker)
python3 -m venv .venv && . .venv/bin/activate && pip install -r requirements.txt
# System-Libs für WeasyPrint: libpango-1.0-0 libpangoft2-1.0-0 fonts-noto-core
cp .env.example .env # Keys eintragen (wird beim Start eingelesen; echte Env-Variablen haben Vorrang)
uvicorn app.main:app --port 8300 # http://localhost:8300 (Seed lädt die 7 Tenants automatisch) – nach .env-Änderungen neu starten
# mit Postgres (wie Coolify)
export POSTGRES_PASSWORD=... SECRET_KEY=$(python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())")
docker compose up -d --build
```
**Coolify:** Neue Ressource → Docker Compose → dieses Repo. Env `POSTGRES_PASSWORD` und `SECRET_KEY` setzen (weitere Variablen: `.env.example`),
Domain auf Service `admin`, Port `8300`. Backup: Volume `pgdata` **und** `SECRET_KEY`.
**Login aktivieren:** Client in login.onyva.dev anlegen (Redirect `https://<admin>/auth/callback`), dann `AUTH_ENABLED=true`, `OIDC_CLIENT_ID/SECRET`,
`OIDC_REDIRECT_URI`, `OIDC_LOGOUT_REDIRECT_URI` und eine Zugriffsregel (`ADMIN_ALLOWED_EMAILS` und/oder `ADMIN_REQUIRED_ROLE`). Ohne Regel wird jeder Login abgelehnt.
## Zusammenspiel mit dem Core
Spezifikation: `transkript-platform/docs/CONTRACT.md`. Kurz: Der Core liefert **Image + Schema + Manifest + Bundle-Format + `/health`**, der Admin liest das –
er hält keine eigene Kopie der Tenant-Optionen.
```
Core-Push → CI: Tests → Image (:sha) → POST /api/core/releases (Job „notify-admin“)
→ Admin neu bauen (Dockerfile.fromcore, FROM Core-Image) → „Core“: Prüfen (alle Tenants) → Freigeben → Ausrollen (Canary zuerst) → Health-Check je Tenant
Tenant-Änderung (ohne Core): Veröffentlichen → Deploy mit dem aktiven Core-Tag (nur Bundle-Neustart, kein Image-Build)
```
- **Admin auf Basis des Core-Images:** `Dockerfile.fromcore` (`CORE_IMAGE=<registry>/transkript-platform:<tag>`). Sandbox, Schriften und Pakete sind identisch zur Produktion. Läuft der Admin auf einem anderen Core als ein gemeldetes Release, zeigt „Core“ das an und erlaubt keine Prüfung/Freigabe. *Noch zu verifizieren in Coolify:* wie der Neubau mit neuem Tag ausgelöst wird (Webhook + `CORE_IMAGE` per Env/API oder `latest` mit „immer pullen“).
- **Lokal ohne Core-Image:** vendored Snapshot (`scripts/sync_core.sh`, zieht den aktuellen Stand der Quelle inkl. Branch).
- **Neue Core-Option?** Steht sie im Schema des Cores (CI erzwingt das), erscheint sie hier automatisch in Formularen, Einstellungen und Container-Env.
- **Release-Eingang:** Env `CORE_RELEASE_TOKEN` (Admin) = Secret `ADMIN_RELEASE_TOKEN` (Core-CI) + Variable `ADMIN_URL`.
## OIDC-Abgleich (Login-Developer-API)
Pro Tenant (Domain & Login) gibt es einen Soll-Zustand und eine **Verwaltung**:
| Modus | Verhalten beim Deploy |
|---|---|
| `off` | nichts anfassen |
| `adopt` (Standard für die 7 importierten Altclients) | prüfen, dass die Redirect-URIs (Subdomain + ggf. eigene Domain) enthalten sind, fehlende ergänzen – sonst nichts ändern |
| `managed` (Standard für neue Tenants) | Client fehlt → **anlegen**; sonst Redirect-URIs, `auth_mode`, `totp_mode`, `allow_registration`, Backchannel exakt wie konfiguriert; Freigaben setzen (optional „exakt“: überzählige entziehen) |
Ablauf eines Deploys: **1.** OIDC abgleichen (bei Neuanlage Client-ID/-Secret sofort verschlüsselt speichern) → **2.** Pflicht-Secrets prüfen → **3.** Service-PATCH mit den Werten
im Env → **4.** Ingress/Health. Schlägt der Abgleich fehl, wird **vor** dem Service-Eingriff abgebrochen. Dry-Run liest nur und zeigt „würde anlegen/anpassen“.
Zusätzlich manuell: „Gegen Login prüfen“, „Jetzt abgleichen“, „Aus Login übernehmen“ (Freigaben), „Secret rotieren“, „OIDC-Client im Login löschen“ (Gefahrenbereich).
Das Secret ist nur bei Anlage/Rotation sichtbar; ein Altclient ohne Secret in der Admin-DB wird **nie** automatisch rotiert (er würde laufende Logins unterbrechen) – Secret eintragen oder bewusst rotieren.
Lokal ausprobieren ohne echtes Login: `python scripts/fake_login_server.py` (siehe Datei-Kopf).
## KI-Agent
**Bedienung:** Header → „KI-Agent“. Der Chat ersetzt in der Mitte das Formular, die **Live-Vorschau bleibt rechts** und zeigt den Arbeitsstand des Agenten *während er arbeitet*
(„Agent-Stand (live)“, rendert nach jeder Änderung neu), danach den „Agent-Vorschlag“. „In Entwurf übernehmen“ schreibt ihn in den Entwurf, „Verwerfen“ lässt alles unverändert.
**Key-Suche:** geteiltes Secret (Einstellungen) → Env/`.env` `CORTECS_API_KEY` → Key des Tenants. Der Chat zeigt, woher der Key kommt, bzw. „Demo-Modus – kein Key gefunden“.
Cortecs bietet neben Chat-Completions auch das **Anthropic-Messages-Protokoll** (`/v1/messages`) – der Agent nutzt standardmäßig dieses
(Einstellung `agent_api`), mit Fallback auf Chat-Completions. Modell: Einstellung `agent_model` (Standard `claude-4-6-sonnet`, wie das Protokoll-Modell;
„Modelle von Cortecs laden“ zeigt die verfügbaren IDs und markiert Tool-Unterstützung). Der Agent arbeitet nur auf einem Entwurfs-Workspace; Namenslisten
sind gesperrt; Secrets kennt er nicht; Veröffentlichen/Deployen kann er nicht.
## Aufbau
```
app/
main.py FastAPI, Startup (DB anlegen, Seed laden)
models.py Tenant, Version (unveränderlich), Blob (content-addressed), Secret (verschlüsselt), Deployment, AuditLog, ChatMessage
services/
store.py Entwurf ↔ Version, Diff, Bundle (tar.gz) – Kern der Versionierung
preview.py Live-Vorschau: Subprozess mit Kopie des Cores + Tenant, ohne Secrets (preview_worker.py)
importer.py Import aus ../transkript-platform (tenants/*, Secrets nur als Schlüsselnamen)
deploy_sim.py Deploy-Plan (wie infra/deploy.py) + Simulation ← hier später die echte Mittwald-Logik
agent_mock.py Mock-Agent ← hier später der Cortecs-Loop
seed.py Start-Daten (seed/tenants.seed.json.gz)
routers/ api.py (JSON), pages.py (HTML)
vendor/platform/ Snapshot des Cores (CORE_VERSION) – Vorschau führt genau diesen Stand aus
scripts/ import_tenants.py, verify_import.py, export_seed.py, sync_core.sh
tests/test_flow.py E2E: Import, Vorschau, Entwurf→Veröffentlichen, Validierung, Bundle, Deploy-Sim, Agent
```
## Wichtige Abläufe
```bash
python scripts/import_tenants.py # Tenants (+ Secret-SCHLÜSSEL) aus ../transkript-platform importieren
python scripts/import_tenants.py --with-secrets # zusätzlich Secret-WERTE via `sops -d` lesen und verschlüsselt speichern (Cut-over)
python scripts/verify_import.py # Beweis: DB-Bundle == Git-Ordner (Hash-Vergleich)
python scripts/export_seed.py # DB-Stand als Seed-Datei ins Repo (ohne Secret-Werte)
scripts/sync_core.sh # vendored Core aus transkript-platform aktualisieren
python -m pytest tests # Tests (51) (isoliert: lesen weder .env noch echte Keys, eigene Temp-DB)
```
## Bundle-Endpoint (Schnittstelle zum Core)
`GET /api/tenants/<id>/bundle` mit `Authorization: Bearer <ADMIN_BUNDLE_TOKEN>` liefert `tenant.json` + alle Dateien
(+ `MANIFEST.json` mit Hashes) – ohne Secrets. Das ist, was ein Tenant-Container später beim Start abholt
(`TENANT_SOURCE=admin`, Phase 1 im Core – noch nicht umgesetzt).
## Nächste Schritte
1. Core: `TENANT_SOURCE=git|admin` + Bundle-Loader mit Cache (Phase 1)
2. OIDC-Login der Admin-Plattform über login.onyva.dev
3. Spike: Cortecs-Modell für den Agent (Tool-Calling, Streaming, Bild-Input) → `agent_mock.py` ersetzen
4. Echte Deploys (Mittwald) aus `deploy_sim.py`, Rollout-Abbruch bei Fehler, Health-Check
5. Hook-Code-Freigabe härten (Sandbox ohne Netz), Rollen