Variables d'environnement
Cette page est la référence des variables lues par le backend. Le tableau est généré à la construction de la documentation depuis backend/src/config/env.config.ts : type, valeur par défaut et contraintes viennent directement du schéma Zod, il ne peut donc pas diverger du code. Le chargement, la validation et les règles propres à la production sont expliqués sur la page Configuration.
Les mêmes variables servent aux deux process, l'API (server.ts) et le worker (worker.ts). Une valeur incohérente entre les deux, par exemple deux REDIS_KEY_PREFIX différents, casse le relais Socket.IO et l'idempotence. Dans la stack Docker, l'ancre x-backend-env de docker-compose.yml garantit qu'ils reçoivent les mêmes valeurs.
Générer les secrets
Quatre variables sont des secrets refusés en production s'ils gardent leur valeur de développement. Les commandes suivantes utilisent Node, déjà requis par le projet. La dernière s'exécute depuis backend/, où ethers est installé.
| Variable | Format attendu | Commande |
|---|---|---|
JWT_SECRET |
au moins 16 caractères | node -e "console.log(require('crypto').randomBytes(48).toString('base64url'))" |
ENCRYPTION_KEY |
exactement 64 caractères hexadécimaux | node -e "console.log(require('crypto').randomBytes(32).toString('hex'))" |
WEBHOOK_SECRET |
au moins 8 caractères | node -e "console.log(require('crypto').randomBytes(32).toString('hex'))" |
RELAYER_PRIVATE_KEY |
0x suivi de 64 caractères hexadécimaux |
node -e "console.log(require('ethers').Wallet.createRandom().privateKey)" |
METRICS_TOKEN |
chaîne non vide en production | node -e "console.log(require('crypto').randomBytes(24).toString('base64url'))" |
Danger
ENCRYPTION_KEY chiffre en AES-256-GCM la clé privée de chaque portefeuille intégré (User.walletEncryptedKey), les secrets TOTP et les images des pièces d'identité en attente. Si elle est perdue ou changée, les portefeuilles ne peuvent plus signer : les ethers fictifs et les tickets qu'ils détiennent deviennent inaccessibles, les citoyens doivent reconfigurer leur 2FA et les pièces en attente ne peuvent plus être examinées. Sauvegardez-la hors du serveur avant la mise en service et ne la faites jamais tourner sans plan de rechiffrement. Voir Clés et garde des portefeuilles.
Changer JWT_SECRET invalide toutes les sessions ouvertes : c'est un moyen radical de déconnecter tout le monde. Changer WEBHOOK_SECRET impose de mettre à jour l'émetteur du webhook en même temps.
Valeurs selon l'environnement
Développement local
cp .env.example .env suffit. Les valeurs visent l'infrastructure lancée par docker compose up -d --wait depuis la racine du dépôt.
| Besoin | Réglage |
|---|---|
| Travailler sans nœud blockchain | BLOCKCHAIN_MODE=simulated : achats, tirages et pause sont simulés, /lottery/claim-prize répond 409 |
| Lire le code de connexion sans ouvrir Mailpit | rien à faire : en NODE_ENV=development, les réponses de /auth/login, /auth/signup et /auth/password/forgot contiennent devOtpPreview |
| Se connecter sans code email | LOGIN_EMAIL_CODE_REQUIRED=false (refusé en production) |
| Utiliser l'administration sans 2FA | ADMIN_MFA_REQUIRED=false (refusé en production) |
| Jouer sans faire vérifier son âge | AGE_VERIFICATION_REQUIRED=false (refusé en production) ; les comptes du seed sont déjà vérifiés |
| Raccourcir la salle d'attente du tirage | DRAW_REVEAL_DELAY_SECONDS=0 pour un tirage dès la fermeture des achats, ou une valeur courte pour une démonstration |
| Tester la connexion Google | renseigner GOOGLE_CLIENT_IDS avec les identifiants client de l'application ; sans eux, la route répond 503 |
| Voir les requêtes SQL | DB_LOG_QUERIES=true |
| Tester l'application mobile sur un téléphone | ajouter l'origine Expo à CORS_ORIGINS (voir Tester sur un téléphone) |
Après un redémarrage du nœud Hardhat, la chaîne repart du bloc 0 alors que la base garde son curseur d'indexation : l'indexeur journalise alors que le curseur dépasse la tête de chaîne. Il faut réinitialiser la base avec npm run prisma:reset, comme décrit dans Blockchain locale.
Stack Docker
Le fichier docker-compose.yml fixe déjà les variables d'infrastructure dans l'ancre x-backend-env : NODE_ENV=production, DATABASE_URL sur postgres:5432, REDIS_URL sur redis:6379, BLOCKCHAIN_MODE=onchain, RPC_URL=http://blockchain:8545, CHAIN_ID=31337, les deux adresses de contrats, SMTP_HOST=mailpit et les origines CORS. Le service backend y ajoute RUN_WORKERS=false ; le service worker démarre dist/src/worker.js.
Comme la stack tourne en production, les règles de production s'appliquent. Les cinq secrets sont lus depuis un fichier .env placé à la racine du dépôt, lu par Docker Compose pour l'interpolation :
JWT_SECRET=valeur-generee
ENCRYPTION_KEY=64-caracteres-hexadecimaux
RELAYER_PRIVATE_KEY=0x64-caracteres-hexadecimaux
WEBHOOK_SECRET=valeur-generee
METRICS_TOKEN=valeur-genereeSi l'une manque, backend et worker s'arrêtent au démarrage avec Configuration invalide. La même RELAYER_PRIVATE_KEY est transmise au conteneur blockchain sous le nom OPERATOR_PRIVATE_KEY pour habiliter ce compte sur les contrats déployés. Le détail est sur la page Stack complète en conteneurs.
Sepolia
Pour brancher le backend sur le déploiement public, reprendre les valeurs du fichier smart-contract/deployments/sepolia.json écrit par npm run deploy:sepolia (voir Déploiement sur Sepolia).
| Variable | Valeur |
|---|---|
BLOCKCHAIN_MODE |
onchain |
CHAIN_ID |
11155111 |
RPC_URL |
URL d'un fournisseur RPC Sepolia avec clé d'API |
FALLBACK_RPC_URL |
un second fournisseur ; la valeur par défaut est déjà un nœud public Sepolia |
LOTTERY_CONTRACT_ADDRESS, POAP_CONTRACT_ADDRESS |
contracts.lottery et contracts.poap du fichier de déploiement |
CHAIN_START_BLOCK |
deploymentBlock du fichier de déploiement, sans quoi l'indexeur parcourt Sepolia depuis le bloc 0 par tranches de 2 000 blocs |
CHAIN_CONFIRMATIONS |
supérieur à 0 sur un réseau public, pour ne pas indexer un bloc qui serait réorganisé |
EXPLORER_URL |
https://sepolia.etherscan.io |
RELAYER_PRIVATE_KEY |
clé du compte operator habilité par le script de déploiement (rôles opérateur, pause et frappe) |
WELCOME_GRANT_WEI |
à dimensionner selon les ethers de test dont dispose le compte opérateur : chaque nouveau citoyen en reçoit une fois |
INDEXER_INTERVAL_MS, KEEPER_INTERVAL_MS |
à rallonger si le fournisseur RPC limite le nombre d'appels |
Attention
Sur Sepolia, le compte opérateur paie le gaz des tirages, des annulations, de la frappe des POAP et des dotations. Un solde vide bloque silencieusement les dotations (journalisées en erreur) puis les achats des nouveaux citoyens, qui reçoivent 402 Solde d'ethers fictifs insuffisant.
POST /admin/treasury/withdraw appelle setTreasury sur le contrat quand l'adresse demandée diffère de la trésorerie actuelle. Cette fonction exige DEFAULT_ADMIN_ROLE. Sur Sepolia, le script de déploiement réserve ce rôle au multisig MAIRIE_ADMIN_ADDRESS : l'opérateur ne l'a pas, et seul un retrait vers la trésorerie configurée (MAIRIE_TREASURY_ADDRESS) fonctionne ; changer de trésorerie passe par le multisig. En local, deploy-local.ts laisse ce rôle à l'opérateur, sauf avec LOCAL_STRICT_ROLES=true.
Référence
Le schéma de configuration compte 59 variables, lues et validées au démarrage par backend/src/config/env.config.ts. Une valeur invalide arrête le processus avec la liste des erreurs.
Serveur HTTP
| Variable | Type | Valeur par défaut | Contraintes | Rôle |
|---|---|---|---|---|
PORT |
entier | 2053 |
strictement positif | Port d'écoute de l'API HTTP et de Socket.IO. Le process worker ouvre ses sondes sur PORT + 1 (2054 par défaut). |
NODE_ENV |
énumération : development, production, test | development |
- | Environnement d'exécution. production active les contrôles de sécurité du schéma, test coupe Redis, les emails, S3 et les logs, development affiche les logs en couleur et renvoie devOtpPreview. |
TRUST_PROXY |
chaîne | false |
- | Réglage trust proxy d'Express : true, false, un nombre de proxys ou une liste d'adresses. Indispensable derrière Caddy pour que le rate limiting voie l'IP du client. |
HTTP_KEEP_ALIVE_TIMEOUT_MS |
entier | 65000 |
strictement positif | Durée de maintien des connexions keep-alive. headersTimeout vaut cette valeur plus une seconde. |
HTTP_REQUEST_TIMEOUT_MS |
entier | 30000 |
strictement positif | Durée maximale de réception d'une requête complète par Node. |
SHUTDOWN_TIMEOUT_MS |
entier | 25000 |
strictement positif | Délai laissé à l'arrêt propre avant sortie forcée avec le code 1. |
Observabilité
| Variable | Type | Valeur par défaut | Contraintes | Rôle |
|---|---|---|---|---|
APP_VERSION |
chaîne | version du package npm, sinon 1.0.0 |
- | Version publiée dans chaque ligne de log (version) et dans la réponse de /health/deep. L'image Docker la fixe par l'argument de build APP_VERSION. |
LOG_LEVEL |
énumération : error, warn, info, http, debug | aucune (facultative) |
- | Niveau minimal des logs Winston. Sans valeur : debug en développement, info ailleurs. |
METRICS_ENABLED |
booléen (true, false, 1, 0) | true |
- | Active les métriques Prometheus (mesure HTTP et métriques Node par défaut). |
METRICS_TOKEN |
chaîne | chaîne vide | production : METRICS_TOKEN est obligatoire en production pour protéger /metrics | Jeton Bearer exigé sur /metrics de l'API et pour le détail de /health/deep. Vide : /metrics est ouvert. |
Base de données
| Variable | Type | Valeur par défaut | Contraintes | Rôle |
|---|---|---|---|---|
DATABASE_URL |
URL | postgresql://ether…a=public |
- | Chaîne de connexion PostgreSQL utilisée par l'adaptateur @prisma/adapter-pg. La valeur par défaut pointe sur le conteneur local exposé sur le port 5434. |
DB_POOL_MAX |
entier | 10 |
strictement positif | Nombre maximal de connexions du pool pg par process (API et worker ont chacun leur pool). |
DB_CONNECTION_TIMEOUT_MS |
entier | 5000 |
strictement positif | Délai maximal pour obtenir une connexion du pool avant erreur. |
DB_IDLE_TIMEOUT_MS |
entier | 30000 |
strictement positif | Durée après laquelle une connexion inutilisée est fermée par le pool. |
DB_STATEMENT_TIMEOUT_MS |
entier | 15000 |
strictement positif | Paramètre PostgreSQL statement_timeout de chaque session. Une requête plus longue est annulée (code 57014, renvoyé en 503). |
DB_LOG_QUERIES |
booléen (true, false, 1, 0) | false |
- | Journalise toutes les requêtes SQL de Prisma (niveau query). À réserver au diagnostic local. |
Redis
| Variable | Type | Valeur par défaut | Contraintes | Rôle |
|---|---|---|---|---|
REDIS_URL |
URL | redis://localhost:6380 |
- | Connexion Redis pour le cache, les sessions révoquées, les codes OTP, l'idempotence, le rate limiting, les files BullMQ et le relais Socket.IO. La valeur par défaut vise le conteneur local exposé sur le port 6380. |
REDIS_KEY_PREFIX |
chaîne | blocklucky |
- | Préfixe des clés applicatives, complété par NODE_ENV (blocklucky:development:...). Il préfixe aussi les clés du rate limiter et le canal du relais Socket.IO, pas les clés BullMQ. |
Sécurité et sessions
| Variable | Type | Valeur par défaut | Contraintes | Rôle |
|---|---|---|---|---|
JWT_SECRET |
chaîne | etherbay-super-sec…ppement) |
au moins 16 caractères ; production : la valeur de développement est refusée en production | Secret HMAC de signature des jetons de session (HS256). Le changer invalide toutes les sessions en cours. |
JWT_EXPIRES_IN |
chaîne | 12h |
format /^\d+[smhd]$/ |
Durée de vie d'un jeton de session, au format nombre suivi de s, m, h ou d. |
ENCRYPTION_KEY |
chaîne | 0123456789abcdef01…89abcdef (développement) |
ENCRYPTION_KEY doit contenir 64 caractères hexadécimaux ; production : la valeur de développement est refusée en production | Clé AES-256 (32 octets en hexadécimal) qui chiffre les clés privées des portefeuilles intégrés et les secrets TOTP, et dérive la clé HMAC des codes OTP. La perdre rend les portefeuilles inutilisables. |
WEBHOOK_SECRET |
chaîne | etherbay-webhook-secret (développement) |
au moins 8 caractères ; production : la valeur de développement est refusée en production | Secret HMAC-SHA-256 qui signe l'en-tête x-webhook-signature du webhook blockchain (mode simulé). |
LOGIN_EMAIL_CODE_REQUIRED |
booléen (true, false, 1, 0) | true |
production : ne peut pas être désactivé en production | Exige un code à 6 chiffres reçu par email avant toute ouverture de session. Désactivé seulement par la suite de tests. |
ADMIN_MFA_REQUIRED |
booléen (true, false, 1, 0) | true |
production : ne peut pas être désactivé en production | Refuse les routes réservées à l'ADMIN tant que la double authentification n'est pas activée sur le compte. |
AGE_VERIFICATION_REQUIRED |
booléen (true, false, 1, 0) | true |
production : ne peut pas être désactivé en production | Exige une pièce d'identité validée par la Mairie (ageVerificationStatus = VERIFIED) pour acheter ou offrir un ticket et échanger une récompense. Refusée à false en production ; désactivée seulement par la suite de tests. |
GOOGLE_CLIENT_IDS |
liste séparée par des virgules | chaîne vide | - | Identifiants client OAuth Google acceptés comme audience des jetons d'identité, séparés par des virgules. Vide par défaut : la connexion Google répond alors 503. |
| Variable | Type | Valeur par défaut | Contraintes | Rôle |
|---|---|---|---|---|
SMTP_HOST |
chaîne | localhost |
- | Serveur SMTP d'envoi des emails transactionnels (Mailpit en local). |
SMTP_PORT |
entier | 1025 |
strictement positif | Port SMTP. La valeur 465 active TLS implicite (secure: true). |
SMTP_USER |
chaîne | chaîne vide | - | Identifiant SMTP. Vide : connexion sans authentification. |
SMTP_PASS |
chaîne | chaîne vide | - | Mot de passe SMTP, utilisé seulement si SMTP_USER est renseigné. |
SMTP_FROM |
chaîne | EtherBay Lottery <noreply@etherbay.fr> |
- | Expéditeur affiché des emails. |
SUPPORT_EMAIL |
chaîne | support@etherbay.fr |
- | Adresse qui reçoit les messages du formulaire de support (POST /user/support). L'email porte l'identifiant, l'adresse, le prénom et le nom du citoyen, et son adresse en réponse directe quand elle est connue. |
Blockchain
| Variable | Type | Valeur par défaut | Contraintes | Rôle |
|---|---|---|---|---|
BLOCKCHAIN_MODE |
énumération : onchain, simulated | onchain |
production : BLOCKCHAIN_MODE doit valoir onchain en production : la simulation est réservée au développement | onchain : achats, tirages et pause passent par le smart contract. simulated : hachages aléatoires, aucun nœud requis, réservé au développement et aux tests. |
RPC_URL |
URL | http://127.0.0.1:8545 |
- | Nœud JSON-RPC principal (nœud Hardhat local par défaut). |
FALLBACK_RPC_URL |
URL | https://ethereum-sepolia-rpc.publicnode.com |
- | Nœud JSON-RPC de secours, utilisé après une bascule du client RPC. |
CHAIN_ID |
entier | 31337 |
strictement positif | Identifiant de réseau attendu (31337 pour Hardhat, 11155111 pour Sepolia). Le fournisseur ethers est créé avec ce réseau statique. |
CHAIN_START_BLOCK |
entier | 0 |
minimum 0 | Premier bloc lu par l'indexeur quand aucun curseur n'existe en base. Mettre le bloc de déploiement du contrat. |
CHAIN_CONFIRMATIONS |
entier | 0 |
minimum 0 ; maximum 64 | Nombre de blocs de confirmation que l'indexeur laisse derrière la tête de chaîne. |
EXPLORER_URL |
chaîne | chaîne vide | URL HTTPS attendue | Racine HTTPS de l'explorateur de blocs pour les liens etherscanUrl et explorerUrl. Vide : aucun lien n'est produit. |
WELCOME_GRANT_WEI |
chaîne | 500000000000000000 |
format /^\d+$/ |
Dotation de bienvenue en wei versée une seule fois par le compte opérateur à chaque portefeuille intégré (0,5 ETH fictif par défaut, 0 la désactive). |
LOTTERY_CONTRACT_ADDRESS |
chaîne | 0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9 |
format /^0x[0-9a-fA-F]{40}$/ |
Adresse du contrat BlockLucky. La valeur par défaut correspond au déploiement local. |
POAP_CONTRACT_ADDRESS |
chaîne | 0x5FC8d32690cc91D4c39d9d3abcBD16989F875707 |
format /^0x[0-9a-fA-F]{40}$/ |
Adresse du contrat BlockLuckyPOAP. La valeur par défaut correspond au déploiement local. |
RELAYER_PRIVATE_KEY |
chaîne | 0xac0974bec39a17e3…f4f2ff80 (développement) |
format /^0x[0-9a-fA-F]{64}$/ ; production : la valeur de développement est refusée en production |
Clé privée du compte opérateur du backend : tirage, annulation, pause, frappe des POAP, retrait des commissions et dotations de bienvenue. La valeur par défaut est le compte 0 public de Hardhat. |
Workers
| Variable | Type | Valeur par défaut | Contraintes | Rôle |
|---|---|---|---|---|
INDEXER_INTERVAL_MS |
entier | 3000 |
minimum 500 | Période de la boucle de l'indexeur d'événements du contrat. |
KEEPER_INTERVAL_MS |
entier | 10000 |
minimum 1000 | Période du keeper qui demande le tirage ou clôt un tour expiré. Elle fixe aussi la durée du verrou Redis du keeper. |
DRAW_REVEAL_DELAY_SECONDS |
entier | 600 |
minimum 0 ; maximum 86400 | Durée de la salle d'attente en secondes : délai que le keeper respecte entre le passage du tour à l'état prêt au tirage (fin du temps ou plafond atteint) et l'appel à requestDraw (600 par défaut, 0 la supprime). |
RUN_WORKERS |
booléen (true, false, 1, 0) | true |
- | Démarre les workers BullMQ, l'indexeur et le keeper dans le process de l'API. Mettre false quand un process worker séparé tourne (stack Docker). |
WORKER_CONCURRENCY |
entier | 5 |
strictement positif | Concurrence du worker d'emails. Les workers ledger et chain-actions restent à 1. |
Stockage S3
| Variable | Type | Valeur par défaut | Contraintes | Rôle |
|---|---|---|---|---|
AWS_REGION |
chaîne | eu-west-3 |
- | Région du client S3 qui stocke les rapports exportés. |
AWS_ACCESS_KEY_ID |
chaîne | minioadmin |
- | Identifiant d'accès S3 (MinIO en local). |
AWS_SECRET_ACCESS_KEY |
chaîne | minioadmin |
- | Clé secrète S3. |
AWS_S3_BUCKET |
chaîne | blocklucky-assets |
- | Bucket de dépôt des rapports reports/etherbay-report-*.csv. |
AWS_S3_ENDPOINT |
URL | http://localhost:9000 |
- | Point d'accès S3 en style chemin. Il sert aussi à construire l'URL downloadUrl renvoyée par l'export. |
Frontend et CORS
| Variable | Type | Valeur par défaut | Contraintes | Rôle |
|---|---|---|---|---|
FRONTEND_URL |
URL | http://localhost:3000 |
- | URL du site web : toujours ajoutée aux origines CORS, base des liens de parrainage, du lien RGPD et de external_url des POAP. |
CORS_ORIGINS |
liste séparée par des virgules | http://localhost:3000,http://localhost:8081 |
- | Origines supplémentaires autorisées pour l'API et Socket.IO (le port 8081 correspond à Expo web). |
Fonctionnalités
| Variable | Type | Valeur par défaut | Contraintes | Rôle |
|---|---|---|---|---|
MOBILE_MIN_VERSION |
chaîne | 1.0.0 |
- | Version minimale de l'application mobile publiée par GET /api/config. |
MAINTENANCE_MODE |
booléen (true, false, 1, 0) | false |
- | Répond 503 avec Retry-After: 300 sur les routes /api/* sauf celles qui commencent par /api/admin. |
SCRATCH_COST_VIOLET |
entier | 50 |
strictement positif | Prix en EtherPoints du ticket à gratter « violet » (POST /marketplace/scratch), qui attribue au hasard une récompense partenaire en stock (50 par défaut). |
SCRATCH_COST_LUCKY |
entier | 100 |
strictement positif | Prix en EtherPoints du ticket à gratter « lucky » (POST /marketplace/scratch), qui attribue au hasard une récompense partenaire en stock (100 par défaut). |