# Infrastructure cloud — état du chantier au 23/08/2026

> **À lire en premier par toute session qui reprend ce sujet.** Ce document dit
> où en est le cadrage, ce qui a été décidé et pourquoi, ce qui attend un
> arbitrage humain, et **ce qu'une session neuve ne peut pas deviner**.
> Il ne remplace pas [`INFRA_CLOUD_ECARTS.md`](./INFRA_CLOUD_ECARTS.md) (les
> mesures) ni [`INFRA_CLOUD.md`](./INFRA_CLOUD.md) (le cadrage) : il les situe.

## L'objectif, en une phrase

Piloter le LMS et lancer des fils de développement **ordinateur fermé**, depuis
un mobile ou une tablette. Critère de réussite : *trier un signalement, lancer un
fil, évaluer son annonce, passer le train, vérifier le déploiement — sans aucune
connexion à la machine locale.*

## Où sont les choses

| Quoi | Où |
|---|---|
| Cadrage d'origine (21/08) | `docs/process/INFRA_CLOUD.md` |
| Document d'écarts + journal des révisions | `docs/process/INFRA_CLOUD_ECARTS.md` |
| Ce document | `docs/process/INFRA_CLOUD_ETAT.md` |
| PR qui porte les trois | **#572**, base `development` |
| Note Docker antérieure (06/08), **à lire** | `docs/roadmap/16_NOTE_DOCKER_CI_LILIAN.md` |

⚠️ **PR #570 est supersédée.** Elle portait le seul cadrage, sa base est
`development`, et elle est **bloquée pour toujours en l'état** : elle a été
ouverte avant le renommage du job de test et porte le contrôle
« Suite — vert ou rouge », alors que la protection de `development` exige
désormais « **Suite — vert ou rouge (MySQL 8)** ». Le contrôle requis ne
s'exécutera jamais sur cette branche ; attendre ne sert à rien.
**#572 contient son commit** (`942bfb0`) après rebase sur `development`, porte
les deux fichiers et **rapporte sous le bon nom de contrôle**. Merger #572 rend
#570 vide. *La fermeture de #570 n'a pas été faite ici : elle ne relève pas de ce
fil.*

## Les issues du lot

| # | Écart | État |
|---|---|---|
| **#585** | Docker en session cloud — la question pivot | **Répondue** : oui, mais bloquée (voir plus bas) |
| **#573** | Script d'environnement de session | Corps révisé 23/08 — **périmètre réduit à MySQL 8** |
| **#575** | La CD ne joue aucune migration | ⚠️ **En contradiction ouverte avec #626** |
| **#576** | Épreuve de bout en bout | En attente |
| **#577** | Journaux serveur | Hors lot, volontairement |
| **FRONT#327** | Playwright non tracé en dépendance | En attente |
| ~~#574~~ | ~~MySQL à la demande~~ | **Fermée** — tranchée en sens inverse par #578 |

## Les décisions prises, et leur pourquoi

**1. Les secrets ne descendent pas dans les sessions — et il n'y a rien à faire
pour ça.** L'inventaire est fait : les deux suites tournent avec **zéro secret**
(prouvé par la CI), et le profil de développement du `.env` ne contient **aucun
identifiant tiers renseigné**. Le `.env` n'est jamais transporté : il est
**reconstruit** depuis `.env.example` (qui, lui, est versionné — c'est un gabarit
à valeurs vides, pas un `.env`). Conséquence heureuse : **une rotation de clés
est un non-événement** pour les sessions, puisque rien n'a été copié.

**2. L'arbitrage de gouvernance des secrets est SANS OBJET**, et pour deux
raisons indépendantes qui ferment la même porte :
- le filtre d'IP autorisées de Brevo **couvre le SMTP** (erreur dédiée
  `525 5.7.1 Unauthorized IP address`, liste commune API/SMTP), et l'IP d'une
  session cloud sort par une passerelle NAT partagée ;
- **`tcp/587` ne sort pas** de la session cloud (mesuré) — il n'y a même pas de
  socket à autoriser.

Il n'y a donc pas de choix à faire : un fil vérifie que le mail est *fabriqué*,
l'envoi réel se constate ailleurs. **Cela ne dissout pas `BACK#316`** (séparer
les clés staging ↔ prod), qui reste entièrement valide et indépendante.

**3. É2 a été tranché en sens inverse du nôtre, par #578, et c'est mieux.** Ce
document proposait MySQL « à la demande » ; #578 en fait le **moteur unique** par
parité production. Motif plus fort que l'argument de coût : 14 migrations portent
un aiguillage `getDriverName() === 'mysql'` jamais joué sous SQLite. #574 fermée.

**4. #573 sera un script NATIF, pas Docker** — non par préférence, par contrainte
(voir plus bas).

## ⛔ Ce qui bloque, et qui ne dépend pas de nous

**Docker fonctionne en session cloud** — prouvé par exécution dans deux sessions
distinctes : image construite `FROM scratch` puis exécutée, image tirée du réseau
puis exécutée. **Mais aucune image utile ne peut être tirée** : tous les registres
délèguent leurs blobs à des CDN hors allow-list.

| Registre | Manifeste | Blobs | Pull réel |
|---|---|---|---|
| Docker Hub | ✅ | `production.cloudfront.docker.com` refusé | ❌ |
| `public.ecr.aws` | ✅ | `d2glxqk2uabbnd.cloudfront.net` refusé | ❌ |
| `ghcr.io` | ✅ | `pkg-containers.githubusercontent.com` refusé | ❌ |
| `mcr.microsoft.com` | ✅ | sert ses blobs lui-même | ✅ |

`mcr.microsoft.com` ne publie ni `mysql:8` ni les images `php` officielles.
**La demande à formuler porte sur les hôtes de BLOBS**, pas sur
`registry-1.docker.io` qui passe déjà et ne changerait rien. **Nous ne
connaissons pas la procédure pour faire évoluer cette allow-list** — c'est la
politique du bac à sable, pas un réglage de notre côté.

**Question jumelle, NON MESURÉE, qui décide des deux branches** :
**`apt-get` fonctionne-t-il derrière le proxy de la session ?** Si oui,
`mysql-server` 8.0 est dans les dépôts Ubuntu 24.04 et #573 est fait. Si non, le
script natif meurt du même mal que Docker.

## Ce qui attend un arbitrage humain

1. **#575 contre #626 — automatiser l'EXÉCUTION ou l'ALERTE ?** Deux
   conversations, deux réponses, sans se voir. Le postulat de ce cadrage, ce qui a
   été écarté et une réconciliation possible sont écrits **dans #575**, sans
   décision. ⚠️ **Point d'honnêteté** : le cadrage d'origine disait « workflow
   déclenché **à la main** » ; une révision du 23/08 l'a déplacé vers un
   déclenchement automatique dans la CD **sans que ce déplacement soit arbitré**.
   C'est cette dérive qui crée la collision, pas le cadrage.
2. **L'ordre des travaux** a reçu un « ok » le 22/08, avant que #585 ne réponde.
   À reconfirmer au vu du périmètre réduit de #573.
3. **La règle de gouvernance des secrets** : sans objet techniquement, mais
   l'écrire pendant que l'ensemble est vide reste recommandé.

## Ce qu'une session neuve ne peut pas deviner

- **Le worktree `wt-infra-cloud`** est en cours, à
  `/Users/enguerranbrembilla/Projects/numedia/dev-agency/wt-infra-cloud`, sur la
  branche `docs/infra-cloud-ecarts`. Il porte les trois documents. **Ne pas
  ouvrir un second chantier dans le clone principal.**
- **Aucune valeur de secret n'a jamais été lue** dans ce chantier. Les mesures
  portent sur des **noms de variables**, des longueurs et des classes de valeur.
  Les variables concernées, **par nom uniquement** : `APP_KEY`, `DB_PASSWORD`,
  `MAIL_USERNAME`, `MAIL_PASSWORD`, `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`,
  `REDIS_PASSWORD` côté serveur ; `DEPLOY_SSH_KEY`, `DEPLOY_HOST`, `DEPLOY_USER`,
  `FRONT_ENV_STAGING` côté secrets GitHub. **Tenir cette règle en reprise.**
- **Deux pièges de mesure déjà payés** : `ps aux | grep -i [d]ockerd` **se matche
  lui-même** sous `bash -c` (utiliser `pgrep -a dockerd`) ; et **la sonde d'entrée
  n'est pas le service** — `docker --version` ne dit rien du démon, une sonde
  `/v2/` ne dit rien de la capacité à tirer une image. Une première mesure a
  conclu à tort que `ghcr.io` passait sur cette base.
- **Un 403 de proxy n'est pas une mesure du sujet** : le double appel à
  `api.ipify.org` proposé pour juger la stabilité de l'IP ne mesurait rien, le
  domaine étant hors allow-list.
- **Environnement de session cloud**, stable d'une session à l'autre : Ubuntu
  24.04, `uid=0`, `sudo` sans mot de passe, **PHP 8.4.19**, **Node 22.22.2**,
  **`mysqld` absent**, composer 2.8.12, `HTTPS_PROXY` sur un **port tiré par
  session** (ne rien y câbler en dur), **cgroup v1**.
- **PHP 8.4 et Node 22 SUFFISENT** : `composer.json` exige `^8.2` et
  `phpspreadsheet 1.30.6` exige `>=7.4.0 <8.5.0` ; `package.json` déclare
  `engines: >=20 <25`. La CI **épingle** 8.2/24 pour la reproductibilité — ce
  n'est pas la même chose que ce que le projet **exige**. Ne pas reconfondre les
  deux : c'est l'erreur qui a fait croire le chantier trois fois plus gros.
- **Le poste local, lui, EST hors bornes** : PHP 8.5.4 (dépasse `<8.5.0`, casse
  `composer install`), Node 26.7 (dépasse `<25`), MySQL 9.6.
- **Une règle de la charte est inapplicable en session cloud** :
  `FIL_DE_CHANTIER.md` §1 impose « `.env` copié du clone principal, **jamais**
  `cp .env.example` ». Il n'y a pas de clone principal dans une session cloud. À
  amender par un critère (« lorsqu'il en existe un »), pas par une énumération.
- **Aucune tâche planifiée** n'a été créée par ce chantier.
- **Aucune page publiée** (artefact) n'a été produite par ce chantier — rien à
  mettre à jour par URL.
- **Rien n'a été mergé ni déployé.** Aucun code n'a été écrit : le chantier est
  en instruction, pas en production.

## Ce qui vient ensuite

Dans l'ordre, et rien ne part avant arbitrage :

1. Mesurer `apt-get` derrière le proxy (sonde dans **#573**) — décide la
   faisabilité du script natif.
2. **#573** — le script, réduit à MySQL 8 + Playwright.
3. **FRONT#327** — Playwright déclaré en dépendance.
4. **#576** — l'épreuve de bout en bout, qui valide tout le reste.
5. **#575** — après l'arbitrage #575/#626.
