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>
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):
- Core-Branch
admin-platform-integration(Vertrag, Bundle-Loader,/health) reviewen und mergen/deployen. Erst danachcore_supports_bundleaktivieren (Einstellungen) – bis dahin sind Deploys von Tenants mit Quelle „Admin“ gesperrt. DEPLOY_LIVE=true+MITTWALD_API_TOKEN+ Projekt-/Stack-ID, sobald echte Deployments gewollt sind.- Cortecs-Key hinterlegen → Agent-Modell auswählen (Einstellungen → „Modelle von Cortecs laden“) und gegen Live testen.
- 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_KEYsetzen; mit „Verbindung testen“ (Einstellungen) prüfen; zum SchreibenOIDC_SYNC_LIVE=true. - Datenbank-Migrationen (Alembic) vor dem ersten produktiven Schemawechsel; Tabellen entstehen derzeit per
create_all.
Starten
# 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_IMAGEper Env/API oderlatestmit „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) = SecretADMIN_RELEASE_TOKEN(Core-CI) + VariableADMIN_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
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
- Core:
TENANT_SOURCE=git|admin+ Bundle-Loader mit Cache (Phase 1) - OIDC-Login der Admin-Plattform über login.onyva.dev
- Spike: Cortecs-Modell für den Agent (Tool-Calling, Streaming, Bild-Input) →
agent_mock.pyersetzen - Echte Deploys (Mittwald) aus
deploy_sim.py, Rollout-Abbruch bei Fehler, Health-Check - Hook-Code-Freigabe härten (Sandbox ohne Netz), Rollen