18 KiB
Prompt Claude Code : déploiement complet de Syllabus
À coller dans Claude Code, lancé depuis la racine du dépôt
Paillassesur 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.pngetdesign/images/logo-syllabus-icone.png. - Les noms techniques internes restent tels quels : le dépôt
Paillasse, le dossierpaillasse-contenus/, l'imagepaillasse-debian, le projet OpenStackpaillasse. 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'aliaspaillassedans~/.ssh/config, avecIdentitiesOnly yespour n'utiliser que cette clé. sudofonctionne sans mot de passe pouradam(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
- Ne jamais toucher à
tripoli-cloudni à aucune autre machine que le serveur Syllabus et le Mac d'Adam. - Aucun secret dans le code, dans Git, dans le journal ou dans la conversation.
- Les secrets vivent dans des fichiers
chmod 600sur le serveur, ou dans un.envignoré par Git. - Les secrets Ansible passent par Ansible Vault. Le mot de passe du vault reste hors du dépôt.
- Kolla :
passwords.ymln'est jamais commité en clair.
- Les secrets vivent dans des fichiers
- 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.
- suppressions (
- 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. - Tu ne commandes et ne paies jamais rien.
- Élèves mineurs : minimisation des données.
- Un élève = un identifiant
prénom.initialeet 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.
- Un élève = un identifiant
- 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.
- chaque configuration passe par un rôle Ansible idempotent dans
- 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.
- Documentation en français (docs, commentaires, messages de commit).
2. Méthode de travail
Pour chaque étape
- Annonce l'étape et son critère de réussite.
- Fais le travail.
- Vérifie le critère par un test réel et montre la sortie.
- É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.
git commit, puisgit push.- 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.iniet un rôledurcissement:- utilisateur d'administration,
sudo; - SSH par clé uniquement,
PermitRootLogin no; - fail2ban,
unattended-upgrades, chrony, fuseauEurope/Paris; - pare-feu nftables : seuls 22, 80 et 443 ouverts sur l'IP publique.
- utilisateur d'administration,
- Attention :
kolla-ansible bootstrap-serverspeut 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.195ne 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(variablesDEDIBACKUP_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
dummy0avec une IP privée (par exemple10.10.0.1/24). Elle servira ànetwork_interfaceetkolla_internal_vip_address(Kolla sans HAProxy). - Crée une paire veth.
neutron_external_interfaceutilise 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_prometheusetenable_grafanasi c'est le plus simple pour l'étape 7.
- Mots de passe :
kolla-genpwd. Jamais depasswords.ymlen clair dans Git. - Lance
bootstrap-servers,prechecks,deploy, puispost-deploy. Installe le clientopenstack. - Rejoue le rôle pare-feu si Kolla l'a modifié.
Critère :
openstack service listetopenstack compute service listsont OK ;- aucune API OpenStack n'est joignable depuis l'IP publique (vérifie avec
nmap).
Étape 6 : Première VM en console série
- Crée le projet
paillasseet un utilisateur de service avec des application credentials, stockés dans un fichierchmod 600. - Construis l'image
paillasse-debianavecvirt-customize, à partir de l'image cloud Debian 12 officielle, en suivant la section « Image commune » depaillasse-contenus/README.md. - Propriété de l'image :
hw_qemu_guest_agent=yes. - Crée la flavor
eleve.petit: 1 vCPU, 768 Mo, 5 Go. - Démarre une VM et récupère l'adresse de sa console série avec l'API Nova
remote-consoles(protocoleserial).
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/, dontpaillasse-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.
- le dossier
- 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 sur127.0.0.1:8081, lancé par systemd. Nginx relaie/consoleen WebSocket. - Déroulé :
- Laravel demande à Nova l'adresse de la console série.
- Laravel la range dans Redis sous un jeton aléatoire, pour 5 minutes.
- Le navigateur reçoit uniquement ce jeton.
- 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.
- réseaux de lab sans DHCP, avec
- 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-execde l'agent QEMU (parvirsh, dans le conteneurnova_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.petiten même temps. Mesure la RAM, le CPU et les temps de démarrage, puis écrisdocs/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.htmletdesign/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.