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>
This commit is contained in:
141
README.md
Normal file
141
README.md
Normal file
@@ -0,0 +1,141 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user