Befunde aus dem ersten echten Erstinstallations-Test auf frischen Debian-12-
und Debian-13-Containern. Der Weg selbst hat getragen (Basis-Install,
Instanz-Anlage, zweite Instanz, Update mit Rauchtest, Rollback) — diese
Stellen haben gelogen oder gefehlt:
- Das Ghostscript-Backports-Angebot auf Debian 12 war ein garantierter
Leerlauf, der "aktualisiert ✓" meldete: bookworm-backports enthaelt gar
kein ghostscript (am Paketindex verifiziert). Die Routine sucht jetzt den
echten Kandidaten, vergleicht vorher/nachher und raeumt eine nur zur Probe
angelegte Quelle wieder weg.
- Derselbe untaugliche Rat stand in der Preflight-Meldung, der
pdfa_level-Warnung, config.example.toml und vier Doku-Dateien — ueberall
ersetzt durch die echten Optionen.
- Mehrzeilige Log-Hinweise waren durch "echo -e" zerrissen und nicht
kopierbar; log_* nutzt jetzt printf mit %s.
- pip-freeze.txt landete beim Rollback als /pip-freeze.txt im
Wurzelverzeichnis, liegt jetzt unter opt/pdf-ocr-hotfolder/.
- git und sudo fehlen auf dem Proxmox-Debian-Template; "sudo ./install.sh"
scheitert dort. Beide Wege dokumentiert, git als Voraussetzung ergaenzt,
HTTPS-Clone als Normalfall.
- Rollback: systemctl start kann kein Glob. journald-Reparatur: Instanzen
danach neu starten, sonst bleibt das Journal leer.
254 Tests gruen.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Vor dem Rollout durchgesehen und die verbliebenen Stellen geschlossen, an
denen etwas schiefgehen konnte, ohne dass es irgendwo sichtbar wurde.
Datenverlust:
- veraPDF: das in [verapdf].binary konfigurierte Programm wird im Preflight
geprueft. Bisher galt bei falschem Pfad JEDE Datei als "nicht konform" —
Ergebnis nach error/, Original geloescht (Default delete). run_verapdf()
trennt jetzt ausserdem ein echtes FAIL-Urteil von einer Stoerung
(VeraPdfUnavailable: nicht startbar, abgestuerzt, kein PASS/FAIL in der
Ausgabe). Bei Stoerung wandern Original UND Ergebnis nach error/, das
Original wird nicht entsorgt.
- Gleichnamige Dateien wurden in outgoing/, error/ und beim Ordner-Upload
mit abweichendem target kommentarlos ueberschrieben. Jetzt Zeitstempel
daneben, mit Warnung; ProcessResult.output traegt den echten Pfad.
Robustheit:
- Kaputtes oder nicht lesbares TOML beim Start: Exit 2 statt Traceback.
- RestartPreventExitStatus=2 in der Unit — Exit 2 (Config/Preflight) laeuft
nicht mehr endlos neu, die Instanz bleibt sichtbar failed stehen.
- Toter watchdog-Observer wird erkannt: Exit 3, systemd setzt den Watch neu
auf. Vorher blieb die Unit "active" und verarbeitete nichts mehr.
- Relative Pfade in [paths]/archive_dir/target sind ein Config-Fehler statt
still unter /opt zu landen.
- Fehler beim Archivieren entwertet den Durchlauf nicht mehr: Upload und
Mail laufen, Sichtbarkeit ueber log.error + "OK mit Warnung"-Mail.
- Nicht-PDFs in incoming/ werden beim Start-Scan gesammelt gemeldet.
- Logging explizit nach stdout (die Doku versprach das schon).
Struktur:
- Neue lib/common.sh, von install.sh und update.sh gesourct. Die doppelte
venv_is_healthy() gibt es nur noch einmal, in der gruendlichen Fassung —
die schlanke in install.sh haette eine nach einem Distro-Sprung kaputte
venv als gesund durchgewunken (nachgewiesen).
- install.sh warnt in Containern, wenn systemd-journald nicht laeuft.
Doku: Dateisystem-Festlegung (ext4/xfs/zfs, kein CIFS/NFS wegen inotify),
Debian 13 in LXC auf Proxmox scheitert an journald (243/CREDENTIALS,
AppArmor blockiert sd-mkdcreds) inkl. Abhilfe, echte Speicher-Messwerte,
Exit-Code-Tabelle.
254 Tests gruen (vorher 152).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Aus den Testlaeufen auf Debian 12 (CT 200) und Debian 13 (CT 201):
- Systemanforderungen: mindestens 2 GB RAM. Eine einzelne A4-Seite in
300 dpi mit deskew=true hat auf einem 512-MB-Container den OOM-Killer
ausgeloest (anon-rss:489676kB). Dazu der Zusammenhang RAM <->
max_workers x jobs x Aufloesung, wie sich ein OOM-Kill aeussert und wie
man ihn nachweist (/sys/fs/cgroup/memory.events, dmesg auf dem Host).
- Troubleshooting: kaputtes systemd-journald ist ein blinder Fleck, weil
der Dienst seit v0.4.1 nur dorthin loggt. Symptom, erste Pruefung und
ein Vordergrund-Notbehelf. Auf einem von zwei Testcontainern aufgetreten
— kein generelles LXC-Muster.
Nebenbei korrigiert: toter Anker in docs/INSTALLATION.md (#3-ocr-sprachen
-> #4-ocr-sprachen) und eine veraltete Stelle im Briefing, die
requirements.txt noch als "ocrmypdf 16.x" beschrieb.
Reine Doku-Version, 152 Tests unveraendert gruen.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Das Paket ist nicht pip-installiert, sondern liegt unter /opt/pdf-ocr-hotfolder
und wird nur ueber das Arbeitsverzeichnis gefunden. Der Hinweis, den update.sh
in der Zusammenfassung ausgibt, lief deshalb so wie gedruckt nicht
("No module named pdf_ocr_hotfolder"). Gleiches galt fuer die Beispiele in
README.md, docs/INSTALLATION.md, docs/UPDATE.md und docs/OS-UPGRADE.md.
Gefunden beim Update-Test v0.3.1 -> v0.6.1 auf CT 200 (Debian 12).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
v0.6.0 hat ocrmypdf auf 16.13.0 gepinnt, um einen ungewollten Major-Sprung
zu verhindern. Auf Bestandsinstallationen war das ein DOWNGRADE (dort lief
via ">=16.0" bereits 17.x) — und 16.13.0 bricht auf Debian 12 mit dem
Bord-Ghostscript 10.0.0 bei JEDER PDF ab, sobald skip_text gesetzt ist.
Im Test auf CT 200 lief das Update mit Exit 0 durch, der Dienst blieb
"active", --check-config meldete "Preflight ok" — und jede Datei landete
in error/. Stiller Totalausfall.
- requirements.txt: ocrmypdf==17.4.1 (real auf Debian 12 + gs 10.0.0
verifiziert). Ab 17.0.0 steht die GS-Pruefung in ocrmypdf unter einem
`if options.output_type.startswith('pdfa')`; bis 16.x lief sie ohne
diesen Guard und schlug auch bei output_type="pdf" zu.
- check_preflight() prueft Ghostscript nicht mehr nur bei gesetztem
pdfa_level, sondern bildet die reale Bedingung ab:
betroffene GS-Version UND skip_text UND (PDF/A ODER ocrmypdf < 17).
Der Dienst bricht damit beim Start ab statt bei der ersten Datei.
- update.sh zeigt Versionsspruenge der gepinnten Pakete; Downgrades als
WARN, auch in der Abschluss-Zusammenfassung.
- update.sh faehrt nach dem Start einen Rauchtest (eingebettete Mini-PDF
durch die echte Pipeline) und raeumt restlos auf. Uebersprungen, wenn
Upload-Ziele oder E-Mail-Notify aktiv sind, damit kein Testmuell zum
Kunden geht. Abschaltbar mit --no-smoke-test.
- Doku korrigiert: pdfa_level = "" allein ist keine Entwarnung, die haengt
an der ocrmypdf-Version.
152 Tests gruen.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Datenverlust behoben:
- Nach hartem Stopp blieb das Original in working/ liegen und wurde nie
wieder angefasst (_scan_existing sah nur incoming/). Es wird jetzt beim
Start an Ort und Stelle wieder aufgegriffen, mit Kollisionsschutz gegen
gleichnamige neue Scans; angefangene __ocr_-Fragmente werden geloescht.
- TimeoutStopSec 30 -> 300, damit laufendes OCR zu Ende laufen darf.
Config-Drift sichtbar gemacht:
- Neues --check-config (Exit 0 sauber / 1 Warnungen / 2 Fehler), das
update.sh vor dem Neustart ueber alle Instanz-Configs laufen laesst.
- Warnungen fuer [ocr].timeout >= 900 (seit 0.4.0 pro SEITE) und gesetztes
pdfa_level, beim Dienststart wie im Check.
- Unbekannte Config-Keys werden nicht mehr still verworfen, sondern genannt.
Updater feldtauglich:
- venv-Health-Check erkennt toten Symlink UND Versions-Drift gegen das
System-Python; --rebuild-venv als ausdruecklicher Weg nach einem Debian-
Major-Upgrade. Neubau ist ganz-oder-gar-nicht mit Rollback.
- apt-Pakete werden auch beim Update synchronisiert (Quelle: install.sh).
- Instanz-Erfassung inkl. activating/failed, Verifikation prueft is-failed
und NRestarts statt sleep 1 + is-active.
- Backup enthaelt Configs, Unit, Drop-ins und pip-freeze.txt, liegt auf
0600 und rotiert auf 5; schlaegt es fehl, bricht das Update vorher ab.
- ERR-Trap faehrt die vorher laufenden Instanzen wieder hoch.
- lxc-compat.conf wird beim Update nachgezogen.
- requirements.txt gepinnt (ocrmypdf 16.13.0, geprueft fuer Python 3.11+3.13).
Doku in Installation / Update / OS-Upgrade aufgeteilt (docs/).
135 Tests gruen.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- Sprach-Abfrage pro Instanz (Default-Vorschlag deu+eng), bewusst
instanz-lokal: ein Hotfolder kann mit "deu" laufen, ein anderer mit
"deu+eng+fra". Hinweis im Prompt, dass jede zusaetzliche Sprache
Laufzeit und Erkennungsqualitaet kostet.
- Jeder Sprachcode wird gegen "tesseract --list-langs" geprueft, fehlende
Pakete (tesseract-ocr-<code>) werden zur Installation angeboten; lehnt
der User ab oder scheitert apt, wird gewarnt und erneut gefragt.
- Abfrage "Original archivieren?" mit $BASE/archive als Default; Archiv
ausserhalb von $BASE wird eigens angelegt und gechownt. Ein Pfad auf
incoming/outgoing/working/error wird abgewiesen.
- sed-Kette der Config-Erzeugung jetzt verankert (^key =) und escaped,
setzt zusaetzlich languages, original_on_success und archive_dir;
die erzeugte Config wird gegen die Eingabe nachgeprueft.
- README und Briefing um "Sprachen pro Instanz" ergaenzt
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- Bei fehlgeschlagener veraPDF-Validierung wurde das Original bisher
bedingungslos geloescht. Es folgt jetzt derselben
[output].original_on_success-Regel wie im Erfolgsfall, damit "archive"
das Original nicht ausgerechnet im Fehlerfall verliert.
- Das nie benutzte Logverzeichnis /var/log/pdf-ocr-hotfolder/ wird nicht
mehr angelegt; der Dienst loggt ausschliesslich nach journald.
README und Briefing nennen stattdessen die journalctl-Kommandos.
- 3 neue Tests (95 gesamt)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- [ocr].timeout wird als tesseract_timeout (pro Seite) an ocrmypdf
durchgereicht; Default 1800 -> 300, 0 = ocrmypdf-Default
- Exceptions nach dem OCR zaehlen als Fehler, Datei wird nach error/ gerettet
- Fehlgeschlagene Uploads zaehlen als Fehler und loesen Fehler-Mail aus
- name_mode wird im Preflight geprueft, nicht erst pro Datei
- Fehlende [paths]-Sektion -> ConfigError mit klarer Meldung statt KeyError
- Stabilitaets-Timeout zaehlt als Fehler (--once liefert Exit 1)
- upload_folder nutzt shutil.copyfile statt read_bytes/write_bytes
- OcrConfig.pdfa_level Default "2" -> "" (Ghostscript-Bug, Issue #3)
- 35 neue Tests (92 gesamt), pytest.ini
- AI_AGENT_BRIEFING.md auf Stand 0.4.0 gebracht
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- #4: LXC/Container Drop-in (lxc-compat.conf) deaktiviert systemd-Hardening;
Installer erkennt Container automatisch und bietet Drop-in an
- #5: WorkingDirectory=/opt/pdf-ocr-hotfolder in Template-Unit ergänzt
- #6: Installer bietet auf Debian 12 bei betroffenen GS-Versionen
automatisch bookworm-backports Upgrade an (statt nur Warnung)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- config.example.toml: pdfa_level="" als sicherer Default
- check_preflight(pdfa_level) erkennt betroffene GS-Versionen und bricht ab
- install.sh warnt bei betroffenen GS-Versionen
- 19 neue Tests (parametrisiert über Versions-Matrix)
Closes#3
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- #1: check_preflight() prüft beim Start tesseract + gs, wirft
PreflightError. CLI endet mit Exit 2 statt grün zu bleiben.
- #2: run_once() gibt Anzahl fehlgeschlagener PDFs zurück, CLI
endet mit Exit 1 wenn mindestens eine Datei scheiterte.
- pytest-Suite mit 11 Tests für beide Szenarien
- ocrmypdf-Import lazy in processor.py (Tests ohne ocrmypdf möglich)
Closes#1, #2
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- pdf-ocr-hotfolder@<name>.service mit Config pro Instanz
- install.sh als Instanz-Manager: erkennt bestehende, fragt nach weiteren
- Optional eigener Service-User pro Instanz (systemd drop-in)
- update.sh stoppt/startet alle aktiven Instanzen automatisch
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Komplettes Rewrite des alten Bash-Tools `pdf-tool` in Python.
- ocrmypdf als Library, watchdog für Hotfolder, ThreadPool für Parallelität
- Upload-Targets: folder, Nextcloud (WebDAV), SFTP
- E-Mail-Notify, optional veraPDF
- Interaktiver Installer mit Service-User-Support (lokal + AD via SSSD)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>