TGV · Master-Konzept · First-Trias · 11 Sektionen + §3a · Stand 2026-09-19
„Wie geheim und geschützt sind meine Daten?" — die Frage hat zwei Antworten, die TGV bewusst trennt: WER sieht es (Governance: Rollen × Vertraulichkeits-Stufe, §2–§4) und WIE ist es gesichert (Schutz-Mechanik: RLS-Realität, Guards, EU-Stack, §5–§6 + §11). Diese Seite führt die Tower-erarbeiteten Konzepte zusammen — entschieden wird in den SSOT-Dokumenten (unten verlinkt). Wie TGV VIELE Kunden trägt (Hierarchie · Topologie · M1–M6 · Onboarding · White-Label) steht im Schwester-Konzept 🏢 Multi-Mandant.
Ein Zugriff muss ALLE Schichten von außen nach innen passieren — jede Schicht ist an einer anderen Ebene strukturell verriegelt. Der interaktive Graph (SYSTEM#01-Baustein, gleiche Karten-Sprache wie der Architektur-Graph) hat zwei Modi: 🛡️ Schutz-Grenzen (Defense-in-Depth) und 🇪🇺 EU-Stack (Datenfluss mit ehrlicher EU/US-Färbung, §11) — jeder Knoten öffnet per Klick sein Detail-Panel mit Regel-Text + SSOT-Verweis:
| Rolle | Wer ist das (Beispiel JC) | Reichweite |
|---|---|---|
| super_admin | TGV-Betreiber (Convis/Kivisai) — im Verwaltet-Modus der Haftungsträger | technischer Voll-Zugriff, aber protokolliert; im Souverän-Modus zurückgezogen; Break-Glass-Regel s.u. |
| mandant_admin | JC für seinen Mandanten | alle Entitäten des Mandanten; setzt Rollen + Status, führt Löschungen aus |
| entity_member | ein C3-Mitarbeiter | NUR seine Entität (C3), Status-gefiltert; sieht nie Privat/C4 |
| viewer | Nur-Lese (z.B. Steuerberaterin auf ein Dossier) | nur Ansicht, keine Beträge bei vertraulich+, kein Export |
| extern | Öffentlichkeit | nur Status „öffentlich" |
Quelle: 01_ITERATIONS/CONTROL_TOWER_HANDOFF/JC_ENTSCHEID_TGV_DATENSCHUTZ_DATENHOHEIT_2026-10-04.md (JC, konsolidiert aus Workshop 03.10. und Klärungsrunde) · Kurzform JE-186 bis JE-199 · Register #3550. Arbeitsprinzip: Recht → Produktregel → technische Umsetzung. Dieser Abschnitt trennt strikt IST (gemessen 04.10.2026, live) und SOLL (JC-Entscheid, Paket in Klammern). ⛔ Die Live-Policies und 08_OPS/chat_engine/scope_gate.py bleiben die technische Wahrheit über den IST-Stand.
| Gegenstand | IST (gemessen 04.10.2026) | SOLL (JC 04.10.2026) |
|---|---|---|
| mandant_admin | Policy-Zweig is_mandant_admin(auth.uid(), mandant_id) öffnet jede Stufe inkl. streng_vertraulich und NULL (documents · prf_contracts · rag_chunks); im Chat hebt app.py _owner_conf_bypass auf streng nur, wenn der Verwalter Mitglied des Instanz-Mandanten ist (descriptor.scope_ref, app.py:3871) — alle sechs Chat-Instanzen zeigen auf jcbinetti, ein c3-Verwalter läuft im Chat mit Decke intern (VERTRAULICH#01 #3560, 04.10.; Korrektur #3565). 2 Personen live. | Verwalter des Datenraums und der Vertragsbeziehung, nicht Eigentümer jedes Inhalts (JE-190): verwaltet User, Rollen, Plan, Entitäten, Projekte, Retention — kein automatisches Leserecht auf streng vertraulichen Inhalt (Paket 2). |
| Owner-Bypass | IST-Definition: der serverseitig als Owner/Admin erkannte Nutzer wird auf der Vertraulichkeitsachse bis streng_vertraulich geöffnet; Mandant und Entität werden nicht aufgeweitet. Wirkorte: app.py _owner_conf_bypass → scope_gate.build_scope(owner_bypass=) → base_conf = streng. | Kein Zielmodell (JE-191): Name und Logik werden nicht übernommen; Ersatz ist die explizite streng-Berechtigung (JE-192). Migration vom Bypass zum Zielmodell = offener Punkt 10. Bis dahin unverändert live. JE-158 (Admin-Rahmen) bleibt davon unberührt. |
| streng_vertraulich | „nur du“ = Ersteller/Hochlader plus Verwalter des Mandanten plus super_admin. Einzelfreigabe (zugriff_freigaben, hat_freigabe()) existiert, reicht nur bis vertraulich (app.py:6151), 0 Zeilen live. | Ersteller + ausdrücklich Berechtigte (JE-192, Variante B). Trägt besonders geschützte Unternehmenskreise (Whistleblowing · Betriebsarzt · Ombudsperson · HR) — Unternehmensdaten, keine Privatdaten. Entität = „wo liegt es“, Stufe = „wer darin darf es sehen“ (Paket 2). |
| Ersteller-Achse | nur documents.hochgeladen_von (Spalte live seit 03.10., #2139); Mail-Eingang ohne Hochlader; prf_contracts und rag_chunks folgen dem Quell-Dokument. | Ersteller / created_by_user_id: hochgeladen · erstellt · erzeugt · aus KAI gespeichert (JE-189). Erstellerbeziehung ≠ Eigentum am Unternehmensinhalt (Paket 2). Entschieden (JE-200, #3561): ungestuft bleibt streng; Mail-Eingang bekommt keinen künstlichen Ersteller — Adressat (received_for_user_id / heute documents.mail_to) und Intake-/Triage-Rolle erhalten ein befristetes Intake-Recht nur zum Einstufen (NUXING: als zugriff_freigaben-Zeile mit grund=intake, endet mit der Klassifizierung); der Verwalter ist kein Stellvertreter-Ersteller. |
| super_admin | is_super_admin(auth.uid()) ist der erste OR-Zweig jeder der drei Policies — unbedingt, ohne Schalter; 1 Person (JC), 6 Zeilen. Im Dienst: is_super_admin ⇒ keine Decke. | DEV Override nur in der Entwicklungs-/Pilotphase auf ausdrücklich bestimmte Testmandanten (Testpersonen informiert) — heißt nicht Break Glass. Nach Marktreife abschaltbar und abgeschaltet: Superadmin = technische Betreiber-/Ops-Rolle, kein regulärer Inhaltsleser (JE-195, Paket 2). |
| Supportzugriff | kein eigener Mechanismus; läuft heute über die super_admin-Rolle. | nur mit ① konkretem Supportfall · ② Freigabe · ③ begrenztem Scope · ④ begrenzter Zeit · ⑤ Audit (JE-195, Paket 2). |
| Break Glass | tgv_log_break_glass() existiert live (Mig 220); 0 Aufrufer im Dienst (08_OPS/**/*.py), 0 Break-Glass-Zeilen in audit_events (680 Zeilen gesamt). | bleibt in Produktion als außergewöhnlicher Notfallmechanismus (Security-Incident · Wiederherstellung · Kunde kann nicht freigeben · technische Notlage). Pflichtfelder: Operator · Mandant · Grund · Beginn · Ende · Scope · Ressourcen/Aktionen · Audit (JE-195, Paket 2). |
| Audit des Betreiberzugriffs | audit_events trägt Pipeline-Ereignisse, keine Betreiberzugriffe; privacy_erasure_log 0 Zeilen. | Jeder Betreiberzugriff auf Kundendaten wird protokolliert. Sicht: Kivisai vollständig · Mandantenadmin die Zugriffe auf seinen Mandanten · Privatnutzer die auf seinen Privatmandanten. Offen: Aufbewahrungsdauer, Detailtiefe, Business/Privat-Unterschiede, Admin-Sicht (JC §18). |
| KAI-Sicht | KAI liest als service_role; die Decke kommt aus _user_scope + build_scope (nur-verengen, fail-closed); Owner-Bypass hebt Verwalter auf streng; Persona nur bei genau einer Entität im Scope. Dokument- und RAG-Pfad weichen voneinander ab (DSGVO_ROLLEN_UND_PFLICHTEN_2026-10-03.md ②/③). | KAI = Assistent des Users, kein privilegierter Datenbanknutzer. KAI-Sicht = User-Sicht ∩ aktueller Scope ∩ Session-Verengung; nie erweitern; das LLM bestimmt die Berechtigung nie. Second Brain bleibt scopegebunden (JE-193). Pfad-Unterschiede sind technische Lücken, kein Rollenmodell (Paket 1 messen, Paket 2 schließen). |
| Rollenmodell | CHECK user_mandant_roles.rolle = super_admin · mandant_admin · viewer · buchhalter; Beziehung und rechtliche Rolle haben keine Spalte. | Drei Ebenen, nie ein Feld (JE-194): technische Rolle (admin · editor · viewer · accountant · contributor) · Beziehung (employee · contractor · tax_adviser · family_member) · rechtliche Rolle (Verantwortlicher · Auftragsverarbeiter · gemeinsam Verantwortliche). Kein Rollenwildwuchs; Collaboration über Scope + Rolle + Vertraulichkeit, keine Sonderarchitektur. |
v1.1-Kern (relational): Die Stufe sagt, wer außer dir es sieht — NIE, ob du es nutzen darfst. Dir gehören deine Daten; deine KI nutzt sie alle, auch „streng" (Owner-Bypass). Die Stufe steuert nur, was bei ANDEREN ankommt.
intern (Buchhaltung MUSS prüfen), als Privat-Doc streng. Selbstständige = ein Mandant, mehrere Entitäten („2 Mützen"); Re-Triage verschiebt nur die FREMD-Sichtbarkeit, nie deine eigene Nutzung. · Fail-closed: unbekannter Status = wie streng behandeln — im Code real: rag_conf_rank(NULL) = 6 (Mig 241, live gemessen 19.09.; Mig 221 hatte 3). · RAG-Opt-out („auch meine KI nie") = separater per-Doc-Schalter, nicht Stufe 5.documents ist heute eine Lücke (nur die Chunks sind fail-closed) → wird Teil der ORCH-Einstufungs-Pipeline oben (Regeln a–d).Dieser Abschnitt zeigt den Kanon, er ersetzt ihn nicht: Quelle bleibt KONZEPT_VERTRAULICHKEIT_GEHEIMHALTUNG.md (§2, §2a, §2c, §4). Jede Zeile trägt ihren Stand: live · gebaut, Deploy offen · offen / JC · Lücke.
Die Entität ist die Basis-Privatsphäre (wer nicht in der C3 ist, sieht keinen C3-Beleg); die Stufe verschärft nur innerhalb. „öffentlich" heißt weltöffentlich. Beispiel von JC (19.09., über TOWER#57): JC ist Verwalter der C3, Marie Mitarbeiterin der C3, dazu die Buchhalterin der C3.
| Beleg | Stufe | JC (Verwalter) | Marie (Mitarbeiterin C3) | Buchhalterin C3 (Kreis, E3) | Wer sonst |
|---|---|---|---|---|---|
| Stromrechnung der C3 | 🔵 intern | ✓ | ✓ | ✓ mit Beträgen | niemand außerhalb der C3 |
| Werkvertrag mit Sonderkondition | 🟡 vertraulich | ✓ | ✗ | ✓ mit Beträgen | Einzelfreigabe möglich (②) |
| Gehaltsabrechnung von Marie | 🟡 vertraulich | ✓ | ✗ (in der C3 nicht; ihre eigene Kopie wäre ihre Privat-Mütze, ③) | ✓ mit Beträgen | — |
| Arztbericht (Entität Privat) | 🔴 streng | ✓ (Eigentümer) | ✗ | ✗ | nur der Eigentümer — auch seine KI nutzt ihn |
| AGB der C3 | 🟢 öffentlich | ✓ | ✓ | ✓ | alle, auch außerhalb von TGV |
intern — Marie sieht Zeile 1 und 5.| Form | Beispiel | Was sie sieht | Baustein | Stand |
|---|---|---|---|---|
| Kreis: Buchhalter je Entität | die Buchhalterin der C3 | heute: alles der Entität C3 bis vertraulich, mit Beträgen; nie streng; kein Schreiben. Die Beschränkung auf Belege und Verträge ist offen (⑩, Register #2150) — der Filter ist gebaut und liegt bereit (#2837) | user_mandant_roles mit scope_type='entity' (vorhanden, bisher ungenutzt) | Datenseite in Bau |
| Kreis: Projekt-Mitglied | Christian Hauf als externer Projektpartner | nur Belege seines Projekts; Projektrolle owner/editor bis vertraulich, commentator/viewer bis intern | user_project_roles (vorhanden) | Datenseite in Bau |
| Einzelfreigabe | ein Gutachter bekommt einen einzelnen Vertrag bis zum 30.11. | genau diesen Beleg / Ordner / dieses Projekt, mit Ablaufdatum, protokolliert, widerrufbar | erst suchen, dann bauen (keine Freigabe-Tabelle vorhanden, gesucht 19.09.) | Datenseite in Bau |
Die Flächen und KAI-Befehle dafür baut die Bahn RECHTE-UX#01; VERTRAULICH#01 liefert Endpunkte und Funktionen. Die SQL fährt JC, der Gegenbeweis läuft mit echten Konten.
Zahlt die Firma eine Vorsorge für eine Person, brauchen beide den Beleg. Am echten Fall: dieselbe Datei kam zweimal per Mail.
| Beleg | Mütze | Stufe | Warum | Stand |
|---|---|---|---|---|
| #8014 | C3 (Firma zahlt) | 🟡 vertraulich | Buchung und Nachweis, aber nur die Chefs | Daten: JC-Hand nach Deploy |
| #8016 | Privat / Mandant jcbinetti | 🔴 streng | Unterlage der Person — oder was sie selbst wählt | Umbuchung: JC-Hand |
duplicate_entity_split); Deduplizierung öffnet nie mandantenübergreifend (RAG-Bauplan §7c).
ORCH-Regel „Stufe folgt der getragenen Entität" #2799. Offen bei JC: ORCH erkennt „Firma zahlt, Person profitiert" und schlägt die zweite Mütze als Klärkarte vor — bis dahin entsteht sie nur durch menschliche Wahl.JC: „Die Wahrheit zur Einstufung liegt im Dokument selbst, nicht in der Mail, mit der es versendet wurde." Absender und Empfänger sind höchstens ein Hinweis.
| Erschließung | Stufe | Entität | Mensch |
|---|---|---|---|
| vollständig | ORCH-Urteil wird gesetzt (nie „öffentlich", nie überschreiben, über der Schwelle) | endgültig | — |
| teilweise · fehlgeschlagen · nicht unterstützt · schon gelesen ohne Nachweis · fehlt | keine (= streng) | gesetzt, aber vorläufig (Vermerk am Beleg) | Klärkarte mit Grund |
Stand: Erschließen vor dem Einstufen #2798 (RAG#01) · Anwenden auf dem Hauptweg orch_apply.apply #2800.
Der n8n-Knoten lief zuletzt am 23.08. — die Naht dort (#2795) ist geschlossen.
Ein Beleg ohne Stufe sieht nur der Eigentümer. Vorher lasen vier Stellen „keine Stufe" verschieden (Liste offen, KAI streng, Datenbank offen).
Die Stufen (fünf, davon Projekt als BALD-Stufe) gelten für jeden Mandanten gleich. Welche Fälle wohin gehören, ist je Mandant verschieden und wächst über den KVP: Zuordnungen, Korrekturen und Klärkarten werden Lernereignisse, aus denen ORCH je Mandant Hinweise präzisiert — oder aufhebt. Feste Regeln (auch „privat + Personenname") sind Startwerte für ORCH, nie Wenn-Dann-Pflicht.
learning_events, pattern_rules hat 0 Zeilen, ORCH liest keine Mandanten-Hinweise.
Vorschlag mit vorhandenen Bausteinen: sammeln in learning_events → verdichten in pattern_rules (Mandant im scope,
Gegenbelege über conflict_count) → ein Hinweis-Block im ORCH-Prompt wie {{supplier_prior}}. Tor: JCDie Person, die ein Dokument hochlädt, sieht es immer — auch ohne Stufe und bei streng. Die Stufen-Klärkarte geht zuerst an sie, an den Verwalter erst, wenn sie weiterreicht oder nach N Tagen. Sie darf verschärfen und ihr eigenes Dokument bis intern öffnen, mit Pflicht-Grund und Protokoll — nie auf öffentlich.
Mails haben keinen Hochlader. Der Mail-Eingang läuft ohne Sitzung eines Menschen; ORCH stuft Mail und Anhänge aus dem Inhalt ein (④), die Klärkarte geht an die Verwalter.
Einführung, SQL fährt JC in dieser Reihenfolge: ① Spalte hochgeladen_von + Lesen (#2139) → ② Trigger-Regel mit Hochlader-Zweig (#2140, bricht ohne ① selbst ab)
→ ③ Hochlader der Altbelege nachtragen (vertraulich01_hochlader_nachtragen.py --apply). Danach Gegenbeweis mit zwei Mitarbeiter-Konten.
Kanon: KONZEPT_VERTRAULICHKEIT_GEHEIMHALTUNG.md §4.
JCs Frage (19.09.): Kommt der Nutzer mit einem „Unterprojekt“ klar? Üblich ist die Ordnerstruktur mit Freigabe für Kreise. Er hat recht, und die Ordner gibt es als Konzept schon: die nummerierte Projektgliederung des Dokumentalisten. Vorlage: Projektordner und Rechte (löst Frage 6 der Rechte-Vorlage neu).
| Gemessen 19.09. | Befund | Fundstelle |
|---|---|---|
| Gliederung A (JC 14.07.) | 01 Akquise · 02 Anfragen · 03 Angebot · 04 Verträge · 05 Rechnungen · 06 Projekt-Daten; einsortiert vom AGENT-DOKUMENTALIST#01; Default je Mandant anpassbar (#196) | GATE_REPORT_DOKUMENTE_ABLAGE.md:97-104 |
| Gliederung B (01.09.) | 00_RECHERCHE · 01_ANGEBOT · 02_VERTRAEGE · 03_RECHNUNGEN/Jahr · 04_PROJEKTDATEN · PROJEKTMANAGEMENT; 09_ABSCHLUSS offen. Ihr Satz „neu, ohne Vorlage“ war falsch und ist korrigiert. | 00_LEITDOKUMENT/ABLAGE_KANON.md §2a |
| Im Code | nur ein flacher Projektpfad, kein Dokumentalist-Skill, keine Ordner-Tabelle; Unterprojekt-Feld 0 von 6 benutzt | credentials/_ordnung.py:224 · _registry.yaml · 05_SCHEMA |
| Buchhalter-Kreis | sieht heute ALLES der Entität bis vertraulich, ohne Filter nach Dokumentart — ② oben sagt denselben Ist-Zustand, die Beschränkung ist offen (⑩) | app.py:3426 · :6700 |
00_RECHERCHE auf · Rechte hängen an diesen Ordnern · Verschieben warnt, blockt nicht. Offen bleiben aus dem Kanon: 09_ABSCHLUSS und Ordnername (Nummer/Name).owner (Vorlage „Verwalter“). Vorher: Gemessen: /dokumente zeigt Projektbelege schon (core_shell.html:9180-9182, Projektfilter nur bei gewähltem Projekt), aber ohne Projekt-Pille. K-44 kennt drei Achsen; die Projekt-Pille ist eine NEUE vierte Achse. ⛔ Korrektur 19.09.: badge_systematik.html:252 kündigt keine Pille an, sondern meint die Stufe „Projekt“ (E4b), die über Projekt-Mitgliedschaften kommt. Empfehlung: Projekt-Pille als vierte Achse (K-44) · die zwei Sorten Projektrechnungen (Eingang: was C3 im Projekt bezahlt · Ausgang: was C3 dem Projektträger stellt) als Merkmal über die vorhandene Richtung party_role (Mig 068), nicht als Unterordner, denn eine Rechnung gehört Projektsteuerung UND Buchhaltung (Touch-once) · Projektsteuerung = Etikett auf der Projektrolle owner (Vorlage „Verwalter“, entschieden statt „Verantwortliche“), sieht die Projektrechnungen mit Beträgen, sonst nichts der Entität; voll wirksam erst mit betrag.sehen (RECHTE#01 §4, S2, noch nicht gebaut).
05_SCHEMA/PROPOSED_vertraulich01_buchhalter_klassen.sql (nicht angewandt). Unabhängig davon kommt die Kreis-Decke vertraulich auch in KAIs Werkzeugen an (#2843).Die Vorlage, über die noch entschieden wird:
Lage. Der Kreis sieht heute alles seiner Entität bis vertraulich; ② sagt denselben Ist-Zustand. Ursprünglich war „Belege und Verträge“ versprochen — die Wand filtert nach keiner Dokumentart — und zwar an zwei Stellen: im Kreis-Zweig selbst, und im allgemeinen Mitglieder-Zweig, der jedem Mitglied des Mandanten alles bis intern zeigt und die Rolle nicht unterscheidet. Ein Filter nur im Kreis würde über den zweiten Zweig umgangen. Gemessen 19.09.: es gibt heute kein Buchhalter-Konto — noch kein Abfluss, aber das Versprechen hat keine Wirkung. Angebote und Protokolle haben heute keine eigene Dokumentart (vorhanden sind 9 Werte, gemessen); sie stehen unter Sonstiges — oder unter einer falschen Art (s. Grenze unten).
Dokumentart (doc_type) | Klasse (Dokumentalist) | C3 heute, bis vertraulich | Variante A (Empfehlung) | Variante B (eng) |
|---|---|---|---|---|
| Rechnung | Buchhaltungs-relevant | 247 | sieht | sieht |
| Gutschrift | Buchhaltungs-relevant | 5 | sieht | sieht |
| Zahlungsbeleg | Buchhaltungs-relevant | 4 | sieht | sieht |
| Quittung | Buchhaltungs-relevant | 19 | sieht | sieht |
| Steuer | Buchhaltungs-relevant | 0 | sieht | sieht |
| Vertrag | jahresübergreifend | 16 | sieht | sieht |
| Banking-CSV (Kontoauszüge) | Buchhaltungs-relevant | 18 | sieht | sieht nicht |
| Sonstiges (Angebot, Protokoll, Korrespondenz) | — | 10 | sieht nicht | sieht nicht |
| ohne Dokumentart | — | 0 | sieht nicht | sieht nicht |
buchhalter_doc_typen(), im Dienst gespiegelt und per Test verglichen); der Mitglieder-Zweig schließt reine Buchhalter-Mitgliedschaften aus; der Kreis-Zweig filtert nach Dokumentart — an Belegen, an ihren Textstücken und an Verträgen. SQL: 05_SCHEMA/PROPOSED_vertraulich01_buchhalter_klassen.sql (Gegenbeweis „Buchhalterin C3 sieht kein Angebot/Protokoll“ als zurückgerollter Block). Dienst und KAI folgen im selben Auftrag; dabei wird auch die Lücke geschlossen, dass die Kreis-Decke den KAI-Werkzeugen bisher nicht übergeben wurde.
| Punkt | Register | Stand |
|---|---|---|
| ORCH-Stufe am Beleg anwenden (Hauptweg) | #2125 · #2800 | gebaut · Deploy-Naht |
| Erst erschließen, dann einstufen | #2104 · #2798 | gebaut · Deploy-Naht |
| Keine Stufe = streng (Dienst, KAI, Lesewege) + E4 | #2118 · #2796 | gebaut · Deploy-Naht |
| Datenbank-Wand (E1) | #2118 | SQL bei JC |
| Weitere Lesewege ohne Decke (8 Endpunkte) | eigene Bahn | in Arbeit |
| ORCH-Prompt „zwei Mützen" | #2122 · #2799 | gebaut · Deploy + SQL im selben Fenster |
| 95 Altbelege nach ORCH-Urteil (E2 = B) + Chunks erben | #2118 · #2120 | Werkzeug bereit · JC nach Deploy |
| #8014 / #8016 zwei Mützen | #2119 · #2121 | JC-Hand nach Deploy |
| Kreise + Einzelfreigabe (E3) | VERTRAULICH#01 · RECHTE-UX#01 | in Bau |
| Hochlader sieht sein Dokument · Klärkarte zuerst an ihn | #2139 · #2820 | live 1.12.179 (Naht 5b) · SQL ① + ③ bei JC |
| Hochlader öffnet bis intern (nie öffentlich) | #2140 · #2817 | gebaut · Deploy JC-Wort 19.09. · SQL ② bei JC |
| Projektgliederung B + Rechte an ihren Ordnern + Verschieben warnt (⑨) | #2128 · JE-175 | entschieden 19.09. · Bau: Ordner-Knoten VERTRAULICH#01, Flächen RECHTE-UX#01 |
| Zweite Mütze als ORCH-Klärkarte | — | JC-Entscheid offen |
| Buchhalter-Kreis: nur Buchhaltungs-Klassen | #2150 | offen, JC 19.09. geparkt — Tendenz A, mit den Unternehmensrollen zu klären (⑩); Filter vorbereitet (#2837 auf Eis) |
| Lernschleife je Mandant | #2123 | läuft nur trocken · Tor JC |
Lesart: was darf diese Rolle mit einem Doc dieses Status, innerhalb der berechtigten Entität? (Governance §3, gekürzt — SSOT hat die volle Matrix inkl. RAG-Abruf-Zeilen.)
| öffentlich | intern | vertraulich | streng_vertr. | |
|---|---|---|---|---|
| Eigentümer / mandant_admin | alles | alles | alles | alles (Owner-Bypass) |
| entity_member | sehen + Beträge + KI | sehen + Beträge + KI | sehen, Beträge maskiert | nur „existiert" |
| viewer | sehen | sehen, kein Export | sehen ohne Beträge | nichts |
| extern | sehen | — | — | — |
| Status ändern | mandant_admin | mandant_admin + Eigentümer | mandant_admin | nur Eigentümer/super_admin |
Owner-Bypass (der Eigentümer nutzt ALLE seine Docs aller Stufen — auch in der KI) und fail-closed (Status NULL = wie 🔴 streng für Andere) sind die zwei Anker der Matrix. Wie Änderungen ablaufen (Gates, Audit, Löschen) → §7 Lebenszyklus.
§4 sagt, was eine Rolle mit einem Daten-Stück darf. Diese Matrix beantwortet die andere Frage (JC 10.08.): welche Menüpunkte / Screens eine Rolle überhaupt zu sehen bekommt. Kern-Satz: Ein Externer eines Projekts sieht NICHT das ganze TGV-Menü — er sieht seinen Projekt-Arbeitsraum, sonst nichts. Zwei Achsen, die zusammenspielen: die Sicht-Klasse (dieser §) entscheidet, welche Screens erscheinen; §4 entscheidet, welche Datenzeilen darin sichtbar sind.
Fünf Sicht-Klassen — Screens werden NICHT einzeln aufgezählt (eine Liste veraltet beim nächsten SCREEN-COMPOSER-Screen), sondern in wenige Klassen einsortiert. Jeder Screen deklariert seine Klasse (Vertrag); die Shell zeigt ihn nur, wenn die Rolle die Klasse trägt.
| Sicht-Klasse | Was liegt darin (heutige Menü-Gruppen) | Wer trägt sie |
|---|---|---|
| T0 · öffentlich | Landing · Login · öffentliche Mandant-Seite (Anna) | jeder, auch unauth. (extern) |
| T1 · Projekt-Raum | Heute/Todo · Projekt-KAI · Dokumente des Projekts · Finanz-Übersicht mit Rechnungen des Projekts — JCs Beispiel | Projekt-Mitglied + Projekt-Externer (user_project_roles), OHNE Mandant-Recht |
| T2 · Arbeitsplatz | Eingang (Inbox/Sammeltore/Triage/Klärung) · Buchungen · Verträge · Auswertungen · Reisen · Bereiche (Finanz/RKA/Steuer/Projekt) | entity_member + mandant_admin (eigene Entität/Mandant) |
| T3 · Verwaltung | Verwaltung (Mitglieder · Module · Aufbewahrung · Audit · Branding · Billing) · Stammdaten (Regeln · Persona) | mandant_admin |
| T4 · Betrieb | Plattform (Assistent · Wissen · Skills · System) · Konzepte · Zur Abnahme · Cross-Mandant | super_admin |
| T0 öffentlich | T1 Projekt-Raum | T2 Arbeitsplatz | T3 Verwaltung | T4 Betrieb | |
|---|---|---|---|---|---|
| super_admin | ✓ | ✓ | ✓ | ✓ | ✓ |
| mandant_admin | ✓ | ✓ | ✓ | ✓ | — |
| entity_member | ✓ | ✓ seiner Projekte | ✓ seiner Entität | — | — |
| viewer | ✓ | ✓ nur lesen | ✓ nur lesen, kein Export | — | — |
| Projekt-Externer | ✓ | ✓ NUR sein Projekt-Raum | — | — | — |
| extern / unauth. | ✓ | — | — | — | — |
Die gezeigten Menüpunkte = Vereinigung der Klassen, die die Mitgliedschaften des Aufrufers tragen. Ein Nutzer mit nur einer Projekt-Rolle (kein Mandant-Recht) bekommt allein T1 — der Rest des Menüs entsteht gar nicht. Default-Deny: trägt die Rolle die Klasse nicht, wird der Eintrag NICHT gerendert (nicht nur ausgegraut).
Nicht „RLS ist an" als Behauptung, sondern je Kern-Tabelle der Code-verifizierte Stand (Migrationen in 05_SCHEMA/). Grundnetz seit Mig 037: RLS wird auf JEDER public-Tabelle erzwungen (dynamischer Loop) — seither wird das grobe Netz Tabelle für Tabelle durch schärfere Policies ersetzt:
| Tabelle | Inhalt | Policy heute (Code-verifiziert) | Migration | Stadium |
|---|---|---|---|---|
| mandanten · entities · user_mandant_roles | Mandanten-Struktur + Rollen | RLS on (live ad-hoc, dokumentiert) + M3-Policies: Self-Read · super_admin-Bypass · Mandant-Scoping | 209 · 211 | ✅ LIVE (M1–M3) |
| documents | Belege & Dokumente | Stand 19.09.2026 (live gemessen, pg_policy): Mitglieder des Mandanten bis intern, Verwalter alles; keine Stufe = streng (E1, #2118). Dazu die Zweige Buchhalter-Kreis bis vertraulich (E3, #2132), Projekt über can_user_see_confidentiality(), Einzelfreigabe (hat_freigabe) und Hochlader (#2139). Vorher (gemessen 05.09., §5b) prüfte der Mitglieder-Ast die Stufe nicht — geschlossen durch E1. Offen: der Mitglieder-Ast wertet die Rolle nicht aus (my_mandant_ids, Grenze #2134, s. §3a ⑩). | 142 → 212 · E1/E3 · 05_SCHEMA/PROPOSED_vertraulich01_hochlader_sieht_sein_dokument.sql:52 | ✅ LIVE |
| rag_chunks | das KI-Gedächtnis | Dieselben Zweige wie documents (Stand 19.09.2026, live gemessen); Projekt, Einzelfreigabe und Hochlader über den Quell-Beleg. Der Chat-Pfad läuft zusätzlich durch die gegatete Retrieval-RPC (§6). Seit 19.09. zieht bei jeder Beleg-Änderung ein Trigger Mandant und Stufe der Chunks nach (Trigger trg_documents_chunk_folgt_beleg, #2146; Wächter check_wand_am_chunk.py --live ③; der Altbestand — 18 Belege Mandant, 4 Stufe — ist dort als bekannt geführt). | 029 → 212 · 221 · 05_SCHEMA/PROPOSED_vertraulich01_hochlader_sieht_sein_dokument.sql:85 | ✅ LIVE |
| txn_master | Bank-Transaktionen | admin-only (txn_master_admin_all, is_global_admin) — eine von 34 Finanz-Tabellen, denen Mig 157 die role-blinde Blanket-Policy entzog | 037 → 157 | ✅ LIVE |
| tax_filings | Steuer-Vorgänge | admin-only (in der 34er-Liste von Mig 157) | 027 → 157 | ✅ LIVE |
| prf_contracts | Verträge | Mandanten-Wand wie documents (Stand 19.09.2026, live gemessen): Mitglieder bis intern, Verwalter alles, Buchhalter-Kreis bis vertraulich, Einzelfreigabe und Hochlader über den Beleg. anon-Zugriff DENIED. | 157 · 201 · 05_SCHEMA/PROPOSED_vertraulich01_hochlader_sieht_sein_dokument.sql:69 | ✅ LIVE |
| auth_allowed_users | der Rollen-Speicher (SSOT) | RLS on; wird nie roh von App-Code gelesen — nur account-status (server-seitig) + RLS-Helper | 037 | ✅ LIVE |
SICHERHEIT#05 hat am 05.09.2026 alle pg_policies der laufenden DB gelesen (nicht die 05_SCHEMA/*.sql-Dateien) und jede Tabelle einer Achse zugeordnet. Anlass: das Konzept nannte für documents zwei Wege (Mandant ODER Projekt-Gast) — die Frage war, ob eine dritte Achse fehlt. Antwort: die dritte Achse ist konzipiert, aber live auf NULL Tabellen.
| Achse | Prädikat | Tabellen live | Vertraulichkeit geprüft? | Stand |
|---|---|---|---|---|
| MANDANT | mandant_id = ANY(my_mandant_ids(auth.uid())) | 23 | NEIN | LIVE |
| PROJEKT | project_id = ANY(get_project_ids_for_user(...)) AND can_user_see_confidentiality(...) | 28 | JA | LIVE |
| ENTITÄT | entity = ANY(my_entities(auth.uid())) | 0 | — | NUR DATEI, nicht angewandt |
| ADMIN/SELF | is_global_admin() · is_super_admin() · user_id = auth.uid() | 75 | — | LIVE |
| OFFEN (qual=true) | keines — jeder Angemeldete | 9 | NEIN | 3 davon kritisch, s.u. |
Regale, über die diese Liste gilt (R-193): ① pg_policies + pg_class.relrowsecurity der laufenden Supabase-DB, gelesen über 08_OPS/db_query.py · ② 05_SCHEMA/*.sql (271 Dateien) · ③ public.tgv_migrations. NICHT gelesen: Views und deren security_invoker-Flag · Trigger und Funktionen ausserhalb der genannten RLS-Helfer · n8n-Workflows · Endpoint-Guards der App-Schicht (die als service_role laufen und RLS per Design umgehen, s. §6). Eine Aussage über diese vier Regale ist hier NICHT enthalten.
Grundnetz bestätigt: 141 von 141 public-Tabellen haben RLS aktiv, keine einzige mit RLS aus; eine Tabelle hat RLS an und null Policies (= vollständig zu, fail-closed).
Analog zur Seitenbau-Lehre („Kanon strukturell binden, nie ans Gedächtnis"): jeder Mechanismus nennt die Ebene, an der er verriegelt ist — Schema, RLS, Guard am Wirkort oder Modul-Vertrag. Das ist die Antwort auf „WIE ist es gesichert?":
| Mechanismus | Was er leistet | Gebunden an | Stand |
|---|---|---|---|
| Default-Deny-Standard | Sensible Tabellen: RLS on, nichts sichtbar ohne explizite Policy — nie viewer, nie cross-mandant | Schema + RLS (Identity-RLS-SSOT §4, durable Regel) | LIVE Standard |
| fail-closed | Unbekannt = dicht: NULL-Status zählt wie streng; User ohne Tabellen-Zeile → 403 | RLS-Prädikat + Funktion (rag_conf_rank(NULL)=3) | LIVE |
| ctx = Object.freeze | Jedes Shell-Modul bekommt seinen Scope EINGEFROREN — es gibt keinen Setter; ein Modul kann seinen Scope nie selbst erweitern | Modul-Vertrag (MODULE_CONTRACT.md: „ctx ist Object.freeze'd, ctx.scope ebenso") | LIVE Kanon |
| RLS-Helper | is_super_admin() · my_mandant_ids() · is_mandant_admin() — SECURITY DEFINER über die EINE Rollen-Quelle; alle Policies rechnen damit | RLS (Mig 211), Quelle = auth_allowed_users/user_mandant_roles | ✅ LIVE |
| service_role-Grenze | n8n/Skills laufen als service_role = RLS-bypassed BY DESIGN → die App-Schicht MUSS scopen („Skills scopen selbst") — deshalb sitzen Gates am Wirkort, nicht nur in der DB | Guard am Wirkort (Endpoint/Skill) | ⚠ teils — Endpoint-Scope-Guard erst 2/57 (§10) |
| Retrieval-RPC-Gate | Der Chat ruft das KI-Gedächtnis NUR durch search_knowledge_scoped: Mandant ∈ erlaubter Menge + Entität + Vertraulichkeits-Decke, fail-closed; Owner-Bypass = der AUFRUFER setzt conf_max (aus scope_gate), NIE das LLM | Funktion (DEFINER-RPC) — das Gate sitzt IN der RPC, weil service_role RLS bypasst | ✅ APPLIED 13.07. + Gate-bewiesen (alle PASS) |
| Break-Glass-Audit | tgv_log_break_glass(): super_admin-Zugriff auf fremden Privat-Mandanten wird protokolliert — bewusst, nie still (§2) | Audit (agent_events.mandant_id, Mig 218) | staged (Mig 220, JC-DDL-GO) |
| Datei-Wand (Bucket je Mandant) | Jede Entität hat ihren Hetzner-Bucket mit eigenem Schlüssel und eigener Policy; der Dienst liest je Bucket mit DESSEN Schlüssel (_storage.config_fuer_bucket, fail-closed). Wer eine Datei sehen darf, entscheidet aber die ZEILE, nicht der Bucket: file-url und die drei Schwester-Endpunkte lösen zuerst documents über RLS (mandant_id, JWT des Aufrufers) plus _user_scope auf (app.py:5455ff) und presignen erst danach.⛔ WARNUNG für jeden künftigen Export- oder Listen-Bau: Ein Objekt kann im Bucket einer anderen Entität liegen als seine Zeile sagt (gemessen 19.09.: 16 Belege, z. B. ein C4-Beleg im C3-Bucket, drei C3-Belege im privaten; Register #2171). Heute liest das kein Mensch, weil kein Weg einen Bucket aufzählt. Ein Export „alles aus Bucket X“ oder ein LIST über einen Mandanten-Bucket würde diese Belege an den falschen Mandanten ausliefern. ⇒ Exporte und Listen laufen immer über documents-Zeilen (mandant_id + Stufe), nie über das Aufzählen eines Buckets. | Hetzner-Policies (Kreuzprobe 08_OPS/pruefe_mandanten_wand.py) + Lese-Chokepoint _supa_get_document | ✅ LIVE — Kreuzprobe 0/56 (19.09.) · 16 Objekte im fremden Bucket, Heilung JC-Ball (#1278/#1630) |
| Encryption-at-rest | Gespeicherte Daten verschlüsselt — Pflichtteil des streng-Schutzes | Infrastruktur (Supabase-Basis) | Basis · streng-Härtung folgt |
| Grant-Verify-Ritual | Supabase-Falle (beim 221-Apply real erlebt): neue public-Funktion bekommt AUTO-EXECUTE für anon+authenticated; REVOKE FROM PUBLIC genügt NICHT → bei DEFINER-RPCs anon/authenticated explizit revoken + Grant-Verify (EXECUTE nur service_role+postgres) | Ops-Ritual, im Mig-File verankert | gelernt + verankert |
Die EINE Liste für alle Bahnen (VERTRAULICH#01 pflegt sie; die rag_chunks-Wege hat RAG#01 gegen master nachgeschlagen, Bauplan #2094 P3). Ein Weg ohne Wache ist ein Befund, keine Zeile zum Weglassen. „CI ㊵“ = Schritt ㊵ in .github/workflows/konformitaets-guards.yml, „skipped“ gilt dort als rot.
| Weg | Eingang | liest | Scope aus | Stufen-Decke | Wache (Test) | läuft |
|---|---|---|---|---|---|---|
| KAI-Chat | /api/skill/core-chat → Werkzeuge | Belege, Verträge, Mails, Textstücke | _user_scope → build_scope (Mitgliedschaft, Kreis-Decke #2156, Aufrufer #2139) | _conf_or_filter · RPC-Gate search_knowledge_scoped | 07_TESTS/agent_chat01/test_aufrufer_id_durchreich_2139.py · 07_TESTS/agent_chat01/test_kreis_decke_durchreich_2156.py · 07_TESTS/agent_chat01/test_hochlader_sieht_sein_dokument_2139.py | CI ㊵ |
| ORCH-Plan | /api/skills/orch/plan | Klassifikation, Entität, Anbieter eines Belegs | nur S2S (api_key) oder Global-Admin (_is_global_admin) — alle anderen 403 | — | 07_TESTS/agent_chat01/test_lesewege_decke_2118.py | CI ㊵ |
| Klärungen | /api/skill/klaerung list_pending | Volltext offener Belege | _listen_decke | Decke aus der Rolle | 07_TESTS/agent_chat01/test_lesewege_decke_2118.py | CI ㊵ |
| Versand an Buchhaltung | /api/skill/accountant-send · presend-report · ustva-report | Pakete mit Beträgen | verwaltete Entitäten | Verwalter / Decke | 07_TESTS/agent_chat01/test_lesewege_decke_2118.py · 07_TESTS/agent_chat01/test_lesewege_decke_p3_2129.py | CI ㊵ |
| Fach-Skills | contract-extract · vat-currency-determine · source-to-fiche · rag-ingest(-project) | Beleg-Inhalt, Schreiben | Schreibrolle im Ziel-Mandanten | _conf_allows | 07_TESTS/agent_chat01/test_lesewege_decke_p3_2129.py | CI ㊵ |
| Ablage-Sicht | /api/skills/ablage · /api/skills/wartet | Anbieter, Dateiname, Beträge | _user_scope (mandant_id) | _ueber_decke_weg | 07_TESTS/agent_chat01/test_lesewege_decke_p3_2129.py | CI ㊵ |
| Datei öffnen | /api/skill/file-url(s) | die Datei selbst | RLS mit dem Nutzer-JWT bzw. _user_scope | _conf_allows + Hochlader + hat_freigabe | 07_TESTS/agent_chat01/test_hochlader_sieht_sein_dokument_2139.py · 07_TESTS/agent_chat01/test_e3_kreise_und_freigabe_2132.py | CI ㊵ |
| Direkt über die Datenbank | PostgREST mit Nutzer-JWT | documents · prf_contracts · rag_chunks | RLS-Policies (§5) | E1/E3/Freigabe/Hochlader in der Policy | 07_TESTS/schema01/gegenbeweis_debtor_rls.sql · 08_OPS/vertraulich01_gegenbeweis.py (echte Sitzung) | Hand |
| Chunk-Wand beim Schreiben | fünf Schreiber von rag_chunks | — | rag_wand.stempel_wand | classify_chunk_visibility | 07_TESTS/sicherheit05/test_wand_am_chunk.py | CI Suiten |
| Chunk folgt Beleg | Trigger trg_documents_chunk_folgt_beleg (#2146) | — | Beleg-Mandant | rag_chunk_soll_stufe | 08_OPS/check_wand_am_chunk.py --live ③ | Hand |
| KAI-Vektorsuche | RPC search_knowledge_scoped (DEFINER, nur service_role) · executors_rag.py:359 · Mig 263:216 | Textstücke | _user_scope + user_project_roles; leere Menge = nichts | rag_conf_rank ≤ Decke (scope_gate.py); Entität: p_entities in der RPC, vor dem LIMIT (#2183, SQL bei JC — bis dahin keine Entitäts-Wand, heute latent) | 07_TESTS/agent_chat01/test_rag_executor.py · 07_TESTS/agent_chat01/test_mandant_kein_default.py · 07_TESTS/agent_chat01/test_chatpath_conf_failclosed.py | CI Suiten |
| KAI-Wortsuche | RPC search_knowledge_lexical (DEFINER) · executors_rag.py:686 · Mig 263:282 | Textstücke | wie die Vektorsuche | dasselbe Prädikat; Entität: dasselbe p_entities (#2183) | dieselben Tests + 07_TESTS/agent_chat01/test_7e_kai_rechte_2182.py · 07_TESTS/agent_chat01/test_rag_entitaets_wand_2183.py | CI Suiten |
| Erinnerungs-Suche (Gabriel) | /api/skill/memory-search-chat (+ prompt-preview) · app.py:3252 | Textstücke per REST, service_role | _require_project_member (app.py:6552); api_key und Global-Admin passieren | Nachfilter can_user_see_confidentiality | 07_TESTS/agent_chat01/test_lesewege_memory_search_2180.py (RAG#01, #2180, PR #2884): Tor und Nachfilter, beide Richtungen | CI ㊵ nach Merge #2884 |
| MCP-Server (lokal) | alle Werkzeuge in TGV -App files/tgv_mcp_server.py, z. B. lookup_pipeline_doc → RPC lookup_user_docs | alles, was die Werkzeuge lesen und schreiben; lookup_user_docs selbst nur die 6 Pipeline-Handbücher (source_type=user_doc, Stufe intern) | keiner — service_role, ohne Mandant, Stufe und Aufrufer | keine | tragbar nur als JCs lokales Einnutzer-Werkzeug über stdio — bewacht von 07_TESTS/agent_chat01/test_mcp_nur_lokal_2181.py (rot bei anderem Transport, HTTP/SSE-App, Host/Port oder .mcp.json per URL) | CI ㊵ |
| n8n-Suche (alt) | Webhook tgv-skill-rag-search → rag_search_chunks | Textstücke | keine (anonym) | keine | Workflow live inaktiv (gemessen 19.09. von RAG#01); Löschung der Funktion liegt als PROPOSED bei JC | inaktiv |
Status ändern mit Leitplanken: Hochstufen jederzeit („im Zweifel strenger") · Runterstufen nur Admin/Eigentümer mit Bestätigungs-Dialog + Audit · der KI-Dokumentalist schlägt vor, entscheidet NIE · jede Änderung zeigt sofort die Konsequenz („wer verliert/gewinnt Zugriff"). Vor Runterstufen/Löschen steht die Awareness-Session als menschliches Gate (§8).
| Lösch-Stufe | Was passiert | Reversibel? | Stand |
|---|---|---|---|
| ① KI vergisst | Chunks raus aus dem KI-Gedächtnis (RAG) — das Dokument bleibt in der Ablage | ja (Re-Index) | Modell GELOCKT |
| ② Papierkorb | 30 Tage Aufbewahrung, dann automatische Endlöschung — Schutz vor Versehen | ja (30 Tage) | Modell GELOCKT |
| ③ Endgültig (DSGVO Art. 17) | Doc + Chunks + Roh-Datei + Spuren — protokolliert (Audit hält nur DASS gelöscht wurde, nie den Inhalt) | nein | Vorbedingung offen: Erasure-fähiges FK-Design (FK-409) — Pflicht BEVOR Art. 17 versprochen wird; Backup-Erasure = Phase 2 |
§7 löscht ein Dokument. Wenn eine Person verlangt, dass ihre Daten gelöscht werden, gilt §7a: Sie steht an vielen Orten, und jeder Ort hat eine eigene Regel.
⛔ Offene Vorfrage (JC-Ball, Register #2241): „Löschen" heißt heute zweierlei — endgültig (02.09.) oder umkehrbar markiert (05.09.), und beide Formen sind schon benutzt worden. Welche davon Hausform wird, entscheidet JC in der Vorlage „Löschen — welche Form?" (Anlass: die Test-Uploads, JE-182). Bis dahin gilt für Personen weiter die Stufenfolge aus §7.
Ziel (JC 19.09.): Kontakte und User können ihre Daten löschen lassen. Das gilt in jedem Mandanten und auch in einer gesamten Kontakt-Datenbank; die Mandanten bleiben dabei getrennt. Schon entschieden, hier nur angewandt: das Regime je Kontakt-Klasse (GATE_A_KONTAKTE.md §6 · §9.2(c)), die Felder rechtsgrundlage + loesch_regime je Zeile (Mig 251:61-62), die Lösch-Stufen ①②③ (§7), das Lösch-Audit „dass · was · wann · wer · Rechtsgrund, nie der Inhalt“ (KONZEPT_VERTRAULICHKEIT §5), der Fristen-Kanon (08_OPS/aufbewahrung_kanon.yaml, JC 03.09.) und „Vorschau → apply nur mit freigegeben_von“ (mail_versand.senden).
| Norm | Pflicht | Folge für TGV |
|---|---|---|
| Art. 17 (1) | Löschen, wenn der Zweck entfällt, die Einwilligung widerrufen wird, jemand widerspricht oder die Verarbeitung unrechtmäßig ist | Werbe-Kontakt (oeffentlichkeit) nach Widerruf: löschen |
| Art. 17 (3) b · e | Ausnahme: eine gesetzliche Aufbewahrungspflicht oder Rechtsansprüche | Beleg-Bezug ⇒ sperren bis Fristende, dann löschen |
| Art. 18 · § 35 (3) BDSG | Einschränken statt löschen, solange eine Frist läuft | gesperrt heißt: nur gespeichert. Nicht im KI-Gedächtnis, nicht in der Suche, nicht in Mailings. |
| Art. 12 (3) · (6) | Unverzüglich, spätestens nach 1 Monat; bis zu +2 Monate mit Begründung. Bei Zweifeln die Identität prüfen. | Die Uhr läuft ab Eingang, frist_bis steht im Löschprotokoll |
| Art. 19 | Empfänger der Daten über die Löschung informieren | Pflicht bei Weitergabe (Steuerberater, Buchhalter-Versand): Das Protokoll führt die Art des Empfängers, keine Adresse |
| Art. 7 (1) · 21 (3) | Die Einwilligung nachweisen können; nach einem Widerspruch nie wieder Werbung | Minimal-Nachweis ohne Freitext ins Protokoll, Sperrliste gegen Wiederanlage |
| Art. 28 (3) e · g | Der Auftragsverarbeiter unterstützt bei Anfragen, der Verantwortliche entscheidet. Bei Vertragsende löschen oder zurückgeben. | Rollen siehe ②, AVV siehe Vorlage E2 |
| Art. 30 (1) f | Die Löschfristen stehen im Verzeichnis der Verarbeitungstätigkeiten | Die Matrix ④ ist die Vorlage dafür (Register #909) |
| DE Fristen | § 257 HGB / § 147 AO: Bücher + Abschlüsse 10 J. · Buchungsbelege 8 J. (seit 01.01.2025, BEG IV) · Handelsbriefe 6 J. — Beginn jeweils Ende des Kalenderjahres | Die Zahlen liest der Löschweg aus aufbewahrung_kanon.yaml. Hier wird keine eigene Tabelle geführt. |
| FR Fristen | Code de commerce L123-22: Buchhaltung + Belege 10 J. · LPF L102 B: 6 J. (Steuer) · CNIL: Interessenten 3 J. ab letztem Kontakt | C4 (SARL) rechnet mit FR-Fristen, C3 (GmbH) mit DE-Fristen |
Quellen: DSGVO · § 257 HGB · § 147 AO · § 35 BDSG · L123-22 · L102 B LPF · CNIL Referenzrahmen · EDPB-Bericht Löschrecht 2025 · SDM-Baustein 60. ⛔ Keine Rechtsberatung: Die Fristen testiert der Steuerberater (C3) bzw. der expert-comptable (C4), wie im Kanon vermerkt (testiert_von ist leer).
| Daten | Verantwortlich (entscheidet) | TGV-Betreiber |
|---|---|---|
| Kontakte, Belege, Mails eines Mandanten | der Mandant (sein mandant_admin), nur für seine Zeilen | Auftragsverarbeiter: sucht, schlägt vor und führt nach Freigabe aus, entscheidet nie |
| TGV-Konto (Login, Rollen, Gesprächsverlauf) | der Betreiber selbst (Nutzungsverhältnis) | Verantwortlicher |
| Modus Verwaltet (§8) | mandant_admin gibt frei | super_admin führt aus, protokolliert |
| Schritt | Was passiert | Wer |
|---|---|---|
| 1 · Anfrage | Eingang per Mail, Formular, KAI oder Abmeldelink. Das Löschprotokoll legt eingang_am und frist_bis (+1 Monat) an; bei Zweifel wird die Identität geprüft (Art. 12 (6)). | jeder Mitarbeitende nimmt an |
| 2 · Vorschau | Findet die Person nur in den Mandanten des Aufrufers und zeigt jedes Fundstück mit Ort, Menge und Vorschlag: löschen, pseudonymisieren oder sperren bis Datum. Die Frist kommt aus dem Kanon. Nichts wird geschrieben. | Skill person-loeschen (Vorschau) |
| 3 · Freigabe | Ein Mensch prüft die Vorschau und gibt sie frei (freigegeben_von). apply läuft nur, wenn der Plan identisch zur Vorschau ist (vorschau_hash). | mandant_admin (im Modus Verwaltet: JC) |
| 4 · Ausführen | In dieser Reihenfolge: ① Einwilligungs-Nachweis ins Protokoll, ② Sperrliste setzen, ③ KI-Gedächtnis (Chunks) entfernen, ④ löschen bzw. pseudonymisieren bzw. sperren, ⑤ Storage-Objekte entfernen, ⑥ Empfänger informieren (Art. 19). | Skill (apply) |
| 5 · Bestätigung | Antwort an die Person: was gelöscht ist und was bis wann gesperrt bleibt, mit Rechtsgrund. Das Protokoll setzt bestaetigt_am. | Mail-Naht mail_bauen() |
⛔ Das „Wie“ ist EIN Wert, kein Code-Zweig: Ob ein Fundstück markiert oder endgültig gelöscht wird, liest die Vorschau an genau einer Stelle aus der Konfig-Fläche (tunable_params.dsgvo01_hausform). Bis JC die Vorfrage beantwortet (Vorlage #2241 „Löschen — welche Form?", liegt JC vor; der Link folgt mit ihrem Merge) steht dort offen, und jedes Fundstück sagt: was geschehen soll, steht fest — wie, noch nicht. Damit wird JCs Antwort eine Zeile in der Konfig und kein Umbau.
| Ort (Fundstelle) | Entscheid | Startzeitpunkt der Frist |
|---|---|---|
| contacts · channels · roles · tags · segment_members (Mig 251) | ohne Beleg-Bezug: löschen · mit Beleg-Bezug: Name wird pseudonymisiert, Kanäle gelöscht, active=false | sofort |
| contact_consents (251:207) | Minimal-Nachweis (Zweck, Daten, Art des Belegs) ins Protokoll, dann löschen | Nachweis-Frist: Vorlage E3 |
| documents · Storage-Objekt (Rechnung, Vertrag, Brief) | sperren bis zum Fristende laut Kanon, danach Stufe ③ (§7). ⛔ Die Datei hängt an keinem Fremdschlüssel: storage_bucket und storage_url sind nur Text (Mig 148:12-13). Wer die Zeile löscht, löscht die Datei nicht mit — und vernichtet zugleich den einzigen Zeiger auf sie. Also erst die Liste einfrieren (doc_id · Bucket · Schlüssel · Mandant), dann Zeile, dann Objekt (_storage.py:49), dann Gegenprobe. Vorprüfung: rka_belege.document_id steht auf RESTRICT (136:133) und bricht eine Lösch-Reihe sonst mitten drin ab. | Ende des Kalenderjahres des Belegs |
| debtor_customers · debtor_invoices (Mig 247) | sperren (Buchungsbeleg 8 J. DE / 10 J. FR) | Ende des Kalenderjahres |
| rag_chunks (028) | sofort entfernen, auch wenn das Dokument gesperrt bleibt, denn gesperrte Daten verarbeitet keine KI (Stufe ①) | sofort |
| outbound_mails (077) | Geschäftsmail = Handelsbrief: sperren 6 J. · Werbemail: löschen | Ende des Kalenderjahres des Versands |
| agent_events · llm_entscheidungen (Freitext) | Treffer schwärzen (Kanal-Wert und Name ersetzen) | sofort |
| chat_sessions · chat_messages (203) | Kontakt-Löschung: Treffer schwärzen (gezählt, nie zitiert; nur in Sitzungen der eigenen Mandanten) · Konto-Löschung: Vorlage E1 | sofort |
| TGV-User (auth, user_mandant_roles, persona_profiles, learning_events) | Konto löschen; Lernsignal bleibt ohne Person (user_id auf NULL) | sofort nach Konto-Ende |
| Backups | Heute gibt es keines (Free Tier). Mit dem ersten Backup wird Pflicht: nach jeder Wiederherstellung die Löschungen aus dem Protokoll erneut anwenden (SDM M60.P08). | — |
Ein Mensch, der bei C3 und bei C4 bekannt ist, steht dort in zwei getrennten Zeilen (contacts.mandant_id NOT NULL, 251:52). Es gibt keine gemeinsame Sammlung. Jeder Mandant löscht nur seine Zeilen. Die Suche der Vorschau läuft nur über die Mandanten, in denen der Aufrufer eine Rolle hat (my_mandant_ids aus user_mandant_roles), Default-Deny. Die Ergebnisse stehen je Mandant getrennt und werden je Mandant entschieden, denn die Fristen sind verschieden (DE ≠ FR). Betrifft eine Anfrage mehrere Mandanten, leitet der Betreiber sie an jeden Verantwortlichen weiter; er löscht nicht über die Wand hinweg. Gegenbeweis (Pflicht vor Merge): Ein Viewer auf c3 sucht eine E-Mail, die nur in c4 steht, und bekommt 0 Treffer und keinen Hinweis, dass es sie woanders gibt.
Ohne Sperrliste legt der nächste Signatur-Vorschlag oder Import die Person wieder an (Modelle: HubSpot sperrt die Adresse nach der DSGVO-Löschung, Brevo und Mailchimp ebenso). contact_suppressions hält je Mandant nur einen HMAC des Kanal-Werts mit einem eigenen Schlüssel je Mandant, keinen Klartext und keinen Namen (CNIL „liste repoussoir“). Getrennte Schlüssel bedeuten: Dieselbe Adresse hat in c3 und c4 verschiedene Werte, die Sperrliste kann also kein mandantenübergreifender Personen-Index werden. Wirkt in contact-propose (Signatur und Import laufen beide dort durch) und vor jedem Werbe-Versand. Wirkt nicht auf Korrespondenz (Rechnung, Vertrag). ⛔ Eine Sperre hat zwei mögliche Wirkungen, und sie zu vermischen wäre teuer: werbung heißt „die Person will keine Werbung" (Art. 21 (3)) — Rechnungen gehen weiter raus. zustellung heißt „der Versand-Anbieter nimmt die Adresse nicht mehr an" — dann kommt auch die Rechnung nicht an, sie sieht bei uns nur so aus. Brevo führt eine solche Liste und wendet sie auch auf Transaktionsmails an (MAIL#02 §V10e ② · §X5b, über KONTAKTE#01 21.09.); sie wird als grund='anbieter_sperre' gespiegelt und trägt zwingend wirkung='zustellung'. ⭐ Sonst beantwortet eine Zeile zwei Fragen — „darf ich werben?" ist eine Rechtsfrage, „kommt es an?" eine Tatsache, und eine unzustellbare Rechnung fällt niemandem auf, bis jemand mahnt. Solange der Kontakt existiert, ist contact_consents die Wahrheit. Die Sperrliste ist das, was die Löschung überlebt. Deshalb schreibt eine Abmeldung oder Beschwerde in einem Schritt beides: eine neue Consent-Zeile widerrufen (MAIL#02 §J5 V1) und den Sperreintrag. Bei der Löschung verschwindet die Consent-Zeile, der Sperreintrag bleibt. Die Sperrliste des Versand-Anbieters wird hier gespiegelt (MAIL_VERSAND_CONCEPT §V10e ②). Ein Hard-Bounce ist kein Sperrgrund: Er setzt den Kanal auf veraltet (§J5 V3) und hält dort jeden Versand auf. Gesperrt wird nur Werbung (Mail-Klasse 4, §J2); Korrespondenz, Buchhalter-Versand und Systempost prüfen die Sperrliste nicht.
KAI ruft person-loeschen als Vorschau auf und zeigt die Fundstücke je Mandant als EINE Tabelle (K-33) mit Entscheid und Frist je Zeile. Darunter steht ein Knopf „Löschung freigeben“, der dem Menschen gehört. KAI sagt nie „gelöscht“, bevor apply gelaufen ist, und nie „es gibt nichts“, ohne die geprüften Orte zu nennen (R-193). Gesperrte Stücke erscheinen mit Grund und Datum: „Rechnung 2024 bleibt gesperrt bis 31.12.2032, § 147 AO“.
| Paket | Inhalt | Stand |
|---|---|---|
| P0 · Konzept + DDL | dieser Abschnitt · 05_SCHEMA/PROPOSED_dsgvo01_personen_loeschen.sql: Consents RESTRICT, learning_events SET NULL, Sperrliste, Löschprotokoll | auf master (#2826) · SQL bei JC |
| P1 · Skill person-loeschen (Vorschau, Kontakt-Stamm) | Fundstücke je Mandant über my_mandant_ids, Entscheid je Stück aus Kanon + loesch_regime. Gegenbeweis-Test c3/c4, auf dem alten Stand rot. | GO TOWER#57 · in Bau |
| P1b · Vorschau über Belege | documents (Absender/Empfänger) · rag_chunks je Beleg · outbound_mails · debtor_customers. ⛔ Die Dokument-Hälfte rechnet der DIENST (_person_dokument_haelfte, app.py), denn die Decke lebt, wo sie gemessen wurde (_ueber_decke_weg/_conf_allows, E3-Kreis, ohne Stufe = streng). Der Skill bekommt die Treffer gereicht und bewertet sie nur — eine zweite Dokument-Decke im Skill wäre eine zweite Wahrheit über dieselbe Frage. Beide Hälften antworten unter EINER ausgewiesenen Reichweite. | gebaut |
| P1c · Gespräche und Protokolle | chat_messages (nur in Sitzungen der eigenen Mandanten) · agent_events · llm_entscheidungen. ⛔ Die Wand steht VOR der Suche: gesucht wird nur in Sitzungen, deren scope_json einen Mandanten des Aufrufers nennt. Treffer werden gezählt und verortet, nie zitiert; der Entscheid heißt schwärzen, nie löschen — ein Gespräch gehört dem, der es geführt hat, ein Protokoll ist die Spur eines Vorgangs. | gebaut |
| P2 · apply | nur mit freigegeben_von + gleichem vorschau_hash; Reihenfolge aus ③; schreibt das Löschprotokoll | nach DDL-Apply |
| P3 · Sperrlisten-Riegel | in contact-propose (Signatur, Import); vor Werbe-Versand in senden() (mail_versand.py, neben der Freigabe-Sperre; nicht in mail_bauen(), sonst sperrt es die Vorschau statt des Versands — MAIL#02) | nach P2 |
| P4 · KAI-Werkzeug + Karte | Vorschau-Tabelle + Freigabe-Knopf, Bestätigungsmail | nach P2 |
| P5 · Backup-Replay | mit dem ersten Backup (Register #907) | wartet auf Backup |
JCs Ziel (sinngemäß): Wer neu in TGV kommt oder Dokumente erhält, hakt beim ersten Zugang alles auf einmal ab: Datenschutz, Nutzungsbedingungen, Widerruf und die Einwilligung, vom Mandanten Werbung zu erhalten. Das soll konform, transparent, extrem einfach und zeitsparend sein, damit TGV und die Mandanten ihr Geschäft entwickeln können. JCs Wunsch dabei: „niemanden reinlassen, der nicht eingewilligt hat, Werbung zu erhalten“.
Was stattdessen geht und JCs Ziel am nächsten kommt: EIN Bildschirm und EIN Klick auf „Weiter“. Pflicht ist nur, was für den Zugang wirklich nötig ist. Die Werbung wird so angeboten, dass möglichst viele freiwillig Ja sagen. Wo das Recht ein Opt-out erlaubt, wird es genutzt.
| Element | Form | Warum so | Protokoll |
|---|---|---|---|
| Nutzungsbedingungen TGV | Pflicht-Kästchen, leer, mit Link | Vertrag über die Nutzung (§ 305 (2) BGB: Hinweis, Kenntnisnahme, Einverständnis; bei Unternehmern reicht nach § 310 (1) ein erkennbarer Verweis — das Kästchen erfüllt beides) | Zustimmungs-Protokoll des Betreibers: Version + Hash des Texts, Zeitpunkt |
| Datenschutzerklärung (TGV + Mandant) | nur Link, kein Kästchen | Das ist eine Information nach Art. 13, keine Einwilligung. Ein Pflicht-Kästchen würde eine Wahl vortäuschen (DSK). | „angezeigt“ mit Version |
| Werbung je Mandant | freiwilliges Kästchen, leer, eines je Mandant mit seinem Rechtsnamen aus entity_signatures.legal_name (nie ein Kürzel wie C3/C4) und dem Thema | Einwilligung nach § 7 (2) Nr. 2 UWG / L34-5 CPCE. Der Zugang funktioniert auch ohne. | contact_consents purpose werbung, evidence formular:zugang@<version> |
| Widerruf | Satz unter dem Kästchen: „jederzeit mit einem Klick, in jeder Mail“ | Art. 7 (3): so leicht wie die Erteilung. Kein Verbraucher-Widerrufsrecht nach § 355 BGB, weil die Eingeladenen nichts kaufen und beruflich handeln. | Abmeldung = neue Zeile widerrufen + Sperreintrag (§7a ⑥) |
| FR · Fachleute (B2B) | Hinweis mit Kästchen „Nein, danke“ (Opt-out) | L34-5 CPCE: berufsbezogene Werbung an Firmenadressen ist ohne Opt-in erlaubt, wenn bei der Erhebung informiert wird und jede Mail einen Widerspruchsweg hat | evidence b2b_info:zugang@<version> (Konvention KONTAKTE#01) |
Ist eine Bestätigungsmail (Double-Opt-in) nötig? Das Double-Opt-in beweist, dass der Inhaber der Adresse selbst eingewilligt hat (BGH I ZR 164/09, DSK Ziff. 3.3). Bei TGV kommt jeder über einen Magic-Link an genau diese Adresse herein (core_shell.html doMagicLink, shouldCreateUser:false). Das Kästchen wird also in einer Sitzung angehakt, die den Besitz der Adresse schon bewiesen hat. ⇒ Empfehlung: keine zweite Mail — mit einer Grenze (MAIL#02): Das gilt nur, wenn die Einwilligung (a) für genau die Login-Adresse erteilt wird und (b) diese Adresse bestätigt ist, also email_confirmed_at gesetzt ist oder die Sitzung per Magic-Link/Code zustande kam. AUTH #2110 baut auch eine Passwort-Anmeldung, und wer so angemeldet ist, hat den Besitz des Postfachs in dieser Sitzung nicht gezeigt. Für eine andere Adresse und für unbestätigte Adressen gilt das Double-Opt-in. Der Login ist sonst die Bestätigung, protokolliert mit Sitzung, Anmeldeart und Zeitpunkt. ⛔ Das ist eine Wertung und kein Urteil; ein Anwalt prüft beide Fälle vor dem Bau.
Gemessen am 19.09. (Zeilen gemessen an origin/master 19.09. abends):
⇒ Jeder Weg führt über den Login. Das Tor sitzt deshalb an EINER Stelle: in onAuth, vor H.onSession. Fehlt die Zustimmung zur aktuellen Fassung der Nutzungsbedingungen, erscheint die Maske. Sonst gibt es keinen Zugang, auch nicht zu einem geteilten Dokument. Ändert sich die Fassung, erscheint die Maske noch einmal, nur mit den Änderungen. Die Maske zeigt Werbe-Kästchen nur für die Mandanten, in denen die Person eine Rolle hat (my_mandant_ids). Die Namen anderer Mandanten sieht sie nie.
| Papier | zwischen | Wirkt in der Maske als | Stand |
|---|---|---|---|
| Nutzungsbedingungen TGV | Betreiber ↔ jede Person mit Zugang (auch Externe) | Pflicht-Kästchen | fehlt · #909 |
| Datenschutzerklärung TGV | Betreiber (für Konto, Login, Protokolle) | Link | fehlt · #909 |
| Datenschutzhinweis je Mandant | Mandant als Verantwortlicher (für seine Kontakte, Projekte, Werbung) | Link je Mandant neben seinem Werbe-Kästchen | fehlt · je Mandant |
| AVV (Art. 28) | Mandant ↔ Betreiber | nicht sichtbar für die Person | Workshop (§7a ⑨ E2) |
| Werbe-Einwilligung | Person → Mandant | freiwilliges Kästchen | Konvention steht (contact_consents, #2141) |
| Impressum | Betreiber | Link im Fuß | fehlt |
Vorbild im Haus für Kästchen mit Zeitstempel-Protokoll und Rechtstext-Gerüst: 00_LEITDOKUMENT/KONZEPT_WEBSITE_FACTORY.md:14,19 (drei Pflicht-Kästchen, Skill website-legal mit Jurist-Gate). ⛔ Die Texte selbst schreibt ein Anwalt (DE + FR); TGV baut nur die Form.
Einmal bestätigen, dann geht es los.
Wie wir und die legal_name Ihre Daten verarbeiten: Datenschutz TGV · Datenschutz legal_name
Weiter* nötig für den Zugang. Alles andere ist freiwillig; Sie kommen auch ohne Ja hinein.
Données personnelles · désinscription en un clic dans chaque e-mail
Continuerlegal_name ist ein Platzhalter: Das Tor gilt je Mandant, der Name kommt aus entity_signatures.legal_name. Beispiel C3: „CONVIS Consult & Marketing GmbH“, C4: „CONVIS Consult & Marketing SARL“. Ein Kürzel steht nie in einer Einwilligung, denn dort muss der Verantwortliche mit seinem Rechtsnamen genannt sein (EDPB Rn. 65). Die Form ist dieselbe wie auf der Login-Karte (#loginView), in derselben Sprache wie der Login-Schalter DE/FR/EN. Auf dem Handy liegen die Kästchen untereinander, die Trefferfläche ist die ganze Zeile. Den Knopf „Weiter“ gibt es EINMAL. Er ist aktiv, sobald das Pflicht-Kästchen gesetzt ist; die freiwilligen Kästchen ändern daran nichts.
| Paket | Inhalt | Voraussetzung |
|---|---|---|
| Z0 · Konzept | dieser Abschnitt + Registerzeile #2153 | geliefert |
| Z1 · Rechtstexte | Nutzungsbedingungen, Datenschutz TGV + Muster je Mandant, Impressum; DE + FR, Anwalt | JC · #909 |
| Z2 · Protokoll (DDL) | Zustimmungs-Protokoll des Betreibers (Person, Dokument, Version, Hash, Zeitpunkt, Oberfläche) — getrennt von contact_consents, denn die Nutzungsbedingungen sind ein Vertrag mit dem Betreiber, keine Einwilligung gegenüber einem Mandanten | PROPOSED, JC fährt |
| Z3 · Tor im Login | Maske in onAuth + Endpunkt; Werbe-Kästchen schreiben über contact-propose (Person noch kein Kontakt ⇒ Vorschlag mit Consent-Zeile) | Z1 + Z2 + Schreibweg für Einwilligungen in contact-propose: heute legt er nur Kontakt und Kanäle an, keine Consent-Zeile. Nötig ist ein optionaler Parameter consents, append-only, evidence Pflicht, nie widerrufen. Das baut KONTAKTE#01 auf Signal. |
| Z4 · Abmelden mit einem Klick | Abmeldelink + List-Unsubscribe-Post (RFC 8058) in jeder Werbe-Mail | MAIL#02 §J5 V1 |
| Z5 · Einladungs-Mail | TGV-Text in der Supabase-Vorlage „Invite user“: wer lädt ein, wofür, Hinweis auf die Maske. Keine zweite Mail über die Mail-Naht, sonst ginge sie doppelt raus. Absender vorerst nur @convis.fr, später je Mandant (JE-177). | AUTH #2110 (mehrsprachige Auth-Vorlagen) |
Quellen (abgerufen am 19.09.2026): EDPB 05/2020 · DSK OH Werbung · CNIL Prospection · § 7 UWG · § 305 BGB · § 310 BGB · OLG Frankfurt 6 U 6/19 · Google Absender-Regeln (One-Click). Weitere Rechtslage zum Anschreiben von Kontakten: Datenfluss §7b. ⛔ Keine Rechtsberatung.
Produktversprechen (JE-196): Der Mandant muss TGV verlassen können und seine Daten vollständig, verständlich und weiterverwendbar zurückerhalten. Geschäftsdaten und im Auftrag erzeugte Ergebnisse gehören zum Datenbestand des Mandanten; Betroffenenrechte bleiben unberührt; Kivisai erzeugt keine künstliche Datenbindung. Quelle: JC_ENTSCHEID_TGV_DATENSCHUTZ_DATENHOHEIT_2026-10-04.md §19–§22, §27–§30.
| Baustein | SOLL (JC 04.10.2026) | IST (gemessen 04.10.2026) |
|---|---|---|
| Export | mindestens Originaldateien · Metadaten · Entitäten · Projekte · Verträge · strukturierte Businessdaten · gespeicherte KAI-Ergebnisse · relevante Chat-/Entscheidungsartefakte · Beziehungen. Chunks/Embeddings sind Ableitungen, kein Pflicht-Primärexport. | kein allgemeiner Export-Endpunkt; Datei-Download nur über file-url; accountant-send ist der einzige Versandweg (Verwalter). |
| Gemeinsames Dateninventar | Was TGV exportieren kann, muss TGV auch finden und kontrolliert löschen können. Inventar: DB-Datensätze · Dateien/Buckets · RAG-Chunks · Embeddings · KAI-Artefakte · Chats · Second Brain/Persona · Mail · Audit · Backups. | kein Inventar als Liste; §7a ④ (Datenart × Entscheid, Personen) und person-loeschen (Vorschau) sind der Anfang für die Personen-Achse. |
| Hartlöschung | Inventar einfrieren → DB-Beziehungen prüfen → RAG/Embeddings entfernen → Storage-Datei löschen → DB-Zeilen entfernen/anonymisieren → Derivate entfernen → Gegenprobe → Erasure-Audit. | kein Lösch-Endpunkt für Belege; privacy_erasure_log 0 Zeilen; Erasure-FK-Design offen (§10). Backup existiert nicht (§6a Härtungs-Reihe). |
| Retention-Matrix | je Datenart: Zweck → Rechtsgrund → Standardfrist → Startpunkt → Löschstrategie → Exportstrategie. TGV-Kanon → zulässiger Mandanten-Override → konkrete Frist. Keine verstreuten Retention-Regeln im Code. | 08_OPS/aufbewahrung_kanon.yaml (ein Kanon), tunable_params, loeschsperren, mandanten.telemetrie_retention_tage — Bausteine ohne gemeinsame Matrix. |
| Data Governance Contract | jede neue persistente Datenart beantwortet vor Produktivgang: ① Scope ② Zugriff ③ Vertraulichkeit ④ KAI ⑤ Retention ⑥ Export ⑦ Erasure (JE-197). Langfristig maschinenlesbare Data Registry als Basis für Guard-Tests, Export, Löschung, TOM, VVT. | nicht vorhanden; die bestehenden Schema-Wächter des Hauses stellen keine dieser sieben Fragen. |
Der Haftungs-Hebel der Vermarktung (AVV/Auftragsverarbeitung): zwei Betriebsmodi, die Awareness-Session ist das menschliche Gate vor Runterstufen/Löschen. Struktur, Fahrplan (M1–M6) und Onboarding → 🏢 Multi-Mandant.
| Modus | Wer steuert Status/Regeln/Löschung | Haftung |
|---|---|---|
| Verwaltet (Default-Start) | Convis/Kivisai kuratiert — der Mandant kann wenig falsch machen | Haftungsschutz beim Betreiber (AVV) |
| Souverän | der Mandant selbst (nach Security-Awareness-Session als Gate) | mehr Kontrolle = mehr Eigenverantwortung (vertraglich abgegrenzt) |
AVV + Lösch-Garantie (§7) sind Verkaufsargument UND Haftungs-Pflicht — beide hängen an diesem Modell. Grundpfeiler: Default-Deny · fail-closed · vollständiges Audit · „im Zweifel strenger". Sub-Processor-Transparenz (wer verarbeitet was, wo) lebt in der Security-Baseline (TGV_CORE_V1_SECURITY_PRIVACY_BASELINE.md).
„First" heißt: die Grenze entsteht mit dem Schema, nicht nachträglich. Analog zur Seitenbau-Lernschleife hat Sicherheit ihre eigene Kette — jede Stufe bindet strukturell (Anti-Amnesie), nichts hängt am Gedächtnis:
| Stufe | Prinzip | Live-Beispiel (bewiesen, nicht behauptet) |
|---|---|---|
| 1 · Schema | Default-Deny entsteht MIT der Tabelle — neue sensible Tabelle = RLS on ab Mig 1 (Standard-Prädikat als Vorlage im SSOT) | STEUER#02 bekam das Trias-Prädikat „beim ersten Apply korrekt" statt Nachrüstung |
| 2 · Guard am Wirkort | Weil service_role RLS bypasst: jeder Read scoped — das Gate sitzt DORT, wo gelesen wird (RPC/Endpoint), nicht nur in der DB | search_knowledge_scoped: conf-Decke + Mandant-Menge IN der Funktion (Mig 221) |
| 3 · Audit jeder Ausnahme | Owner-Bypass und Break-Glass sind erlaubt — aber NIE still | tgv_log_break_glass für JCs anna-Setup-Zugriff (Mig 220) |
| 4 · Awareness-Gate | Der Mensch bleibt Gate vor Runterstufen/Löschen (Human First §2) | Security-Awareness-Session als Souverän-Modus-Voraussetzung |
| 5 · Guard für immer | Jeder Vorfall wird zum verankerten Ritual/Guard — nicht zur toten Doku-Regel. Guard-First (17.07.): fertig ist erst, was ein Guard sichert — und ein Guard, den niemand absichtlich gebrochen hat, ist kein Guard (Negativ-Test-Pflicht; „Guard grün" heißt nur „Guard lief") | Grant-Gotcha beim 221-Apply → Grant-Verify-Ritual im Mig-Kopf; Gate-Beweis als Skript (r5_gate_proof.py) wiederholbar = gelebter Negativ/Positiv-Test · Guard-First |
„Dauerhaft gesichert" ist nur ehrlich mit den offenen Punkten sichtbar — jede Lücke hat eine Owner-Lane. (Früher stand hier auch das rag_chunks-Retrieval-Gate — seit 13.07. gebaut + Gate-bewiesen, §6.)
| Lücke | Warum sie zählt | Owner | Stand |
|---|---|---|---|
| Endpoint-Scope-Guard 2/57 | Nur 2 von 57 Skill-Endpoints validieren den Scope server-seitig; service_role bypasst RLS ⇒ Scope ist heute faktisch Client-Erlaubnis. DIE strukturelle Lücke vor Mehr-Nutzer | MANDANT#01 | 🔴 größte offene Flanke |
| documents-RLS-Härtung | Mig 208 v4 geparkt; kein akutes Leck im Solo-Betrieb, aber Pflicht vor Mehr-Nutzer | MANDANT#01 / PLAT | geparkt |
| Test/Prod-Isolation | BR#04-Incident: E2E-Test schrieb 4 Testbelege in Prod-documents (1 bank-abgeglichen) — Wurzel: kein is_test-Flag + Konsumenten-Filter | ORCH / Plattform | Fix definiert, offen |
| account-status.mandant hardcoded | Der Endpoint liefert heute fest "jcbinetti" (Code-verifiziert im SSOT) — Multi-Mandant braucht die echte Mitgliedschafts-Auflösung | MANDANT#01 | offen (Identitäts-Naht) |
| Menü nicht rollen-/projekt-scoped | core_shell rendert ~31 der ~33 Sidebar-Einträge für JEDEN eingeloggten User; nur super_admin ist gegatet (§4b IST). Ein Projekt-Externer sähe heute das ganze Menü. Sicht-Klassen T0–T4 + min_klasse-Deklaration = SOLL, noch nicht gebaut | SICHTBARKEIT#01 / SHELL | Konzept steht (§4b), Bau + entity_member/extern als DB-Rolle offen |
| Erasure-FK-Design | DSGVO-Art.-17-Endlöschung scheitert heute an FK-Abhängigkeiten (FK-409) — Vorbedingung der Lösch-Garantie (§7). Für Personen (§7a) gemessen 19.09.: contact_consents CASCADE löscht den Nachweis mit (251:209), learning_events.user_id blockiert das Löschen eines Users (050:19), chat_sessions.user_id hat kein FK (203:14) | Plattform · DSGVO#01 | offen · Personen-Teil als PROPOSED bei JC (#2145) |
| gabriel-Stufen-Mapping | Gabriels Eigen-Levels sind noch nicht auf die CORE-Leiter gemappt — fail-closed fängt es ab (unbekannt = streng), aber sauber ist anders | RAG#01 / Gabriel | offen (fail-closed gedeckt) |
| Supabase-Region unbestätigt | Die Security-Baseline selbst sagt: „konkrete Region bestätigen" — für die EU-Garantie (§11) Pflicht-Verifikat, nicht raten | JC / Tower | 1 Blick ins Supabase-Dashboard |
| Gabriel-Embeddings = OpenAI | EU-Bruch im Neben-Pfad: CORE embeddet Mistral (EU), Gabriel noch OpenAI | RAG#01 | nachziehen |
Die Garantie an Mandanten (Ziel-Satz): „Ihre Daten gehen NICHT an US-Firmen und werden nicht zum Training genutzt." Das ist Wettbewerbsvorteil, kein Detail — und darf erst versprochen werden, wenn jede Zeile hier grün ist. Grafisch: §1-Graph, Modus 🇪🇺 EU-Stack (Datenfluss mit EU/US-Färbung). Ehrlicher Stand je Komponente:
| Komponente | Anbieter / Lösung | Standort | Stand (ehrlich) |
|---|---|---|---|
| Embeddings | Mistral mistral-embed (1024) | EU (FR) | ✅ CORE live · ⚠ Gabriel noch OpenAI |
| OCR / Vision | heute docling on-box · Ziel: Mistral OCR (self-host Container) | on-box / EU | docling ✅ · ⚠ letzter US-Vision-Call für Bilder |
| Reranker | mxbai-rerank-large-v2 (mixedbread, self-host, Apache-2.0) | Berlin / self-host | ✅ EU |
| Chat / Generierung | heute Anthropic Claude · Swap-Ziel mistral-large (E1-Naht provider-agnostisch) | US ⚠ | ⚠ DER Kern-Bruch — „Ziel EU, heute US" |
| Datenbank | Supabase / Postgres (alle persistenten TGV-Daten) | Projekt-Region bei Anlage gewählt | ⚠ Region VERIFIZIEREN (Baseline-Prüfpunkt, §10) |
| Datei-Storage | Migration Drive → Hetzner (nbg1), mandanten-getrennt, provider-flexibel (Resolver-Abstraktion Mig 147) | Nürnberg (EU) | Kurs ✅ · Migration ROT (STORAGE#01) |
| Provider-Governance | LLM-Provider per Mandant + Skill konfigurierbar + Cost-Telemetrie | — | ⬜ Ziel — heute hardcoded |