Déploiement

Une app Fougere est une app Nitro plus une chose que Nitro ignore : les sources des Fronds sont lues au runtime (scan, parse AST, chargement jiti). Cette page est ce que le déploiement de ce site même a appris — la checklist, puis la réalisation Docker.

La checklist runtime

  1. .output/ — le build Nitro (pnpm build).
  2. Les sources des Fronds — à côté de .output : le scanner les lit depuis process.cwd() au boot. fronds/**/*.ts, et quand la racine est elle-même la Frond, les dossiers de la convention à la racine (entities/, handlers/, presenters/, collectors/, seeds/, services/, repositories/). Ne copier que fronds/ laisse une app à plat sans domaine et sans erreur — voir le log de boot plus bas.
  3. Un node_modules résoluble pour les fronds — ces sources importent @fougere/schema, @fougere/core, better-auth… par leur nom. pnpm deploy --prod produit exactement cette fermeture ; trier les paquets à la main, c'est réimplémenter un tracer — non.
  4. Un TS avec l'API JS du compilateur (5.9 — TS 7 n'en a pas) — importé dynamiquement par le scanner, invisible au tracer de Nitro.
  5. Des paquets lazy complets — la trace pnpm de Nitro pour jiti est partielle (sa transformation babel est chargée à la demande). Le remplacer par le vrai paquet.
  6. Un .data/ writable — le fichier SQLite et l'index Nuxt Content y vivent. C'est la seule chose à persister : un volume.
  7. Les secrets par envAUTH_SECRET (32+ caractères), le SITE_URL public.

Le signal de vérification est le log de boot — pas un 200 sur les pages (le SSR rend très bien avec zéro frond chargée) :

INF [boot:app] scanned 2 frond(s) in 467ms      ← cette ligne
{"jsonrpc":"2.0","id":1,"result":[]}            ← et un appel d'enveloppe

Docker

Le site livre une paire qui marche — site/Dockerfile + site/docker-compose.yml :

SITE_AUTH_SECRET='…32+ caractères…' SITE_URL='https://votre-domaine' \
  docker compose -f site/docker-compose.yml up -d --build

Le Dockerfile réalise la checklist en deux étages :

# étage build : install workspace → build packages → build site
RUN pnpm --filter "./packages/**" -r run build \
 && pnpm --filter "@fougere/site" run build
# la fermeture de résolution des fronds, par pnpm lui-même :
RUN pnpm --filter "@fougere/site" deploy --prod --legacy /deploy
# compléter la trace partielle de jiti + ajouter TS 5.9

# étage run : node:22-slim, non-root
COPY --from=build /repo/site/.output ./.output
COPY --from=build /repo/site/fronds  ./fronds
COPY --from=build /deploy/node_modules ./node_modules
VOLUME /app/.data
CMD ["node", ".output/server/index.mjs"]

L'état vit dans le volume site-data monté sur /app/.data — un backup est une copie de fichier.

Splitter en production

Le même modèle d'image s'applique au split : le process host qui sert une Frond a besoin des points 1–5 pour ses fronds, et le consommateur la déclare dans remotes. Un host mort dégrade en 503 typées dans les pages (Le gradient) ; le relancer est la récupération — le consommateur n'est jamais rebuildé.

Trois choses s'appellent « ensemble » et une seule est obligatoire. Le contrat doit voyager — le consommateur doit savoir ce que l'hôte sert. Le code, non : fougere sync lit la carte d'identité de l'hôte et reconstruit un schéma vivant en local, ce qui est d'ailleurs la seule façon de consommer une Frond écrite dans un autre langage. Les ports doivent se joindre, et c'est le seul des trois qu'un déploiement décide.

Le récepteur écoute en loopback sauf mention contraire : le split le plus simple met donc les deux process sur une même machine — le même pod, ou une seule image. hosts: ['0.0.0.0'] l'élargit en une ligne — et remet à l'exploitant ce que le défaut retenait : un récepteur croit l'identité qu'on lui tend, donc l'atteindre à travers un réseau suppose de refermer ce chemin autrement (mesh, pare-feu, sidecar) tant que l'identité n'est pas réglée au niveau de la Frond.

Ces étapes sont candidates à un futur fougere build — un utilisateur ne devrait pas avoir à les découvrir. D'ici là, cette page est le contrat.

Construit avec Fougere — ce site tourne sur le framework qu'il documente.