Gabarit : la porte joue enfin (hooks, gitleaks), socle 0.23.0, tests web, variante une-adresse #42

Merged
marc-agbanavor merged 12 commits from marc/hub-gabarit into dev 2026-10-15 07:39:27 +00:00

Correctifs trouvés en créant le premier satellite réel depuis le gabarit (EQS/hub, le hub d'identité), comme convenu avec Nicolas.

Sécurité — la porte bloquante ne jouait pas

  • Hooks livrés en 100644 : git les ignorait (simple « hint »), donc ni gitleaks ni pnpm check au push dans AUCUN clone. Passés en 100755 + tâche hooks dans pnpm check (scripts/verifier-hooks.mjs) qui échoue si un hook perd son bit d'exécution. (5a6899e)
  • gitleaks en conteneur ne scannait rien dans un git worktree (0 octet, « no leaks found ») : le répertoire git commun est monté en lecture seule. Avant : fuite non vue ; après : commit refusé. (cc7f0e0)
  • Le pre-push ne scannait jamais une branche NOUVELLE (--not --all excluait la branche elle-même) : sur un distant jetable, l'ancien hook laissait passer une clé AWS, le nouveau la refuse. (4d2c97e)

Socle et outillage

  • @eqs/* 0.23.0 (TRUSTED_PROXIES dans .env.example, conformité à jour ; la variante next-fullstack suit : appliquée à un clone, check vert et intégration 17/17). (c5d18e7)
  • next 16.4.0 : l'audit du pre-commit bloquait GHSA-cjq9-62q9-8jv4 (HIGH) sur 16.3.7 ; 16.4.1 a moins d'un jour (minimumReleaseAge). (fb0e4fa)
  • Tests web : apps/web n'avait pas de configuration vitest (ses tests ne tournaient jamais) et globsTests de la conformité ne connaissait que l'API (testing.fichier-orphelin déclarait les tests web orphelins). (f5c12c9)
  • agentRules: false : Next 16.4 réécrivait apps/web/AGENTS.md à chaque next dev. (9e6262c)

Variante opt-in variantes/une-adresse

Une seule adresse publique : proxy Caddy par chemin (/api/*, /.well-known/*, /health, /version, /openapi.json, /docs* → api, le reste → web), front qui appelle l'API sur la même origine, compose à un seul domaine. C'est le montage du hub (déployé sur hub.dev.equilibre-structures.fr). Le défaut reste deux domaines. eqs coolify verify demande une dispense datée, documentée dans le README de la variante. (3b9ffb9)

Vérifications

  • pnpm check vert, pnpm --filter api build, pnpm test:integration 17/17.
  • Clone neuf de la branche : pnpm install --frozen-lockfile + pnpm check verts, hooks en 100755, un secret est refusé au commit et au push, un push propre passe.
  • origin/dev (08f47c0, plus d'e2e en CI) fusionné dans la branche, check vert.

À voir côté eqs-platform (pas dans cette PR)

  1. Guide étape 5 : le gabarit livre bien un lockfile.
  2. Guide étape 6 : env -u DATABASE_URL pnpm --filter api start recharge .env et ne rend jamais la main ; DATABASE_URL= pnpm --filter api start sort en 1 en nommant la variable.
  3. Guide, tableau d'état : encore « au 2026-09-09, 0.7.0 ». Étape 13 : mentionner la variante une-adresse.
  4. eqs inspect rend hooks.preCommit: true dès que le fichier existe, sans vérifier le bit d'exécution.
  5. eqs coolify verify lit le Dockerfile racine pour chaque service construit (faux secret-de-build-absent sur proxy).
  6. testing.fichier-orphelin : les globs ne sont pas rattachés à un projet (les globs web masquent un test API non couvert).
  7. Probable défaut, non corrigé ici : dans le compose par défaut, NEXT_PUBLIC_API_URL n'est posé qu'à l'exécution, pas au build — le bundle client l'a vide, donc en deux domaines les appels du navigateur partent vers le domaine du front.

🤖 Generated with Claude Code

Correctifs trouvés en créant le premier satellite réel depuis le gabarit (EQS/hub, le hub d'identité), comme convenu avec Nicolas. ## Sécurité — la porte bloquante ne jouait pas - **Hooks livrés en 100644** : git les ignorait (simple « hint »), donc ni gitleaks ni `pnpm check` au push dans AUCUN clone. Passés en 100755 + tâche `hooks` dans `pnpm check` (`scripts/verifier-hooks.mjs`) qui échoue si un hook perd son bit d'exécution. (`5a6899e`) - **gitleaks en conteneur ne scannait rien dans un git worktree** (0 octet, « no leaks found ») : le répertoire git commun est monté en lecture seule. Avant : fuite non vue ; après : commit refusé. (`cc7f0e0`) - **Le pre-push ne scannait jamais une branche NOUVELLE** (`--not --all` excluait la branche elle-même) : sur un distant jetable, l'ancien hook laissait passer une clé AWS, le nouveau la refuse. (`4d2c97e`) ## Socle et outillage - `@eqs/*` 0.23.0 (`TRUSTED_PROXIES` dans `.env.example`, conformité à jour ; la variante next-fullstack suit : appliquée à un clone, check vert et intégration 17/17). (`c5d18e7`) - next 16.4.0 : l'audit du pre-commit bloquait GHSA-cjq9-62q9-8jv4 (HIGH) sur 16.3.7 ; 16.4.1 a moins d'un jour (minimumReleaseAge). (`fb0e4fa`) - Tests web : `apps/web` n'avait pas de configuration vitest (ses tests ne tournaient jamais) et `globsTests` de la conformité ne connaissait que l'API (`testing.fichier-orphelin` déclarait les tests web orphelins). (`f5c12c9`) - `agentRules: false` : Next 16.4 réécrivait `apps/web/AGENTS.md` à chaque `next dev`. (`9e6262c`) ## Variante opt-in `variantes/une-adresse` Une seule adresse publique : proxy Caddy par chemin (`/api/*`, `/.well-known/*`, `/health`, `/version`, `/openapi.json`, `/docs*` → api, le reste → web), front qui appelle l'API sur la même origine, compose à un seul domaine. C'est le montage du hub (déployé sur hub.dev.equilibre-structures.fr). Le défaut reste deux domaines. `eqs coolify verify` demande une dispense datée, documentée dans le README de la variante. (`3b9ffb9`) ## Vérifications - `pnpm check` vert, `pnpm --filter api build`, `pnpm test:integration` 17/17. - Clone neuf de la branche : `pnpm install --frozen-lockfile` + `pnpm check` verts, hooks en 100755, un secret est refusé au commit et au push, un push propre passe. - `origin/dev` (08f47c0, plus d'e2e en CI) fusionné dans la branche, check vert. ## À voir côté eqs-platform (pas dans cette PR) 1. Guide étape 5 : le gabarit livre bien un lockfile. 2. Guide étape 6 : `env -u DATABASE_URL pnpm --filter api start` recharge `.env` et ne rend jamais la main ; `DATABASE_URL= pnpm --filter api start` sort en 1 en nommant la variable. 3. Guide, tableau d'état : encore « au 2026-09-09, 0.7.0 ». Étape 13 : mentionner la variante une-adresse. 4. `eqs inspect` rend `hooks.preCommit: true` dès que le fichier existe, sans vérifier le bit d'exécution. 5. `eqs coolify verify` lit le Dockerfile racine pour chaque service construit (faux `secret-de-build-absent` sur `proxy`). 6. `testing.fichier-orphelin` : les globs ne sont pas rattachés à un projet (les globs web masquent un test API non couvert). 7. Probable défaut, non corrigé ici : dans le compose par défaut, `NEXT_PUBLIC_API_URL` n'est posé qu'à l'exécution, pas au build — le bundle client l'a vide, donc en deux domaines les appels du navigateur partent vers le domaine du front. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Reviewed-on: #41
De 0.17.1 à 0.23.0 en une fois. Ce que la montée demande, et rien de plus :

- `.env.example` déclare `TRUSTED_PROXIES` (0.21, #137 du socle) :
  `config.env-example` la réclamait, bloquant.
- La suite de conformité fournit `jetons.cleCompteSuspendu` — une clé
  valide dont le propriétaire est `banned` — au nouveau contrôle
  `auth.cle-compte-suspendu` (0.21, #138), qui restait « annoncé et jamais
  joué ». La variante suit, et importe `registerEqs` de
  `@eqs/platform-next/register` (#142).
- Baselines rejouées (`eqs baseline`), lockfile rafraîchi.
- README : la section « état » est re-datée (0.23.0, 2026-10-15) ; la
  tâche `knip` tourne ; la réserve « ^0.3.0 / 0.4.0 » de la variante n'était
  plus vraie depuis longtemps.

Porte verte, intégration 17/17. Variante posée sur un clone : check vert,
intégration 17/17.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
`next@16.3.7` portait GHSA-cjq9-62q9-8jv4 (HIGH, SSRF de l'optimisation
d'images) et cinq avis de moindre gravité, corrigés en 16.3.8. Le
pre-commit le refusait dès qu'un manifeste bougeait — c'est-à-dire dès qu'il
a recommencé à tourner.

16.4.0 et pas 16.4.1 : la seconde a moins d'un jour, et pnpm 11 ne
l'installe qu'en l'ajoutant à `minimumReleaseAgeExclude` — une protection
qu'on ne lève pas pour un correctif déjà présent dans la précédente.
La variante suit.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Les trois fichiers de `.githooks` étaient committés en 100644 : git ignore
un hook sans bit `x` (un `hint:` que personne ne lit), et la porte
bloquante — gitleaks au commit, `pnpm check` + gitleaks au push — ne jouait
dans AUCUN clone. `eqs inspect` annonçait pourtant `hooks.preCommit: true`,
parce qu'il regarde si le fichier existe. Découvert en créant le hub
(EQS/hub ea57baa).

- `.githooks/*` passent en 100755.
- `scripts/verifier-hooks.mjs` lit le mode dans l'index (`git ls-files -s`,
  donc juste aussi sous Windows) et, hors Windows, le bit sur le disque. Il
  est déclaré en tâche `hooks` de `pnpm check` (`eqs.check.taches`) et
  s'appelle seul par `pnpm hooks`. Éprouvé dans les deux sens : rouge sur
  les modes d'origine, vert après.

Le premier commit joué par le hook réveillé a aussitôt refusé un avis HIGH
sur `next` (commit précédent) : la porte n'avait rien vu depuis le début.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Dans un worktree, `.git` est un fichier qui pointe vers
`<dépôt principal>/.git/worktrees/<nom>` par un chemin absolu de l'hôte. Le
repli docker de `_gitleaks.sh` ne montait que la copie de travail : git
répondait « not a git repository » dans le conteneur, gitleaks scannait
0 octet et concluait « no leaks found », code 0. Le pre-commit et le
pre-push passaient sans avoir rien regardé.

Le conteneur reçoit maintenant le répertoire git COMMUN, en lecture seule,
et `GIT_DIR`/`GIT_WORK_TREE` désignent le git-dir : une seule forme pour un
clone ordinaire, un worktree ou un sous-module. Un git-dir hors du
répertoire commun est refusé en le disant, plutôt que scanné à vide.

Éprouvé sur un clone et un worktree jetables, une clé AWS indexée : avant,
le worktree rendait « no leaks found » (0 octet) ; après, « leaks found: 1 »
et le commit est refusé, dans les deux. Les commits poussés (`--log-opts`)
sont lus aussi. Ce commit-ci est passé par le hook corrigé, depuis un
worktree.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
`globsTests` de la suite de conformité ne listait que les motifs de l'API :
dès qu'`apps/web` a un test, `testing.fichier-orphelin` le déclare jamais
exécuté et l'intégration échoue (EQS/hub e009d97). Il gagne
`src/**/*.test.ts` et `src/**/*.test.tsx`.

Et le contrôle avait raison à moitié : `apps/web` n'avait pas de
configuration Vitest, et `eqs-check` ne crée `unit:web` que pour un projet
qui en a une. Un test du front tournait sous `pnpm --filter web test`, et
JAMAIS dans `pnpm check` ni au pre-push. `apps/web/vitest.config.mts` pose
le même motif, `passWithNoTests` pour le gabarit qui n'a aucun test.

Éprouvé avec un `apps/web/src/essai.test.ts` jetable : avant,
`[ecart] testing.fichier-orphelin` et 16/17 ; après, 17/17, et `unit:web`
le joue (1 test) — puis passe à vide une fois le fichier retiré.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Pour une branche qui n'existe pas encore sur le distant, la plage passée à
gitleaks était `<sha> --not --all`. `--all` compte la branche LOCALE qu'on
pousse, qui pointe sur ce même `<sha>` : la plage était toujours vide.
Toute branche neuve partait avec « 0 commits scanned. no leaks found » —
c'est ce qu'a affiché le premier push de cette branche-ci.

La plage exclut maintenant ce que le distant visé a déjà, d'après nos
références de suivi (`--remotes=<distant>`, ou `--remotes` quand on pousse
vers une URL). Un premier push vers un distant vide scanne tout
l'historique, ce qui est juste.

Éprouvé sur un distant nu jetable, une clé AWS dans un commit de branche
neuve : l'ancien hook l'a laissée partir (0 commit scanné), le nouveau la
refuse (1 commit, « leaks found: 1 »), depuis un clone comme depuis un
worktree.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Next 16.4 ajoute au premier `next dev` un bloc « This is NOT the Next.js
you know » à `apps/web/AGENTS.md`, et le recrée si on l'enlève : l'arbre
est sale après chaque `pnpm dev`, et le fichier écrit à la main a un second
auteur qui se régénère seul (EQS/hub).

Éprouvé en 16.4.0 : sans le réglage, `next dev` annonce « Generated
AGENTS.md for AI agents » et modifie le fichier ; avec, rien ne bouge.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Facultative ; le défaut reste deux domaines. Tirée du hub (EQS/hub dc339c4,
proxy/), qui en avait besoin pour servir un serveur d'autorisation à une
seule adresse — et qui gagne au passage une image de front sans aucune
adresse d'API dedans, la même en dev et en prod.

- `variantes/une-adresse/proxy/` : Caddy, `/api/*`, `/.well-known/*`,
  `/health`, `/version`, `/openapi.json`, `/docs*` vers l'API, le reste vers
  le front ; `X-Forwarded-For` du client conservé derrière Traefik.
- `variantes/une-adresse/docker-compose.coolify.yml` : le compose par défaut
  plus le service `proxy` (sonde, restart, estampille), seul à recevoir un
  domaine ; `NEXT_PUBLIC_API_URL` vidée pour le front.
- `apps/web/next.config.mjs` : en développement ET `NEXT_PUBLIC_API_URL`
  vide, les mêmes chemins sont réécrits vers l'API ; inerte sinon. Le port
  de l'API arrive par `PORT_API`, que `scripts/next.mjs` relève avant de
  lancer Next : `next dev` pose `PORT` au port du FRONT, et lire `PORT` dans
  la configuration faisait boucler la réécriture sur elle-même (le hub a ce
  défaut). 127.0.0.1 plutôt que `localhost`, que bien des postes résolvent
  d'abord en `::1`.
- README de la variante : quand la choisir, la poser, les chemins, Coolify,
  et la dispense que `eqs coolify verify` exige à tort (il lit le Dockerfile
  de la racine pour tout service construit).

Joué : API + `next dev` en même origine (`/version`, `/health`, `/api/moi`,
`/openapi.json`, `/docs`, `/api/auth/get-session` servis par le port du
front), défaut inchangé (404 sur ces chemins côté front) ; image du proxy
construite et branchée sur les deux processus, même routage, sonde verte ;
`eqs doctor` et `eqs coolify verify` propres sur un clone où la variante est
posée, et ce dernier refuse toujours un `proxy` sans sonde ni estampille.
Pas joué : un déploiement Coolify depuis le gabarit.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- README : une section « Où ce dépôt et le guide divergent » — le guide
  d'`eqs-platform` dit qu'il n'y a pas de lockfile (il y en a un), propose à
  l'étape 6 une vérification qui ne vérifie rien (`env -u DATABASE_URL pnpm
  --filter api start` recharge `.env` et ne rend jamais la main ;
  `DATABASE_URL= …` sort en 1 en la nommant, joué dans les deux sens), et
  ne connaît ni la tâche `hooks` ni `variantes/une-adresse`.
- README : `/eqs-init` vit dans `eqs-platform`, pas dans ce dépôt.
- `apps/web/AGENTS.md` : la CSP est tenue par `src/proxy.ts`, plus par un
  `middleware.ts` que Next 16 ne lit plus.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
`cli.baseline-presente` signalait une baseline plus ancienne que le dernier
commit sur `apps/`. Rien d'autre ne bouge que la date, le commit et la
durée de la porte. Porte verte, intégration 17/17.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
marc-agbanavor deleted branch marc/hub-gabarit 2026-10-15 07:39:27 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Infrastructure/eqs-satellite-template!42
No description provided.