Cómo era antes

Mi portal de licencias corre en un VPS con Windows: el host de FoxServer, la DLL del backend en Visual FoxPro, las tablas DBF y el sitio web estático. Trabajo con Claude en mi máquina de desarrollo, y durante mucho tiempo el VPS quedaba fuera de su alcance.

El flujo era este. Claude proponía un comando, yo abría el escritorio remoto, lo pegaba en PowerShell, copiaba la salida y se la devolvía. Si la salida era larga, la recortaba. Si había un error, volvía a empezar. Yo hacía de intermediario entre Claude y el servidor, y en cada viaje se perdía algo: una línea que no copié, un comando que pegué en la ventana equivocada, un resultado que resumí mal.

Desplegar era la parte más delicada. Para cambiar la DLL del backend había que parar el servicio, copiar el fichero, registrarlo con regsvr32, volver a arrancar y comprobar en el navegador que todo respondía. Para el sitio web, copiar la carpeta compilada encima de la publicada. Cada paso dependía de que yo no me saltara ninguno, y no quedaba registro de lo que se había hecho ni de cómo estaba el servidor antes.

Qué es Golem

Golem es un agente MCP que le da a Claude acceso temporal a otra máquina Windows. Tiene dos piezas: el agente, un único ejecutable que arranco a mano dentro de la sesión del VPS, y un cliente que tengo instalado una vez en mi máquina y que Claude usa para conectarse.

Al arrancar, el agente imprime una línea con la dirección, un token y la huella de su certificado. Se la paso a Claude y a partir de ahí puede ejecutar PowerShell, leer y escribir ficheros, subir y bajar carpetas, manejar servicios, registrar DLLs y ver la pantalla. El token cambia en cada arranque, la conexión va cifrada y se comprueba contra esa huella, y el agente se apaga solo a los 30 minutos sin peticiones. En el VPS no queda nada instalado ni ningún puerto abierto cuando termino.

Cada llamada queda en un registro de auditoría junto al ejecutable, sin tokens, para poder revisar después qué se hizo.

Un despliegue del backend

Esto es lo que pasa ahora cuando cambio la DLL del portal. Lo cuento con los datos de uno del 18 de septiembre.

Claude compila la DLL en mi máquina y la sube al VPS con el nombre final pero a una carpeta de preparación, con el servicio todavía en marcha. La subida comprueba el SHA-256 de cada fichero al llegar, así que si algo se corrompe por el camino lo sé antes de tocar nada.

Después hace una copia de la DLL que está en producción, junto a la original, con la fecha en el nombre. Y entonces lanza un único script que para el servicio, espera a que el proceso muera, copia la DLL nueva, registra la clase COM, arranca el servicio y comprueba que el portal contesta. Si cualquiera de esos pasos falla, el mismo script restaura la copia y vuelve a arrancar.

El registro de la DLL era el paso que más problemas daba a mano. La MTDLL del backend es un servidor COM de 32 bits y hay que registrarla con el regsvr32 de 32 bits; un helper en .NET, en cambio, se registra con regasm. Si te equivocas de herramienta o de versión, el error es poco claro o no aparece, porque regsvr32 /s no avisa aunque falle. Golem tiene una tool para esto, register_dll, que lee la cabecera de la DLL para saber si es nativa o .NET y de cuántos bits, elige la herramienta que corresponde y después busca en el registro, en las vistas de 32 y 64 bits, las clases COM que apuntan a esa ruta. La respuesta dice qué clases quedaron registradas y si ya lo estaban antes.

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

trap { Restaurar; exit 9 }

Stop-Service FoxServer -Force
if (-not (EsperarMuerte)) { Restaurar; exit 2 }
Copy-Item "$carpetaPreparacion\portal.dll" "$carpetaProyecto\portal.dll" -Force
Start-Service FoxServer
if (-not (EsperarRunning)) { Restaurar; exit 4 }

En ese despliegue el servicio estuvo parado 0,7 segundos. Después Claude pidió la página de versión de la API y una ruta que requiere sesión, y comparó las respuestas con las que había guardado antes de empezar.

El script corre en segundo plano en el VPS. Si mi conexión se cae a la mitad, el script sigue y el resultado se recupera al reconectar, porque un despliegue que se interrumpe entre parar y arrancar deja el portal caído.

Un despliegue del host

Actualizar FoxServer, el motor que sirve el portal y otros proyectos, es más delicado porque son veinte ficheros en la carpeta del host. Claude los empaqueta en un zip junto con una lista de sus SHA-256, lo sube, lo descomprime en el VPS y coteja cada fichero contra la lista antes de tocar la carpeta en uso.

Luego copia la carpeta entera del host a una de respaldo, compara los veinte ficheros de las dos para asegurarse de que el respaldo está completo, y ejecuta el mismo esquema de antes: parar, esperar, copiar, comprobar hashes, arrancar, leer la versión del proceso vivo. Si la versión no es la esperada, restaura y arranca la anterior.

El 16 de septiembre, al pasar a la 0.9.20, el servicio estuvo parado 1,5 segundos. El primer intento de ese día se deshizo solo, y ahí apareció el motivo del bucle de EsperarMuerte: Wait-Process no espera a un proceso que pertenece a un servicio de Windows. Devuelve al instante sin error, así que el script comprobaba si el proceso seguía vivo cuando todavía no había terminado de cerrarse, lo daba por fallido y restauraba la versión anterior. Con el bucle, el proceso muere en milisegundos y el siguiente intento salió bien.

El sitio web

La web es estática. La construyo en mi máquina y la carpeta resultante se sube encima de la publicada. Esa subida es aditiva: no borra nada del servidor, porque en la misma carpeta viven otros ficheros que no salen del build.

Antes de subir, Claude hace una copia completa de la carpeta publicada con una sola llamada. El 18 de septiembre fueron 1.178 ficheros y unos 380 MB. Después, la subida compara hashes y solo manda lo que ha cambiado: ese día, 62 ficheros nuevos o modificados, y 263 que ya estaban igual.

Para comprobar el resultado, Claude pide cada página cambiada al sitio público y compara su SHA-256 con el del fichero que construí. Si coinciden, lo que ven los visitantes es lo que yo construí, sin depender de cachés ni de que alguien mire la página a ojo.

Datos en producción

Algunas tareas no son despliegues. Esta semana publiqué un webhook de Patreon que escribe en una tabla DBF, y para probarlo necesitaba ver cómo quedaba la fila después de cada evento.

El portal tiene las tablas abiertas mientras está en marcha, así que Claude copia los ficheros de la tabla a una carpeta aparte y los consulta con FoxAgent, que en el VPS arranca a través del propio Golem. Para escribir, por ejemplo para borrar una fila de prueba, escribe un .prg en el VPS con la codificación correcta, lo ejecuta con Visual FoxPro en modo sin interfaz y lee el fichero de resultado. Ese .prg abre la tabla compartida, comprueba que solo hay una fila que cumpla la condición y deja constancia de cuántos registros había antes y después.

Leer los logs del servidor también ha cambiado. Claude lee las últimas líneas de un log de cientos de megas sin cargarlo entero, y lo hace en el momento: cuando Patreon envió su evento de prueba, el resultado estaba en el log antes de que yo volviera a la conversación.

También en mi propia máquina

Golem no está pensado solo para servidores. Para registrar el webhook de Patreon había que usar su panel web, y el navegador de Claude no puede abrir patreon.com. Arranqué el agente en mi propio PC con un túnel, y Claude pudo ver mi navegador con capturas de pantalla y decirme qué pulsar en cada paso mientras yo hacía los clics.

Los límites

Hay cosas que Golem no hace, algunas por decisión y otras porque todavía no las resuelve.

  • El agente no es un servicio. Tengo que arrancarlo cuando lo necesito y pasarle a Claude la línea de conexión. Si la sesión se alarga y pasan 30 minutos sin llamadas, el agente se apaga y hay que volver a arrancarlo con un token nuevo. Es una molestia pequeña, y la prefiero a tener un puerto abierto en el VPS todo el día.
  • Para ver la pantalla, la sesión de escritorio remoto tiene que estar conectada. Si cierro la ventana sin cerrar sesión, Windows deja de dibujar ese escritorio y las capturas fallan.
  • Claude ve todo lo que un script imprime. Si un script mostrara una contraseña, la vería. Los secretos del servidor se quedan en sus ficheros y los scripts no los imprimen.
  • El agente hace lo que se le pide. La seguridad del despliegue la dan el respaldo previo, la comprobación por hash y la vuelta atrás, y un script de despliegue falla como cualquier otro código. El 18 de septiembre, al actualizar el host a la 0.9.25, el primer script se detuvo después de parar el servicio: dos variables solo se diferenciaban en mayúsculas, PowerShell no las distingue, y una pisó a la otra. La vuelta atrás estaba en las comprobaciones y no llegó a ejecutarse, así que el servicio estuvo parado 39 segundos hasta que un segundo script terminó el trabajo. Desde entonces los scripts usan nombres largos y distintos, y la vuelta atrás va en un trap que salta ante cualquier error.

Lo que ha cambiado

Ya no hago de intermediario. Claude trabaja en el VPS con las mismas herramientas que usaría yo, y yo reviso lo que hace y decido cuándo tocar producción. Cada despliegue deja una copia de lo que había, una comprobación por hash de lo que se subió y un registro de lo que se ejecutó.

Los cortes del servicio se miden en segundos y quedan apuntados en cada despliegue, incluido el de 39 segundos. Antes no llevaba esa cuenta.

Golem está en la tienda. No tiene licencia de prueba: sin licencia funciona en modo de solo lectura, y así es como se evalúa. Claude puede mirar el servidor, leer logs y ficheros y ver la pantalla, pero no cambiar nada. Para desplegar, parar servicios o registrar DLLs hace falta la licencia.