Quoi de nouveau
Chaque fonctionnalité, correctif et amélioration livrés dans sh0. 29 phases, 2 audits de sécurité, plus de 90 sessions d'ingénierie -- construit de A à Z en Rust.
v1.10.0 : le port que vous choisissez atteint votre application
Deux versions depuis la v1.8.0. Celle qui compte : le port saisi dans le formulaire de déploiement atteint enfin l’application elle-même. Un champ pré-rempli à 80 neutralisait en silence deux correctifs déjà livrés, et aucun test unitaire ne pouvait le voir — chaque couche était juste isolément. Plus un filet sous le second facteur, une page de connexion 99,5 % plus légère au retour, et un domaine propre pour le panneau.
Endpoints d'API
Outils MCP
Tests réussis
Templates de déploiement
Chronologie du développement
Du premier commit à la mise en production -- l'historique de construction complet
v1.10.0 : le port que vous choisissez atteint votre application
- Le port que vous saisissez atteint désormais votre application. Un formulaire de déploiement pré-rempli à 80 faisait déclarer un port explicite à toutes les applications, ce qui neutralisait d’un coup deux correctifs déjà livrés : la valeur arrivait bien jusqu’à `EXPOSE`, au port publié et à la sonde de santé, mais jamais jusqu’au processus. Le champ part maintenant vide sur les quatre voies — envoi d’archive, git, Dockerfile, image Docker —, douze gabarits de plus posent `ENV PORT`, les gabarits Java posent `ENV SERVER_PORT` pour Spring Boot, et quand vous ne dites rien c’est l’`EXPOSE` de l’image qui décide.
- Une sonde de santé qui échoue nomme le port réellement ouvert : « Nothing answers on port 3000; the container listens on 9091 ». Un Dockerfile qui ne déclare aucun `EXPOSE` dit sur quel port il est retombé au lieu d’y retomber en silence, et un port qui contredit l’`EXPOSE` de l’image est signalé avant le déploiement, pas après.
- Le second facteur gagne un filet et une mémoire. Les codes de secours survivent au rechargement du profil qui les effaçait, se régénèrent depuis le panneau contre un code d’authentificateur frais, et chaque connexion protégée — par mot de passe ou par Google — inscrit une ligne datée au journal d’audit, refus compris.
- Le poids. Un visiteur télécharge une langue au lieu de cinq, et les actifs statiques sont mis en cache, revalidés par ETag et précompressés en brotli et gzip. La page de connexion est passée de 1106 Ko à 168 Ko à la première visite, et à 763 octets à la suivante.
- La patte vers le nuage devient propre à chaque instance. Chaque installation porte maintenant son identifiant et un canal chiffré vers le proxy, au lieu d’une clé partagée par tous, et le panneau peut déménager sur un domaine à vous — annoncé dans le panneau, avec l’ancienne adresse nommée au moment où elle cesse de répondre.
- Et le reste : onze boucles de crash de gabarits lues et classées — huit corrigées, deux retirées —, un nom d’hôte de service qui ne sert plus le contenu du conteneur principal, un changement de mot de passe qui révoque vraiment les autres sessions, et les origines de Google Identity autorisées par la politique de sécurité du contenu. Chaque correctif porte ici une observation datée sur une machine réelle ; le compteur « corrigé en code, jamais vu marcher » reste à zéro.
v1.9.0 : un domaine pour le panneau, et des pannes qui se distinguent
- Le panneau reçoit un chemin d’adoption pour son propre domaine. Le poser ou le retirer nomme l’adresse qui va cesser de répondre, un retrait qui échoue s’affiche en rouge au lieu d’un faux succès, et la bascule n’est proposée qu’une fois le certificat actif.
- Trois pannes différentes du proxy rendaient le même 404. Elles rendent maintenant trois codes distincts, et un domaine connu dont l’amont manque ne se déclare plus inconnu.
- Les trois pastilles de statut du tableau de bord parlent la langue du panneau au lieu de répondre toujours en anglais.
- Le document OpenAPI annonce la version du binaire qui le sert réellement, au lieu d’un numéro figé dans les sources.
v1.8.0 : projets multi-services, sauvegarde de l’instance, mise à jour autonome
- Les services d’un projet se joignent par leur nom. Un alias interne stable (`<app>.internal`) suit le conteneur quand son adresse change : au redéploiement, l’IP passe de 172.20.0.4 à 172.20.0.5, le nom répond toujours, et aucune URL n’est à modifier. Variables partagées au niveau du projet, ordre de démarrage, et import Compose de bout en bout.
- sh0 se sauvegarde lui-même. `sh0 instance backup` prend un instantané cohérent de sa propre base par `VACUUM INTO` — jamais une copie de fichier, la base tourne en WAL —, laisse la clé maîtresse hors de l’artefact mais y inscrit son empreinte, et `sh0 instance restore` la remet sur une machine neuve. Plus l’import de projet entre instances et la rotation de la clé de chiffrement des secrets.
- Le diagnostic arrive pendant l’attente, plus après elle. Les journaux du conteneur s’affichent pendant la sonde de santé, chaque étape de construction dit ce qu’elle a coûté, une ligne de contexte ouvre le journal de build, l’état du disque est lisible par l’API, le ramassage des couches pendantes se consigne, et une alerte précède la saturation au lieu de la constater.
- La voie archive, pour qui déploie son propre code sans dépôt git. Le type d’application est accepté à l’envoi, la commande de démarrage est réglable, un échec de construction affiche sa ligne de contexte et sa cause, et un worker sans port n’immobilise plus l’envoi pendant 300 secondes en laissant un conteneur derrière lui. Node, Python, Go et Rust déployés par les deux chemins, archive et Dockerfile.
- Une installation se répare et se met à jour sans SSH. `sh0 update` choisit son canal — stable ou préliminaire —, vérifie l’empreinte du binaire téléchargé, et revient en arrière dans les deux sens ; une mise à jour ne coupe plus les applications déployées. `sh0 doctor` vérifie Docker, les ports, le disque, les certificats et l’horloge, et sort en erreur quand quelque chose manque. Désinstallation propre, et unité systemd durcie.
- Le courrier et le stockage de fichiers deviennent utilisables : Stalwart v0.16 piloté par JMAP, les rotations DKIM automatiques suivies dans le DNS, un identifiant refusé qui le dit au lieu de se taire. Et quarante-huit correctifs dont chacun porte une observation datée sur une machine réelle — pas un test unitaire, une machine.
v1.7.1 : licences datées, renouvellement automatique
- Une licence porte désormais une date d’expiration alignée sur l’abonnement qui l’a payée. Les abonnements mensuels émettaient des licences perpétuelles : un mois payé donnait un accès à vie.
- Une passe quotidienne récupère la clé renouvelée, vérifie sa signature et remplace celle qui est stockée — rien à faire au renouvellement d’un abonnement. Un abonnement résilié révoque la clé et le plan retombe à la passe suivante.
- Défi DNS-01 par domaine : un serveur derrière un CDN qui termine le TLS (nuage orange Cloudflare) obtient maintenant un certificat sans désactiver sa protection, au lieu de brûler son budget Let’s Encrypt en boucle.
- Routage et cycle de vie : un redémarrage qui change le port publié met à jour la route Caddy ; une application qui n’écoute pas le port configuré n’est plus déclarée saine tout en rendant 502 ; un redéploiement ne laisse plus l’ancien conteneur vivant à côté du nouveau.
- Les limites de ressources suivent le matériel et non la licence sur une installation auto-hébergée, et les rafraîchisseurs de statut SSL savent désormais rétrograder un certificat, pas seulement le promouvoir.
- La v1.7.1 est requise pour toute licence achetée après le 9 septembre 2026. Les versions antérieures ne savent pas récupérer un renouvellement et s’arrêtent à la fin de la première période.
v1.7.0 : une licence est un document signé
- Une clé de licence est désormais un document signé en Ed25519 qui porte son propre plan, et non une chaîne dont on croyait le préfixe. Toute chaîne non reconnue activait le plan Pro.
- La vérification est hors ligne : la clé publique est embarquée dans le binaire, donc un serveur isolé du réseau valide sa licence sans rien appeler.
v1.6.29 : correctifs du dogfooding -- parité .dockerignore, boucles de crash nommées, applications worker sans port
- Le contexte de build appliquait un motif .dockerignore sans barre oblique à toute profondeur (sémantique gitignore) ; Docker ancre chaque motif à la racine et seul **/ traverse. Un projet qui ignorait *.md et importait src/lib/resources/x.md construisait sur Easypanel et échouait sur sh0. sh0 applique désormais les règles de Docker ; sa propre liste générée écrit **/ explicitement.
- La boucle de santé ne lisait que State.Running. Un conteneur qui sort au démarrage sous unless-stopped est relancé en moins d'une seconde : il paraissait en marche, la sonde TCP échouait 300 s et le journal concluait à un délai dépassé. La boucle lit désormais RestartCount et Restarting, échoue en quelques secondes avec « crashed N times (exit code X) », arrête la boucle et nomme un OOM-kill. v1.6.30, le même jour : le code de sortie et l'indicateur OOM sont lus sur un instantané arrêté, Docker les remettant à zéro dès qu'il relance le conteneur -- un OOM-kill était annoncé « exit code 0 ».
- Les builds d'image utilisaient rm=true, qui ne supprime les conteneurs intermédiaires qu'en cas de succès ; chaque build raté laissait un conteneur Exited (1) au nom aléatoire qu'aucun nettoyage ne voyait. forcerm=true le supprime dans les deux cas.
- Les applications ont un type : web (inchangé) ou worker. Un worker ne résout aucun port, n'en publie aucun, ne reçoit ni route Caddy ni domaine de prévisualisation, et est sain dès qu'il reste en marche 15 s sans redémarrage. Celery, BullMQ et les processus de type cron se déploient sans être tués par une sonde à laquelle rien ne pouvait répondre. Les formulaires du tableau de bord exposent le choix.
Correctif v1.6.28 : l'API se figeait après deux redéploiements, et Sinatra répondait 403
- Le proxy de prévisualisation des adresses *.sh0.app gardait un garde de lecture DashMap vivant pendant toute la requête relayée, des heures pour un WebSocket. La purge du cache ajoutée au redéploiement bloquait alors un thread du runtime, un écrivain en attente bloquait les nouveaux lecteurs, et deux redéploiements sous trafic ont figé l'API avec des centaines de connexions en file. L'entrée du cache est désormais copiée avant tout await. Ne pas installer v1.6.27 en production.
- Le gabarit Ruby ne posait pas RACK_ENV : Puma démarrait en développement et l'autorisation d'hôte de Sinatra 4 rendait 403 à tout hôte public, alors que la sonde sur localhost passait. L'étage de production pose désormais RACK_ENV et RAILS_ENV à production.
Dix-neuf correctifs avant la production : contexte de build, connexion en clair, gabarits cassés et un installateur qui apporte Docker
- Contexte de build : un seul chemin de plus de 100 octets faisait échouer l'archive tar (l'en-tête GNU n'a pas de champ prefix) ; les chemins longs passent désormais en liens longs GNU. sh0 remplaçait aussi le .dockerignore du projet par sa propre liste, qui excluait tous les fichiers *.md : le fichier du projet est maintenant lu et complété, rien n'est imposé quand vous apportez votre Dockerfile, et le nombre de fichiers exclus est journalisé.
- Authentification : les cookies de session et de rafraîchissement portent Secure dès que la requête est arrivée en HTTPS, le port 80 répond un 308 vers https:// pour chaque hôte géré (les défis ACME passent toujours), l'installateur avertit que le premier login par IP est en clair et montre comment poser un domaine TLS sur le panneau, et les identifiants fictifs [email protected] disparaissent de l'installateur, du panneau et de la bannière du serveur.
- Piles : Go construit n'importe quelle version de go.mod (GOTOOLCHAIN=auto sur une image courante) et retrouve la racine de son module quand l'exécutable vit dans cmd/<nom> ; la configuration Bundler de Ruby atteint l'étage de production et Puma ne se lie qu'une fois ; .NET déduit son assemblage d'entrée à la construction, embarque curl pour sa sonde et tourne sur .NET 10 avec roll-forward. Un redéploiement ou une mise à l'échelle ne sert plus de 502 sur *.sh0.app pendant l'expiration du cache.
- Plateforme : une chaîne de licence inconnue est refusée au lieu d'activer Pro, le message de quota nomme le projet qui tient la place, `sh0 users list` lit la base locale sur la machine serveur, le proxy cloud route /api/* vers votre application au lieu de répondre à sa place, la tuile Active Servers du site lit de vrais battements, et get.sh0.dev installe Docker (ou échoue tôt avec la commande exacte) avant d'écrire l'unité systemd.
Deux correctifs avant le dogfooding : les conteneurs arrêtés survivent au nettoyage, les certificats à la demande n'exigent plus d'adresse
- Le nettoyage Docker horaire supprimait tous les conteneurs arrêtés gérés par sh0, y compris celui d'une application qui venait de planter : code de sortie, journaux et bouton Démarrer disparaissaient avec lui. Un conteneur arrêté est désormais conservé tant qu'une ligne de la base le référence ; seuls les vrais orphelins sont supprimés, et l'étape est sautée plutôt que jouée à l'aveugle si la base est illisible.
- L'émission de certificats à la demande n'était armée qu'avec une adresse ACME configurée, alors que le journal l'annonçait armée dans tous les cas -- sur une installation par défaut, Caddy émettait encore au chargement de la route. La politique attrape-tout à la demande est maintenant émise avec ou sans adresse ; l'adresse est simplement omise de l'émetteur quand il n'y en a pas.
Quatre lots partent ensemble : verrou d'instance, certificats à la demande et un statut SSL honnête
- `sh0 serve --port 9001` à côté d'une instance en marche installait des paquets, migrait la base et arrêtait le Caddy en place avant de découvrir qu'il ne pouvait pas démarrer. Un `flock` consultatif sur `sh0.pid` refuse désormais la seconde instance immédiatement, nomme le PID qui tient le répertoire et ne touche à rien. Un plantage laisse le fichier sans conséquence : le verrou meurt avec le processus, le démarrage suivant se poursuit.
- Les certificats sont émis à la demande, à la première poignée de main TLS, et non au chargement de la route -- un domaine dont le DNS n'est pas encore arrivé ne consomme plus une tentative Let's Encrypt. Un domaine reste `pending` tant que personne ne s'y connecte, et le badge dit désormais pourquoi (`ssl_pending_reason`). `ssl_expiry` valait toujours `null` ; il est lu dans le certificat sur disque. Un domaine `active` dont le certificat disparaît redevient `pending` après deux vérifications consécutives.
- Un conteneur en boucle de crash se disait `running` parce que seul l'état Docker était lu ; le compteur de redémarrages et le code de sortie entrent dans le verdict. Les chemins de flux Docker avaient des tampons mémoire sans borne -- sh0 a été tué par l'OOM à 7,3 Go en isolation -- et sont bornés. Le service d'une pile Compose qui recevait la route publique était choisi par l'ordre d'itération d'une table de hachage et pouvait changer d'un déploiement à l'autre ; il est déterministe.
- Les abonnements de la page de statut étaient morts par construction : le courriel de confirmation ne partait jamais alors que l'API affirmait le contraire. Le formulaire n'apparaît plus que si SMTP est configuré, les abonnements non confirmés sont plafonnés à 200 par domaine, et les lignes non confirmées sont purgées après 48 heures au lieu de 7 jours.
- « Lancer maintenant » sur une planification de sauvegarde exécutait une sauvegarde ponctuelle dont les archives échappaient à toute rétention ; il exécute désormais la planification elle-même. `restore-external` refusait un serveur de base jamais provisionné mais acceptait un serveur arrêté ; un serveur arrêté est aussi refusé en synchrone.
- L'importateur Compose exigeait `image:` et ignorait `build:` en silence ; il refuse désormais un service `build:` avec un message qui dit quoi faire. `--acme-email` est persisté comme `--panel-domain`, donc les renouvellements ne reposent plus sur un repli non vérifié. Et l'API n'annonce plus `panel_domain_ssl: "provisioning"` quand aucun approvisionnement n'a lieu.
Laravel se déploie, Compose parle la forme longue, et le port cesse de se perdre
- Toute application Laravel était construite comme du Node. Le détecteur rendait la main au premier `package.json`, et un Laravel moderne en embarque toujours un — c'est ainsi que Vite construit les assets —, si bien que le conteneur mourait sur `npm start`, script qu'aucun `package.json` Laravel ne contient. PHP l'emporte désormais quand le projet en porte la preuve : un cadre dans `composer.json`, un `artisan`, ou un `public/index.php`. Un projet Node qui se contente d'embarquer une bibliothèque PHP reste du Node.
- Faire détecter Laravel n'était pas le faire fonctionner. Le `package.json` devient une étape d'assets dans l'image PHP, Composer s'installe en deux temps parce qu'`artisan` n'existe pas encore au premier, une `APP_KEY` est frappée au premier déploiement et rangée chiffrée plutôt que cuite dans l'image, et les pilotes de session, de cache et de file d'attente prennent par défaut ceux qui n'exigent aucune base. Ce dernier point a été trouvé en déployant sur la boîte démo et en recevant un 500, pas en relisant du code.
- Les fichiers Compose écrits en forme longue pour `ports` et `volumes` échouaient à la lecture — `invalid type: map, expected a string`. Les deux clés acceptent maintenant l'une ou l'autre forme, entiers nus compris, et `read_only: true` devient un montage réellement non inscriptible. Un chemin hôte en forme longue reste refusé, mais le refus nomme désormais la politique au lieu de rendre une erreur de type.
- Sur la voie « image Docker », le port résolu servait à publier le conteneur puis était jeté : une application déployée sans port rendait `port: null`, et tout domaine qui lui était attaché routait vers 3000 pendant que le conteneur écoutait ailleurs. Le port est désormais enregistré sur toutes les voies, et le test ajouté ne vérifie pas la fonction — il vérifie ses appelants, si bien qu'une sixième voie qui l'oublierait ferait rougir la suite.
- La branche par défaut était la chaîne `main`, en dur. sh0 ne consultait jamais le distant : `laravel/laravel` (sur `13.x`) et tout dépôt resté sur `master` échouaient dès la première étape sur un message libgit2. Une branche vide signifie maintenant « celle du dépôt » : le clone résout le HEAD distant, écrit le nom trouvé sur l'application, et une branche inexistante est signalée en nommant celles qui existent.
Des gardes qui ne tournaient pas : CSRF, limitation de débit et dix commandes CLI cassées
- `sh0 push` ne pouvait pas déployer du tout. Onze exemptions CSRF étaient écrites avec un préfixe `/api/v1/` qu'axum avait déjà retiré : la garde ne correspondait à rien, et `/apps/upload` et `/auth/logout` répondaient 415. Les exemptions correspondent maintenant aux chemins que la couche voit réellement ; un déploiement par `sh0 push` a été mesuré à 25,7 s.
- `sh0 login` échouait avec `Unexpected response format`. La commande désérialisait encore un jeton que l'API ne renvoie plus -- elle pose des cookies HttpOnly et ne rend que le jeton CSRF. La connexion authentifie désormais, franchit le second facteur lorsqu'il est configuré, frappe une clé d'API et la persiste ; la clé porte le nom de l'utilisateur, celui de la machine et la date.
- Le limiteur de débit global était inerte, et le défaut a été mesuré sur l'instance de démonstration publique elle-même avant correction : 1100 lectures, 1100 réponses, pas un seul 429, face aux 1000 lectures et 500 écritures par minute annoncées. Même cause racine -- la couche est posée à l'intérieur du nid `/api/v1`, donc ses chemins `/api/v1/...` ne correspondaient jamais. C'est un correctif de sécurité.
- `X-Forwarded-For` n'est cru que lorsque la requête arrive d'un pair loopback ou privé, ce qu'est le processus fils Caddy de sh0 lui-même. Note de compatibilité : une installation frontée par un proxy à adresse publique -- Cloudflare et assimilés -- compte désormais tout son trafic dans un seul compartiment, faute de liste de proxys déclarés.
- Huit commandes `list` étaient cassées -- `database-servers`, `auth-servers`, `realtime-servers`, `function-servers`, `mail`, `file-storage`, `uptime` et `nodes` mouraient toutes sur `Failed to parse paginated response`. `sh0 templates info` et `sh0 db connection-info` échouaient sur des noms de champs qui avaient dérivé côté serveur. Le bandeau de démarrage annonçait aussi systemd sur macOS, où l'installeur ne pose aucun service de ce type ; il nomme désormais launchd, et se tait quand le serveur tourne déjà sous LaunchAgent.
Huit cas de déploiement joués sur une vraie machine, trois murs trouvés
- Cette version vient d'une matrice de fumée du déploiement jouée en entier sur une vraie machine -- huit cas, pas une relecture de code. Six passent : Node générique, Next avec pnpm, site statique, Dockerfile arbitraire, et les gabarits PostgreSQL et Redis. Deux échouaient et restent ouverts : Laravel et docker-compose.
- Le faux vert, et le plus grave des trois. Le contrôle de santé sondait `docker-proxy`, qui complète la poignée de main TCP quelle que soit la santé du conteneur : une application morte était déclarée saine. La sonde rend désormais un état à trois valeurs, et c'est son issue qui décide, non son échec. Mesuré : un `nginx:alpine` sans HEALTHCHECK sur un port délibérément faux échoue maintenant après 304 738 ms, là où la v1.6.20 le déclarait vert en moins d'une minute.
- Sites statiques : nginx mourait en boucle sur son pidfile. Le `chown` n'avait pas suivi le déplacement du pidfile vers `/tmp/nginx`. Corrigé, plus un test qui vérifie chaque chemin inscriptible individuellement -- le test existant ne cherchait que la sous-chaîne `chown` et passait au vert pendant tout l'épisode.
- Le port saisi écrasait en silence l'`EXPOSE` de l'image. sh0 lit désormais l'`EXPOSE` quand aucun port n'est saisi, et avertit en cas de désaccord, sur les cinq voies de déploiement.
- pnpm. Les dépendances à script de build n'étaient plus construites, et l'élagage pnpm échouait deux fois. Critère d'acceptation atteint : `ts-nextjs-tailwind-starter` construit en 211 s et sert un 200.
- 804 tests passés, `clippy --workspace --all-targets -- -D warnings` vert.
Une rétention qui épargne les sauvegardes manuelles, et des restaurations qui atteignent les serveurs autonomes
- La rétention ne touche plus aux sauvegardes manuelles. Une planification ne compte et ne purge que les archives qu'elle a créées ; tout ce qui est déclenché à la main est immunisé par construction. La migration 057 relie chaque sauvegarde à sa planification et n'adopte les lignes existantes que lorsque l'attribution est certaine -- une source dotée de plusieurs planifications conserve ses anciennes lignes intactes plutôt que de deviner. C'est le changement moteur annoncé comme à venir en v1.6.19.
- Le modal de planification liste les serveurs de base autonomes. La carte Base de données les comptait, la liste de sélection non : un serveur visible dans le compte ne pouvait pas être planifié. Les deux montrent maintenant la même population -- bases d'applications, services à base embarquée et serveurs autonomes signalés par un badge « DB Server » -- et le retour arrière ramène à la bonne liste.
- Une restauration externe peut viser un serveur de base autonome, serveur entier ou base unique. Le chemin serveur entier préserve les bases déjà présentes sur la cible, la garantie introduite en v1.6.19. Trois cas sont désormais refusés par un message clair au lieu d'échouer en silence : un serveur sans conteneur démarré, un nom de base commençant par un tiret, et une cible inconnue.
- L'état de restauration d'une sauvegarde est enregistré dès la création de sa ligne. Les restaurations externes affichaient un statut vide jusqu'à la première mise à jour -- une lacune présente depuis l'insertion d'origine, révélée par les tests neufs. La confirmation d'édition d'une planification est également traduite dans les cinq langues du panneau au lieu d'afficher sa clé brute.
Sécurité de restauration, réparation des lignes et contrat de rétention
- Restaurer une sauvegarde de serveur PostgreSQL entier ne détruit plus la cible. La restauration v1.6.18 supprimait toutes les bases du serveur cible puis ne restaurait rien -- une régression publiée avec le correctif pg_dumpall. La restauration filtre désormais le script au moment de restaurer : les archives déjà produites par la v1.6.18 restent restaurables.
- Les sauvegardes MongoDB s'authentifient. mongodump et mongorestore étaient lancés sans identifiants : aucune sauvegarde MongoDB d'un serveur managé n'avait jamais produit d'archive -- chaque tentative mourait au mur d'authentification. Le mot de passe atteint désormais les outils par l'environnement d'exécution du conteneur.
- Les sauvegardes de serveur entier CockroachDB sont refusées avec un message clair au lieu de faire semblant. L'image n'embarque aucun outil client PostgreSQL, la fonction ne peut donc pas marcher ; la branche morte qui suggérait le contraire est retirée.
- La migration 056 répare les lignes de sauvegarde abîmées par les versions précédentes : restaurations externes enregistrées comme archives (un chemin sans taille) et lignes bloquées en « restoring » par l'ancien chemin de restauration. Deux invariants mesurés sur tout l'historique des versions rendent la réparation sûre ; les lignes inclassables sont laissées intactes et documentées.
- La rétention n'a plus qu'un contrat : défaut 7 partout, 0 = illimité. Le formulaire disait 30 quand l'API disait 7, et taper 0 devenait silencieusement 30 parce que zéro est falsy. Bornes 0-365, libellés mis à jour dans les cinq langues du panneau.
- La rétention compte toutes les sauvegardes d'une source -- planifiées comme manuelles. La documentation promettait que les manuelles n'étaient jamais comptées : c'était faux contre le moteur, et la doc dit désormais ce qui se passe réellement. Rendre les manuelles immunisées est un changement moteur futur, suivi à part.
Rétention des sauvegardes et sauvegardes de serveur entier
- La rétention des sauvegardes s'exécute enfin. La fonction de purge existait, n'était appelée de nulle part, et n'avait jamais tourné depuis la v1.0.0 : le champ « Rétention » du panneau ne pilotait rien et les dépôts grossissaient sans limite. Elle s'exécute désormais après chaque sauvegarde planifiée réussie, avec un balayage horaire de rattrapage pour les dépôts déjà débordés.
- Une sauvegarde de serveur PostgreSQL entier contient enfin vos données. Le planificateur passait « postgres » comme nom de base : on sauvegardait la base d'amorçage, vide -- l'archive était bien formée, restaurable et vide. Les sauvegardes de serveur entier utilisent maintenant pg_dumpall, et la restauration ne s'exécute plus en une transaction unique, la création de bases l'interdisant.
- Les sauvegardes de serveur MySQL entier fonctionnent, tout simplement. Elles n'avaient jamais tourné : le planificateur passait « --all-databases » comme nom de base, que la validation rejetait pour son tiret initial. Même cause que la panne PostgreSQL, symptôme inverse -- celle-ci échouait bruyamment au lieu de mentir en silence.
- Le panneau affichait « Rétention (jours) » pour ce qui a toujours été un nombre de sauvegardes. Corrigé dans les cinq langues du tableau de bord.
- Ces deux correctifs sont vérifiés par des tests, pas encore sur un serveur réel. Aucun test de la suite ne touche une véritable instance PostgreSQL : ils contrôlent la commande construite, jamais sa sortie. Tenez cette version pour corrigée-mais-non-prouvée sur les sauvegardes de serveur entier tant qu'elle n'a pas tourné sur votre propre machine.
Sauvegardes des bases managées -- déclenchement, planification, restauration
- Une base créée sur un serveur de base managé peut désormais être sauvegardée : le déclencheur n'interrogeait que la table des bases autonomes, si bien qu'une base managée répondait « source de sauvegarde introuvable ». Vérifié sur un serveur réel en ouvrant l'artefact -- il contient bien la table et ses lignes.
- Les sauvegardes d'un serveur de base entier peuvent enfin être restaurées. Le déclencheur acceptait un type de source que la restauration ne connaissait pas : chaque restauration de ce type était acceptée, puis échouait en tâche de fond. Les cibles invalides sont désormais refusées d'emblée.
- Une restauration externe ne fait plus purger de vraies sauvegardes. L'enregistrement d'une restauration portait le même statut « terminé » qu'une archive : la rétention le comptait et supprimait de véritables sauvegardes -- lignes et objets stockés -- pour tenir le quota. Sauvegardes et opérations de restauration sont maintenant deux natures d'enregistrement distinctes.
- Les bases managées peuvent être placées sur un calendrier de sauvegarde, et plus seulement sauvegardées à la demande.
- Les projets Python utilisant un Pipfile embarquent enfin leurs dépendances dans l'image de production. Constaté en ouvrant l'image construite plutôt qu'en lisant une réponse HTTP : le correctif précédent passait ses tests et livrait malgré tout une image sans elles.
- Les projets pnpm se construisent de nouveau : l'image générée installe exactement la version de pnpm qui a produit le fichier de verrouillage, sur une base Node assez récente pour l'exécuter. Un cas reste ouvert -- un projet pnpm dont une dépendance exécute un script d'installation échoue à la construction ; correctif en cours.
Corrections de build des déploiements Git -- gestionnaires de paquets, Django, sondes de santé
- Les projets yarn et bun se construisent de nouveau : l'image générée installe le gestionnaire de paquets au lieu d'en appeler un qui n'a jamais été présent. Les projets pnpm restent bloqués -- la vérification en conditions réelles a montré que l'image de base Node est trop ancienne pour la version de pnpm téléchargée ; corrigé en 1.6.17.
- Les déploiements Django exécutent collectstatic dans l'étage de production, là où les dépendances se trouvent réellement -- l'étape échouait en silence et les fichiers statiques renvoyaient une erreur 500
- Les projets Python utilisant un Pipfile ne sont pas encore réparés : la vérification en conditions réelles a montré que le correctif livré ici reste incomplet -- les dépendances n'atteignent toujours pas l'image finale. Corrigé et vérifié sur un serveur réel en 1.6.17.
- Les sondes de santé des images Alpine interrogent 127.0.0.1 avec un repli en IPv6 -- busybox wget résolvait ::1 en premier, si bien que les serveurs en IPv4 seule étaient redémarrés comme défaillants
- Les projets Rust se compilent avec une chaîne d'outils à jour (rust:1-alpine) -- l'image 1.77 épinglée ne savait même plus lire un Cargo.lock moderne
Renforcement des alertes et de la supervision -- RBAC, SSRF, serveurs d'authentification
- Les règles d'alerte et les canaux de notification appliquent désormais les permissions de rôle -- les observateurs d'un projet ne peuvent plus créer ni modifier d'alertes, ni lire les secrets des routes de notification
- Les vérifications d'uptime et d'alerte sont renforcées contre le SSRF -- les requêtes sortantes ne peuvent plus être dirigées vers des adresses internes ou privées
- Le DNS rebinding est neutralisé en épinglant l'IP résolue pour toute la requête, et la supervision échoue désormais bruyamment si son client HTTP durci contre le SSRF ne peut pas être construit, au lieu de basculer silencieusement sur un client non sécurisé
- Le provisionnement des serveurs d'authentification (Logto) n'accorde plus que les rôles strictement nécessaires et échoue bruyamment en cas de création partielle, au lieu de laisser un serveur à moitié créé marqué comme prêt
- Les routes de notification mortes ont été supprimées et les entrées de configuration des alertes et de l'uptime sont validées plus strictement
Correctifs des hubs BaaS -- secrets, domaines, fonctions edge
- Les restaurations de base de données remplacent désormais la base cible et s'annulent de façon atomique en cas d'erreur, au lieu de fusionner silencieusement les lignes avec les données existantes
- Les identifiants des serveurs realtime ne sont renvoyés qu'aux développeurs et au-delà -- les observateurs d'un projet ne reçoivent plus les secrets de connexion
- Les serveurs realtime, auth et fonctions sont désormais joignables sur leurs domaines publics *.sh0.app
- Les serveurs de fonctions Deno démarrent de manière fiable -- un démarrage échoué est signalé immédiatement au lieu d'un faux statut « en cours d'exécution »
- Les serveurs de fonctions reçoivent un domaine public fonctionnel avec HTTPS automatique dès la création
Parité d'exécution des conteneurs -- correctifs de fiabilité
- Restauration externe corrigée -- la restauration d'une sauvegarde stockée sur une destination externe (S3, R2) lit désormais correctement l'artefact téléversé
- Parité des previews -- les conteneurs de preview s'exécutent désormais sous le même utilisateur qu'en production, si bien que les applications nécessitant des privilèges élevés ne bouclent plus en échec en preview
- Parité de la montée en charge -- les réplicas héritent désormais de l'utilisateur du conteneur principal, si bien que la mise à l'échelle d'une application dont l'image s'exécute en root ne produit plus de réplicas en boucle d'échec
- Correctifs de templates -- Garage, Logto et Plane démarrent désormais correctement dès l'installation
Durcissement des sauvegardes et du stockage
- Restauration corrigée -- la restauration d'une sauvegarde de base de données ou de volume depuis le tableau de bord ou l'API aboutit désormais correctement
- Contrôle d'accès des sauvegardes -- lister, déclencher, télécharger, restaurer et supprimer une sauvegarde exige désormais les permissions adéquates sur le projet
- Identifiants protégés -- les points d'accès du stockage de fichiers ne renvoient plus d'identifiants administrateur partagés, et les destinations de sauvegarde ne sont gérables que par leur propriétaire
- Listes cloisonnées -- les domaines de stockage et les clés d'accès ne sont visibles et gérables que dans vos propres projets et instances
- Nettoyage complet -- la suppression d'une sauvegarde efface son artefact stocké, et la suppression d'une instance de stockage retire ses routes proxy et sa configuration TLS
Fiabilité des déploiements -- correctifs complémentaires
- Déploiements Git accessibles -- le port détecté d'une application est désormais mémorisé, si bien que les projets Next.js, Django, FastAPI et Rust répondent sur leur URL de prévisualisation sans configuration manuelle
- TLS plus propre -- le serveur d'origine ne réessaie plus d'obtenir des certificats impossibles à délivrer pour les domaines proxy *.sh0.app, ce qui libère le quota de certificats partagé
- Plausible Analytics -- le modèle crée désormais le rôle et la base PostgreSQL, de sorte que la pile démarre correctement
- Zitadel -- le modèle fournit désormais un écran de connexion fonctionnel par défaut
- Convex -- le backend démarre désormais (marqué expérimental en attendant la prise en charge multi-port)
- Nettoyage amélioré -- les déploiements en échec ne laissent plus de conteneurs ni d'images orphelins
Renforcement production -- lot de corrections issu de l'audit
- Les stacks populaires se déploient correctement -- les applications reçoivent leur véritable en-tête Host, si bien que WordPress, Laravel et Django fonctionnent derrière les domaines *.sh0.app
- État de déploiement fiable -- les conteneurs en boucle de redémarrage sont détectés et signalés comme en échec au lieu d'apparaître comme actifs
- Indicateur SSL exact -- les domaines affichent l'état réel de leur certificat et les liens utilisent HTTPS
- Valeurs par défaut plus sûres -- les panneaux d'administration secondaires (consoles de base de données, stockage objet, fournisseurs d'identité) ne sont plus exposés publiquement par défaut
- Next.js et Rust -- les projets create-next-app standard et les vrais projets Rust se déploient désormais sans configuration manuelle
- Réparations du catalogue de modèles -- gestion des identifiants corrigée pour Zitadel, Chatwoot, Langfuse, SurrealDB et Convex ; suppression des modèles dont l'image amont n'est plus disponible
sh0 Manager -- Gestion mobile de flotte
- Application mobile sh0 Manager -- gérez toutes vos instances sh0 depuis votre téléphone (iOS et Android)
- Flux de revendication par QR code -- scannez depuis le tableau de bord pour lier vos instances, jetons sécurisés à durée limitée
- Vue d'ensemble de la flotte -- voyez toutes vos instances, statut en ligne/hors ligne, nombre d'applications, informations du plan
- Authentification par Bearer token -- l'application mobile utilise l'authentification par session via l'en-tête Authorization
- Section Manager dans le tableau de bord -- générez des jetons de revendication, consultez les informations de l'instance, gérez la propriété
Logs de build en temps réel, variables d'environnement et fiabilité
- Logs de build en streaming -- suivez les builds Docker en temps réel avec défilement automatique
- Correction de la condition de course des variables d'environnement -- sauvegarde atomique avant le lancement du pipeline
- Timeout de build augmenté à 30 minutes -- supporte Rust et autres projets à compilation longue
- Score de santé amélioré -- plafond de déduction par règle, règles de sécurité rétrogradées
- Déploiement réussi de 0cron.dev (backend Rust + frontend SvelteKit) sur sh0 -- première vraie application Rust
- Bruit des logs de heartbeat éliminé -- logs de routine en mode debug, info uniquement en cas de changement d'IP
Domaines d’aperçu — une URL immédiate pour chaque application
- Chaque instance sh0 s’enregistre au démarrage auprès du proxy cloud — sans aucune configuration
- Format de sous-domaine à plat : undefined-undefined.sh0.app — un seul certificat joker, tenable au-delà de 100 000 installations
- Proxy inverse des domaines d’aperçu : un intergiciel Axum route le trafic vers le bon conteneur Docker
- Aucun enregistrement DNS Cloudflare nécessaire — le joker *.sh0.app suffit
- Le tableau de bord affiche une pastille « Aperçu » et des liens HTTPS cliquables pour tous les domaines d’aperçu
- Fonctionne sur l’offre gratuite — aucune licence requise pour les domaines d’aperçu
Passe de QA 1 — corrections et finitions
- Correction de la pastille SSL affichant « en attente » alors que HTTPS était actif
- DNS du courrier : l’enregistrement PTR devient optionnel (une IP partagée ne permet pas de DNS inverse)
- URLs du courrier : suppression des ports Docker aléatoires, points d’accès HTTPS propres via le proxy Caddy
- Ajout des redirections /webmail et /admin à la manière de cPanel pour les noms d’hôte du courrier
- Correction de la boucle de rafraîchissement infinie de l’onglet File d’attente du courrier
- Les identifiants d’administration du courrier s’affichent dans l’onglet Vue d’ensemble, avec bascule d’affichage
- Tâches planifiées : retrait automatique des redirections shell (> /dev/null 2>&1) des commandes
- L’historique d’exécution des tâches planifiées montre la sortie standard et d’erreur, dans une visionneuse dépliable
- Déclenchement d’une tâche planifiée : fenêtre en temps réel avec scrutation, au lieu d’une notification muette
- Correction du serveur d’authentification : bonne image Docker (svhd/logto), point d’entrée correct, base amorcée au premier démarrage
- Authentification, temps réel et fonctions : états vides enrichis d’explications et de prérequis
- Les erreurs internes rendent désormais leur message réel (produit auto-hébergé, l’utilisateur est administrateur)
- Terminal de la machine et navigateur de fichiers dans les Réglages, pour un accès direct au serveur
- Onglets Fichiers et Volumes ajoutés aux pages de détail des serveurs de fonctions et de temps réel
- Correction de la contrainte de domaine sur service_id (migration 049)
- 475 nouvelles clés d’internationalisation, et 4 pages du site converties à Paraglide
Sécurité, finitions et fonctionnalités complètes
- Fonctionnalités avancées du courrier : configuration du filtre anti-spam, réponse automatique d'absence, règles de filtrage Sieve, surveillance de la file d'attente
- Rotation des clés DKIM et streaming des journaux de conteneurs pour les serveurs de courrier
- Restriction par licence : tous les services BaaS réservés au plan Pro+ avec popups UpgradePrompt sur 12 pages
- Intégration Trustpilot sous forme d'onglet fixe sur toutes les pages du tableau de bord
- Statistiques rapides BaaS sur la page d'accueil du tableau de bord
- 8 nouvelles pages SEO d'alternatives : Vercel, Supabase, Firebase, Netlify, cPanel, Render, Railway, Fly.io
- Refonte de la page d'accueil avec hero BaaS, section capacités et bandeau de tarification
- Matrice de comparaison des fonctionnalités sur la page de tarification avec 27 lignes
- Page relations investisseurs avec livre blanc et présentations PDF
- Prompts système IA mis à jour avec les fonctionnalités et capacités actuelles
- 979 clés i18n pour les pages SEO dans 5 langues
- 5 correctifs de sécurité critiques : injection Sieve, perte de données de réponse automatique d'absence, contournement de portée MCP sur 92 outils, traversée de chemin dans les sauvegardes, écrasement d'état Cloudflare
- 10 correctifs de sécurité importants : validation des entrées, nettoyage DNS, validation des cibles sandbox, invites de confirmation CLI
Stockage de fichiers, serveurs de bases de données et courrier
- Stockage objet S3 géré propulsé par MinIO -- buckets, clés d'accès, surveillance de l'utilisation
- Serveurs de base de données autonomes : PostgreSQL, MySQL, MariaDB, MongoDB, Redis avec gestion des utilisateurs et des droits
- Hébergement e-mail géré via Stalwart avec configuration automatique DKIM, SPF et DMARC
- Assistant DNS en 4 étapes pour le courrier avec support de configuration automatique Cloudflare
- Boîtes mail, alias, règles catch-all et surveillance de la délivrabilité
- Intégration d'interface d'administration de base de données : dbGate, phpMyAdmin, Mongo Express
- Accès externe aux bases de données avec liste d'adresses IP autorisées pour les connexions distantes sécurisées
- Page de domaines globale : vue centralisée de toutes les URL de service de chaque application
- Plus de 50 nouveaux endpoints d'API pour le stockage de fichiers, les serveurs de bases de données et le courrier
- Fonctionne immédiatement avec AWS SDK, Laravel, WordPress et tout client compatible S3
Infrastructure de base
- Workspace Cargo avec 10 crates Rust, gestion partagée des dépendances
- sh0-db : SQLite avec mode WAL, pool r2d2, 30 migrations
- sh0-docker : client complet de l'API Docker Engine via socket Unix (hyper 1.x)
- sh0-api : serveur REST Axum avec CRUD, pagination, streaming de logs par WebSocket
- sh0-git : clone/pull Git (libgit2), analyse de webhooks GitHub/GitLab/Bitbucket avec HMAC-SHA256
- sh0-builder : 19 détecteurs de stack, 15 templates Dockerfile, builds multi-étapes
- Moteur de vérification de santé du code : 34 règles en pur Rust réparties en 8 catégories
Proxy inverse et pipeline de déploiement complet
- sh0-proxy : gestion du processus enfant Caddy avec API admin JSON
- SSL automatique via Let's Encrypt avec e-mail ACME configurable
- Pipeline de déploiement de bout en bout : git pull -> analyser -> construire -> déployer -> vérification de santé -> routage
- Basculement de conteneur blue-green avec déploiement sans interruption
- Système de nettoyage Docker : images orphelines, anciennes images d'apps, purge du cache de build
- Vérification d'espace disque avant déploiement (échec à >90 %, avertissement à >80 %)
- Rollback par réutilisation de l'image en cache
Authentification et surveillance
- Hachage de mots de passe Argon2id, JWT HS256 (accès 1h + rotation du refresh 30j)
- 2FA TOTP avec codes de secours hachés et URI de provisionnement
- Système de clés API : hachées SHA-256, préfixe sh0_, comparaison en temps constant
- Clé de chiffrement maître AES-256-GCM (PBKDF2, 100K itérations)
- Collecte de métriques en temps réel : CPU, mémoire, E/S réseau par conteneur
- Évaluation d'alertes : high_cpu, high_memory, app_down avec intervalle de 5 min
Moteur de sauvegarde et tableau de bord
- Dumps de bases de données via Docker exec : pg_dump, mysqldump, mongodump
- Sauvegarde de volumes (tar + gzip) avec chiffrement AES-256-GCM
- Planification de sauvegardes via cron avec rétention et purge
- SPA SvelteKit 2 + Svelte 5 runes avec TailwindCSS 4
- Thème sombre/clair, i18n en 5 langues (EN, FR, ES, PT, SW)
- Client API avec Bearer token automatique, WebSocket avec reconnexion automatique
- Tableau de bord intégré au binaire Rust via include_dir + build.rs
Tableau de bord principal et pages étendues
- Page de détail d'app avec disposition à 6 onglets : Vue d'ensemble, Déploiements, Logs, Domaines, Environnement, Paramètres
- Visualiseur de logs en temps réel : WebSocket, style terminal, défilement automatique, tampon de 1000 lignes
- Éditeur de variables d'environnement avec bascule chiffrement/révélation AES-256-GCM
- Page de gestion de bases de données avec sélecteur de moteur (postgres, mysql, mongodb, redis, mariadb)
- Gestionnaire de sauvegardes : déclencher, restaurer, supprimer, créer une planification avec cron
- Page de surveillance : jauges CPU/mémoire, CRUD d'alertes, rafraîchissement automatique
- Paramètres : infos serveur, configuration/désactivation 2FA TOTP, gestion des clés API
CLI, templates, alertes et Compose
- Client CLI : 7 commandes (apps, deploy, logs, env, check, ssh, status)
- Vérification de santé du code en local via sh0 check avec sortie terminal en couleur
- 170 templates YAML en un clic répartis en 15 catégories (de WordPress à Ollama)
- Moteur de substitution de variables avec génération automatique pour les secrets/mots de passe
- 5 canaux de dispatch d'alertes : e-mail (SMTP), Slack, Discord, Telegram, webhook
- Analyseur Docker Compose v3 avec déploiement multi-services
RBAC, export, cron, prévisualisations et hooks
- RBAC à 4 niveaux : Owner > Admin > Developer > Viewer (global + portée par projet)
- Journalisation d'audit sur tous les handlers (asynchrone fire-and-forget)
- 7 formats d'export : Docker Compose, Kubernetes, AWS ECS, GCP Cloud Run, Vercel, Railway, Render
- Planificateur de tâches cron avec exécution basée sur les ticks, capture stdout/stderr, historique d'exécution
- Environnements de prévisualisation : déploiement automatique par PR avec sous-domaines uniques et nettoyage TTL
- Hooks de déploiement : pre_build, post_build, pre_deploy, post_deploy avec timeout enforci
- sh0.yaml Infrastructure as Code : définissez des stacks entières dans un seul fichier
Scaling et surveillance de disponibilité
- Scaling horizontal : 1 à 50 réplicas avec répartition de charge (round_robin, least_conn, random)
- Mise à l'échelle automatique sur seuils CPU/mémoire avec intervalles configurables
- Surveillance de disponibilité : vérifications de santé HTTP avec détection et cycle de vie des incidents
- Pages de statut publiques avec historique de disponibilité par domaine
- Système d'abonnement par e-mail pour les notifications de pages de statut
51 problèmes trouvés et corrigés
- 2 audits de sécurité complets sur l'ensemble des 25 phases
- 13 correctifs critiques : comparaison de clés API en temps constant, authentification JWT WebSocket, hachage des codes de secours, verrouillage de déploiement par app
- 20 correctifs élevés : validation de domaine, prévention de traversée de chemin, masquage des secrets dans les logs de build, arguments Docker encodés en URL
- Expiration JWT réduite (7j -> 1h) avec rotation de jeton de rafraîchissement sur 30 jours
- Protection CSRF (validation Content-Type + pattern double-submit)
- Limitation de débit : connexion (10/15min), TOTP (5/5min), global (1000/500s)
- En-têtes de sécurité : HSTS, CSP, X-Frame-Options, X-Content-Type-Options
Architecture basée sur les stacks et finitions
- Barre latérale icônes 56px avec tooltips + barre contextuelle 240px pour la navigation des projets
- Architecture basée sur les stacks : regroupez les services par projet (frontend + backend + BDD + cache)
- Deploy Hub : assistant unifié avec 183 options de déploiement, onglets par catégorie, section mise en avant
- 7 composants de formulaire de déploiement : FormGit, FormUpload, FormDockerImage, FormDockerfile, FormCompose, FormService, FormFramework
- Palette de commandes Cmd+K : recherche instantanée de pages, stacks et services
- Barre latérale contextuelle responsive mobile avec tiroir + bouton hamburger
- Refonte de la page d'accueil avec statistiques serveur, grille de stacks, actions rapides
- Vue détaillée d'app à 16 onglets : Vue d'ensemble, Déploiements, Logs, Terminal, Fichiers, Stockage, Domaines et SSL, Environnement, Redirections, Prévisualisations, Hooks, Scaling, Disponibilité, Cron, Services, Paramètres
Image Docker, Dockerfile, ZIP et PHP/Ruby/.NET
- Déploiement direct d'image Docker : pull de l'image, sans pipeline git/build
- Déploiement Dockerfile uniquement : coller le contenu, build local
- Déploiement par téléversement ZIP avec limite de 500 Mo et prévention de traversée de chemin
- Ajout des templates Dockerfile pour PHP, Ruby et .NET
- Noms d'apps à portée de projet (index unique composé)
- Nommage des conteneurs avec préfixe de projet : sh0-undefined-undefined
- 45 nouveaux tests (scaling, compose, YAML)
Templates, terminal, explorateur de fichiers, volumes
- Page Deploy Hub avec catalogue de 183 options de déploiement
- Barre de progression du déploiement avec suivi des étapes dans les logs
- 170 templates : bases de données (33), CMS (18), analytics (7), auth (6), IA/ML (8), DevTools (12), et plus
- Gestion des sous-services (conteneurs MySQL, phpMyAdmin, dbGate)
- URL d'accès aux services : endpoints internes/locaux/externes avec bascule externe
- Terminal web : xterm.js avec sélecteur de shell (sh/bash/ash), prise en charge SSH TTY
- Explorateur de fichiers : explorateur à deux panneaux style Docker Desktop avec éditeur en ligne
- Stockage persistant : gestion de volumes Docker, montages bind, montages de fichiers
- Page de documentation API avec playground interactif (26 groupes d'endpoints)
URL de connexion, identifiants, variables d'environnement en masse
- URL de connexion aux bases de données : internes et externes, calculées automatiquement à partir de la config du conteneur
- Carte d'identifiants de base de données : nom d'utilisateur, mot de passe, chaînes de connexion
- Opérations en masse sur les variables d'environnement avec mode éditeur .env
- Persistance des identifiants de templates (variables d'environnement stockées en BDD)
Certificats SSL, fiabilité Caddy, fournisseurs de stockage
- Migration vers cookies HTTP-only avec pattern CSRF double-submit (SameSite=Strict)
- Génération automatique de la clé maître (dérivée du secret JWT au premier lancement)
- Certificats SSL personnalisés : génération CSR (rcgen), téléversement PEM (x509-parser), ssl_mode par domaine
- Chiffrement de la clé privée (AES-256-GCM) avec permissions de fichier restreintes (0o600)
- Fiabilité du proxy Caddy : arrêt des processus obsolètes, relance de la config, synchronisation des routes en BDD
- 13 fournisseurs de stockage via OpenDAL : S3, R2, Wasabi, Backblaze B2, SFTP, FTP/FTPS, Dropbox, Google Drive, et plus
- Correctif FTP/FTPS IPv6 (mode EPSV, client suppaftp direct)
- Alignement des plans/tarifs : Free (0 $), Pro (19 $/mois), Business (97 $/mois)
CronBuilder, redirections, restauration de sauvegarde, refonte de la surveillance
- Composant CronBuilder avec 18 préréglages et synchronisation bidirectionnelle
- Règles de redirection d'URL : correspondance exacte, préfixe, regex avec codes de statut 301/302/307/308
- Restauration de sauvegarde : Flux A (sauvegardes sh0) et Flux B (téléversement depuis source externe)
- Refonte de la page de surveillance : tableau de bord à 4 onglets (Vue d'ensemble, Apps, Disponibilité, Alertes)
- Réduction des faux positifs de la vérification de santé (plus de 80 nouveaux tests, ignorer les fichiers env/lock/doc)
- Modale de configuration DNS avec IP réelle du serveur et guide Cloudflare
- Configuration de l'e-mail ACME (stocké en BDD, mises à jour à chaud)
Multi-serveur BYOS
- Enregistrement de nœuds distants avec gestion de tunnels SSH (russh)
- SSH TOFU (Trust on First Use) avec vérification d'empreinte de clé hôte
- Transfert d'images Docker via save/load par tunnels SSH
- Pipeline de déploiement compatible nœuds qui dispatche les builds vers les serveurs distants
- Tableau de bord de gestion des nœuds avec indicateurs de statut et surveillance de santé
- Durcissement des nœuds : gestion d'erreurs, validation de connexion, nettoyage gracieux
Serveur MCP et assistant IA
- Serveur MCP avec 103 outils : transport Streamable HTTP sur /mcp (JSON-RPC 2.0)
- Génération d'outils pilotée par OpenAPI via utoipa avec extensions x-mcp-*
- Sécurité à 3 niveaux : portées de clés API (read/standard/admin), classification de risque par outil, jetons de confirmation pour les opérations destructives
- Passerelle IA : routage de chat à 3 voies (connecteur MCP, legacy, expert docs)
- AI Sandbox : conteneur sidecar Alpine 3.19 par application avec shell root, installation de paquets, volumes en écriture
- Intégration Claude MCP Connector pour l'exécution agentique d'outils côté serveur
- Chat IA dans le tableau de bord avec sélecteur de modèle, historique de conversations, chronologie des étapes de traitement
- Modale de capacités : 24 capacités réparties en 5 catégories, traduites en 5 langues
- Facturation par portefeuille prépayé avec tarification par jeton (Haiku, Sonnet, Opus)
- Prise en charge BYOK sur le plan Business (clés API Anthropic ou OpenRouter)
- 10 rounds d'audit sur 5 phases -- 0 problème critique en production
Plan gratuit, Docker et distribution
- Sauvegardes ouvertes au plan Free (stockage local) ; stockage cloud réservé au plan Pro+
- Image Docker publiée sur Docker Hub (zerosuiteinc/sh0) et GHCR
- Système de mise à jour automatique : sh0 update vérifie les GitHub Releases et met à jour sur place
- Configuration automatique du service systemd sous Linux (sh0 serve installe et active l'unité)
- Remplacement des bloqueurs plein écran par des bannières intégrées sur surveillance, sauvegardes, équipe
- Matrice de fonctionnalités de licence affinée : sauvegardes granulaires (local/cloud) et surveillance (métriques/alertes/uptime)
- Commande de désinstallation : sh0 uninstall supprime proprement le binaire, l'unité systemd et les données
- Bannière de démarrage avec version, URL du tableau de bord et astuce systemd
Tout dans la v1.0.0
Moteur de déploiement
- 5 pipelines de déploiement : Git, Image Docker, Dockerfile, téléversement ZIP, Template/Compose
- Détection automatique de 19 stacks avec 15 templates Dockerfile optimisés
- Déploiements blue-green avec basculement sans interruption et rollback
- 183 options de déploiement dans le Deploy Hub
- Analyseur Docker Compose v3 avec déploiement multi-services
Magasin de templates
- 170 templates en un clic répartis en 15 catégories
- Bases de données, CMS, IA/ML, DevTools, analytics, auth, e-mail, files d'attente, recherche
- Substitution de variables avec génération automatique (secrets, mots de passe)
- Déploiement multi-services avec tri topologique
Domaines et SSL
- SSL automatique via Let's Encrypt avec Caddy ACME
- Téléversement de certificat SSL personnalisé avec génération CSR
- Règles de redirection d'URL (préfixe, exacte, regex)
- Répartition de charge : round_robin, least_conn, random
Surveillance et alertes
- Métriques CPU, mémoire, réseau en temps réel avec graphiques sparkline
- 5 canaux d'alerte : e-mail, Slack, Discord, Telegram, webhook
- Surveillance de disponibilité avec pages de statut publiques
- Mise à l'échelle automatique sur seuils CPU/mémoire (1 à 50 réplicas)
Sauvegarde et stockage
- Dumps de bases de données (pg_dump, mysqldump, mongodump) + sauvegardes de volumes
- Chiffrement AES-256-GCM avec sauvegardes planifiées via cron
- 13 fournisseurs de stockage via OpenDAL (S3, R2, SFTP, Dropbox, Google Drive)
- Restauration depuis une sauvegarde ou un stockage externe
Sécurité
- 51 problèmes de sécurité trouvés et corrigés lors de 2 audits
- Mots de passe Argon2id, JWT + rotation du refresh, 2FA TOTP, Google OAuth
- Chiffrement AES-256-GCM pour tous les secrets (variables d'environnement, jetons, clés)
- Prévention CSRF et SSRF, limitation de débit, durcissement des conteneurs
Expérience développeur
- Terminal dans le navigateur (xterm.js) avec sélection du shell
- Explorateur de fichiers avec éditeur en ligne, mkdir, suppression
- Vérification de santé du code : plus de 34 règles analysées avant chaque build
- Export vers 7 plateformes : K8s, AWS, GCP, Vercel, Railway, Render, Compose
Tableau de bord
- 22 pages, plus de 70 composants, vue détaillée d'app à 16 onglets
- Palette de commandes Cmd+K, thème sombre/clair, 5 langues
- Architecture basée sur les stacks avec double barre latérale
- Documentation API avec playground interactif (26 groupes d'endpoints)
Automatisation
- Tâches cron avec CronBuilder (18 préréglages, 50 jobs/app)
- Hooks de déploiement : pre_build, post_build, pre_deploy, post_deploy
- Environnements de prévisualisation depuis les webhooks de PR avec nettoyage TTL
- Infrastructure as Code via sh0.yaml
Équipe et RBAC
- 4 niveaux de rôles : Owner, Admin, Developer, Viewer
- Contrôle d'accès à portée de projet
- Journalisation d'audit sur toutes les opérations
- Système de licences : Free, Pro (19 $/mois), Business (97 $/mois)
Assistant IA
- Serveur MCP avec 103 outils (37 lecture, 44 écriture, 16 destructives, 5 sandbox, 1 confirmation)
- AI Sandbox : conteneur Alpine par application pour le débogage et le développement
- Connexion depuis Claude Desktop, Cursor ou tout client MCP
- Passerelle IA avec chat web, sélection de modèle, historique de conversations
- Portefeuille prépayé avec option BYOK sur le plan Business
En chiffres
Crates Rust
Migrations
Endpoints d'API
Pages du tableau de bord
Composants UI
Langues
Templates de déploiement
Outils MCP
Règles de santé
Formats d'export
Fournisseurs de stockage
Canaux d'alerte
Tests réussis
Prototype legacy (Python/FastAPI)
Le sh0 original, construit en Python. Architecture basée sur des agents avec détection de stack, vérifications de santé et génération de Dockerfile. A servi de plan pour la réécriture en Rust.
Pourquoi Rust ? Réécriture en Rust pour : environ 30 Mo de mémoire au repos (contre environ 500 Mo en Python), distribution en binaire unique, API Docker socket directe (pas de shell CLI), zéro pause GC sous charge.
Prêt à déployer ?
Installez sh0 en moins de 60 secondes et déployez votre première application.