Files
archivmail/features/PROJ-15-cli-import-export.md
T
sysops bb963a796f security: Zufallspasswörter beim Erststart, kryptographisch sichere JTI-Generierung
- seedDefaultUsers: generiert kryptographisch zufällige Passwörter (crypto/rand)
  statt hartkodiertes "archivmailrockz" — Passwörter werden einmalig im Terminal
  angezeigt und können danach nicht wiederhergestellt werden
- generateJTI: verwendet crypto/rand (16 Byte, hex) statt time.UnixNano XOR deadbeef

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-17 01:19:24 +01:00

7.6 KiB
Raw Blame History

PROJ-15: CLI Import & Export

Status: Deployed

Created: 2026-03-13 Last Updated: 2026-03-17

Dependencies

  • Requires: PROJ-5 (Speicherung & Indexierung) Import nutzt Storage Coordinator
  • Requires: PROJ-1 (Authentifizierung) CLI läuft als Systembenutzer archivmail, kein Web-Login

Hinweis

Die CLI läuft direkt auf dem Server als Systembenutzer archivmail kein Web-Login, kein API-Key. Zugriff über den gleichen Storage Coordinator wie der Daemon. Gedacht für automatisierte Skripte, Cron-Jobs und administrative Bulk-Operationen.

User Stories

  • Als Systemadministrator möchte ich EML/MBOX-Dateien per CLI importieren, damit ich Bulk-Importe skriptbasiert automatisieren kann.
  • Als Systemadministrator möchte ich E-Mails per CLI exportieren (EML/MBOX), damit ich Sicherungen oder Migrationen durchführen kann.
  • Als Systemadministrator möchte ich Import/Export mit Pfadangabe starten, damit ich Quell- und Zielverzeichnisse flexibel festlegen kann.
  • Als System möchte ich Import-Fortschritt und Ergebnis auf stdout ausgeben, damit Skripte den Status auswerten können.

Acceptance Criteria

Import

  • archivmail import --file /pfad/zu/datei.eml einzelne EML importieren
  • archivmail import --file /pfad/zu/archiv.mbox MBOX importieren
  • archivmail import --dir /pfad/zum/verzeichnis/ alle EML-Dateien in einem Verzeichnis importieren (rekursiv optional: --recursive)
  • Fortschrittsausgabe auf stdout (eine Zeile pro 100 Mails)
  • Exit-Code 0 bei Erfolg, 1 bei Fehler
  • Duplikate werden übersprungen (SHA256-Dedup im Store), kein Fehler
  • --dry-run Flag: zeigt was importiert würde ohne tatsächlich zu speichern

Export

  • archivmail export --out /pfad/ziel/ alle Mails als EML-Dateien exportieren
  • archivmail export --out /pfad/archiv.mbox alle Mails als MBOX exportieren
  • archivmail export --from alice@firma.de --out /pfad/ Filter nach Absender
  • archivmail export --date-from 2024-01-01 --date-to 2024-12-31 --out /pfad/ Filter nach Datum
  • archivmail export --query "Rechnung" --out /pfad/ Filter per Volltext-Suche (Xapian)
  • Exportierte Mails als Klartext EML auf Disk
  • --format eml (Standard) oder --format mbox

Allgemein

  • CLI läuft als Systembenutzer archivmail Config aus /etc/archivmail/config.yml
  • Fehler werden auf stderr ausgegeben
  • archivmail help zeigt Übersicht aller Befehle
  • archivmail version zeigt Version

Edge Cases

  • Verzeichnis beim Import enthält keine EML-Dateien → Hinweis + Exit-Code 0
  • Zieldatei beim Export bereits vorhanden → Fehler mit --force Flag zum Überschreiben
  • Kein Lese-/Schreibrecht auf Pfad → klare Fehlermeldung auf stderr
  • Import unterbrochen (Ctrl+C) → partiell importierte Mails werden gespeichert, kein Rollback (Archiv ist append-only)
  • Export bei leerem Archiv → leeres Verzeichnis / leere MBOX, Exit-Code 0

Technical Requirements

  • CLI ist Teil desselben Go-Binaries (archivmail) Subcommands via archivmail <command>
  • Zugriff auf Storage Coordinator direkt (kein HTTP-Umweg über den laufenden Daemon)
  • Key-Datei muss lesbar sein (/etc/archivmail/keyfile, chmod 400, Owner archivmail)
  • Kann parallel zum laufenden Daemon betrieben werden (Xapian WritableDatabase: Lock beachten)
  • Strukturierte Ausgabe optional: --json Flag für maschinenlesbare Ausgabe

Tech Design (Solution Architect)

CLI-Struktur

archivmail <command> [flags]

Commands:
  import    E-Mails importieren (EML, MBOX, Verzeichnis)
  export    E-Mails exportieren (EML, MBOX)
  version   Version anzeigen
  help      Hilfe anzeigen

archivmail import
  --file       /pfad/datei.eml oder .mbox
  --dir        /pfad/verzeichnis/
  --recursive  Unterverzeichnisse einschließen (mit --dir)
  --dry-run    Simulation ohne Speichern
  --json       Maschinenlesbare Ausgabe (JSON)

archivmail export
  --out        /pfad/ziel/ oder /pfad/archiv.mbox (Pflicht)
  --format     eml (Standard) | mbox
  --from       Absender-Filter
  --to         Empfänger-Filter
  --date-from  Datum von (ISO 8601: 2024-01-01)
  --date-to    Datum bis (ISO 8601: 2024-12-31)
  --query      Volltext-Suche (Xapian QueryParser)
  --force      Zieldatei überschreiben
  --json       Maschinenlesbare Ausgabe (JSON)

Komponentenstruktur

archivmail (Go-Binary)
│
├── main.go                    ← Subcommand-Router (import / export / ...)
│
├── cmd/import.go
│   ├── Flag-Parsing
│   ├── Dateityp-Erkennung (.eml / .mbox / Verzeichnis)
│   ├── EML-Parser
│   ├── MBOX-Parser (zeilenweise)
│   └── → Storage Coordinator (PROJ-5, direkt, kein HTTP)
│
└── cmd/export.go
    ├── Flag-Parsing
    ├── Filter-Builder (from, to, date, query)
    ├── → Xapian ReadonlyDatabase (Suche/Filter)
    ├── → PostgreSQL Metadaten-Lookup
    ├── → .m-Datei lesen + AES-256-GCM entschlüsseln
    └── Schreiben als EML-Dateien oder MBOX

Import-Fluss

$ archivmail import --dir /backup/mails/ --recursive

Key laden aus /etc/archivmail/keyfile
Verzeichnis scannen → 3.842 .eml-Dateien gefunden
[████████░░] 2.150 / 3.842  (übersprungen: 12 Duplikate)

Fertig:
  Importiert:   2.130
  Übersprungen: 12 (Duplikate)
  Fehler:       0

Export-Fluss

$ archivmail export --from alice@firma.de \
    --date-from 2024-01-01 \
    --out /backup/export/

Key laden aus /etc/archivmail/keyfile
Xapian: 847 Mails gefunden (Filter: from=alice, date>=2024-01-01)
Exportiere nach /backup/export/
[████████████] 847 / 847

Fertig:
  Exportiert: 847 EML-Dateien
  Ziel:       /backup/export/

JSON-Ausgabe (--json Flag)

{
  "status": "done",
  "imported": 2130,
  "skipped": 12,
  "errors": 0,
  "duration_sec": 42
}

Xapian-Lock beim parallelen Betrieb

Daemon läuft (WritableDatabase hält Lock für Index-Worker)
        │
CLI export → ReadonlyDatabase → kein Lock-Konflikt ✓
CLI import → Storage Coordinator → WritableDatabase
        │
        └── Lock bereits gehalten?
              → Warten (max. 30 Sek.) → dann Fehlermeldung:
                "Index locked by running daemon. Stop daemon or retry."

Technische Entscheidungen

Entscheidung Begründung
Gleiche Binary, Subcommands Kein separates CLI-Tool archivmail import und archivmail serve teilen Code und Storage Coordinator
Direkter Speicherzugriff, kein HTTP CLI läuft als archivmail-User mit Dateisystem-Zugriff kein laufender Daemon nötig für Import/Export
--dry-run Sicher testen ohne Daten zu verändern wichtig für große Bulk-Imports
--json Flag Maschinenlesbar für Cron-Jobs, Monitoring-Skripte, Ansible-Playbooks
Exit-Codes 0 = Erfolg, 1 = Fehler Standard für Shell-Skripting
Xapian ReadonlyDatabase für Export Export kann parallel zum Daemon laufen ohne Lock-Konflikte

Abhängigkeiten

Paket Zweck
github.com/spf13/cobra Subcommand-CLI-Framework
Xapian CGo-Bindings Volltext-Filter beim Export (bereits PROJ-5)

Implementation Notes

  • Subcommands in cmd/archivmail/main.go via os.Args[1] Router (kein cobra nötig)
  • cmd_import.go: EML + MBOX Import, --file, --dir, --recursive, --dry-run, --json
  • cmd_export.go: EML + MBOX Export, alle Filter, --force, --json
  • MBOX Parser in pkg/mailparser/mbox.go (SplitMbox)
  • MBOX Export mit korrektem >From Escaping
  • Deployed auf root@192.168.1.131, Daemon läuft

QA Test Results

To be added by /qa

Deployment

To be added by /deploy