Kansho/docs/architecture/technical/privacy_gateway.md
2026-08-26 10:42:20 +02:00

14 KiB
Raw Blame History

title version status date product_family document_role parent_document
Kanshō Privacy Gateway und AI-Egress 0.1 Arbeitsstand 2026-08-19 Jinkendo Technical Chapter / Privacy Gateway / Egress technische_zielarchitektur.md

Kanshō Privacy Gateway und AI-Egress

Technische Abbildung der fachlichen Guardrails. Kanonisches Fachhome: ../functional/guardrails.md (inkl. Root Cause §2, Datenklassen §5, Gateway §6, Entscheidungsstand §22). Dieses Kapitel dupliziert die Herleitung nicht.

Rang: Datenschutz und Guardrails sind im Fachkonzept grundlegend und entschieden. Sie stehen nicht auf derselben Ebene wie unfertige Dialog- oder MVP-Schnitte. Offene Punkte betreffen Verfahren und rechtliche Ausprägung (Entity Detection, Encryption at rest, DSFA), nicht die Invariante selbst.

1. Invariante

Identität bleibt lokal. Externe Intelligenz erhält nur den für die Aufgabe notwendigen, minimierten und soweit sinnvoll pseudonymisierten Kontext.

Herkunft: guardrails.md §2.4; querschnittlich in der fachlichen Zielarchitektur. Providerunabhängig: OpenRouter ist Kandidat, kein Ersatz für das lokale Gateway. OpenRouter-eigene Guardrail-Features ergänzen höchstens, sie ersetzen die Kanshō-Schicht nicht.

Pseudonymisierung ist keine Anonymisierung. Kanshō behauptet keine vollständige Anonymität nach außen.

Persönlicher Kontext wird nie direkt aus UI, Dialog-Router, Prompt-Engine oder Admin-Preview an OpenRouter, Provider oder Tools gesendet. Die Engine darf den Provider nur über den Gateway-Callback erreichen (platform_extensibility.md).

1.1 Guardrail-Vorrang

Fachlich §19: Guardrails haben Vorrang vor Modellqualität, Kosten, Latenz, Komfort und automatischem Provider-Fallback. Eine Anfrage wird abgelehnt oder degradiert, wenn die Policy nicht erfüllbar ist. Administrierbare Prompts, Workflows oder Feature-Flags dürfen das Gateway nicht abschalten.

2. Trust Zones

Zone Inhalt Egress
Local Trusted Zone Identity Mapping, Klarnamen, Secrets, volle Primärquellen, Obsidian-Pfade kein Roh-Egress
External AI Zone External Model Context (Klasse B) nur nach Policy-Check, ZDR
Tool-/Plugin-Zone Suche, Plugins, Speech eigene Policy, nicht implizit mit LLM-Freigabe

3. Datenklassen

  • A Local Only: Mapping, Secrets, vollständige Identität, interne IDs soweit identifizierend. Verlassen die Trusted Zone nicht.
  • B Pseudonymized AI Context: Normalfall persönlicher Dialoge. Stabile, nicht sprechende Platzhalter ([[SELF]], [[PERSON:PARTNER]]). Minimierung zusätzlich zur Maskierung.
  • C Low-Identity: generische, nicht-personalisierte Inhalte; schwächere Transformation zulässig, eigene Policy bleibt nötig.

Quasi-Identifikatoren (Beruf+Ort+Familie) sind fachlich erkannt; die Bewertungsmethode ist offen, der Gateway-Auftrag „nicht nur Klarnamen“ gilt trotzdem.

4. Pflichtpipeline

Jeder persönliche Modellaufruf durchläuft lokal:

  1. Context Minimization
  2. Entity Detection
  3. Pseudonymization
  4. Policy Check
  5. Provider/Endpoint Eligibility (ZDR, kein Training, kein Prompt-Logging)
  6. Egress Validation
  7. Response Validation
  8. Local Rehydration / Demasking
  9. Audit Metadata (ohne volle Prompts)
  10. Fail Closed

Fail Closed: kein stillschweigender Fallback auf einen weniger geschützten Provider.

LLM-Egress und Tool-/Web-/Transkriptions-Egress sind getrennte Policies. Ein für ZDR-LLM freigegebener Kontext darf nicht automatisch an Web Search oder Speech-Cloud.

5. Internal vs. External Context

Der Context Builder darf intern mit Klaridentität arbeiten. Vor dem Socket nach außen existiert eine zweite Repräsentation ohne Mapping-Tabelle.

Demasking ausschließlich in der Trusted Zone, nach Validierung (Platzhalter erhalten, keine erratenen Klarnamen, keine Policy-Verstöße).

6. Audit

Speichern: Request-ID, Zeit, Policy, Provider-/Modellkennung, ZDR-/Region-Status, Typ maskierter Entitäten, Prüf- und Fallback-Status.

Nicht speichern: volle Prompts, volle Antworten, echte Namen, Mapping.

Admin-Sichtbarkeit der Mapping-Tabelle nur für besonders berechtigte Rollen (admin_diagnostics.md).

7. Beziehung zu anderen Kapiteln

  • Resurfacing darf keinen breiteren Egress rechtfertigen als der aktuelle Task braucht.
  • Re-Grounding läuft lokal und erzeugt danach erneut einen minimierten External Context.
  • Voice/Transkription: eigener Egress-Typ (voice_and_media.md).
  • Lived Experience / Re-Grounding: volle Primärquellen bleiben lokal; externes Modell bekommt erneut nur Klasse B.
  • Prompt-Engine: Preview ohne LLM ist erlaubt; Debug-Ausgaben an den Admin ohne Klasse-A-Klartext und ohne Mapping.

8. Was Phase H noch offen lässt

Interview H1 (Encryption at rest, vollständiges Löschen, Portabilität, DSFA bei späterem Mehrbenutzerbetrieb) ergänzt diese Invariante, ersetzt sie nicht. Privacy Profiles (Strict / Balanced / Low-Identity) sind fachlich perspektivisch; Default muss die Invariante erfüllen.

9. Entscheidungsstand

Thema Stand Status
Privacy Gateway vor persönlichem Egress zwingend, nicht abschaltbar entschieden
Guardrails vor Qualität/Kosten/Latenz ja entschieden (fachlich)
Providerunabhängige Guardrails ja entschieden (fachlich)
ZDR / kein Training / kein Prompt-Log persönliche Kontexte entschieden bzw. bevorzugte verbindliche Policy
Fail Closed ja bevorzugte Richtung
Keine Anonymitätsbehauptung Pseudonymisierung entschieden (fachlich)
OpenRouter Kandidat, nicht Gateway-Ersatz entschieden
EU-Routing bevorzugt offen in Ausprägung
Encryption at rest, Delete, DSFA Phase H offen
Implementierung Entity Detection Wörterbuch + optionaler Detect-Provider; Qualität offen bevorzugte Richtung im Code, nicht abgeschlossen

9.1 Implementierungsstand (MVP-Egress)

Status: Code vorhanden. Zwei getrennt konfigurierbare Provider. Fit-Gap und Guardrail-Abstand (Detect-Testphase, Security Layer): ../functional/mvp_stand_und_abgleich.md.

backend/privacy_gateway.py ist die einzige Stelle für den generativen Egress. Reihenfolge: Policy-Check → lokale Wörterbuch-Maskierung mit Wortgrenzen (kein Teilstring, kein ich in nicht) → Entity Detection nur auf user-Zeilen (Muster + Wörterbuch, in der Testphase zusätzlich OpenRouter-Detect) → Remaskierung: im Dialogzug nur user:/assistant:-Zeilen, beim Journalentwurf der ganze Prompt → ein Generative-Call nur mit Platzhaltern → Antwortprüfung → Demaskierung.

Detect-Vorschläge werden nur gespeichert, wenn das Wort im user-Text steht, kein Funktions- oder Stoffwort ist und nicht zum Promptvokabular gehört. mit plus Allerweltsnomen (mit Getöse) ist kein Personenname; mit Anna bleibt einer. Bereits gespeicherte unzulässige Labels werden beim nächsten Detect entfernt.

Intern in der Testspur ist keine dritte Anfrage, sondern die lokale Klartext-Vorlage desselben Dialogzugs. Detect und Dialogzug bleiben getrennte Calls.

Pronomen werden nicht als eigene Detect-Treffer gespeichert. Nach der Namensmaskierung bindet eine lokale Schicht im Dialogzug in user:-Zeilen er/sie/ihm/ihr an das letzte kompatible Person-Token, damit das Sprachmodell dieselbe Person sieht. Beim Journalentwurf bleiben diese Pronomen ungebunden, sonst macht die Demaskierung aus jedem Bezug den Klarnamen. Anrede-ihr und pluralisches sie bleiben ungebunden. Dieselbe Person behält ein Token; PERSON:KURZ ist kein Mapping. Ein Personenname, der auch eine Sache ist, wird bei Verwandtschaftswort maskiert und bei Verben wie essen/kochen/kaufen davor oder danach als Sache stehen gelassen (Sushi essen, esse Sushi).

Zwei Provider, getrennt konfigurierbar (providers.py, Admin Schnittstellen):

Drei Schichten, zwei technische Endpunkte (provider_settings-Rollen generate / detect sind Implementierungsnamen):

  • Maskierung (Rolle detect): nur schützenswerte Muster. Keine Operation, kein Impuls, kein Journaltext. Testphase: OpenRouter-Detect auf user-Zeilen. Produktivziel: Muster + lokales Modell. Wiederverwendbar für spätere Dialogtypen.
  • Sprachmodell (Rolle generate): maskierter Kontext. Dient zwei fachlichen Calls: Dialogzug (Operation + Impuls) und Journalentwurf (explizit). Das größere Modell gehört hierhin, nicht an die Maskierung.

URL, Modell und Policy liegen in provider_settings (Seed config/providers.seed.json, Admin GET/PUT /api/admin/providers). Keys nur in backend/.env, nie in der DB und nie in der API-Antwort. Env überschreibt Admin-Werte, wenn gesetzt.

Lokale URLs (localhost / 127.0.0.1) brauchen keine ZDR-Flags. Die Mapping-Tabelle bleibt Klasse A und lokal; neue Treffer werden dort persistiert, damit der nächste Prompt dieselben Platzhalter per Muster nutzen kann, ohne Detection-Call.

Klasse A verlässt die Zone nicht. Mapping wird nicht mitgeschickt. Kein stiller Fallback auf einen unsicheren Generate-Provider.

Prompts für Dialog, Journal-Rekonstruktion, Journal-Narration, Detection und Profile-Review liegen in der DB (mvp.dialogue_turn, mvp.journal_reconstruct, mvp.journal_generate, mvp.entity_detect, mvp.profile_review), nicht im Anwendungscode.

mvp.dialogue_turn bleibt ein generativer Call (Foundation: ein Hauptcall pro Turn). Ton, Tiefe und Erzählmodus kommen lokal als register_hint plus Writing-Profile-Hinweis in denselben Prompt, nicht als zweites Modell. JSON {operation, impulse}. Persistiert wird nur impulse.

Admin-Testspur (trace) zeigt intern gerenderten Prompt und maskierten Egress. Das ist ein bewusster Test-Einblick, nicht der fachliche Default aus §2 (Diagnose ohne volle Prompts). Mapping-Tabelle bleibt ausgeschlossen.

9.2 Offene Implementierungspunkte (Security Layer)

Status: bewusst offen. Der MVP-Pfad ist durchgängig, der fachliche Security Layer aus ../functional/guardrails.md ist nicht vollständig implementiert. Für Testdaten akzeptabel; nicht als abgeschlossen führen.

Noch offen im Code:

  1. Inhaltliche Context Minimization. Es geht der zusammengebaute Dialog, nur durch eine Zeichenkappe begrenzt. Minimum-sufficient-context nach Aufgabe fehlt.
  2. Robuste Entity Detection. Wörterbuch plus Muster plus Detect-Call. Testphase: externes kleines Modell über OpenRouter. Ziel: lokales Ollama. Keine belastbare Erkennung von Quasi-Identifikatoren (Beruf + Ort + Familie, seltene Konstellationen).
  3. Response Validation. Minimale Prüfung: Klartext-Identität in der Rohantwort vor Demask wird blockiert. Dieselbe Homonym-Regel wie beim Maskieren (Speise vs. Person). Dialog-Egress maskiert den gesamten gerenderten Prompt (Opening-Hinweis, Space-Ausschnitte, Titel), nicht nur user:-Zeilen; Detect sieht denselben gerenderten Prompt. Bei einem Leak gibt es einen Korrekturversuch. Der Dialogzug fällt lokal zurück statt leer zu bleiben. Journal-Generate übernimmt ungültiges oder leckendes Stufe-1-JSON nicht; lokal vollständige Nutzerquellen, Stufe 2 analog mit lokalem Entwurf statt der leckenden Antwort. Keine Quasi-Identifikatoren, keine vollständige Platzhalterprüfung, keine inhaltliche Minimierung.
  4. Audit ohne Prompt-Inhalt. Es gibt keine persistente Audit-Spur (Request-ID, Policy, Provider, ZDR-Status, maskierte Entitätstypen) ohne volle Prompts oder Mapping.
  5. Mapping-Härtung. identity_mappings ist lokal, aber unverschlüsselt; keine Pflege-UI; keine Verschlüsselung at rest.
  6. Detect-Klartext in der Testphase. Der Detect-Provider darf Klartext sehen. Bei externem Detect (OpenRouter) ist das ein bewusster Übergang, nicht der Zielpfad. Produktiv nur lokales Modell.
  7. Weitere Egress-Klassen. Tool-, Web- und Speech-Egress bleiben getrennt und ungebaut.
  8. Löschen, Export, DSFA. Journal-Entries haben Soft Delete, Papierkorb und explizites Purge. Kein vollständiges Account-Löschen, keine Portabilität als öffentliches Produkt, keine Datenschutz-Folgenabschätzung.

Diese Punkte dürfen nicht stillschweigend als erledigt gelten, nur weil Generate bereits maskiert.

9.3 Journal-Budget und Context Compression (2026-08-25)

Additiv zu §9.1. Fachliche Trennung Inhalt/Stil: ../functional/writing_profile_and_journaling.md §16. Technische Umsetzung: mvp_implementation.md §16.

Persönlicher Journal-Egress bleibt ausschließlich über dieses Gateway. Zwei Zwecke: journal_reconstruct dann journal_generate. Kein stiller Fallback auf ein anderes Modell. OpenRouter-Context-Compression ist für Journal-Calls ausdrücklich deaktiviert (plugins.id=context-compression, enabled=false). Diese Plugin-Felder gehen nicht an lokale oder fremde OpenAI-kompatible Endpunkte.

Journal-Prompts werden nicht mehr durch MAX_EGRESS_CHARS still in der Mitte abgeschnitten. Passt der Tagesdialog nicht ins Budget, bricht Kanshō mit prompt_budget_exceeded ab. Stufe 2 erhält den vollständigen lokal rehydrierten Nutzerinhalt als VerifiedArtifact (provenance_verification.md); bei Übergröße ebenfalls Abbruch, kein stilles Kürzen.

Usage-Metadaten (Tokens, Kosten, Fenster, Reserve, Marge, Compression-Status) liegen in der Admin-Testspur trace.budget bzw. trace.stages. Journal-Generate setzt beide Stufen aus den Rückgabewerten der jeweiligen Gateway-Calls zusammen; es gibt keine globale debug_history und keinen globalen Promptspeicher. Vollständige Prompts und Antworten höchstens im direkten Admin-Response des aktuellen Requests. Mapping bleibt ausgeschlossen.

10. Offene Fragen

Übernommen aus guardrails.md §21, hier nicht vorentschieden: Entitätstypen, Quasi-Identifikatoren, Pseudonym-Stabilität, Mehrnutzer-Trennung der Mappings, Verschlüsselung der Mapping-Tabelle, rechtliche DSFA bei Mehrbenutzer-Produktbetrieb.

11. Querverweise

  • Fachlich: ../functional/guardrails.md
  • Technisch: ai_architecture.md, security.md, voice_and_media.md, platform_extensibility.md