Wie es vorher war
Mein Lizenzportal läuft auf einem Windows-VPS: der Host von FoxServer, die Backend-DLL in Visual FoxPro, die DBF-Tabellen und die statische Website. Ich arbeite mit Claude auf meinem Entwicklungsrechner, und lange Zeit lag der VPS außerhalb seiner Reichweite.
Der Ablauf sah so aus. Claude schlug einen Befehl vor, ich öffnete den Remotedesktop, fügte ihn in PowerShell ein, kopierte die Ausgabe und gab sie zurück. War die Ausgabe lang, kürzte ich sie. Gab es einen Fehler, ging es von vorn los. Ich vermittelte zwischen Claude und dem Server, und bei jedem Durchgang ging etwas verloren: eine Zeile, die ich nicht kopiert hatte, ein Befehl im falschen Fenster, ein schlecht zusammengefasstes Ergebnis.
Das Ausrollen war der heikelste Teil. Um die Backend-DLL zu ersetzen, musste ich den Dienst anhalten, die Datei kopieren, sie mit regsvr32 registrieren, den Dienst wieder starten und im Browser prüfen, ob alles antwortet. Für die Website kopierte ich den erstellten Ordner über den veröffentlichten. Jeder Schritt hing davon ab, dass ich keinen ausließ, und es blieb kein Protokoll darüber, was getan wurde und wie der Server vorher aussah.
Was Golem ist
Golem ist ein MCP-Agent, der Claude vorübergehend Zugriff auf einen anderen Windows-Rechner gibt. Er besteht aus zwei Teilen: dem Agenten, einer einzelnen ausführbaren Datei, die ich von Hand in der Sitzung des VPS starte, und einem Client, den ich einmal auf meinem Rechner installiert habe und über den Claude sich verbindet.
Beim Start gibt der Agent eine Zeile mit seiner Adresse, einem Token und dem Fingerabdruck seines Zertifikats aus. Diese Zeile gebe ich Claude, und von da an kann es PowerShell ausführen, Dateien lesen und schreiben, Ordner hoch- und herunterladen, Dienste steuern, DLLs registrieren und den Bildschirm sehen. Der Token ändert sich bei jedem Start, die Verbindung ist verschlüsselt und wird gegen diesen Fingerabdruck geprüft, und der Agent beendet sich nach 30 Minuten ohne Anfragen von selbst. Wenn ich fertig bin, bleibt auf dem VPS nichts installiert und kein Port offen.
Jeder Aufruf landet in einem Audit-Log neben der ausführbaren Datei, ohne Tokens, damit ich später nachsehen kann, was getan wurde.
Ein Backend-Deployment
So läuft es heute, wenn ich die Portal-DLL ersetze. Ich nehme die Zahlen eines Deployments vom 18. September.
Claude erstellt die DLL auf meinem Rechner und lädt sie mit ihrem endgültigen Namen in einen Bereitstellungsordner auf dem VPS hoch, während der Dienst noch läuft. Der Upload prüft beim Eintreffen den SHA-256 jeder Datei, sodass ich eine beschädigte Übertragung bemerke, bevor ich die Produktion anfasse.
Danach legt Claude eine Kopie der DLL an, die in Produktion läuft, neben dem Original und mit dem Datum im Namen. Anschließend startet es ein einziges Skript, das den Dienst anhält, auf das Ende des Prozesses wartet, die neue DLL kopiert, die COM-Klasse registriert, den Dienst startet und prüft, ob das Portal antwortet. Schlägt einer dieser Schritte fehl, stellt dasselbe Skript die Kopie wieder her und startet den Dienst erneut.
Das Registrieren der DLL war von Hand der Schritt mit den meisten Problemen. Die MTDLL des Backends ist ein 32-Bit-COM-Server und muss mit dem 32-Bit-regsvr32 registriert werden; ein .NET-Helper dagegen wird mit regasm registriert. Nimmt man das falsche Werkzeug oder die falsche Bitbreite, ist der Fehler unklar oder erscheint gar nicht, denn regsvr32 /s meldet nichts, auch wenn es scheitert. Golem hat dafür ein eigenes Tool, register_dll. Es liest den Header der DLL, um zu erkennen, ob sie nativ oder .NET ist und wie viele Bits sie hat, wählt das passende Werkzeug und sucht danach in der Registry, in der 32-Bit- und in der 64-Bit-Ansicht, die COM-Klassen, die auf diesen Pfad zeigen. Die Antwort sagt, welche Klassen registriert wurden und ob sie es vorher schon waren.
function AufEndeWarten {
$uhr = [Diagnostics.Stopwatch]::StartNew()
while (Get-Process FoxServer.Host -ErrorAction SilentlyContinue) {
if ($uhr.ElapsedMilliseconds -gt 30000) { return $false }
Start-Sleep -Milliseconds 250
}
return $true
}
trap { Wiederherstellen; exit 9 }
Stop-Service FoxServer -Force
if (-not (AufEndeWarten)) { Wiederherstellen; exit 2 }
Copy-Item "$bereitstellungsOrdner\portal.dll" "$projektOrdner\portal.dll" -Force
Start-Service FoxServer
if (-not (AufLaufenWarten)) { Wiederherstellen; exit 4 }Bei diesem Deployment stand der Dienst 0,7 Sekunden still. Danach rief Claude die Versionsseite der API und eine Route auf, die eine Sitzung verlangt, und verglich die Antworten mit denen, die es vor dem Start gespeichert hatte.
Das Skript läuft auf dem VPS im Hintergrund. Bricht meine Verbindung mittendrin ab, läuft das Skript weiter, und das Ergebnis lässt sich nach dem erneuten Verbinden abholen. Denn ein Deployment, das zwischen Anhalten und Starten abbricht, lässt das Portal offline.
Ein Host-Deployment
FoxServer zu aktualisieren, also die Engine, die das Portal und andere Projekte ausliefert, ist heikler, weil es zwanzig Dateien im Host-Ordner betrifft. Claude packt sie zusammen mit einer Liste ihrer SHA-256-Werte in ein Zip, lädt es hoch, entpackt es auf dem VPS und gleicht jede Datei mit der Liste ab, bevor es den laufenden Ordner anfasst.
Dann kopiert es den gesamten Host-Ordner in einen Sicherungsordner, vergleicht die zwanzig Dateien in beiden, um sicherzugehen, dass die Sicherung vollständig ist, und führt dasselbe Schema wie oben aus: anhalten, warten, kopieren, Hashes prüfen, starten, die Version des laufenden Prozesses lesen. Stimmt die Version nicht, stellt es die vorherige wieder her und startet sie.
Am 16. September, beim Wechsel auf 0.9.20, stand der Dienst 1,5 Sekunden still. Der erste Versuch an diesem Tag machte sich selbst rückgängig, und daher stammt die Schleife AufEndeWarten: Wait-Process wartet nicht auf einen Prozess, der zu einem Windows-Dienst gehört. Es kehrt sofort ohne Fehler zurück, sodass das Skript prüfte, ob der Prozess noch lief, bevor er sich beendet hatte, das als Fehlschlag wertete und die vorherige Version wiederherstellte. Mit der Schleife endet der Prozess in Millisekunden, und der nächste Versuch klappte.
Die Website
Die Website ist statisch. Ich erstelle sie auf meinem Rechner, und der entstandene Ordner wird über den veröffentlichten hochgeladen. Dieser Upload ist additiv: Er löscht auf dem Server nichts, weil im selben Ordner weitere Dateien liegen, die nicht aus dem Build kommen.
Vor dem Upload legt Claude mit einem einzigen Aufruf eine vollständige Kopie des veröffentlichten Ordners an. Am 18. September waren das 1.178 Dateien und etwa 380 MB. Danach vergleicht der Upload die Hashes und überträgt nur, was sich geändert hat: an diesem Tag 62 neue oder geänderte Dateien und 263, die schon identisch waren.
Zur Kontrolle ruft Claude jede geänderte Seite von der öffentlichen Website ab und vergleicht ihren SHA-256 mit dem der Datei, die ich erstellt habe. Stimmen sie überein, sehen die Besucher das, was ich erstellt habe, ohne von Caches abzuhängen oder davon, dass jemand die Seite mit bloßem Auge prüft.
Produktionsdaten
Nicht jede Aufgabe ist ein Deployment. Diese Woche habe ich einen Patreon-Webhook veröffentlicht, der in eine DBF-Tabelle schreibt, und um ihn zu testen, musste ich sehen, wie die Zeile nach jedem Ereignis aussah.
Das Portal hält seine Tabellen offen, solange es läuft. Deshalb kopiert Claude die Tabellendateien in einen eigenen Ordner und fragt die Kopie mit FoxAgent ab, das auf dem VPS über Golem selbst gestartet wird. Zum Schreiben, etwa um eine Testzeile zu löschen, legt es auf dem VPS ein .prg mit der richtigen Kodierung an, führt es mit Visual FoxPro ohne Oberfläche aus und liest die Ergebnisdatei. Dieses .prg öffnet die Tabelle gemeinsam, prüft, dass nur eine Zeile die Bedingung erfüllt, und hält fest, wie viele Datensätze es vorher und nachher gab.
Auch das Lesen der Server-Logs hat sich geändert. Claude liest die letzten Zeilen eines Logs von mehreren hundert Megabyte, ohne es ganz zu laden, und zwar sofort: Als Patreon sein Testereignis schickte, stand das Ergebnis im Log, bevor ich zur Unterhaltung zurückkam.
Auch auf meinem eigenen Rechner
Golem ist nicht nur für Server gedacht. Um den Patreon-Webhook anzulegen, musste man das Web-Dashboard von Patreon benutzen, und Claudes Browser kann patreon.com nicht öffnen. Ich startete den Agenten auf meinem eigenen PC über einen Tunnel, und Claude konnte meinen Browser über Bildschirmfotos sehen und mir bei jedem Schritt sagen, was ich anklicken sollte, während ich klickte.
Die Grenzen
Es gibt Dinge, die Golem nicht tut, manche aus Absicht und manche, weil es sie noch nicht löst.
- Der Agent ist kein Dienst. Ich muss ihn starten, wenn ich ihn brauche, und Claude die Verbindungszeile geben. Dauert die Sitzung länger und vergehen 30 Minuten ohne Aufrufe, beendet sich der Agent und muss mit einem neuen Token neu gestartet werden. Das ist eine kleine Unannehmlichkeit, und sie ist mir lieber als ein Port, der den ganzen Tag auf dem VPS offen steht.
- Um den Bildschirm zu sehen, muss die Remotedesktop-Sitzung verbunden bleiben. Schließe ich das Fenster, ohne mich abzumelden, zeichnet Windows diesen Desktop nicht mehr, und die Bildschirmfotos schlagen fehl.
- Claude sieht alles, was ein Skript ausgibt. Würde ein Skript ein Passwort anzeigen, sähe Claude es. Die Geheimnisse des Servers bleiben in ihren Dateien, und die Skripte geben sie nicht aus.
- Der Agent tut, was man ihm aufträgt. Die Sicherheit eines Deployments kommt von der vorherigen Sicherung, der Hash-Prüfung und der Rücknahme, und ein Deployment-Skript scheitert wie jeder andere Code. Am 18. September, beim Update des Hosts auf 0.9.25, blieb das erste Skript direkt nach dem Anhalten des Dienstes stehen: Zwei Variablen unterschieden sich nur in der Groß- und Kleinschreibung, PowerShell unterscheidet sie nicht, und eine überschrieb die andere. Die Rücknahme steckte in den Prüfungen und lief nie, sodass der Dienst 39 Sekunden stillstand, bis ein zweites Skript die Arbeit beendete. Seitdem verwenden die Skripte lange, eindeutige Namen, und die Rücknahme steht in einem
trap, der bei jedem Fehler greift.
Was sich geändert hat
Ich vermittle nicht mehr. Claude arbeitet auf dem VPS mit denselben Werkzeugen, die ich benutzen würde, und ich prüfe, was es tut, und entscheide, wann die Produktion angefasst wird. Jedes Deployment hinterlässt eine Kopie des vorherigen Stands, eine Hash-Prüfung des Hochgeladenen und ein Protokoll des Ausgeführten.
Die Ausfälle des Dienstes werden in Sekunden gemessen und bei jedem Deployment festgehalten, auch der mit 39 Sekunden. Früher habe ich diese Zahl nicht erfasst.
Golem gibt es im Shop. Es hat keine Testlizenz: Ohne Lizenz läuft es im Nur-Lese-Modus, und so wird es evaluiert. Claude kann den Server ansehen, Logs und Dateien lesen und den Bildschirm sehen, aber nichts ändern. Zum Ausrollen, zum Anhalten von Diensten oder zum Registrieren von DLLs braucht man die Lizenz.