OCR-Worker pausieren optional in konfigurierbarem Zeitfenster
(paused_hours), Jobs bleiben pending statt verworfen zu werden.
IMAP-Scheduler verteilt Sync-Starts via deterministischem
Pro-Account-Jitter, um Lastspitzen bei vielen Postfächern mit
gleichem Intervall zu vermeiden. Beides per Config opt-out,
Default-Verhalten unverändert. Build + Smoke-Test auf 132 verifiziert.
PageSkeleton, das shadcn Toast-System (toaster/sonner/toast/use-toast)
und das sonner-Paket waren im Code nirgends importiert. Build erfolgreich
verifiziert nach Entfernung.
systemctl is-active zeigt nur, dass der Prozess läuft — nicht, dass er
tatsächlich auf Anfragen antwortet (z.B. bei hängendem DB-Connect).
Beide Dienste werden jetzt per HTTP gegen /api/health (Backend) bzw.
Port 3000 (Frontend) mit Retry (15x1s) geprüft, bevor das Update als
erfolgreich gemeldet wird.
Der automatische Reindex lief bisher bei jedem Deploy mit (volle
Mailmenge), obwohl er nur einmalig für die Xapian→Manticore-Migration
gedacht war. Neue Mails werden ohnehin synchron beim Import indiziert
(IndexSync). Reindex bei Bedarf (z.B. Schema-Änderungen) jetzt manuell.
Kritischer Sicherheitsbug: Auditoren mit zugewiesenem Tenant sahen Mails
und Audit-Log-Einträge anderer Tenants (DSGVO-relevant). auditor wird
jetzt analog zu domain_auditor pro Tenant gescoped, sofern tenant_id
gesetzt ist (Abwärtskompatibilität: ohne tenant_id bleibt der bisherige
globale Zugriff erhalten). Betrifft Mail-Suche, Mail-Detailzugriff,
Export, eDiscovery, Threads, OCR sowie das Audit-Log (DB + tamper-evidentes
Flat-File), inkl. Befüllung von tenant_id an allen Audit-Log-Schreibstellen.
Behebt 5 von 7 gemeldeten Schwachstellen (picomatch ReDoS/Method-Injection,
mehrere Next.js-Advisories) via semver-konformer Patch-Updates in
package-lock.json. Verbleibende 2 moderate (postcss <8.5.10) sind in
next@16.2.9 selbst gebündelt, kein stabiler Fix verfügbar (nur 16.3.0
canary/preview) — npm audit fix --force würde next auf 9.3.3 downgraden.
Filtert die Mails des Nutzers (From/To/Cc/Bcc) jetzt serverseitig via
req.AnyAddress vor LIMIT/OFFSET im Index, statt wie bisher per
Post-Filter nach der Paginierung. total und totalPages stimmen damit
mit den tatsächlich sichtbaren Treffern überein.
Der Admin-Endpoint "Firewall aktivieren" (POST /api/admin/security/fix,
enable_firewall) ruft "nft -f /etc/nftables.conf" auf. flush ruleset
benötigt CAP_NET_ADMIN, das fehlte bisher in der systemd-Unit, wodurch
der Aufruf mit "Operation not permitted" fehlschlug.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Architektur-Entwurf: neue dsgvo_requests-Tabelle, Admin-Workflow "DSGVO-Anfragen"
(Antrag -> Suche -> retain_until-Prüfung -> Ablehnung/Löschbar -> Export/Löschung),
Erweiterung des Manticore-Index um cc_addr/bcc_addr (inkl. Reindex-Hinweis). Status
auf In Progress gesetzt.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
PROJ-51 (Aufbewahrungsfristen nach Dokumentenart, inkl. minimaler PROJ-43-Basis)
ist auf Test (132) und Produktion (131) deployed und verifiziert.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- search_handlers.go: retain_until_source wird nur noch an Rollen != user
ausgegeben, um interne Archivierungsregel-IDs nicht an normale
Endbenutzer zu exponieren
- cmd_status.go: archivmail status zeigt [WARN] statt [OK] wenn Detail
mit "WARNUNG" beginnt (z.B. PROJ-51 Retention-Check); Exit-Code/r.OK
bleibt unverändert
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fuehrt archiving_rules ein (PROJ-43-Basis: Tabelle + CRUD-API + Admin-UI) und
erweitert die Retention-Logik (PROJ-34) um Regel-basierte Fristen, eine
globale Mindestfrist (min_retention_days) sowie Nachvollziehbarkeit der
Frist-Quelle (retain_until_source) in API und Mail-Detailansicht.
Architektur-Entwurf: archiving_rules-Tabelle (minimale PROJ-43-Basis) + CRUD-API +
Regel-Verwaltung im Admin-Bereich, erweitert um retention_days pro Regel und globale
Mindest-Aufbewahrungsfrist (min_retention_days). Status auf In Progress gesetzt.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Erfolgreich auf Test (192.168.1.132) und Produktion (192.168.1.131)
ausgerollt und verifiziert.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
storage.loadKey() startet bei fehlendem/unlesbarem/ungültigem Keyfile
weiterhin unverschlüsselt (kein Hard-Fail), aber:
- einmalige WARN-Logzeile beim Start mit konkretem Grund
- neuer Healthcheck-Prüfpunkt "Encryption" in archivmail status
- Dashboard-API liefert encryption.enabled
- README: GoBD-Hinweis zu storage.keyfile
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Erfolgreich auf Test (192.168.1.132) und Produktion (192.168.1.131)
ausgerollt und verifiziert.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Gleiches Muster wie bei IMAP (730099d): domain_admin konnte POP3-Konten
fremder Tenants auflisten, löschen und Importe/Progress fremder Tenants
ansehen, da pop3_accounts keine tenant_id hatte und Store.List() für
Admins ungefiltert alle Konten lieferte.
- pop3_accounts: neue Spalte tenant_id (ALTER TABLE ADD COLUMN IF NOT EXISTS)
- Store.List() filtert nach tenant_id, außer für superadmin
- Store.Create() setzt tenant_id beim Anlegen
- delete/start-import/progress prüfen zusätzlich tenantAccessAllowed()
domain_admin sah und konnte IMAP-Konten (inkl. Credentials) fremder
Tenants auflisten, löschen, synchronisieren und umkonfigurieren, da
Store.List() für Admins ungefiltert alle Konten lieferte und die
Einzelhandler nur den Owner, nicht den Tenant prüften.
- Store.List() filtert jetzt nach tenant_id, außer für superadmin
- Store.Create() setzt tenant_id beim Anlegen
- Alle Einzelhandler (delete/start-import/progress/sync/update)
prüfen zusätzlich tenantAccessAllowed()
ldap_url kommt via LEFT JOIN tenant_ldap und ist NULL für Mandanten
ohne LDAP-Konfiguration. Scan in *string schlug fehl ("cannot scan
NULL into *string") und ließ GET /api/admin/quotas mit 500 fehlschlagen
("Quota-Daten konnten nicht geladen werden").
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Permanenter Link zur Admin-Login-Seite unterhalb der bestehenden
Links (Passwort vergessen / Registrieren), statt nur als Hinweis
nach einem fehlgeschlagenen Admin-Login-Versuch.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
IMAP- und POP3-Importer haben Mails immer nur in emails_global
indexiert (TenantID nie gesetzt, idxMgr.Global() statt
ForTenant(tenantID)). Dadurch fehlten neue Mails ab dem letzten
Server-Neustart im Tenant-Index (Suche zeigte veraltete Ergebnisse).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Signup ohne Invite-Token gibt 400 zurück (war: optional)
- Use() statt Peek() vor User-Erstellung: verhindert TOCTOU bei parallelen
Requests mit demselben Token und Enumeration via "Token noch gültig?"
- invite_used Audit-Eintrag ergänzt
- Doppeltes IsConfigured()-Check entfernt
- Frontend: ohne ?invite= im URL wird Formular nicht gerendert
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Serves the static OpenAPI YAML via go:embed. Completes the last
open acceptance criterion for PROJ-13. PROJ-44 marked Deployed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- SEC: requireMailAccess auf GET /api/threads/{threadID} — superadmin/domain_admin konnten Mail-Metadaten lesen
- SEC: requireMailAccess auf POST /api/export/ediscovery — superadmin/domain_admin konnten bis zu 10k EML exportieren
- SEC: V1-API user-role Keys müssen 'contact=' angeben — verhindert vollständige Tenant-Enumeration
- SEC: Domain-Regex-Validierung in handleCertACME vor filepath.Join und certbot-Aufruf
- docs: README und config.test.yml auf Manticore Search aktualisiert (kein Xapian mehr)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
PMG mit German-Locale erzeugt "So, 24 Aug 2025 00:05:17 +0200" statt
"Sun, ..." — Go's net/mail und alle bisherigen Fallbacks scheitern daran.
Fix: Wochentag-Präfix (≤3 Zeichen vor dem ersten Komma) abschneiden
und erneut mit den numerischen Offset-Formaten parsen.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
mailparser: weitere Layouts (Timezone +02:00 mit Doppelpunkt, ohne Sekunden)
storage: GetReceivedAts() für Batch-Lookup von received_at
search_handlers: received_at als Fallback wenn pm.Date.IsZero()
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Next.js/Turbopack kann sich den Workspace-Root falsch ableiten wenn
im Parent-Verzeichnis des Build-Dirs eine package-lock.json liegt
(z.B. durch alten Deploy-Stand). __dirname als expliziter Root behebt
die Warnung dauerhaft.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
toLocaleDateString → toLocaleString mit dateStyle+timeStyle, zeigt
jetzt z.B. "11.05.2026, 00:05" statt nur "11.05.2026".
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
resolveTenant() now tries RCPT TO first, then falls back to parsing
To/Cc/From headers. Needed because BCC-journaled mails arrive with
RCPT TO = the archive's own BCC address, not the real recipient's domain.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- scheduler.go: BUG-1 fix — preserve stored uid_validity when server returns 0
- scheduler.go: BUG-2 fix — replace inline switch with DecideResync() call
- scheduler.go: SetAuditLogger wired; imap_uidvalidity_reset audit event
- cmd_reindex.go: read existing attachment_text before IndexSync to prevent
Manticore REPLACE INTO from wiping OCR text written by the OCR worker
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Strukturbug auf 132 gefunden: Tenant-User (Rolle user) sahen ihren
OCR-Text nicht, obwohl ocr_chars>0 in PostgreSQL stand. Ursache:
- OCR-Worker hat in den per Job.TenantID gewaehlten Index geschrieben.
Beim Reprocess via CLI kam TenantID aus dem Submitter-Kontext und
konnte vom in emails.tenant_id gespeicherten Wert abweichen.
- /ocr-text-Endpoint hat fuer die Index-Auswahl session.TenantID
benutzt. Bei Admin/Auditor (nil Session-Tenant) wurde immer global
gelesen, auch wenn die Mail einem Tenant gehoert.
Fix: Beide Stellen lesen jetzt die TenantID **immer** aus
storage.GetTenantForMail(emails.tenant_id) und routen den
Manticore-Index entsprechend. ACL-Check im Endpoint bleibt
unveraendert auf session.TenantID == mail.tenant_id — die
Tenant-Isolation wird nicht aufgeweicht.
Edge cases:
- Mail mit tenant_id NULL: GetTenantForMail liefert nil -> globaler
Index (vorher und nachher gleich).
- DB-Fehler beim Lookup: faellt auf nil zurueck -> globaler Index,
liefert leeren Text fuer Tenant-Mails -> 404. Safe (keine
Querleckage zwischen Tenants).
CALL SNIPPETS liefert einen anderen MySQL-Pakettyp als SELECT, den der
Go-Treiber (go-sql-driver/mysql) mit "malformed packet" ablehnt.
SELECT SNIPPET(text, query) FROM table ist die korrekte Alternative
fuer Manticore 25.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Die enrichedHit-Struct in search_handlers.go fehlten die PROJ-44-Felder
Snippet und MatchField, sodass die vom Index berechneten Snippets in der
API-Response verworfen wurden.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Manticore 25.0.0 wirft SNIPPETS() expects exactly 3 arguments wenn
zusätzliche Options-Strings übergeben werden. Standard-Marker <b>/<b/>
sind Manticore-Default, daher sind keine Options nötig.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Bisher haben nur der SMTP-Pfad und der Boot-Backfill ocrWorker.Submit
gerufen. IMAP- und POP3-Importer riefen nur idx.IndexSync auf —
neue Mails blieben dadurch dauerhaft in ocr_status='pending' (auf 132
44 Tage 54 Mails so haengen geblieben).
Fix: Importer-Strukturen bekommen einen optionalen ocrSubmit-Callback,
in main.go via SetOCRSubmit gehookt. Kein Import von internal/ocr in
die Importer-Packages -> kein Risiko von Cycles. Submit ist
non-blocking; bei Mails ohne Attachments markiert der Worker selbst
'skipped'.
GET /api/mails/{id} liefert jetzt ocr_status + ocr_chars fuer das GUI-
Badge und den konditionalen Download-Button. Die externe v1-API
(/api/v1/mails/{id}) bekommt zusaetzlich ocr_status, damit CRM-
Integrationen entscheiden koennen, ob ein OCR-Text-Pull lohnt.