Projekt 01 — Digitale PSM

Eine Datenbank,
die im Browser wohnt.

Digitale PSM ist eine kostenlose, quelloffene Web-App für Betriebe im Gartenbau und in der Landwirtschaft: Pflanzenschutzmaßnahmen berechnen, dokumentieren, als Nachweis exportieren. Ohne Konto, ohne Server, ohne Cloud. Alle Daten liegen in einer SQLite-Datei beim Nutzer — und die Datenbank selbst läuft als WebAssembly im Browser.

Von Abbas Hoseiny· Seit 04/2025· Version 2.0· MIT-Lizenz

Wer beruflich Pflanzenschutzmittel ausbringt, muss jede Anwendung festhalten: Mittel, Menge, Fläche, Kultur, Datum, Wartezeit. In der Praxis landet das in Papierordnern oder in Cloud-Diensten mit Konto, Abo und fremdem Server. Digitale PSM geht den dritten Weg: eine Web-App, die sich wie ein Programm auf dem eigenen Rechner verhält, die Daten als Datei beim Nutzer lässt und auch ohne Netz läuft.

Startbildschirm von Digitale PSM am Desktop: Neu erstellen oder bestehende Datei öffnen
Startbildschirm von Digitale PSM am Handy
Abb. 1 — der Startdigitale-psm.de, Desktop und Handy

Kein Login, keine Registrierung. Die App stellt genau eine Frage: neue Datenbank anlegen oder eine bestehende Datei öffnen. Die Datei ist eine gewöhnliche SQLite-Datenbank auf dem Rechner des Nutzers — kopierbar, sicherbar, mit jedem SQLite-Werkzeug lesbar.

Was die App kann

Berechnen, dokumentieren, nachweisen.

Berechnung
Aufwandmenge nach Fläche oder Menge, Umrechnung verschiedener Aufwandangaben, Mittel-Profile für häufige Kombinationen, EPPO-Codes und BBCH-Stadien mit Schnellauswahl.
Dokumentation
Jede Anwendung wird erfasst, Kalenderansicht mit Filtern, Export als PDF oder CSV.
Amtliche Zulassungsdaten
Die BVL-Zulassungsdatenbank liegt nach dem Import in der eigenen Datei: Suche nach Kulturen, Schaderregern und Wirkstoffen, auch offline.
Als Desktop-App, offline
Am Desktop als App installierbar (Chrome, Edge), startet ohne Netz, merkt sich die zuletzt benutzte Datenbank und öffnet .sqlite-Dateien per Doppelklick. GPS-Standorte für Flächen.
Technik

Wie die Datenhaltung funktioniert

Der übliche Bauplan für so eine Anwendung ist ein Server mit Datenbank und ein Konto je Nutzer. Ich wollte das Gegenteil: Die Daten gehören dem Betrieb und liegen als eine einzige Datei auf dessen Rechner. Dafür muss die Datenbank dorthin, wo die Daten sind — in den Browser.

1. Datei statt Server

Die App besteht nur aus statischem HTML, CSS und JavaScript und wird über GitHub Pages ausgeliefert. Es gibt keinen Server, der Fachdaten sieht. Die Datenbank ist SQLite, als WebAssembly-Modul kompiliert (@sqlite.org/sqlite-wasm), lokal mit der App gebündelt und beim Build gehasht und versioniert wie jede andere Datei. Nichts wird zur Laufzeit von einem fremden CDN nachgeladen — darum funktioniert die Datenbank auch offline.

Ein Detail, das man leicht übersieht: Die Datei auf der Platte des Nutzers ist eine echte SQLite-Datenbank, kein eigenes Exportformat. Sie lässt sich mit jedem SQLite-Werkzeug öffnen, sichern, kopieren und auf einen anderen Rechner mitnehmen.

2. Drei Schichten

Die Datenbank läuft nie im Hauptthread des Browsers. Sie lebt in einem eigenen Web Worker, damit die Oberfläche auch während eines großen Imports bedienbar bleibt. Dazwischen gibt es nur Nachrichten.

Oberfläche · Hauptthread
Astro, TypeScript, Bootstrap
  • Formulare, Kalender, Listen, Export (PDF, CSV)
  • Zustand und Ereignis-Bus
  • Spricht mit der Datenbank nur über Nachrichten
  • Hält das Datei-Handle und schreibt die Datei
⇄postMessage
ID, Zeitlimit,
Transfer großer Puffer
Datenbank · Web Worker
SQLite 3.54 als WebAssembly
  • Schema mit Migrationen (user_version 1 bis 11)
  • WAL, Fremdschlüssel, 20 MB Seitencache
  • Alle Abfragen, seitenweise Listen, BVL-Import
  • Export der ganzen Datenbank als Bytes
⇄Bytes
Export · Import
Ablage · beim Nutzer
Eine .sqlite-Datei
  • File System Access API, Handle in IndexedDB für den Auto-Start
  • OPFS-Modus, wenn der Browser ihn erlaubt
  • Fallbacks: JSON-Datei, localStorage, Arbeitsspeicher

3. Speichern, Schritt für Schritt

  1. Eine Eingabe, etwa eine neue Anwendung, geht als Nachricht an den Worker. Jede Nachricht trägt eine ID und ein Zeitlimit; die Antwort kommt asynchron zurück.
  2. Der Worker schreibt in seine SQLite-Datenbank. Die liegt im Arbeitsspeicher des Workers oder, wenn der Browser es zulässt, im Origin Private File System (OPFS).
  3. Danach exportiert der Worker die komplette Datenbank als Bytefolge (sqlite3_js_db_export) und übergibt den Puffer ohne Kopie an den Hauptthread (Transferable).
  4. Der Hauptthread schreibt die Bytes über das Datei-Handle in die .sqlite-Datei des Nutzers (createWritable, write, close). Die Datei auf der Platte ist damit immer eine vollständige, konsistente Datenbank.
  5. Änderungen kurz hintereinander werden gebündelt: Läuft schon ein Schreibvorgang, merkt sich die App nur, dass danach noch einmal geschrieben werden muss. Ein Schreibvorgang pro Änderungsschub statt einer je Tastendruck.

4. Öffnen: Inhalt statt Dateiendung

Beim Öffnen liest die App die Datei als Bytes und prüft die Signatur am Anfang: SQLite format 3. Der Inhalt entscheidet, nicht die Endung. Eine Datei darf .json heißen und trotzdem eine Binärdatenbank sein — frühere Versionen speicherten als JSON, und genau diese Mischung hat einmal dazu geführt, dass eine intakte Datenbank als „leer“ erschien. Seitdem gilt die Signatur.

Ist es SQLite, macht der Worker die Datei per sqlite3_deserialize direkt zur aktiven Datenbank; im OPFS-Modus importiert OpfsDb.importDb sie in die persistente Datei. Ist es JSON, wird der Bestand als Schnappschuss importiert. Beide Wege enden in derselben Datenbank, und die Migrationen laufen in jedem Fall.

5. Die BVL-Daten: eigene Pipeline

Die amtliche Zulassungsdatenbank des BVL (Bundesamt für Verbraucherschutz und Lebensmittelsicherheit) ist über eine öffentliche API erreichbar, aber nicht für den Browser gemacht: viele Endpunkte, viele Anfragen, kein Offline-Betrieb. Deshalb läuft die Aufbereitung vorab in einer eigenen Pipeline, dem öffentlichen Repository pflanzenschutz-db:

  1. Alle zwei Tage holt ein GitHub-Actions-Lauf die Daten aller BVL-Endpunkte, prüft sie und reichert sie an — unter anderem mit Bio-Kennzeichnung, Wirkstoffnamen und Gefahrenhinweisen.
  2. Das Ergebnis ist eine SQLite-Datei, komprimiert mit Brotli und als ZIP, veröffentlicht über GitHub Pages samt Manifest mit Größe und SHA-256-Prüfsumme je Datei.
  3. Die App lädt das Manifest, wählt das kleinste Format, das der Browser entpacken kann, zeigt den Fortschritt an und übergibt die Bytes an den Worker.
  4. Der Worker öffnet den Download als temporäre In-Memory-Datenbank und kopiert die Tabellen in einer Transaktion in die Datenbank des Nutzers — in Abhängigkeitsreihenfolge, damit die Fremdschlüssel stimmen.
  5. Danach steckt der komplette Katalog in der eigenen .sqlite-Datei und ist offline durchsuchbar. Ein Protokoll in der Datenbank hält fest, welcher Stand importiert ist.
Stand 25.10.2026Wert
Mittel im Katalog2 165
Anwendungen (Mittel je Kultur und Schaderreger)30 836
Wirkstoffe482
Download, Brotli / ZIP6,8 MB / 12,5 MB
Entpackt (SQLite)113 MB

6. Offline und als App

Ein Service Worker lädt beim Installieren alle gehashten Build-Dateien vor, das WebAssembly-Modul eingeschlossen. Danach startet die App ohne Netz. Jeder Build stempelt eine neue Cache-Version in den Service Worker; ohne diesen Schritt würden installierte Nutzer auf altem Code hängen bleiben, weil der Browser einen byte-gleichen Service Worker nie neu installiert.

Als installierte Desktop-App registriert sich Digitale PSM über die File Handling API für .sqlite, .db und .json: Ein Doppelklick im Explorer öffnet die Datenbank direkt in der App. Das zuletzt benutzte Datei-Handle liegt in IndexedDB; beim nächsten Start fragt die App nur die Berechtigung ab und öffnet dieselbe Datei wieder.

7. Leistung und Robustheit

8. Grenzen, ehrlich

9. Steckbrief

Appdigitale-psm.de — kostenlos, werbefrei, ohne Konto
SpracheDeutsch (die App richtet sich nach dem deutschen Zulassungsrecht und der BVL-Datenbank)
SeitApril 2025, aktuell Version 2.0
Lizenz & CodeMIT, Quellcode auf GitHub
StackAstro 5, TypeScript, Bootstrap 5 (lokal gebündelt), SQLite 3.54 als WebAssembly, Web Worker, Service Worker, Web App Manifest
DatenhaltungSQLite-Datei beim Nutzer über die File System Access API; OPFS, wenn verfügbar; Fallbacks JSON-Datei und localStorage
ZulassungsdatenBVL-API, aufbereitet in pflanzenschutz-db (GitHub Actions, alle zwei Tage), ausgeliefert als Brotli/ZIP mit Manifest und SHA-256
HostingStatisch über GitHub Pages, Build und Deploy per GitHub Actions
Dazust.digitale-psm.de: Statistik-Dashboard zur BVL-Datenbank, SQL-Abfragen direkt im Browser