Zum Hauptinhalt springen

Dokumenten-Verwaltung — NextCloud-Integration (A-1)

Bezug: A-1, R3, R9, S-2, OP-DOC-1 · Status: Richtung gesetzt; MVP-Integrationsweg entschieden (Slice 2, 0.3.0)WebDAV + App-Passwort; öffentlicher Upload-Link (OCS) + Webhook-Push umgesetzt (0.49.0, MED-D-55); Group-Folders bleiben Produktiv-Verfeinerung, OAuth2 bewusst verworfen (OP-DOC-1 Rest).

⚠️ A-1-Revision umgesetzt — R2 ist produktiv aktiv (MED-D-54 → Aktivierung MED-D-61, 2026-07-06, PR #88): Die Ablage (R3) läuft produktiv auf der geschlossenen Eigenverwahrung in Cloudflare R2 (ABLAGE_BACKEND=r2 + Bucket-Binding, §Nachtrag unten). NextCloud wird produktiv nicht mehr benutzt, bleibt aber als umschaltbarer Alternativ-Treiber hinter demselben NextCloudClient-Vertrag erhalten — dieses Dokument beschreibt den NextCloud-Treiber und die Ablage-Architektur (Referenz-Modell, Statusfluss).

Entscheidung (A-1)

Dokumente liegen in der Ablage (R3) — produktiv auf Cloudflare R2 (MED-D-61); NextCloud ist nur ein read-only-Treiber/-Sicht und Medidentas baut kein eigenes DMS (G-1 Standardwerkzeuge-bevorzugen). Medidentas hält pro Dokument nur Referenz + Status + Metadaten (S-2), nicht die Datei-Kopie. NextCloud bringt out-of-the-box: Versionierung, Zugriffsrechte, Verschlüsselung, Sharing-Links, EU-/Self-Hosting (Datenresidenz, G-5).

Referenz-Modell

  • Die Datei lebt in der Ablage (R2 produktiv, MED-D-61; NextCloud nur als read-only-Sicht); nextcloud_pfad (R3-F06, Feldname historisch) ist der stabile Verweis.
  • Status ist sprechend (G-3) und treibt die True-North-Fragen (was fehlt? was ist fällig?).
  • Vollständigkeit ("Onboarding-Dokumente vollständig?") wird aus den Pflicht-Items abgeleitet (G-2), nicht als Flag gespeichert.

Integrationsweg (OP-DOC-1)

MVP-Entscheidung (Slice 2, 0.3.0) — die linke Spalte ist umgesetzt, die rechte bleibt offene Produktiv-Verfeinerung (OP-DOC-1 Rest):

AspektMVP (umgesetzt)Produktiv-VerfeinerungBegründung
APIWebDAV (MKCOL/HEAD/PUT/GET) für Ordner + Datei-CRUDOCS Sharing für öffentliche Upload-Links ✅ (0.49.0); OCS-Group-Folders offenWebDAV deckt Datei-CRUD; OCS ergänzt Shares (kein Ersatz).
AblagestrukturOrdner je Mandant (Medidentas/Mandanten/<Buchstabe>/<Name> (<id>)) + Standard-Unterordner + Eingang/<dokumentId> (File Drop)Group Folders je Team/Rolle (offen)Eindeutiger Pfad genügt; Group Folders koppeln an R7/OP-AUTH-1.
AuthApp-Passwort (Basic Auth, Worker-Secret) — OAuth2 verworfen (MED-D-55)Rotation/least-privilege-NutzerNextCloud-OAuth2 kann kein client_credentials/kein Scoping → für Maschinen-Zugriff ungeeignet; App-Passwort auf Service-Konto ist der korrekte Weg (RISK-16).
BenachrichtigungPush via Webhook Listeners App (NC ≥ 30) ✅ (0.49.0) + idempotenter Pull-Abgleich als Fallback (RISK-6)Push spart Polling; notify_push (WebSocket/Redis) ist für Worker ungeeignet → nicht verwendet.

Umgesetzt (Slice 2): NextCloudClient-Vertrag (server/src/nextcloud/client.ts, WebDAV-Impl + FakeNextCloud für Tests), Ablage-Pfadlogik (domain/ablage.ts), Dokument-Dienst (api/dokument-service.ts: provisionieren · Abgleich · Status + Item-Propagation · Hochladen). Konfiguration über Worker-Secrets NEXTCLOUD_URL/NEXTCLOUD_USER/NEXTCLOUD_APP_PASSWORD (nie im Repo, RISK-8); ohne Konfiguration liefert die API 503 (kein Eigenbau-DMS, G-1).

Anfangsbuchstaben-Ebene + strukturierter Name (D-31 — ✅ umgesetzt, 0.34.0)

Ordner je Mandant liegen unter einer Anfangsbuchstaben-Ebene:

Medidentas/Mandanten/<Buchstabe>/<Anzeigename> (<id>)/<Standard-Unterordner>
  • <Buchstabe> = erster Buchstabe des Anzeigenamens (Person → Nachname): Umlaute/Akzente→Grundbuchstabe (Ä→A, Ö→O, Ü→U, É→E; ß→S), nicht-alphabetisch→#, Großbuchstabe (anfangsbuchstabe() in domain/ablage.ts).
  • <Anzeigename> wird aus strukturierten Namensfeldern nachname/titel/vorname (R1-F08..F10) abgeleitet (G-2, domain/mandant-name.ts) als "Nachname Titel Vorname"; ohne Nachname gilt der Einrichtungs-/Freitextname.
  • Alt-Ordner: bereits provisionierte Ordner bleiben an ihrem gespeicherten Pfad gültig (Dokument-Referenzen je Datei); ein optionaler WebDAV-MOVE-Umzug unter die Buchstaben-Ebene ist ein Folge-Schritt (nicht automatisch, kein Massen-MOVE beim Deploy).

Geplant: Mandanten-Gruppierung (D-32 — noch nicht implementiert)

Mandanten-Gruppierung (D-32, Variante A — vorläufig, Kundenbestätigung offen): Gruppen werden flach + referenziell abgebildet — jede Person behält ihren eigenen Ordner unter ihrem Buchstaben; zusätzlich ein gemeinsamer Gruppenordner für geteilte Dokumente:

Medidentas/Mandanten/<Buchstabe>/<Gruppenname> (grp<id>)/Gemeinsames/

Keine physische Verschachtelung der Personenordner (Variante B verworfen). ⚠️ Weicht von der Kunden-Vorgabe ab → vor Umsetzung bestätigen. Altbestand-Übernahme: OP-IMPORT-1. Bezug: OP-MANDGRP-1.

Sequenz: Dokument anfordern → erhalten

  • Öffentlicher Upload-Link ("File Drop"): je Dokument-Referenz lässt sich ein OCS-Share (shareType=3, permissions=4 = nur Anlegen, publicUpload=true) auf einen dokument-eigenen Ordner …/Eingang/<dokumentId> erzeugen (erstelleUploadLink() im NextCloudClient-Vertrag). Der Kunde lädt ohne NextCloud-Account hoch; Link + Share-Token werden am Dokument persistiert (R3-F10/F11, Migration v25). Route POST /api/dokumente/:id/upload-link (idempotent, optionales Ablaufdatum); UI "Upload-Link" im Mandant-Detail. Schließt den Kreis "Dokument anfordern → Kunde lädt hoch" aus dem Sequenzdiagramm.
  • Webhook (Push statt Polling): öffentlicher Endpunkt POST /oeffentlich/nextcloud/webhook (außerhalb Access, wie /oeffentlich/*) — verifiziert ein Shared Secret (NEXTCLOUD_WEBHOOK_SECRET, Header x-medidentas-webhook-secret). Aus dem gemeldeten Datei-Pfad wird über das Eingang/<dokumentId>- Schema das Dokument exakt zurückgeführt (dokumentIdAusUploadPfad) und angefordert → erhalten gehoben (idempotent). Quelle: Webhook Listeners App (NC ≥ 30, NodeCreatedEvent). Der Pull-abgleich bleibt als Fallback (RISK-6).
  • Auth-Entscheidung (MED-D-55): OAuth2 ist verworfen (NextCloud-OAuth2 unterstützt kein client_credentials/kein Scoping) → App-Passwort auf einem technischen Service-Konto bleibt der Weg; Rotation/least-privilege dokumentieren (RISK-16). NextCloud = ein technischer Service-User, nicht je Benutzer ein Account.

Sandbox-Wurzel + Invariante "nie löschen" (0.49.0, MED-D-56 — ✅ umgesetzt)

  • Sandbox/Test-Trennung: der Ablage-Basisordner (Wurzel) ist über die Var NEXTCLOUD_BASIS_ORDNER konfigurierbar (Default Medidentas/Mandanten). Für eine Sandbox: NEXTCLOUD_BASIS_ORDNER=medidentas-digital-test → alle Dateien liegen unter medidentas-digital-test/<Buchstabe>/<Anzeigename> (<id>)/…, sichtbar getrennt von der Produktion. mandantOrdner() (domain/ablage.ts) ist der einzige Chokepoint → jeder Pfad folgt automatisch. (Alternativ/ergänzend sandboxen ein eigenes NextCloud-Konto NEXTCLOUD_USER oder eine eigene Instanz NEXTCLOUD_URL.)
  • Invariante "nie löschen" (verbindlich): die Anbindung löscht niemals in NextCloud — der NextCloudClient-Vertrag hat bewusst keine delete()/DELETE-Methode (per Test abgesichert). Erlaubt sind nur Anlegen (MKCOL/PUT/OCS-Share), Lesen (HEAD/PROPFIND/GET) und Umbenennen/Verschieben (WebDAV MOVE). put()-Überschreiben ist durch NextCloud-Versionierung verlustfrei. Retention/Löschung ist ein separater, bewusst manueller Prozess außerhalb der Integration (G-4, GoBD/§147 AO).

Alternativen-Check: "Besseres Datei-Werkzeug?" + lokaler Mac-Sync (MED-OP-FILES-1, 2026-07-06)

Anlass: Nutzer-Frage "Was wären bessere Optionen, um mit Dateien zu arbeiten? Nice-to-have: lokaler Sync auf dem Mac." Der Zeitpunkt ist günstig: A-1 ist entschieden und der WebDAV-Code gebaut, aber noch keine echte Instanz live (DEMO_MODE) — Wechselkosten wären jetzt minimal, nach dem Scharfschalten steigen sie (Dokument-Referenzen, Ablagestruktur, Runbooks).

Anforderungsprofil (was das Datei-Werkzeug für Medidentas leisten muss)

AnforderungQuelle
API-Automatisierung: Ordner je Mandant provisionieren, PDF aus dem Worker ablegen, Existenz-Abgleich — heute via WebDAVR3/R9/R12, client.ts/ablage.ts
Öffentlicher Upload-Link (Kunde lädt ohne Konto hoch)geplanter OCS-Share shareType=3 (§Phase 2)
EU-/CH-Datenresidenz + AVV; in Dokumenten stecken Art.-9-Gesundheitsdaten (Gesundheitsbogen R6-F18, signierte Verträge R4)G-5/G-6, OP-DSGVO-1
Versionierung · Rechte/Gruppen · Papierkorb · SharingA-1-Begründung
Neu (nice-to-have): lokaler Sync auf dem Mac — Ablage im Finder bearbeitenNutzer-Wunsch 2026-07-06

Optionen im Vergleich

OptionMac-SyncAPI / WechselkostenResidenz/DSGVOEinschätzung
NextCloud behalten (A-1)✅ Offizieller Desktop-Client; klassischer Sync (selektive Ordner) ausgereift. Der separate Virtual-Files-Client (Apple File Provider) ist Stand 2026 fehleranfällig ("signed out"-Domains, stehenbleibende Syncs) → meiden, bis stabil0 — WebDAV/OCS-Code existiertfrei wählbar EU/CH, AVV je HosterEmpfehlung — Nice-to-have ist ohne Wechsel erfüllt
Infomaniak kDrive (CH)✅ eigene, solide Sync-App (inkl. Lite-Sync)niedrig — WebDAV-Endpunkt vorhanden (https://<ID>.connect.kdrive.infomaniak.com), aber offiziell "ohne Support-Garantie" (zweite Klasse); OCS-Shares fehlen✅ CH-Hosting, DSGVO + revDSG🟡 Plan B, v. a. als CH-Marktsignal
Microsoft 365 / SharePoint+OneDrive✅✅ bester Mac-Sync (File Provider, sehr ausgereift)hoch — Graph-API statt WebDAV = Datei-Adapter neu schreiben⚠️ US-Anbieter (CLOUD Act) trotz EU Data Boundary → bei Art.-9-Daten kritisch🟡 nur falls der Betrieb ohnehin komplett auf M365 lebt
Dropbox Business✅✅ Sync-Referenz auf dem Machoch — proprietäre API⚠️ US-Anbieter, kein Self-Hosting⛔ gleiche Art.-9-Bedenken, kein Zusatznutzen
Tresorit (CH, E2EE)✅ guter Mac-Clientsehr hoch — E2E-Verschlüsselung macht server-seitige Automatisierung (Worker legt PDFs ab, R12) konzeptionell schwer; API schmal✅✅ stärkste Vertraulichkeit⛔ bricht den Orchestrierungs-Kern
Seafile / ownCloud Infinite Scale (self-hosted)✅ schnelle Sync-Clientsmittel (Seafile: eigenes Blockformat; oCIS: WebDAV-nah)wie NextCloud⛔ kein fachlicher Mehrwert ggü. NextCloud, kleineres Ökosystem
Eigenbau auf Cloudflare R2⛔ kein Sync-Client⛔ verworfen: Eigenbau-DMS (G-1)

Ergebnis

  1. A-1 bleibt. Das Nice-to-have "lokaler Mac-Sync" ist mit dem offiziellen NextCloud-Desktop-Client erfüllt: die Ablage Medidentas/Mandanten/… synchronisiert selektiv in den Finder. Klassischen Sync-Client nutzen; den macOS-Virtual-Files-Client (File Provider) vorerst meiden (bekannte Stabilitätsprobleme 2026).
  2. Konkretisierung Hosting (integrate-before-build auch hier): statt Selbstbetrieb ein managed NextCloud (z. B. Hetzner Storage Share, EU, AVV, ab ~5 €/Monat/1 TB, unbegrenzte Nutzer) — Entscheidung beim Kunden/Betreiber → MED-KB-4.
  3. Sync-Rollout ≠ App-Zugriff: die App behält ihren einen technischen Service-User (D-48); Mitarbeiter-Macs bekommen eigene NextCloud-Accounts + Gruppen-Share. Achtung: ein lokaler Voll-Sync umgeht das App-RBAC (D-48, Mandant-Scope) — welche Ordner (mit Art.-9-Inhalten!) auf Mitarbeiter-Geräten liegen dürfen, ist Teil von MED-KB-4; Kontrollpunkt sind dann die NextCloud-Gruppenrechte, nicht die App.
  4. Wechsel-Fenster festgehalten: solange keine echte Instanz live ist, wäre kDrive der günstigste Ausweich (WebDAV-kompatibel); nach Go-Live steigen die Wechselkosten deutlich.

Nachtrag: Variante "Geschlossene Ablage" — Eigenverwahrung (R2) + Zugriff nur über die App-UI (Nutzer-Idee, 2026-07-06)

Idee: Dateien selbst verwahren und ausschließlich über die Medidentas-UI zugänglich machen — kein direkter Datei-Zugriff für Nutzer, damit keine Nutzer-Eingriffe möglich sind (umbenennen, verschieben, löschen, an der App vorbei ändern).

Was das löst (stärker als jede Option oben):

  • RBAC lückenlos (D-48): die App ist der einzige Zugriffsweg — der Mandant-Scope kann nicht per Sync/Finder umgangen werden (genau die Schwachstelle aus §Ergebnis Punkt 3).
  • Zugriffs-Protokollierung lückenlos (D-50/G-6): jedes Öffnen eines Dokuments läuft durch GET /api/dokumente/:id/dateiaktion=eingesehen in der R8-Hash-Kette. Jeder direkte Datei-Kanal (NextCloud-Web-UI, Sync-Client) ist heute ein Loch im Zugriffslog — das schließt nur die geschlossene Ablage.
  • Referenz-Integrität (G-2/G-4): nextcloud_pfad kann nicht durch manuelles Umbenennen/Verschieben brechen; der Reconciliation-Abgleich (RISK-6) entfällt als Fehlerquelle.
  • Weniger Betrieb: kein NextCloud-Hosting, kein zusätzlicher AVV — R2 ist schon im Stack (A-4 Cloudflare, Audit-Cold-Storage nutzt R2 ohnehin als Ziel, OP-AUDIT-1); EU-Jurisdiction bei Bucket-Anlage wählbar; Bucket Locks (GA 2025) liefern WORM-Retention je Prefix → GoBD/§147-AO-Anker.

Was es kostet (der ehrliche G-1-Preis):

  • Eigenbau des tatsächlich genutzten DMS-Ausschnitts. Wichtig: die Anforderung "keine Nutzer-Eingriffe" entwertet den Großteil des NextCloud-Funktionsumfangs selbst (Sharing, Kollaboration, Sync, Web-UI sind dann unerwünscht). Übrig bleibt: Versionierung (nachbaubar: jede Version = neues Objekt, Referenz in D1), Papierkorb (Soft-Delete-Prefix + Bucket Lock), öffentlicher Kunden-Upload (Muster existiert schon: Token-Links /oeffentlich/* beim Onboarding/Fragebogen), Vorschau/Volltextsuche (bewusst verzichtbar, True North braucht sie nicht).
  • Der Mac-Sync-Wunsch entfällt — "keine Nutzer-Eingriffe" und "lokale Dateien im Finder" schließen sich grundsätzlich aus, egal welches Werkzeug. Abgeschwächt möglich: Download/Export aus der UI (read-only).
  • Bearbeiten-Workflow wird spürbar: Word-Entwürfe etc. heißen dann Download → lokal bearbeiten → Re-Upload als neue Version (kein "in der Ablage arbeiten"). Das ist der versteckte Alltagspreis.
  • Worker-Body-Limits bei großen Dateien → S3-Presigned-URLs als Ausweich (Standard-Muster).

Zwei Zuschnitte:

V1 "NextCloud unsichtbar"V2 "R2 + App-UI"
BackendNextCloud bleibt, null menschliche Accounts, nur der technische Service-User (D-48)Cloudflare R2 (EU-Jurisdiction, Bucket Locks)
Aufwand~0 Code; nur Rollout-DisziplinR2-Treiber hinter dem bestehenden NextCloudClient-Vertrag + Versionierung/Papierkorb/Upload-Endpunkte
Bewertungschneller Kompromiss, aber eine ganze PHP-Plattform + Hoster + AVV nur als Blob-Storekonsequenter: ein Anbieter (A-4), Zugriffslog/RBAC by design lückenlos

Einordnung (G-1-Begründung): Eigenbau ist hier begründbar — nicht "DMS nachbauen", sondern: jeder zweite Schreib-/Lesepfad an der App vorbei untergräbt die rechtssichere, lückenlose Dokumentation (G-4) und das Zugriffslog (D-50); der differenzierende Kern (Orchestrierung + Auditierbarkeit) verlangt den einzigen Zugriffsweg. Aber: es ist eine A-1-Revision und widerspricht dem Mac-Sync-Wunsch vom selben Tag → Grundsatzentscheid beim Kunden/Betreiber (MED-KB-4, Punkt 0): offene Ablage mit Sync (Team arbeitet in den Dateien) oder geschlossene Ablage nur über die App (maximale Kontrolle) — beides zugleich geht nicht.

✅ Rollen-Entscheid final (2026-07-20, löst MED-D-54/MED-KB-4 Punkt 0 ab): Verzicht auf NextCloud als Storage — geschlossene Ablage nach V2 "R2 + App-UI" ist der einzige schreibende Pfad. NextCloud ist ausschließlich eine Read-Only-Sicht auf dieselben Dokumente (Notfall-/Katastrophenfall- Zugriff — "hier sind deine Dokumente, du bist handlungsfähig"; kein zweiter Schreibpfad, da manuelles Verschieben/Umbenennen die Ordner-Referenzen zerstören würde). Der NextCloudClient-Vertrag bleibt als Storage-Abstraktion bestehen (R2-Treiber dahinter). Final entschieden ist damit nur die Rolle (read-only, kein zweiter Schreibpfad) — nicht entschieden, und weiterhin echter offener Punkt, ist das Zugriffskonzept dieser Sicht: eine Read-only-Sicht umgeht das Lese-Zugriffslog (D-50), d. h. wer welche Ordner/Rollen sehen darf, ist eine eigene, noch zu treffende Sicherheitsentscheidung — kein reines Umsetzungsdetail. Offen bleiben also zwei Dinge: der Bau dieser Read-Only-Sicht und die Ausgestaltung ihres Zugriffskonzepts (Action-Item A8, Kontakt zum bisherigen NextCloud-Betreiber — kein Code heute, beides kein Teil dieser Entscheidung).

✅ V2 gebaut (0.50.0, MED-D-58) — seit MED-D-61 (2026-07-06, PR #88) produktiv aktiv: server/src/ablage/r2.ts (R2Ablage) implementiert den NextCloudClient-Vertrag gegen Cloudflare R2 — Ordner = Marker-Objekte, put() sichert Vorfassungen unter _versionen/… (verlustfrei), keine Lösch-Operation (MED-D-56 gilt weiter). Der Upload-Link zeigt auf die App-eigene öffentliche Route GET/POST /oeffentlich/ablage/upload/:token (eigenes Formular; Token-Marker mit optionalem Ablaufdatum; Status-Hub angefordert→erhalten + Audit wie beim Webhook) — auch der Kunden-Upload läuft durch die App. Aktivierung (erfolgt mit MED-D-61; Runbook in server/wrangler.toml): Bucket medidentas-ablage mit --jurisdiction eu anlegen (G-5), [[r2_buckets]]-Binding + ABLAGE_BACKEND = "r2" einkommentieren, deployen; optional Bucket Lock (WORM-Retention) als GoBD-Anker. Dienste R3/R12/R4 laufen unverändert; NextCloud-Webhook/OCS-Share werden bei aktivem R2-Backend schlicht nicht mehr benutzt.

Quellen: NextCloud VFS-Client für macOS (Wiki) · VFS-Bugreports 2026 (#8599) · kDrive via WebDAV (Infomaniak-FAQ) · Hetzner Storage Share · R2 Bucket Locks (Retention/WORM, GA 2025)

Offene Punkte

  • OP-DOC-1 (Rest): OCS-Group-Folders je Rolle (R7/OP-AUTH-1). Datei-CRUD (WebDAV), öffentlicher Upload-Link (OCS) und Webhook-Push sind umgesetzt; OAuth2 ist bewusst verworfen (MED-D-55).
  • MED-OP-FILES-1: ✅ Alternativen-Check + Mac-Sync-Antwort dokumentiert (§oben, 2026-07-06). Offen ist nur noch der Rollout-Entscheid Hosting + Sync-Umfang → MED-KB-4.
  • MED-OP-OFFLINE-1: Offline-fähiger Kunden-Upload (V2, /oeffentlich/ablage/upload/:token) — lokal puffern (PWA/Service-Worker + IndexedDB + Background Sync), bei Wiederverbindung senden; Server-Upload ist bereits idempotent. ⚠️ Compliance (G-5/G-6): gepufferte Dateien ggf. Art.-9-Daten → im Browser nur kurz, nach Upload löschen, kein PII in SW-Caches. Fallback wo Background Sync fehlt (Chromium-only).
  • OP-DSGVO-1: AVV mit NextCloud-Hosting, Verschlüsselung at-rest/in-transit, Datenresidenz.
  • Verknüpfung zu R4 Unterschriften: signiertes Dokument fließt nach Abschluss zurück nach NextCloud (siehe Unterschriften.md).