Commit Graph
100 Commits
Author SHA1 Message Date
sysops ebab716006 docs(PROJ-66): Backup-Strategie-Spec anlegen; PROJ-65 Deployed-Status nachziehen
PROJ-66 (Planned): kein App- oder Cron-Backup für /var/archivmail/store,
Keyfile, PostgreSQL auf 131 und 132 vorhanden. Nutzer-Rückmeldung ergänzt:
Infra-Ebene sichert bereits per Proxmox Backup Server + Sync auf Zweitserver,
das entschärft den Off-Site-Blocker deutlich - verbleibende Kernlücke ist
ein nie getesteter Restore, nicht das fehlende Backup-Ziel. Bitwarden-
Keyfile-Escrow als zusätzlicher, von PBS unabhängiger Wiederherstellungsweg
ergänzt.

PROJ-65: Deploy-Agent hatte Status/QA-Ergebnisse bereits lokal geschrieben,
aber noch nicht committed.
2026-07-04 13:52:05 +02:00
sysops a15fa37619 feat(PROJ-65): Physische Tenant-Trennung im Storage-Layer (Hardlink-Ordner)
Jeder Tenant bekommt ein eigenes Verzeichnis store/tenant_<id>/, das per
Hardlink auf die kanonische content-adressierte Datei zeigt — das bestehende
Cross-Tenant-Dedup-Modell (email_refs M:N, PROJ-32/37) bleibt dadurch
erhalten, kein Speicherplatz-Mehrverbrauch. Neues CLI-Subcommand
`archivmail migrate-tenant-dirs` zieht Bestandsdaten einmalig nach
(idempotent). Zusätzlich neuer Status-Check checkStoragePermissions
(warnt bei zu offenen store_path-Rechten, analog checkEncryption/PROJ-49).

DB-gestützte Zugriffskontrolle bleibt der maßgebliche Zugriffspfad im Code;
die Tenant-Ordner sind eine zusätzliche Defense-in-Depth-Ebene für manuelle
Dateisystem-Audits. Kein lokaler go build möglich, QA folgt auf Testserver.
2026-07-04 13:03:52 +02:00
sysopsandClaude Sonnet 5 cc30440e99 docs(PROJ-46): Status auf Deployed setzen nach Produktiv-Deploy (2026-07-04)
Backend/Frontend erfolgreich auf 192.168.1.131 deployt (update.sh, code war
bereits committed). Deployment-Abschnitt mit Smoke-Test-Ergebnis ergänzt,
Breaking-Change-Hinweis (Tenant-User Login nur noch per E-Mail) ins DEVLOG.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-04 12:41:08 +02:00
sysopsandClaude Sonnet 5 08f486a28c docs(PROJ-46): QA-Testergebnisse (9/9 PASS auf 192.168.1.132) dokumentieren
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-04 12:38:55 +02:00
sysops 81744562c3 docs: README auf aktuellen Stand bringen (PROJ-43/52 Deployed, Config-Referenz vervollständigt)
"In Entwicklung" und Funktionsübersicht listeten PROJ-43/PROJ-52 noch als
Planned, obwohl seit 2026-07-04 deployt. Config-Referenz fehlten mehrere
produktiv genutzte Sektionen (server.fqdn, api.trusted_proxies/secure_cookies,
smtp_out, smtp.tenant_routing, storage.retention_days/min_retention_days/
compress, ocr.paused_hours, imap_scheduler, reconciliation, imap_server,
metrics) — jetzt vollständig dokumentiert.
2026-07-04 12:25:26 +02:00
sysops bd727616b6 chore: Screenshots aus dem Repo entfernen
Screenshots dürfen nicht auf Gitea landen, waren in af16138 versehentlich
mit committet. Aus Tracking entfernt (git rm --cached, Dateien bleiben
lokal erhalten) und in .gitignore aufgenommen.
2026-07-04 12:21:17 +02:00
sysops 45ac68564b docs(PROJ-65): Spec für physische Tenant-Trennung im Storage-Layer
Bewertet drei Optionen (Hardlink-Farm pro Tenant, Metadaten-Trennung
dokumentieren, Dedup nur noch pro Tenant) gegen das bestehende
Cross-Tenant-Dedup-Modell (email_refs M:N). Empfehlung: Option B
zuerst, Option A nur bei konkreter Kundenanforderung.
2026-07-04 12:17:17 +02:00
sysops 17633cb86c docs: Migrations-Runbook für Compliance-Erhalt-Check ergänzen (GoBD Punkt 10)
Baseline/Post-Vergleich, PROJ-18-Integritätscheck, Manticore-Konsistenz,
PROJ-52-Reconciliation und Byte-Vergleich als Ablauf nach jeder Schema-/
Index-/Tenant-Migration. Checklist-Punkt 10 auf Erfüllt gesetzt.
2026-07-04 12:14:30 +02:00
sysops 6d67a6bbe7 docs: GoBD-Checklist auf aktuellen Stand bringen, Spec für PROJ-56c nachtragen
Checklist war seit 2026-06-13 nicht aktualisiert: PROJ-48/49/50/51/52 waren
laengst deployt, standen aber noch als fehlend/teilweise drin. PROJ-56c
(Cron-Purge mit Markierungspflicht) war im Code implementiert, hatte aber
nie eine eigene Feature-Spec — nachtraeglich dokumentiert.
2026-07-04 12:12:08 +02:00
sysops a7390151a1 docs: GoBD-Checklist auf aktuellen Stand bringen (PROJ-52, PROJ-56c)
Punkt 1 (Vollständigkeit) war noch als "Teilweise" markiert, obwohl PROJ-52
(Reconciliation-Report) das inzwischen abdeckt. Punkt 7 (Löschsperre)
beschrieb noch den alten Purge() ohne Markierungspflicht; ergänzt um
PROJ-56c Cron-Purge, der nur retain_until < NOW() UND marked_for_deletion
löscht.
2026-07-04 12:07:21 +02:00
sysopsandClaude Sonnet 5 30b7586d94 docs(PROJ-43,PROJ-52): Status auf Deployed setzen nach Produktiv-Deploy
Beide Features wurden auf 192.168.1.131 deployt (Commit 4c92587), inkl. der
zugehörigen QA-Bugfixes (Dry-Run-Adressmatching PROJ-43, days-Clamp PROJ-52).
Deployment-Abschnitte in den Feature-Specs und INDEX.md aktualisiert.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-04 12:00:07 +02:00
sysopsandClaude Sonnet 5 4c92587b60 fix(PROJ-43): Dry-Run für from_addr/to_addr matcht bare Adressen statt <addr>-Form
dryRunCondition() erwartete faelschlich die Winkelklammer-Form "Name <addr>",
waehrend mail_from/mail_to die Adresse bare speichern - Dry-Run zeigte dadurch
immer 0 Treffer fuer Adress-Regeln, obwohl der Live-Matcher (routeBareAddr)
korrekt matcht. QA-Ergebnisse (Bug-1) in die Feature-Spec uebernommen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-04 11:57:21 +02:00
sysopsandClaude Sonnet 5 04e5b0f74a fix(security-audit): HTTPS/Port-Checks erkennen nftables Set-Syntax nicht
strings.Contains(nftStr, "dport 443") fand den Substring nicht, wenn
nftables mehrere Ports als Set ausgibt ("tcp dport { 80, 443 } accept" statt
"tcp dport 443 accept") — das ist bei archivmail-Installationen der
Normalfall (80+443 stehen zusammen in einer Regel). Dashboard zeigte
dadurch fälschlich "Kein HTTPS — Verbindungen unverschlüsselt", obwohl
Port 443 korrekt offen war (bestätigt auf 131 und 132).

Neue Helper-Funktion nftHasPort() per Regex erkennt beide Formen
(Einzelport und Set). Betrifft die Checks für HTTPS/443, Port 3000 und
Port 8080.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-04 00:02:20 +02:00
sysopsandClaude Sonnet 5 c274df5023 fix(deploy): update.sh synchronisiert systemd-Units bei jedem Deploy
Bisher schrieb nur install.sh die Unit-Dateien (einmalig beim Erst-Setup).
Server, die vor einer Unit-Änderung installiert wurden, blieben dauerhaft
auf altem Stand — konkret fehlte auf 192.168.1.132 CAP_NET_ADMIN (wurde in
install.sh ergänzt, aber nie auf bereits laufende Installationen
nachgezogen), wodurch die Security-Tab-Aktion "Firewall aktivieren"
(nft -f /etc/nftables.conf) mit "Operation not permitted" fehlschlug.

update.sh schreibt jetzt bei jedem Deploy beide Unit-Dateien neu (identisch
zum install.sh-Template) und lädt sie per daemon-reload nach, bevor die
Dienste gestartet werden.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-03 23:55:47 +02:00
sysopsandClaude Sonnet 5 af16138687 docs: README aktualisieren, Dev-Log/Dateiindex/GoBD-Checklist/Screenshots ins Repo
README: Feature-Tabelle und Inhaltsverzeichnis auf aktuellen Stand gebracht
(POP3, OCR, eDiscovery, DSGVO, Retention, LDAP, TOTP, IMAP-Server,
Prometheus, Tenant-Export etc. ergänzt).

Zusätzlich aufgenommen: CODEBASE.md (Dateiindex, Stand 2026-03-31 — vor
PROJ-44 eingefroren, sollte bei Gelegenheit aktualisiert werden), DEVLOG.md
(Session-Log), docs/GOBD_DSGVO_CHECKLIST.md (Compliance-Checkliste),
resume (Session-Notiz), screenshots/.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-03 23:45:59 +02:00
sysopsandClaude Sonnet 5 767373b206 feat(PROJ-46): E-Mail als primärer Login-Identifier für Tenant-User
Tenant-User (tenant_id IS NOT NULL) melden sich künftig per E-Mail an statt
per Username — behebt Verwechslungen wie im Support-Fall vom 2026-06-13
(Login schlug trotz Passwort-Reset fehl, weil E-Mail statt Username
verwendet wurde). Nicht-Tenant-User (Superadmin/System) können weiterhin
Username ODER E-Mail nutzen.

Neue Store.VerifyLogin() prüft erst per E-Mail (alle User), fällt dann auf
Username zurück (nur tenant_id IS NULL). VerifyPassword() bleibt für den
IMAP-Server-Login-Pfad (PROJ-26) unverändert. Bewusster Breaking Change für
Tenant-User, Datenqualität vorab geprüft (0 Kollisionen).

Security-Nachtrag: bcrypt-Dummy-Compare im "user not found"-Pfad ergänzt,
um Timing-basierte Identifier-Enumeration zu verhindern.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-03 23:37:20 +02:00
sysopsandClaude Sonnet 5 804cd62201 fix(PROJ-50): DSGVO-Tab lädt nicht — API-Contract-Mismatch Frontend/Backend
Backend liefert bei GET /api/admin/dsgvo ein Objekt {"requests":[...]},
Frontend erwartete rohes Array → .map() auf Nicht-Array warf Exception,
angezeigt als "DSGVO-Anträge konnten nicht geladen werden".

Zusätzlich stimmten DSGVOResultSummary/DSGVOMailResult-Feldnamen nicht mit
dem Go-JSON überein (total vs. total_hits; id/status/received_at existieren
im Backend nicht, stattdessen mail_id/deletable/deleted/date). Status wird
jetzt im Frontend aus deletable/deleted abgeleitet (mailStatus()).

Bug bestand seit Feature-Implementierung (2026-06-13), fiel erst jetzt beim
ersten Live-Test auf.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-03 23:06:14 +02:00
sysopsandClaude Sonnet 5 e91206c2b5 chore(PROJ-52): Cron-Job für Reconciliation in /etc/cron.d/archivmail ergänzen
Läuft nachts um 04:10 Uhr, nach dem Purge-Job und außerhalb der
OCR-Pause. Ohne diesen Eintrag existierte der neue Subcommand
"archivmail reconcile" nur manuell aufrufbar, ohne produktiven Betrieb.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-03 22:54:29 +02:00
sysopsandClaude Sonnet 5 99e4c1bbe4 fix(security): Fail-closed Tenant-Filter in v1-Suche + SMTP-Out-Key-Kette gehärtet
Security-Audit-Nachtrag (siehe PROJ-64):

- internal/api/v1_handlers.go: handleV1SearchMails fehlte der fail-closed
  Tenant-Post-Filter-Fallback für den Fall idxMgr==nil (gleiches Muster wie
  bereits in search_handlers.go). Aktuell nicht ausnutzbar, da idxMgr in
  main.go immer gesetzt wird, aber strukturelle Absicherung gegen künftige
  Regressionen (analog PROJ-55 BUG-1).
- internal/smtpoutconfig/store.go: Verschlüsselungsschlüssel wird jetzt aus
  dem HKDF-abgeleiteten aesKey gebildet statt aus dem rohen cfg.API.Secret,
  konsistent mit internal/ldapconfig und internal/imap/store.go (SEC-08).
  Verifiziert: smtp_out_config auf Produktiv (131) war leer, kein
  Breaking Change für bestehend gespeicherte Zugangsdaten.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-03 22:48:55 +02:00
sysopsandClaude Sonnet 5 be93614c9f feat(PROJ-52): Vollständigkeits-Reconciliation (Zähl-Report Mailserver vs. Archiv)
Täglicher Cron-Job (archivmail reconcile) berechnet pro Tenant/Quelle
(SMTP-Journal, IMAP-Konto, POP3-Konto, Datei-Import) archivierte Mail-Zahlen,
für IMAP zusätzlich einen Soll/Ist-Vergleich via UID-Tracking. Abweichungen
über Schwellenwert erzeugen Audit-Log-Warnung. Neue Admin-Dashboard-Kachel
"Vollständigkeits-Check" (letzte 7 Tage, Warn-Badge, CSV-Export).

Schließt die "teilweise erfüllt"-Lücke bei Vollständigkeit im
GoBD/DSGVO-Compliance-Check (VOI-Grundsatz 2).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-03 22:48:42 +02:00
sysopsandClaude Sonnet 5 b286352d07 fix(PROJ-64): Session-Invalidation bei Passwort-Change + Datei-Permissions gehärtet
Security-Audit deckte zwei Medium-Findings auf: JWTs blieben bis zu 8h nach
Passwort-Change/-Reset oder Admin-TOTP-Reset gültig (kein Session-Invalidation),
und archivierte Mails/Anhänge wurden mit 0644/0755 statt 0600/0700 geschrieben.

- users.tokens_valid_after (neue Spalte) wird bei SetPassword() und
  InvalidateTokensBefore() gesetzt; ValidateToken() lehnt JWTs mit iat davor ab.
- Admin-TOTP-Reset revoked jetzt aktive Sessions des Zielnutzers.
- Mail-/Attachment-Dateien und ihre Verzeichnisse nur noch für den
  archivmail-Service-Account lesbar.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-03 22:23:27 +02:00
sysopsandClaude Sonnet 4.6 0ccbd5bafb fix(mailparser): kaputte/truncated Multipart-Mails partiell indexieren statt scheitern
Bei NextPart()-Fehler (EOF, malformed header) wird der bereits geparste Inhalt
zurückgegeben statt der gesamte Parse abgebrochen. parseMultipart() gibt keinen
Fehler mehr zurück — partieller Text/HTML ist besser als gar kein Index-Eintrag.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-30 20:17:33 +02:00
sysops 5e7ae6e056 fix: Manticore-Architektur in install.sh dynamisch (arm64-Unterstützung) 2026-06-30 20:02:35 +02:00
sysopsandClaude Sonnet 4.6 909a3b0782 chore: update.sh OS-versions-agnostisch machen (Debian 14+ safe)
Go: Versionsprüfung + Auto-Install von upstream falls < 1.24 oder fehlt —
unabhängig von apt und Debian-Version, überlebt OS-Upgrades auf Debian 14+.
Manticore: bookworm-Paket hartcodiert, arm64 in update.sh ergänzt.
Fehlermeldung "apt-get install golang-go" entfernt (falsch nach Umstellung).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-30 19:59:25 +02:00
sysopsandClaude Sonnet 4.6 681d81c469 chore: Installer auf Debian 13 (trixie) als Mindestanforderung festlegen
- OS-Check: bricht ab wenn nicht Debian 13 erkannt
- Go: apt-golang-go entfernt, Go 1.24.4 von dl.google.com (arm64/amd64)
- Manticore: bookworm-Paket hardcodiert (kein natives trixie-Paket), in
  install.sh und update.sh konsistent
- Native config: batch_mode: true für index + ocr (wie PROJ-58 vorgesehen)
- systemd: NEXT_PUBLIC_API_URL entfernt (Next.js baked bei Build, nicht Runtime)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-30 19:57:04 +02:00
sysopsandClaude Sonnet 4.6 261a95437c feat(PROJ-50): Admin-Tab "DSGVO-Anfragen" implementiert
Neuer Tab im Admin-Bereich: Antrags-Liste, Formular (Adresse + Zeitraum),
Detail-Dialog mit Mail-Tabelle, PDF-Export und Löschen mit Bestätigung.
Rollen-Gating: admin/auditor sehen den Tab, nur admin kann anlegen/löschen.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-30 19:34:25 +02:00
sysopsandClaude Sonnet 4.6 0552ce49e2 fix(PROJ-50): Cross-Tenant-Lücke + Fehlerbehandlung in DSGVO-Handlern behoben
Bug-1: GetDSGVOMailMeta mit tenant_id-Filter (Defense-in-Depth an DB-Schicht).
Bug-2: Verwaiste open-Einträge bei Auswertungsfehler werden auf failed markiert.
Bug-3: Fehlgeschlagene Suchen werden im Audit-Log protokolliert.
Bug-6: Ungültige date_from/date_to-Eingaben liefern HTTP 400.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-30 18:17:25 +02:00
sysopsandClaude Sonnet 4.6 dcb88317ac fix(PROJ-63): Defense-in-Depth Tenant-Scope-Härtung der Admin-Endpunkte
tenantAccessAllowed()-Check in allen {id}-Handlern von tenant_handlers.go,
tenant_domain_handlers.go und tenant_logo_handlers.go ergänzt — No-op für
globale Admins, zweite Verteidigungslinie für hypothetische tenant-gebundene
Admin-Sessions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-30 14:37:39 +02:00
sysopsandClaude Sonnet 4.6 c1338f6721 chore(PROJ-60): Xapian-Reste aus Dockerfile, Makefile und Doku entfernen
Dockerfile und Makefile bauten noch mit CGO_ENABLED=1 -tags xapian und
installierten libxapian-dev/libxapian30, obwohl der Xapian-Code bereits
entfernt wurde. PRD.md und api-v1.md erwähnten Xapian noch im Tech-Stack
bzw. als Such-Syntax-Referenz.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 09:48:52 +02:00
sysopsandClaude Sonnet 4.6 7c028601cf fix(PROJ-62): Cross-Tenant IDOR bei POP3-Konto-Löschung/-Import behoben (Sicherheitsbug)
handleDeletePop3 und handleStartPop3Import prüften nur Owner/Rollen-Level,
nicht den Tenant-Scope (anders als das korrekte IMAP-Pendant). Ein
domain_admin konnte dadurch POP3-Konten eines fremden Tenants löschen
oder deren Import anstoßen. Fix: tenantAccessAllowed(sess, acc.TenantID)
ergänzt, analog zum IMAP-Handler. Gefunden bei gezielter Nachsuche nach
Geschwister-Bugs zu PROJ-61.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 01:52:01 +02:00
sysopsandClaude Sonnet 4.6 363874767b fix(PROJ-61): Cross-Tenant Stored XSS via Mandanten-Logo behoben (Sicherheitsbug)
GET /api/tenants/{id}/logo prüfte nur die Authentifizierung, aber keinen
Tenant-Scope — jeder eingeloggte Nutzer konnte das Logo jedes beliebigen
Tenants lesen. Kombiniert mit dem bisher erlaubten SVG-Upload (kann
eingebettetes JavaScript enthalten) ergab das einen Cross-Tenant Stored-XSS:
ein domain_admin konnte ein bösartiges SVG als eigenes Logo hochladen und
Opfer aus beliebigen anderen Tenants per direktem Link darauf locken.

Fix: tenantAccessAllowed()-Scope-Check beim Logo-Lesepfad (analog PROJ-55),
SVG aus erlaubten Upload-Typen entfernt, X-Content-Type-Options: nosniff
als Defense-in-Depth ergänzt.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 00:31:41 +02:00
sysopsandClaude Sonnet 4.6 5f63bfe8d4 chore(PROJ-60): Xapian-Legacy-Backend vollständig aus dem Code entfernt
Xapian war seit der Manticore-Migration (PROJ-30) nur noch ein
build-tag-gatetes, in Produktion nie genutztes Legacy-Backend.
internal/index/xapian.go, xapian_stub.go, xapian_wrapper.cpp/.h entfernt;
index.New() unterstützt jetzt nur noch "manticore". Default-Fallbacks in
Nebenwerkzeugen (archivmail-import/-export, cmd_export/cmd_reindex/...)
von "xapian" auf "manticore" korrigiert, Xapian-spezifische Tests entfernt,
Kommentare bereinigt.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 00:12:06 +02:00
sysopsandClaude Sonnet 4.6 043520ecc3 chore(PROJ-59): tote Xapian-Reste aus config.docker.yml.example entfernen
xapian_path/backend: xapian waren toter Datenmüll (StorageConfig hat kein
xapian_path-Feld, wird beim YAML-Unmarshal stillschweigend ignoriert).
Auf den tatsächlich genutzten Manticore-Default umgestellt
(backend: manticore, manticore_dsn). Behebt das in PROJ-56 QA als
INFO 3 dokumentierte, außerhalb des Scopes liegende Finding.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 00:03:49 +02:00
sysopsandClaude Sonnet 4.6 46502abe75 fix(PROJ-58): Lockfile gegen überlappende Cron-Batch-Läufe + update.sh synct Cron-Dateien
Root Cause für ausbleibende Lastsenkung: update.sh hat /etc/cron.d/archivmail
nie auf den Server kopiert, daher fehlten die PROJ-58-Cronzeilen trotz
aktivem batch_mode komplett. Jetzt kopiert update.sh die Cron-Datei und alle
Wrapper-Skripte bei jedem Deploy automatisch ein.

Zusätzlich: Wrapper-Skripte (analog mailpiler indexer.delta.sh) verhindern
per Lockfile, dass sich Cron-Läufe bei großem Backlog überlappen.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 23:54:37 +02:00
sysopsandClaude Sonnet 4.6 c91eeac0a0 docs(PROJ-58): batch_mode für Neuinstallationen standardmäßig aktivieren
config.docker.yml.example setzt index.batch_mode/ocr.batch_mode jetzt
auf true statt es nur kommentiert zu zeigen, damit neue Installationen
direkt mit Cron-Batch-Verarbeitung statt Dauerbetrieb starten. Bestehende
Installationen (131/132) sind unberührt, da ihre config.yml unabhängig
vom Repo-Beispiel ist.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 23:16:34 +02:00
sysopsandClaude Sonnet 4.6 fae274f930 feat(PROJ-58): Indexierung + OCR optional als Cron-Batch statt Dauerbetrieb
Neues config.yml-Feld batch_mode (index/ocr, Default false = unverändertes
Verhalten). Bei batch_mode:true verarbeiten neue Cron-Jobs (index-pending,
ocr-reprocess) die Backlogs in größeren Abständen statt sofort bei jedem
Mail-Import, um Schreiblast auf der Festplatte zu glätten. Zeiten in
/etc/cron.d/archivmail frei anpassbar.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 23:07:17 +02:00
sysopsandClaude Sonnet 4.6 76655f78a2 fix(PROJ-57): UTF-8-Encoding für Mails mit Nicht-UTF-8-Charset korrigieren
Der Mail-Parser ignorierte das charset-Parameter aus Content-Type und
interpretierte Bytes immer als UTF-8, wodurch iso-8859-1/windows-1252
kodierte Mails (z.B. mit Umlauten) als Mojibake gespeichert wurden.
Zusätzlich fehlte das Charset für die Manticore-MySQL-Verbindung und
der charset-Parameter im JSON-Response-Header.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 22:47:00 +02:00
sysopsandClaude Sonnet 4.6 69c120a268 feat(PROJ-56d): archivmail import um --tenant Flag erweitert
archivmail import --dir/--file ordnete importierte Mails bisher immer
ohne Mandant zu (Save(..., nil) fest verdrahtet). Neuer --tenant <id> Flag
übergibt die Tenant-ID an Store.Save() und an idxMgr.ForTenant(), sodass
Mail sowohl in der DB als auch im richtigen Tenant-Suchindex landet —
und automatisch die Retention-Regeln des Tenants greifen (applyRetention
nutzt bereits die übergebene tenantID). Relevant für den geplanten
Watch-Folder-Import pro Mandant.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 17:03:15 +02:00
sysopsandClaude Sonnet 4.6 b3ff8e6cf9 feat(PROJ-56c): Purge-Cron löscht nur explizit markierte Mails
Der automatische Purge-Cron darf nicht allein anhand des abgelaufenen
retain_until löschen — eine Mail muss zusätzlich von einem Admin im UI
zur Löschung markiert worden sein. Dafür: neue Spalten
marked_for_deletion(_by/_at) auf emails, Store.ListExpiredMarkedMailIDs()
(retain_until abgelaufen UND markiert) für den Cron-Pfad, und
Store.SetMarkedForDeletion() zum Setzen/Löschen der Markierung.

Neue Endpoints (domain_admin+, tenant-scoped):
- GET  /api/admin/retention/expired      Metadaten abgelaufener Mails
                                          (kein Body-Zugriff, SEC-29)
- PUT  /api/admin/mails/{id}/mark-deletion  Markierung setzen/entfernen,
                                          mit Audit-Log-Eintrag

RetentionTab.tsx zeigt abgelaufene Mails mit Checkbox zum Markieren.
Der bestehende manuelle "Jetzt löschen"-Button (/api/admin/purge) bleibt
unverändert und löscht weiterhin alle abgelaufenen Mails auf einen Klick —
nur der unbeaufsichtigte Cron-Job ist jetzt auf markierte Mails beschränkt.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 16:40:18 +02:00
sysopsandClaude Sonnet 4.6 586af2478c feat(PROJ-56c): GoBD-Retention-Purge als Cron-Job + Delete()-Konsistenzfix
archivmail purge ist ein neuer CLI-Befehl, der Mails mit abgelaufener
retain_until löscht, aus dem Suchindex entfernt und pro Mail einen
Audit-Eintrag (mail_purged) schreibt — analog zu Pilers purge.sh, nachts
03:40 Uhr über deploy/cron.d/archivmail. Nur Mails mit explizit gesetztem
und abgelaufenem retain_until werden angefasst; ohne retain_until bleibt
alles unberührt, die Löschsperre (PROJ-34) greift weiterhin.

Beim Testen aufgedeckt: Store.Delete() entfernte die Datei vor dem
DB-Delete und verschluckte den Fehler, wenn email_refs/email_attachments
per Fremdschlüssel die Löschung blockierten — Ergebnis war ein DB-Eintrag
ohne zugehörige Datei. Jetzt läuft die DB-Löschung (inkl. abhängiger
Zeilen) zuerst in einer Transaktion, die Datei wird erst nach
erfolgreichem Commit entfernt.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 16:00:41 +02:00
sysopsandClaude Sonnet 4.6 d826fe1da7 chore(PROJ-56): Cron-Dateien nach mailpiler-Vorbild konsolidieren
Statt einer Datei pro Job (wie bisher archivmail-ocr-pause) bündelt
/etc/cron.d/archivmail jetzt alle archivmail-Wartungsjobs in einer Datei,
analog zu /etc/cron.d/piler. Neue Jobs (Purge, nächtlicher Reindex,
Watch-Folder-Import) werden hier künftig als weitere Zeile ergänzt statt
in eigenen Dateien.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 15:41:50 +02:00
sysopsandClaude Sonnet 4.6 dba9939880 feat(PROJ-56): OCR-Pausenfenster per SIGHUP-Reload statt Restart
paused_hours konnte bisher nur über einen vollen Prozess-Restart geändert
werden, was SMTP/IMAP/API unnötig unterbricht. Worker.pausedHours ist jetzt
ein atomic.Pointer mit SetPausedHours(); SIGHUP liest config.yml neu und
aktualisiert nur die OCR-Pausenzeit im laufenden Prozess. Neue
deploy/cron.d/archivmail-ocr-pause(.sh) lässt Admins die Pausenzeiten direkt
in der Cron-Datei pflegen und löst per systemctl reload statt restart aus.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 14:46:13 +02:00
sysops be48a99af9 docs(PROJ-56): Status auf Deployed setzen
Erfolgreich auf 192.168.1.132 (Test) und 192.168.1.131 (Produktion)
deployed, keine Fehler.
2026-06-22 14:35:10 +02:00
sysops 12c4dc7ea7 docs(PROJ-56): Test-Deployment auf 192.168.1.132 dokumentieren 2026-06-22 14:30:33 +02:00
sysops 4dbf27cc1d feat(PROJ-56): Last-Entzerrung für OCR und IMAP-Sync
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.
2026-06-22 14:21:09 +02:00
sysops a55faf74b1 chore: Frontend-Seiten in Komponenten/Hooks aufteilen
admin/page.tsx (1019→446), search/page.tsx (871→304), imap/page.tsx
(738→142), settings/page.tsx (641→56). Reine Umstrukturierung,
keine Verhaltensänderung, Build verifiziert.
2026-06-22 11:29:43 +02:00
sysops ce197a3ab7 docs(PROJ-55): Status auf Deployed setzen
Erfolgreich auf 192.168.1.132 (Test) und 192.168.1.131 (Produktion)
deployed, Live-Verifikation bestanden.
2026-06-22 00:13:23 +02:00
sysops ef83174553 docs(PROJ-55): Live-Verifikation auf 132 dokumentieren
Initial verdächtigter Cross-Tenant-Treffer als beabsichtigtes
email_refs-Dedup-Verhalten verifiziert (kein Leak). PROJ-55 ist
production-ready, Live-Test PASS.
2026-06-22 00:10:51 +02:00
sysops 1d27dc2d8b refactor: große API-Handler-Dateien in fokussierte Module aufteilen
Reine Code-Verschiebung, keine Logikänderung. Betroffen:
- ldap_tenants.go (994 Zeilen) -> ldap_tenants.go (Routing) + ldap_handlers.go
  + tenant_handlers.go + tenant_domain_handlers.go + tenant_logo_handlers.go
  + tenant_helpers.go
- import_handlers.go (621 Zeilen) -> imap_handlers.go + pop3_handlers.go
  + import_helpers.go
- admin_handlers.go (702 Zeilen) -> admin_users_handlers.go +
  admin_status_handlers.go + admin_services_handlers.go +
  admin_security_handlers.go

Lokal kein Go-Build möglich — Verifikation manuell per Funktions- und
Import-Abgleich Alt/Neu (alle Symbole exakt einmal vorhanden). Build
muss vor Deploy auf 192.168.1.131/132 bestätigt werden.
2026-06-21 23:38:57 +02:00
sysops 6d3ca7fd50 chore: ungenutzten Frontend-Code entfernen
PageSkeleton, das shadcn Toast-System (toaster/sonner/toast/use-toast)
und das sonner-Paket waren im Code nirgends importiert. Build erfolgreich
verifiziert nach Entfernung.
2026-06-21 23:38:51 +02:00
sysops d80a5d8050 chore: echten HTTP-Health-Check statt nur systemd-Status in update.sh
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.
2026-06-21 22:59:31 +02:00
sysops f55a40c77d chore: Manticore-Reindex aus update.sh entfernen
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.
2026-06-21 22:46:55 +02:00
sysops 92e57431c3 fix(PROJ-55): Tenant-Isolation für Rolle "auditor" + Audit-Log korrigieren
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.
2026-06-21 22:26:06 +02:00
sysops 4a8b8964e5 docs(PROJ-53, PROJ-54): Status auf Deployed setzen
Beide Features auf 192.168.1.132 (Test) und 192.168.1.131 (Produktion)
deployed und vom Nutzer bestätigt.
2026-06-14 23:26:57 +02:00
sysops 62693fa36a chore: npm audit fix — Dependency-Updates für picomatch/Next.js-Advisories
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.
2026-06-14 23:12:35 +02:00
sysops 776dee8a56 fix(PROJ-54): Listenansicht/Pagination für Rolle "user" korrigieren
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.
2026-06-14 22:58:56 +02:00
sysops 4c20a0025a fix(PROJ-53): TypeScript-Build-Fehler durch fehlendes list_page_size in LoginResponse
setCachedUser() in den Login-Seiten erforderte alle MeResponse-Felder;
list_page_size wird jetzt aus der Login-Response übernommen.
2026-06-14 22:31:59 +02:00
sysops 472ba6a087 feat(PROJ-53): Konfigurierbare Listenanzahl pro Seite
- users.list_page_size (Default 25), PATCH /api/auth/preferences,
  Whitelist 25/50/100/200, Wert in login/me-Response
- Settings-UI mit Select, /search nutzt gespeicherte Seitengröße
- /api/search page_size serverseitig auf max. 500 gecappt

fix(PROJ-46): login_attempts-Migration nutzte s.db statt s.pool
(Backend kompilierte nicht)

feat(PROJ-50): DSGVO-Löschersuchen Backend (dsgvo_requests, Handler,
cc_addr/bcc_addr Indexerweiterung) — noch nicht QA'd/deployed
2026-06-14 22:25:02 +02:00
sysopsandClaude Sonnet 4.6 b73ef55a65 fix: archivmail.service benötigt CAP_NET_ADMIN für Firewall-Aktivierung
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>
2026-06-13 22:01:29 +02:00
sysopsandClaude Sonnet 4.6 1c52181e75 docs(PROJ-50): Tech Design für DSGVO-Löschersuchen für Mail-Inhalte
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>
2026-06-13 21:41:29 +02:00
sysopsandClaude Sonnet 4.6 a87d2c80fa docs(PROJ-51): Status auf Deployed setzen
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>
2026-06-13 21:12:28 +02:00
sysopsandClaude Sonnet 4.6 7ac0391205 fix(PROJ-51): retain_until_source nicht für Endbenutzer, WARN-Status in CLI
- 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>
2026-06-13 21:05:07 +02:00
sysops 507dee6431 feat(PROJ-51): Aufbewahrungsfristen nach Dokumentenart (Retention-Kategorien)
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.
2026-06-13 20:48:16 +02:00
sysopsandClaude Sonnet 4.6 7c08ebe1b7 docs(PROJ-51): Tech Design für Retention-Kategorien (inkl. minimaler PROJ-43-Basis)
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>
2026-06-13 20:19:15 +02:00
sysopsandClaude Sonnet 4.6 e1ed4e521e chore(PROJ-49): Status auf Deployed setzen
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>
2026-06-13 20:11:40 +02:00
sysopsandClaude Sonnet 4.6 46096db802 feat(PROJ-49): Verschlüsselungspflicht at-rest sichtbar machen (Healthcheck & Warnung)
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>
2026-06-13 20:01:38 +02:00
sysopsandClaude Sonnet 4.6 dc9e11fc9f chore(PROJ-48): Status auf Deployed setzen
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>
2026-06-13 19:55:00 +02:00
sysopsandClaude Sonnet 4.6 7e4175923f feat(PROJ-48): Audit-Log Unveränderbarkeit (Trigger, append-only Logfile, Healthcheck)
DB-Trigger audit_log_immutable verhindert UPDATE/DELETE auf audit_log,
zusätzliches append-only JSON-Lines-Logfile (audit.log_path) als
tamper-evident Backup, neuer Healthcheck-Prüfpunkt in archivmail status.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 19:44:07 +02:00
sysopsandClaude Sonnet 4.6 cca27c663a feat: mobile Responsiveness für Suche und Mail-Detailansicht
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 13:18:19 +02:00
sysops bc82854165 feat: archivmail status CLI mit Healthcheck für PostgreSQL, Manticore und Storage
Prüft DB-Erreichbarkeit + Mail-Statistiken, Manticore-Verbindung/Version
und Storage (Keyfile, Pfad, freier Speicherplatz). --json für maschinenlesbare Ausgabe.
2026-06-13 00:01:40 +02:00
sysops 0ecde0c1ef feat(cli): archivmail update — Wrapper für update.sh
Statt update.sh manuell zu suchen/laden kann jetzt
`archivmail update` (als root) ausgeführt werden. Nutzt
/opt/archivmail/update.sh falls vorhanden, sonst Download von Gitea.
2026-06-12 23:45:15 +02:00
sysops 501ee8f7ea fix(sec): Cross-Tenant-IDOR bei POP3-Konten schließen
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()
2026-06-12 23:31:56 +02:00
sysops 730099d2aa fix(sec): Cross-Tenant-IDOR bei IMAP-Konten schließen
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()
2026-06-12 23:26:31 +02:00
sysopsandClaude Sonnet 4.6 d07e65021f fix(tenants): NULL-Scan-Fehler in tenantstore.List() beheben
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>
2026-06-12 22:38:28 +02:00
sysopsandClaude Sonnet 4.6 f82e3eb907 feat: Admin-/Auditor-Login-Link auf Startseite
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>
2026-06-12 22:08:21 +02:00
sysopsandClaude Sonnet 4.6 a3bab16f07 fix(imap): IMAP/POP3-Import in korrekten Tenant-Index schreiben
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>
2026-06-12 21:15:10 +02:00
sysopsandClaude Sonnet 4.6 f32f83ff8e fix(PROJ-28): Invite-Token Pflicht bei Signup — TOCTOU + Enumeration-Leak schließen
- 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>
2026-05-29 18:20:07 +02:00
sysopsandClaude Sonnet 4.6 15a5da33fd feat(PROJ-13): OpenAPI 3.0 Spec + GET /api/v1/docs Endpoint
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>
2026-05-29 18:03:58 +02:00
sysopsandClaude Sonnet 4.6 fa9f77782c fix(sec): Authorization-Bypässe und Path-Traversal schließen, Xapian-Doku bereinigen
- 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>
2026-05-28 23:55:31 +02:00
sysopsandClaude Sonnet 4.6 8d950b48f7 fix(mailparser): Deutsche Wochentagsabkürzungen im Date-Header parsen
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>
2026-05-11 23:46:13 +02:00
sysopsandClaude Sonnet 4.6 a1c4e59fff fix: Date-Parsing-Fallback für nicht-standard MTA-Datumsformate
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>
2026-05-11 23:36:18 +02:00
sysopsandClaude Sonnet 4.6 1f7e02dc53 fix(build): turbopack.root explizit setzen, verhindert Warning bei multiplen lockfiles
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>
2026-05-11 20:46:46 +02:00
sysopsandClaude Sonnet 4.6 33539d281a fix(ui): Uhrzeit in Datumsspalte der Suchergebnisse anzeigen
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>
2026-05-11 20:38:54 +02:00
sysopsandClaude Sonnet 4.6 726dd78f3a fix(PROJ-4): SMTP tenant routing: header fallback for BCC journaling
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>
2026-05-11 14:56:32 +02:00
sysopsandClaude Sonnet 4.6 799c828548 feat(PROJ-45): IMAP per-folder UID-tracking, UIDVALIDITY-check + reindex OCR protection
- 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>
2026-05-11 14:56:28 +02:00
sysopsandClaude Sonnet 4.6 4151b6f8c5 feat(PROJ-45): IMAP Per-Folder UID-Tracking + UIDVALIDITY-Check
- FolderState: GetFolderState, UpsertFolderState, ListFolderStates, DecideResync
- syncFolder nutzt per-folder UID-Tracking statt globalem highest_uid
- UIDVALIDITY-Check loest automatisch Full-Resync aus
- imap_folder_state Tabelle in initSchema (CREATE TABLE IF NOT EXISTS)
- SetAuditLogger in main.go verdrahtet

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 10:49:14 +02:00
sysops 16013e8b66 fix(PROJ-44): OCR-Tenant-Routing nutzt kanonische DB-tenant_id
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).
2026-05-10 23:13:57 +02:00
sysopsandClaude Sonnet 4.6 5e1a51b028 fix(PROJ-44): SNIPPET via SELECT statt CALL SNIPPETS (Go MySQL-Treiber-Kompatibilitaet)
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>
2026-05-10 22:53:27 +02:00
sysopsandClaude Sonnet 4.6 bb71ef2fd1 fix(PROJ-44): snippet + match_field in enrichedHit-Struct und JSON-Response verdrahten
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>
2026-05-10 22:48:20 +02:00
sysopsandClaude Sonnet 4.6 a4fe2c6b64 fix(PROJ-44): CALL SNIPPETS entfernt Options-Args (Manticore 25 akzeptiert exakt 3)
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>
2026-05-10 22:44:05 +02:00
sysops 032892bc2b fix(PROJ-44): IMAP+POP3 Live-Import triggert OCR-Worker
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'.
2026-05-10 22:23:24 +02:00
sysops a44fd1ae44 feat(PROJ-44): ocr_status/ocr_chars in MailDetail + v1 API
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.
2026-05-10 22:21:05 +02:00
sysops 62a130d208 feat(PROJ-44): GET /api/mails/{id}/ocr-text + Audit-Event
Neuer Endpoint liefert den OCR-extrahierten Reintext als text/plain
mit Content-Disposition. ACL identisch zu /raw (Tenant-Isolation,
Auditor-Regeln, User-Ownership-Check). Status-Mapping:
  done + chars>0 -> 200, attachment "<id16>.ocr.txt"
  pending        -> 202 JSON {"error":"ocr_pending"}
  skipped/failed/disabled/empty -> 404 JSON {"error":"ocr_not_available"}
Jeder erfolgreiche Download landet im Audit-Log als mail:ocr_download.
2026-05-10 22:20:59 +02:00
sysops 7b75433999 feat(PROJ-44): Snippet + match_field fuer Suche, GetAttachmentText
Hit-Struct um Snippet + MatchField erweitert. enrichHitsWithSnippets
fuellt diese pro Treffer: detectMatchField probt subject>body>
attachment_text>attachment_names>from_addr>to_addr; buildSnippet ruft
CALL SNIPPETS mit <b>-Markern. Snippet-Fehler droppen den Treffer nicht.

AttachmentTextReader-Interface + Manticore-Implementation
GetAttachmentText liefert den indexierten OCR-Text fuer den neuen
/ocr-text-Endpoint.
2026-05-10 22:20:52 +02:00
sysops 5078830469 feat(PROJ-44): ocr_chars-Spalte + SetOCRResult-Helper
DB-Schema bekommt eine idempotente ocr_chars BIGINT-Spalte (Default 0).
SetOCRResult schreibt status und chars atomar; GetOCRMeta liest beide
mit COALESCE-Defaults. Der OCR-Worker ersetzt jeden SetOCRStatus-Call
durch SetOCRResult und uebergibt die extrahierte Zeichenzahl bei 'done'.
2026-05-10 22:20:46 +02:00
sysopsandClaude Opus 4.7 7be73c1041 feat(PROJ-44): Snippet + Quellen-Badge in Such-Trefferliste
- Zweite Zeile unter dem Betreff zeigt Manticore-Snippet
  mit <b>-Highlights, gerendert via dangerouslySetInnerHTML
  ueber sanitizeSnippet
- Quellen-Badge je match_field (Subject, Body, PDF-Anhang,
  Dateiname, Absender, Empfaenger) als kleines Tailwind-Pill
- Nur sichtbar wenn das Backend ein snippet zurueckliefert

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 22:18:55 +02:00
sysopsandClaude Opus 4.7 f4403c8e6c feat(PROJ-44): OCR-Badge + OCR-Text-Download auf Mail-Detail-Seite
- OcrBadge neben dem Verifikations-Status im Mail-Header
- "OCR-Text"-Button (lucide FileText) in der Action-Leiste,
  sichtbar nur bei ocr_status=done und ocr_chars>0
- Tooltip via title-Attribut zeigt erkannte Zeichenzahl
- Pending-/Not-Available-Antworten werden als Alert angezeigt
  ("OCR laeuft noch, bitte gleich nochmal versuchen")

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 22:18:49 +02:00
sysopsandClaude Opus 4.7 83039dcf8d feat(PROJ-44): OcrBadge-Komponente fuer Mail-Detail-Header
- Rendert OCR-Status als shadcn Badge mit passender Farbe
  (done=gruen, failed=rot, skipped=grau, pending=blau)
- disabled und undefined rendern null, damit die Komponente
  unbedingt eingebunden werden kann

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 22:18:43 +02:00
sysopsandClaude Opus 4.7 23a9a7ea37 feat(PROJ-44): API-Types + Sanitize-Helper fuer OCR-GUI
- MailDetail um ocr_status/ocr_chars erweitert
- SearchHit um snippet + match_field erweitert
- Neue API-Funktionen getOCRTextDownloadURL und downloadMailOCRText
  inkl. 202/404-Handling fuer pending/not-available
- src/lib/sanitize.ts: sanitizeSnippet escaped HTML und laesst nur
  <b>-Tags fuer Manticore-Highlights durch
- Re-exports in src/lib/api/index.ts ergaenzt

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 22:18:38 +02:00
sysops 88e9d0c08c docs(PROJ-35): Status auf Deployed + Bekannte Pitfalls dokumentiert
- 132 + 131 deployed
- hashMailID-Bit-Mask-Fix dokumentiert (negative IDs Problem)
- OCR-Tempo-Constraint (CPU-bound) erklärt
- Boot-Resume-Loop als bekannter, unkritischer Bug vermerkt
2026-05-08 23:10:59 +02:00