skills/deploy-server/SKILL.md

```
name: deploy-server
description: Use this skill to provision or update a Welley server on a supported cloud (currently DigitalOcean) when the user asks to deploy a new instance or run a server update. Covers single-instance deployments and SSH-based updates, including interactive collection of missing credentials, SSH keys, domain, region, and sizing.
```

# Desplegar servidor

## Resumen

Esta skill crea un stack de Welley de una sola instancia en DigitalOcean y puede ejecutar actualizaciones por SSH. Guía al agente para reunir los datos que falten, consultar las especificaciones de instancia disponibles en una fuente definida, crear la VM, configurar el host, desplegar con `wlycli deployment create` y validar el resultado.

Los nombres de los comandos deben coincidir con los de la CLI:

-   `wlycli deployment create`
-   `wlycli deployment update`
-   `wlycli deployment delete`

## Valores predeterminados (DigitalOcean)

-   Región: `nyc3`
-   Imagen: `latest LTS available via API` (se resuelve con `images?type=distribution`)
-   Nombre del droplet: se genera al azar con el formato `wlsly-<adjective>-<surname>` (por ejemplo, `wlsly-bright-hopper`). Usa el fragmento generador de nombres de `references/digitalocean.md`. Si se crea una base de datos administrada, usa el mismo nombre para ella.
-   Plan objetivo: Basic / Regular (CPU compartida)
-   Tamaño objetivo: 2 vCPU / 4 GB de RAM / 80 GB de SSD / 4 TB de transferencia (~$24/mes)

## Regla para cambiar los valores predeterminados

Si el usuario indica un valor propio para alguno de los valores predeterminados (región, imagen, nombre del droplet, proyecto, tamaño), el agente debe:

1.  Repetir el cambio tal cual lo indicó.
2.  Pedir confirmación antes de continuar.

## Plantilla de entrada interactiva

Pregunta solo lo que falte. Usa estas preguntas tal cual para reducir ambigüedades. Pregunta por rondas. Ronda 1: máximo 2 preguntas por turno. En la ronda 2 se puede ofrecer una sola opción para «aceptar todos los valores predeterminados». Si el entorno admite opciones, presenta 2 o 3 opciones por pregunta (A/B/C). Evita las respuestas libres salvo que sea necesario.

Preguntas de la ronda 1 (identidad y acceso):

1.  “¿Tienes un token de la API de DigitalOcean para usar, o espero a que me des uno?”
2.  “¿Qué dominio debe usar este servidor (por ejemplo, `super-puper.example.com`)? ¿Controlas su DNS?”
3.  “¿Ya tienes una clave pública SSH para usar, o la creo y la subo yo?”
4.  “¿Quieres asignar este droplet a un proyecto existente de DigitalOcean? Si es así, indica el nombre o el ID del proyecto.”
5.  “Requisito de DNS: debes crear registros A/AAAA para `<domain>` y `storage.<domain>` (si no hay CDN). ¿Puedes hacerlo antes de que se ejecute certbot?”
6.  “Requisito de DNS para el correo: el dominio de `--smtp-from` necesita registros SPF, DMARC y DKIM, o los correos de invitación y confirmación a destinatarios nuevos pueden descartarse sin aviso. ¿Ya los tienes, o te enumero los registros que necesita tu proveedor de SMTP?”
7.  Si el usuario pregunta dónde conseguir un token de DO, dale este enlace:

```
https://cloud.digitalocean.com/account/api/tokens
```

Preguntas de la ronda 2 (tamaño, ubicación y opciones de despliegue):

1.  “¿Quieres la región predeterminada `nyc3` u otra distinta?”
2.  “¿Quieres el tamaño predeterminado (2 vCPU / 2 GB) u otro objetivo de CPU/RAM?”
3.  “¿Uso la última versión LTS de Ubuntu disponible en la API, o una versión concreta de Ubuntu?”
4.  “¿Qué usuario SSH usamos después del arranque inicial (predeterminado: `wlsly`)? Puedes dejar el predeterminado.”
5.  “¿Quieres una base de datos Postgres administrada, o usar el contenedor de Postgres incluido?”
6.  Si se elige Postgres administrado: “¿Qué región y tamaño uso para la base de datos? Puedo usar por defecto `nyc3` y 1 vCPU / 2 GB.”
7.  Si se elige Postgres administrado: “¿Quieres que continúe con la creación al precio mensual que indica el proveedor?”
8.  “Indícame los ajustes de SMTP obligatorios (host, usuario, contraseña, remitente). OTEL es opcional: indícalo solo si lo necesitas.”
9.  “El correo del administrador es obligatorio para crear. Indícalo ahora (flag: `--email` / `-e`). Si no lo indicas, te lo pediré dos veces para confirmarlo.”

Ofrece una opción de «aceptar todos los valores predeterminados» en la ronda 2 para agilizar la recogida de datos.

Si falta alguna respuesta, haz una pregunta de seguimiento concreta (una o dos preguntas).

## Flujo de trabajo (DigitalOcean, instancia única)

1.  **Reunir los datos necesarios (preguntar si faltan).**

-   Token de la API de DigitalOcean
-   Nombre o ID del proyecto (opcional; si falta o no se encuentra, usar el proyecto predeterminado)
-   Región
-   Tamaño objetivo de la instancia (por ejemplo, 4 vCPU / 16 GB de RAM)
-   Nombre de dominio
-   Clave pública SSH (o confirmación para crear y registrar una)
-   Correo del administrador para el primer inicio de sesión y la recuperación de contraseña (obligatorio para crear; `--email` / `-e`). Si falta, pedirlo dos veces y confirmar que coinciden.
-   Opcional: certificados TLS propios o certbot
-   Confirmación de acceso al DNS (debe poder crear registros A/AAAA para `<domain>` y para `storage.<domain>` si se usa)

1.  **Consultar los tipos de instancia y elegir un tamaño.**

-   Usa el fragmento de tamaños de `references/digitalocean.md` para listar y filtrar por CPU/RAM.
-   Si hay varias coincidencias, confirma con el usuario.

2b. **Resolver el slug de la imagen de Ubuntu.**

-   Llama a `GET /v2/images?type=distribution&per_page=200` y filtra por Ubuntu.
-   Si el usuario pidió una versión concreta, comprueba que su slug exista y confírmalo.
-   Si no, elige el slug de la última LTS de la lista y confírmalo con el usuario antes de crear el droplet.

1.  **Crear el droplet.**

-   Usa directamente la API de DigitalOcean (doctl es opcional).
-   Imagen: da preferencia a la que indique el usuario; si no, elige la última LTS de la lista de la API.
-   Asocia la huella de la clave SSH, o crea y sube una clave.
-   Activa el monitoreo.
-   Si se indicó un nombre o ID de proyecto pero no se encontró, crea el droplet sin proyecto (proyecto predeterminado).
-   Nota: la creación de la base de datos administrada puede hacerse en paralelo con la del droplet.

## Guía de paralelización

El agente debe solapar las tareas independientes para reducir el tiempo total:

-   Mientras reúnes los datos del usuario (ronda 2), empieza a crear el droplet si el token, la región, el tamaño, la imagen y la clave SSH ya están confirmados.
-   Mientras el droplet se está creando, empieza a crear el Postgres administrado (si se pidió).
-   Mientras el droplet arranca, sigue reuniendo los datos que faltan (SMTP, OTEL, correo del administrador).
-   Si falta un dato crítico para un paso, pausa ese paso y continúa con otras tareas que se puedan hacer en paralelo.
-   Si el DNS se administra en DigitalOcean, crea o actualiza los registros A/AAAA en cuanto esté disponible la IP del droplet (no esperes al paso completo de despliegue).

1.  **DNS y dominio.**

-   Pregunta si se debe crear o actualizar el DNS en DigitalOcean o dejar un DNS externo.
-   Si se usa el DNS de DigitalOcean, crea registros A/AAAA que apunten a la IP del droplet.

1.  **Preparar el host por SSH.**

-   Crea un usuario sin privilegios de root (por ejemplo, `wlsly`), dale sudo y copia las claves autorizadas de SSH.
-   **Sal de root y vuelve a iniciar sesión con el usuario sin privilegios de root** antes de continuar.
-   Instala Docker y docker compose.
-   Descarga el binario de la CLI de Welley desde AppStore y hazlo ejecutable:
    -   URL base: `https://appstore.allroads.social/v1/social.wellesley.wlycli/latest?develop=stable&platform=<os>/<arch>`
    -   Elige el valor correcto de `platform` según el sistema operativo y la arquitectura:
        -   Linux x86\_64: `linux/amd64`
        -   Linux arm64: `linux/arm64`
        -   macOS x86\_64: `darwin/amd64`
        -   macOS arm64: `darwin/arm64`
        -   Windows x86\_64: `windows/amd64`
    -   Ejemplo (Linux x86\_64):
        
        ```
        curl -fL 'https://appstore.allroads.social/v1/social.wellesley.wlycli/latest?develop=stable&platform=linux/amd64' -o wlycli
        chmod +x wlycli
        ```
        
    -   Las cabeceras de respuesta esperadas incluyen:
        -   `x-artifact-display-name: Welley CLI`
        -   `x-artifact-full-name: social.wellesley.wlycli`
        -   `x-artifact-kind: cli`
        -   `x-artifact-platform: <os>/<arch>` como `linux/amd64`
        -   `x-artifact-sha256: <sha256>`
        -   `x-artifact-version: <version>`
        -   `x-channel: develop`
    -   Antes de instalar, conviene revisar las cabeceras con una petición HEAD y comprobar que el nombre del artefacto, el tipo, la plataforma y el canal coinciden con el binario que pediste.
-   Verifica el binario de la CLI: ejecuta `wlycli -v` y `wlycli deployment --help` para confirmar la información de compilación esperada.

1.  **Desplegar Welley.**

-   Ejecuta `wlycli deployment create` con las opciones necesarias.
-   Usa los valores predeterminados de instancia única (sin Postgres externo, salvo que el usuario lo pida).
-   Si se pide Postgres administrado, créalo primero (base de datos de DigitalOcean) y luego usa el host, puerto, base de datos, usuario y contraseña que te devuelva en los flags `--postgres-*`.
-   Si la API o la interfaz no devuelven la contraseña del usuario de la base de datos, restablece la autenticación de ese usuario para obtener credenciales nuevas antes de ejecutar `wlycli`.
-   Las ejecuciones no interactivas deben incluir `--email` / `-e`, o el comando fallará.
-   `--base-dir` apunta al directorio padre; la CLI crea el subdirectorio del proyecto automáticamente.
-   La CLI se niega a ejecutarse si el directorio del proyecto o los contenedores ya existen; hay que limpiar antes de reintentar.

1.  **Certificados (obligatorio).**

-   Por defecto: usa Certbot en modo standalone (no interactivo).
-   **El usuario debe apuntar los registros DNS A/AAAA del dominio (y de `storage.<domain>` si se usa) a la IP del droplet antes de que se ejecute certbot.**
-   La CLI admite `--public-ip` (para reemplazar la IP detectada) y `--skip-dns-preflight` si tienes que saltarte la comprobación.
-   **Nunca des por hecho que el DNS está configurado. Pregunta y espera una confirmación explícita.**
-   **Nunca uses `--skip-dns-preflight` salvo que el usuario lo pida de forma explícita.**
-   Si no hay CDN configurado, certbot debe incluir `storage.<domain>` en la solicitud del certificado.
-   Usa certificados autofirmados solo si el usuario lo pide de forma explícita, y pasa `--self-signed-certs` (genera certificados autofirmados en Go).
-   Dale al usuario las instrucciones de configuración del DNS y espera su confirmación:
-   “Crea registros A/AAAA para que `<domain>` apunte a la IP del droplet.”
-   “Si se usa `storage.<domain>`, crea también un registro A/AAAA para él.”
-   “Cuando el DNS se propague, avísame y continúo. No puedo ver la propagación a menos que me pidas comprobar la resolución.”
-   El agente puede comprobar la propagación del DNS por su cuenta con `ping -c 1 <domain>` y comparar la IP con la del droplet. Si coincide, continúa.

7b. **DNS del correo (SPF / DMARC / DKIM).**

-   Gmail y Outlook suelen descartar el correo de un dominio sin SPF ni DMARC cuando el destinatario es nuevo, aunque el proveedor de SMTP lo marque como entregado.
-   `deployment create` muestra un `Warning:` cuando el dominio de `--smtp-from` no tiene registro SPF, o no tiene registro DMARC en `_dmarc.<domain>` ni en un dominio superior. No bloquea el despliegue. Nunca ignores el aviso: muéstraselo al usuario y dale los registros que debe añadir:
    -   `<mail-domain>` TXT `v=spf1 include:<provider> ~all` (el include exacto lo da el proveedor de SMTP; intégralo en un registro SPF existente, nunca añadas un segundo registro)
    -   `_dmarc.<mail-domain>` TXT `v=DMARC1; p=none; rua=mailto:dmarc@<mail-domain>`
    -   DKIM: los registros `<selector>._domainkey.<mail-domain>` que genera el proveedor de SMTP cuando se verifica el dominio
-   Compruébalo con `dig +short TXT <mail-domain>` y `dig +short TXT _dmarc.<mail-domain>`.
-   La CLI no puede comprobar DKIM (el selector depende del proveedor). Pídele siempre al usuario que confirme que DKIM está verificado en el proveedor de SMTP, incluso cuando `create` no haya mostrado ningún aviso. Con Amazon SES y proveedores similares, el SPF se comprueba sobre el dominio del remitente del sobre (envelope) del propio proveedor, así que solo DKIM puede hacer que DMARC pase. En SES, en _Verified identities_ abre `<mail-domain>` y comprueba que DKIM indique _Successful_.

1.  **Validar e informar.**

-   Revisa `docker ps` y los endpoints de estado de los servicios.
-   Entrega los datos finales de conexión y los siguientes pasos.

## Flujo de actualización por SSH

Cuando el usuario pida «actualizar el servidor»:

1.  Pide el host o la IP de destino y el usuario y la clave de SSH.
2.  Ejecuta `wlycli deployment update --domain <domain> [--force-restart]`.
3.  Verifica el estado de los contenedores y de Nginx.

## Recursos

-   Fragmentos y ejemplos de la API de DigitalOcean: `references/digitalocean.md`.

## Secretos y sus flags

Usa estos nombres de flag para los secretos:

-   Host SMTP: `--smtp-host`
-   Usuario SMTP: `--smtp-user`
-   Contraseña SMTP: `--smtp-password`
-   Remitente SMTP: `--smtp-from`
-   Endpoint de OTEL: `--otel-endpoint`
-   Cabeceras de OTEL: `--otel-headers`

## Comando mínimo de creación

Flags obligatorios:

-   `--domain`
-   `--email`
-   `--smtp-host`
-   `--smtp-user`
-   `--smtp-password`
-   `--smtp-from`

Ejemplo mínimo:

```
wlycli deployment create \
  --domain example.com \
  --email admin@example.com \
  --smtp-host smtp.example.com \
  --smtp-user user \
  --smtp-password pass \
  --smtp-from noreply@example.com
```

## Valores predeterminados de Postgres administrado (DigitalOcean y CLI)

Cuando uses el Postgres administrado de DigitalOcean, no dependas de los valores predeterminados de la CLI. Define todos los flags `--postgres-*` de forma explícita:

-   Valores típicos de DO:
    -   Puerto: `25060`
    -   Base de datos: `defaultdb`
    -   Usuario: `doadmin`
-   Valores predeterminados de la CLI (si omites los flags):
    -   Puerto: `5432`
    -   Base de datos: `wlsly`
    -   Usuario: `wlsly`

## Notas de uso de la CLI

-   Comprobar la versión: `wlycli -v` o `wlycli --version` (flag). No existe un subcomando `version`.
-   Usa `--base-dir` solo si quieres una ubicación distinta de la predeterminada (por defecto es `~/.welley`). La CLI creará el directorio si tiene permisos.
-   Las ejecuciones SSH no interactivas deben incluir `--email` y cualquier otro flag obligatorio para evitar preguntas.
-   Si el usuario ya puso un binario de la CLI en el servidor (por ejemplo, `~/wcli`), úsalo directamente. No lo muevas a menos que te lo pidan.
-   **Copia los fragmentos tal cual** de `references/digitalocean.md` para todas las llamadas a la API de DO. No reescribas, adaptes ni improvises el análisis en Python: incluso cambios pequeños (por ejemplo, de escape o de comillas) pueden fallar según el shell.
-   **Scripts remotos por SSH:** usa siempre `ssh host 'bash -s' << 'EOF'` (heredoc entre comillas). Nunca uses `ssh host "..."` con comillas dobles: zsh expande `$(...)` y `[...]` en local antes de enviar el comando al host remoto.

## Recuperación tras un fallo

Si `deployment create` falla y necesitas reintentar:

-   Usa `wlycli deployment delete --domain <domain> --yes-please-delete-all-the-data` para limpiar.
-   El comando delete intentará limpiar mediante contenedores los archivos que pertenecen a root. Si aun así falla, elimina con permisos elevados el directorio del proyecto dentro de `--base-dir` y vuelve a ejecutar.

## Prueba previa del puerto 80 (opcional)

Si certbot falla, prueba que el puerto 80 sea accesible antes de volver a ejecutarlo:

```
docker run --rm -d -p 80:80 --name test-http nginx:alpine
curl -s http://<domain>:80
docker rm -f test-http
```

## Otros documentos

-   [**Welley CLI** docs/index.md](https://welley.com/es/administrators/docs/cli/)
-   [**Inicio rápido: despliegue mínimo de Welley** docs/quick-start.md](https://welley.com/es/administrators/docs/quick-start/)
-   [**openai.yaml** skills/deploy-server/agents/openai.yaml](https://welley.com/es/administrators/docs/deploy-server-openai-yaml/)
-   [**Referencia de DigitalOcean** skills/deploy-server/references/digitalocean.md](https://welley.com/es/administrators/docs/deploy-server-digitalocean/)
