Déploiement de CMonChoix Platform¶
Status: CURRENT / P0 DEPLOYMENT AUTHORITY / P0.11 CERTIFIED 2026-09-03
Cette procédure décrit l'autorité de déploiement de production. Le checkout live n'est pas une zone de build ni une cible de reset Git : il contient de l'état historique protégé et ne doit pas être utilisé pour fabriquer un artefact.
Autorités¶
- GitHub
mainfournit le commit source certifié. - Une release VPS est un worktree propre, détaché sur un SHA complet explicitement autorisé.
docker-compose.ymlde cette release fournit la spécification canonique.- Les images de production doivent être référencées par RepoDigest.
- Le
.envlive reste hors Git et contient uniquement les secrets/configurations d'environnement. - Les volumes et bind mounts persistants restent attachés au projet Compose
cmonchoix-platform.
Le workflow GitHub historique .github/workflows/ccx-deploy.yml est supprimé et ne constitue plus une autorité de mutation du VPS.
État certifié P0.11 — 2026-09-03¶
P0.10 a certifié l'exécution métier depuis le worker canonique, puis P0.11 a supprimé toute dépendance active aux overrides de récupération du 1er septembre et re-provenancé les services stateful sur le Compose canonique.
Baseline observée et certifiée :
mainet release :bea709e6d4fc910dfd4fbd9a051ff27492152b2a;- Compose canonique :
~/.local/state/cmonchoix/releases/bea709e6d4fc910dfd4fbd9a051ff27492152b2a/docker-compose.yml; - worker :
9e17b183f0a5; - image worker :
ghcr.io/neweden52/cmonchoix-platform-worker@sha256:85cdadb6db1f193a990bfe8d039e9802be1a80eb9ec6f5bbbd42abc8507911d3; - WordPress :
3aafdd78a702; - MariaDB :
139ad5203b7b; - Redis :
887fab68058b; - volume WordPress :
cmonchoix-platform_wordpress_runtime; - volume MariaDB :
cmonchoix-platform_cmonchoix_mariadb_data; - volume Redis :
cmonchoix-platform_redis_data; - tables de la base applicative :
59; - dernier run Samsung certifié :
20260903_145152_5a781a3024; Runtime Guard v2 --runtime:RUNTIME_GUARD_FAILURES=0,RUNTIME_GUARD_WARNINGS=0,CCX_RUNTIME_GUARD_V2_RUNTIME=PASS.
Les IDs de conteneurs sont une photographie de certification, pas une identité durable à coder dans les scripts. Les opérations futures continuent à résoudre les services par l'autorité Compose et/ou leurs labels.
Interdictions¶
Ne jamais utiliser pour un déploiement de production :
git reset --hardsur le checkout live ;- toute modification, restauration ou reset du checkout live pendant le déploiement ;
docker compose up --build;- un build applicatif improvisé sur le VPS ;
docker compose downpour un rollout worker ;--remove-orphanspendant un rollout ciblé ;- suppression ou prune de volumes ;
- un tag flottant comme autorité d'image ;
- un health check basé uniquement sur
/ == 200lorsque WordPress Maintenance est volontairement actif.
Contrat DB du worker autonome¶
Le worker self-contained reçoit explicitement :
WORDPRESS_DB_HOST=mariadb;WORDPRESS_DB_NAMEdepuisMYSQL_DATABASE;WORDPRESS_DB_USERdepuisCCX_WORDPRESS_DB_USER;WORDPRESS_DB_PASSWORDdepuisCCX_WORDPRESS_DB_PASSWORD;WORDPRESS_TABLE_PREFIXdepuisCCX_WORDPRESS_TABLE_PREFIX.
Les trois variables CCX_WORDPRESS_* doivent reprendre l'identité WordPress runtime réellement active. Elles peuvent volontairement différer des anciennes variables bootstrap MYSQL_USER et MYSQL_PASSWORD. Leur absence rend le Compose invalide : aucun fallback silencieux n'est autorisé.
Installation initiale du contrat DB P0.4¶
Cette étape est une mutation de configuration live et exige donc un go opérateur explicite distinct de l'audit read-only.
Pour la transition P0.4, la source historique certifiée des trois valeurs était le worker suivant :
ghcr.io/neweden52/cmonchoix-platform-worker@sha256:d6d13151c72c80f962136043601782606ec8f1b0db1c4bd44c25bc5c0606fffe
Le helper scripts/platform-worker-env-contract-install.sh :
- refuse tout worker source inattendu ;
- lit
DB_USER,DB_PASSWORDet$table_prefixvia WP-CLI sans afficher leur valeur ; - vérifie que cette identité peut réellement exécuter une requête DB read-only ;
- refuse d'utiliser
MYSQL_USERouMYSQL_PASSWORDcomme fallback ; - refuse d'écraser une clé
CCX_WORDPRESS_*déjà présente ; - refuse les valeurs qu'il ne sait pas encoder sans ambiguïté ;
- construit un
.envcandidat0600dans le même répertoire que le.envlive ; - valide le candidat contre le Compose de la release exacte et vérifie le round-trip des trois valeurs sans les imprimer ;
- sauvegarde le
.envprécédent sous~/.local/state/cmonchoix/env-migrations/<timestamp>/.env.beforeen mode0600; - remplace ensuite le
.envpar renommage atomique dans le même filesystem.
Commande autorisée uniquement après go explicite :
CCX_TARGET_SHA=<SHA_COMPLET_CERTIFIE> \
CCX_RELEASE_ROOT=<WORKTREE_RELEASE_PROPRE> \
bash scripts/platform-worker-env-contract-install.sh
Le seul marqueur de succès est :
CCX_WORKER_ENV_CONTRACT_INSTALL=PASS
Une erreur avant le renommage atomique laisse le .env live inchangé. Le helper ne recrée, ne redémarre et ne supprime aucun conteneur.
Préparation d'une release¶
Une release candidate est créée hors du checkout live à partir du SHA complet certifié. Elle doit être propre et son HEAD doit être exactement égal au SHA demandé.
Le déploiement worker est piloté par :
CCX_TARGET_SHA=<SHA_COMPLET_CERTIFIE> \
CCX_RELEASE_ROOT=<WORKTREE_RELEASE_PROPRE> \
bash scripts/platform-worker-safe-deploy.sh preflight
Le preflight est bloquant. Il vérifie notamment :
- release propre et
HEADexact ; - Compose valide ;
- image worker résolue par digest et déjà présente localement ;
- aucun WordPress Core sur le host ;
- checkout live Git présent et entièrement propre (
git status --porcelainvide) ; .envmode0600et contrat DB worker présent ;- timer backup actif/enabled, dernier backup réussi et suffisamment récent ;
- WordPress, MariaDB et Redis healthy ;
- absence de lock/claim de sync Samsung ;
- worker courant identifiable avant mutation.
Un preflight en erreur interdit le rollout.
Rollout ciblé¶
Après autorisation opérateur explicite, le même script exécute :
CCX_TARGET_SHA=<SHA_COMPLET_CERTIFIE> \
CCX_RELEASE_ROOT=<WORKTREE_RELEASE_PROPRE> \
bash scripts/platform-worker-safe-deploy.sh deploy
Avant toute mutation Docker, il capture une preuve de rollback sous :
~/.local/state/cmonchoix/deployments/<timestamp>/
Cette preuve contient sans secret :
- image précédente ;
- liste exacte des fichiers Compose ayant créé le worker précédent ;
- SHA256 de ces fichiers ;
- working directory Compose précédent ;
- mounts et networks précédents ;
- IDs WordPress, MariaDB, Redis et worker précédents ;
- SHA cible et hash du Compose cible.
Le rollout utilise uniquement le service platform-worker avec les garde-fous Compose équivalents à :
--no-deps --no-build --pull never
WordPress, MariaDB et Redis ne doivent donc pas être recréés. Le script vérifie explicitement que leurs IDs sont identiques avant et après.
Certification immédiate¶
Le nouveau worker n'est accepté que si :
- son image est exactement le RepoDigest du Compose ciblé ;
- il est running ;
- il possède exactement deux mounts mutables : uploads et runtime-spool ;
- aucun mount root
/var/www/htmln'existe ; wp core versionpasse ;wp core is-installedpasse ;wp ccx --helppasse ;wp ccx sync status --feed=samsung --limit=1passe ;wp ccx sync doctor --feed=samsungpasse ;wp ccx health projectionspasse ;- WordPress, MariaDB et Redis conservent exactement leurs IDs ;
- WordPress Core reste absent du checkout host ;
- le checkout live Git reste entièrement propre après le remplacement du worker.
La racine HTTP peut rester en 503 lorsque WordPress Maintenance est volontairement actif ; /wp-json/ doit continuer à respecter son contrat opérationnel.
Runtime Guard v2 — autorité read-only P0.5¶
scripts/runtime-readonly-guard.sh fournit deux certifications distinctes :
bash scripts/runtime-readonly-guard.sh --static
bash scripts/runtime-readonly-guard.sh --runtime
Le mode --static certifie le contrat Git sans exiger de runtime Docker actif. Son marqueur de succès est :
CCX_RUNTIME_GUARD_V2_STATIC=PASS
Le mode --runtime est volontairement plus strict : il certifie uniquement le runtime self-contained canonique. Son marqueur de succès est :
CCX_RUNTIME_GUARD_V2_RUNTIME=PASS
Le runtime guard v2 exige notamment :
- le RepoDigest worker certifié et son image ID réel ;
- aucune image candidate rejetée ;
- aucune déclaration Docker
VOLUMEhéritée par le worker ; - exactement deux mounts worker RW : uploads et runtime-spool ;
- aucun mount root
/var/www/html, plugin,src,vendor, thème, mu-plugin ouwordpress_runtimedans le worker ; - exactement les réseaux frontend/backend attendus ;
- WordPress Core absent du host ;
- Core 7.1, plugin CCX, mu-plugin,
srcetvendorprésents dans l'image worker ; - contrat DB injecté égal au contrat réellement lu par WP-CLI, y compris le mot de passe sans jamais l'afficher ;
- connexion DB read-only de diagnostic valide ;
- registry marchand, activation authority, fonctions CCX et erreurs SQL non résolues dans leur état attendu.
Les conteneurs sont résolus par labels Compose project/service, et non par simple confiance dans leurs noms statiques.
Tant que le VPS live tourne encore sur le worker historique d6d131..., le mode --runtime v2 doit échouer. Cette transition n'est pas un défaut du guard et ne doit jamais être contournée en acceptant deux autorités worker simultanément. Le mode runtime v2 ne devient le verdict live officiel qu'après le cut contrôlé vers le worker self-contained.
make doctor respecte également le contrat HTTP de maintenance : / peut répondre 200 ou 503, /wp-json/ doit répondre 200, et /wp-admin/ doit rester compatible avec le routing WordPress attendu.
Rollback automatique¶
Dès que la mutation du worker a commencé, toute erreur déclenche le rollback avec l'autorité Compose capturée avant mutation.
Le rollback refuse de continuer si un fichier Compose précédent a changé de SHA256 ou si l'image précédente n'est plus disponible localement. Il recrée uniquement platform-worker, sans dépendances, sans build et sans pull.
Après rollback, il exige :
- image précédente restaurée ;
- mounts identiques au snapshot précédent ;
- networks identiques ;
- IDs WordPress, MariaDB et Redis inchangés.
Un rollback exécuté interdit tout marqueur PASS de déploiement.
Rollback manuel¶
Le répertoire de rollback affiché par un rollout courant peut être rejoué explicitement tant qu'il reste une autorité active et que toutes ses dépendances SHA256 sont présentes :
CCX_ROLLBACK_DIR=<REPERTOIRE_ETAT_DEPLOIEMENT> \
bash scripts/platform-worker-safe-deploy.sh rollback
La même validation de hashes, image, mounts, networks et IDs s'applique.
Les rollbacks historiques P0.9 (20260903T103838Z) et P0.11 (20260903T151247Z) ont été retirés comme autorités actives lors de la clôture P0.11. Ils sont conservés byte-for-byte dans l'archive de preuve décrite plus bas et ne doivent pas être rejoués directement depuis l'archive. Toute restauration de cette autorité historique exige une décision de recovery distincte et une revalidation complète avant de restaurer les chemins historiques.
Maintenance et sauvegardes¶
La maintenance HTTP volontaire n'est pas une panne. Le contrat actuel peut être :
/->503intentionnel ;/wp-json/->200;/wp-admin/-> réponse compatible avec le routing/authentification WordPress.
Le déploiement refuse de démarrer si le timer de backup offsite n'est pas actif/enabled ou si son dernier run n'est pas réussi et suffisamment récent. Le déploiement ne lance aucun prune Restic et ne supprime aucun volume.
Recovery historique archivé — P0.11 clôturé¶
P0.10 a certifié le runtime canonique et P0.11 a ensuite certifié l'indépendance complète du runtime vis-à-vis des overrides historiques. Les chemins actifs suivants ont été retirés sans suppression destructive :
~/.local/state/cmonchoix/live-ops/20260901T104715Z/
~/.local/state/cmonchoix/deployments/20260903T103838Z/
~/.local/state/cmonchoix/p0-11-reprovenance/20260903T151247Z/
La preuve complète est conservée sous :
~/.local/state/cmonchoix/archive/p0-11-recovery-retirement-20260903T151852Z/
Cette archive contient notamment :
- les quatre fichiers
live-opsoriginaux ; - le rollback P0.9 ;
- le rollback P0.11 ;
- une copie du
docker-compose.ymlhistorique ; make-doctor-after-recovery.txtcomme preuve d'incident ;RETIREMENT.txt;MANIFEST.sha256, avec 35 entrées vérifiées lors de la publication.
Les fichiers de rollback archivés conservent volontairement leurs chemins absolus historiques. Ils constituent une preuve forensique, pas une autorité exécutable. Ne jamais modifier l'archive pour la rendre artificiellement rejouable.
Avant toute utilisation exceptionnelle de cette preuve dans un futur incident :
- vérifier
MANIFEST.sha256dans l'archive ; - qualifier le runtime actuel, les volumes, la base, les backups et les traitements actifs ;
- décider explicitement si une restauration des chemins historiques est réellement nécessaire ;
- préparer une procédure de recovery dédiée avec validation des SHA256 et autorisation opérateur ;
- ne restaurer aucun override ou rollback historique de manière ad hoc.
La présence résiduelle d'une référence textuelle historique dans un journal d'incident n'est pas une dépendance runtime et ne doit pas être réécrite après coup.
Voir aussi recovery-p0-11-archive.md pour le statut forensique et les conditions de restauration exceptionnelle.