--- title: "Kanshō – Backend und API" version: "0.1" status: "Arbeitsstand" date: "2026-08-19" product_family: "Jinkendo" document_role: "Technical Chapter / Backend / API" parent_document: "technische_zielarchitektur.md" --- # Kanshō – Backend und API Kanonisches Home für FastAPI-Struktur, Router-Schnitt und API-Vertrag. Mitai-Referenz: `.claude/rules/ARCHITECTURE.md` §1, `backend/main.py`, `backend/routers/`. ## 1. Prinzipien Herkunft: Mitai-Architekturregeln, als Rahmen übernommen. ### 1.1 Ein Modul = ein Router Jedes fachliche Modul besitzt genau eine Router-Datei unter `backend/routers/`. Endpoints liegen nicht in `main.py` außer Health/Status. Neue Module = neue Datei, Registrierung via `app.include_router(..., prefix="/api")`. Rahmen-Module (erste erwartbare Dateien, keine vollständige Liste): - `auth` - `profiles` - später: Dialog, Memory, Journal-Handoff, Gateway-Audit-Metadaten, Admin-Diagnose Domänenrouter für Mitai-Tracking werden nicht angelegt. ### 1.2 API-First Jede vom UI benötigte Funktion ist zuerst ein Endpoint. Das Frontend spricht ausschließlich diese API (Mitai: `utils/api.js`). Keine fachliche Berechnung, kein Context-Building und kein Privacy-Demasking im Browser. ### 1.3 Fehlerformat ```text HTTPException → {"detail": "menschenlesbare Meldung"} ``` Keine parallelen Formate (`error`, `success: false`). Herkunft: Mitai. ### 1.4 Auth an der Grenze Geschützte Routen nutzen `Depends(require_auth)` bzw. `require_admin`. Ressourcenfilter immer über Session-`profile_id`. Siehe `auth_identity_and_roles.md`. ## 2. Schichten im Backend Bevorzugte innere Trennung, analog Mitai plus Kanshō-spezifische Gateway-Schicht: ```text routers/ HTTP, Validierung, Auth-Depends services/ fachliche Orchestrierung (später) privacy_gateway/ Minimierung, Pseudonymisierung, Egress, Validation data/ PostgreSQL-Zugriff ``` **Status: bevorzugte Richtung** für Ordnernamen. Kein Zwang, Mitai-Dateibaum 1:1 zu klonen, wenn die Trennlinie Router / Gateway / Persistenz klar bleibt. Privacy Gateway darf nicht umgangen werden, indem ein Router den Provider direkt aufruft. KI-Ausführung nur über die Prompt-Engine (`platform_extensibility.md`), Callback = Gateway. ## 3. Versionierung Mitai führt `backend/version.py` mit `APP_VERSION` und `MODULE_VERSIONS`. **Status: bevorzugte Richtung** – gleiches Muster für Kanshō, sobald Code existiert. Semantic Versioning. ## 4. Health und Betrieb Ein ungeschützter oder schwach geschützter Status-Endpoint für Deploy-Healthchecks analog Mitai `/api/auth/status` bzw. Health nach Container-Start. Konkreter Pfad: offen, Muster übernommen. ## 5. Entscheidungsstand | Thema | Stand | Status | |---|---|---| | FastAPI + ein Router je Modul | ja | entschieden | | API-First, keine Business-Logik im Frontend | ja | entschieden | | Fehlerkörper `{detail}` | ja | entschieden | | Direktaufruf externer LLMs aus Routern | verboten | entschieden | | OpenAPI als dokumentierter Vertrag | später | offen | | Interne service-Schicht vs. fette Router | service-Schicht | bevorzugte Richtung | ## 5.1 Implementierungsstand (MVP-Journal-API) **Status: Code vorhanden.** Router: `backend/routers/journal.py`, Prefix `/api/journal`. Zusammenhängende Umsetzung: `mvp_implementation.md`. Produkt-Endpunkte hinter Session-Auth, Isolation über `profile_id`: - Spaces: `GET/POST /spaces`, `GET/PATCH /spaces/{id}`, `POST /spaces/{id}/days`, `GET /spaces/{id}/entries`, `GET /spaces/{id}/trash` - Days: `GET /days/{id}` inkl. Conversations, Current Draft, Entries, `consolidation_offer` - Start: `GET /continuity` - Dialog: `POST /days/{id}/conversations` (erster Impuls), `POST /conversations/{id}/turn` (ein Modell-Call, bei Erzähler-Fortsetzung einmal Korrektur), `GET /conversations/{id}`, `DELETE /conversations/{id}` (Profil-isoliert; Nachrichten und Derived dieser Conversation gehen mit) - Status: `GET /egress-status` (Provider bereit / fail-closed) - Derselbe Zug auch unter `POST /api/dialogue/conversations/{id}/turn` - Admin-Antwort zusätzlich `decision` und `trace` (Testphase) - Generate: `POST /days/{id}/generate` nur explizit; optionale `conversation_ids`; `include_existing` nur nach ausdrücklicher UI-Auswahl; ohne Auswahl kein stilles Mergen; optionaler `generation_policy`-Snapshot und `remember_generation_policy` - Generation Policy: `GET /generation-settings` — profilbezogene Defaults der vier Journal-Ausgabeeinstellungen, nicht Writing Profile. Die Anweisungstexte kommen aus `generation_instruction_fragments`, nicht aus dem Nutzerregler. - **Additiv 2026-08-27:** Generate sendet `generation_selection` (`transformation_id`, `detail_id`, `voice_id`, `narrative_id`) und `remember_generation_selection` (Default false). `GET /generation-settings` liefert `selection`, aktive `options` ohne Promptanweisung und System-`defaults`. Unbekannte oder nicht aktive IDs: 400 `invalid_generation_selection`. Der gespeicherte Entwurf enthält `generation_snapshot` und `generation_summary`. - **Additiv 2026-08-28:** Ungültige `generation_selection` bricht vor Detect- und Generate-Provideraufrufen ab. Preview nutzt denselben `compile_selection`-Pfad. Katalog-Publish/Default ändert gespeicherte Profilauswahl nicht. - **Additiv 2026-08-29:** Voice-Ausprägungen tragen `style_context` (`include_core`, `include_facet`, `include_traits`, `include_style_examples`). `GET /generation-settings` liefert diese Flags an den Journal-Tag, ohne Promptanweisung. Admin `PUT/POST` versioniert Anweisung und Stilkontext gemeinsam. Kein zusätzlicher Generate-Parameter. - **Additiv 2026-08-29:** Snapshot und Trace führen `cloned_from` der Voice-Ausprägung. Legacy-Vergleichs-IDs sind normale aktive Optionen, nicht Default. ## 5.3 Implementierungsstand (Generierungsrichtlinien) **Status: Code vorhanden.** Admin-Session. Purpose zunächst `journal_generate`. - `GET /api/admin/generation-instructions/{purpose}` - `PUT /api/admin/generation-instructions/{purpose}` — vollständiger Satz, transaktional, ungültige Teilstände werden abgelehnt - `POST /api/admin/generation-instructions/{purpose}/reset` - `POST /api/admin/generation-instructions/{purpose}/preview` — lokal, kein Provider, keine persönlichen Quellen **Additiv 2026-08-27 (benannte Ausprägungen):** Der numerische Katalog-PUT entfällt. Übersicht ohne Promptkörper. Detail, Klon, Draft-Update, Veröffentlichen, Archivieren, Standard und Löschen nur für unverwendete Drafts: - `GET /api/admin/generation-instructions/{purpose}` — Kartenfelder, keine Anweisungstexte - `GET /api/admin/generation-instructions/{purpose}/{id}` - `POST /api/admin/generation-instructions/{purpose}` — neuer Draft - `PUT /api/admin/generation-instructions/{purpose}/{id}` — nur Draft - `POST .../{id}/clone|publish|archive|default` - `DELETE .../{id}` — nur ungenutzter Draft - `POST .../reset` — neue Seed-Drafts, keine Überschreibung historischer Varianten - `POST .../preview` — lokal gerenderter Prompt, Dummy-Kontext, kein Provider UI: `/admin/generation` mit getrennten Dimensionen, Karten und einem Editor nach „Öffnen“. Source Modes eigener Abschnitt. Keine Seed-Dateipfade in der normalen Adminbeschreibung. **Additiv 2026-08-27 (kein Source Mode):** Preview und Admin-UI verwalten nur die vier Gestaltungsdimensionen. `source_mode` ist kein Request-Feld mehr. Ein Custom-Prompt mit veraltetem Platzhalter: 409 `prompt_contract_incompatible`, kein Provideraufruf, kein Draft. - Entries: `POST /entries`, `GET /entries/{id}`, Versionen, Versions-Restore, Soft-Delete, `POST /entries/{id}/undelete`, `POST /entries/{id}/purge`. Speichern aus einem Entwurf mit `entry_id` + `origin=accepted_draft` legt eine neue Version desselben Entry an. - Media: Upload/GET/DELETE; Bilder und einzelne Videos; Position/Unterschrift im Entry-Body als Markdown `![…](kansho-media:)` - Tagesstichpunkte: `PATCH /days/{id}/scratch` — lokal, nicht Context-Builder - Writing Profile: `GET /writing-profile`, Import (optional `occurred_at`, `context_hint`), Korpus `GET/POST /writing-profile/corpus`, Rebuild, `PATCH` Governance, Facet-Edit/Lock, Trait-Edit `PATCH /writing-profile/traits/{slug}`, Vorschläge annehmen/verwerfen. Brief ist eine abgeleitete Sicht aus Traits und zeitgestempelten Quellen. Media-Token werden vor dem Brief entfernt. JSON-Export/Restore: `GET /writing-profile/export`, `POST /writing-profile/restore`. Review: `GET /writing-profile/review`, `POST /writing-profile/review/paste`, `POST /writing-profile/review/api`, `POST /writing-profile/review/import`, `POST /writing-profile/review/accept`, `POST /writing-profile/review/reject`. Initial Build: `POST /writing-profile/initial-build/paste`, `POST /writing-profile/initial-build/api`. API und Copy/Paste nutzen denselben Vertrag `kansho.profile_analysis_package` / `kansho.profile_analysis_result` (`mode` `initial_build`\|`review`, `corpus`, `existing_before_new`, `expected_result`). Legacy-`kind` `kansho.profile_review_*` bleibt lesbar. Import erzeugt ein Proposal, kein Current Profile; Übernahme erst nach Accept. Der Current Brief ist kein Import-/Exportformat. **Additiv 2026-08-29:** Fehler von Review/Initial-Build-API tragen `cost_report` und den Providergrund (`code`, `message`, optional `http_status` / `provider_message`); die generische Client-Meldung „Anfrage fehlgeschlagen“ ist nicht mehr der einzige sichtbare Text. - Interaction Profile: `GET /interaction-profile`, `PATCH` Governance und Präferenzen, Vorschläge annehmen/verwerfen. Keine Ableitung aus Nicht-Widersprechen. Der Slot liegt unter `/api/journal` nur als Settings-Nachbar, nicht als Journal-Artefakt. JSON-Export/Restore: `GET /interaction-profile/export`, `POST /interaction-profile/restore`. - Beide Profile: `GET /profiles/export`, `POST /profiles/restore` (`kind: kansho.profiles`). Journal-Backup ist `local_backup.py`, nicht dieser Profil-Export. `/api/dialogue/*` bleibt Low-Level-Source und Admin-Diagnose, nicht die Produkt-IA. Context Builder und Retrieval liegen nicht im Frontend. ## 5.2 Implementierungsstand (Provider-Admin) **Status: Code vorhanden.** `GET /api/admin/providers`, `PUT /api/admin/providers/{generate|detect}`. Session-Admin. Body darf einen write-only `key` enthalten; die Antwort enthält ihn nicht. Persistenz: URL/Modell/Flags in `provider_settings`, Key in `backend/.env`. **Additiv 2026-09-08:** `llm_profiles`. `POST /api/admin/providers/{role}/activate`, CRUD unter `/api/admin/llm-profiles`. Keys weiterhin nicht in der DB. ## 5.4 Implementierungsstand (Debug-Persistenz) **Additiv 2026-08-28.** Session-Admin. Default aus. Nur eigenes Profil in Liste/Export. - `GET/PUT /api/admin/debug` — Schalter `persist_enabled` - `GET /api/admin/debug/tree` — Space / Tag / Gespräche / Schritte - `GET /api/admin/debug/latest?journal_day_id=&purpose=` — letzter passender Lauf - `GET /api/admin/debug/runs`, `GET/DELETE /api/admin/debug/runs/{id}`, `POST /api/admin/debug/runs/clear` - `GET /api/admin/debug/export?format=json|markdown` — optional `conversation_id`, `journal_day_id`, `purpose`. `kansho.debug_export` v2 UI: `/admin/debug` (Baum). Im Dialog bei angeschalteter Persistenz: Gesprächs-JSON. Am Journal-Entwurf: sichtbare Testspur und Entwurf-JSON. Kein Gateway-Bypass. Mapping-Labels nicht in der Payload. Keine Live-Testspur unter der Konversation. ## 6. Offene Fragen 1. Internes API für mindnet/Kairo: synchron REST vs. Events? → `integrations_technical.md` 2. Idempotenz-Keys für Offline-Sync-Queue? → `memory_storage_and_offline.md` 3. Rate Limits außer Auth: welche Dialog-Endpoints? ## 7. Querverweise - Technisch: `auth_identity_and_roles.md`, `privacy_gateway.md`, `runtime_and_deploy.md` - Mitai: `ARCHITECTURE.md` §1, `INTERNAL_API_REFERENCE.md`