How it used to be

My licensing portal runs on a Windows VPS: the FoxServer host, the backend DLL written in Visual FoxPro, the DBF tables and the static website. I work with Claude on my development machine, and for a long time the VPS was out of its reach.

The workflow went like this. Claude suggested a command, I opened remote desktop, pasted it into PowerShell, copied the output and sent it back. If the output was long, I trimmed it. If there was an error, we started over. I was the go-between for Claude and the server, and something got lost on every round trip: a line I did not copy, a command pasted into the wrong window, a result I summarized badly.

Deploying was the most delicate part. To replace the backend DLL I had to stop the service, copy the file, register it with regsvr32, start the service again and check in the browser that everything answered. For the website, I copied the built folder over the published one. Every step depended on me not skipping any of them, and nothing recorded what had been done or what the server looked like before.

What Golem is

Golem is an MCP agent that gives Claude temporary access to another Windows machine. It has two parts: the agent, a single executable that I start by hand inside the VPS session, and a client that I installed once on my machine and that Claude uses to connect.

When it starts, the agent prints one line with its address, a token and the fingerprint of its certificate. I hand that line to Claude, and from then on it can run PowerShell, read and write files, upload and download folders, manage services, register DLLs and see the screen. The token changes on every start, the connection is encrypted and checked against that fingerprint, and the agent shuts itself down after 30 minutes without requests. When I finish, nothing is left installed on the VPS and no port is left open.

Every call goes into an audit log next to the executable, without tokens, so I can review afterwards what was done.

A backend deployment

This is what happens now when I replace the portal DLL. I will use the figures from one deployment on 18 September.

Claude builds the DLL on my machine and uploads it to the VPS under its final name, into a staging folder, while the service is still running. The upload checks the SHA-256 of each file as it arrives, so if anything is corrupted on the way I know before touching production.

Then it makes a copy of the DLL currently in production, next to the original, with the date in its name. After that it runs a single script that stops the service, waits for the process to exit, copies the new DLL, registers the COM class, starts the service and checks that the portal answers. If any of those steps fails, the same script restores the copy and starts the service again.

Registering the DLL was the step that caused the most trouble by hand. The backend MTDLL is a 32-bit COM server and has to be registered with the 32-bit regsvr32; a .NET helper, on the other hand, is registered with regasm. If you pick the wrong tool or the wrong bitness, the error is unclear or does not show up at all, because regsvr32 /s stays silent even when it fails. Golem has a tool for this, register_dll, which reads the DLL header to learn whether it is native or .NET and how many bits it has, picks the matching tool, and then searches the registry, in both the 32-bit and the 64-bit views, for the COM classes that point to that path. The answer says which classes ended up registered and whether they already were.

function WaitForExit {
    $clock = [Diagnostics.Stopwatch]::StartNew()
    while (Get-Process FoxServer.Host -ErrorAction SilentlyContinue) {
        if ($clock.ElapsedMilliseconds -gt 30000) { return $false }
        Start-Sleep -Milliseconds 250
    }
    return $true
}

trap { Restore; exit 9 }

Stop-Service FoxServer -Force
if (-not (WaitForExit)) { Restore; exit 2 }
Copy-Item "$stagingFolder\portal.dll" "$projectFolder\portal.dll" -Force
Start-Service FoxServer
if (-not (WaitForRunning)) { Restore; exit 4 }

In that deployment the service was down for 0.7 seconds. Afterwards Claude requested the API version page and a route that requires a session, and compared the answers with the ones it had saved before starting.

The script runs in the background on the VPS. If my connection drops halfway through, the script keeps going and the result can be collected after reconnecting, because a deployment interrupted between stopping and starting leaves the portal down.

A host deployment

Updating FoxServer, the engine that serves the portal and other projects, is more delicate because it means twenty files in the host folder. Claude packs them into a zip along with a list of their SHA-256 hashes, uploads it, unpacks it on the VPS and checks every file against the list before touching the folder in use.

Next it copies the whole host folder to a backup folder, compares the twenty files in both to make sure the backup is complete, and runs the same scheme as before: stop, wait, copy, check hashes, start, read the version of the running process. If the version is not the expected one, it restores the previous version and starts it.

On 16 September, when moving to 0.9.20, the service was down for 1.5 seconds. The first attempt that day rolled itself back, and that is where the WaitForExit loop comes from: Wait-Process does not wait for a process that belongs to a Windows service. It returns immediately without an error, so the script checked whether the process was still alive before it had finished shutting down, treated that as a failure and restored the previous version. With the loop, the process exits within milliseconds and the next attempt went through.

The website

The website is static. I build it on my machine and the resulting folder is uploaded over the published one. That upload is additive: it deletes nothing on the server, because the same folder holds other files that do not come out of the build.

Before uploading, Claude makes a full copy of the published folder in a single call. On 18 September that was 1,178 files and about 380 MB. Then the upload compares hashes and sends only what changed: that day, 62 new or modified files, and 263 that were already identical.

To check the result, Claude requests every changed page from the public site and compares its SHA-256 with that of the file I built. If they match, visitors see what I built, without depending on caches or on someone eyeballing the page.

Production data

Some tasks are not deployments. This week I released a Patreon webhook that writes to a DBF table, and to test it I needed to see how the row looked after each event.

The portal keeps its tables open while it runs, so Claude copies the table files to a separate folder and queries the copy with FoxAgent, which on the VPS is started through Golem itself. To write, for instance to delete a test row, it writes a .prg on the VPS with the right encoding, runs it with Visual FoxPro without a user interface and reads the result file. That .prg opens the table shared, checks that only one row matches the condition and records how many records there were before and after.

Reading the server logs has changed too. Claude reads the last lines of a log several hundred megabytes long without loading all of it, and it does so at once: when Patreon sent its test event, the result was in the log before I got back to the conversation.

On my own machine too

Golem is not only meant for servers. Registering the Patreon webhook required using its web dashboard, and Claude’s browser cannot open patreon.com. I started the agent on my own PC through a tunnel, and Claude could see my browser through screenshots and tell me what to click at each step while I did the clicking.

The limits

There are things Golem does not do, some by design and some because it does not solve them yet.

  • The agent is not a service. I have to start it when I need it and give Claude the connection line. If the session runs long and 30 minutes pass without calls, the agent shuts down and has to be started again with a new token. It is a small nuisance, and I prefer it to having a port open on the VPS all day.
  • To see the screen, the remote desktop session has to stay connected. If I close the window without logging off, Windows stops drawing that desktop and screenshots fail.
  • Claude sees everything a script prints. If a script displayed a password, Claude would see it. The server’s secrets stay in their files and the scripts do not print them.
  • The agent does what it is asked. Deployment safety comes from the prior backup, the hash check and the rollback, and a deployment script fails like any other code. On 18 September, while updating the host to 0.9.25, the first script stopped right after stopping the service: two variables differed only in case, PowerShell does not tell them apart, and one overwrote the other. The rollback lived in the checks and never ran, so the service was down for 39 seconds until a second script finished the job. Since then the scripts use long, distinct names, and the rollback sits in a trap that fires on any error.

What has changed

I am no longer the go-between. Claude works on the VPS with the same tools I would use, and I review what it does and decide when to touch production. Every deployment leaves a copy of what was there, a hash check of what was uploaded and a record of what was run.

Service interruptions are measured in seconds and recorded for every deployment, the 39-second one included. Before, I did not keep that count.

Golem is available in the store. It has no trial license: without a license it runs in read-only mode, and that is how you evaluate it. Claude can look at the server, read logs and files and see the screen, but it cannot change anything. Deploying, stopping services or registering DLLs requires the license.