Fehler-Schnittstelle
- Neuer schlanker Eingang POST /api/errors/v1/report für den globalen
Exception-Handler einer Anwendung. Titel und Dringlichkeit leitet der Server
ab; gespeichert wird in derselben Tabelle wie der Bugtracker. Ein zweiter
Speicher wäre nur ein zweiter Ort, an dem man suchen müsste.
- error_level (fatal/error/warning) trennt die technische Art des Ereignisses
von der geschäftlichen Dringlichkeit. Ein Duplicate-Entry ist technisch ein
error, geschäftlich belanglos — beides zu vermischen war der Grund, warum
solche Meldungen als Bug im Dashboard landeten.
Ignore-Regeln gegen bekanntes Rauschen
- bugtracker_ignore_rules mit contains/regex/exception_class, Pflichtfeld für
die Begründung und optionaler Alarmschwelle.
- Ein Treffer bedeutet nicht "wegwerfen": Der Fehler wird weiterhin erfasst und
hochgezählt, bleibt aber aus der Übersicht heraus und löst keine
Benachrichtigung aus. Der Zähler ist der eigentliche Zweck — dass ein
bekannter Fehler auftritt, ist normal; dass er plötzlich hundertmal so oft
auftritt, ist ein Signal. Dafür das rollende Stundenfenster und
error.rate_exceeded.
- Neue Regeln lassen sich rückwirkend auf bestehende Einträge anwenden.
Gruppierung überarbeitet
- Der Schlüssel nahm bisher 300 Zeichen Stacktrace auf. Derselbe Fehler
zersplitterte dadurch, sobald ein Aufrufer den Stack einmal mitschickte und
einmal nicht. Jetzt zählt der Ursprungsort: bevorzugt die Dateiangabe, sonst
der erste Rahmen des Stacktrace.
- Die Normalisierung ersetzte nur Zahlen ab vier Stellen, wodurch
'AA-1' und 'BB-2' getrennt blieben. Werte in Anführungszeichen, die Ziffern
enthalten, gelten jetzt als veränderlich — der Schlüsselname bleibt erhalten,
sodass verschiedene Unique-Keys unterscheidbar sind. Mit 9 Testfällen belegt.
Metrik-Verlauf
- watchdog_metrics speichert numerische Heartbeat-Werte mit Zeitstempel.
Zuvor wurde metrics_json bei jedem Heartbeat überschrieben; damit ließ sich
"die Platte läuft seit drei Tagen voll" nicht erkennen, nur "sie ist voll".
- GET /api/watchdog/v1/metrics liefert den verdichteten Verlauf und die
Abweichung vom eigenen Sieben-Tage-Durchschnitt. Dieser relative Ansatz
braucht keine projektspezifischen Schwellwerte.
- Aufbewahrung 14 Tage, Bereinigung stündlich durch den Evaluator.
Health-Checks per Push statt Abruf
- Der Heartbeat nimmt ein checks-Objekt entgegen, das die Anwendung selbst
ermittelt. Das Deploymentcenter interpretiert die Namen nicht, es liest nur
ok und message — was "gesund" bedeutet, entscheidet jede Anwendung selbst.
Schlägt eine Prüfung fehl, wird ein als ok gemeldeter Heartbeat auf warning
herabgestuft.
- Bewusst ausgehend: auf den Zielmaschinen müssen keine Ports geöffnet werden.
Abhängigkeitsbewusste Alarmierung
- Fällt ein Monitor aus, dessen Parent selbst unten ist, wird der Alarm
unterdrückt. Der Zustand bleibt sichtbar. Vorher erzeugte ein ausgefallener
Hypervisor mit zwölf VMs dreizehn Meldungen für ein Problem.
- Mehrere Ebenen und fehlerhafte Hierarchien (Zyklen, gelöschte Parents) sind
abgesichert; mit 10 Testfällen belegt.
WebUI
- Neue Ansicht "Fehler-Stream" mit Filtern nach Projekt, Fehlerklasse,
Umgebung, Zeitraum und Sichtbarkeit sowie Volltextsuche und Pagination.
Stummgeschaltete Einträge sind standardmäßig ausgeblendet.
- Verwaltung der Ignore-Regeln inklusive Trefferzähler.
- Die Detailansicht zeigt Fehlerklasse, Stummschaltungsgrund und die Häufung
im laufenden Stundenfenster.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Regression aus dem vorigen Commit
- /api/license/v1/validate lieferte die Antwort im neuen status/error-Umschlag.
Der Vertrag dieses Endpunkts ist aber bereits ausgerollt: das Feld "status"
auf oberster Ebene trägt den Lizenzzustand (valid, revoked, expired ...).
LicenseClient las dadurch "success" statt "valid" — jeder ausgelieferte
Client hätte seine Lizenz für ungültig gehalten. Die Lizenz-Endpunkte
antworten jetzt wieder ohne Umschlag (Http::raw).
Gefunden durch Ausführen der projekteigenen Test-Suite gegen den Server.
Packager
- FTP-Zugangsdaten standen als Standardwerte im Quelltext und zusätzlich in
packager.config.json und in der Integrationsanleitung. Alle drei Fundstellen
bereinigt; die Konfigurationsdatei ist nicht mehr versioniert. Zugangsdaten
kommen aus Datei, Umgebungsvariablen oder CLI-Argument, sonst bricht das
Programm mit einer klaren Meldung ab.
- Das Veröffentlichen sendet jetzt ein Token (updateservice:publish) und nutzt
den Endpunkt /api/updateservice/v1/publish.
- Fehler wurden von einem leeren catch verschluckt, und ohne Erfolgsfall wurde
gar nichts ausgegeben. Das Werkzeug meldete am Ende immer Erfolg und lieferte
Rückgabewert 0, selbst wenn FTP-Upload und API-Aufruf fehlgeschlagen waren.
Jetzt ehrliche Meldungen und Rückgabewerte 0/1/2.
- packager.config.json wurde vom csproj nie ins Ausgabeverzeichnis kopiert,
weshalb sie dort nie gefunden wurde und stets die hartkodierten Werte griffen.
UpdateClient
- IsVersionNewer entfernte die Vorabkennung, aber kein führendes "v". Damit
scheiterte Version.TryParse bei "v1.4.2" und es wurde auf einen
alphabetischen Vergleich zurückgefallen, in dem "v1.9.0" als neuer gilt als
"v1.10.0" — derselbe Fehler wie zuvor serverseitig im SQL. Ersetzt durch
einen vollständigen semantischen Vergleich, verifiziert mit 16 Testfällen.
- Der Rückfall auf die API lag in einem catch-Block, aber GetAsync wirft bei
einem 404 keine Exception. Fehlte die statische latest.json, brach die
Prüfung ab, statt die API zu befragen.
Dokumentation
- BUGTRACKER_INTEGRATION_GUIDE.md beschrieb denselben Workflow ein zweites Mal
und war bereits auseinandergelaufen: Aufrufe ohne Token, alte Pfade, weder
Claim/Lease noch Idempotenz. Ersetzt durch einen Verweis auf das gepflegte
Agenten-Handbuch samt Übersicht der Änderungen.
- UPDATESERVICE_INTEGRATION_GUIDE.md um Token, Umgebungsvariablen und
Rückgabewerte ergänzt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sicherheit
- install_db.php war ohne Authentifizierung erreichbar und setzte bei jedem
Aufruf das Admin-Passwort auf einen fest im Code stehenden Wert zurück.
Jetzt Auth-Pflicht; ein Konto wird nur bei leerer Benutzertabelle angelegt.
- Stored XSS im Bugtracker-Detail-Modal: Titel, Beschreibung, Fehlermeldung,
Stacktrace und Kommentare gingen ungefiltert durch innerHTML.
- report.php, projects.php und das Veröffentlichen von Releases verlangen jetzt
zwingend ein Token. Publish war zuvor völlig ungeschützt.
- CSRF-Token in allen Formularen, Session-Regenerierung nach Login,
Drosselung fehlgeschlagener Anmeldeversuche.
- Zugangsdaten aus der Versionskontrolle entfernt (Serverdaten.txt,
config.php, .htpasswd, deploy_config.json). Historie enthält sie weiterhin,
Rotation erforderlich (siehe docs/UPGRADE.md).
- Token-Validierung nur noch über SHA-256-Hash; expires_at wird ausgewertet.
Behobene 500er
- Audit::log() war in index.php weder eingebunden noch importiert. Jeder
Klick auf "Aktivierung freigeben" endete in einem Fatal Error.
- Derselbe benannte PDO-Platzhalter mehrfach je Statement (:id in
revokeToken/deleteToken, :q siebenfach in der Volltextsuche). Bei
EMULATE_PREPARES=false ist das nicht zulässig und warf HY093.
- Migration 005 nutzte dynamisches SQL, dessen Semikolons in String-Literalen
vom alten explode(';')-Installer als Statement-Ende gelesen wurden. Sie
schlug still fehl, wodurch push_id/target_agent/tags dauerhaft fehlten.
- Monitor-Umbenennung ohne Transaktion, verschachtelte Transaktionen im
RateLimiter.
Funktionale Korrekturen
- Der Watchdog-Evaluator fehlte vollständig: Monitor-Zustände änderten sich nur
beim Eintreffen eines Heartbeats, ein ausgefallenes System blieb dauerhaft
"up". Erster Lauf auf dem Produktivsystem: 7 von 10 Monitoren waren
tatsächlich seit über einem Tag nicht erreichbar.
- Das Feld "os" fehlte im Monitor-Dialog, wurde aber gespeichert und löschte
damit bei jedem Speichern das Betriebssystem.
- Der Resolve-Dialog existierte im HTML nicht; der Button war funktionslos.
- Versionsvergleich erfolgte lexikografisch, wodurch 1.9.0 als neuer galt
als 1.10.0.
- Schreiboperationen meldeten Erfolg auch für nicht existierende IDs.
- Post/Redirect/Get gegen doppelte Einträge beim Neuladen.
Neue Struktur
- src/bootstrap.php mit PSR-4-Autoloader ersetzt die require-Ketten.
- Core: Config, Http, Csrf, ApiAuth, Logger, Migrator, ErrorReporter.
- Migrator mit zeichenweisem SQL-Parser, dc_migrations und Baseline-Verfahren,
damit bestehende Installationen keine Beispieldaten zurückbekommen.
Agenten-Workflow
- Claim/Lease: Items werden exklusiv übernommen, damit nicht zwei Agenten am
selben Problem arbeiten. action=next holt und reserviert in einem Zug.
- Idempotenz über client_ref, Deduplizierung auch für Feature Requests,
Erkennung von Regressionen, automatische Eskalation des Schweregrads.
- Strukturierter Code-Kontext (repo_url, commit_sha, file_path, line_no).
- Delta-Abfragen über updated_since, Pagination, Bulk-Update.
- Beim Veröffentlichen eines Releases schließen sich Items mit passendem
resolved_in_build selbst.
- Ausgehende Webhooks mit HMAC-Signatur, /api/health, /api/openapi.json.
- Unbehandelte Fehler meldet die Plattform in ihren eigenen Bugtracker.
WebUI
- Serverseitige Filterung mit Pagination statt Rendern aller Datensätze.
- Migrations-Schranke, Evaluator-Warnung, Übersicht aktiver Agenten.
Zeitstempel liegen in der Datenbank durchgängig in UTC und werden für die
Anzeige in die App-Zeitzone umgerechnet.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>