Projet personnel

Collaborative Code Editor

Éditer du code à plusieurs, en temps réel, et l'exécuter en sécurité

  • Temps réel · CRDT
  • 9 langages exécutables
  • Open source · MIT
Interface de l'éditeur : code partagé, curseurs de plusieurs participants, liste des utilisateurs et chat

< Description />

Collaborative Code Editor est un éditeur de code collaboratif temps réel, inspiré de CodeSandbox. On crée une room, on partage le lien, et plusieurs personnes écrivent dans le même fichier simultanément — avec les curseurs de chacun visibles en direct, un chat intégré, et la possibilité d'exécuter le code sans jamais quitter le navigateur.

L'intérêt du projet n'est pas l'éditeur en lui-même, mais les deux problèmes difficiles qu'il oblige à résoudre : faire converger un même document entre plusieurs machines sans jamais perdre une frappe ni bloquer les autres, et exécuter du code arbitraire envoyé par un inconnu sans exposer la machine hôte.

La convergence repose sur un CRDT (Yjs) : chaque édition est une opération commutative, les modifications concurrentes fusionnent sans conflit et sans serveur d'arbitrage. L'exécution passe par un conteneur Docker éphémère, sans réseau et sous quotas, détruit à la fin de chaque run.

  • Rooms partageables

    Une room se crée depuis un seul lien, sans compte ni installation. Le lien suffit à rejoindre la session.

  • Édition simultanée

    Fusion sans conflit via CRDT : deux personnes peuvent taper au même endroit, le document converge toujours.

  • Curseurs et présence

    Chaque participant a un nom, une couleur et un curseur suivis en direct, avec la liste des personnes connectées.

  • Exécution sandboxée

    Neuf langages exécutés dans des conteneurs Docker jetables, la sortie étant diffusée à toute la room.

  • Chat par room

    Une discussion textuelle par session, pour se coordonner sans quitter l'éditeur.

  • Undo collaboratif et replay

    Annuler/rétablir sans écraser le travail des autres, et rejouer l'historique d'édition d'une room a posteriori.

< Architecture />

Monorepo npm workspaces séparant le client du serveur. Le client édite un document Yjs local ; un provider Socket.io écrit sur mesure le synchronise avec le serveur, qui relaie les mises à jour aux autres participants et maintient un Y.Doc par room.

La synchronisation suit une poignée de main en deux temps : le client envoie son state vector, le serveur répond avec les seules mises à jour manquantes, puis chaque édition ultérieure est diffusée en incrémental. Redis porte l'état volatil (présence), PostgreSQL les snapshots qui permettent de recharger une room et d'en rejouer l'historique.

  • React · Vite

    Client monté autour de Monaco Editor, l'éditeur qui fait tourner VS Code

  • Yjs · CRDT

    Fusion sans conflit, avec un provider Socket.io développé pour le projet

  • Node.js · Express

    API REST des rooms et du replay, serveur Socket.io pour le temps réel

  • Redis

    État volatil : présence et données de session, non persistées

  • PostgreSQL

    Persistance des rooms et des snapshots Yjs qui alimentent le replay

  • Docker

    Un conteneur éphémère par exécution, plus Redis et PostgreSQL en local

< Sécurité de l'exécution />

Laisser un inconnu lancer du code sur sa machine est le vrai risque du projet. La réponse est une défense en profondeur : si une couche cède, la suivante tient.

  • Filtre heuristique

    Avant même de démarrer un conteneur, des motifs par langage sont rejetés : eval, boucles infinies, fork bombs, accès réseau, exécution de processus, réflexion JVM.

  • Aucun réseau

    Le conteneur démarre avec --network none : le code exécuté ne peut ni appeler l'extérieur ni exfiltrer quoi que ce soit.

  • Privilèges retirés

    --cap-drop ALL et no-new-privileges : toutes les capacités Linux sont abandonnées et aucune escalade n'est possible.

  • Quotas stricts

    CPU, mémoire et nombre de processus plafonnés, avec des budgets élargis pour les langages compilés qui doivent tenir leur compilation.

  • Délai maximal

    Un timeout dur tue le conteneur, doublé d'une suppression forcée pour le cas où il survivrait au signal.

  • Espace jetable

    Le code est écrit dans un dossier temporaire monté en lecture seule, effacé après chaque exécution, réussie ou non.

< Langages supportés />

Neuf langages, chacun avec sa propre image Docker. Les interprétés exécutent le fichier directement ; les compilés compilent dans un chemin temporaire accessible en écriture, puis lancent l'artefact produit.

  • JavaScript
  • TypeScript
  • Python
  • Lua
  • Go
  • C++
  • Java
  • Kotlin
  • C#

Chaque exécution part d'un conteneur neuf, sans cache de compilation : les toolchains lourdes (JDK, .NET, Kotlin) reçoivent donc des quotas CPU, mémoire et temps plus larges pour compiler dans le bac à sable. C'est le prix de l'isolation, et un compromis assumé.