Der Stand lag ueber eine Bestandsaufnahme, drei Konzeptpapiere, vier Umsetzungsplaene und zwei Deploymentcenter-Dokumente verteilt - jedes mit eigener Reihenfolge, teils widersprueglich. Alle offenen Punkte daraus sind in docs/Roadmap.md zusammengefuehrt. Aufbau der neuen Roadmap - Statusvokabular: erledigt / beschlossen-offen / Entscheidung noetig / zurueckgestellt / Idee. Damit steht das Geparkte sichtbar drin, statt in einem Konzeptpapier zu verschwinden. - Herkunft-Spalte traegt das alte Kuerzel (S4, K2, B9, T4, F-A1, C1, DC1), damit die archivierten Papiere auffindbar bleiben, ohne sie zu lesen. - Abschnitt 1 Reihenfolge, 2 offene Entscheidungen, 3 die Vorhaben nach Bereich, 4 Ideenspeicher, 5 Chronik des Erledigten, 6 Modell-Einstufung, 7 Herkunftskarte. Was dabei sichtbar wurde - Neun Entscheidungen blockieren Arbeit, ohne dass sie Aufwand kosten - allen voran Matrix oder Rocket.Chat. Sie stehen jetzt gesammelt in Abschnitt 2 statt verstreut in den Diskussionsteilen der Konzepte. - B8 (keine Tiefenbegrenzung bei AgentComm) ist unveraendert offen. Das ist keine Theorie: A haelt sein Gate, waehrend es auf B wartet - ruft B nun A, warten beide bis zum Timeout. Der Testfall A13 dafuer fehlt bis heute. - Die vier Umsetzungsplaene vom 2026-08-05 sind alle unumgesetzt und waren in keiner Roadmap verzeichnet. Archiv Sechs Dokumente ziehen nach docs/archiv/: Bestandsaufnahme, Konzepte Backup/Finanz/Analyse, Linux-Portierung-Analyse, Lizenz-HardwareId-v2 (gegenstandslos - LicenseLabrador ist ersetzt), Deploymentcenter-Review und der 2.4-Integrationsplan. Sie bleiben als Begruendung lesbar, werden aber nicht mehr fortgeschrieben; das README ordnet jedes einzeln ein und warnt, dass ihre Quelltext-Verweise ins Leere gehen koennen. Bauplan bleibt Bauplan Taskboard, Audit, Staging, Memory, Agentenkommunikation, RocketChat und Deploymentcenter-Integration bleiben in docs/ - sie sind die Detailvorgabe fuer die Umsetzung, nicht Vorhabenlisten. Jedes bekommt oben eine Zeile, die seine Rolle und den Umsetzungsstand nennt und auf die Roadmap zeigt. Dasselbe fuer die vier Umsetzungsplaene: die Reihenfolge gilt in der Roadmap, nicht im Plan. Nebenbei repariert: Taskboard-Konzept und Teststrategie verwiesen auf AgentScheduler und ToolJobScheduler, die seit der Scanner-Konsolidierung geloescht sind. Alle Dokument-Verweise in docs/ sind geprueft. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
17 KiB
Deploymentcenter-Integration
Beschreibung der Verdrahtung (Heartbeat, Fehler-Stream, Bugtracker, Updates). Offene Punkte dazu stehen in der Roadmap 3.5, nicht hier.
Stand: 2026-08-08, Deploymentcenter 2.1. Ersetzt den früheren
Integrationsplan-WatchDog-LicenseLabrador.md.
ClawdDotNet spricht das Deploymentcenter als eine Gegenstelle an. Vorher waren es zwei Fremdprojekte mit je eigenem Server, eigenem Schlüssel und eigener Anleitung:
| Vorher | Jetzt |
|---|---|
WatchDog (watchdog.mhdf.de, X-Watchdog-Key) |
Deploymentcenter-Modul Watchdog, Authorization: Bearer |
LicenseLabrador (license.mhdf.de, Ed25519-Public-Key) |
Deploymentcenter-Modul Lizenz |
| — | Update-Prüfung |
| — | Fehler-Stream (ungefangene Ausnahmen) |
| — | Bugtracker |
Eine Adresse, ein Token. Beides steht in den Anwendungseinstellungen.
1. Was der Betreiber einzutragen hat
| Ort | Wert |
|---|---|
| Einstellungen → Deploymentcenter → Server-URL | https://dc.mhdf.de (Vorgabe) |
| Einstellungen → Deploymentcenter → Token | Master-Token mit watchdog:ping + bugtracker:report |
| Einstellungen → Lizenz → Lizenzschlüssel | Der Schlüssel für das Projekt clawddotnet |
| Worker-Tab → Dienst Instanz-Watchdog | einschalten, greift beim nächsten Start der Instanz |
Das Token entsteht im WebUI unter Token-Verwaltung → Master-Token erstellen. Es
wird verschlüsselt (DPAPI) in Settings.json abgelegt.
Bis die Avalonia-Einstellungsansicht steht, gibt es für diese Felder noch keine Oberfläche — die Seite „Einstellungen" ist ein Platzhalter. Die Werte kommen vorläufig von Hand in
Settings.json(Ort steht beim Start im Protokoll:%APPDATA%\ClawdDotNet\Settings.json, unter Linux$XDG_CONFIG_HOME) bzw. in dieinstance.jsonder Instanz. Das Token wird beim ersten Speichern durch die Anwendung verschlüsselt; im Klartext eingetragen funktioniert es ebenfalls, weil derSecretProtectorbeide Richtungen verträgt.
Serverseitig ist eine Sache Pflicht, sonst ist die Überwachung wertlos: der Evaluator-Cron. Ohne ihn ändert sich ein Monitor-Zustand nur beim Eintreffen eines Heartbeats — eine abgestürzte Instanz bliebe dauerhaft grün.
* * * * * curl -fsS -H "Authorization: Bearer <SHARED_KEY>" https://dc.mhdf.de/api/watchdog/v1/evaluate > /dev/null
2. Watchdog — ein Monitor je Instanz
Das war die Vorgabe und ist jetzt sauber abgedeckt: Der Server führt Monitore über das
Paar source + instance (UNIQUE KEY uq_monitor (source, instance) in
sql/schema.sql). Alle Instanzen melden unter source = "clawddotnet" und tragen ihre
eigene instance. Fällt eine von dreien aus, fällt genau deren Monitor — und nur der
schlägt Alarm.
instance ist standardmäßig die InstanceId (stabil, aber im Dashboard nichtssagend).
In den Instanz-Einstellungen lässt sich stattdessen ein Name eintragen
(WatchdogConfig.Instance); ein
späterer Wechsel legt allerdings einen neuen Monitor an.
Eine Registrierung vorab gibt es nicht mehr. Der Monitor entsteht beim ersten
Heartbeat von selbst (INSERT … ON DUPLICATE KEY UPDATE). Der frühere Weg über
POST /api/register hatte im Deploymentcenter nie ein Gegenstück — die alte Anbindung
lief in dieser Form also gegen einen Endpunkt, den es nicht gibt.
Was der Heartbeat trägt
POST /api/watchdog/v1/ping
Authorization: Bearer <Instanz-Token>
| Feld | Inhalt |
|---|---|
source / instance |
clawddotnet / InstanceId bzw. eingestellter Name |
status |
ok, warning, error — beim Beenden stopped |
interval |
60 s (Vorgabe). Daraus leitet der Evaluator ab: 2× → warning, 4× → down |
message |
Instanzname + Kurzbegründung |
os |
Betriebssystem + .NET-Version |
version |
Produktversion (2.1). Landet in watchdog_monitors.app_version — bei mehreren Instanzen der Unterschied zwischen „läuft" und „läuft noch auf der alten Fassung" |
checks |
agents, scheduler, budget — siehe unten |
metrics |
agentCount, runningChats, todayCostUsd, todayTokens |
checks — der eigentliche Gewinn
Ein Heartbeat beweist nur, dass ein Faden läuft. Deshalb geht der selbst ermittelte
Zustand je Teilbereich mit; schlägt eine Prüfung fehl, stuft der Server einen als ok
gemeldeten Beat auf warning herab und nennt in der Antwort die betroffene.
| Prüfung | Fehlschlag bedeutet |
|---|---|
agents |
Kein OpenRouter-Key — die Instanz läuft, arbeitet aber nichts ab |
scheduler |
Die Taktschleife des Aufgaben-Scanners ist ausgestiegen |
budget |
Tagesgrenze für Kosten oder Token erreicht |
„Scanner noch nicht gestartet" gilt nicht als Fehlschlag: Er läuft erst nach der
Startabgleichung an, der erste Heartbeat geht sofort raus. Sonst gäbe es bei jedem
Start ein warning_raised und kurz darauf ein recovered — zwei Einträge im
Ereignisprotokoll für einen Normalvorgang.
Metriken sind nur Zahlen
Der Server legt numerische Werte mit Zeitstempel ab (14 Tage) und vergleicht den
aktuellen Wert mit dem Sieben-Tage-Schnitt desselben Monitors. Nicht-numerische Werte
verwirft er dabei stillschweigend — instanceId, instanceName und buildVersion
standen früher in den Metriken und waren dort wirkungslos. Beschreibendes steht jetzt
in message und os.
Angekündigtes Ende
Beim Herunterfahren geht ein Heartbeat mit status: "stopped" raus, danach das Ereignis
stopped_graceful. Der Evaluator lässt einen so gemeldeten Monitor in Ruhe. Ohne das
erzeugte jedes geplante Beenden wenige Minuten später einen Fehlalarm.
Nebenbei korrigiert: Die alte Anbindung schickte die Ereignisarten start und stop —
beide stehen nicht auf der Liste des Servers und landeten stillschweigend als started.
Jetzt sind es started und stopped_graceful.
Token je Instanz
Beim ersten Start tauscht die Instanz das anwendungsweite Token über
POST /api/tokens/v1/provision gegen ein eigenes, auf watchdog:ping und
bugtracker:report beschränktes Sub-Token und legt es verschlüsselt in der
Instanzkonfiguration ab. Danach liegt auf der Instanz nicht mehr das Master-Token, und
ein einzelner Zugang lässt sich widerrufen, ohne die anderen mitzunehmen.
Das ist derselbe Zweck, den die frühere Selbstregistrierung hatte. Scheitert es (etwa weil das hinterlegte Token selbst ein Sub-Token ist und keine weiteren ausstellen darf), wird mit dem hinterlegten Token gemeldet — Monitoring, das nur bei perfekter Rechtelage läuft, ist genau dann still, wenn man es braucht.
3. Lizenz
Startprüfung in LicenseGate. Die
Offline-Gnadenfrist steckt im SDK: Es legt nach jeder erfolgreichen Prüfung einen mit
AES-GCM verschlüsselten, an die Hardware gebundenen Zwischenspeicher an (LLS2,
seit 2.1 Schema 3) und trägt damit über Ausfälle hinweg.
Urteil und Fehlversuch sind zwei verschiedene Dinge
Das ist der Kern der 2.1-Anpassung. LicenseValidationResult.IsTransient unterscheidet:
| Statuswerte | Folge | |
|---|---|---|
| Urteil des Servers | revoked, expired, not_found, activation_limit, suspended, clock_rollback |
Anwendung startet nicht bzw. beendet sich |
| Kein Urteil erhalten | server_unavailable, cache_expired |
Warnung, Betrieb läuft weiter |
Nur das Urteil sperrt. Ein Serverausfall darf nicht jede Installation gleichzeitig
aussperren — und eine Drosselung (429) oder ein 500 sind Aussagen über den Server,
nicht über die Lizenz. Das gilt an beiden Stellen gleich: Startprüfung und laufende
Nachprüfung fragen dasselbe Merkmal ab.
Bewusst in Kauf genommen: Ein Rechner, der die Gegenstelle nie erreicht, läuft damit auf Dauer mit Warnung weiter — auch nach Ablauf der Gnadenfrist (
cache_expiredist laut SDK-Vertrag vorübergehend). Wer das anders will, prüft inLicenseGatezusätzlich aufcache_expiredund behandelt es als Urteil. Es sollte eine Entscheidung sein, nicht ein Nebeneffekt.
Offline-Gnadenfrist ist echt begrenzt
Seit 2.1 wertet der Client cache_ttl_hours des Projekts aus (Vorgabe 168 h). Vorher
galt faktisch das Ablaufdatum der Lizenz — bei einer Lizenz bis 2040 also unbegrenzt.
Der verbleibende Rest steht in CacheExpiresAt und wird beim Start angezeigt, wenn die
Prüfung aus dem Zwischenspeicher kam.
state.dat steigt auf Schema 3; Schema 2 wird weiter gelesen. Ein Rückschritt auf ein
älteres SDK verwirft den Zwischenspeicher — dann ist einmal eine Online-Prüfung nötig.
Kein Public-Key mehr
Die frühere Fassung führte einen Ed25519-Public-Key als „Vertrauensanker". Im
Deploymentcenter gibt es dazu keine Gegenseite — der Client liest ausschließlich das Feld
status. Ein Schlüssel, der nichts prüft, ist schlimmer als keiner: Er lässt Schutz
vermuten, wo keiner ist. Details in
Deploymentcenter-Anbindung-Review, Abschnitt 2.1.
Der Projekt-Slug ist clawddotnet
LicenseInfo.ProductSlug gilt für
alle Module — Lizenz, Bugtracker, Fehler-Stream und Update-Prüfung greifen auf
dieselbe Tabelle dc_projects zu.
Bis zur Umstellung stand hier clawd, der Name aus dem LicenseLabrador-Backend. Das
ist eine Falle mit langer Zündschnur: Der Server beantwortet ein unbekanntes Projekt mit
demselben not_found wie einen unbekannten Schlüssel — der Unterschied steht
ausschließlich in message ("Project not found" gegen "Invalid license key"). Wer
den Text nicht durchreicht, sucht den Fehler beim Lizenzschlüssel, während das Projekt
gar nicht existiert. Der Torwächter gibt die Serverantwort deshalb mit aus und schreibt
sie ins Protokoll.
Deaktivieren läuft über das WebUI
POST /api/license/v1/deactivate verlangt den shared_key des Servers. Der gehört nicht
in eine ausgelieferte Anwendung, deshalb ist der Weg die Hardware-Liste im WebUI
(Schaltfläche „Freigeben").
Laufende Nachprüfung
LicenseWatch prüft alle zwölf
Stunden nach — dieselbe Unterscheidung wie oben. Ohne das wirkt ein Widerruf erst beim
nächsten Start, bei einem wochenlang laufenden Dienst also praktisch nie.
4. Version und Updates
Eine Stelle für die Version
<Version> in Directory.Build.props ist die Wahrheit.
Deploymentcenter.BuildInfo.targets (seit 2.1 einbindbar) erzeugt daraus zur
Übersetzungszeit ClawdDotNet.App.ReleaseInfo mit Version, GitCommit,
GitCommitShort, BuildDateUtc, Channel und Summary.
Der Wert geht an vier Stellen nach draußen, die vorher alle geraten haben:
| Stelle | Vorher |
|---|---|
Aktivierungsliste (app_version) |
fest "1.0.0" im SDK — jede Installation gleich |
Heartbeat (version) |
gab es nicht |
Fehlermeldungen (build) |
— |
| Versionsvergleich der Update-Prüfung | 0.0.<BuildInfo.Build>, behelfsweise |
Die Klasse heißt bewusst ReleaseInfo, nicht BuildInfo: Diesen Namen trägt in
ClawdDotNet.Core schon ein von Hand geführter Zähler mit Änderungstext. Zwei
gleichnamige Klassen mit verschiedener Bedeutung wären eine Falle. Umgestellt über
DeploymentcenterBuildInfoClass in der csproj.
Prüfung
Einmalig beim Start gegen GET /api/updateservice/v1/check, über
Deploymentcenter.Client.UpdateClient. Läuft nebenher und blockiert nichts; liegt eine
neuere Version vor, erscheint ein Hinweis mit Changelog. Ob und wann aktualisiert wird,
entscheidet der Benutzer — eine Anwendung, die sich beim Start selbst beendet, um sich zu
erneuern, ist genau dann im Weg, wenn man sie braucht.
Seit 2.1 liefern beide Wege vollständige Daten: die statische latest.json in camelCase,
die API in snake_case, jeweils über ein eigenes Modell (VersionInfo bzw.
ApiReleaseInfo). Vorher kam über den API-Zweig außer der Versionsnummer nichts an — und
der ist genau der Rückfall, wenn die latest.json fehlt. Die Download-Adresse wird
mitgeführt (UpdateAvailability.DownloadUrl), damit der update-agent später ohne
weitere Änderung anschließen kann.
Der update-agent ist noch nicht eingebunden, und solange kein Release über
pack-and-deploy veröffentlicht wird, hat die Prüfung nichts zu finden.
5. Fehler-Stream
Ungefangene Ausnahmen gehen an POST /api/errors/v1/report. Verdrahtet in
App.axaml.cs an drei Stellen:
AppDomain.UnhandledException, TaskScheduler.UnobservedTaskException und
Dispatcher.UIThread.UnhandledException.
Erst nach dem Aufbau verdrahtet, nicht in Main: Vorher gibt es weder Einstellungen
noch Token. Die Kehrseite ist bewusst in Kauf genommen — ein Absturz während des
Starts erreicht das Deploymentcenter nicht, steht aber im Protokoll.
Eigene Drosselung in
ErrorReporter: Derselbe
Fehler (Typ + oberste Stelle im Stacktrace) geht höchstens einmal alle fünf Minuten
raus. Der Server drosselt auch, aber erst, nachdem die Anfragen über die Leitung waren.
Die Fehlermeldung selbst gehört nicht zum Kennzeichen — sie enthält oft wechselnde
Werte, und dann wäre jeder Aufruf ein neuer Fehler.
Bekannte, harmlose Fehler lassen sich serverseitig unter Bugtracker → Ignore-Regeln stummschalten. Sie werden weiter gezählt; der Zähler ist der Zweck: Dass ein bekannter Fehler auftritt, ist normal — dass er plötzlich hundertmal so oft auftritt, bedeutet, dass sich etwas geändert hat.
6. Bugtracker
BugtrackerClient für
bewusst formulierte Einträge (Fehler, Wunsch, Idee) mit Titel und Beschreibung, gegen
POST /api/bugtracker/v1/report. Der Absender wird serverseitig aus dem Token
abgeleitet und lässt sich nicht frei wählen.
Der Client ist da und über AppHost.Deploymentcenter.Bugtracker erreichbar; eine
Oberfläche dafür fehlt noch („Fehler melden"-Schaltfläche). Ein Agenten-Tool wäre der
nächste sinnvolle Schritt — Agenten könnten dann selbst Wünsche und Fehler eintragen,
und der Agenten-Workflow des Deploymentcenters (Claim/Lease über
manage?action=next) würde sie abarbeiten.
7. Wo was liegt
| Datei | Inhalt |
|---|---|
Deploymentcenter/DeploymentcenterApi.cs |
Gemeinsamer Unterbau: Bearer-Header, HTTPS-Pflicht, Umschlag auspacken, DeploymentcenterException mit stabilem Code |
Deploymentcenter/Watchdog/ |
Heartbeat-Client, Zustandsermittlung, Takt-Dienst |
Deploymentcenter/ErrorReporter.cs |
Fehler-Stream mit Drosselung |
Deploymentcenter/BugtrackerClient.cs |
Bugtracker-Einträge |
Deploymentcenter/TokenProvisioner.cs |
Sub-Token je Instanz |
Services/DeploymentcenterService.cs |
Verdrahtung: Token beschaffen, Heartbeat starten, Update prüfen |
Services/LicenseGate.cs |
Startprüfung |
Services/LicenseWatch.cs |
Laufende Nachprüfung |
Die Lizenz läuft bewusst nicht über DeploymentcenterApi: Sie hat ein eigenes
Antwortformat (kein status/error-Umschlag — status trägt dort den Lizenzzustand),
einen eigenen Zwischenspeicher und muss vor allem anderen laufen.
Watchdog, Fehler-Stream und Bugtracker deckt das SDK Deploymentcenter.Client nicht ab;
dafür ist der eigene Unterbau da. Hardware-ID v2, Lizenz-Zwischenspeicher und
Update-Prüfung kommen aus dem SDK — die nachzubauen wäre Verdopplung.
8. Tests
tests/ClawdDotNet.Core.Tests/Deploymentcenter/:
Bearer-Header, Fehlerumschlag → Ausnahme mit Code (auch bei HTTP 200), HTTPS-Pflicht mit
Localhost-Ausnahme, Heartbeat-Pfad und -Rumpf, zwei Instanzen → zwei Monitore, Checks
und Metriken, Antwort-Auswertung, Drosselung des Fehler-Streams, Sub-Token-Bezug.
9. Offen
- Oberfläche für den Bugtracker — Client vorhanden, Schaltfläche fehlt.
- Agenten-Tool für den Bugtracker — würde den Agenten-Workflow des Deploymentcenters nutzbar machen.
- Release-Strecke —
pack-and-deployaufrufen und<Version>dabei mitgeben, danachupdate-agenteinbinden. Die Versionsnummer selbst ist mit 2.1 erledigt. - SDK als Git-Submodul unter
external/statt Cross-Repo-Pfad. - Hierarchie (
parent_source): Läuft die Instanz auf einem Host, der selbst als Monitor geführt wird, sollte sie ihn als übergeordnete Entität eingetragen bekommen — sonst erzeugt ein Hostausfall eine Meldung je Instanz. Das ist im WebUI zu pflegen, nicht im Client (siehe aber Anmerkung 2 in den Rückmeldungen).