clawops

Implementa y opera OpenClaw autoalojado en AWS, GCP, Azure y máquinas virtuales locales: una CLI y un servidor MCP en uno. Los cambios de infraestructura pasan por un plan de despliegue que revisas antes de aplicar, las herramientas destructivas confirman primero, y cada llamada se registra en un registro de auditoría.

Documentación

clawops

npm version npm downloads clawops MCP server – quality and maintenance score on Glama

Operaciones de infraestructura nativas de MCP para OpenClaw, con modo de solo lectura, confirmación de acciones destructivas y registros de auditoría integrados.

clawops es una CLI y un servidor MCP para implementar y operar instancias de OpenClaw autoalojadas. Aprovisiona en AWS, GCP, Azure o cualquier VM Linux, y luego gestiona las operaciones diarias desde la terminal, o permite que Claude Code y Cursor las controlen mediante herramientas MCP tipadas con controles de seguridad explícitos.


Novedades: notas de la versión 2.0.1 y 2.0 a continuación, y el historial completo en CHANGELOG.md.


Para quién es esto

  • Usuarios de OpenClaw que quieren la ruta más simple para autoalojarse en la nube o en VMs locales, con implementación confiable, verificaciones de estado, registros, copias de seguridad y actualizaciones en una sola CLI.
  • Usuarios de Claude Code / Cursor / MCP que buscan una implementación de referencia del mundo real de operaciones de infraestructura seguras a través de MCP. Esquemas de herramientas tipados, modo de solo lectura, confirmación de acciones destructivas y registros de auditoría.
  • Desarrolladores de IA autoalojada y local-first que quieren ejecutar su propio asistente de IA sin comprometerse con Kubernetes, una plataforma SaaS gestionada o un único proveedor de nube.

Qué hace clawops

  • Aprovisiona y elimina infraestructura de OpenClaw en AWS, GCP, Azure y VMs locales usando la API de Automatización de Pulumi. No necesitas instalar Pulumi; clawops instala la CLI que necesita en ~/.clawops/.pulumi-cli en el primer uso.
  • Gestiona las operaciones diarias: estado, registros, SSH, túneles, configuración, agentes, gateway, copias de seguridad.
  • Expone cada operación como una herramienta MCP tipada para que los agentes de IA puedan operar de forma segura.
  • Aplica una disciplina de plan → revisión → aplicación para implementaciones en la nube.
  • Emite salida JSON en todas partes (--json) para scripting y automatización.
  • Nunca almacena credenciales de nube. Las lee de los perfiles CLI existentes en tu entorno.

Qué no hace clawops

  • Sin alta disponibilidad ni clustering. Optimizado para implementaciones de un solo nodo.
  • Sin Kubernetes. Implementa en VMs, no en plataformas de orquestación de contenedores.
  • Sin creación de habilidades/agentes de OpenClaw. clawops gestiona la infraestructura; lo que se ejecuta en ella depende de ti y de OpenClaw.
  • Sin automatización de TLS o dominios (por ahora). Trae tu propio proxy inverso o consulta docs/limitations.md para la ruta manual.
  • Sin almacenamiento de credenciales. Las credenciales de nube deben configurarse en tu entorno antes de usar clawops. Nunca se escriben en ~/.clawops/config.json.
  • Sin Windows nativo. WSL2 es totalmente compatible; consulta docs/support-matrix.md.

Inicio rápido

npm install -g @clawops/cli
clawops setup

clawops setup es un asistente interactivo que pone OpenClaw en funcionamiento en unos 2 minutos. Maneja todo en un solo flujo, sin archivos de configuración que escribir a mano, sin comandos que memorizar.

Qué hace el asistente

Paso 1. Elige un objetivo de implementación

Elige un servidor existente al que puedas acceder por SSH (Linux o macOS), o una nueva VM en la nube en AWS, GCP o Azure. Las implementaciones en la nube te guían para autenticarte con la CLI del proveedor si aún no has iniciado sesión.

Paso 2. Elige un proveedor de LLM

Elige entre Anthropic, OpenAI, Amazon Bedrock, Ollama u otros. El asistente solicita tu clave de API y la guarda localmente (en ~/.clawops/secrets/, chmod 600); nunca se envía a ningún lugar excepto a OpenClaw en el host de destino cuando se aplica la configuración.

Paso 3. Añade integraciones de chat (opcional)

Selecciona cualquier combinación de Discord, Telegram, Slack, WhatsApp o Teams. El asistente recopila el token de bot de cada integración de la misma manera que la clave de API. Pégalo, referencia una variable de entorno o apunta a un archivo.

Paso 4. Conecta tu editor de IA

Selecciona qué aplicaciones de IA deben tener acceso a clawops. Claude Desktop, Claude Code, Cursor, Windsurf, VS Code y Zed son compatibles. El asistente escribe una entrada de servidor MCP en el archivo de configuración de cada aplicación usando la ruta binaria absoluta para que la aplicación pueda iniciarlo de forma independiente.

Paso 5. Implementa

El asistente arranca OpenClaw en el host de destino a través de SSH (instala Docker, extrae la imagen, inicia el contenedor), aplica tu configuración de LLM e integraciones, genera un token de autenticación de gateway e imprime una URL directa del panel:

✔ All done! OpenClaw is running.
ℹ Open dashboard: http://192.168.1.50:18789?token=<your-token>
ℹ Token saved to ~/.clawops/secrets/GATEWAY_TOKEN_my-stack

Requisitos previos: Node.js ≥ 22, una clave SSH y un host Linux/macOS accesible por SSH o una cuenta en la nube con credenciales CLI configuradas (aws configure, gcloud auth login o az login). No necesitas Pulumi. La primera implementación en la nube instala la CLI que clawops utiliza en ~/.clawops/.pulumi-cli y lo indica mientras lo hace.

Para un recorrido completo narrado con salida de ejemplo, consulta docs/demo-script.md.


Configuración manual, servidor existente

Si prefieres un control paso a paso, o estás añadiendo clawops a una implementación ya en ejecución:

npm install -g @clawops/cli

clawops doctor   # verify environment

clawops init --provider local --host 192.168.1.50 --user ubuntu --key-path ~/.ssh/id_ed25519
clawops up       # installs Docker + OpenClaw over SSH
clawops status

Consulta docs/examples/local-vm.md para requisitos previos de SSH, configuración del firewall y solución de problemas.

Configuración manual, nube (AWS)

npm install -g @clawops/cli

# Requires AWS credentials in your environment (AWS_PROFILE or ~/.aws/credentials)
clawops init --provider aws

# Edit ~/.clawops/config.json: set stateUrl to your S3 bucket

clawops plan --provider aws --stack default --ssh-cidr auto --out /tmp/plan.json
clawops apply /tmp/plan.json

--ssh-cidr auto permite SSH desde la IP pública de esta máquina, resuelta mientras se genera el plan y escrita en él. Sin él, el plan no permite ninguna entrada y nada, incluido clawops, podrá conectarse.


Conecta un editor de IA

El asistente setup maneja esto automáticamente (Paso 4). Para conectar o reconectar editores en cualquier momento:

clawops mcp install

Esto abre la misma casilla de verificación interactiva utilizada en el asistente; selecciona Claude Desktop, Claude Code, Cursor, Windsurf, VS Code o Zed y clawops escribe la entrada MCP en la configuración de cada aplicación usando la ruta binaria absoluta correcta.

Para añadir la entrada manualmente, pega esto en la configuración MCP de tu editor:

{
  "mcpServers": {
    "clawops": {
      "command": "npx",
      "args": ["-y", "@clawops/cli", "mcp", "serve", "--read-only"]
    }
  }
}

Esa forma no necesita nada en $PATH y es lo que un directorio o un instalador copiará. Si prefieres apuntar al binario que ya tienes, usa su ruta absoluta — la salida de which clawops — con los mismos argumentos:

{
  "mcpServers": {
    "clawops": {
      "command": "/path/to/clawops",
      "args": ["mcp", "serve", "--read-only"]
    }
  }
}

De cualquier manera, pasa los argumentos. mcp serve es lo que habla el protocolo, y una configuración explícita es una que aún se lee claramente un año después. clawops no abandona a un cliente que los omita: ejecuta sin ningún comando y con una tubería en stdin — así es como cada cliente MCP inicia un servidor — y inicia mcp serve, indicándolo en stderr. Escrito en una terminal, clawops aún imprime ayuda. Ubicaciones de archivos de configuración:

AplicaciónRuta
Claude Desktop (macOS)~/Library/Application Support/Claude/claude_desktop_config.json
Claude Desktop (Linux)~/.config/Claude/claude_desktop_config.json
Claude Code~/.claude.json
Cursor~/.cursor/mcp.json
Windsurf~/.codeium/windsurf/mcp_config.json
VS Code (macOS)~/Library/Application Support/Code/User/mcp.json
VS Code (Linux)~/.config/Code/User/mcp.json
Zed~/.config/zed/settings.json (clave: context_servers)

Comienza con --read-only. Habilita estado, registros, lecturas de configuración y diagnósticos mientras bloquea mutaciones. Elimínalo solo después de revisar docs/security/mcp-safety.md.

Las herramientas destructivas (clawops_destroy, clawops_up, clawops_config_set, etc.) requieren confirmación explícita antes de ejecutarse; nunca se ejecutarán en silencio.

Para la configuración del modo HTTP, consulta docs/mcp/.


Operaciones diarias

clawops status              # Stack outputs: IP, gateway URL, SSH info
clawops logs -f             # Tail OpenClaw logs over SSH
clawops ssh                 # Interactive SSH session
clawops ssh --command "docker ps"

clawops config get maxAgents
clawops config set maxAgents 8

clawops tunnel              # Port-forward gateway UI to localhost

clawops destroy --yes       # Destroy cloud-provider stack
clawops down --yes          # Destroy local-provider stack

Comandos

ComandoDescripción
setupAsistente de primera ejecución: LLM guiado, integraciones y generación de plan de implementación
initRegistra un stack en ~/.clawops/config.json sin aprovisionar. Aditivo; los stacks existentes se conservan; --force solo se necesita para sobrescribir uno
upAprovisiona o actualiza el stack (--dry-run para vista previa, --gateway-port para un puerto no predeterminado)
downDestruye el stack del proveedor local (requiere --yes; --dry-run muestra las salidas actuales)
destroyDestruye el stack del proveedor de nube con mensaje de confirmación (--dry-run muestra las salidas actuales)
statusMuestra las salidas del stack: IP, URL del gateway, región, tiempo de aprovisionamiento
planGenera un artefacto JSON de plan de implementación (seguro en seco). --ssh-cidr <list|auto> y --gateway-cidr deciden quién puede conectarse; --publish-gateway loopback|all decide qué está escuchando; --private-only cierra el acceso público en un stack alcanzado a través de su tailnet
applyAplica un archivo de plan previamente revisado (--dry-run valida y muestra el diff sin aplicar)
sshSesión SSH interactiva o ejecuta un comando remoto
logsTransmite registros de OpenClaw (-f, --tail N, --since 5m)
tunnelReenvío de puerto local a la interfaz del gateway a través de SSH
configObtiene/establece valores de configuración remotos de OpenClaw (--dry-run muestra el JSON que se escribiría)
agentsLista los agentes de OpenClaw o transmite los registros de un agente
gatewayReinicia el servicio del gateway de OpenClaw
backupCrea y restaura copias de seguridad del estado de OpenClaw (restore se expande en un directorio de preparación, nunca en el lugar)
stacksLista los stacks nombrados y su estado
doctorVerifica la máquina local; con --stack, también la salud de la implementación; con --provider, las credenciales y la configuración de la cuenta de una nube exista o no un stack; con --instance-type, las verificaciones de cuenta preguntan sobre ese tamaño en lugar del predeterminado del proveedor. --json para el informe. Sale con 1 en cualquier fallo
secretGestiona secretos: list, set, delete, rotate, audit
monitorPanel en vivo: salud del gateway, estadísticas de contenedores, cola de registros, selector de stacks
mcp serveInicia el servidor MCP integrado (stdio, o HTTP con --http <port> --token <t>)
mcp installConecta interactivamente clawops a editores de IA
mcp wireConecta la IA del gateway como cliente MCP de clawops (verifica la conexión antes de guardar)
helpLista todos los comandos y banderas globales
hardenAplica endurecimiento de seguridad a un stack implementado (SSH, UFW, fail2ban, unattended-upgrades, socket de Docker; AWS: auditoría de SG, verificación de SSM, Flow Logs, GuardDuty). --tailscale une el stack a tu tailnet y mueve clawops a esa dirección una vez que responde; --tailscale-revert lo deshace
bugAbre un problema de GitHub prellenado con contexto del sistema desde doctor

Referencia completa de banderas: clawops <command> --help


Flujo de trabajo Plan → Aplicar

Para proveedores no locales, clawops aplica una disciplina de revisión antes de aplicar:

# 1. Generate a plan: runs `pulumi preview` internally, produces JSON
#    --ssh-cidr decides who may connect. `auto` means this machine; omit it and nobody can.
clawops plan --provider aws --region us-east-1 --ssh-cidr auto --out /tmp/plan.json

# 2. Review plan.json: the `diff` field shows projected changes at plan-generation time
cat /tmp/plan.json | jq .diff

# 3. Apply: reads and validates the plan file, then runs `pulumi up`
clawops apply /tmp/plan.json

# Without --yes, apply prompts: "Continue? (y/N)"
clawops apply /tmp/plan.json --yes    # skip prompt in automation

El JSON del plan se ajusta a spec/deploy-plan.schema.json (validado por AJV) y captura la intención revisada: proveedor, región, tipo de instancia, rangos CIDR y versión de OpenClaw. apply vuelve a ejecutar pulumi up usando esos parámetros contra el estado vivo actual; no reproduce un artefacto de ejecución bloqueado. Revisa y aplica en la misma sesión para minimizar el riesgo de desviación.

Consulta docs/plan-apply.md para la semántica completa, la guía de desviación y el patrón CI seguro.


Servidor MCP

clawops incluye un servidor MCP integrado. Claude Code, Cursor y cualquier agente compatible con MCP pueden controlar implementaciones sin salir de la interfaz de chat.

Conecta tu editor

clawops mcp install   # interactive checkbox: writes config for selected apps

El asistente resuelve la ruta binaria absoluta automáticamente para que los lanzadores de aplicaciones puedan encontrar clawops sin heredar el PATH de tu shell. Consulta Conecta un editor de IA arriba para rutas de configuración manuales.

Conecta la IA del gateway

El gateway de OpenClaw ejecuta su propio agente de IA. Una vez conectado, ese agente puede llamar a clawops directamente en lugar de adivinar el estado de la infraestructura:

clawops mcp wire --stack prod   # write MCP client entry into gateway config + restart

Requiere OpenClaw ≥ 2026.4 en el gateway. El asistente clawops setup ofrece este paso automáticamente después de una implementación exitosa.

Modo Stdio (Claude Code / Cursor / VS Code)

Inicia el servidor manualmente o confirma que tu configuración es correcta:

clawops mcp serve --read-only   # safe for first evaluation
clawops mcp serve               # full mode: enables provisioning, config write, ssh exec

Modo HTTP (remoto / multi-cliente)

clawops mcp serve --http 3333 --bind 127.0.0.1
# MCP HTTP server listening on 127.0.0.1:3333

No te vincules a una dirección que no sea de loopback sin controles de autenticación adicionales delante de ella.

Herramientas disponibles

HerramientaConjunto de herramientasDescripción
clawops_statuscliMostrar salidas del stack (qué está desplegado, no si funciona)
clawops_doctorcliEjecutar diagnósticos: requisitos previos locales y, con un stack, salud remota
clawops_logs_tailcliSeguir los registros de OpenClaw
clawops_monitorcliMuestrear métricas de gateway y host
clawops_stacks_listadminListar todos los stacks y su estado
clawops_config_getcliLeer un valor de configuración remoto
clawops_agents_listcliListar agentes en ejecución
clawops_upcliAprovisionar o actualizar un stack
clawops_destroycliDestruir un stack (solicita confirmación)
clawops_applycliAplicar un archivo de plan
clawops_plancliGenerar un plan de despliegue
clawops_config_setcliEscribir un valor de configuración remoto
clawops_config_unsetcliEliminar una clave de configuración remota
clawops_config_validatecliValidar la configuración desplegada contra el esquema de OpenClaw
clawops_gateway_restartcliReiniciar el gateway (solicita confirmación)
clawops_hardencliAplicar módulos de endurecimiento; unirse o abandonar una tailnet (solicita confirmación)
clawops_initcliRegistrar un stack y escribir ~/.clawops/config.json (sin recursos en la nube)
clawops_workflow_deploy_appworkflowDespliegue de extremo a extremo: plan → confirmar → aplicar → estado
clawops_workflow_recoverworkflowFlujo de trabajo de diagnóstico para un stack no saludable
clawops_task_statuscliConsultar una tarea de larga duración

Las herramientas del conjunto de herramientas read también están disponibles en modo --read-only; la columna Conjunto de herramientas de la tabla muestra el conjunto principal. Todos los demás conjuntos de herramientas requieren modo completo. Las herramientas destructivas requieren confirmación explícita (solicitación) a menos que se pase yes: true.

Consulta docs/security/tool-risk-matrix.md para la clasificación de riesgo completa de cada herramienta.


Configuración

La configuración se encuentra en ~/.clawops/config.json (anular con $CLAWOPS_HOME).

{
  "version": 1,
  "defaults": {
    "provider": "aws",
    "stack": "default"
  },
  "stacks": {
    "default": {
      "provider": "aws",
      "region": "us-east-1",
      "stateUrl": "s3://my-clawops-state"
    }
  },
  "ssh": {
    "keyPath": "~/.clawops/id_ed25519",
    "knownHostsPath": "~/.clawops/known_hosts"
  }
}

Las credenciales de la nube nunca se almacenan en la configuración. Clawops las lee del entorno:

ProveedorFuente de credenciales
AWSAWS_PROFILE o cadena de credenciales estándar de AWS (~/.aws/credentials)
GCPGOOGLE_APPLICATION_CREDENTIALS o gcloud auth application-default login
AzureAZURE_CLIENT_ID / AZURE_CLIENT_SECRET o az login
LocalHost SSH + clave configurados en stacks[name].localOpts

Limitaciones conocidas

Consulta docs/limitations.md para la lista completa. Puntos clave:

  • Solo despliegues de un solo nodo, no una plataforma de alta disponibilidad o agrupamiento.
  • clawops apply no es una ejecución de plan inmutable. Consulta docs/plan-apply.md.
  • Sin automatización de TLS/dominio en la versión actual.
  • Las herramientas MCP ejecutan operaciones privilegiadas, usa --read-only para la primera evaluación.

Arquitectura

clawops
├── src/cli/          citty-based commands (one file per verb)
├── src/config/       ~/.clawops/config.json management
├── src/providers/    Cloud adapters (AWS, GCP, Azure, local)
│   ├── aws/          Pulumi inline program + ProviderAdapter
│   ├── gcp/
│   ├── azure/
│   └── local/        SSH bootstrap (no Pulumi)
├── src/pulumi/       Pulumi Automation API wrapper + output helpers
├── src/transport/    SSH client (ssh2) + connection pool + tunnels
├── src/mcp/          MCP server, tool handlers, progress tracking
├── src/plan/         Maker plan generation, AJV validation, apply
├── src/output/       ASCII table, spinner, JSON, human-readable output
├── src/errors/       Typed error hierarchy with exit codes
└── spec/             Machine-readable ground truth (JSON Schema, YAML)

Decisiones de diseño clave:

  • API de automatización de Pulumi: el usuario no instala Pulumi. Clawops instala la CLI que la API controla en ~/.clawops/.pulumi-cli, fijada al SDK incluido, sin editar $PATH (ADR 0010); el directorio de inicio de Pulumi está aislado en ~/.clawops/.pulumi; los programas de stack son cierres TypeScript en línea
  • Estado en almacenamiento de blobs en la nube: GCS (gs://), S3 (s3://), Azure Blob, sin archivos de estado locales, sin pulumi.yaml
  • SSH a través de ssh2: nunca invoca /usr/bin/ssh; verificación de host TOFU contra ~/.clawops/known_hosts; grupo de conexiones con TTL de inactividad de 5 minutos
  • Disciplina de plan → aplicar: cada despliegue no local pasa por generatePlan() → revisión → applyPlan(); los cambios destructivos siempre requieren revisión humana del JSON del plan
  • MCP primero: cada operación de CLI tiene una herramienta MCP tipada; los esquemas se generan desde spec/mcp-tools.yaml; todas las herramientas destructivas usan solicitación

Consulta docs/architecture.md para una narrativa completa y docs/decisions/ para los ADR.

Stacks de proveedores de nube

Cada proveedor de nube es un programa Pulumi en línea que crea los recursos siguientes. Los tres comparten las mismas salidas (publicIp, gatewayUrl, sshHost, sshPort, sshUser) consumidas por las capas de SSH y superposición de configuración.

AWS

flowchart LR
    subgraph NET["Networking"]
        VPC["VPC (10.0.0.0/16)"]
        IGW[Internet Gateway]
        SUBNET["Subnet (10.0.1.0/24)"]
        RT[Route Table]
        SG["Security Group (ports 22, 18789)"]
    end
    subgraph IAM["IAM"]
        ROLE[IAM Role]
        SSM[SSM Policy Attachment]
        BED["Bedrock Policy Attachment (optional)"]
        IP[Instance Profile]
    end
    subgraph COMPUTE["Compute"]
        KP[EC2 Key Pair]
        EC2["EC2 Instance (Ubuntu 22.04, IMDSv2)"]
        EIP[Elastic IP]
    end

Diagrama detallado →

GCP

flowchart LR
    subgraph NET["Networking"]
        NW[VPC Network]
        SN["Subnetwork (10.0.0.0/24)"]
        FW1["Firewall: SSH port 22 (conditional)"]
        FW2["Firewall: Gateway port 18789 (conditional)"]
        ADDR[Static External IP]
    end
    subgraph COMPUTE["Compute"]
        VM["Compute Instance (Debian 12, 20 GB)"]
    end

Diagrama detallado →

Azure

flowchart LR
    RG[Resource Group]
    subgraph NET["Networking"]
        VNET["Virtual Network (10.0.0.0/16)"]
        SUBNET["Subnet (10.0.1.0/24)"]
        NSG["Network Security Group (ports 22, 18789)"]
        PIP["Public IP Address (Static)"]
        NIC[Network Interface]
    end
    subgraph COMPUTE["Compute"]
        VM["VM (Ubuntu 22.04, managed identity)"]
    end
    subgraph KV["Key Vault (optional)"]
        VAULT["Key Vault (RBAC, name max 24 chars)"]
        RA["Role Assignment (Secrets User)"]
        SECRET["Secret: gateway-token"]
    end

Diagrama detallado →


Desarrollo

Configuración

git clone https://github.com/dfridkin/clawops.git
cd clawops
# Node 22+ required; use nvm: nvm use
pnpm install
pnpm dev doctor        # verify toolchain

Scripts

pnpm dev                   # run CLI from src/ via tsx
pnpm build                 # tsup → dist/
pnpm test                  # vitest (1897 tests, ~13s)
pnpm test:changed          # vitest --changed (fast edit loop)
pnpm test:integration      # Docker-based SSH integration tests
pnpm typecheck             # tsc --noEmit
pnpm lint                  # eslint src/ tests/ scripts/ (--max-warnings=0)
pnpm gen:schemas           # regenerate src/providers/types.ts + src/mcp/tools/_generated.ts
pnpm gen:schemas --check   # CI guard: committed generated files match spec
pnpm graph                 # local coupling report (--base <ref> for this branch's delta)
pnpm verify:pack           # install the packed tarball elsewhere and run it (CI gate)
pnpm sync:server-json      # write package.json's version into server.json
pnpm changeset             # record a release note before merging

Estructura del proyecto

RutaPropósito
spec/Fuente de verdad legible por máquina: JSON Schema, YAML. Trátalo como fuente de verdad.
SPEC.mdEspecificación técnica completa (hitos, reglas, esquemas)
DESIGN_RULES.md25 reglas normativas (R1–R25) referenciadas en todo el código
docs/architecture.mdResumen narrativo del sistema
docs/plan-apply.mdSemántica de plan/aplicar, guía de deriva, patrón de CI
docs/ci.mdGuía de integración de CI: OIDC, variables de entorno, plan → aplicar en CI
docs/security/Modelo de seguridad de MCP, matriz de riesgo de herramientas, redacción, registros de auditoría
docs/providers/matrix.mdMatriz de capacidades por proveedor
docs/decisions/Registros de decisiones de arquitectura
.claude/skills/Procedimientos invocables: /add-provider, /release, /tdd, /mcp-tool
.claude/rules/Reglas de lint por ruta cargadas por Claude Code

Generación de código

Dos archivos se generan desde spec/ y no deben editarse a mano:

  • src/providers/types.ts. Interfaz ProviderAdapter desde spec/providers.schema.json
  • src/mcp/tools/_generated.ts. Esquemas Zod y exportaciones de tipos desde spec/mcp-tools.yaml

Ejecuta pnpm gen:schemas después de modificar cualquiera de los archivos de especificación. CI lo aplica con --check.

Añadir un proveedor

Usa la habilidad /add-provider en Claude Code, o sigue src/providers/CLAUDE.md. Cada adaptador debe cumplir ProviderAdapter en src/providers/types.ts. No relajes el esquema para ajustar el adaptador.

Añadir una herramienta MCP

Usa la habilidad /mcp-tool. La habilidad añade la herramienta a spec/mcp-tools.yaml, ejecuta pnpm gen:schemas, crea el manejador en src/mcp/tools/<toolset>/<name>.ts y lo conecta al registro. Las cuatro pistas de anotación (readOnlyHint, destructiveHint, idempotentHint, openWorldHint) son obligatorias en cada herramienta.

Commits convencionales

feat(scope): description
fix(scope): description
docs / refactor / chore / test / perf / ci

Usa pnpm changeset para registrar una nota de versión antes de fusionar un feat o fix.


Novedades en 2.1

Redes privadas, endurecimiento en todas las nubes y dos correcciones a comandos que no podían iniciar. 2.1.1 sigue con las correcciones que se indican a continuación.

Alcanza un stack a través de tu tailnet

  • clawops harden --tailscale instala Tailscale en un stack, lo une a tu tailnet como clawops-<stack> e informa la dirección que se le asignó.
  • El mismo comando mueve luego clawops a esa dirección, pero solo después de abrir una sesión SSH hacia ella, contra claves de host fijadas sobre la conexión pública que ya confía.
  • La clave de autenticación de Tailscale proviene de clawops secret set TAILSCALE_AUTH_KEY y llega al host a través del canal de datos SSH. Nunca aparece en una línea de comandos, una lista de procesos o un registro.
  • clawops plan --private-only → clawops apply cierra el acceso público SSH y de gateway en un stack alcanzado a través de su tailnet. Ambos se niegan a menos que esa dirección responda SSH en ese momento (ADR 0013).
  • clawops harden --tailscale-revert saca un host de la tailnet y devuelve clawops a su dirección pública. En un stack solo privado se niega e imprime los comandos que reabren SSH.
  • clawops destroy olvida las claves de host para ambas direcciones de un stack en su tailnet, en lugar de dejar la pública fijada para una instancia que ya no existe.

El endurecimiento cubre las tres nubes

  • Azure: auditoría de NSG, cifrado de disco, Defender for Cloud y acceso JIT a VM, todo solo verificación.
  • GCP: auditoría de firewall de VPC, Shielded VM y OS Login, todo solo verificación.
  • Las instancias de GCP arrancan con Secure Boot activado. Los stacks existentes lo reciben como una actualización que conserva el disco de arranque y todo el estado de OpenClaw.
  • Una verificación que no puede ejecutarse se informa como omitida, nombrando lo que faltaba, en lugar de como aprobada.

Los planes dicen qué perturbarán

  • clawops plan cuenta y lista los recursos que se reemplazarían. Solía resumir una vista previa que destruiría la instancia y su disco de arranque como "0 para crear, 0 para actualizar".
  • Un plan que cambia un despliegue en vivo advierte antes de aplicarlo: un reemplazo nombra qué va con él y apunta a clawops backup create; una actualización dice que el gateway se cae.

Correcciones

  • clawops mcp serve no podía iniciar en absoluto cuando se instalaba desde npm: fallaba en la importación antes de emitir ningún protocolo, por lo que cada cliente MCP no recibía nada. pnpm verify:pack ahora habla MCP con el tarball empaquetado, por lo que esta clase de fallo no puede volver a publicarse.
  • server.json, el manifiesto del registro MCP, se versiona con el paquete en lugar de reescribirse en el momento de la publicación. El archivo confirmado había leído 1.7.3 contra un 2.0.2 publicado.
  • El paquete publicado lleva su licencia, palabras clave y rastreador de problemas, por lo que se puede encontrar en npm y su listado está completo.

2.1.1

  • clawops iniciado sin comando y con una tubería en stdin sirve MCP en lugar de imprimir ayuda. Así es como cada cliente MCP inicia un servidor, y cómo los directorios que infieren un comando de ejecución inician uno; varios estaban recibiendo el texto de ayuda e informando el servidor como roto. Escrito en una terminal, clawops aún imprime ayuda, y también lo hace clawops | less.
  • El ejemplo de configuración de MCP es npx -y @clawops/cli mcp serve, que se ejecuta tal como está escrito. Solía decir /path/to/clawops, que nada podía ejecutar y ningún directorio podía copiar.
  • La descripción está dentro del límite de 100 caracteres del registro MCP. La que envió 2.1.0 era 110, y el registro la rechazó con un 422 después de que npm ya había publicado, por lo que 2.1.0 llegó a npm y no al registro.
  • clawops harden dice que un stack no está desplegado, o que su estado no pudo leerse, en lugar de transmitir code: -2 y un volcado de subproceso.
  • El relleno del registro MCP registra la versión que se publicó en lugar de la que la herramienta de publicación está preparando, por lo que una entrada que se queda atrás puede repararse realmente.
  • La imagen de Docker se compila. Nunca se había compilado, y no lo hacía: npm pack --pack-destination no crea su destino. pnpm verify:docker la compila y habla MCP con el contenedor en ejecución, tanto a través de su punto de entrada como como binario simple, en CI.

Novedades en 2.0.1

Una versión de parche, y una grande: en 2.0.0 ningún despliegue en la nube tuvo éxito por ninguna vía. Cada elemento a continuación es una corrección o una adición en 2.0.1. El razonamiento detrás de cada uno está en su mensaje de commit, y las decisiones que surgieron de ellos están en docs/decisions/.

Desplegar en una nube

  • clawops plan → clawops apply aprovisiona un stack en la nube y despliega OpenClaw en él.
  • clawops up despliega en AWS, GCP y Azure, ejecutando la misma ruta que plan → apply.
  • clawops instala la CLI de Pulumi que necesita en ~/.clawops/.pulumi-cli, o usa una compatible ya presente en $PATH (ADR 0010).
  • clawops crea y almacena la frase de contraseña que su backend de estado requiere (ADR 0011).
  • clawops plan toma --ssh-cidr, --gateway-cidr y --publish-gateway, y apply los pasa al firewall de la nube. auto resuelve la dirección de esta máquina.
  • clawops plan se detiene, y nombra la causa, cuando no puede abrir el backend de estado.
  • --instance-type toma un alias de clawops (micro–gpu) o un tipo de máquina que tu nube nombra por sí misma, y el plan registra el tipo concreto.
  • Los despliegues fijan la cuenta contra la que se planificaron: gcp:project en GCP, azure-native:subscriptionId en Azure.

Verificar la cuenta antes de gastar

  • clawops doctor --provider <cloud> verifica las credenciales y la configuración de la cuenta de una nube, con o sin una pila. --instance-type apunta la verificación de tamaño al tamaño que estás desplegando.
  • AWS. La cuenta a la que resuelven las credenciales, el bucket de estado y si el tipo de instancia está disponible en la región.
  • GCP. El proyecto, las API que necesita un despliegue y el bucket de estado.
  • Azure. La suscripción, los proveedores de recursos, el tamaño de la VM y las credenciales de azblob con las que Pulumi se autentica.
  • clawops setup ejecuta las mismas verificaciones y ofrece corregir lo que puede de forma segura, habilitando una API, creando un bucket de estado con versionado activado y acceso público bloqueado, nombrando el cambio antes de realizarlo.
  • Una verificación que clawops no pudo realizar se reporta como una advertencia que nombra el error, en lugar de como un aprobado o un fallo.
  • Azure acepta tu az login; ya no se requiere una entidad de servicio.

Nomenclatura, configuración y preparación

  • clawops nombra el backend de estado según la cuenta en la que está desplegando, en lugar de pedirte un nombre o escribir un marcador de posición (ADR 0012).
  • Un nombre que escribas se verifica contra las reglas de la nube que debe aceptarlo.
  • clawops init conserva las pilas que ya están en tu configuración.
  • clawops init genera una clave SSH que clawops puede leer. Si ejecutaste init antes de esta versión, clawops doctor te dirá si la tuya es utilizable.
  • gcloud config set project se respeta.
  • El asistente de configuración escribe la configuración del modelo que OpenClaw acepta e instala el plugin que necesita tu proveedor elegido.
  • Amazon Bedrock funciona: el transporte correcto y un perfil de inferencia resuelto contra tu región de despliegue y registrado en el plan. Necesita bedrock:ListInferenceProfiles.

Mientras se ejecuta un despliegue

  • apply espera a SSH y luego espera a que la puerta de enlace responda, antes de reportar éxito.
  • apply reporta el progreso a medida que avanza en lugar de quedarse en silencio durante minutos.
  • Un despliegue que agota el tiempo imprime lo que el host estaba haciendo, desde su registro de arranque.
  • Un host que aún está instalando Docker se trata como si aún estuviera arrancando, en lugar de como un despliegue fallido.

Comandos de segundo día

  • clawops logs lee desde la puerta de enlace en AWS.
  • doctor --stack, ssh, logs, gateway, config y agents funcionan contra una pila recién desplegada.
  • clawops doctor valida las credenciales de la nube.
  • clawops distingue un socket de Docker rechazado de un contenedor faltante y dice cuál encontró.
  • clawops destroy olvida la clave de host de la instancia, por lo que volver a desplegar en una dirección que la nube ha reciclado ya no falla en la verificación.

Documentación

  • La guía de GCP nombra la fuente de credenciales que clawops realmente lee y describe el comportamiento del firewall 2.0.
  • El plan de prueba de humo cubre 2.0, y pnpm test:cloud aws|gcp|azure lo ejecuta contra un despliegue real y lo destruye después.

Novedades en 2.0

clawops 2.x está dirigido a OpenClaw >= 2026.9.2. La línea 1.x continúa para OpenClaw <= 2026.7.1-2 bajo la etiqueta de distribución legacy hasta 2027-03-31:

npm install -g @clawops/cli            # 2.x
npm install -g @clawops/cli@legacy     # 1.x maintenance

Fija la etiqueta en CI. latest se mueve a 2.x, por lo que una canalización sin fijar cambiará de líneas. CHANGELOG.md contiene el historial completo; esta sección cubre lo que cambió sobre cómo se comporta clawops.

Tu despliegue conserva su estado

OpenClaw 2.0 almacena sesiones, transcripciones y credenciales en SQLite. clawops no montaba ningún estado, por lo que cada reinicio las destruía, y un reinicio es lo que gateway restart, gateway update y config set hacen todos.

Un directorio del host (/var/lib/clawops/openclaw) ahora está montado por enlace en la ubicación predeterminada de OpenClaw, que contiene la configuración, la base de datos y cualquier plugin de proveedor. Los despliegues existentes migran en el próximo up/apply.

clawops up / clawops apply

flowchart TD
    A["clawops plan"] --> B{"config valid<br/>against OpenClaw schema?"}
    B -- no --> B1["refuse: plan is still<br/>a file you can edit"]
    B -- yes --> C["clawops apply"]
    C --> D{"OpenClaw version<br/>in supported range?"}
    D -- no --> D1["refuse: names<br/>@clawops/cli@legacy"]
    D -- yes --> E["provision host"]
    E --> F["state dir, owned 1000:1000<br/>migrate any pre-2.0 config"]
    F --> G["write config<br/>validated before writing"]
    G --> H["install provider plugins<br/>while egress exists"]
    H --> I["start gateway"]
    I --> J{"/startupz says started?"}
    J -- no --> J1["fail with the reason"]
    J -- yes --> K{"configured providers<br/>all loaded?"}
    K -- no --> K1["warn: healthy gateway,<br/>missing model backend"]
    K -- yes --> L["done"]

Tres de esos pasos son nuevos, y cada uno existe porque el flujo anterior podía reportar éxito mientras algo estaba mal: la configuración nunca se validaba antes de escribirse, los plugins de proveedor se dejaban para obtenerlos al arrancar (o faltaban silenciosamente en un host con denegación total), y "iniciado" se infería de que docker run saliera con 0.

clawops gateway update

Anteriormente: extraer, ejecutar, reportar éxito. Que docker run salga con 0 significa que el contenedor fue creado, y el contenedor que reemplaza ya no existe.

flowchart TD
    A["clawops gateway update X"] --> B{"X in supported range?"}
    B -- no --> B1["refuse before pulling"]
    B -- yes --> C["docker pull X"]
    C --> D["snapshot state database"]
    D -- cannot snapshot --> D1["refuse: no rollback point"]
    D --> E{"target release understands<br/>this schema?"}
    E -- no --> E1["refuse: downgrade across<br/>a schema boundary"]
    E -- yes --> F["swap container"]
    F --> G{"/startupz says started?"}
    G -- yes --> H["done"]
    G -- no --> I["one-shot doctor --fix<br/>in a throwaway container"]
    I --> J["re-run, re-gate"]
    J -- started --> K["done: reported as repaired"]
    J -- still not --> L["roll back to previous image"]
    L -- started --> M["rolled back, reason reported"]
    L -- still not --> N["failed: snapshot path named"]

La instantánea no es solo un punto de reversión: database preflight rechaza una base de datos en vivo porque la versión del esquema reside en el WAL hasta que se marca como punto de control, por lo que la instantánea consolidada es lo que hace posible la verificación de compatibilidad.

clawops gateway restart

Un reinicio no cambia ni la versión desplegada ni quién puede acceder a la puerta de enlace. Ambos se leen del contenedor en ejecución en lugar de adivinarse:

flowchart LR
    A["gateway restart"] --> B["read current image"]
    B -- no container --> B1["refuse: nothing to reuse.<br/>latest and stable point at 2.0"]
    B --> C["read current publish scope"]
    C --> D["recreate with the same<br/>version and reachability"]
    D --> E{"/startupz says started?"}
    E -- no --> E1["fail with the reason"]
    E -- yes --> F["done"]

Migrar un despliegue 1.x existente

flowchart TD
    A["clawops migrate"] --> B{"1.x container running?"}
    B -- no --> B1["nothing to rescue: state was<br/>already lost to an earlier restart"]
    B -- yes --> C["verified backup, inside the running container"]
    C -- "backup fails" --> C1["refused: nothing touched"]
    C --> D["extract state from the RUNNING container"]
    D --> E["chown 1000:1000"]
    E --> F["stop and remove 1.x"]
    F --> G["synthesise a valid 2.0 config"]
    G --> H["start 2.0 with the state directory"]
    H --> I{"/startupz started?"}
    I -- "no: schema still migrating" --> J["restart once"]
    J --> K{"started?"}
    K -- no --> K1["failed: points at the backup"]
    K --> L["report"]
    I -- yes --> L
    L --> M["what carried over,<br/>device identity, config to review"]

Dos cosas sobre esa forma no son obvias, y ambas provienen de ejecutar una migración real:

El estado se extrae del contenedor en ejecución. Todo el estado 1.x vivía dentro de él; clawops no montaba nada, por lo que detener primero destruye lo que la migración vino a salvar.

La configuración se sintetiza, no se traslada. 1.x nunca tuvo una que se aplicara; el archivo que clawops montaba no lo leía nada. Tus configuraciones antiguas se reportan como intención de revisión, nunca se aplican a ciegas. Sus bloques de canal no se validarían contra 2.0 de todos modos.

La puerta de enlace también necesita dos arranques: el primero realiza la migración del esquema de estado y lo reporta como pendiente. migrate espera al segundo en lugar de declarar éxito temprano.

Si ejecutaste gateway restart, gateway update o config set en un clawops antes de 2.0, tu estado ya no existe; no se montó nada para sobrevivir al reemplazo del contenedor. migrate lo dice claramente en lugar de fingir rescatarlo.

clawops backup restore funciona de nuevo, y nunca en el lugar

v1.7.5 hizo que la restauración fallara con una explicación, porque el OpenClaw que soportaba no tenía subcomando de restauración al que llamar. 2.0 lo tiene, y clawops se lo delega:

flowchart TD
    A["clawops backup restore --file X"] --> B["upload archive to the host"]
    B --> C["openclaw backup restore --target &lt;staging&gt;"]
    C -- "target not empty" --> C1["refused by OpenClaw"]
    C --> D["archive verified, expanded<br/>into a fresh directory"]
    D --> E["warnings printed verbatim<br/>time travel, channel relink,<br/>approvals, plugins"]
    E --> F["nothing activated"]
    F --> G["you stop the gateway, swap the<br/>state dir, restart, re-apply"]

clawops no extrae archivos por sí mismo y no restaura en el lugar. El paso final es manual a propósito, y volver a aplicar importa: el archivo no lleva el plugin node_modules, por lo que un despliegue restaurado arranca sin sus proveedores de modelo, luciendo saludable mientras lo hace.

El archivo es una credencial. Lleva la base de datos de estado, mcp_oauth_stores, secret_store_entries, worker_environment_credentials, device_auth_tokens, sin cifrar. clawops ahora lo escribe 0600 localmente; anteriormente usaba el 0644 predeterminado.

Los proveedores de modelo que necesitan un plugin se instalan por ti

OpenClaw 2.0 convirtió los proveedores de modelo en plugins con puerta de instalación. Veinticuatro vienen en la imagen, anthropic, openai, google, ollama, openrouter entre ellos, pero no todos. Configurar uno que no está incluido, sin instalarlo, produce una puerta de enlace que arranca, reporta saludable y no tiene backend de modelo.

clawops instala lo que tu configuración necesita, fijado a una versión exacta, durante apply:

Resolving clawhub:@openclaw/deepseek-provider@2026.9.2…
Downloading plugin @openclaw/deepseek-provider@2026.9.2 from ClawHub…
Installed plugin: deepseek

Esto añade una dependencia saliente que la línea 1.x no tenía: clawhub.ai. Se necesita mientras apply se está ejecutando, no al arrancar. Deliberadamente, para que un fallo llegue a la persona que ejecuta el comando en lugar de a un host bloqueado a las 3 a. m. Bloqueado, se ve así:

fetch failed | getaddrinfo EAI_AGAIN clawhub.ai | EAI_AGAIN

clawops verifica los ID de proveedor instalados después y no llamará al despliegue terminado mientras falte un proveedor configurado. Acceso saliente requerido enumera cada destino y cuándo se necesita.

Los canales de chat también se instalan por ti

Cada canal en OpenClaw 2.0 es un plugin con puerta de instalación. clawops apply instala los que tu configuración nombra, durante el despliegue mientras existe la salida, y luego pregunta a la puerta de enlace si realmente están instalados:

[clawops] warning: the gateway is running, but these configured channels are not installed:
discord. They will never connect.

Tiene que preguntar. openclaw channels add. El comando obvio devuelve éxito incluso cuando la instalación del plugin falla, por lo que clawops usa openclaw plugins install y verifica contra channels list --all --json.

Los plugins de canal están fijados al runtime compatible. El latest actual no se instala en él: plugin "discord" requires plugin API >=2026.9.3, but this OpenClaw runtime exposes 2026.9.2. La misma deriva que forzó los pines de versión en los proveedores de modelo.

Telegram no necesita que se instale nada: viene en la imagen.

La configuración incorrecta se detecta antes de escribirse

La configuración se valida contra el propio esquema de OpenClaw, capturado de la imagen, no escrito a mano, antes de enviar cualquier cosa al host, y de nuevo antes de que una escritura reemplace un archivo que funciona. clawops plan rechaza un plan cuya configuración la puerta de enlace rechazaría, mientras el plan sigue siendo un archivo que puedes editar.

Una configuración rechazada se conserva en <path>.rejected.<timestamp> y la activa se deja intacta, por lo que un fallo de validación nunca te cuesta lo que intentabas escribir.

Una regla es propia de clawops: gateway.mode es opcional en el esquema y obligatoria en la práctica. Una configuración sin ella pasa openclaw config validate y luego sale con 78.

Los contenedores están endurecidos

La puerta de enlace se ejecuta con --cap-drop=ALL, --security-opt no-new-privileges, --init y --pids-limit 512. El estado es propiedad numérica de 1000:1000, que coincide con el usuario del contenedor en lugar de una cuenta del host que puede no tener ese uid.

El pin de versión se aplica en todos los lugares donde puede cambiar

doctor, plan, up y apply rechazan una versión de OpenClaw fuera del rango compatible, y gateway restart reutiliza la versión ya desplegada en lugar de resolver una etiqueta móvil. Un reinicio no cambia ni la versión ni quién puede acceder a ella.

La puerta de enlace ya no está expuesta a tu red

El contenedor publica en 127.0.0.1:18789 en lugar de 0.0.0.0:18789. Accede a ella con clawops tunnel o un proxy inverso en el host.

Anteriormente, el asistente establecía allowedGatewayCidrs a partir del CIDR que diste para SSH, por lo que un panel HTTP en texto plano. Token en la URL. Se abría a toda tu red de acceso de shell como efecto secundario de una respuesta no relacionada. Para vincular todas las interfaces deliberadamente, establece network.publishGateway: "all".

Debes actuar si un cliente o proxy inverso en otra máquina accede a la puerta de enlace directamente, o si la monitorización externa golpea /health. Un proxy en el host no se ve afectado; uno en un contenedor en el host necesita --network host.

Las verificaciones de salud pueden fallar de verdad

La puerta de enlace sirve su UI de control en una ruta de captura total, por lo que cualquier ruta no coincidente responde 200 con HTML:

/healthz                        200  application/json   {"ok":true,"status":"live"}
/health-typo                    200  text/html          <!doctype html>…

clawops sondeaba con curl -fsS … >/dev/null, que tiene éxito en un error tipográfico. Probaba que algo estaba escuchando en el puerto, no que la puerta de enlace estuviera saludable. Los sondeos ahora leen el cuerpo de la respuesta, y la puerta de reinicio usa /startupz en lugar de vivacidad; después de un reinicio, el proceso escucha mucho antes de que el arranque termine.

clawops mcp wire ahora realmente conecta algo

Nunca ha funcionado, ni en 2.0, ni en ninguna versión 1.x. Escribía gateway.mcpClients, que no es una clave que OpenClaw tenga: verificado contra los esquemas de configuración de 2026.4.5, 2026.7.1-2 y 2026.9.2. La clave real es de nivel superior mcp.servers. Y la entrada que escribía era command: "clawops" sobre stdio, que se genera dentro del contenedor de la puerta de enlace, donde clawops no está instalado y nada lo instala.

En 1.x nada validaba la escritura, por lo que clawops almacenaba una clave que nada leía, reiniciaba tu puerta de enlace y reportaba: "La IA de la puerta de enlace ahora puede ejecutar comandos de clawops." No podía.

flowchart TD
    A["clawops mcp wire"] --> B["openclaw mcp add --transport streamable-http"]
    B --> C{"gateway connects<br/>to the URL?"}
    C -- no --> C1["probe fails, nothing saved,<br/>clawops prints the reason"]
    C -- yes --> D["saved to mcp.servers.clawops"]
    D --> E["openclaw mcp reload"]

Ahora delega en openclaw mcp add, que sondea el servidor antes de guardar, por lo que "conectado" significa que la puerta de enlace se conectó, no que se escribió un archivo.

Tienes que ejecutar el servidor tú mismo. clawops no está instalado en el host de la puerta de enlace:

clawops mcp serve --http 18790 --bind 0.0.0.0 --token "$(openssl rand -hex 16)"
clawops mcp wire --stack prod --token <same token>

Instalar clawops en el host de la puerta de enlace es un seguimiento deliberado, no parte de 2.0: coloca credenciales de implementación en la máquina implementada, y la IA de la puerta de enlace es accesible desde cada canal al que está conectada. Ver docs/security/threat-model.md T11.

clawops mcp serve --http sirve a más de un cliente, y pregunta quién eres

Dos errores, encontrados al probar contra una puerta de enlace real en lugar de un simulacro.

Construyó un transporte para todo el proceso, así que el primer cliente en conectarse lo reclamó y todos los posteriores. Un segundo editor, una reconexión, la propia sonda de la puerta de enlace, recibió "Server already initialized". El modo HTTP es el modo multi-cliente.

No tenía autenticación, mientras exponía todas las herramientas incluyendo clawops_destroy. Ahora toma un token de portador, lo compara en tiempo constante, y se niega a vincularse en cualquier lugar que no sea loopback sin uno.

El firewall sigue a la implementación

flowchart TD
    A["clawops plan"] --> B{"publishGateway?"}
    B -- "loopback (default)" --> C{"allowedGatewayCidrs empty?"}
    C -- no --> C1["refuse: those rules would admit<br/>traffic to a closed port"]
    C -- yes --> D["SSH rules only"]
    B -- all --> E["SSH rules + gateway rules<br/>on spec.network.gatewayPort"]
    D --> F["clawops harden"]
    E --> F
    F --> G["read the container's port bindings"]
    G --> H{"published to the network?"}
    H -- no --> H1["ufw: SSH only"]
    H -- yes --> H2["ufw: SSH + the published port"]

Tres controles de seguridad estaban haciendo lo contrario de lo que dicen.

clawops harden abrió el puerto de la puerta de enlace en cada implementación. El módulo ufw ejecutó ufw allow 18789/tcp incondicionalmente. Dado que la puerta de enlace publica en 127.0.0.1, eso abrió un puerto en el que nada estaba escuchando. Un paso de endurecimiento que amplía el firewall más allá de lo que la implementación expone. Ahora lee los enlaces de puerto del contenedor en ejecución y agrega la regla solo cuando la puerta de enlace está realmente publicada, en cualquier puerto en el que esté publicada.

La auditoría del grupo de seguridad de AWS eximió los dos puertos que existe para verificar. Los puertos 22 y 18789 estaban en una lista "esperada", así que un grupo que abre SSH o la puerta de enlace a 0.0.0.0/0 volvió como "No se encontraron reglas de entrada abiertas inesperadas". Tampoco leyó nunca las reglas IPv6, así que ::/0 era invisible.

El asistente de configuración predeterminó el acceso SSH a 0.0.0.0/0. Presionar Enter abrió SSH a todo internet, en el camino que la mayoría de los usuarios primerizos toman. Ahora ofrece tu propia IP como /32, y cuando eso no se puede detectar, no ofrece un valor predeterminado y requiere una respuesta.

clawops plan no podía expresar nada de eso, y apply nunca pasó nada de eso a Pulumi. Ambos están corregidos en 2.0.1. Ver la lista al principio de esta sección.

El puerto de la puerta de enlace proviene del plan

"network": {
  "allowedSshCidrs": ["203.0.113.4/32"],
  "allowedGatewayCidrs": [],
  "publishGateway": "loopback",
  "gatewayPort": 9443
}

Un valor ahora llega a las reglas del grupo de seguridad, la bandera de publicación del contenedor, el gateway.port predeterminado y la URL de la puerta de enlace. Era una constante redeclarada en once lugares, así que cambiarla significaba encontrarlos todos, y omitir uno producía un contenedor publicando un puerto, una puerta de enlace escuchando en otro, y un firewall abriendo un tercero.

Las implementaciones locales usan clawops up --gateway-port 9443.

clawops doctor responde si funciona, y lo dice en su código de salida

flowchart TD
    A["clawops doctor"] --> B["local: Node, Pulumi CLI + home,<br/>config, SSH key, credentials"]
    B --> C{"--stack given?"}
    C -- no --> Z["report"]
    C -- yes --> D["container state"]
    D --> E["deployed OpenClaw version"]
    E --> F["probe /startupz<br/>and read the body"]
    F --> G["published scope, disk,<br/>log rotation, hardening drift"]
    G --> Z
    Z --> Y{"any check failed?"}
    Y -- no --> Y1["exit 0"]
    Y -- yes --> Y2["exit 1"]

Tres cambios:

Le pregunta a la puerta de enlace. doctor solía leer el campo de healthcheck de docker inspect, que la imagen de OpenClaw no establece, así que reportaba "no hay healthcheck configurado" y seguía adelante. Un contenedor en ejecución significa que el proceso comenzó, no que sirve. Ahora sondea /startupz y lee el cuerpo.

Sale con 1 cuando algo falló. Solo un Node.js antiguo solía hacer eso; una clave SSH ilegible o una puerta de enlace no compatible salía con 0, así que un paso de CI ejecutando clawops doctor leía una implementación rota como éxito. Las advertencias aún salen con 0, una máquina nueva sin stacks está sin configurar, no rota.

Es una herramienta MCP. clawops_doctor devuelve el mismo informe como datos estructurados, así que un agente que encuentra una falla puede descubrir por qué. Solo informa; nunca ejecuta openclaw doctor --fix. --json le da al CLI el mismo informe.

clawops agents list deja de inventar una lista vacía

El comando terminaba en || echo '[]', así que un contenedor detenido, una puerta de enlace aún iniciando, o un error de permisos de Docker producían "No hay agentes ejecutándose.", una respuesta incorrecta en lugar de un error. Ahora falla, y dice cuál.

Los comandos del día dos funcionan en AWS

gateway restart, logs, monitor, backup, agents, config set y las verificaciones de contenedor de doctor estaban todas rotas en AWS: clawops se conecta como ubuntu, pero el aprovisionamiento solo puso clawops en el grupo docker, así que cada comando de Docker fallaba con permission denied. GCP and Azure connect as clawops, así que solo AWS se vio afectado.

Eliminado

clawops agents restart y la herramienta MCP clawops_agents_restart. OpenClaw 2.0 no tiene reinicio por agente, solo gateway restart y daemon restart, ambos de los cuales interrumpen cada agente en el host. Usa clawops gateway restart, o quédate en @clawops/cli@legacy.

clawops agents list y clawops agents logs no se ven afectados.


Hitos

HitoEstadoQué incluye
M0: Andamiaje✅Herramientas, CI, stubs, tipos generados
M1: MVP de GCP✅init / up / down / status / ssh / logs en GCP
M2: Gestión remota✅tunnel, config, agents, gateway; grupo de conexiones SSH
M3: AWS + Azure✅Adaptadores de AWS EC2 + Azure VM; stacks list
M4: VM local✅Adaptador local (bootstrap SSH, sin Pulumi); doctor
M5: Capa MCP✅mcp serve (stdio), todas las operaciones CLI como herramientas MCP, seguimiento de progreso
M6: Plan/Aplicar✅plan + apply; esquema de plan de implementación; transporte HTTP MCP; workflow_deploy_app
M7: Pulido v1.0✅Superficie completa de doctor; comando destroy; --dry-run en todos los comandos; guía de CI

Ver docs/roadmap.md para la hoja de ruta pública y el trabajo próximo.


Licencia

MPL-2.0, ver LICENSE.