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

# Déployer un serveur

## Vue d’ensemble

Cette compétence met en place une pile Welley à instance unique sur DigitalOcean et peut lancer des mises à jour par SSH. Elle guide l’agent pour qu’il rassemble les informations manquantes, récupère les spécifications d’instance disponibles depuis une source définie, crée la machine virtuelle, configure l’hôte, déploie avec `wlycli deployment create`, puis valide le résultat.

Les noms de commandes doivent correspondre à ceux du CLI :

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

## Valeurs par défaut (DigitalOcean)

-   Région : `nyc3`
-   Image : `latest LTS available via API` (à résoudre avec `images?type=distribution`)
-   Nom du droplet : généré au hasard sous la forme `wlsly-<adjective>-<surname>` (par ex. `wlsly-bright-hopper`). Utilisez l’extrait de génération de noms de `references/digitalocean.md`. Si une base de données gérée est créée, utilisez le même nom pour elle.
-   Formule visée : Basic / Regular (Shared CPU)
-   Taille visée : 2 vCPU / 4 Go de RAM / 80 Go de SSD / 4 To de transfert (environ 24 $ par mois)

## Règle de remplacement des valeurs par défaut

Si l’utilisateur précise l’une des valeurs par défaut (région, image, nom du droplet, projet, taille), l’agent doit :

1.  Répéter mot pour mot ce qui a été demandé.
2.  Demander une confirmation avant de continuer.

## Modèle de questions interactives

Ne demandez que ce qui manque. Utilisez ces formulations exactes pour éviter les ambiguïtés. Posez les questions par tours successifs. Tour 1 : 2 questions au maximum à la fois. Au tour 2, vous pouvez proposer une option groupée « accepter toutes les valeurs par défaut ». Si l’environnement d’exécution gère les choix, proposez 2 ou 3 options par question (A/B/C). Évitez les réponses libres, sauf si c’est nécessaire.

Questions du tour 1 (identité et accès) :

1.  « Avez-vous un jeton d’API DigitalOcean à utiliser, ou dois-je attendre que vous m’en fournissiez un ? »
2.  « Quel domaine ce serveur doit-il utiliser (par ex. `super-puper.example.com`) ? Gérez-vous son DNS ? »
3.  « Avez-vous déjà une clé publique SSH à utiliser, ou dois-je en créer une et l’envoyer ? »
4.  « Voulez-vous rattacher ce droplet à un projet DigitalOcean existant ? Si oui, indiquez le nom ou l’ID du projet. »
5.  « Prérequis DNS : vous devez créer des enregistrements A/AAAA pour `<domain>` et `storage.<domain>` (si vous n’utilisez pas de CDN). Pouvez-vous le faire avant l’exécution de certbot ? »
6.  « Prérequis DNS pour les e-mails : le domaine de `--smtp-from` a besoin d’enregistrements SPF, DMARC et DKIM, sinon les e-mails d’invitation et de confirmation envoyés à de nouveaux destinataires peuvent être supprimés sans avertissement. Sont-ils en place, ou dois-je lister les enregistrements dont votre fournisseur SMTP a besoin ? »
7.  Si l’utilisateur demande où obtenir un jeton DO, donnez ce lien :

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

Questions du tour 2 (taille, emplacement et options de déploiement) :

1.  « Voulez-vous la région par défaut `nyc3`, ou une autre région ? »
2.  « Voulez-vous la taille par défaut (2 vCPU / 2 Go), ou un autre objectif de CPU/RAM ? »
3.  « Dois-je utiliser la dernière version Ubuntu LTS disponible dans l’API, ou une version précise d’Ubuntu ? »
4.  « Quel utilisateur SSH devons-nous utiliser après l’initialisation (par défaut : `wlsly`) ? Vous pouvez garder la valeur par défaut. »
5.  « Voulez-vous une base Postgres gérée, ou le conteneur Postgres intégré ? »
6.  Si Postgres géré est choisi : « Quelle région et quelle taille dois-je utiliser pour la base ? Je peux prendre par défaut `nyc3` et 1 vCPU / 2 Go. »
7.  Si Postgres géré est choisi : « Voulez-vous que je lance la création au prix mensuel indiqué par le fournisseur ? »
8.  « Veuillez fournir les paramètres SMTP requis (hôte, utilisateur, mot de passe, expéditeur). OTEL est facultatif : ne le fournissez que si nécessaire. »
9.  « L’e-mail de l’administrateur est obligatoire pour la création. Indiquez-le maintenant (option : `--email` / `-e`). Sinon, je vous le demanderai deux fois pour le confirmer. »

Proposez une option « accepter toutes les valeurs par défaut » au tour 2 pour accélérer la collecte.

Si une réponse manque, posez une question de suivi ciblée (une ou deux questions).

## Déroulement (DigitalOcean, instance unique)

1.  **Rassembler les informations requises (demander ce qui manque).**

-   Jeton d’API DigitalOcean
-   Nom ou ID du projet (facultatif ; s’il manque ou s’il est introuvable, utiliser le projet par défaut)
-   Région
-   Taille d’instance visée (par ex. 4 vCPU / 16 Go de RAM)
-   Nom de domaine
-   Clé publique SSH (ou confirmation qu’il faut en créer une et l’enregistrer)
-   E-mail de l’administrateur pour la première connexion et la récupération du mot de passe (obligatoire pour la création ; `--email` / `-e`). S’il manque, le demander deux fois et vérifier que les deux saisies concordent.
-   Facultatif : certificats TLS personnalisés ou certbot
-   Confirmation de l’accès au DNS (il faut pouvoir créer des enregistrements A/AAAA pour `<domain>` et pour `storage.<domain>` s’il est utilisé)

1.  **Récupérer les types d’instance et choisir une taille.**

-   Utilisez l’extrait des tailles de `references/digitalocean.md` pour lister les tailles et filtrer par CPU/RAM.
-   Demandez confirmation à l’utilisateur s’il y a plusieurs correspondances.

2b. **Trouver le slug de l’image Ubuntu.**

-   Appelez `GET /v2/images?type=distribution&per_page=200` et filtrez sur Ubuntu.
-   Si l’utilisateur a demandé une version précise, vérifiez que son slug existe et demandez confirmation.
-   Sinon, choisissez le slug de la dernière version LTS de la liste et demandez confirmation à l’utilisateur avant la création.

1.  **Créer le droplet.**

-   Utilisez directement l’API DigitalOcean (doctl est facultatif).
-   Image : préférez celle que l’utilisateur a indiquée ; sinon, prenez la dernière version LTS de la liste de l’API.
-   Associez l’empreinte de la clé SSH, ou créez une clé et envoyez-la.
-   Activez la surveillance.
-   Si un nom ou un ID de projet a été fourni mais reste introuvable, créez le droplet sans projet (projet par défaut).
-   Remarque : la création de la base gérée peut se faire en parallèle de celle du droplet.

## Exécution en parallèle

L’agent doit faire se chevaucher les tâches indépendantes pour réduire le temps réel d’exécution :

-   Pendant la collecte des réponses (tour 2), lancez la création du droplet si le jeton, la région, la taille, l’image et la clé SSH sont déjà confirmés.
-   Pendant que le droplet se met en place, lancez la création de Postgres géré (si demandé).
-   Pendant que le droplet démarre, continuez à collecter les informations restantes (SMTP, OTEL, e-mail de l’administrateur).
-   Si une information essentielle manque pour une étape, mettez cette étape en pause et poursuivez les autres tâches qui peuvent avancer en parallèle.
-   Si le DNS est géré chez DigitalOcean, créez ou mettez à jour les enregistrements A/AAAA dès que l’IP du droplet est disponible (n’attendez pas l’étape de déploiement complète).

1.  **DNS et domaine.**

-   Demandez s’il faut créer ou mettre à jour le DNS chez DigitalOcean, ou laisser un DNS externe.
-   Si vous utilisez le DNS de DigitalOcean, créez des enregistrements A/AAAA vers l’IP du droplet.

1.  **Configurer l’hôte par SSH.**

-   Créez un utilisateur non root (par ex. `wlsly`), donnez-lui les droits sudo et copiez les clés SSH autorisées.
-   **Quittez la session root et reconnectez-vous avec l’utilisateur non root** avant de continuer.
-   Installez Docker et docker compose.
-   Récupérez le binaire du CLI Welley depuis AppStore et rendez-le exécutable :
    -   URL de base : `https://appstore.allroads.social/v1/social.wellesley.wlycli/latest?develop=stable&platform=<os>/<arch>`
    -   Choisissez la bonne valeur de `platform` selon le système et l’architecture :
        -   Linux x86\_64 : `linux/amd64`
        -   Linux arm64 : `linux/arm64`
        -   macOS x86\_64 : `darwin/amd64`
        -   macOS arm64 : `darwin/arm64`
        -   Windows x86\_64 : `windows/amd64`
    -   Exemple (Linux x86\_64) :
        
        ```
        curl -fL 'https://appstore.allroads.social/v1/social.wellesley.wlycli/latest?develop=stable&platform=linux/amd64' -o wlycli
        chmod +x wlycli
        ```
        
    -   Les en-têtes de réponse attendus comprennent :
        -   `x-artifact-display-name: Welley CLI`
        -   `x-artifact-full-name: social.wellesley.wlycli`
        -   `x-artifact-kind: cli`
        -   `x-artifact-platform: <os>/<arch>`, par exemple `linux/amd64`
        -   `x-artifact-sha256: <sha256>`
        -   `x-artifact-version: <version>`
        -   `x-channel: develop`
    -   Avant d’installer, il vaut mieux vérifier les en-têtes avec une requête HEAD et s’assurer que le nom de l’artefact, son type, la plateforme et la chaîne correspondent au binaire demandé.
-   Vérifiez le binaire du CLI : lancez `wlycli -v` et `wlycli deployment --help` pour confirmer les informations de build attendues.

1.  **Déployer Welley.**

-   Lancez `wlycli deployment create` avec les options requises.
-   Utilisez les valeurs par défaut d’une instance unique (pas de Postgres externe, sauf si l’utilisateur le demande).
-   Si Postgres géré est demandé, créez-le d’abord (base DigitalOcean), puis utilisez l’hôte, le port, la base, l’utilisateur et le mot de passe renvoyés dans les options `--postgres-*`.
-   Si le mot de passe de l’utilisateur de la base n’est renvoyé ni par l’API ni par l’interface, réinitialisez l’authentification de cet utilisateur pour obtenir de nouveaux identifiants avant de lancer `wlycli`.
-   Les exécutions non interactives doivent passer `--email` / `-e`, sinon la commande échoue.
-   `--base-dir` désigne le répertoire parent ; le CLI crée lui-même le sous-répertoire du projet.
-   Le CLI refuse de s’exécuter si le répertoire du projet ou les conteneurs existent déjà ; il faut nettoyer avant de réessayer.

1.  **Certificats (obligatoires).**

-   Par défaut : utilisez Certbot en mode standalone (non interactif).
-   **L’utilisateur doit faire pointer les enregistrements DNS A/AAAA du domaine (et de `storage.<domain>` s’il est utilisé) vers l’IP du droplet avant l’exécution de certbot.**
-   Le CLI accepte `--public-ip` (pour remplacer l’IP détectée) et `--skip-dns-preflight` si vous devez absolument contourner la vérification.
-   **Ne supposez jamais que le DNS est configuré. Demandez, puis attendez une confirmation explicite.**
-   **N’utilisez jamais `--skip-dns-preflight` sauf si l’utilisateur le demande explicitement.**
-   Si aucun CDN n’est configuré, certbot doit inclure `storage.<domain>` dans la demande de certificat.
-   N’utilisez des certificats autosignés que si l’utilisateur le demande explicitement, et passez `--self-signed-certs` (génère des certificats autosignés en Go).
-   Donnez à l’utilisateur les instructions de configuration DNS et attendez sa confirmation :
-   « Créez des enregistrements A/AAAA pour que `<domain>` pointe vers l’IP du droplet. »
-   « Si `storage.<domain>` est utilisé, créez aussi un enregistrement A/AAAA pour lui. »
-   « Dès que le DNS est propagé, dites-le-moi et je continuerai. Je ne peux pas observer la propagation, sauf si vous me demandez de vérifier la résolution. »
-   L’agent peut vérifier lui-même la propagation du DNS avec `ping -c 1 <domain>` et comparer l’IP à celle du droplet. Si elles correspondent, il peut continuer.

7b. **DNS des e-mails (SPF / DMARC / DKIM).**

-   Les e-mails envoyés depuis un domaine sans SPF ni DMARC sont souvent supprimés par Gmail et Outlook pour les destinataires qui les reçoivent pour la première fois, même si le fournisseur SMTP indique qu’ils ont été remis.
-   `deployment create` affiche un message `Warning:` quand le domaine de `--smtp-from` n’a pas d’enregistrement SPF, ou pas d’enregistrement DMARC à `_dmarc.<domain>` ni sur un domaine parent. Cela ne bloque pas le déploiement. N’ignorez jamais cet avertissement : montrez-le à l’utilisateur et donnez-lui les enregistrements à ajouter :
    -   `<mail-domain>` TXT `v=spf1 include:<provider> ~all` (la valeur exacte de include vient du fournisseur SMTP ; fusionnez-la avec un enregistrement SPF existant, n’en ajoutez jamais un second)
    -   `_dmarc.<mail-domain>` TXT `v=DMARC1; p=none; rua=mailto:dmarc@<mail-domain>`
    -   DKIM : les enregistrements `<selector>._domainkey.<mail-domain>` que le fournisseur SMTP génère quand le domaine est vérifié
-   Vérifiez avec `dig +short TXT <mail-domain>` et `dig +short TXT _dmarc.<mail-domain>`.
-   Le CLI ne peut pas vérifier DKIM (le sélecteur dépend du fournisseur). Demandez toujours à l’utilisateur de confirmer que DKIM est vérifié chez le fournisseur SMTP, même si `create` n’a affiché aucun avertissement. Avec Amazon SES et les fournisseurs similaires, SPF est vérifié sur le domaine d’enveloppe propre au fournisseur, donc seul DKIM peut faire réussir DMARC. Dans SES, ouvrez _Verified identities_, puis `<mail-domain>` : la section _DKIM_ doit afficher _Successful_.

1.  **Valider et rendre compte.**

-   Vérifiez `docker ps` et les points de contrôle d’état des services.
-   Donnez les informations de connexion finales et les prochaines étapes.

## Mise à jour par SSH

Quand l’utilisateur demande « mettre à jour le serveur » :

1.  Demandez l’hôte ou l’IP cible, ainsi que l’utilisateur et la clé SSH.
2.  Lancez `wlycli deployment update --domain <domain> [--force-restart]`.
3.  Vérifiez l’état des conteneurs et de Nginx.

## Ressources

-   Extraits et exemples de l’API DigitalOcean : `references/digitalocean.md`.

## Secrets et options

Utilisez ces noms d’options pour les secrets :

-   Hôte SMTP : `--smtp-host`
-   Utilisateur SMTP : `--smtp-user`
-   Mot de passe SMTP : `--smtp-password`
-   Expéditeur SMTP : `--smtp-from`
-   Point de terminaison OTEL : `--otel-endpoint`
-   En-têtes OTEL : `--otel-headers`

## Commande de création minimale

Options requises :

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

Exemple minimal :

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

## Valeurs par défaut de Postgres géré (DigitalOcean et CLI)

Avec Postgres géré par DigitalOcean, ne vous fiez pas aux valeurs par défaut du CLI. Définissez explicitement toutes les options `--postgres-*` :

-   Valeurs par défaut habituelles de DO :
    -   Port : `25060`
    -   Base de données : `defaultdb`
    -   Utilisateur : `doadmin`
-   Valeurs par défaut du CLI (si vous omettez les options) :
    -   Port : `5432`
    -   Base de données : `wlsly`
    -   Utilisateur : `wlsly`

## Notes d’utilisation du CLI

-   Vérification de la version : `wlycli -v` ou `wlycli --version` (option). Il n’existe pas de sous-commande `version`.
-   N’utilisez `--base-dir` que si vous voulez un emplacement différent de celui par défaut (`~/.welley`). Le CLI crée le répertoire s’il en a l’autorisation.
-   Les exécutions SSH non interactives doivent inclure `--email` et toutes les options requises pour éviter les questions.
-   Si l’utilisateur fournit un binaire du CLI sur le serveur (par ex. `~/wcli`), utilisez-le directement. Ne le déplacez pas, sauf si on vous le demande.
-   **Copiez les extraits mot pour mot** depuis `references/digitalocean.md` pour tous les appels à l’API DO. Ne réécrivez pas, n’adaptez pas et n’improvisez pas l’analyse en Python : même de petits changements (par ex. d’échappement ou de guillemets) peuvent casser selon le shell.
-   **Scripts distants par SSH :** utilisez toujours `ssh host 'bash -s' << 'EOF'` (heredoc entre guillemets simples). N’utilisez jamais `ssh host "..."` avec des guillemets doubles : zsh développe `$(...)` et `[...]` en local avant l’envoi à l’hôte distant.

## Reprise après un échec

Si `deployment create` échoue et que vous devez réessayer :

-   Utilisez `wlycli deployment delete --domain <domain> --yes-please-delete-all-the-data` pour nettoyer.
-   La suppression tente un nettoyage par conteneur des fichiers appartenant à root. Si cela échoue encore, supprimez le répertoire du projet situé dans `--base-dir` avec des droits élevés, puis relancez la commande.

## Test préalable du port 80 (facultatif)

Si certbot échoue, testez l’accessibilité du port 80 avant de relancer :

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

## Autres documents

-   [**Welley CLI** docs/index.md](https://welley.com/fr/administrators/docs/cli/)
-   [**Démarrage rapide : déploiement minimal de Welley** docs/quick-start.md](https://welley.com/fr/administrators/docs/quick-start/)
-   [**openai.yaml** skills/deploy-server/agents/openai.yaml](https://welley.com/fr/administrators/docs/deploy-server-openai-yaml/)
-   [**Référence DigitalOcean** skills/deploy-server/references/digitalocean.md](https://welley.com/fr/administrators/docs/deploy-server-digitalocean/)
