Yayaw
Documentation

Configuration de l'environnement de déploiement

Collecter, borner et vérifier les variables d'environnement nécessaires au déploiement.

Cette page est le guide opérateur des variables d'environnement de déploiement. Utilisez-la lors de la restauration d'un environnement production, de la vérification d'une prévisualisation Coolify avec le même câblage fournisseur que la production, ou de la préparation du runtime Docker portable.

Règles

  • Ne commitez jamais de secrets dans .env, .env.production ou un fichier suivi.

  • Stockez les secrets production et preview dans le magasin de secrets Coolify/de l'orchestrateur. Pour la base Compose, utilisez un fichier non suivi .env.self-host copié depuis .env.self-host.example.

  • Stockez les secrets locaux dans un .env.local non suivi, alimenté depuis la source de secrets de confiance.

  • Gardez les valeurs NEXT_PUBLIC_* non secrètes: elles sont exposées au bundle navigateur.

  • Préférez les réglages admin/runtime pour les non-secrets opérationnels lorsque le dashboard les supporte. Par exemple, les réglages dépôt GitHub pour l'accès au code se gèrent depuis /dashboard/admin/billing-settings; les variables d'environnement ne sont que des replis.

Cible De Déploiement

La production et la prévisualisation Yayaw sont auto-hébergées via Docker/Coolify. Vercel n'est pas une cible de déploiement. N'exécutez pas la CLI Vercel, ne liez pas ce checkout, n'ajoutez pas de workflow de déploiement Vercel et ne créez pas de dossier local .vercel. Le vercel.json suivi est conservé uniquement pour maintenir git.deploymentEnabled: false pendant la suppression de toute ancienne intégration Git.

Utilisez les environnements GitHub development et production pour les entrées de build/déploiement d'images, et le magasin de secrets Coolify/orchestrateur pour les secrets runtime. Copiez les valeurs nécessaires au développement dans .env.local uniquement sur une machine de confiance.

Vérifiez que chaque clé listée dans .env.example est présente localement:

while IFS='=' read -r key _; do
  [[ -z "$key" || "$key" == \#* ]] && continue
  grep -q "^${key}=" .env.local || echo "Missing in .env.local: $key"
done < .env.example

Workflow Docker Auto-Hébergé

La base portable utilise Docker Compose avec Postgres, MinIO/S3, le serveur app Next.js standalone, un worker Page AI et Caddy:

cp .env.self-host.example .env.self-host
docker compose --env-file .env.self-host -f docker-compose.self-host.yml --profile setup run --rm migrate
docker compose --env-file .env.self-host -f docker-compose.self-host.yml up --build

Les builds Docker utilisent NEXT_BUILD_WORKERS=1 par défaut pour une génération statique Next.js stable sur de petits hôtes. Sur une machine plus large, vous pouvez l'augmenter, par exemple:

NEXT_BUILD_WORKERS=2 docker compose --env-file .env.self-host -f docker-compose.self-host.yml up --build

Les builds Docker auto-hébergés fixent aussi NEXT_STATIC_PAGE_GENERATION_TIMEOUT=180 par défaut. Cela donne aux hôtes lents assez de temps pour pré-rendre de grosses pages docs sans déclencher le timeout Next.js de 60 secondes. Baissez-le sur des builders CI rapides ou augmentez-le uniquement si les logs montrent des pages statiques légitimes en timeout.

Les builds Docker auto-hébergés rendent les pages Fumadocs dynamiquement au lieu de prégénérer toute la documentation pendant la construction d'image. Cela évite aux petits hôtes Coolify ou Docker de dépenser l'essentiel du budget de déploiement sur les docs tout en les servant normalement au runtime.

Les fichiers Compose taguent les cibles app et worker avec YAYAW_APP_IMAGE et YAYAW_WORKER_IMAGE. Gardez le même tag worker pour les services migrate et worker afin que Docker ou Coolify puisse réutiliser la cible worker au lieu de la construire deux fois.

Le déploiement Coolify managé par Yayaw utilise des images précompilées plutôt qu'un build sur la VM production. GitHub Actions construit les images app et worker sur des runners ubuntu-24.04-arm pour la plateforme linux/arm64 du Mac mini, pousse les images vers GHCR, les fait tirer par le serveur Coolify via le runner auto-hébergé privé, met à jour YAYAW_APP_IMAGE et YAYAW_WORKER_IMAGE, puis demande à Coolify de lancer le déploiement Compose. Cela conserve la release automatique sur main, déplace le build coûteux hors de la VM Mac mini et évite le quota de stockage artifacts GitHub Actions.

Coolify doit stocker ces tags d'image comme valeurs build-time et runtime, parce que Docker Compose interpole les entrées image: pendant la préparation du déploiement.

Le profil setup initialise le journal, applique les migrations Drizzle versionnées, puis lance le seed et bun run dynamic-data:repair-deployed-storage. Relancez-le avant le démarrage d'une base auto-hébergée fraîche et après les changements de schéma. Le setup ne pousse jamais le schéma. Une base sans aucune table publique reçoit, dans une seule transaction, la fonction préalable de validation des receipts, le schéma figé de la migration 0050 (src/lib/db/baseline/0050_better_auth_1_7_stable.sql), l'historique des migrations de 0000 à 0050, puis chaque migration suivante ; toute erreur sort avec un code non nul et laisse la base vide. La migration 0051 recrée et vérifie l'ensemble des fonctions et triggers propres aux migrations après réconciliation de la provenance de publication, des têtes de révision et du singleton runtime-floor. Une ancienne base gérée par push n'est acceptée que si toute sa forme table/colonne pré-0050 correspond à cette baseline, si elle ne contient aucune table que seule une migration ultérieure crée, si le préflight Better Auth passe et si l'attestation de suspension est active ; le setup enregistre 0049 avant d'appliquer 0050, la même réconciliation 0051 et chaque migration suivante. Le garde relance le préflight sur une base déjà migrée pour détecter les comptes créés par un rollback beta. Pour une base existante dont 0050 est en attente, il exige BETTER_AUTH_17_WRITES_PAUSED=true avant db:migrate. Le wrapper Compose résout aussi le contrat /api/ready actuellement servi via son BETTER_AUTH_RUNTIME_PROBE_URL privé : seule une preuve exacte stable-1.7 évite une nouvelle suspension sur une base déjà migrée ; un runtime indisponible, beta ou inconnu exige de nouveau l'attestation. Une valeur de contrat statique n'est pas considérée comme une preuve. Le bootstrap d'une base sans aucune table est la seule exception interne. Cette variable est une attestation opérateur, pas un verrou runtime : sauvegardez et vérifiez d'abord la base, bloquez les écritures /api/auth à l'ingress ou en arrêtant l'ancienne app, puis retirez immédiatement l'attestation après la bascule. L'étape dynamic-data couvre les tables natives MCP ydm_* qui ne font volontairement pas partie du schéma Drizzle statique.

Ne commitez pas .env.self-host. En production, déplacez les mêmes valeurs dans le secret manager de l'hôte cible ou l'environnement Compose. Générez BETTER_AUTH_SECRET avant le premier build Docker, par exemple avec openssl rand -hex 32; le Dockerfile échoue immédiatement lorsqu'il est vide. Le flag --env-file rend les mêmes valeurs publiques disponibles aux arguments de build Docker. BETTER_AUTH_SECRET est aussi passé à next build pour éviter que l'évaluation des routes Better Auth ne retombe sur un secret de développement pendant la construction d'image. Reconstruisez l'image quand les valeurs NEXT_PUBLIC_* changent.

Migration Rolling Coolify

docker-compose.coolify.yml reste l'unité de déploiement historique pendant la validation du chemin rolling. Il remplace ensemble migrate, l'app, le worker et Caddy; il ne peut donc pas fournir une release sans interruption. Ne repointez pas les workflows automatiques development ou production vers les nouvelles ressources avant que le workflow rolling manuel ait validé le rollback.

Migrez d'abord development:

  1. Sauvegardez Postgres et MinIO. Conservez l'UUID de la ressource Compose Coolify existante afin de réutiliser ses volumes postgres_data et minio_data.

  2. Activez Connect to Predefined Network sur la ressource d'infrastructure existante. Notez les hostnames résolus postgres-<resource-uuid> et minio-<resource-uuid>.

  3. Créez une Application Coolify Docker Image depuis l'image immuable ghcr.io/<owner>/yayaw-app:<sha>. Exposez le port conteneur 3000, sans mapping de port hôte, sans nom de conteneur, et connectez-la au réseau prédéfini. Configurez les identifiants du registre GHCR dans Coolify si le package est privé; les identifiants de pull temporaires du workflow ne configurent pas le registre de l'Application.

  4. Copiez l'environnement runtime de l'app vers cette Application. Remplacez les hôtes de DATABASE_URL et S3_ENDPOINT par ceux de l'infrastructure. Utilisez d'abord un domaine development temporaire.

  5. Gardez les healthchecks actifs. L'image Docker vérifie /api/ready, qui ne renvoie 200 que si Postgres répond et si le SHA immuable de l'image correspond au SHA demandé. /api/health reste limité à la liveness.

  6. Configurez une période de grâce d'arrêt de 20 à 30 secondes dans Coolify. Next.js traite SIGTERM et termine les requêtes en vol pendant cette fenêtre.

  7. Déplacez le worker Page AI vers une ressource séparée basée sur ghcr.io/<owner>/yayaw-worker:<sha>. Il ne doit pas rester dans la stack d'infrastructure qui survit aux releases web.

  8. Sur un clone de la base, vérifiez que drizzle.__drizzle_migrations représente fidèlement les migrations SQL déjà appliquées. Le runner rolling refuse de migrer une base non vide dont le journal est absent ou vide. Ne fabriquez pas de baseline directement en production.

  9. Ajoutez COOLIFY_ROLLING_APPLICATION_UUID et NEXT_SERVER_ACTIONS_ENCRYPTION_KEY à l'environnement GitHub development. Générez la seconde valeur une fois avec openssl rand -base64 32 et conservez la même valeur entre les builds.

  10. Lancez Coolify Rolling Deploy pour development. Il tire les deux images avec retry, exécute le garde de bascule Better Auth, les migrations Drizzle versionnées, la vérification du schéma, le seed et la réparation dynamic-data, puis met à jour l'Application Docker Image. Pour l'unique bascule Better Auth 1.7 d'une base existante, ne laissez pas l'ancienne app accepter des écritures auth : bloquez d'abord toutes les écritures /api/auth en externe, puis définissez la variable GitHub non secrète BETTER_AUTH_17_WRITES_PAUSED=true. Retirez-la après la migration. Un échec de déploiement ou du smoke public sur le SHA restaure automatiquement le tag précédent; les changements de base ne sont pas annulés.

  11. Après le succès du rollout web, promouvez la ressource worker séparée vers le tag immuable yayaw-worker:<sha> émis par le workflow. Gardez l'ancien worker actif jusqu'à ce que le processus de remplacement soit sain; toutes les évolutions du worker doivent rester compatibles avec le schéma migré pendant cette bascule.

  12. Validez volontairement une mauvaise image ou un échec de readiness et confirmez que l'ancien SHA public reste disponible. Répétez ensuite la procédure en production.

Une fois la nouvelle app et le worker sains, changez la ressource Compose existante vers docker-compose.coolify.infrastructure.yml. Définissez MINIO_MEDIA_FQDN sur l'origine publique suivie de /media et MINIO_ORGANIZATION_LOGOS_FQDN sur la même origine suivie de /organization-logos; Coolify crée les routeurs de chemin stables vers MinIO. Retirez le domaine de l'ancien Caddy et assignez le domaine racine à l'Application Docker Image pendant la même bascule. Le fichier d'infrastructure n'expose aucun port hôte et ne doit pas être redéployé lors des releases applicatives normales.

Chaque migration de ce chemin doit suivre expand/contract: ajoutez d'abord un schéma compatible, déployez du code tolérant les deux versions, puis supprimez l'ancien schéma seulement après la disparition de tous les anciens conteneurs et workers. Le rollback automatique d'image ne peut pas annuler une migration de base destructive.

Environnements CI Coolify

Les déploiements Coolify auto-hébergés sont pilotés depuis GitHub Actions:

  • les pull requests déploient vers une application Coolify development partagée après réussite de la CI

  • les pushes vers main évaluent la release production après réussite de la CI. Une flotte reader/freeze est déployée automatiquement ; une flotte exact produit un résumé de promotion requise sans modifier la production

Configurez deux environnements GitHub avec des UUID d'applications Coolify séparés:

Environnement GitHubSecret ou variable requisUsage
developmentCOOLIFY_URLOrigine API Coolify.
developmentCOOLIFY_API_TOKENToken autorisé à mettre à jour et déployer l'app development.
developmentCOOLIFY_APPLICATION_UUIDUUID de l'app development partagée.
developmentCOOLIFY_ROLLING_APPLICATION_UUIDUUID de l'Application Docker Image utilisée par le workflow manuel de validation rolling.
developmentDEVELOPMENT_BASIC_AUTH_USERNAMEUsername HTTP Basic de l'app development protégée.
developmentDEVELOPMENT_BASIC_AUTH_PASSWORDPassword HTTP Basic de l'app development protégée.
developmentvariable DEVELOPMENT_URLURL optionnelle de smoke test. Défaut https://dev.yayaw.app; utilisez une URL joignable par le runner interne si le DNS public n'est pas encore routé.
productionCOOLIFY_URLOrigine API Coolify.
productionCOOLIFY_API_TOKENToken autorisé à déployer l'app production.
productionCOOLIFY_APPLICATION_UUIDUUID de l'app production.
productionCOOLIFY_ROLLING_APPLICATION_UUIDUUID de l'Application Docker Image production après validation en development.
development, productionBETTER_AUTH_SECRETSecret build-time requis par le build de l'image Docker Next.js.
development, productionDATABASE_URLURL de base build-time pour l'évaluation des routes serveur.
developmentPOSTGRES_ROTATION_DATABASE_URLURL candidate temporaire utilisée seulement par la rotation des identifiants et la synchronisation runtime. Gardez DATABASE_URL sur l'identifiant fonctionnel courant jusqu'au succès du premier déploiement après rotation.
development, productionsecrets build NEXT_PUBLIC_*Valeurs publiques intégrées au bundle client Docker.
development, productionNEXT_SERVER_ACTIONS_ENCRYPTION_KEYClé AES base64 stable intégrée pendant next build afin que les instances superposées acceptent les mêmes payloads Server Actions.
development, productionvariable NEXT_BUILD_WORKERSNombre de workers de build. Défaut 1 pour stabiliser les builds Docker auto-hébergés.
development, productionvariable NEXT_STATIC_PAGE_GENERATION_TIMEOUTTimeout de génération statique. Défaut 180.
development, productionvariable COOLIFY_SERVER_SSH_HOSTOverride optionnel pour charger les images sur la VM Coolify. Défaut 127.0.0.1 depuis le runner Mac mini.
development, productionvariable COOLIFY_SERVER_SSH_PORTPort SSH optionnel. Défaut 2222.
development, productionvariable COOLIFY_SERVER_SSH_USERUtilisateur SSH optionnel. Défaut yannis.
development, productionvariable COOLIFY_SERVER_SSH_KEY_PATH ou secret COOLIFY_SERVER_SSH_KEYClé SSH optionnelle. Le runner Mac mini utilise par défaut /Users/yannis/.ssh/coolify_vm_ed25519.
development, productionvariable COOLIFY_SERVER_KNOWN_HOSTS_PATHKnown-hosts optionnel. Défaut /Users/yannis/.ssh/known_hosts_coolify.

Le préflight de release CMS synchronise aussi le contrat base, stockage privé, preview signé, personnalisation et maintenance worker vers l'app et le worker Coolify. Configurez ces secrets d'environnement GitHub :

  • S3_ACCESS_KEY_ID, S3_ENDPOINT et S3_SECRET_ACCESS_KEY

  • CMS_PREVIEW_SIGNING_SECRET, CMS_PERSONALIZATION_RATE_LIMIT_SECRET et CMS_PERSONALIZATION_FORM_CONTEXT_SECRET, chacun d'au moins 32 caractères ; gardez le secret de contexte de formulaire dédié

  • STORAGE_TRANSFER_SIGNING_SECRET optionnel lorsque les tokens de transfert stockage ne doivent pas partager le secret de signature des previews

  • DATABASE_URL dans les deux environnements

  • MINIO_ROOT_PASSWORD, POSTGRES_PASSWORD et le temporaire POSTGRES_ROTATION_DATABASE_URL et POSTGRES_ROTATION_PREVIOUS_PASSWORD en development

Configurez STORAGE_PROVIDER=s3, STORAGE_MEDIA_BUCKET=media, STORAGE_MEDIA_STAGING_BUCKET=media-staging, STORAGE_CMS_FORM_BUCKET=cms-form-submissions, CMS_PERSONALIZATION_CLIENT_IP_HEADER=x-real-ip, PAGE_AI_WORKER_MAINTENANCE_MS=60000, S3_REGION=us-east-1 et S3_FORCE_PATH_STYLE=true comme variables d'environnement GitHub. STORAGE_PUBLIC_BASE_URL est optionnel si NEXT_PUBLIC_BASE_URL désigne déjà la même origine publique. S3_SIGNED_PUBLIC_ENDPOINT est une variable optionnelle pour une origine d'upload direct explicitement configurée. Ne persistez pas de phase de rollout comme variable GitHub : les workflows de déploiement et de rollout dérivent et valident la phase sûre reader, freeze ou exact à partir de la flotte active avant de synchroniser les flags runtime correspondants.

Préparez une seule fois les identifiants development, hors déploiement :

database_password="$(openssl rand -hex 32)"
storage_password="$(openssl rand -hex 32)"
database_fingerprint="$(
  printf '%s' "$database_password" | shasum -a 256 | awk '{print $1}'
)"
rotation_database_url="$(
  printf 'postgresql://yayaw:%s@postgres:5432/yayaw' "$database_password"
)"

printf '%s' "$database_password" | gh secret set POSTGRES_PASSWORD --env development
printf '%s' "$rotation_database_url" \
  | gh secret set POSTGRES_ROTATION_DATABASE_URL --env development
printf '%s' "$storage_password" | gh secret set MINIO_ROOT_PASSWORD --env development
printf '%s' "$storage_password" | gh secret set S3_SECRET_ACCESS_KEY --env development
gh variable set CMS_DATABASE_CREDENTIALS_FINGERPRINT \
  --env development \
  --body "$database_fingerprint"

Le mot de passe de base est volontairement une valeur hexadécimale brute de 32 octets afin de ne pas nécessiter d'encodage URL. Pour un volume development existant, laissez DATABASE_URL sur l'identifiant fonctionnel courant pendant le build des images de PR. Le workflow transmet cette URL active à l'étape de rotation, qui en extrait le mot de passe brut en mémoire sans l'exposer dans les logs. Définissez POSTGRES_ROTATION_PREVIOUS_PASSWORD explicitement seulement si l'URL active emploie un encodage de mot de passe non pris en charge. Le job de déploiement n'utilise POSTGRES_ROTATION_DATABASE_URL qu'une fois les images disponibles : il teste l'identifiant candidat via l'alias privé yayaw-postgres-development; si nécessaire, il utilise un fichier env distant en permission 0600 pour modifier le rôle, reteste le nouvel identifiant puis synchronise l'URL candidate vers Coolify. Il s'arrête avant toute mutation des env Coolify si aucun identifiant ne fonctionne.

Après le premier déploiement development réussi avec l'identifiant tourné, promouvez l'URL candidate conservée puis supprimez le secret candidat temporaire :

printf '%s' "$rotation_database_url" \
  | gh secret set DATABASE_URL --env development
gh secret delete POSTGRES_ROTATION_DATABASE_URL --env development

Supprimez également POSTGRES_ROTATION_PREVIOUS_PASSWORD si le fallback explicite a été configuré.

Pour un volume neuf sans application en cours d'exécution, définissez aussi DATABASE_URL avec la même URL candidate avant le premier build. Ne remplacez jamais seulement DATABASE_URL et POSTGRES_PASSWORD sur un volume existant avant la fin du workflow de rotation.

Les workflows ont aussi besoin des permissions packages du dépôt: les jobs de build utilisent packages: write pour publier ghcr.io/<owner>/yayaw-app:<sha> et ghcr.io/<owner>/yayaw-worker:<sha>, tandis que les jobs de déploiement Coolify utilisent packages: read pour tirer ces images depuis le runner auto-hébergé.

L'app development partagée est volontairement mutable: chaque pull request force la branche technique coolify/development du dépôt vers le SHA de tête de la PR, met à jour la git_branch de l'app dans Coolify vers cette branche stable et déploie sans forcer de rebuild. Cela évite les échecs lorsqu'une branche PR mergée est supprimée avant que Coolify clone le dépôt, tout en laissant BuildKit réutiliser les layers entre development et production. GitHub Actions sérialise les déploiements development avec le groupe de concurrence development-coolify-deploy, ce qui garde l'environnement prévisible pendant que les nouvelles runs attendent la fin du déploiement en cours.

Gardez l'origine development privée. Préférez un hostname Tailscale-only ou activez HTTP Basic au niveau de l'app Coolify tout en renvoyant X-Robots-Tag: noindex, nofollow, noarchive. Le smoke test part du runner auto-hébergé avec les secrets Basic Auth development; ce runner doit donc atteindre l'URL protégée.

Socle Production

Ces valeurs sont requises avant qu'un déploiement production puisse servir l'app correctement:

VariableOù la récupérerIndication
NODE_ENVEnv application CoolifyDéfinir à production pour les conteneurs de production.
NEXT_PUBLIC_BASE_URLConfiguration reverse proxy/DNSDéfinir sur l'origine publique canonique, par exemple https://yayaw.app.
BETTER_AUTH_SECRETSecret managerValeur longue, aléatoire et stable pour cookies et tokens Better Auth.
SCIM_CREDENTIAL_HASH_SECRETSecret managerValeur aléatoire, stable, distincte et d'au moins 32 caractères pour les empreintes HMAC des credentials SCIM managés.
DATABASE_URLFournisseur PostgresUtiliser la connection string poolée production lorsque le fournisseur en expose une.
BETTER_AUTH_TRUSTED_ORIGINSDomaines de déploiementAjouter les origines preview/local en liste séparée par virgules. L'hôte canonique et sa variante www sont dérivés de NEXT_PUBLIC_BASE_URL.
CADDY_HOST_PORTEnv application CoolifyPort hôte du conteneur Caddy Compose. Défaut production 3080; utilisez par exemple 3081 pour l'app development partagée.
MINIO_API_HOST_PORTEnv application CoolifyPort hôte de l'API S3-compatible MinIO. Défaut production 9000; utilisez par exemple 9100 en development.
MINIO_CONSOLE_HOST_PORTEnv application CoolifyPort hôte de la console MinIO. Défaut production 9001; utilisez par exemple 9101 en development.

Réglages optionnels de pool base:

VariableQuand la définir
DATABASE_POOL_MAXAugmenter seulement si le fournisseur DB supporte plus de connexions par runtime.
DATABASE_IDLE_TIMEOUT_SECONDSAjuster le cycle de vie idle pour les déploiements non-serverless.
DATABASE_MAX_LIFETIME_SECONDSAjuster la rotation de connexions pour les runtimes long-lived.
DATABASE_CONNECT_TIMEOUT_SECONDSAjuster le démarrage sur réseaux privés lents.
DATABASE_STATEMENT_TIMEOUT_SECONDSAjouter un timeout serveur par statement pour chaque connexion DB.
DATABASE_PREPARE_STATEMENTSGarder false pour les connexions production pooler/serverless sauf support explicite du fournisseur.

Base De Données

DATABASE_URL est requis par le runtime applicatif, les scripts setup, les seeds et les opérations Drizzle.

Pour le déploiement Coolify auto-hébergé, la synchronisation de schéma et les seeds production tournent dans le service Compose migrate avant le démarrage de l'app et du worker. Gardez le DATABASE_URL production dans Coolify ou le secret store de l'orchestrateur afin que l'étape de setup résolve les noms de services Docker internes comme postgres.

Pour les fournisseurs hébergés qui n'exécutent pas la pile Compose auto-hébergée, lancez l'opération de schéma Drizzle choisie depuis un runner capable d'atteindre la base production avant de promouvoir la nouvelle version.

Pour les fournisseurs Postgres hébergés:

  1. Créez la base production.

  2. Ouvrez le panneau des connection strings.

  3. Copiez la connection string poolée lorsqu'elle existe.

  4. Stockez-la comme DATABASE_URL dans le secret store Coolify/de l'orchestrateur.

  5. Utilisez une base de développement séparée dans .env.local ou l'application Coolify de développement.

Si le fournisseur expose des URL directes et poolées, utilisez le mode de connexion recommandé pour les conteneurs long-lived et dimensionnez le pool applicatif selon la limite de la base.

Hôte Canonique Et Auth

Définissez:

NEXT_PUBLIC_BASE_URL=https://yayaw.app
BETTER_AUTH_TRUSTED_ORIGINS=https://*.yayaw.app

NEXT_PUBLIC_BASE_URL pilote les URL canoniques, l'URL de base Better Auth, les métadonnées OAuth, les liens sitemap/robots et les URL absolues de ressources.

Ne définissez pas NEXT_PUBLIC_SITE_URL; l'application ne le lit pas. Ne définissez pas BETTER_AUTH_URL pour les nouveaux déploiements; Better Auth reçoit son URL de base depuis NEXT_PUBLIC_BASE_URL.

Le domaine de prévisualisation branch-backed est https://preview.yayaw.app et suit la branche durable preview.

Les hôtes .eu retirés peuvent rester attachés uniquement comme redirections 308 du reverse proxy vers leurs remplaçants .app, comme yayaw.eu et www.yayaw.eu vers yayaw.app. Ils ne doivent pas servir de trafic dashboard/auth ni figurer dans BETTER_AUTH_TRUSTED_ORIGINS.

Préflight De Bascule Better Auth 1.7

Avant d'appliquer la migration Better Auth 1.7 à une restauration récente de production ou à la base de bascule, arrêtez les écritures d'authentification et exécutez :

bun run ba:1-7:preflight

Ce préflight en lecture seule inventorie les providers de comptes et leurs futurs issuers, les clients/ressources OAuth, les codes Device Authorization et les lignes SCIM legacy. Il bloque les issuers inconnus, collisions d'identité, métadonnées OAuth dangereuses, codes device dupliqués, liens de ressources orphelins, clients machine sans propriétaire utilisateur durable ni référence d'organisation correspondant à une adhésion vérifiée, ou données SCIM legacy non vides. Le client machine dont le propriétaire n'appartient qu'à une seule organisation est backfillé de façon déterministe ; zéro ou plusieurs adhésions exigent un choix explicite dans oauth_client.reference_id, sans jamais réenregistrer le client. Relancez-le juste avant la migration, une fois la fenêtre de maintenance ouverte.

Better Auth 1.7.3 restaure l'identité des comptes sur (provider_id, account_id) et cesse d'écrire le champ temporaire issuer introduit par les versions 1.7.0 à 1.7.2. La migration 0052 refuse les paires provider/compte dupliquées, remplace l'index unique temporaire fondé sur issuer et conserve account.issuer nullable pour permettre un rollback. Ne rendez pas cette colonne obligatoire et ne l'utilisez plus pour rechercher un compte.

Provisionnement SCIM Managé

SCIM managé est activé par le flag seedé enable-scim-plugin. Définissez un SCIM_CREDENTIAL_HASH_SECRET stable et distinct dans chaque environnement activé avant de démarrer l'application :

openssl rand -hex 32

Stockez le résultat dans le gestionnaire de secrets de l'orchestrateur et transmettez-le au runtime applicatif. Les définitions Compose Coolify et auto-hébergée portable transmettent cette valeur. Ne l'exposez jamais comme variable publique ou de build. Better Auth stocke des empreintes HMAC-SHA256 versionnées : changer le secret sans migration des données invalide tous les tokens managés.

Un administrateur disposant de organization-settings:manage crée et fait tourner les connexions depuis /dashboard/organization/settings. Configurez le fournisseur d'identité avec le bearer token affiché une seule fois et :

https://yayaw.app/api/auth/scim/v2

Le token brut n'est renvoyé qu'à la création ou à la rotation et doit être copié avant d'actualiser le tableau de bord. Chaque credential expire par défaut après 365 jours, permet la lecture et l'écriture des Users/Groups SCIM et peut être révoqué pendant qu'un credential renouvelé reste actif. Le décommissionnement est irréversible. Les groupes SCIM restent des données de provisionnement : aucun nom, membre ou attribut de rôle ne crée un rôle, binding ou permission Yayaw.

Stripe Billing

Stripe a trois secrets requis lorsque la facturation est activée:

VariableOù la récupérerNotes
STRIPE_SECRET_KEYStripe Dashboard, Developers, API keysUtilisez sk_live_... en production et sk_test_... en staging/local.
STRIPE_WEBHOOK_SECRETStripe Dashboard, Developers, Webhooks, signing secret endpoint abonnementUtilisé par l'endpoint webhook Better Auth Stripe.
STRIPE_ONE_TIME_WEBHOOK_SECRETStripe Dashboard, Developers, Webhooks, signing secret endpoint achat ponctuelUtilisé par l'endpoint custom one-time checkout.

Créez deux endpoints webhook Stripe pour la production:

https://yayaw.app/api/auth/stripe/webhook
https://yayaw.app/api/billing/stripe/webhook

Utilisez le signing secret du premier endpoint pour STRIPE_WEBHOOK_SECRET et celui du second pour STRIPE_ONE_TIME_WEBHOOK_SECRET. Ne réutilisez pas le secret d'un endpoint pour l'autre.

L'endpoint abonnement doit recevoir les événements de checkout abonnement et de cycle de vie billing. L'endpoint one-time a seulement besoin de checkout.session.completed pour les achats lifetime.

Les prix des produits de facturation sont gérés depuis /dashboard/admin/billing-products ou via le plan de contrôle MCP. Les Stripe Price IDs sont stockés en base après synchronisation catalogue; ce ne sont pas des secrets de déploiement.

Liens Livrables D'Accès Au Code

Ces valeurs sont des URL non secrètes optionnelles affichées sur /dashboard/organization/code-access après un achat payant qui débloque l'accès au code:

VariableExempleQuand l'utiliser
BILLING_CODE_ACCESS_REPOSITORY_URLhttps://github.com/Yayaw-eu/yayawLien vers le dépôt privé après attribution de l'accès GitHub.
BILLING_CODE_ACCESS_DOWNLOAD_URLhttps://github.com/Yayaw-eu/yayaw/releasesLien vers artifacts de release ou archives.
BILLING_CODE_ACCESS_DOCUMENTATION_URLhttps://docs.yayaw.appLien vers la documentation de setup ou d'usage.
BILLING_CODE_ACCESS_SUPPORT_URLmailto:[email protected]Lien support pour les problèmes d'accès.

Laissez une valeur vide lorsque le livrable requiert un provisioning manuel.

GitHub Code Access

L'accès dépôt production doit utiliser une GitHub App, pas un personal access token. L'app permet à Yayaw d'inviter les clients payants dans le dépôt configuré avec l'accès lecture seule pull, sans stocker les identifiants GitHub d'un utilisateur humain.

Créer La GitHub App

Créez l'app depuis le compte qui possède le dépôt privé:

GitHub organization -> Settings -> Developer settings -> GitHub Apps -> New GitHub App

Valeurs recommandées:

ChampValeur
GitHub App nameYayaw Code Access
Homepage URLURL de production, par exemple https://yayaw.app
WebhookDésactivé sauf future fonctionnalité GitHub callbacks
Where can this GitHub App be installedOnly on this account

Permissions de dépôt:

PermissionAccès
AdministrationRead and write
MetadataRead-only, accordé automatiquement par GitHub

Administration: Read and write est requis car Yayaw appelle l'API GitHub collaborators pour inviter le username demandé dans le dépôt. L'app n'a pas besoin de permission Contents pour le flux actuel: le client reçoit l'accès dépôt normal via son propre compte GitHub.

Installer La GitHub App

Après création:

  1. Générez une clé privée depuis les réglages de l'app et téléchargez le .pem.

  2. Installez l'app sur le compte ou l'organisation propriétaire.

  3. Sélectionnez uniquement le dépôt auquel les clients payants doivent accéder.

  4. Copiez l'App ID depuis les réglages.

  5. Copiez l'installation ID depuis l'URL d'installation.

L'URL d'installation ressemble généralement à ceci pour une organisation:

https://github.com/organizations/Yayaw-eu/settings/installations/12345678

Le segment numérique final est l'ID d'installation de la GitHub App.

Stocker Le Secret GitHub App

Stockez la clé privée dans le magasin de secrets Coolify/de l'orchestrateur pour chaque environnement cible. Utilisez son éditeur de secret multiligne afin que le PEM ne passe jamais en argument de commande. Pour un .env.local ou fichier secret auto-hébergé, collez la clé multiligne entre guillemets ou échappez les sauts de ligne sur une ligne:

printf 'BILLING_CODE_ACCESS_GITHUB_APP_PRIVATE_KEY="%s"\n' "$(perl -0pe 's/\n/\\n/g' yayaw-code-access.pem)" >> .env.local

Le runtime accepte les séquences \n échappées et les reconvertit en vrais sauts de ligne avant de signer le JWT GitHub App. Si une plateforme aplatit le PEM en une ligne avec des espaces entre header, body et footer, Yayaw le normalise en PEM au runtime.

Configurer Les Valeurs Non Secrètes GitHub

Préférez l'UI admin pour les valeurs non secrètes:

/dashboard/admin/billing-settings

Définissez:

Réglage adminValeur
EnabledOn
RepositoryYayaw-eu/yayaw ou le dépôt cible exact
GitHub App IDApp ID numérique depuis les réglages GitHub App
GitHub App installation IDInstallation ID numérique depuis l'URL d'installation

Des replis d'environnement existent pour les déploiements qui ont besoin de config au démarrage:

BILLING_CODE_ACCESS_GITHUB_REPOSITORY=Yayaw-eu/yayaw
BILLING_CODE_ACCESS_GITHUB_APP_ID=123456
BILLING_CODE_ACCESS_GITHUB_APP_INSTALLATION_ID=12345678
BILLING_CODE_ACCESS_GITHUB_APP_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----"

Repli Token Local Ou Staging

Utilisez BILLING_CODE_ACCESS_GITHUB_TOKEN uniquement pour les environnements locaux ou staging sans GitHub App:

BILLING_CODE_ACCESS_GITHUB_REPOSITORY=Yayaw-eu/yayaw
BILLING_CODE_ACCESS_GITHUB_TOKEN=github_pat_...

Le propriétaire du token doit pouvoir ajouter des collaborateurs au dépôt. Pour un token fine-grained, accordez l'accès au seul dépôt cible et incluez la permission d'administration de dépôt nécessaire aux invitations. N'utilisez pas ce repli en production sauf contournement d'incident avec plan de rotation.

E-mail

RESEND_API_KEY active les e-mails transactionnels d'invitation, reset, magic link et facturation.

Pour la récupérer:

  1. Vérifiez le domaine d'envoi dans Resend pour l'e-mail production.

  2. Créez une clé API server-side dans Resend.

  3. Stockez-la comme RESEND_API_KEY dans l'environnement cible.

  4. Envoyez un e-mail de test depuis la surface admin des e-mails transactionnels après déploiement.

Définissez EMAIL_SENDER sur un expéditeur vérifié du domaine Resend, par exemple [email protected]. Définissez EMAIL_SUPPORT sur l'adresse support affichée dans les modèles et EMAIL_USERNAME sur le nom affiché. Ces variables sont des défauts bootstrap et replis; après la première connexion admin, Admin > Site Settings > Email est prioritaire au runtime.

L'absence de RESEND_API_KEY ne casse pas le traitement des webhooks de facturation, mais ignore les e-mails transactionnels.

Fournisseurs OAuth

Configurez OAUTH_PROVIDER_ENCRYPTION_KEYS dans les secrets d'exécution Coolify sur chaque instance avant de configurer les fournisseurs. Générez une clé dédiée avec openssl rand -base64 32, précédée d'un identifiant (key1:<valeur>). Ce secret ne sert pas au build et doit être distinct de BETTER_AUTH_SECRET.

Les identifiants Google, GitHub et Microsoft se gèrent dans Administration > Réglages du site avec le droit global auth-provider:manage et un code TOTP. Enregistrez un brouillon, déclarez l'URL de retour affichée chez le fournisseur, testez puis activez. Modifier un fournisseur ne nécessite ni build ni redémarrage. La configuration initiale ou la rotation de la clé nécessite un redémarrage. L'application OAuth GitHub est distincte de la GitHub App d'accès au code.

Utilisez des applications distinctes pour la préproduction et la production. Sauvegardez les clés séparément de la base. Lors d'une rotation, gardez les deux clés (nouvelle:<valeur>,ancienne:<valeur>) jusqu'au nouvel enregistrement, test et activation de chaque fournisseur. Conservez les clés des anciennes sauvegardes.

Object Storage

Le stockage média est optionnel jusqu'à l'utilisation d'upload, miniatures, logos ou génération d'images. Définissez STORAGE_PROVIDER=s3 pour MinIO, S3, R2 ou un autre fournisseur compatible S3.

Variables communes:

VariableOù la récupérer
STORAGE_PROVIDERDéfinir sur s3.
STORAGE_MEDIA_BUCKETNom du bucket des ressources média. Défaut media.
STORAGE_MEDIA_STAGING_BUCKETBucket privé des téléversements directs en staging. Le self-host fourni utilise media-staging.
STORAGE_CMS_FORM_BUCKETBucket privé durable des pièces jointes client. Le self-host fourni utilise cms-form-submissions.
STORAGE_PUBLIC_BASE_URLOrigine publique des objets pour stockage compatible S3, servant /<bucket>/<key>. Utilisez l'origine app ou CDN seulement si ces préfixes de bucket sont routés vers le stockage objet.
STORAGE_TRANSFER_SIGNING_SECRETSecret dédié facultatif des grants privés same-origin opaques. Il se replie sur le secret de preview ou d'auth et doit fournir au moins 32 caractères en production.
CMS_PERSONALIZATION_FORM_CONTEXT_SECRETSecret dédié obligatoire pour la provenance éphémère des formulaires CMS publics. Générez au moins 32 caractères aléatoires et ne l'exposez jamais au navigateur ou au contenu CMS.
S3_SIGNED_PUBLIC_ENDPOINTOrigine d'API S3 publique explicite et facultative pour les uploads signés directs quand l'hébergeur app ne supporte pas la limite CMS de 250 Mo. Ne jamais renseigner l'endpoint Docker interne.

Pour un stockage S3 same-origin ou derrière CDN, proxifiez chaque préfixe de bucket public vers le stockage objet avant le repli app. Les buckets publics intégrés sont /media/* pour les médias téléversés et /organization-logos/* pour les logos d'organisation. Si STORAGE_PUBLIC_BASE_URL pointe directement vers une origine de stockage objet dédiée, cette origine doit servir les mêmes chemins /<bucket>/<key>.

Créez les buckets de staging et de formulaires avant d'activer leurs écritures et ne leur accordez aucun accès anonyme. Limitez le nettoyage par cycle de vie du fournisseur au bucket ou préfixe de staging ; le worker Page AI pilote le ledger de rétention à 180 jours des photos de formulaires et reprend les suppressions interrompues. Définissez CMS_PERSONALIZATION_CLIENT_IP_HEADER=x-real-ip avec le Caddy fourni, qui écrase ce header, ou utilisez un autre header mono-valeur possédé par le proxy seulement après avoir vérifié que l'ingress retire les valeurs client. Gardez le worker actif avec PAGE_AI_QUEUE_DRIVER=db-worker en self-host.

Les transferts privés utilisent par défaut un grant applicatif same-origin opaque, donc S3_ENDPOINT peut rester privé. Sur une plateforme dont la limite de corps entrant est plus faible, définissez S3_SIGNED_PUBLIC_ENDPOINT sur une origine d'API S3 HTTPS publique et sûre ; seuls les uploads deviennent des PUT directs conditionnels, tandis que les téléchargements privés restent derrière le proxy applicatif. Autorisez l'origine app, PUT, Content-Type, Content-Length et If-None-Match dans la politique CORS de ce bucket. Les octets de staging promus restent un tombstone sans écrasement pendant l'expiration du grant et une stabilisation de 24 heures. Le reaper répète la suppression et n'enregistre le nettoyage qu'après son DELETE final.

Activez les écritures de prototypes exacts dans cet ordre :

  1. Déployez le reader compatible avec CMS_LEGACY_PROTOTYPE_WRITES_ENABLED=true et CMS_PROTOTYPE_RUNTIME_V2_WRITES_ENABLED=false.

  2. Vous pouvez lancer le workflow manuel CMS Prototype Runtime Rollout avec reader et le SHA complet déjà déployé pour prouver la flotte reader.

  3. Lancez ensuite ce workflow de confiance avec exact. Il vérifie ou crée les buckets privés, synchronise freeze, redéploie chaque ressource app/worker typée sur le SHA complet demandé et enregistre UUID de ressource, UUID de déploiement, digest, nom d'image, commit et jeu complet d'identifiants des conteneurs actifs dans un reçu durable version 1. Chaque conteneur actif doit prouver séparément le SHA et les valeurs legacy, v2, staging et opération de rollout attendues.

  4. Après avoir revérifié qu'aucun déploiement plus récent n'a invalidé la preuve de drain, le workflow fait passer le reçu de issued à activating, synchronise legacy false, v2 true, staged uploads true et CMS_PROTOTYPE_ROLLOUT_OPERATION_ID, puis redéploie et revérifie toutes les ressources. L'UUID de déploiement exact doit être nouveau ; UUID de ressource typée, digest, nom d'image et SHA complet restent identiques.

  5. Le workflow vérifie l'état runtime final, réinspecte tous les conteneurs exacts et transmet ce JSON frais directement à la consommation du reçu dans la même étape shell. La consommation active le floor de base avant toute écriture de gate. Seule l'opération consommée courante peut autoriser un nouveau prototypeGateV2.

Le workflow récupère ses scripts de control plane à un SHA de workflow immuable : le SHA capturé par GitHub lors du déclenchement d'un run manuel, ou le SHA de control plane immuable transmis par le parent qui autorise un enfant réutilisable. L'autorisation prouve que ce SHA reste un ancêtre du main courant récupéré ; l'avancement de main ne peut pas modifier les scripts d'un run déjà autorisé. Le SHA de déploiement demandé est une preuve, jamais du code de workflow exécutable. Le dispatcher de reçus durables est copié depuis ce checkout épinglé, vérifié par checksum, puis exécuté dans un conteneur one-shot durci attaché au réseau Docker Coolify privé avec l'image worker construite depuis ce même SHA de control plane immuable. Son entrypoint est remplacé et son ID d'image est épinglé avant tout accès à la base. Gardez Postgres privé ; le rollout n'exige ni URL ni port de base de données public. Si une exécution échoue après activating, le handler d'échec peut seulement restaurer puis redéployer freeze. Reprenez explicitement la même opération avec resume_operation_id et sa chaîne de confirmation. La reprise exige le même environnement, dépôt, SHA de déploiement, ressources typées et images, ainsi qu'une nouvelle tentative de preuve live avec sa propre expiration. Le parent immuable conserve le SHA de control plane qui l'a émis ; la tentative enfant reprise enregistre et valide le SHA immuable de workflow/control plane capturé pour sa propre autorisation. Elle ne peut ni créer un reçu actif concurrent ni revenir au reader.

Un dispatch exact autonome en production n'est accepté que si son SHA déployé correspond au SHA immuable de workflow/control plane capturé pour ce run. Ce garde s'exécute avant toute mutation des writers : une demande de reprise obsolète ne force donc pas la flotte live en freeze. Si main a avancé au-delà du SHA déployé d'un reçu activating, déployez le descendant strict en préservant freeze, puis utilisez la supersession explicite ci-dessous au lieu de reprendre l'ancien SHA.

Si la flotte a déjà avancé vers un SHA descendant strict alors qu'un ancien reçu reste activating, ne reprenez pas l'ancien SHA et ne faites pas de rollback. Lancez exact sur le descendant live avec superseded_operation_id, superseded_deploy_sha et la confirmation exact:<nouveau-sha>:supersede:<ancien-sha>:<ancien-operation-id>. Le workflow prouve l'ascendance Git, redéploie et revérifie le descendant en freeze, puis crée et active atomiquement le reçu de remplacement en liant l'ancien comme terminal superseded. Le remplacement conserve les UUID de ressources typées et prouve de nouveaux UUID de déploiement. La supersession ne fait jamais avancer le runtime floor ; seule la consommation du remplacement le fait.

Le déploiement automatique de production garde bootstrap/reader en reader et peut préserver freeze uniquement lorsque la configuration Coolify et chaque conteneur app/worker live prouvent déjà freeze. Lorsque la production est déjà en exact, le run automatique se termine maintenant sans erreur avant build, migration ou rollout et indique qu'une promotion CMS est requise, avec le SHA live vérifié et le SHA cible demandé dans son résumé. Le résumé mène directement à la page du workflow CMS Production Promotion, où l'opérateur doit sélectionner main, vérifier les trois valeurs prêtes à copier, puis valider manuellement Run workflow. La confirmation exacte reste obligatoire. Il ne modifie ni ne préserve jamais exact implicitement. Le déploiement de PR development partagé suit les mêmes règles reader/freeze et n'active jamais exact. Si main avance avant la validation, utilisez les valeurs de son run différé réussi le plus récent : les anciennes valeurs échouent volontairement face au nouveau SHA de workflow sélectionné.

Pour une évolution ultérieure du renderer en development :

  1. Lancez CMS Prototype Runtime Rollout avec freeze sur le SHA complet actuellement déployé.

  2. Relancez le déploiement de la PR cible. Sous le lock partagé development-coolify-deploy, il préserve freeze, déploie le nouveau SHA immuable de la PR, puis prouve que chaque conteneur app/worker live expose ce SHA et les flags freeze. Une ressource Compose development unique est prouvée comme un seul déploiement contenant les deux images attendues, avec un digest d'image agrégé stable et le jeu complet d'IDs de conteneurs app/worker.

  3. Lancez CMS Prototype Runtime Rollout avec exact sur ce même SHA de PR. Il crée et consomme le reçu suivant, fait avancer l'opération courante et conserve l'opération initiale du floor.

Pour une évolution ultérieure du renderer en production, déclenchez l'unique workflow CMS Production Promotion avec le SHA complet actuellement déployé, le SHA immuable de main capturé par le déclenchement du workflow et promote-exact:<sha-courant>:<sha-cible>. Il conserve le lock rolling-coolify-production pendant toute l'opération et appelle les workflows réutilisables de confiance dans l'ordre : preuve et redéploiement du SHA courant en freeze, build/migration/rollout de la cible en conservant freeze, puis émission et consommation du reçu exact de la cible. Un échec interrompt la chaîne ; un échec d'activation exacte restaure freeze sans créer un second reçu. Le parent prouve que ce SHA capturé appartient toujours à main, puis épingle le même SHA du workflow/control plane dans chaque enfant : un nouveau commit sur main ne peut donc pas changer les scripts de déploiement ni l'image worker attendue par le reçu en cours de promotion. Les trois workflows manuels de bas niveau restent disponibles pour une reprise ou une supersession explicite de reçu.

Le job normal ne simule jamais cette transition. Une fois le floor actif, n'utilisez jamais reader comme reset et ne réactivez jamais les writers legacy.

Les migrations 0047_cms_prototype_runtime_floor et 0049_cms_prototype_rollout_receipt_supersession rendent cette frontière durable. Elles persistent les reçus parents et tentatives immuables, contraignent chaque état à sens unique issued → activating → consumed, ou la branche de récupération terminale explicite activating → superseded, puis verrouillent les writers de sessions pendant le backfill sûr de tout gate exact préexistant avec une opération initiale/courante synthétique irréversible. La consommation d'un reçu active le singleton indépendant. Son timestamp d'activation et sa première opération sont immuables ; l'ID du premier contexte reste nul jusqu'au premier nouveau gate exact, puis devient immuable. Les reçus consommés ultérieurs ne peuvent que faire avancer current_v2_rollout_operation_id. Les sessions exactes existantes continuent leurs mises à jour ordinaires sans rejouer la GUC transactionnelle ; un gate exact nouvellement ajouté doit porter l'opération courante. La chaîne synthétique de backfill ne constitue jamais une autorisation suffisante : l'opération courante doit aussi correspondre à un reçu réellement consommé avant l'ajout d'un autre gate exact. Le floor ne peut être ni supprimé, ni tronqué, ni réinitialisé, ni annulé par l'expiration/suppression de la session d'origine. Des triggers de base de données et des gardes writer dans la même transaction sérialisent les écritures legacy de session, composant, page, review, publication et média sur ce singleton. Réinitialiser un flag d'environnement ne peut donc pas rouvrir les mutations legacy. Les lectures legacy, dry-runs purs, lectures idempotentes d'uploads terminés, annulations/expirations et nettoyages média restent disponibles. Un commit média refusé par ce floor supprime l'objet promu, tandis que le nettoyage d'un upload préparé reste repris par le reaper.

Le workflow manuel constitue aussi la frontière de sûreté des binaires en rolling : chaque ressource doit exécuter le SHA immuable en freeze avant qu'une ressource passe en exact. Ne modifiez pas ces flags directement et ne contournez pas ce workflow, car les anciens binaires ne contiennent pas toutes les gardes applicatives du floor.

Le préflight stockage de l'image worker requiert HeadBucket, l'optionnel CreateBucket, GetBucketPolicy, GetBucketAcl, PutObject, GetObject et DeleteObject sur les buckets staging et formulaires. Il n'affiche jamais les identifiants de stockage ni les erreurs provider complètes.

Pendant le setup, bun run seed téléverse les ressources par défaut de variables globales de site dans le bucket média sous global/site-variables/* et écrit ces URL publiques dans l'entrée site-variables/main. Les URL de ressources personnalisées existantes sont préservées lorsque le seed est relancé.

Variables de stockage compatible S3:

VariableOù la récupérer
S3_ENDPOINTEndpoint fournisseur, par exemple http://minio:9000.
S3_REGIONRégion fournisseur. Utilisez us-east-1 pour MinIO sauf configuration différente.
S3_ACCESS_KEY_IDIdentifiant de stockage server-side.
S3_SECRET_ACCESS_KEYIdentifiant de stockage server-side.
S3_FORCE_PATH_STYLEDéfinir true pour MinIO et la plupart des endpoints compatibles S3 locaux.

N'exposez jamais S3_ACCESS_KEY_ID ou S3_SECRET_ACCESS_KEY au navigateur ni dans une variable NEXT_PUBLIC_*.

S3_SIGNED_PUBLIC_ENDPOINT est optionnel et n'est jamais déduit de S3_ENDPOINT. En production, sa présence impose HTTPS, un hostname DNS public multi-label, sans credentials, query ni fragment. Il active uniquement les PUT directs signés ; les downloads restent derrière le proxy same-origin de l'app. Avant de définir cette variable, configurez le CORS du bucket pour l'origine de l'app, PUT et les headers Content-Type, Content-Length et If-None-Match. STORAGE_TRANSFER_SIGNING_SECRET peut fournir un secret dédié aux tokens opaques de transfert ; sinon le runtime se replie volontairement sur CMS_PREVIEW_SIGNING_SECRET, puis BETTER_AUTH_SECRET. Le secret effectif en production doit contenir au moins 32 caractères.

Domaines Publics

Les domaines publics d'organisation utilisent PUBLIC_DOMAIN_PROVIDER.

VariableUsage
PUBLIC_DOMAIN_PROVIDERDéfinir à manual-dns pour le chemin supporté de vérification DNS auto-hébergée.
APP_MANAGED_HOSTSHôtes possédés par l'app, séparés par virgules, que les clients ne peuvent pas réclamer.
RESERVED_PUBLIC_DOMAIN_SUFFIXESSuffixes supplémentaires que les clients ne peuvent pas réclamer.
PUBLIC_DOMAIN_CNAME_TARGETCible CNAME DNS manuel affichée aux opérateurs.
PUBLIC_DOMAIN_IPV4_TARGETSCibles A-record DNS manuel séparées par virgules.
PUBLIC_DOMAIN_TXT_PREFIXPréfixe TXT de vérification, défaut _yayaw.

manual-dns génère un challenge TXT de propriété et vérifie le DNS public. Il n'automatise ni les changements chez le fournisseur DNS ni l'émission de certificats TLS.

OpenAI

Les fonctions de génération IA requièrent:

OPENAI_API_KEY=

Créez la clé depuis la page API keys de la plateforme OpenAI et stockez-la côté serveur dans l'environnement cible. Elle active le repli IA de composants, Page AI et la génération d'images lorsque les feature flags et réglages runtime correspondants sont actifs.

Toggles runtime IA optionnels:

VariableDéfautUsage
OPENAI_COMPONENTS_AI_FALLBACKtrueActive le repli IA pour inférence et classification de composants.
OPENAI_IMAGE_GENERATION_ENABLEDtrueActive la génération d'images du page builder lorsqu'une clé API existe.
OPENAI_IMAGE_MODELgpt-image-1.5Modèle de génération d'images.
PAGE_AI_QUEUE_DRIVERAutodirect localement ou db-worker avec le worker Coolify long-lived. La production choisit db-worker; le driver legacy vercel-queue exige une valeur explicite et n'est jamais déduit de variables VERCEL_* résiduelles.
PAGE_AI_DEEP_REFINEMENTfalseToggle qualité interne, non exposé dans les réglages admin.
PAGE_AI_WORKER_POLL_MS1500Intervalle de polling pour le driver worker base de données.
PAGE_AI_WORKER_MAINTENANCE_MS60000Intervalle de rétention durable, nettoyage des objets orphelins et purge des buckets anti-abus.
PAGE_AI_WORKER_IDVideIdentifiant optionnel pour un worker long-lived.

Analytics

Choisissez un fournisseur analytics et un mode de capture:

NEXT_PUBLIC_ANALYTICS_PROVIDER=umami
NEXT_PUBLIC_ANALYTICS_CAPTURE_MODE=hybrid

Fournisseurs supportés: posthog, umami, none. Le mode de capture peut être hybrid, server ou client. hybrid est le défaut et enregistre des événements facturation/auth fiables côté serveur tout en gardant l'analytics produit navigateur. server ne rend pas de scripts analytics navigateur et envoie les vues CMS plus les conversions depuis le serveur applicatif. client conserve la capture uniquement navigateur.

Les événements de conversion serveur incluent checkout_started, purchase_completed, subscription_started, subscription_updated, subscription_cancel_scheduled, subscription_canceled et payment_failed. Les événements de facturation incluent revenue, value, currency, plan, product_key et les IDs Stripe pour la réconciliation. Les événements Stripe en mode test sont suivis avec billing_mode=test et is_test_data=true, mais ne comptent pas comme conversion, revenue ou value sauf activation du réglage admin Track Stripe Test Billing Analytics. Le montant test original reste dans test_amount_cents et test_revenue pour le débogage.

PostHog

La capture client et les feature flags utilisent les valeurs PostHog publiques:

NEXT_PUBLIC_POSTHOG_KEY=
NEXT_PUBLIC_POSTHOG_HOST=https://eu.i.posthog.com
NEXT_PUBLIC_POSTHOG_ENABLE_LOCAL=false

Les analytics dashboard côté serveur utilisent les valeurs PostHog privées:

POSTHOG_PERSONAL_API_KEY=
POSTHOG_PROJECT_ID=
POSTHOG_API_HOST=https://eu.posthog.com
POSTHOG_ORG_ID_PROPERTY=organization_id
POSTHOG_FLAG_LOOKUP_TIMEOUT_MS=

Récupérez la clé projet publique et l'ID projet depuis les réglages PostHog. Créez une personal API key avec l'accès suffisant à l'API Query et stockez-la comme POSTHOG_PERSONAL_API_KEY. Gardez POSTHOG_ORG_ID_PROPERTY=organization_id sauf changement de la propriété d'événement analytics dans le produit. Définissez POSTHOG_FLAG_LOOKUP_TIMEOUT_MS uniquement si les lookups de feature flags PostHog côté serveur ont besoin d'un timeout différent du défaut 1500 millisecondes.

Umami

La capture client utilise les valeurs Umami publiques:

NEXT_PUBLIC_UMAMI_HOST_URL=https://analytics.example.com
NEXT_PUBLIC_UMAMI_SCRIPT_URL=
NEXT_PUBLIC_UMAMI_WEBSITE_ID=
NEXT_PUBLIC_UMAMI_DOMAINS=
NEXT_PUBLIC_UMAMI_AUTO_TRACK=true

Les lectures dashboard côté serveur et la capture serveur utilisent les valeurs Umami privées:

UMAMI_API_URL=https://analytics.example.com
UMAMI_WEBSITE_ID=
UMAMI_API_TOKEN=
UMAMI_API_KEY=
UMAMI_USERNAME=
UMAMI_PASSWORD=
UMAMI_CMS_EVENT_PAGE_SIZE=1000

Utilisez soit UMAMI_API_TOKEN (ou l'alias legacy UMAMI_API_KEY), soit UMAMI_USERNAME plus UMAMI_PASSWORD pour les lectures de données dashboard. La capture d'événements serveur utilise Umami /api/send; elle a donc seulement besoin de UMAMI_API_URL et UMAMI_WEBSITE_ID.

Plan De Contrôle MCP

Le MCP production ne requiert pas de variable d'environnement dans le runtime applicatif. Les clients se connectent à /api/mcp avec une clé API Better Auth ou un access token OAuth.

Les tests stdio locaux peuvent utiliser:

YAYAW_MCP_API_KEY=
YAYAW_MCP_LOCAL_USER_ID=

Créez, inspectez et révoquez les clés MCP avec:

bun run mcp:key

Stockez les clés client de confiance hors du dépôt et passez-les au client comme YAYAW_MCP_API_KEY.

Transforms Runtime Dynamic Data

Définissez DYNAMIC_DATA_RUNTIME_TRANSFORM_SECRET uniquement lorsque des routes runtime dynamiques déployées utilisent des transforms de valeur hmac_sha256, par exemple un code de pairing transformé en code_hash stocké.

DYNAMIC_DATA_RUNTIME_TRANSFORM_SECRET=$(openssl rand -hex 32)

Stockez la valeur comme secret server-only dans l'orchestrateur. Sa rotation change les futures sorties HMAC; faites-la donc avec une migration de données spécifique au produit ou une fenêtre d'expiration des pairing sessions.

Mode Maintenance

Le mode maintenance est optionnel:

MAINTENANCE_MODE=false
MAINTENANCE_MODE_END_DATE=

Définissez MAINTENANCE_MODE=true uniquement lorsque l'app doit servir la surface de maintenance. Utilisez MAINTENANCE_MODE_END_DATE pour le contexte opérateur.

Métadonnées De Déploiement

Les déploiements Coolify/auto-hébergés fournissent des métadonnées statiques:

VariableUsage
DEPLOYMENT_PROVIDERUtilisez static dans Coolify et local en développement local.
DEPLOYMENT_URLURL publique de déploiement.
DEPLOYMENT_ENVLabel d'environnement, par exemple production ou preview.
DEPLOYMENT_GIT_COMMIT_SHASHA de commit pour statut dashboard/plan de contrôle.
DEPLOYMENT_GIT_COMMIT_REFRef ou nom de branche.

Checklist De Déploiement

Avant promotion:

  1. L'environnement cible possède chaque variable requise de .env.example ou .env.self-host.example.

  2. La prévisualisation a soit des valeurs fournisseurs équivalentes à la production, soit des valeurs staging explicites.

  3. NEXT_PUBLIC_BASE_URL correspond à l'origine HTTPS publique de l'environnement.

  4. Stripe a deux endpoints webhook et chaque secret d'endpoint est mappé à la variable correspondante.

  5. L'accès au code GitHub a soit les réglages admin plus BILLING_CODE_ACCESS_GITHUB_APP_PRIVATE_KEY, soit les replis d'environnement explicites pour dépôt, App ID, installation ID et clé privée.

  6. Les variables Resend, PostHog, S3, OpenAI et OAuth ne sont présentes que lorsque ces fonctions sont activées.

  7. Coolify ou l'orchestrateur possède le DATABASE_URL runtime utilisé par le service Compose migrate, et les environnements GitHub possèdent la valeur build-time DATABASE_URL requise par next build.

  8. Un nouveau déploiement ou redémarrage de conteneur a été déclenché après la dernière modification de variable.

  9. Lancez les barrières qualité locales avant de merger des changements de config de déploiement:

bun run check
bun run docs:generate
bun run docs:check-links
bun run docs:check-translations
bun run docs:llm:check
bunx tsc --noEmit
bun run build

Références Fournisseurs