From 9372da5dae6cce92c74efee1a68b4c54a2a5f12c Mon Sep 17 00:00:00 2001 From: "l.kirchner" Date: Thu, 11 Jun 2026 14:11:09 +0200 Subject: [PATCH] feat(nexus-dev): Skill nexus-invariants (SICHT/PRIV/DEL-Implementierungsmuster) --- nexus-dev/skills/nexus-invariants/SKILL.md | 37 ++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 nexus-dev/skills/nexus-invariants/SKILL.md diff --git a/nexus-dev/skills/nexus-invariants/SKILL.md b/nexus-dev/skills/nexus-invariants/SKILL.md new file mode 100644 index 0000000..03b1cf9 --- /dev/null +++ b/nexus-dev/skills/nexus-invariants/SKILL.md @@ -0,0 +1,37 @@ +--- +name: nexus-invariants +description: Security- und Privacy-Invarianten von nexus (Family Knowledge Hub) korrekt implementieren. Verwenden bei JEDER Karte, die Daten liest, LLMs aufruft, Sichtbarkeit beruehrt oder loescht. Deckt ab - ACL-vor-Retrieval (SICHT-1/2), fail-closed LLM-Routing (PRIV-3), Privacy-Klassen, Log-Redaction, Loesch-Kaskade, Pflicht-Testmuster (SICHT-4). +--- + +# nexus Invarianten — Implementierungsleitfaden + +Quelle der Wahrheit: PRD Phase 1 (SICHT-*, PRIV-*, QUEUE-4, DEL-*) und ADR-0004. Dieser Skill übersetzt sie in Code-Muster. + +## ACL-vor-Retrieval (SICHT-1/2) + +- Sichtbarkeit wird **in der Query** durchgesetzt, nie als Python-Nachfilter: jede Repository-/Query-Funktion nimmt den `viewer` als Pflichtparameter und joint/filtert auf dessen sichtbaren Korpus (eigene + an ihn geteilte + familie). +- Es gibt EINEN zentralen Visibility-Baustein (z. B. `visible_to(viewer)`-Query-Helper); Fach-Code baut Sichtbarkeit nie ad hoc nach. +- Nicht-sichtbar verhält sich identisch zu nicht-existent: gleiche 404, gleiche Fehlertexte, keine abweichenden Trefferzahlen oder IDs in Fehlern. +- Auch Suchindex-Queries (FTS) laufen über den Visibility-Helper — niemals erst suchen, dann filtern. + +## Fail-closed LLM-Routing (PRIV-1…4) + +- Fach-Code ruft NIE direkt einen Modell-Endpoint auf — ausschließlich `router.run(task_profile, privacy_class, payload)`. +- Router-Auflösung: exakter Matrix-Eintrag (Task-Profil × Privacy-Klasse) erforderlich. Fehlt er, ist er mehrdeutig oder ist der lokale Endpoint unhealthy ⇒ Job-Status `blocked` + Audit-Event. **Es gibt keinen Codepfad, der auf extern ausweicht** — auch nicht bei Timeouts. +- `privat`/`familie` an externe Endpoints ist auch bei expliziter Policy-Fehlkonfiguration zu verweigern (Hard Guard im Router, zweite Verteidigungslinie vor der Policy). +- Vor jedem Call: Sensitivitäts-Pre-Check (PRIV-6, Regex: Keys/IBAN/Passwörter) → bei Treffer kein LLM-Call, Flag `sensitive-content`, direkt in die Queue. + +## Logging (PRIV-5) + +- structlog mit Redaction-Processor: niemals Bodies, OCR-Text, Titel oder Dateinamen mit Inhaltssemantik in Logs/Fehlermeldungen/Exceptions. IDs + Metadaten ja, Inhalte nein. Gilt auch für Audit-Events (AUDIT-3). + +## Löschung (DEL-*, ADR-0004) + +- Löschen = Referenz entfernen + Kaskade (Entry-Versionen, Suchindex, später Embeddings/Graph) in EINER Transaktion + Audit-Event; Blob-GC bei refcount=0. +- Pflichttest: geteilter Blob — Löschen durch A entfernt nur As Referenz/Derivate, Bs Exemplar bleibt. + +## Pflicht-Testmuster (SICHT-4 — Teil jeder Lese-API) + +Für jede neue Lese-Route/Query parametrisierte Tests mit zwei Nutzern A, B (B besitzt einen privaten Eintrag): +1. A listet → Bs privater Eintrag fehlt; 2. A holt Bs ID direkt → 404 identisch zu nicht-existent; 3. A sucht passende Keywords → kein Treffer; 4. Geteilt-Fall: B teilt mit A → jetzt sichtbar; Familie-Fall analog. +Routing-Test je Karte mit LLM-Beteiligung: Policy-Eintrag entfernen → Job `blocked`, kein Outbound-Call (Mock-Assert).