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.
```

# Развёртывание сервера

## Обзор

Этот навык разворачивает на DigitalOcean один экземпляр Welley и может запускать обновления по SSH. Он помогает агенту собрать недостающие данные, получить список доступных конфигураций из заданного источника, создать виртуальную машину, настроить хост, развернуть Welley командой `wlycli deployment create` и проверить результат.

Названия команд должны совпадать с названиями в CLI:

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

## Значения по умолчанию (DigitalOcean)

-   Регион: `nyc3`
-   Образ: `latest LTS available via API` (определите через `images?type=distribution`)
-   Имя дроплета: случайное, в виде `wlsly-<adjective>-<surname>` (например, `wlsly-bright-hopper`). Возьмите фрагмент для генерации имени из `references/digitalocean.md`. Если создаётся управляемая база данных, используйте для неё то же имя.
-   Тариф: Basic / Regular (Shared CPU)
-   Размер: 2 vCPU / 4 GB RAM / 80 GB SSD / 4 TB трафика (около $24 в месяц)

## Правило изменения значений по умолчанию

Если пользователь задаёт любое из значений по умолчанию (регион, образ, имя дроплета, проект, размер), агент должен:

1.  Повторить это значение пользователю дословно.
2.  Попросить подтверждения, прежде чем продолжить.

## Шаблон интерактивного ввода

Спрашивайте только о том, чего не хватает. Используйте именно эти формулировки, чтобы вопросы не поняли неверно. Задавайте вопросы по раундам. Раунд 1: не больше 2 вопросов за один ход. В раунде 2 можно предложить общий вариант «принять все значения по умолчанию». Если среда выполнения поддерживает выбор из вариантов, предлагайте на каждый вопрос 2 или 3 варианта (A/B/C). Свободный ввод используйте только при необходимости.

Вопросы раунда 1 (данные и доступ):

1.  «Есть ли у вас API-токен DigitalOcean, который я могу использовать, или мне подождать, пока вы его предоставите?»
2.  «Какой домен должен использовать этот сервер (например, `super-puper.example.com`)? Управляете ли вы его DNS?»
3.  «Есть ли у вас публичный SSH-ключ, который можно использовать, или мне создать его и загрузить?»
4.  «Нужно ли добавить этот дроплет в существующий проект DigitalOcean? Если да, укажите название или ID проекта.»
5.  «Требование к DNS: вам нужно создать A/AAAA-записи для `<domain>` и `storage.<domain>` (если нет CDN). Сможете сделать это до запуска certbot?»
6.  «Требование к DNS для почты: у домена из `--smtp-from` должны быть записи SPF, DMARC и DKIM, иначе письма с приглашениями и подтверждениями для новых получателей могут молча пропадать. Эти записи уже есть, или мне перечислить записи, которые нужны вашему SMTP-провайдеру?»
7.  Если пользователь спрашивает, где взять токен DO, дайте эту ссылку:

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

Вопросы раунда 2 (размер, расположение и параметры развёртывания):

1.  «Оставить регион по умолчанию `nyc3` или выбрать другой?»
2.  «Оставить размер по умолчанию (2 vCPU / 2 GB) или нужны другие параметры процессора и памяти?»
3.  «Взять последнюю версию Ubuntu LTS, доступную в API, или вам нужна конкретная версия Ubuntu?»
4.  «Какого SSH-пользователя использовать после начальной настройки (по умолчанию `wlsly`)? Можно оставить значение по умолчанию.»
5.  «Создать управляемую базу Postgres или использовать встроенный контейнер Postgres?»
6.  Если выбран управляемый Postgres: «Какой регион и размер базы использовать? По умолчанию я возьму `nyc3` и 1 vCPU / 2 GB.»
7.  Если выбран управляемый Postgres: «Создавать базу по месячной цене, которую указывает провайдер?»
8.  «Пришлите обязательные настройки SMTP (хост, пользователь, пароль, адрес отправителя). OTEL необязателен: указывайте его, только если он нужен.»
9.  «Для создания нужен email администратора. Укажите его сейчас (флаг: `--email` / `-e`). Если вы его не укажете, я попрошу ввести его дважды для проверки.»

Чтобы ускорить сбор данных, предложите в раунде 2 вариант «принять все значения по умолчанию».

Если на какой-то вопрос нет ответа, задайте короткий уточняющий вопрос (один или два).

## Порядок работы (DigitalOcean, один экземпляр)

1.  **Соберите необходимые данные (спросите, если их нет).**

-   API-токен DigitalOcean
-   Название или ID проекта (необязательно; если не указан или не найден, используйте проект по умолчанию)
-   Регион
-   Нужный размер экземпляра (например, 4 vCPU / 16 GB RAM)
-   Доменное имя
-   Публичный SSH-ключ (или согласие создать и зарегистрировать ключ)
-   Email администратора для первого входа и восстановления пароля (обязателен для создания; `--email` / `-e`). Если его нет, запросите дважды и проверьте, что значения совпадают.
-   По желанию: свои TLS-сертификаты вместо certbot
-   Подтверждение доступа к DNS (нужно уметь создавать A/AAAA-записи для `<domain>` и, если он используется, для `storage.<domain>`)

1.  **Получите список типов экземпляров и выберите размер.**

-   Возьмите фрагмент для получения размеров из `references/digitalocean.md`, выведите список и отфильтруйте его по процессору и памяти.
-   Если подходит несколько вариантов, уточните выбор у пользователя.

2b. **Определите slug образа Ubuntu.**

-   Вызовите `GET /v2/images?type=distribution&per_page=200` и отфильтруйте Ubuntu.
-   Если пользователь запросил конкретную версию, убедитесь, что такой slug существует, и подтвердите выбор.
-   Иначе возьмите из списка slug последней LTS-версии и подтвердите его у пользователя до создания.

1.  **Создайте дроплет.**

-   Используйте напрямую API DigitalOcean (doctl необязателен).
-   Образ: предпочитайте тот, что указал пользователь; иначе возьмите последнюю LTS из списка API.
-   Прикрепите отпечаток SSH-ключа или создайте и загрузите ключ.
-   Включите мониторинг.
-   Если название или ID проекта указаны, но проект не найден, создайте дроплет без проекта (в проекте по умолчанию).
-   Примечание: управляемую базу данных можно создавать параллельно с дроплетом.

## Параллельное выполнение

Чтобы сократить общее время, агент должен совмещать независимые задачи:

-   Пока вы собираете ответы пользователя (раунд 2), начинайте создавать дроплет, если токен, регион, размер, образ и SSH-ключ уже подтверждены.
-   Пока дроплет создаётся, начинайте создавать управляемый Postgres (если он нужен).
-   Пока дроплет запускается, продолжайте собирать остальные данные (SMTP, OTEL, email администратора).
-   Если для какого-то шага не хватает важных данных, приостановите этот шаг и продолжайте другие задачи, которые можно выполнять параллельно.
-   Если DNS управляется в DigitalOcean, создайте или обновите A/AAAA-записи, как только станет известен IP дроплета (не ждите шага полного развёртывания).

1.  **DNS и домен.**

-   Спросите, создавать или обновлять DNS в DigitalOcean или оставить внешний DNS.
-   Если используется DNS DigitalOcean, создайте A/AAAA-записи, указывающие на IP дроплета.

1.  **Настройте хост по SSH.**

-   Создайте пользователя без прав root (например, `wlsly`), выдайте ему sudo и скопируйте SSH-ключи из authorized keys.
-   **Выйдите из root и войдите снова как этот пользователь**, прежде чем продолжать.
-   Установите Docker и docker compose.
-   Скачайте бинарный файл Welley CLI из AppStore и сделайте его исполняемым:
    -   Базовый URL: `https://appstore.allroads.social/v1/social.wellesley.wlycli/latest?develop=stable&platform=<os>/<arch>`
    -   Выберите правильное значение `platform` по ОС и архитектуре:
        -   Linux x86\_64: `linux/amd64`
        -   Linux arm64: `linux/arm64`
        -   macOS x86\_64: `darwin/amd64`
        -   macOS arm64: `darwin/arm64`
        -   Windows x86\_64: `windows/amd64`
    -   Пример (Linux x86\_64):
        
        ```
        curl -fL 'https://appstore.allroads.social/v1/social.wellesley.wlycli/latest?develop=stable&platform=linux/amd64' -o wlycli
        chmod +x wlycli
        ```
        
    -   В ответе ожидаются такие заголовки:
        -   `x-artifact-display-name: Welley CLI`
        -   `x-artifact-full-name: social.wellesley.wlycli`
        -   `x-artifact-kind: cli`
        -   `x-artifact-platform: <os>/<arch>`, например `linux/amd64`
        -   `x-artifact-sha256: <sha256>`
        -   `x-artifact-version: <version>`
        -   `x-channel: develop`
    -   Перед установкой лучше проверить заголовки HEAD-запросом и убедиться, что название артефакта, его тип, платформа и канал совпадают с запрошенным файлом.
-   Проверьте исполняемый файл CLI: выполните `wlycli -v` и `wlycli deployment --help`, чтобы убедиться, что сведения о сборке соответствуют ожидаемым.

1.  **Разверните Welley.**

-   Запустите `wlycli deployment create` с нужными параметрами.
-   Используйте значения по умолчанию для одного экземпляра (без внешнего Postgres, если пользователь его не просит).
-   Если нужен управляемый Postgres, сначала создайте его (база данных DigitalOcean), а затем передайте полученные хост, порт, базу, пользователя и пароль во флаги `--postgres-*`.
-   Если пароль пользователя базы данных не возвращается ни через API, ни в интерфейсе, сбросьте аутентификацию этого пользователя и получите новые учётные данные до запуска `wlycli`.
-   В неинтерактивном режиме обязательно передайте `--email` / `-e`, иначе команда завершится с ошибкой.
-   `--base-dir` указывает на родительский каталог; подкаталог проекта CLI создаёт сам.
-   CLI не запустится, если каталог проекта или контейнеры уже существуют; перед повторной попыткой их нужно удалить.

1.  **Сертификаты (обязательно).**

-   По умолчанию: Certbot в режиме standalone, в неинтерактивном режиме.
-   **Пользователь должен направить A/AAAA-записи домена (и `storage.<domain>`, если он используется) на IP дроплета до запуска certbot.**
-   CLI поддерживает `--public-ip` (заменяет автоматически определённый IP) и `--skip-dns-preflight`, если проверку нужно обойти.
-   **Никогда не считайте, что DNS уже настроен. Спросите и дождитесь явного подтверждения.**
-   **Не используйте `--skip-dns-preflight`, если пользователь прямо об этом не попросил.**
-   Если CDN не настроен, в запрос сертификата certbot нужно включить `storage.<domain>`.
-   Самоподписанные сертификаты используйте, только если пользователь прямо об этом просит, и передайте `--self-signed-certs` (самоподписанные сертификаты создаются средствами Go).
-   Дайте пользователю инструкции по настройке DNS и дождитесь подтверждения:
-   «Создайте A/AAAA-записи, чтобы `<domain>` указывал на IP дроплета.»
-   «Если используется `storage.<domain>`, создайте A/AAAA-запись и для него.»
-   «Когда DNS обновится, сообщите мне, и я продолжу. Я не вижу, обновился ли DNS, пока вы не попросите меня проверить разрешение имени.»
-   Агент может сам проверить обновление DNS командой `ping -c 1 <domain>` и сравнить IP с адресом дроплета. Если они совпадают, можно продолжать.

7b. **DNS для почты (SPF / DMARC / DKIM).**

-   Gmail и Outlook часто отбрасывают письма с домена без SPF и DMARC, если они адресованы новым получателям, даже когда SMTP-провайдер сообщает, что письмо доставлено.
-   `deployment create` выводит `Warning:`, если у домена из `--smtp-from` нет записи SPF или нет записи DMARC в `_dmarc.<domain>` или в родительском домене. Развёртывание это не блокирует. Не игнорируйте предупреждение: покажите его пользователю и дайте записи, которые нужно добавить:
    -   `<mail-domain>` TXT `v=spf1 include:<provider> ~all` (точное значение include зависит от SMTP-провайдера; объедините его с существующей записью SPF и никогда не добавляйте вторую)
    -   `_dmarc.<mail-domain>` TXT `v=DMARC1; p=none; rua=mailto:dmarc@<mail-domain>`
    -   DKIM: записи `<selector>._domainkey.<mail-domain>`, которые SMTP-провайдер создаёт при подтверждении домена
-   Проверьте командами `dig +short TXT <mail-domain>` и `dig +short TXT _dmarc.<mail-domain>`.
-   CLI не может проверить DKIM (селектор зависит от провайдера). Всегда просите пользователя подтвердить, что SMTP-провайдер успешно проверил DKIM, даже если `create` не вывел предупреждений. У Amazon SES и похожих провайдеров SPF проверяется для собственного домена конверта провайдера, поэтому DMARC может пройти только за счёт DKIM. В SES: в разделе _Verified identities_ откройте `<mail-domain>`, там в поле DKIM должно быть _Successful_.

1.  **Проверьте результат и сообщите о нём.**

-   Проверьте `docker ps` и эндпоинты состояния сервисов.
-   Сообщите итоговые данные для подключения и дальнейшие шаги.

## Обновление по SSH

Когда пользователь просит «обновить сервер»:

1.  Спросите адрес хоста или IP, а также SSH-пользователя и ключ.
2.  Выполните `wlycli deployment update --domain <domain> [--force-restart]`.
3.  Проверьте состояние контейнеров и Nginx.

## Ресурсы

-   Фрагменты и примеры для API DigitalOcean: `references/digitalocean.md`.

## Секреты и флаги

Для секретов используйте такие флаги:

-   Хост SMTP: `--smtp-host`
-   Пользователь SMTP: `--smtp-user`
-   Пароль SMTP: `--smtp-password`
-   Адрес отправителя SMTP: `--smtp-from`
-   Эндпоинт OTEL: `--otel-endpoint`
-   Заголовки OTEL: `--otel-headers`

## Минимальная команда создания

Обязательные флаги:

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

Минимальный пример:

```
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
```

## Значения Postgres по умолчанию (DigitalOcean и CLI)

Если вы используете управляемый Postgres от DigitalOcean, не полагайтесь на значения по умолчанию в CLI. Задайте все флаги `--postgres-*` явно:

-   Обычные значения DO:
    -   Порт: `25060`
    -   База данных: `defaultdb`
    -   Пользователь: `doadmin`
-   Значения по умолчанию в CLI (если не указывать флаги):
    -   Порт: `5432`
    -   База данных: `wlsly`
    -   Пользователь: `wlsly`

## Заметки по работе с CLI

-   Проверка версии: `wlycli -v` или `wlycli --version` (это флаг). Подкоманды `version` нет.
-   Используйте `--base-dir` только если нужен нестандартный каталог (по умолчанию `~/.welley`). CLI создаст каталог, если у него хватает прав.
-   При запуске по SSH без диалога добавляйте `--email` и все обязательные флаги, чтобы не появлялись вопросы.
-   Если пользователь уже положил исполняемый файл CLI на сервер (например, `~/wcli`), используйте его как есть. Не перемещайте его без просьбы.
-   **Копируйте фрагменты дословно** из `references/digitalocean.md` для всех вызовов API DO. Не переписывайте, не подгоняйте и не придумывайте заново разбор в Python: даже небольшие изменения (например, в экранировании или кавычках) могут перестать работать в другой оболочке.
-   **Удалённые скрипты по SSH:** всегда используйте `ssh host 'bash -s' << 'EOF'` (heredoc в кавычках). Никогда не используйте `ssh host "..."` с двойными кавычками: zsh раскрывает `$(...)` и `[...]` локально, до отправки на удалённый хост.

## Восстановление после сбоя

Если `deployment create` завершилась с ошибкой и нужно повторить попытку:

-   Для очистки выполните `wlycli deployment delete --domain <domain> --yes-please-delete-all-the-data`.
-   Команда delete попробует очистить файлы, принадлежащие root, с помощью контейнера. Если и это не получится, удалите каталог проекта внутри `--base-dir` с повышенными правами и запустите команду снова.

## Предварительная проверка порта 80 (по желанию)

Если certbot завершился с ошибкой, перед повторным запуском проверьте, доступен ли порт 80:

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

## Другие документы

-   [**Welley CLI** docs/index.md](https://welley.com/ru/administrators/docs/cli/)
-   [**Быстрый старт: минимальное развертывание Welley** docs/quick-start.md](https://welley.com/ru/administrators/docs/quick-start/)
-   [**openai.yaml** skills/deploy-server/agents/openai.yaml](https://welley.com/ru/administrators/docs/deploy-server-openai-yaml/)
-   [**Справка по DigitalOcean** skills/deploy-server/references/digitalocean.md](https://welley.com/ru/administrators/docs/deploy-server-digitalocean/)
