Paillasse/docs/prompt-deploiement-paillasse.md
Adam Belkacemi ca4669156a Prompt final
2026-10-07 01:12:34 +02:00

18 KiB
Raw Blame History

Prompt Claude Code : déploiement complet de Syllabus

À coller dans Claude Code, lancé depuis la racine du dépôt Paillasse sur le Mac d'Adam.


0. Contexte

Nom du produit : Syllabus (ancien nom de travail : Paillasse).

  • Tout ce que voient les utilisateurs (pages, titres, e-mails, mentions légales) dit Syllabus, avec les logos design/images/logo-syllabus.png et design/images/logo-syllabus-icone.png.
  • Les noms techniques internes restent tels quels : le dépôt Paillasse, le dossier paillasse-contenus/, l'image paillasse-debian, le projet OpenStack paillasse. Ne les renomme pas.
  • Si tu trouves « Paillasse » dans un texte visible, remplace-le par « Syllabus ».

Tu vas déployer Syllabus de bout en bout sur un serveur bare metal, jusqu'à une version utilisable par une vraie classe.

Lis d'abord CLAUDE.md et paillasse-contenus/README.md. Ils contiennent le contexte complet du projet, la stack et les règles.

Syllabus : une plateforme de labs Linux et réseau pour les écoles. Chaque élève a une vraie VM Debian, avec une console dans le navigateur et une correction automatique par scripts. Pas d'IA dans le produit.

Le serveur

  • Scaleway Dedibox Start-2-L, datacenter DC2 (France).
  • Xeon D-1531 (6 cœurs / 12 threads), 32 Go de RAM, 2 × 250 Go SSD en RAID 1 (/ fait 226 Go).
  • Une seule IP publique : 163.172.4.195.
  • Debian 12, installé par Adam depuis la console Dedibox.
  • Accès SSH : ssh adam@163.172.4.195, avec la clé dédiée ~/.ssh/id_ed25519_claude. Crée l'alias paillasse dans ~/.ssh/config, avec IdentitiesOnly yes pour n'utiliser que cette clé.
  • sudo fonctionne sans mot de passe pour adam (fichier /etc/sudoers.d/90-adam).
  • Sauvegardes : Dedibackup 100 Go (FTP, joignable uniquement depuis le serveur). Adam a déposé les identifiants dans /root/.dedibackup (chmod 600). Ne les affiche jamais.

Image et labs : suis la section « Image commune » de paillasse-contenus/README.md, et crée ce que demandent les labs 04 et 07 (étape preparation, /opt/labs/07/lycee.sql, /opt/paquets/).

La forge : ssh://git@forge.adam.belkacemi.me:2222/adam/Paillasse.git. Tu n'y fais que git push : aucune connexion SSH à la machine qui l'héberge.

Le domaine : pas encore acheté. Fais les étapes 1 à 11 sans domaine (accès par IP ou tunnel SSH). Avant l'étape 12, arrête-toi et demande-moi le domaine.


1. Règles non négociables

  1. Ne jamais toucher à tripoli-cloud ni à aucune autre machine que le serveur Syllabus et le Mac d'Adam.
  2. Aucun secret dans le code, dans Git, dans le journal ou dans la conversation.
    • Les secrets vivent dans des fichiers chmod 600 sur le serveur, ou dans un .env ignoré par Git.
    • Les secrets Ansible passent par Ansible Vault. Le mot de passe du vault reste hors du dépôt.
    • Kolla : passwords.yml n'est jamais commité en clair.
  3. Commandes à risque : explique, montre la commande, vérifie la sauvegarde, puis attends le « OK » d'Adam. Sont concernés :
    • suppressions (rm -rf, DROP, kolla-ansible destroy…) ;
    • disques, partitions, RAID ;
    • toute modification du réseau, du pare-feu ou de SSH ;
    • redémarrage du serveur.
  4. Anti-coupure SSH : avant toute modification du réseau, du pare-feu ou de SSH, programme un retour automatique à l'ancienne configuration dans 5 minutes (par exemple avec systemd-run --on-active=5min), puis annule-le une fois l'accès vérifié depuis une nouvelle session SSH.
  5. Tu ne commandes et ne paies jamais rien.
  6. Élèves mineurs : minimisation des données.
    • Un élève = un identifiant prénom.initiale et un code secret. Pas d'e-mail. Le nom complet est facultatif et visible uniquement du prof.
    • Suppression automatique 90 jours après l'archivage d'une classe.
  7. Tout est du code :
    • chaque configuration passe par un rôle Ansible idempotent dans ansible/ ;
    • une commande tapée à la main pour explorer doit ensuite être codifiée ;
    • relancer un playbook ne doit rien casser.
  8. Vérifie les versions dans la documentation officielle, jamais de mémoire : Kolla-Ansible, OS supportés, Laravel, PHP, Go. Utilise Context7 ou le web.
  9. Documentation en français (docs, commentaires, messages de commit).

2. Méthode de travail

Pour chaque étape

  1. Annonce l'étape et son critère de réussite.
  2. Fais le travail.
  3. Vérifie le critère par un test réel et montre la sortie.
  4. Écris dans docs/journal.md :
    • ce qui a été fait ;
    • les commandes clés ;
    • les problèmes rencontrés et leur solution ;
    • un paragraphe « Comprendre » de 3 à 6 phrases, qui explique simplement le pourquoi à Adam.
  5. git commit, puis git push.
  6. Passe à l'étape suivante sans attendre, sauf si une règle de la section 1 impose l'accord d'Adam.

Limite de temps

1 h 30 maximum par étape. Si elle est dépassée :

  • arrête-toi ;
  • écris le blocage dans docs/journal.md (symptôme, pistes essayées, hypothèses) ;
  • propose 2 options à Adam et attends sa décision.

Arborescence du dépôt

ansible/          inventaire, playbooks, rôles
kolla/            globals.yml, inventaire all-in-one (passwords.yml : chiffré ou hors Git)
openstack/        modèles Heat, flavors, scripts de construction d'images
portail/          application Laravel
proxy-console/    service Go
labs/             copie de travail des labs YAML
design/           maquettes Claude Design (référence exacte, ne pas modifier)
docs/             journal.md, runbooks/, inventaire-serveur.md, post-mortems/

3. Les étapes

Étape 1 : Reconnaissance (lecture seule)

  • Relève sur le serveur :
    • OS, noyau, CPU ;
    • virtualisation matérielle (vmx) et /dev/kvm ;
    • RAM, disques, RAID ;
    • interfaces réseau, IP, passerelle, DNS.
  • Vérifie quelle version stable de Kolla-Ansible supporte Debian 12, et choisis-la.
  • Écris docs/inventaire-serveur.md.

Critère : inventaire écrit, KVM disponible, version de Kolla-Ansible choisie et justifiée.

Étape 2 : Durcissement (Ansible depuis le Mac)

  • Installe Ansible sur le Mac s'il manque. Crée ansible/inventory.ini et un rôle durcissement :
    • utilisateur d'administration, sudo ;
    • SSH par clé uniquement, PermitRootLogin no ;
    • fail2ban, unattended-upgrades, chrony, fuseau Europe/Paris ;
    • pare-feu nftables : seuls 22, 80 et 443 ouverts sur l'IP publique.
  • Attention : kolla-ansible bootstrap-servers peut désactiver le pare-feu. Prévois-le dès maintenant (réglage Kolla adapté, ou rôle nftables rejoué après Kolla).

Critère :

  • connexion par clé OK ;
  • connexion par mot de passe refusée, root refusé ;
  • depuis le Mac, nmap 163.172.4.195 ne montre que 22, 80 et 443.

Étape 3 : Sauvegardes vers Dedibackup

  • Dedibackup (dedibackup-dc3.online.net) est limité à 1000 fichiers et passe par du FTP non chiffré.
  • Fais des archives quotidiennes tar.zst, chiffrées avec age avant l'envoi.
  • Rotation : 7 quotidiennes et 4 hebdomadaires.
  • À sauvegarder : /etc, /home, /root, /etc/kolla (plus tard), les dumps MariaDB de l'app (plus tard), le dossier du portail.
  • Programmation : timer systemd quotidien.
  • Identifiants dans /root/.dedibackup (variables DEDIBACKUP_HOST, DEDIBACKUP_USER, DEDIBACKUP_PASS). La clé privée age ne quitte jamais le serveur : rappelle-moi d'en garder une copie hors du serveur.
  • Écris docs/runbooks/sauvegardes.md.

Critère : une sauvegarde est envoyée et un fichier est restauré avec succès dans /tmp.

Étape 4 : Réseau pour OpenStack (accord d'Adam obligatoire)

L'objectif est de ne jamais mettre Neutron sur l'interface publique.

  • Crée une interface dummy0 avec une IP privée (par exemple 10.10.0.1/24). Elle servira à network_interface et kolla_internal_vip_address (Kolla sans HAProxy).
  • Crée une paire veth. neutron_external_interface utilise un bout de la paire. L'autre bout porte la passerelle du réseau externe OpenStack, avec un NAT masquerade nftables.
  • Rends le tout persistant avec systemd-networkd et le rôle Ansible reseau-openstack.
  • Applique la règle anti-coupure SSH, puis redémarre, avec l'accord d'Adam.

Critère : après redémarrage, le SSH répond, dummy0 et la paire veth sont présents, et Internet reste joignable depuis le serveur.

Étape 5 : Kolla-Ansible tout-en-un

  • Dans un venv Python : la version de Kolla-Ansible choisie à l'étape 1, puis kolla-ansible install-deps.
  • Dans kolla/globals.yml (vérifie les noms exacts dans la doc de la version), les idées sont :
    • enable_haproxy: "no", enable_horizon: "no" ;
    • enable_heat: "yes", enable_nova_serialconsole_proxy: "yes" ;
    • nova_compute_virt_type: "kvm" ;
    • enable_prometheus et enable_grafana si c'est le plus simple pour l'étape 7.
  • Mots de passe : kolla-genpwd. Jamais de passwords.yml en clair dans Git.
  • Lance bootstrap-servers, prechecks, deploy, puis post-deploy. Installe le client openstack.
  • Rejoue le rôle pare-feu si Kolla l'a modifié.

Critère :

  • openstack service list et openstack compute service list sont OK ;
  • aucune API OpenStack n'est joignable depuis l'IP publique (vérifie avec nmap).

Étape 6 : Première VM en console série

  1. Crée le projet paillasse et un utilisateur de service avec des application credentials, stockés dans un fichier chmod 600.
  2. Construis l'image paillasse-debian avec virt-customize, à partir de l'image cloud Debian 12 officielle, en suivant la section « Image commune » de paillasse-contenus/README.md.
  3. Propriété de l'image : hw_qemu_guest_agent=yes.
  4. Crée la flavor eleve.petit : 1 vCPU, 768 Mo, 5 Go.
  5. Démarre une VM et récupère l'adresse de sa console série avec l'API Nova remote-consoles (protocole serial).

Critère : un petit script (websocat ou Python) connecté au WebSocket de la console série affiche l'invite de connexion, et une commande tapée s'exécute.

Étape 7 : Supervision et alertes

  • Prometheus, node_exporter et Grafana : ceux de Kolla si possible.
  • Uptime Kuma en conteneur Docker, écoutant sur 127.0.0.1.
  • Alertes : disque > 80 %, RAM > 90 %, service OpenStack arrêté, dernière sauvegarde de plus de 26 h, serveur injoignable.
  • Notifications par webhook : l'URL est un secret, demande-la-moi.
  • Tant qu'il n'y a pas de domaine, accès par tunnel SSH uniquement.

Critère : une alerte de test est reçue par Adam.

Étape 8 : Portail Laravel

  • Nginx et PHP-FPM, avec une version de PHP compatible avec la dernière version de Laravel.
  • MariaDB et Redis pour l'app en Docker Compose, liés à 127.0.0.1, sur des ports différents de ceux de Kolla (par exemple 3307 et 6380).
  • Laravel avec Blade et Vite, dans portail/.
  • Design :
    • le dossier design/ est la référence exacte. Il contient déjà des vues Blade (design/resources/views/) et des routes (design/routes/web.php) : reprends-les telles quelles ;
    • le JS existant (design/js/, dont paillasse-terminal.js) et le CSS (design/css/) sont conservés ;
    • compare chaque page avec une capture Playwright de design/, et corrige jusqu'à ce qu'elles soient identiques ;
    • si design/ est absent, arrête-toi et demande-le-moi.
  • Modèles :
    • Etablissement, User (prof), Classe, Eleve (identifiant, code secret haché, nom complet facultatif) ;
    • Lab, Seance, LabSession (élève, lab, id de la stack ou des VM, état, expiration).
  • Connexion, sur un seul écran /connexion :
    • le prof avec un e-mail et un mot de passe ;
    • l'élève avec son identifiant prénom.initiale (avec un numéro pour les homonymes) et un code secret de 6 caractères.
  • Le prof crée une classe, saisit ou importe en CSV la liste des élèves, et génère leurs identifiants et codes (fiches imprimables avec QR code).
  • Une seule session active par compte : la précédente est coupée et le prof est alerté.
  • Salle d'attente de séance : le prof admet les élèves.
  • Tests Pest pour la connexion, la session unique et la création de classe.

Critère : les tests Pest passent, et un élève fictif se connecte et voit son tableau de bord, identique à la maquette.

Étape 9 : Proxy console Go et xterm.js

  • Service Go dans proxy-console/, écoutant sur 127.0.0.1:8081, lancé par systemd. Nginx relaie /console en WebSocket.
  • Déroulé :
    1. Laravel demande à Nova l'adresse de la console série.
    2. Laravel la range dans Redis sous un jeton aléatoire, pour 5 minutes.
    3. Le navigateur reçoit uniquement ce jeton.
    4. Le proxy lit Redis, ouvre le WebSocket vers nova-serialproxy et recopie les octets dans les deux sens.
  • La console série n'accepte qu'une seule connexion : c'est le proxy qui duplique le flux vers la vue du prof, en lecture seule.
  • Côté front : xterm.js, avec un onglet par VM du lab.

Critère : dans le navigateur, l'élève tape ip a dans sa VM et voit le résultat, et un autre élève ne peut pas ouvrir cette console.

Étape 10 : Liaison Laravel ↔ OpenStack et lab 08

  • Client OpenStack dans Laravel, avec le client HTTP de Laravel ou php-opencloud/openstack, via les application credentials.
  • Modèle Heat du lab 08, d'après paillasse-contenus/08-deux-lan-routeur/lab.yaml :
    • réseaux de lab sans DHCP, avec port_security_enabled: false ;
    • aucun routeur vers l'extérieur : les VM des labs n'ont pas accès à Internet.
  • Jobs dans une file Redis :
    • CreerStackLab ;
    • SupprimerStackLab ;
    • expiration automatique des sessions.

Critère : le bouton « Lancer le lab 08 » crée une stack CREATE_COMPLETE, et les terminaux de chacune de ses VM s'ouvrent.

Étape 11 : Stock chaud et correction automatique

Stock chaud

  • Une liste Redis de stacks prêtes par lab, remplie par un job.
  • Taille par défaut : calculée par toi selon la RAM disponible, et configurable.
  • Pré-remplissage automatique 10 minutes avant une séance.

Correction

  • Un petit service correcteur, lancé par systemd, écoutant sur un socket Unix.
  • Il accepte uniquement : uuid d'instance, id du lab, id de la vérification.
  • Il exécute les vérifications définies dans le YAML du lab avec guest-exec de l'agent QEMU (par virsh, dans le conteneur nova_libvirt).
  • Jamais de commande libre venant du portail.
  • Laravel enregistre la note et le message pour l'élève.

Critère :

  • un lab pris dans le stock s'ouvre en moins de 5 s ;
  • une configuration correcte donne « réussi » ;
  • une configuration incorrecte donne « échoué » avec un message utile.

Étape 12 : Domaine, HTTPS et sécurité

Avant de commencer, arrête-toi et demande-moi le domaine (je l'achète et je configure le DNS moi-même).

  • Certificat Let's Encrypt (certbot, plugin Nginx) et renouvellement automatique.
  • En-têtes de sécurité : HSTS, CSP adaptée à xterm.js, etc.
  • Grafana et Uptime Kuma derrière authentification, et la page de statut publique.
  • Vérifie à nouveau que seuls 22, 80 et 443 sont ouverts, et qu'aucune VM élève n'a d'IP publique ni d'accès sortant.

Critère : le domaine affiche un certificat valide en HTTPS, et le parcours prof → élève → lab → correction fonctionne.

Étape 13 : Tests de capacité et de résistance

  • Lance 20 VM eleve.petit en même temps. Mesure la RAM, le CPU et les temps de démarrage, puis écris docs/capacite.md.
  • Restaure une sauvegarde complète du portail sur une copie de test.
  • Avec l'accord d'Adam, redémarre le serveur : tout doit revenir seul (Kolla, portail, proxy, correcteur, supervision).

Critère :

  • les chiffres de capacité sont écrits ;
  • la restauration est réussie ;
  • tout est revenu seul après le redémarrage.

Étape 14 : Pages légales et RGPD

Pages à créer :

  • /legal : mentions légales (Esquisse Formation ; le SIREN et l'adresse sont à compléter par Adam) ;
  • la politique de confidentialité ;
  • la page de statut.

Job de purge : suppression 90 jours après l'archivage d'une classe.

Interdits :

  • logo Marianne ou ministère ;
  • drapeau de l'UE ;
  • faux « logo RGPD » ;
  • KPI inventés ou faux clients.

Formulations autorisées : « Hébergé en France » (c'est vrai), et « Conforme au RGPD » avec un lien vers la politique de confidentialité.

Critère : pages en ligne, et un test Pest prouve que la purge fonctionne.

Étape 15 : Visio de classe (seulement après validation des étapes 1 à 11)

  • Jitsi auto-hébergé, derrière Nginx.
  • Accès par jeton JWT émis par Laravel : seuls les élèves de la classe entrent, avec un nom non modifiable.
  • Le prof est seul modérateur. Tout le monde arrive micro et caméra coupés, et seul le prof donne la parole.
  • Main levée synchronisée avec celle de l'app, salle d'attente, chat désactivé par défaut.
  • Reprends exactement design/apercu-visio.html et design/resources/views/visio.blade.php.

Critère : un prof et deux élèves fictifs rejoignent la visio ; un élève lève la main, le prof lui donne la parole, et un intrus sans jeton ne peut pas entrer.

Étape 16 : Nettoyage

Propose de retirer /etc/sudoers.d/90-adam.


4. Fin de mission

Écris docs/bilan.md, avec :

  • ce qui fonctionne, avec la preuve de chaque critère ;
  • le temps réel passé par étape ;
  • la capacité mesurée (nombre d'élèves en même temps) ;
  • les dettes et les risques connus ;
  • ce qu'Adam doit faire lui-même : secrets à changer, comptes à créer, tests avec un prof, etc. ;
  • les prochaines étapes du bloc A :
    • self-healing et post-mortem ;
    • montée de version d'OpenStack ;
    • pilote avec une vraie classe.

Commence par l'étape 1.