Cosmos Corp : un RPG pour apprendre le DevOps en faisant vraiment le job
J’ai suivi pas mal de tutos DevOps. Le format est presque toujours le même : une démo, quelques commandes à recopier, et quelques jours plus tard il n’en reste rien. Pas parce que le sujet est trop dur, mais parce qu’un terminal, un dépôt git ou un cluster, ça s’apprend en le manipulant, pas en lisant un article dessus.
Alors j’ai pris le problème par l’autre bout. J’ai construit un petit RPG desktop, Cosmos Corp: Day One, qui place le joueur dans la peau d’un ingénieur à son premier jour. Pas de faux shell, pas de QCM. On reçoit un objectif, un vrai terminal branché sur un vrai conteneur, et un mentor qui donne le modèle mental avant de lâcher le joueur dans le grand bain. L’objectif ne passe au vert que quand le travail est réellement fait.
Le pari : une vraie sandbox, pas une simulation
Le pitch est simple. Première semaine chez Cosmos Corp. Sage, un SRE barbu qui a vu passer son lot d’incidents, écrit sur Slack : « Tout est à toi. T’inquiète pas de casser des trucs, c’est fait pour. » À partir de là, on travaille.
Le jeu est découpé en quatre chapitres qui suivent la vraie trajectoire d’un ingénieur qui débute :
- Le terminal : navigation, fichiers, pipes, recherche, processus, permissions, variables d’environnement.
- La codebase : commit, branche, merge, rebase, résolution de conflits, remotes.
- Le cluster : pods, deployments, services, config, debug sur Kubernetes.
- La boucle GitOps : FluxCD, reconciliation, correction de drift, la boucle push-to-deploy.
En tout, vingt-huit missions, du premier cd jusqu’au commit qui redéploie un cluster tout seul. Et à chaque étape, la même règle : bash, git, kubectl et flux sont les vrais outils, pas des imitations.
Quatre chapitres, du terminal à GitOps. Chacun garde la progression et donne accès à son cours de théorie.
Sous le capot : du réel, y compris le cluster
C’est là que le projet devient intéressant à raconter. Chaque chapitre lance son propre conteneur Docker. Pour le terminal et git, un conteneur suffit. Pour les chapitres Kubernetes et Flux, il faut un vrai cluster, alors le jeu démarre un k3s mono-nœud à l’intérieur d’un conteneur privilégié, avec k3s en PID 1. C’est un vrai control plane, pas un mock : on tape kubectl describe, on obtient de vrais Events écrits par le kubelet.
Un détail qui compte pour la crédibilité : le cluster tourne en root parce que k3s a besoin de monter des cgroups et de bricoler iptables, mais le shell du joueur, lui, est attaché en tant qu’utilisateur non privilégié via docker exec -u dev. Autrement dit, la mémoire musculaire qu’on construit correspond à une vraie machine, pas à un compte root permissif.
Côté application, l’ossature est en Tauri 2 : un backend Rust qui gère le cycle de vie Docker et un bridge PTY, et une interface en React 19. Le terminal affiché, c’est xterm.js relié au vrai PTY du conteneur. Et pour les deux derniers chapitres, une cluster view en React Flow interroge l’état toutes les deux secondes et redessine les pods en direct. Quand on répare un déploiement cassé, on voit le pod passer de rouge à orange à vert. C’est le reconciliation loop qui devient visible, et c’est étonnamment satisfaisant.
Une mission k8s en cours : le vrai terminal en bas, la cluster view à gauche qui suit les pods en direct, les objectifs et les indices à droite.
Le validateur qui ne se laisse pas berner
Le cœur pédagogique, c’est le validateur. Un objectif ne se coche pas parce qu’on a cliqué « suivant », il se coche parce qu’un script a vérifié que le travail était fait. Et il y a deux façons de vérifier, choisies exprès selon la nature de la tâche.
Pour les commandes read-only, le script lit l’historique bash. Prenons le debug d’un pod : kubectl describe et kubectl logs ne changent rien à l’état du monde, donc les valider sur l’état du cluster n’aurait aucun sens. Ce qui compte, c’est d’avoir eu le réflexe de les lancer. Le validateur grep donc l’historique, et accepte toutes les variantes raisonnables de la commande.
Pour les résultats, il interroge l’état réel. Sur la mission de crashloop, le setup applique volontairement un Deployment avec une image qui n’existe pas (nginx:cosmos-broken-do-not-exist), ce qui déclenche un vrai ImagePullBackOff. L’objectif « répare l’image » ne passe au vert que lorsqu’un pod portant le bon label est réellement Running et que son conteneur reporte ready=true, vérifié en jsonpath. Impossible de copier-coller sa sortie de prison. Soit le cluster est réparé, soit il ne l’est pas.
Le validateur tourne toutes les deux secondes, ce qui fait qu’un objectif s’illumine à peu près au moment où on réussit. C’est ce feedback immédiat, adossé à un état réel, qui fait que ça colle.
Sage, ou pourquoi j’ai écrit une spec de personnage avant la première ligne
Un mentor qui balance la solution tue l’apprentissage. Un mentor qui laisse ramer aussi. Sage marche sur cette ligne parce que je l’ai spécifié avant d’écrire le moindre dialogue.
Il y a une bible de personnage dans le repo. Elle fixe sa façon d’enseigner : le modèle mental d’abord (« git, c’est juste un outil qui prend des snapshots »), puis un indice si on bloque, puis la réponse, parce que c’est comme ça qu’on apprend vraiment sur le tas. Elle fixe aussi son caractère : il assume ses propres bêtises (« quelqu’un, pas moi évidemment, a déployé un truc cassé dans ton namespace »), il ne prend jamais le joueur de haut, il reste présent physiquement dans le décor plutôt que d’être une voix désincarnée.
Écrire cette bible avant le code, c’est exactement le même geste que rédiger une build spec pour un agent : on contraint la voix en amont pour que tout ce qui en découle soit cohérent, au lieu de rattraper le ton dialogue par dialogue. Et il y a un clin d’œil que je ne pouvais pas laisser passer : la charte d’écriture de Sage interdit l’em dash et impose le tiret espacé. C’est ma propre règle, promue en canon d’un personnage. C’est aussi pour ça que cet article n’en contient aucun.
Le cours de théorie du chapitre, relisible à tout moment. L’avatar de Sage veille en haut à gauche.
Le contenu comme donnée
Dernière brique, invisible mais décisive pour itérer : rien de tout ça n’est codé en dur. Une mission, c’est un dossier avec un mission.yaml, un dialogue.md, un outro.md, et deux scripts shell, setup.sh qui prépare le décor et check.sh qui valide. Ajouter ou corriger une mission ne demande pas de recompiler l’application, juste d’éditer des fichiers texte. Ça rend le jeu ouvrable aux contributions et, franchement, ça rend l’écriture de nouvelles missions presque agréable.
Essayer
Cosmos Corp: Day One est une app macOS, construite avec Tauri, sous licence MIT, packagée en .dmg via une pipeline GitHub Actions. Il faut Docker qui tourne, et au premier lancement on se donne un nom, on choisit un rôle, on reçoit son badge Cosmos Corp, et la première journée commence.
Le repo est là : github.com/gothub97/cosmos-corp.
Il reste des chapitres à écrire et des outils à ajouter au parcours, et le projet est grand ouvert aux contributions : infra, data, frontend, contenu pédagogique ou n’importe quel autre scope technique, il y a de quoi faire. Le socle, lui, est déjà posé : un endroit où l’on peut casser de vraies choses sans conséquence.
Lead internal product & AI engineer chez Flowdesk. Je développe avec Claude Code au quotidien et j'écris à ce sujet ici.