⚠️ Watchdog-Evaluator läuft nicht. Die Monitor-Zustände sind derzeit nicht verlässlich – ein ausgefallenes System bleibt „up“, bis es sich wieder meldet. Letzter Lauf: (). Der Evaluator lief noch nie.
Empfohlen (intern):
* * * * * /usr/bin/php /cli/tick.php --quiet
Alternativ über HTTP:
* * * * * curl -fsS -H "Authorization: Bearer <SHARED_KEY>" /api/watchdog/v1/evaluate > /dev/null
Aktive Projekte
Aktive Lizenzen
Hardware Aktivierungen
Watchdog Monitore
/ /

🛡️ Watchdog System-Hierarchie & Monitore

0); $iconUrl = getMonitorIconUrl($m['icon']??null, $m['source']??null, $m['os']??null, $m['type']??null); ?>
Hierarchie / Entity Source Typ Status Version Letzte Meldung Zuletzt Gesehen
icon ()

📁 Neues Projekt anlegen / Bearbeiten

Projekte Übersicht & Verwaltung

$l['product_slug'] === $p['slug'])); $relCount = count(array_filter($releases, fn($r) => $r['product_slug'] === $p['slug'])); ?>
ID Slug Projekt Name Cache TTL Verknüpfte Lizenzen Verknüpfte Releases Aktionen
h ( Tage) Lizenzen Releases
$l['status'] === 'active')); $blockedActivations = count(array_filter($activations, static fn($a): bool => !empty($a['is_blocked']))); ?>
Lizenzen gesamt
davon aktiv
Aktivierte Systeme
Gesperrte Hardware

🔑 Neuen Lizenzschlüssel generieren

Erstellte Lizenzschlüssel

0 && $used >= $max; $licJson = e((string)json_encode($l, JSON_INVALID_UTF8_SUBSTITUTE)); ?>
Projekt Lizenzschlüssel Kunde Aktivierungen Status Ablaufdatum Aktionen
Noch keine Lizenzen angelegt.
/
📥 .lic

🖥️ Aktivierte Hardware

Projekt Lizenzschlüssel Hardware-ID / Quelle Plattform Hostname Zuletzt gesehen Status Aktionen
Noch keine Aktivierungen.
Quelle:
· v

💾 Offline-Lizenzdatei (.lic)

Für Systeme ohne Internetzugang. Die Datei wird serverseitig erzeugt und mit HMAC-SHA256 über den Schlüssel aus security.license_key signiert. Am schnellsten geht es über den 📥 .lic-Knopf direkt in der Lizenzliste.

Lizenz auswählen und auf „Vorschau erzeugen“ klicken.

Diese Vorschau dient nur der Ansicht. Die gültige, signierte Datei liefert der 📥 .lic-Knopf in der Lizenzliste.

📜 Audit-Protokoll

ID Zeitpunkt Akteur Aktion Details
Noch keine Einträge.

💻 Endpunkte & Anbindung

Die Antwort von /validate trägt den Lizenzzustand im Feld status auf oberster Ebene (valid, revoked, expired, not_found, activation_limit) — bewusst ohne den Umschlag der übrigen API, weil bereits ausgelieferte Clients dieses Format erwarten.
POST /api/license/v1/validate Content-Type: application/json { "product": "myapp", "license_key": "XXXXX-XXXXX-XXXXX-XXXXX-XXXXX", "hardware_id": "2:win:a765bd47...", "hostname": "DESKTOP-01", "app_version": "v1.4.2", "nonce": "zufallswert" } Antwort: { "type": "validation_result", "status": "valid", "expires_at": 1786435199, "cache_ttl_hours": 168, "message": "License is valid" }

Fertige Anbindung für .NET: Deploymentcenter.Client (LicenseClient, HardwareId, verschlüsselter Offline-Cache). Ausführlich in docs/LICENSE_INTEGRATION_GUIDE.md.

Gesund / Up
Warnungen
Down / Kritisch

📁 Gruppen-Übersicht

$gData): ?>
/ OK

🌳 Watchdog System-Hierarchie

0); $iconUrl = getMonitorIconUrl($m['icon']??null, $m['source']??null, $m['os']??null, $m['type']??null); ?>
Name Typ Status Version Zuletzt gesehen Kurz-Info / Message Aktionen
icon

🖥️ Monitor Anlegen

📜 Chronologisches Event-Log

Zeitpunkt (UTC) Source Kind Severity Nachricht

Neuen Agent-Token erstellen

Agent Tokens

Token ID Bezeichnung Gebundene Source Token Value Status Aktion

📦 Veröffentlichte Software Releases

'badge-up', 'beta' => 'badge-warning', 'dev' => 'badge-stopped', default => 'badge-up' }; $sizeFormatted = !empty($r['size_bytes']) ? round($r['size_bytes'] / (1024 * 1024), 2) . ' MB' : '-'; ?>
Projekt Kanal Plattform Version Git Commit Größe Release Notes Download & SHA256 Datum Aktionen
v KRITISCH UNSIGNIERT ⬇️ Download Package
SHA: ...

🔒 Zugangsschutz der Releases

Die Release-Verzeichnisse liegen hinter HTTP-Basic-Auth. Zugang hat, wer einen gültigen Lizenzschlüssel für das jeweilige Produkt besitzt — Benutzername und Passwort sind beide der Schlüssel. Die Anwendung kennt ihn ohnehin und versorgt sich damit selbst. Zusätzlich stehen die Installationskonten in jeder Datei: bei einer Erstinstallation gibt es noch keinen Schlüssel.

Je Produkt eine eigene Datei. Eine gemeinsame Liste würde bedeuten, dass eine Lizenz für Produkt A auch Produkt B öffnet. Die Zuordnung ergibt sich aus license_licenses.product_id.
Produkt Status Zugänge Zuletzt erzeugt
Es liegen noch keine Release-Verzeichnisse vor.
OFFEN GESPERRT GESCHÜTZT NICHT SCHREIBBAR
Selbsttest vom UTC: von Adresse(n) antworten ohne Zugangsdaten korrekt mit 401.
  • :

Der Selbsttest ist die einzige belastbare Aussage. Dass die Dateien vorhanden sind, beweist nichts: Unter Nginx werden .htaccess-Dateien vollständig ignoriert — die Verzeichnisse wären dann offen und die .htpasswd öffentlich abrufbar, während hier „GESCHÜTZT“ stünde. Der Test ruft die eigene Paket-Adresse ohne Zugangsdaten ab und erwartet 401.

OFFEN heißt: jeder im Internet kann die Pakete laden. GESPERRT heißt: niemand kommt heran, weil es weder gültige Lizenzen noch Installationskonten gibt — bei internen Anwendungen ohne Lizenzen ist das der Normalfall, solange kein Installationskonto existiert.

Geschieht automatisch bei jeder Lizenz- und Kontoänderung. Abgelaufene Lizenzen lösen von sich aus nichts aus — dafür gleicht cli/tick.php nach, spätestens alle sechs Stunden.

⬇️ Installer herunterladen

Ein Werkzeug für alles: Dasselbe Programm richtet eine Anwendung erstmalig ein und aktualisiert sie später. Es bringt die .NET-Laufzeit mit — auf dem Zielsystem muss nichts vorinstalliert sein.

Noch nichts hinterlegt. Die Binaries werden mit scripts/build_installer.ps1 gebaut und mit scripts/upload_installer.py nach /installer/ hochgeladen.
Version · Build ·
Plattform Größe SHA256 Download
MB ⬇️ Herunterladen
Prüfsumme vergleichen. „Programm herunterladen und Zugangsdaten eingeben“ ist genau das Muster, das Phishing nachahmt. Die vollständigen Hashes stehen im Tooltip und unter installer.json.

Direkt auf dem Zielsystem

Linux — lädt das passende Binary, prüft die Prüfsumme und legt es ab:

wget -qO- /installer/install.sh | sh

Oder ohne Skript, direkt das Binary:

wget /installer/update-agent-linux-x64 -O update-agent chmod +x update-agent ./update-agent --action install

Windows (PowerShell):

irm /installer/install.ps1 | iex

Die Skripte richten nichts von selbst ein — sie laden, prüfen und sagen, wie es weitergeht. Ein Skript aus dem Netz, das ungefragt eine Anwendung aufsetzt und dabei nach Zugangsdaten fragt, wäre genau das, wovor man Nutzer sonst warnt.

Für die Installation wird ein Konto der Rolle installer gebraucht — anzulegen unter System → 👤 Benutzer. Ein Administratorkonto funktioniert auch, gehört aber nicht auf ein Zielsystem: seine Zugangsdaten würden damit auf jedem Rechner landen, auf dem je etwas installiert wurde.

Neues Software-Release veröffentlichen

Clients ohne Plattformangabe sehen ausschließlich any.
Dev Bugs (Entwicklung)
Prod Bugs (Produktion)
Offene Feature Requests
💡 Ideen & Gedanken
🤖 Von Agenten belegt
Gelöst / Umgesetzt

🤖 Aktive Agenten

Item(s) · Lease bis

🐛 Bugs, Features & Ideen Treffer

Zurücksetzen
💡 FEATURE' : '🐛 BUG'; $envBadge = match ((string)$item['environment']) { 'development' => '🔵 DEV', 'production' => '🔴 PROD', default => '' . e(strtoupper((string)$item['environment'])) . '', }; $statusBadge = match ((string)$item['status']) { 'open' => 'OFFEN', 'planned' => 'GEPLANT', 'in_progress' => 'IN BEARBEITUNG', 'resolved' => 'GELÖST', 'rejected' => 'ABGELEHNT', default => '' . e(strtoupper((string)$item['status'])) . '', }; $sevBadge = match ((string)$item['severity']) { 'idea' => '💡 IDEE', 'wishlist' => '⭐ WUNSCH', 'critical' => '🔥 KRITISCH', 'high' => 'HOCH', 'medium' => 'MITTEL', default => 'NIEDRIG', }; // json_encode liefert eine gueltige JS-Literaldarstellung; das // vorherige addslashes() konnte an Zeilenumbruechen zerbrechen. $jsonItem = e((string)json_encode($item, JSON_INVALID_UTF8_SUBSTITUTE)); $jsTitle = e((string)json_encode((string)$item['title'], JSON_INVALID_UTF8_SUBSTITUTE)); $isClaimed = !empty($item['claimed_by']) && !empty($item['lease_until']) && strtotime((string)$item['lease_until'] . ' UTC') > time(); ?>
ID / Typ Umgebung Projekt & Titel Build Schweregrad Anzahl Status Aktionen
Keine Einträge für diese Filterkombination.
#
Projekt: Von: 🔒 📲 🤖 📄 🏷️
x
1): ?>
Seite von ( Einträge)

➕ Bug, Feature Request oder Idee Erfassen

Fehler (24 h)
💀 Offene Abstürze
🔇 Stummgeschaltete Gruppen
davon Vorkommnisse
0): ?>
Vorkommnisse wurden als bekannt eingestuft und aus dieser Ansicht herausgehalten — ohne sie zu verwerfen. Der Zähler bleibt erhalten, damit eine auffällige Häufung trotzdem auffällt.

⚡ Fehler-Stream Gruppen

Zurücksetzen
'💀 FATAL', 'warning' => 'ℹ️ WARN', default => '⚠️ ERROR', }; // Rate im laufenden Fenster – auffällige Häufung sichtbar machen $windowCount = (int)($item['rate_window_count'] ?? 0); $windowFresh = !empty($item['rate_window_start']) && (time() - (int)strtotime((string)$item['rate_window_start'] . ' UTC')) < 3600; ?>
Klasse Fehler Projekt / Umgebung Build Anzahl Zuletzt Aktionen
Keine Fehler für diese Filterkombination.
Stummgeschaltete Einträge sind ausgeblendet — über „Nur stummgeschaltete“ sichtbar.
🔇 stummgeschaltet 1): ?> 📈 × in dieser Stunde 📄
×
1): ?>
Seite von ( Gruppen)

🔇 Bekannte, harmlose Fehler

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 dabei der eigentliche Gewinn — dass ein bekannter Fehler auftritt, ist normal; dass er plötzlich hundertmal so oft auftritt, ist ein Signal. Dafür ist die Alarmschwelle da.

0 oder leer = nie alarmieren
Pflichtfeld: In sechs Monaten weiß sonst niemand mehr, warum hier weggeschaut wird.

Bestehende Regeln

# Projekt Muster Begründung Alarmschwelle Treffer Status Aktionen
Noch keine Regeln angelegt.
ALLE
/h
Item(s) ·

💻 Anbindung

Ein Aufruf, den man in den globalen Exception-Handler hängt. Titel und Dringlichkeit leitet der Server ab, die Regeln oben greifen automatisch.

POST /api/errors/v1/report Authorization: Bearer <DC_TOKEN> Content-Type: application/json { "project_slug": "polytrader", "exception": "PDOException", "message": "SQLSTATE[23000]: Duplicate entry '...' for key 'uq_market'", "stack_trace": "...", "level": "error", // fatal = Prozess beendet "build": "v2.0.1", "environment": "production", "file": "src/Market/Importer.php", "line": 142 } Antwort: { "status": "success", "item_id": 42, "ignored": true, "occurrence_count": 3841, "rate_alerted": false, "message": "Als bekannt eingestuft, gezaehlt, nicht gemeldet." }

👑 Master-Tokens (Selbst-Provisionierung für Client-Apps & Host-Skripte)

Token ID Bezeichnung Typ / Identität Projekt / Lizenz Rechte (Scopes) Sub-Tokens Token Key Aktionen

Lic: ' . htmlspecialchars($mTok['license_key']) . '' : '' ?> Sub-Tokens
WIDERUFEN

🔑 Aktive Sub-Tokens (Per Provisioning API Erstellt)

$t['type'] === 'sub'); foreach ($subTokens as $sTok): $scopesArr = json_decode($sTok['scopes'], true) ?: []; $sRaw = !empty($sTok['raw_token']) ? $sTok['raw_token'] : $sTok['token_id']; $sMasked = (strlen($sRaw) > 12) ? substr($sRaw, 0, 12) . '••••••••••••••••' : $sRaw; ?>
Token ID Parent Master ID Bezeichnung / Client Umgebung Scopes Token Key Zuletzt Genutzt Status Aktionen

➕ Neuen Master-Token Erstellen

Stärkere Rechte schließen die schwächeren ein: bugtracker:manage umfasst Lesen und Melden, updateservice:publish auch Lesen. Für einen Coding-Agenten genügt daher Bugtracker verwalten.

['🌟 Alle Rechte', 'Uneingeschränkter Zugriff'], 'bugtracker:manage' => ['⚙️ Bugtracker verwalten', 'Übernehmen, kommentieren, schließen — schließt Lesen und Melden ein'], 'bugtracker:report' => ['🐛 Bugtracker melden', 'Nur Einträge anlegen — schließt Lesen ein'], 'bugtracker:read' => ['👁️ Bugtracker lesen', 'Nur Abfragen'], 'watchdog:ping' => ['🛡️ Watchdog Heartbeat', 'Statusmeldungen senden'], 'watchdog:read' => ['📊 Watchdog lesen', 'Monitore und Ereignisse abfragen'], 'updateservice:read' => ['📦 Updates prüfen', 'Auf neue Releases prüfen'], 'updateservice:publish' => ['🚀 Releases veröffentlichen', 'Neue Releases einstellen — schließt Prüfen ein'], ]; foreach ($scopeChoices as $value => $labels): ?>
Leer lassen für unbegrenzte Gültigkeit. Abgelaufene Tokens werden ab sofort tatsächlich abgewiesen — die Spalte existierte zuvor, wurde aber nie geprüft.

⚙️ Server & Umgebung

🩺 Systemzustand

PrüfungZustandHinweis
Watchdog-Evaluator Letzter Lauf: () Noch nie gelaufen — ohne ihn bleiben ausgefallene Monitore „up“.
Log-Verzeichnis beschreibbar var/log/ — Fehlerprotokolle
Konfiguration ausserhalb Git OK config/config.php ist per .gitignore ausgeschlossen

💬 Rocket.Chat Benachrichtigungen & Statusberichte

Hier konfigurieren Sie die automatischen Rocket.Chat-Benachrichtigungen. Alle 12 Stunden wird ein detaillierter Systemstatusbericht an den Status-Kanal gesendet. Kritische Systemausfälle und Bugs landen sofort im Alarm-Kanal.

Standard: 43200 Sekunden (12 Stunden)

🧪 Aktionen & Testlauf

Hier können Sie die Erreichbarkeit Ihres Rocket.Chat-Servers testen oder sofort einen 12-Stunden-Statusbericht auslösen.

📌 Status des automatischen 12h Cron-Jobs:
• Letzter Bericht:
• Letzter Status:
• Nächster geplanter Lauf: In ca.

👤 Benutzerkonten

Migration 012 fehlt. Rollen, Deaktivierung und der Zeitpunkt der letzten Anmeldung stehen erst danach zur Verfügung. Bis dahin gelten alle Konten als Administratoren.

Für die Erstinstallation von Anwendungen gibt es die Rolle installer. Solche Konten melden sich ausschließlich über /api/setup/v1/login an und bekommen dafür ein Token, das nach 30 Minuten verfällt. Am WebUI können sie sich nicht anmelden. Der Grund: Die Zugangsdaten werden auf jedem Zielsystem eingegeben — mit einem Administratorkonto verteilte man damit den Zugang zu Tokens, Lizenzen und Monitoren auf jeden Rechner, auf dem je etwas installiert wurde.

>
Benutzer Rolle Status Letzte Anmeldung Angelegt Aktionen
Sie DEAKTIVIERT' : 'aktiv' ?>

Neues Konto anlegen

📖 API-Dokumentation

Die Schnittstelle beschreibt sich selbst. Der frühere fest im Quelltext hinterlegte Textblock ist durch eine maschinenlesbare OpenAPI-Beschreibung ersetzt — ein Agent kann sie abrufen und sich daran orientieren, ohne dass eine Prompt-Vorlage gepflegt werden muss.

Kernendpunkte im Überblick BUGTRACKER (Token mit bugtracker:report / :read / :manage) POST /api/bugtracker/v1/report Bug, Feature oder Idee melden GET /api/bugtracker/v1/projects Projekt-Discovery GET /api/bugtracker/v1/manage?action=list Filtern, Pagination, updated_since POST /api/bugtracker/v1/manage?action=next Nächstes Item holen UND übernehmen POST /api/bugtracker/v1/manage?action=comment&id=42 POST /api/bugtracker/v1/manage?action=resolve&id=42 UPDATESERVICE GET /api/updateservice/v1/check?product=myapp&version=1.0.0 POST /api/updateservice/v1/publish (Token mit updateservice:publish) schliesst automatisch alle Items mit passendem resolved_in_build WATCHDOG POST /api/watchdog/v1/ping Heartbeat (watchdog:ping) GET /api/watchdog/v1/evaluate Zustandsbewertung (Cron, Shared Key) TOKENS POST /api/tokens/v1/provision Sub-Token aus Master-Token SYSTEM GET /api/health Verfügbarkeit & Schema-Status GET /api/openapi.json Vollständige Spezifikation

🗄️ Datenbank-Migration

Führt ausstehende Migrationen aus. Bereits angewendete Versionen werden in dc_migrations vermerkt und übersprungen.

Hinweis zur Sicherheit: Dieser Endpunkt war zuvor ohne Anmeldung erreichbar und setzte bei jedem Aufruf das Administrator-Passwort auf einen fest im Quelltext stehenden Wert zurück. Er verlangt jetzt eine angemeldete Sitzung oder den Shared Key und legt ein Konto nur an, wenn noch keines existiert.
Migration jetzt ausführen