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 demselbenNextCloudClient-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):
| Aspekt | MVP (umgesetzt) | Produktiv-Verfeinerung | Begründung |
|---|---|---|---|
| API | WebDAV (MKCOL/HEAD/PUT/GET) für Ordner + Datei-CRUD | OCS Sharing für öffentliche Upload-Links ✅ (0.49.0); OCS-Group-Folders offen | WebDAV deckt Datei-CRUD; OCS ergänzt Shares (kein Ersatz). |
| Ablagestruktur | Ordner 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. |
| Auth | App-Passwort (Basic Auth, Worker-Secret) — OAuth2 verworfen (MED-D-55) | Rotation/least-privilege-Nutzer | NextCloud-OAuth2 kann kein client_credentials/kein Scoping → für Maschinen-Zugriff ungeeignet; App-Passwort auf Service-Konto ist der korrekte Weg (RISK-16). |
| Benachrichtigung | Push 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()indomain/ablage.ts).<Anzeigename>wird aus strukturierten Namensfeldernnachname/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 + Webhook (0.49.0, MED-D-55 — ✅ umgesetzt)
- Ö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()imNextCloudClient-Vertrag). Der Kunde lädt ohne NextCloud-Account hoch; Link + Share-Token werden am Dokument persistiert (R3-F10/F11, Migration v25). RoutePOST /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, Headerx-medidentas-webhook-secret). Aus dem gemeldeten Datei-Pfad wird über dasEingang/<dokumentId>- Schema das Dokument exakt zurückgeführt (dokumentIdAusUploadPfad) undangefordert → erhaltengehoben (idempotent). Quelle: Webhook Listeners App (NC ≥ 30,NodeCreatedEvent). Der Pull-abgleichbleibt 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_ORDNERkonfigurierbar (DefaultMedidentas/Mandanten). Für eine Sandbox:NEXTCLOUD_BASIS_ORDNER=medidentas-digital-test→ alle Dateien liegen untermedidentas-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-KontoNEXTCLOUD_USERoder eine eigene InstanzNEXTCLOUD_URL.) - Invariante "nie löschen" (verbindlich): die Anbindung löscht niemals in NextCloud — der
NextCloudClient-Vertrag hat bewusst keinedelete()/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)
| Anforderung | Quelle |
|---|---|
| API-Automatisierung: Ordner je Mandant provisionieren, PDF aus dem Worker ablegen, Existenz-Abgleich — heute via WebDAV | R3/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 · Sharing | A-1-Begründung |
| Neu (nice-to-have): lokaler Sync auf dem Mac — Ablage im Finder bearbeiten | Nutzer-Wunsch 2026-07-06 |
Optionen im Vergleich
| Option | Mac-Sync | API / Wechselkosten | Residenz/DSGVO | Einschä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 stabil | 0 — WebDAV/OCS-Code existiert | frei wählbar EU/CH, AVV je Hoster | ✅ Empfehlung — 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 Mac | hoch — proprietäre API | ⚠️ US-Anbieter, kein Self-Hosting | ⛔ gleiche Art.-9-Bedenken, kein Zusatznutzen |
| Tresorit (CH, E2EE) | ✅ guter Mac-Client | sehr 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-Clients | mittel (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
- 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). - 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.
- 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.
- 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/datei→aktion=eingesehenin 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_pfadkann 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" | |
|---|---|---|
| Backend | NextCloud bleibt, null menschliche Accounts, nur der technische Service-User (D-48) | Cloudflare R2 (EU-Jurisdiction, Bucket Locks) |
| Aufwand | ~0 Code; nur Rollout-Disziplin | R2-Treiber hinter dem bestehenden NextCloudClient-Vertrag + Versionierung/Papierkorb/Upload-Endpunkte |
| Bewertung | schneller Kompromiss, aber eine ganze PHP-Plattform + Hoster + AVV nur als Blob-Store | konsequenter: 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).