Zum Hauptinhalt springen
Schneespur
Dokumentation durchblättern

Module entwickeln

Modul-Signierung & Verteilung

Wie Module als signierte Pakete über einen Katalog-Server verteilt, geprüft und installiert werden — Trust-Kette, Sync-Prozess und Hinweise für Modul-Herausgeber.

Module werden nicht offen aus einem Repository gezogen, sondern als signierte Pakete über einen Katalog-Server verteilt. Schneespur prüft jedes Paket gegen eine Vertrauensliste, bevor es installiert wird — so lässt sich nachvollziehen, dass ein Modul tatsächlich von einem berechtigten Schlüssel stammt und unterwegs nicht verändert wurde.

Wie Module verteilt werden

Die Verteilung läuft über einen Katalog-Server. Das Update-System der Anwendung übernimmt das Abrufen, Prüfen, Installieren und Aktualisieren der Module — Sie stoßen den Vorgang an, die Schritte selbst laufen automatisch ab.

Den Katalog-Server konfigurieren Sie in config/schneespur_modules.php:

'server_url'       => 'https://jenni.noschmarrn.dev',
'collection_slug'  => 'schneespur-module',
'catalog_endpoint' => '/api/modules/{slug}',

Der Sync-Prozess

Der Befehl schneespur:modules-sync gleicht Ihre Installation mit dem Katalog ab. Er durchläuft dabei diese Schritte:

  1. Katalog abrufen — HTTP-GET auf den catalog_endpoint, mit ETag-Caching
  2. Signaturen prüfen — der Katalog ist mit libsodium signiert
  3. Versionen vergleichen — installierte gegen im Katalog angebotene Version
  4. Updates herunterladen — ZIP-Dateien für neue oder aktualisierte Module
  5. ZIP-Integrität prüfen — Signaturprüfung der heruntergeladenen Datei
  6. Installieren/aktualisieren — Entpacken ins Verzeichnis modules/
  7. Migrationen ausführenartisan migrate --path=...
  8. Verwaiste Module erkennen — lokal vorhanden, aber aus dem Katalog entfernt

Der Grund für diese Reihenfolge: Signaturen werden vor dem Entpacken geprüft. Ein Paket, das die Prüfung nicht besteht, wird gar nicht erst auf die Platte geschrieben.

Mit der Option --dry-run zeigt der Befehl nur an, was geschehen würde, ohne etwas zu verändern — nützlich, um vor einem echten Sync zu sehen, welche Module aktualisiert werden.

php artisan schneespur:modules-sync --dry-run

Signaturprüfung

Damit ein Paket installiert wird, muss seine Signatur zu einem Schlüssel passen, dem die Installation vertraut. Dieses Vertrauen ist nicht fest verdrahtet, sondern als Kette aufgebaut — vom einen Wurzelschlüssel in Ihrer Konfiguration bis zum einzelnen Modul-ZIP.

Die Trust-Kette

Root public key (in config)
  → fetches trust list from server (/api/signing/trust)
    → trust list contains valid signing keys + revoked keys
      → module ZIPs/catalogs are signed with a valid key

Der Wurzelschlüssel ist der einzige Vertrauensanker, der lokal liegt. Alles Weitere — welche Signaturschlüssel aktuell gültig und welche zurückgezogen sind — kommt über die signierte Vertrauensliste vom Server. So lassen sich Schlüssel sperren, ohne dass Sie Ihre Konfiguration anfassen müssen.

Der ModuleSignatureVerifier

Der Dienst ModuleSignatureVerifier führt die Prüfung in dieser Reihenfolge durch:

  1. Vertrauensliste vom Server abrufen
  2. Signatur der Vertrauensliste gegen den root_pubkey prüfen
  3. auf zurückgezogene Schlüssel prüfen
  4. Modul-Manifest- und ZIP-Signaturen gegen die gültigen Schlüssel prüfen
  5. Vertrauensstufe melden: unsigned, signed, verified oder failed

Die gemeldete Stufe macht den Zustand eines Pakets nachvollziehbar: verified bedeutet eine gültige Signatur eines vertrauenswürdigen Schlüssels, failed eine fehlgeschlagene Prüfung — ein solches Paket wird nicht installiert.

Der Root Public Key

Der Wurzelschlüssel liegt als base64-codierter libsodium-Public-Key in der Konfiguration:

'root_pubkey_b64' => 'bbYkDrjwTapdcONvnhB3tfcwe0aA+lAcgnd0dLMlkmg=',

Es ist derselbe Schlüssel, den auch das Update-System der Anwendung verwendet — Module und Anwendungs-Updates teilen sich denselben Vertrauensanker.

Modul-Installation

Der SchneespurModuleInstaller

Das Entpacken übernimmt der SchneespurModuleInstaller. Er behandelt ein ZIP-Archiv nicht als vertrauenswürdig, sondern wendet mehrere Schutzmaßnahmen an:

  • Path-Traversal-Schutz — verwirft Einträge mit .. oder führendem /
  • macOS-Metadaten filtern — überspringt __MACOSX/-Verzeichnisse
  • Auto-Prefix-Erkennung — entfernt ein gemeinsames Wurzelverzeichnis (typisches Artefakt von ZIP-Exporten)
  • Atomare Updates — sichert die aktuelle Version vor dem Entpacken und rollt bei einem Fehler zurück

Der Path-Traversal-Schutz ist hier kein Detail: Er verhindert, dass ein Archiv-Eintrag außerhalb des Modulverzeichnisses schreibt. Deshalb wird jeder Eintrag vor dem Schreiben auf Pfadsicherheit geprüft.

Ablauf der Installation

install(zipPath, slug)
  1. Extract ZIP to modules/{slug}/
  2. Detect and strip common prefix directory
  3. Validate all entries for path safety
  4. Write files to disk

update(zipPath, slug)
  1. Backup current module to modules/{slug}.bak/
  2. Extract new version
  3. On failure: rollback from .bak

remove(slug)
  1. Delete modules/{slug}/

Der Backup-Schritt beim Update ist der Grund, warum ein fehlgeschlagenes Update Ihre laufende Installation nicht beschädigt: Schlägt das Entpacken fehl, stellt der Installer die gesicherte Version aus .bak wieder her.

Die Modul-Zustandsdatei

Welche Module installiert sind und welcher Trust-Stand gilt, hält Schneespur in storage/app/schneespur_modules_state.json fest:

{
    "catalog_etag": "\"abc123\"",
    "catalog_cache": [...],
    "synced_at": "2026-05-26T10:00:00Z",
    "installed": ["telegram", "documents"],
    "orphans": [],
    "trust_version": 1,
    "valid_keys": ["key1_b64", "key2_b64"],
    "revoked_keys": [],
    "trust_expires_at": "2026-12-31T23:59:59Z"
}

Diese Datei wird atomar geschrieben (temporäre Datei plus Umbenennen), damit ein Abbruch mitten im Schreiben sie nicht beschädigt. Das ETag erspart beim nächsten Sync einen vollständigen erneuten Download, wenn sich am Katalog nichts geändert hat.

Module entfernen

Ein Modul entfernen Sie mit schneespur:modules-remove:

php artisan schneespur:modules-remove telegram
php artisan schneespur:modules-remove telegram --force

Der Befehl arbeitet diese Schritte ab:

  1. Modul deaktivieren (prüft umgekehrte Abhängigkeiten)
  2. Modul-Migrationen zurückrollen
  3. Einstellungen aufräumen (Setting::where('key', 'like', '{slug}.%')->delete())
  4. Modulverzeichnis löschen
  5. öffentlichen Symlink entfernen

Die Prüfung der umgekehrten Abhängigkeiten verhindert, dass Sie ein Modul entfernen, auf das ein anderes noch angewiesen ist. Mit --force überspringen Sie diese Prüfung — das sollten Sie nur tun, wenn Sie die Folgen kennen.

Für Modul-Herausgeber

Wenn Sie ein Modul für den Katalog herausgeben, muss Ihr Paket einem festen Aufbau folgen, damit der Installer und die Signaturprüfung greifen.

Aufbau des ZIP-Archivs

my-module/
  module.json
  src/
    MyModuleServiceProvider.php
  resources/views/
  database/migrations/
  lang/
  dist/

Anforderungen

  1. Gültige module.json mit allen Pflichtfeldern
  2. ServiceProvider erweitert Illuminate\Support\ServiceProvider
  3. Der Namespace stimmt mit der Angabe in module.json überein
  4. Tabellennamen tragen das Präfix mod_{slug}_
  5. Keine Änderungen an Core-Dateien — Erweiterungen ausschließlich über die Registries
  6. Übersetzungen für de und en werden empfohlen

Die Forderung „keine Änderungen an Core-Dateien” ist der Kern des Modulsystems: Module erweitern den Core über die Registries, statt ihn umzuschreiben. Dadurch bleiben Updates der Anwendung konfliktfrei. Wie diese Erweiterungspunkte funktionieren, steht unter ServiceProvider und Registries.

Versionsnummern

Folgen Sie der semantischen Versionierung MAJOR.MINOR.PATCH:

  • MAJOR — brechende Änderungen (umbenannte Einstellungen, entfernte Funktionen)
  • MINOR — neue Funktionen, abwärtskompatibel
  • PATCH — Fehlerbehebungen, keine neuen Funktionen

Saubere Versionsnummern sind kein Selbstzweck: Der Sync-Prozess entscheidet anhand des Versionsvergleichs, ob ein Update angeboten wird. Wer eine brechende Änderung als PATCH ausliefert, überrascht damit jede Installation, die automatisch aktualisiert.

Lokale Entwicklung ohne Katalog-Server

Während der Entwicklung brauchen Sie weder Signatur noch Katalog. Legen Sie Ihr Modul einfach direkt in modules/ ab:

modules/my-module/
  module.json
  src/
  ...

Aktivieren Sie es danach über die Modul-Verwaltung in der Oberfläche oder setzen Sie default_enabled: true in der module.json. Den Einstieg in den Aufbau eines Moduls finden Sie im Schnelleinstieg; ein vollständiges Beispiel liegt der Anwendung als Referenzmodul bei.