Gemäß arc42. Stand: aktueller Quellcode (app/src), GeekInfinity.
1. Einführung und Ziele
1.1 Aufgabenstellung
DocMind ist eine selbst gehostete, mandantenfähige Webanwendung zur KI-gestützten Verwaltung, Analyse und Durchsuchung von Dokumenten mittels Retrieval-Augmented Generation (RAG). Nutzer laden Dokumente hoch, die automatisch analysiert (Kategorie, Zusammenfassung, Schlüsselinformationen, erkannte Fristen) und durchsuchbar gemacht werden; ein integrierter Chat beantwortet Fragen direkt auf Basis der eigenen Dokumente.
1.2 Qualitätsziele (Top 5, priorisiert)
- Datensicherheit — Dokumente sind häufig vertraulich (Rechnungen, Verträge, Behördenpost); API-Schlüssel und SMTP-Zugangsdaten liegen verschlüsselt in der Datenbank
- Betreibbarkeit ohne Fremdabhängigkeiten — Bewusster Verzicht auf externe Programme (kein Imagick, kein Ghostscript) — die Anwendung muss auf einem einzelnen Windows-Server lauffähig sein, ohne zusätzliche Systemsoftware
- Nachvollziehbarkeit — Jede sicherheitsrelevante Aktion wird protokolliert und auditiert (
lib/audit.php,lib/log.php) — nichts darf stillschweigend fehlschlagen - Änderbarkeit der Tarifstruktur — Tarife (Limits, Preise) sind vollständig datengetrieben, keine Codeänderung für neue/geänderte Tarife nötig
- Internationalisierung — Durchgängig sechs Sprachen (DE/EN/FR/IT/ES/PL), sowohl in der Oberfläche als auch in systemgenerierten E-Mails
1.3 Stakeholder
- Endnutzer — Einfache Bedienung, verlässliche Analyse, Datenhoheit (jederzeitiger Export, jederzeitige Löschung)
- Administrator — Volle Kontrolle über Nutzer, Tarife und Inhalte, ohne die Instanz versehentlich aussperren zu können
- Betreiber (GeekInfinity) — Wartbarkeit auf einem einzelnen dedizierten Windows-Server, geringe Betriebskosten
2. Randbedingungen
2.1 Technische Randbedingungen
- Zielplattform: Windows Server mit IIS, PHP über FastCGI (
php-cgi.exe), keine Linux-Abhängigkeit - Datenbank: MySQL 8, ausschließlich über gespeicherte Prozeduren angesprochen (mindestens 148 unterschiedliche Prozeduren werden aus dem PHP-Code heraus aufgerufen) — kein eingebettetes SQL im Anwendungscode
- Keine externen Programme: PDF-Verarbeitung ausschließlich über die reine PHP-Bibliothek FPDI, keine Kommandozeilen-Aufrufe an Imagick, Ghostscript o. Ä.
- Zwei getrennte Umgebungen mit eigener Konfiguration: ein lokaler Entwicklungsserver (
C:\inetpub\docmind) und der produktive, eigenständige Server in einem Rechenzentrum — jeweils mit eigenerconfig.php, die bewusst nicht im Git-Quellbaum liegt (Zugangsdaten und Verschlüsselungs-Hauptschlüssel dürfen dort nicht landen)
2.2 Organisatorische Randbedingungen
- Ein-Personen-/Kleinstteam-Betrieb (GeekInfinity), entsprechend hoher Stellenwert von Automatisierung, Protokollierung und Testabdeckung, um Fehler früh und ohne manuelles Nachforschen zu erkennen
- Abrechnung über ein zentrales GeekInfinity-Stripe-Konto (Model A), nicht über ein Stripe-Konto je Kunde
2.3 Konventionen
- Durchgängige Kopfkommentare in jeder Quelldatei, die Warum, nicht nur Was beschreiben (z. B. Begründung für Bibliothekswahl, bewusste Design-Entscheidungen, Verweise auf verwandte Dateien)
- Sicherheitsrelevante Sperren (Guard Rails) werden sowohl in PHP als auch zusätzlich direkt in der jeweiligen gespeicherten Prozedur durchgesetzt — eine PHP-seitige Prüfung allein gilt nicht als ausreichend abgesichert
3. Kontextabgrenzung
3.1 Fachlicher Kontext
3.2 Technischer Kontext — externe Systeme
- DocMind AI — Dokumentenanalyse (Text und eingescannte Seiten nativ), Fristenerkennung, Chat-Antworten sowie die Embeddings (
text-embedding-3-small, 1536 Dimensionen) für die RAG-Suche. Extern realisiert über zwei unterschiedliche Anbieter (einer für Analyse/Chat, einer ausschließlich für die Embeddings) — nach außen und im Anwendungscode durchgängig einheitlich als "DocMind AI" bezeichnet. Die jeweils anbieterspezifischen Bezeichner (Endpunkt, Kopfzeilen, API-Schlüssel-Einstellung, Modellkennung) sind bewusst als Wire-Protocol markiert und dürfen nicht umbenannt werden. Kommunikation über HTTPS/REST - Stripe — Abrechnung, Tarifverwaltung, Webhook-Benachrichtigungen bei Statusänderungen. Kommunikation über HTTPS/REST + Webhook
- SMTP-Server — Versand aller systemgenerierten E-Mails (Bestätigungscodes, Export-Benachrichtigungen, Kalendereinladungen). Kommunikation über SMTP
- DocMind.ExportService — Separater .NET-Windows-Dienst, baut asynchron die Dokumenten-Export-ZIPs — kommuniziert ausschließlich über gemeinsame Datenbanktabellen, nicht direkt mit dem Webserver. Kommunikation über MySQL (gemeinsame Datenbank)
4. Lösungsstrategie
- Datensicherheit → API-Schlüssel und SMTP-Passwörter liegen AES-256-GCM-verschlüsselt in der Datenbank (
lib/apikeys.php), mit einem zentralen, außerhalb des Git-Quellbaums gehaltenen Hauptschlüssel (app_secret) - Betreibbarkeit ohne Fremdabhängigkeiten → Bewusste Bibliothekswahl: FPDI (reines PHP) statt Imagick/Ghostscript für alle PDF-Operationen; eingebaute mbstring-Ersatzfunktionen, falls die PHP-Erweiterung auf einem Hosting-Paket fehlt
- Nachvollziehbarkeit → Zentraler
bootstrap.php-Einstiegspunkt in jedem Seiten-/API-Aufruf installiert globale Fehler-/Ausnahme-/Fatal-Handler, sodass kein Fehler unbemerkt bleiben kann; getrennte, ergänzende Protokollierung (lib/log.php, technisch) und Auditierung (lib/audit.php, fachlich/sicherheitsrelevant) - Änderbarkeit der Tarifstruktur → Tarife liegen vollständig als ein JSON-Datensatz in der Einstellungstabelle (
billing.tiers), über die Admin-Oberfläche editierbar — keine Codeänderung für neue oder geänderte Tarife - Internationalisierung → Ein zentrales i18n-Modul (
lib/i18n.php) mit sechs vollständigen Sprachdateien; Übersetzungsschlüssel statt fertiger Texte werden zwischen Backend und Oberfläche ausgetauscht - Datenintegrität bei kritischen Aktionen → Sicherheitsrelevante Sperren (z. B. "Administrator darf sich nie selbst löschen") werden doppelt durchgesetzt — einmal in PHP, zusätzlich direkt in der jeweiligen gespeicherten Prozedur, sodass ein direkter Prozeduraufruf unter Umgehung von PHP die Sperre nicht aushebeln kann
5. Bausteinsicht
5.1 Ebene 1 — Gesamtsystem
Drei eigenständig lauffähige Bausteine, verbunden über die gemeinsame Datenbank (Webanwendung ↔ Export-Dienst) bzw. über die REST-Schnittstelle der Webanwendung (Mobile-App ↔ Webanwendung).
5.2 Ebene 2 — Webanwendung, Baustein-Übersicht (lib/)
Die Webanwendung gliedert sich fachlich in folgende Modulgruppen (app/src/lib/*.php):
- Fundament (
bootstrap.php,db.php,log.php,audit.php,net.php,helpers.php,i18n.php) — Zentraler Einstiegspunkt, Datenbankzugriff, Protokollierung/Auditierung, Hilfsfunktionen, Übersetzung - Konto & Zugriff (
auth.php,account.php,register.php,totp.php,deletion.php,cancellation.php,consent.php) — Anmeldung, Registrierung, Zwei-Faktor-Authentifizierung, Kontolöschung, Abo-Kündigung, Zustimmungsverfolgung - Administration (
users.php) — Nutzerverwaltung mit eingebauten Schutzmechanismen gegen Selbstaussperrung - Dokumente (
extract.php,pdf_assemble.php,pdf_split.php,versions.php,shares.php) — Textextraktion, PDF-Zusammenfügen/-Aufteilen, Versionsverwaltung, Freigaben - KI & Suche (
ai.php,rag.php,analysis_preview.php,deadlines.php,chat_history.php) — Anbindung an DocMind AI, RAG-Pipeline, Analyse-Vorschau, Fristenerkennung, Chat-Verläufe - Abrechnung (
billing.php,tier_content.php) — Stripe-Anbindung/Tarifverwaltung, Marketing-Texte je Tarif - Kommunikation (
mailer.php,smtp.php,ical.php) — E-Mail-Versand, SMTP-Konfiguration, Kalendereinladungen - Sicherheit (
apikeys.php) — Verschlüsselte Ablage von API-Schlüsseln/Passwörtern - Hilfe & Recht (
help.php,help_docs.php,legal.php,legal_store.php) — Zustandsgetriebene Hinweise, Hilfetext-Verwaltung, Rechtstexte mit Versionierung - Export (
export.php) — Aufbau der Exportanfrage (Bereitstellung der Daten, die der separate Windows-Dienst asynchron abholt)
5.3 Ebene 2 — API-Schicht (api/)
Jeder Endpunkt in app/src/api/*.php bindet zu Beginn bootstrap.php ein und delegiert die eigentliche Logik an das jeweils zuständige lib/-Modul — die API-Dateien selbst enthalten bewusst wenig eigene Logik, sondern binden Anfrage/Antwort an die Fachmodule an.
5.4 Ebene 2 — DocMind.ExportService (.NET)
ExportPollingJob— Fragt zeitgesteuert (Quartz) die Warteschlange offener Exportaufträge abExportProcessor— Baut das passwortgeschützte ZIP (SharpZipLib), verschickt die Benachrichtigungs-E-Mail (MailKit)ExportCleanupJob/ExportCleanupProcessor— Stündliche Aufräumung abgelaufener, nie abgeholter ExporteMySqlExportDatabase— Datenbankzugriff des Dienstes, über dasselbe Schema wie die Webanwendung
6. Laufzeitsicht
6.1 Szenario: Dokumenten-Upload und -Analyse
- Nutzer lädt eine Datei hoch (
api/upload_init.php→upload_chunk.php→upload_finalize.php) — vor dem endgültigen Speichern werden Speicherlimit und Duplikate geprüft api/analyze_doc.phpstößt die Analyse an: Text wird extrahiert (lib/extract.php) oder bei PDFs direkt an DocMind AI übergeben (native Lesefähigkeit für Text und eingescannte Seiten)- DocMind AI liefert strukturiert Kategorie, Zusammenfassung, Schlüsselinformationen, Schlagworte, Lesemethode, Dokumentdatum — und, im selben Aufruf, erkannte Fristen (kein zweiter, kostenpflichtiger KI-Durchgang)
- Ergebnis wird über
update_analysis()gespeichert, inklusive automatischer Versionierung - Der Dokumenttext wird zusätzlich in Abschnitte zerlegt und über DocMind AI in Vektoren umgewandelt (
lib/rag.php) — Grundlage für spätere Chat-Anfragen zu diesem Dokument - Falls Fristen erkannt wurden: optionaler Versand einer echten Kalendereinladung (
lib/ical.php) über den konfigurierten SMTP-Server
6.2 Szenario: Kontolöschung
- Nutzer bestätigt in den Kontoeinstellungen die Löschabsicht mit dem aktuellen Passwort (
api/account_delete_start.php) — ein 6-stelliger Code wird per E-Mail verschickt - Nutzer gibt den Code ein (
api/account_delete_verify.php) del_finalize()(lib/deletion.php) läuft sequenziell und fehlertolerant: a. Export aller Dokumente wird gebaut (damit auch bei einem späteren Fehler in Schritt b/c nichts unwiderruflich verloren geht) b. Ein eventuell bestehendes Stripe-Abo wird sofort gekündigt — schlägt dies fehl, wird die Löschung trotzdem fortgesetzt, der Fehlschlag wird eigens auditiert, damit ein Administrator manuell nachfassen kann c. Das Konto und alle zugehörigen Daten werden über die gespeicherte Prozedursp_user_deletein einer einzigen Transaktion entfernt- Nutzer erhält eine Bestätigung, bei fehlgeschlagener Stripe-Kündigung zusätzlich einen Hinweis, den Support zu kontaktieren
Diese doppelte Absicherung (Datenbank-Riegel und PHP-Prüfung) verhindert insbesondere, dass sich ein Administrator selbst löschen kann — auch nicht über einen direkten, PHP umgehenden Aufruf der gespeicherten Prozedur.
7. Verteilungssicht
7.1 Umgebungen
- Lokaler Entwicklungsserver — Entwicklung, Test. Eigene lokale MySQL-Instanz, eigene
config.php, PHPUnit-Testsuite (app/tst) läuft gegen diese Instanz - Produktion (Rechenzentrum) — Live-Betrieb. Eigenständiger, dedizierter Windows-Server (kein Shared Hosting), eigene
config.phpmit den tatsächlichen Zugangsdaten und dem produktiven Verschlüsselungs-Hauptschlüssel
Beide Umgebungen laufen mit identischem Aufbau (IIS + FastCGI + php-cgi.exe, dieselbe Ordnerstruktur, dieselbe MySQL-Version) — bewusst, damit ein lokal erfolgreich getesteter Stand ohne Überraschungen produktiv übernommen werden kann.
7.2 Knoten auf dem Produktivserver
IIS leitet Anfragen per FastCGI an php-cgi.exe (die DocMind-Webanwendung) weiter. Sowohl php-cgi.exe als auch der separate DocMind-Dienst greifen unabhängig voneinander auf dieselbe lokale MySQL-8-Datenbank zu.
7.3 Git-Quellbaum vs. tatsächlich deployte Dateien
Der Git-Quellbaum (app/src) enthält bewusst keine config.php mit echten Zugangsdaten — jede Umgebung führt ihre eigene, nicht versionierte config.php direkt im deployten Ordner. Ein Kopiervorgang aus dem Quellbaum überschreibt daher nie versehentlich die Umgebungskonfiguration.
Projektstruktur im Git-Repository:
app/src— Die PHP-Webanwendung selbstapp/tst— PHPUnit-Testsuite für die Webanwendungsvc— DocMind.ExportService (.NET-Windows-Dienst), inkl. eigener xUnit-Testsuitemaui— DocMind.Mobile (.NET-MAUI-App, Android und Windows)db— Datenbank-Sicherungen/Schema-Exportedoc— Begleitdokumentation
8. Querschnittliche Konzepte
8.1 Datenzugriff ausschließlich über gespeicherte Prozeduren
Kein Anwendungscode enthält eingebettetes SQL. Jeder Datenbankzugriff läuft über benannte gespeicherte Prozeduren (sp_*), aufgerufen über einheitliche Hüllfunktionen (db_call(), db_call_row(), db_call_out() in lib/db.php). Vorteil: Geschäftslogik mit Sicherheitsrelevanz (z. B. "Administrator darf sich nie selbst löschen") lässt sich direkt in der Datenbank erzwingen, unabhängig davon, über welchen Weg die Prozedur aufgerufen wird.
8.2 Protokollierung und Auditierung, bewusst getrennt
lib/log.php— technische Protokollierung (jede Anfrage, Fehler, Zeitmessung), primär zur Fehlersuchelib/audit.php— fachliche, sicherheitsrelevante Ereignisse (Löschungen, Rollenänderungen, fehlgeschlagene Berechtigungsprüfungen), dauerhaft nachvollziehbar, unabhängig von der technischen Protokollierung
bootstrap.php installiert in jedem Einstiegspunkt globale Fehler-, Ausnahme- und Fatal-Handler — ein unbehandelter Fehler kann nicht stillschweigend zu einer leeren weißen Seite führen, sondern wird immer protokolliert.
8.3 Internationalisierung
Sechs vollständig gepflegte Sprachen (Deutsch, Englisch, Französisch, Italienisch, Spanisch, Polnisch) — sowohl für die Benutzeroberfläche als auch für systemgenerierte E-Mails. Backend und Frontend tauschen ausschließlich Übersetzungsschlüssel aus (t() im Browser, entsprechende PHP-Gegenstücke serverseitig), nie fertig übersetzten Text.
8.4 Verschlüsselung sensibler Konfigurationswerte
API-Schlüssel und SMTP-Passwörter werden nie im Klartext in der Datenbank abgelegt — AES-256-GCM-Verschlüsselung mit einem zentralen Hauptschlüssel (app_secret, Teil der umgebungsspezifischen config.php). Ein Schlüsselwechsel erfordert ein eigenes Neu-Verschlüsselungs-Werkzeug (setup_rekey_secret.php), da bestehende verschlüsselte Werte sonst unlesbar würden.
8.5 Zweistufige Bestätigung bei sicherheitsrelevanten Kontoänderungen
E-Mail-/Passwort-Änderung, Kontolöschung und Abo-Kündigung folgen demselben Muster: Die Änderung wird nicht sofort übernommen, sondern erst nach Eingabe eines per E-Mail zugestellten, zeitlich begrenzten 6-stelligen Codes wirksam — mit Versuchszähler und Sperre nach zu vielen Fehlversuchen.
8.6 Selbstschutz der Administration
lib/users.php verhindert strukturell, dass sich eine Instanz selbst aussperrt: Ein Administrator kann sich weder selbst deaktivieren noch sich selbst die Administrator-Rolle entziehen noch sich selbst löschen, und der letzte verbleibende aktive Administrator kann nicht entfernt werden.
9. Architekturentscheidungen
- FPDI statt Imagick/Ghostscript für PDF-Verarbeitung — Reines PHP, keine zusätzliche Systemsoftware auf dem Zielserver nötig. Verworfene Alternative: Imagick/Ghostscript, hätte externe Programme und deren Wartung erfordert
- Ein zentrales GeekInfinity-Stripe-Konto statt Stripe-Konto je Kunde — Geringerer Verwaltungsaufwand, einfachere Abrechnungslogik. Verworfene Alternative: Stripe Connect / eigenes Konto je Kunde
- Fristenerkennung im selben KI-Aufruf wie die Dokumentanalyse — Vermeidet einen zweiten, kostenpflichtigen KI-Durchgang; das Modell sieht Frist und Bezugsdatum im selben Kontext. Verworfene Alternative: separater, dedizierter Erkennungsdurchlauf
- Löschung bei fehlgeschlagener Stripe-Kündigung trotzdem fortsetzen — Nutzerinteresse (Kontolöschung muss immer greifen) hat Vorrang vor einer sauberen Abrechnungskündigung; Fehlschlag wird stattdessen auditiert statt die Löschung zu blockieren. Verworfene Alternative: Löschung bei Kündigungsfehler komplett abbrechen (frühere Umsetzung, bewusst geändert)
- Sicherheits-Riegel doppelt (PHP und gespeicherte Prozedur) — Ein direkter Datenbankzugriff unter Umgehung der Anwendung darf kritische Regeln nicht aushebeln können. Verworfene Alternative: alleinige Absicherung in PHP
- DocMind AI über zwei spezialisierte Anbieter im Hintergrund realisiert — Für Embeddings und für Analyse/Chat kommt jeweils der Anbieter zum Einsatz, der für die konkrete Teilaufgabe die passende Stärke mitbringt, statt sich auf einen einzelnen Anbieter für beides festzulegen; nach außen und im Anwendungscode einheitlich als "DocMind AI" bezeichnet. Verworfene Alternative: ausschließlich ein Anbieter für Embeddings und Antworten
10. Qualitätsanforderungen im Detail
Qualitätsbaum (Auszug, oberste Ebene)
- Sicherheit → Vertraulichkeit (Verschlüsselung sensibler Konfigurationswerte, 8.4), Integrität (doppelte Riegel PHP+DB, 8.1/8.6), Nachvollziehbarkeit (Auditierung, 8.2)
- Betreibbarkeit → Wartungsarm (keine externen Programme, 2.1), Selbstschutz (Admin-Guard-Rails, 8.6)
- Änderbarkeit → Tarife (datengetrieben, 4), Sprachen (zentrales i18n-Modul, 8.3)
Konkretisierte Szenarien
- Ein Administrator versucht, sich selbst zu löschen (auch per direktem Datenbankzugriff) → wird sowohl in PHP als auch in der gespeicherten Prozedur abgelehnt
- Die Stripe-Kündigung schlägt während einer Kontolöschung fehl → das Konto wird dennoch vollständig gelöscht; der Fehlschlag wird auditiert
- Ein neuer Tarif soll ergänzt werden → reine Konfigurationsänderung über die Admin-Oberfläche, keine Codeänderung
- Die PHP-
zip-Erweiterung fehlt auf dem Zielserver → der Dokumenten-Export schlägt mit einer klaren, protokollierten Fehlermeldung fehl, statt unbemerkt zu scheitern
11. Risiken und technische Schulden
- Abhängigkeit von zwei externen KI-Anbietern hinter DocMind AI — Beide der intern genutzten Anbieter (einer für Analyse/Chat, einer für die Embeddings) müssen erreichbar sein, damit Analyse bzw. RAG-Suche funktionieren. Umgang: kein automatischer Ausweichanbieter vorgesehen — bewusst in Kauf genommen angesichts der geringen Systemgröße
- Betrieb ohne Hochverfügbarkeit — Ein einzelner dedizierter Server, keine automatische Ausfallsicherung. Umgang: angemessen für die aktuelle Betriebsgröße; müsste bei signifikantem Wachstum neu bewertet werden
- Manuelle Nachbearbeitung bei fehlgeschlagener Stripe-Kündigung — Kein automatischer Wiederholungsversuch, sondern Verweis an den Support. Umgang: bewusste Entscheidung (siehe Abschnitt 9), da Priorität auf sicherer Kontolöschung liegt
- PHP-Erweiterungsverfügbarkeit je Server — Muss auf jedem neuen/migrierten Server einzeln geprüft werden (z. B.
zip-Erweiterung), keine automatisierte Prüfung beim Deployment. Umgang: ließe sich durch ein Vorab-Prüfskript beim Deployment absichern (aktuell nicht vorhanden)
12. Glossar
- RAG (Retrieval-Augmented Generation) — Technik, bei der relevante Textabschnitte per Ähnlichkeitssuche gefunden und als Kontext an ein Sprachmodell übergeben werden, statt das Modell allein aus seinem Trainingswissen antworten zu lassen
- Embedding — Numerische Vektordarstellung eines Textabschnitts, die inhaltliche Ähnlichkeit über den Abstand zweier Vektoren abbildbar macht
- Gespeicherte Prozedur (Stored Procedure) — Vorab in der Datenbank hinterlegte, benannte SQL-Routine — hier der einzige erlaubte Weg für jeden Datenbankzugriff
- Mandantenfähig — Eine einzelne Anwendungsinstanz bedient mehrere, voneinander unabhängige Nutzerkonten
- Guard Rail — Eingebaute Schutzregel gegen eine bestimmte, unerwünschte Aktion (z. B. Selbstlöschung eines Administrators)
- FastCGI — Protokoll, über das IIS PHP-Anfragen an den
php-cgi.exe-Prozess weiterreicht - FPDI — Reine PHP-Bibliothek zur PDF-Manipulation, ohne externe Systemabhängigkeiten
- Wire Protocol (im Kontext von
lib/ai.php) — Bezeichner (Endpunkt, Kopfzeilen, Modellname), die exakt der externen Schnittstelle entsprechen müssen und nicht umbenannt werden dürfen