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>
142 lines
11 KiB
Markdown
142 lines
11 KiB
Markdown
# 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
|