Files
ClawdDotNet/docs/Deploymentcenter-Integration.md
RichardandClaude Opus 5 740649789e Eine Roadmap statt neun verteilter Listen
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>
2026-08-23 18:31:06 +02:00

17 KiB
Raw Permalink Blame History

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 die instance.json der Instanz. Das Token wird beim ersten Speichern durch die Anwendung verschlüsselt; im Klartext eingetragen funktioniert es ebenfalls, weil der SecretProtector beide 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_expired ist laut SDK-Vertrag vorübergehend). Wer das anders will, prüft in LicenseGate zusätzlich auf cache_expired und 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

  1. Oberfläche für den Bugtracker — Client vorhanden, Schaltfläche fehlt.
  2. Agenten-Tool für den Bugtracker — würde den Agenten-Workflow des Deploymentcenters nutzbar machen.
  3. Release-Streckepack-and-deploy aufrufen und <Version> dabei mitgeben, danach update-agent einbinden. Die Versionsnummer selbst ist mit 2.1 erledigt.
  4. SDK als Git-Submodul unter external/ statt Cross-Repo-Pfad.
  5. 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).