# Variables d'environnement Veza > **Référence canonique de toutes les variables lues par la plateforme Veza.** > Sync effectué le 2026-04-23 — survey direct du code (`os.Getenv` en Go, `std::env::var` en Rust, `import.meta.env` côté React). > **SSOT Go** : [`veza-backend-api/internal/config/config.go`](../veza-backend-api/internal/config/config.go) lignes 276–465. > **Templates** : [`veza-backend-api/.env.template`](../veza-backend-api/.env.template), [`veza-stream-server/.env.example`](../veza-stream-server/.env.example), [`docker-compose.env.example`](../docker-compose.env.example). > **Documents liés** : [`ENV_CONFIG.md`](ENV_CONFIG.md) (setup dev veza.fr), [`ENVIRONMENT_REAL_SETUP.md`](ENVIRONMENT_REAL_SETUP.md) (walkthrough). > **Règle** : quand tu ajoutes une variable dans le code, mets à jour ce fichier + `.env.template` dans le même commit. Référence audit : `AUDIT_REPORT.md` §9 item #15. --- ## Démarrage rapide — 5 variables minimum Pour booter un backend dev (`make dev`) : | Variable | Exemple | Pourquoi | | --- | --- | --- | | `DATABASE_URL` | `postgres://veza:password@veza.fr:15432/veza?sslmode=disable` | Postgres — boot échoue sans. | | `REDIS_URL` | `redis://veza.fr:16379` | CSRF + rate-limit + cache. Pas de fallback silencieux en prod. | | `JWT_SECRET` | ≥32 caractères aléatoires | HS256 fallback. Préférer `JWT_PRIVATE_KEY_PATH` + `JWT_PUBLIC_KEY_PATH` en prod. | | `CORS_ALLOWED_ORIGINS` | `http://veza.fr:5173,http://veza.fr:3000` | Pas de wildcard en prod. | | `FRONTEND_URL` | `http://veza.fr:5173` | OAuth redirects + liens emails. | Tout le reste a un défaut raisonnable ou est opt-in. --- ## Table des matières 1. [Core / environnement](#1-core--environnement) 2. [Base de données](#2-base-de-données) 3. [Redis](#3-redis) 4. [JWT / authentification](#4-jwt--authentification) 5. [OAuth providers](#5-oauth-providers) 6. [CORS / cookies](#6-cors--cookies) 7. [Rate limiting](#7-rate-limiting) 8. [SMTP / email](#8-smtp--email) 9. [Hyperswitch / paiements](#9-hyperswitch--paiements) 10. [Stripe Connect / payouts créateurs](#10-stripe-connect--payouts-créateurs) 11. [RabbitMQ / event bus](#11-rabbitmq--event-bus) 12. [Stockage S3 / MinIO](#12-stockage-s3--minio) 13. [HLS streaming](#13-hls-streaming) 14. [Stream server (backend ↔ stream)](#14-stream-server-backend--stream) 15. [Elasticsearch](#15-elasticsearch) 16. [ClamAV / antivirus](#16-clamav--antivirus) 17. [Sentry / tracking d'erreurs](#17-sentry--tracking-derreurs) 18. [Logging](#18-logging) 19. [Monitoring / metrics](#19-monitoring--metrics) 20. [Frontend (Vite)](#20-frontend-vite) 21. [Feature flags](#21-feature-flags) 22. [Politique de mot de passe](#22-politique-de-mot-de-passe) 23. [Build / version info](#23-build--version-info) 24. [RTMP / Web Push / divers](#24-rtmp--web-push--divers) 25. [Stream server (Rust) — schéma propre](#25-stream-server-rust--schéma-propre) 26. [Security headers (rappel)](#26-security-headers-rappel) 27. [Variables dépréciées / legacy](#27-variables-dépréciées--legacy) 28. [Règles de validation production](#28-règles-de-validation-production) 29. [Drift template ↔ code](#29-drift-template--code) 30. [Checklist de démarrage](#30-checklist-de-démarrage) **Légende** : **variable en gras** = critique en production (validée au boot). --- ## 1. Core / environnement | Variable | Défaut | Lu à | Rôle | | --- | --- | --- | --- | | **`APP_ENV`** | `development` | `config.go:276` | `development` / `staging` / `production`. Conditionne les validations et beaucoup de défauts. | | `APP_DOMAIN` | `veza.fr` | `config.go:299` | Single source of truth pour les URLs dérivées en dev. `APP_DOMAIN=example.com` + `/etc/hosts` suffit. | | `APP_PORT` | `8080` | `config.go:309` | Port HTTP. | | `LOG_LEVEL` | `INFO` | `config.go:296` | `DEBUG` / `INFO` / `WARN` / `ERROR`. **`DEBUG` refusé en prod** (`validation.go:876`). | | `GO_ENV`, `NODE_ENV` | — | legacy | Alias historiques de `APP_ENV`. Pas canoniques. | ## 2. Base de données Hard requirement : `DATABASE_URL`. Pool par défaut tuné pour un dev mono-pod ; bump pour multi-replica. | Variable | Défaut | Lu à | Rôle | | --- | --- | --- | --- | | **`DATABASE_URL`** | — | `config.go:320` | DSN Postgres. Boot échoue sans. | | `DATABASE_READ_URL` | (vide) | `config.go:342` | DSN read-replica optionnel. Fallback sur `DATABASE_URL`. | | `DB_MAX_OPEN_CONNS` | `50` | `database/db_init.go` | Pool : connexions concurrentes. | | `DB_MAX_IDLE_CONNS` | `12` | `database/db_init.go` | Pool : idle. | | `DB_MAX_IDLE_TIME` | `5m` | `database/db_init.go` | Timeout idle. | | `DB_MAX_LIFETIME` | `10m` | `database/db_init.go` | Lifetime max avant recycle. | | `DB_READ_MAX_OPEN_CONNS` | `25` | `database/db_init.go` | Pool read-replica : max open. | | `DB_READ_MAX_IDLE_CONNS` | `6` | `database/db_init.go` | Pool read-replica : max idle. | | `DB_MAX_RETRIES` | `5` | `config.go:380` | Retries sur échec connexion. | | `DB_RETRY_INTERVAL` | `5s` | `config.go:381` | Délai entre retries. | > **Drift** : le template utilise `DATABASE_MAX_OPEN_CONNS` / `DATABASE_MAX_IDLE_CONNS` / `DATABASE_CONN_MAX_LIFETIME` — **ces noms ne sont pas lus par le code**. Utiliser `DB_MAX_*`. Template corrigé en v1.1.0. ## 3. Redis | Variable | Défaut | Lu à | Rôle | | --- | --- | --- | --- | | **`REDIS_URL`** | `redis://veza.fr:6379` | `config.go:338` | URL complète. **Doit être explicite en prod** (`validation.go:916`) — pas de fallback qui casserait multi-pod. | | `REDIS_ENABLE` | `true` | `config.go:339` | Désactiver Redis désactive CSRF, rate-limit, cache. | `REDIS_ADDR`, `REDIS_PASSWORD`, `REDIS_DB` apparaissent encore dans le template mais **ne sont plus lus** — utiliser `REDIS_URL`. Voir [§27](#27-variables-dépréciées--legacy). ## 4. JWT / authentification RS256 préféré (prod), HS256 fallback (dev). Clés générées par `scripts/generate-jwt-keys.sh`. | Variable | Défaut | Lu à | Rôle | | --- | --- | --- | --- | | `JWT_PRIVATE_KEY_PATH` | (vide) | `config.go:316` | Chemin clé privée RSA (PEM). Préféré en prod. | | `JWT_PUBLIC_KEY_PATH` | (vide) | `config.go:317` | Chemin clé publique RSA. | | `JWT_SECRET` | — | `config.go:318` | Fallback HS256. ≥32 chars. Requis si pas de RS256. | | `JWT_ISSUER` | `veza-api` | `config.go:335` | Claim `iss`. | | `JWT_AUDIENCE` | `veza-platform` | `config.go:336` | Claim `aud`. | | **`CHAT_JWT_SECRET`** | fallback sur `JWT_SECRET` | `config.go:337` | Tokens chat WebSocket. **Doit différer de `JWT_SECRET` en prod** (`validation.go:891`). | > `JWT_ACCESS_TOKEN_DURATION` et `JWT_REFRESH_TOKEN_DURATION` dans le template ne sont **pas lues** — durées hardcodées (access 5min, refresh 7j). **Génération RS256** : ```bash scripts/generate-jwt-keys.sh # ou manuellement : openssl genrsa -out jwt-private.pem 2048 openssl rsa -in jwt-private.pem -pubout -out jwt-public.pem ``` ## 5. OAuth providers Opt-in par provider : définir `CLIENT_ID` + `CLIENT_SECRET` pour activer. | Variable | Rôle | | --- | --- | | **`OAUTH_ENCRYPTION_KEY`** | Clé AES-256 (≥32 bytes) pour chiffrer les refresh tokens OAuth au repos. **Requis en prod** (`validation.go:896`). | | `OAUTH_ALLOWED_REDIRECT_DOMAINS` | Whitelist des domaines redirect URI. Défauts dérivés de `CORS_ALLOWED_ORIGINS`. | | `OAUTH_GOOGLE_CLIENT_ID` / `_SECRET` | Google OAuth. | | `OAUTH_GITHUB_CLIENT_ID` / `_SECRET` | GitHub OAuth. | | `OAUTH_DISCORD_CLIENT_ID` / `_SECRET` | Discord OAuth. | | `OAUTH_SPOTIFY_CLIENT_ID` / `_SECRET` | Spotify OAuth. | Tout lu dans `veza-backend-api/internal/config/services_init.go`. ## 6. CORS / cookies | Variable | Défaut | Lu à | Rôle | | --- | --- | --- | --- | | **`CORS_ALLOWED_ORIGINS`** | env-aware | `config.go:302` | Liste CSV. **Pas de wildcards en prod** (`validation.go:869`). | | `COOKIE_SECURE` | `false` (dev), `true` (prod) | middleware/cors | Cookies HTTPS-only. | | `COOKIE_SAME_SITE` | `lax` | `config.go:401` | `strict` / `lax` / `none`. | | `COOKIE_DOMAIN` | (vide) | `config.go:402` | Vide = domaine courant. | | `COOKIE_HTTP_ONLY` | `true` | `config.go:403` | Empêche accès JS. | | `COOKIE_PATH` | `/` | `config.go:404` | Path cookie. | ## 7. Rate limiting | Variable | Défaut | Lu à | Rôle | | --- | --- | --- | --- | | `RATE_LIMIT_LIMIT` | env-aware | `config.go:305` | Requêtes par fenêtre. | | `RATE_LIMIT_WINDOW` | `60` (secondes) | `config.go:306` | Taille fenêtre. | | `RATE_LIMIT_IP_PER_HOUR` | `100` prod / `5000` dev | `middleware/rate_limit.go` | Plafond par IP (non-auth). | | `RATE_LIMIT_USER_PER_HOUR` | `1000` prod / `2000` dev | `middleware/rate_limit.go` | Plafond par user (auth). | | `AUTH_RATE_LIMIT_LOGIN_ATTEMPTS` | `5` prod / `100` test | `config.go:374` | Seuil lockout login échoué. | | `AUTH_RATE_LIMIT_LOGIN_WINDOW` | `1` prod / `60` test (minutes) | `config.go:375` | Fenêtre lockout. | | `ACCOUNT_LOCKOUT_EXEMPT_EMAILS` | (vide) | `config.go:376` | CSV d'emails exemptés. | | `USER_RATE_LIMIT_BURST` | `100` | `middlewares_init.go:54` | Burst tokens par user (BE-SVC-002). | | `USER_RATE_LIMIT_PER_MINUTE` | `1000` | `middlewares_init.go:54` | RPM par user. | | `DISABLE_RATE_LIMIT_FOR_TESTS` | — | middleware | Bypass E2E (test only). | ## 8. SMTP / email | Variable | Défaut | Lu à | Rôle | | --- | --- | --- | --- | | `SMTP_HOST` | `localhost` | `mailer/sender.go:120` | MailHog par défaut en dev. | | `SMTP_PORT` | `1025` | `mailer/sender.go:121` | MailHog. | | `SMTP_USERNAME` | (vide) | `mailer/sender.go:122` | Nom canonique (v1.0.6). | | `SMTP_PASSWORD` | (vide) | `mailer/sender.go:123` | — | | `SMTP_FROM` | `no-reply@veza.local` | `mailer/sender.go:124` | Adresse expéditeur. | | `SMTP_FROM_NAME` | `Veza (dev)` | `mailer/sender.go:125` | Nom affiché. | | `EMAIL_TEMPLATE_DIR` | — | email handlers | Dossier MJML/HTML templates. | `SMTP_USER`, `FROM_EMAIL`, `FROM_NAME` fonctionnent encore mais loggent un warning deprecation — voir [§27](#27-variables-dépréciées--legacy). ## 9. Hyperswitch / paiements Désactivé par défaut. Si activé, plusieurs clés deviennent obligatoires. | Variable | Défaut | Lu à | Rôle | | --- | --- | --- | --- | | **`HYPERSWITCH_ENABLED`** | `false` | `config.go:407` | Master switch. **Fail-closed en prod** (`validation.go:908`) — boot prod refuse de démarrer si activé sans clés live. | | `HYPERSWITCH_LIVE_MODE` | `false` | `config.go:408` | Clés live vs test. | | `HYPERSWITCH_URL` | `http://localhost:18081` | `config.go:409` | Endpoint router. | | **`HYPERSWITCH_API_KEY`** | (vide) | `config.go:410` | Requis si `HYPERSWITCH_ENABLED=true`. | | **`HYPERSWITCH_WEBHOOK_SECRET`** | (vide) | `config.go:411` | Requis en prod pour vérification signature. | | `CHECKOUT_SUCCESS_URL` | `/purchases` | `config.go:412` | Redirect après paiement. | | `HYPERSWITCH_WEBHOOK_LOG_RETENTION_DAYS` | `90` | `config.go:433` | Jours de rétention `hyperswitch_webhook_log`. | ## 10. Stripe Connect / payouts créateurs | Variable | Défaut | Lu à | Rôle | | --- | --- | --- | --- | | `STRIPE_CONNECT_ENABLED` | `false` | `config.go:415` | Active le flow payout créateur (v0.602). | | `STRIPE_SECRET_KEY` | (vide) | `config.go:416` | Requis si activé. | | `STRIPE_CONNECT_WEBHOOK_SECRET` | (vide) | `config.go:417` | Requis si activé. | | `PLATFORM_FEE_RATE` | `0.10` | `config.go:418` | Commission plateforme (0–1). | | `TRANSFER_RETRY_ENABLED` | `true` | `config.go:421` | Worker retry transfers échoués. | | `TRANSFER_RETRY_MAX` | `3` | `config.go:422` | — | | `TRANSFER_RETRY_INTERVAL` | `5m` | `config.go:423` | Délai entre retries. | | `REVERSAL_WORKER_ENABLED` | `true` | `config.go:426` | Worker async reverse-charge (v1.0.7 item B). | | `REVERSAL_MAX_RETRIES` | `5` | `config.go:427` | — | | `REVERSAL_CHECK_INTERVAL` | `1m` | `config.go:428` | Poll. | | `REVERSAL_BACKOFF_BASE` | `1m` | `config.go:429` | Backoff exponentiel base. | | `REVERSAL_BACKOFF_MAX` | `1h` | `config.go:430` | Plafond backoff. | | `RECONCILE_WORKER_ENABLED` | `true` | `config.go:436` | Réconciliation orders/refunds stuck (v1.0.7 item C). | | `RECONCILE_INTERVAL` | `1h` | `config.go:437` | Sweep. | | `RECONCILE_ORDER_STUCK_AFTER` | `30m` | `config.go:438` | Seuil staleness order. | | `RECONCILE_REFUND_STUCK_AFTER` | `30m` | `config.go:439` | Seuil staleness refund. | | `RECONCILE_REFUND_ORPHAN_AFTER` | `5m` | `config.go:440` | Seuil orphan refund. | ## 11. RabbitMQ / event bus | Variable | Défaut | Lu à | Rôle | | --- | --- | --- | --- | | `RABBITMQ_ENABLE` | `true` | `config.go:397` | Active les events async. | | **`RABBITMQ_URL`** | `amqp://veza:password@veza.fr:15672/` | `config.go:326` | URL connexion. Requis en prod si activé. | | `RABBITMQ_MAX_RETRIES` | `10` | `config.go:395` | Retries connexion. | | `RABBITMQ_RETRY_INTERVAL` | `5s` | `config.go:396` | Délai retries. | ## 12. Stockage S3 / MinIO Opt-in. Le path upload principal n'utilise pas encore S3 (FUNCTIONAL_AUDIT §4 item 2 — stockage local disque only). | Variable | Défaut | Lu à | Rôle | | --- | --- | --- | --- | | `AWS_S3_ENABLED` | `false` | `config.go:364` | Master switch. | | `AWS_S3_BUCKET` | (vide) | `config.go:359` | Nom bucket. | | `AWS_REGION` | `us-east-1` | `config.go:360` | Région. | | `AWS_S3_ENDPOINT` | (vide) | `config.go:361` | Endpoint custom (MinIO). | | `AWS_ACCESS_KEY_ID` | (vide) | `config.go:362` | Optionnel si IAM role EC2. | | `AWS_SECRET_ACCESS_KEY` | (vide) | `config.go:363` | — | ## 13. HLS streaming + track storage backend ### HLS | Variable | Défaut | Lu à | Rôle | | --- | --- | --- | --- | | `HLS_STREAMING` | `false` | `config.go:355` | Active les endpoints HLS manifest/segments. Off = player fallback sur `/tracks/:id/stream` avec Range. | | `HLS_STORAGE_DIR` | `/tmp/veza-hls` | `config.go:356` | Stockage segments HLS. | | `HLS_OUTPUT_DIR` | (contextuel) | `workers/job_worker.go` | Output segments transcodés. | | `STREAM_HLS_BASE_URL` | (vide) | `live_stream_callback.go` | Base URL publique pour liens m3u8. | Pattern d'activation : `HLS_STREAMING=true` + stream server up + transcoding pipeline active. Défaut off, fallback direct-stream suffit pour le démarrage. ### Track upload storage backend (v1.0.8 Phase 0) | Variable | Défaut | Lu à | Rôle | | --- | --- | --- | --- | | **`TRACK_STORAGE_BACKEND`** | `local` | `config.go` (bloc S3) | Où `TrackService.UploadTrack()` écrit les fichiers track. Valeurs : `local` (disque, `uploads/tracks/`) ou `s3` (via `S3StorageService`). **En prod, `s3` nécessite `AWS_S3_ENABLED=true`** — boot échoue sinon (`validation.go` règle 11). Dev/staging warn et fallback sur `local`. | Pour migrer un environnement : 1. Deploy avec `TRACK_STORAGE_BACKEND=local` (aucun changement). 2. Activer S3 : `AWS_S3_ENABLED=true`, `AWS_S3_BUCKET=`, `AWS_S3_ENDPOINT=`, credentials. 3. Flipper : `TRACK_STORAGE_BACKEND=s3`. Nouveaux uploads vont sur MinIO/S3, tracks existants restent `local`. 4. (Optionnel) Lancer `cmd/migrate_storage` (Phase 3) pour déplacer les tracks `local` vers `s3`. Rollback : revert `TRACK_STORAGE_BACKEND=local`, nouveaux uploads repartent en local. Les tracks déjà migrés restent en `s3` (read path les sert via signed URL en Phase 2). ## 13bis. WebRTC ICE servers (v1.0.9 item 1.2) Public endpoint `GET /api/v1/config/webrtc` returns `{ iceServers }` to the SPA so 1:1 calls (`features/chat/hooks/useWebRTC.ts`) can hand a runtime ICE list to `RTCPeerConnection` without baking secrets into the bundle. See `infra/coturn/README.md` for the relay deploy guide. | Variable | Défaut | Lu à | Rôle | | --- | --- | --- | --- | | `WEBRTC_STUN_URLS` | `stun:stun.l.google.com:19302` | `config.go` | Liste comma-separated. STUN seul suffit hors NAT symétrique. | | `WEBRTC_TURN_URLS` | (vide) | `config.go` | Liste comma-separated, e.g. `turn:turn.veza.fr:3478,turns:turn.veza.fr:5349`. Vide = pas de relais TURN exposé, le SPA bascule sur l'advisory "TURN non configuré". | | `WEBRTC_TURN_USERNAME` | (vide) | `config.go` | Identifiant statique `lt-cred-mech` côté coturn. **Doit matcher `user=` dans `infra/coturn/turnserver.conf`**. | | `WEBRTC_TURN_CREDENTIAL` | (vide) | `config.go` | Mot de passe statique. Rotation = update conf coturn + ces deux env vars + reload coturn. | **Half-config = aucune TURN diffusée.** Le handler omet le bloc TURN si n'importe lequel de URLs/Username/Credential est vide — un demi-config est pire que rien (le navigateur surfacerait une erreur d'auth au moment du call au lieu du fallback STUN propre). ## 14. Stream server (backend ↔ stream) | Variable | Défaut | Lu à | Rôle | | --- | --- | --- | --- | | `STREAM_SERVER_URL` | `http://veza.fr:8082` | `config.go:344` | URL stream server côté backend. | | **`STREAM_SERVER_INTERNAL_API_KEY`** | (vide) | `config.go:345` | Secret partagé `/internal/jobs/transcode`. Doit matcher le `INTERNAL_API_KEY` du stream server. | | `CHAT_SERVER_URL` | `http://veza.fr:8081` | `config.go:346` | Historique — le chat est dans le backend Go depuis v0.502. | ## 15. Elasticsearch | Variable | Défaut | Lu à | Rôle | | --- | --- | --- | --- | | `ELASTICSEARCH_URL` | (vide) | `elasticsearch/config.go:14` | Vide = search désactivée (fallback Postgres). | | `ELASTICSEARCH_INDEX` | `veza-platform` | `elasticsearch/config.go:15` | Préfixe indices. | | `ELASTICSEARCH_AUTO_INDEX` | `false` | search service | Auto-create index au boot. | Admin : `POST /api/v1/admin/search/reindex` pour réindexation manuelle. ## 16. ClamAV / antivirus | Variable | Défaut | Lu à | Rôle | | --- | --- | --- | --- | | `ENABLE_CLAMAV` | `true` | `api/router.go` | Active le scan AV sur upload. | | **`CLAMAV_REQUIRED`** | `true` | `validation.go:886` | **Boot prod échoue si ClamAV indispo**. Dev seul peut mettre `false`. | | `CLAMAV_ADDRESS` | — | upload service | `host:port` clamd. | | `CLAMAV_CLAMD_PATH` | — | upload service | Alternative socket local. | ## 17. Sentry / tracking d'erreurs | Variable | Défaut | Lu à | Rôle | | --- | --- | --- | --- | | `SENTRY_DSN` | (vide) | `config.go:367` | Vide = Sentry désactivé. | | `SENTRY_ENVIRONMENT` | dérivé de `APP_ENV` | `config.go:368` | Label UI Sentry. | | `SENTRY_SAMPLE_RATE_ERRORS` | `1.0` | `config.go:369` | 0–1. | | `SENTRY_SAMPLE_RATE_TRANSACTIONS` | `0.1` | `config.go:370` | 0–1. | ## 18. Logging | Variable | Défaut | Lu à | Rôle | | --- | --- | --- | --- | | `LOG_DIR` | `/var/log/veza` | `config.go:444` | Dossier fichiers log. | | `LOG_FORMAT` | env-aware | logging | `json` (prod) / `console` (dev). | | `LOG_AGGREGATION_ENABLED` | dérivé | `config.go:464` | True auto si endpoint défini. | | `LOG_AGGREGATION_ENDPOINT` | (vide) | `config.go:386` | Loki ou compatible. | | `LOG_AGGREGATION_BATCH_SIZE` | `100` | `config.go:387` | Lignes par batch. | | `LOG_AGGREGATION_FLUSH_INTERVAL` | `5s` | `config.go:388` | Auto-flush. | | `LOG_AGGREGATION_TIMEOUT` | `10s` | `config.go:389` | Timeout HTTP. | | `LOG_AGGREGATION_LABELS` | `service=veza-api` | `config.go:390` | CSV `key=value`. | ## 19. Monitoring / metrics | Variable | Défaut | Lu à | Rôle | | --- | --- | --- | --- | | `PROMETHEUS_URL` | (vide) | `config.go` | Push gateway optionnel. | | `SLOW_REQUEST_THRESHOLD_MS` | `1000` | `middleware/request_logger.go` | Log + counter pour requêtes plus lentes. | | `METRICS_ALLOWED_IPS` | — | metrics middleware | CIDR whitelist `/metrics`. | | `METRICS_BEARER_TOKEN` | — | metrics middleware | Auth bearer optionnelle. | | `METRICS_PUBLIC_IN_DEV` | `false` | metrics middleware | Skip auth en dev. | En prod, si ni `METRICS_ALLOWED_IPS` ni `METRICS_BEARER_TOKEN` n'est défini, `/metrics` retourne 403. ## 20. Frontend (Vite) Tout lu dans `apps/web/src/lib/env.ts` (+ `apps/web/src/lib/featureFlags.ts`). Préfixe `VITE_` obligatoire — les autres vars ne sont pas exposées au navigateur. **URLs de base** — dérivées de `VITE_DOMAIN` si omises : | Variable | Défaut | Rôle | | --- | --- | --- | | `VITE_DOMAIN` | `veza.fr` | Domaine racine pour dérivations. | | `VITE_API_URL` | `/api/v1` | API backend (relatif ou absolu). | | `VITE_WS_URL` | dérivé | URL WebSocket. | | `VITE_STREAM_URL` | `ws://veza.fr:8082/stream` | WS stream server. | | `VITE_HLS_BASE_URL` | dérivé | Base m3u8. | | `VITE_UPLOAD_URL` | `/upload` | Endpoint upload. | | `VITE_BACKEND_PORT` | `18080` | Target proxy Vite dev. | | `VITE_APP_NAME` | `Veza` | Nom affiché. | | `VITE_API_VERSION` | `v1` | Suffix version API. | **Clés tierces** : | Variable | Rôle | | --- | --- | | `VITE_HYPERSWITCH_PUBLISHABLE_KEY` | Widget checkout Hyperswitch. | | `VITE_FCM_VAPID_KEY` | FCM push notifications. | | `VITE_SENTRY_DSN` | Sentry frontend. | **Toggles runtime** : | Variable | Défaut | Rôle | | --- | --- | --- | | `VITE_DEBUG` | `false` | Log API verbeux (`true` / `1`). | | `VITE_USE_MSW` | `0` | Mock Service Worker. | | `VITE_LOG_LEVEL` | (utils) | Niveau log frontend. | | `VITE_LOG_ENDPOINT` | (vide) | Logging distant. | | `VITE_ENABLE_VALIDATION_ALERTING` | — | Alertes validation UI. | | `VITE_GITHUB_REPO` | — | Lien feedback. | | `VITE_STORYBOOK` | — | Mode Storybook. | ## 21. Feature flags Backend (`config.go`) : | Variable | Défaut | Lu à | Rôle | | --- | --- | --- | --- | | `MAINTENANCE_MODE` | `false` | `middleware/maintenance.go` | 503 pour routes non-admin. TTL-cache depuis `platform_settings`. | | `BYPASS_CONTENT_CREATOR_ROLE` | `false` | `validation.go:1018` | Dev/test only — validation prod échoue si true. | | `CSRF_DISABLED` | `false` | CSRF middleware | Dev only. | | `HARD_DELETE_CRON_ENABLED` | `false` | cleanup jobs | Sweep nightly hard-delete. | Frontend (`apps/web/src/lib/featureFlags.ts`) : | Variable | Défaut | Feature | | --- | --- | --- | | `VITE_FEATURE_TWO_FACTOR_AUTH` | `true` | 2FA. | | `VITE_FEATURE_PLAYLIST_COLLABORATION` | `true` | Playlists partagées. | | `VITE_FEATURE_PLAYLIST_SEARCH` | `false` | Search intra-playlist. | | `VITE_FEATURE_PLAYLIST_SHARE` | `false` | Liens token partageables. | | `VITE_FEATURE_PLAYLIST_RECOMMENDATIONS` | `false` | — | | `VITE_FEATURE_HLS_STREAMING` | `true` | HLS playback player (gate frontend). | | `VITE_FEATURE_ROLE_MANAGEMENT` | `false` | Matrice rôles admin. | | `VITE_FEATURE_NOTIFICATIONS` | `false` | Push notifications. | ## 22. Politique de mot de passe | Variable | Défaut | Lu à | | --- | --- | --- | | `PASSWORD_MIN_LENGTH` | (built-in) | `password_validator.go` | | `PASSWORD_MAX_LENGTH` | (built-in) | `password_validator.go` | | `PASSWORD_REQUIRE_UPPER` | (built-in) | `password_validator.go` | | `PASSWORD_REQUIRE_LOWER` | (built-in) | `password_validator.go` | | `PASSWORD_REQUIRE_NUMBER` | (built-in) | `password_validator.go` | | `PASSWORD_REQUIRE_SPECIAL` | (built-in) | `password_validator.go` | ## 23. Build / version info Injecté au build via `-ldflags` : | Variable | Défaut | Lu à | Rôle | | --- | --- | --- | --- | | `APP_VERSION` | `v1.0.0` | `routes_core.go` | Version sémantique. | | `GIT_COMMIT` | `unknown` | `routes_core.go` | SHA Git. | | `BUILD_TIME` | (vide) | `routes_core.go` | Timestamp RFC3339. | ## 24. RTMP / Web Push / divers | Variable | Rôle | | --- | --- | | `VAPID_PRIVATE_KEY` / `VAPID_PUBLIC_KEY` | Web Push (requis si push activé). | | `RTMP_CALLBACK_SECRET` | Signature callback `on_publish` Nginx-RTMP. | | `NGINX_RTMP_HOST` | Hôte affiché aux streamers (`rtmp_url`). Défaut `stream.veza.app`. | | `NGINX_RTMP_ADDR` | Endpoint interne RTMP. | | `GEOIP_DB_PATH` | Chemin DB GeoIP2 (optionnel — géolocalisation IP). | | `HANDLER_TIMEOUT` | Timeout handler global (défaut `30s`, `config.go:377`). | | `MAX_CONCURRENT_UPLOADS` | Backpressure upload (défaut `10`, `config.go:312`). | | `MAX_JSON_BODY_SIZE` | Plafond body JSON (défaut 1MB). | | `CONFIG_WATCH` | Reload sur changement `.env` (T0040, défaut `false`, `config.go:765`). | | `FRONTEND_URL` | URL frontend côté backend (OAuth, emails). | | `BASE_URL` | URL publique backend (callbacks OAuth). | | `PGPASSWORD` | Pour CLI `psql` — pas le backend. | | `VEZA_ROOT` / `VEZA_SKIP_INTEGRATION` | Helpers test-suite. | | `E2E_TEST` / `TEST_EMAIL` / `TEST_USERNAME` / `TEST_PASSWORD` | Setup E2E. | **Live streaming** (v0.10.6 F471) : profiles Docker `live`. Ports 1935 (RTMP), 18083 (HTTP HLS). Callbacks vers `POST /api/v1/live/callback/publish` et `.../publish_done`. ## 25. Stream server (Rust) — schéma propre Le stream server Rust a ~50 variables propres couvrant compression audio, TLS, workers Tokio, caching, tracing. Fichier principal : `veza-stream-server/src/config/mod.rs`. **Connexion** : `DATABASE_URL`, `REDIS_URL`, `RABBITMQ_URL`, `BACKEND_URL`, `PORT`, `SECRET_KEY`, `INTERNAL_API_KEY`, `ALLOWED_ORIGINS`, `JWT_PUBLIC_KEY_PATH`, `JWT_SECRET`, `JWT_EXPIRATION`. **Stockage / audio** : `AUDIO_DIR`, `STREAM_OUTPUT_DIR`, `COMPRESSION_ENABLED`, `COMPRESSION_LEVEL`, `COMPRESSION_MAX_CONCURRENT_JOBS`, `COMPRESSION_OUTPUT_DIR`, `COMPRESSION_TEMP_DIR`, `COMPRESSION_CLEANUP_AFTER_DAYS`, `FFMPEG_PATH`. **Pool DB** : `DB_POOL_SIZE`, `DB_CONNECTION_TIMEOUT`, `DB_IDLE_TIMEOUT`, `DB_MAX_LIFETIME`, `DB_MIN_CONNECTIONS`, `DB_MIGRATE_ON_START`, `DB_ENABLE_LOGGING`. **Networking** : `TLS_CERT_PATH`, `TLS_KEY_PATH`, `TCP_NODELAY`, `TCP_KEEPALIVE`, `MAX_FILE_SIZE`, `MAX_RANGE_SIZE`, `MAX_CONCURRENT_STREAMS`, `STREAM_TIMEOUT`, `CORS_MAX_AGE`. **Runtime Tokio** : `THREAD_STACK_SIZE`, `MAX_BLOCKING_THREADS`, `WORKER_THREADS`. **Cache** : `CACHE_TTL_SECONDS`, `CACHE_MAX_SIZE_MB`, `CACHE_COMPRESSION`, `CACHE_CLEANUP_INTERVAL`. **Notifications** : `NOTIFICATIONS_ENABLED`, `NOTIFICATIONS_BATCH_SIZE`, `NOTIFICATIONS_MAX_QUEUE_SIZE`, `NOTIFICATIONS_DELIVERY_WORKERS`, `NOTIFICATIONS_RETRY_ATTEMPTS`, `NOTIFICATIONS_RETRY_DELAY`, `ALERT_WEBHOOKS`. **Sécurité** : `SECURE_HEADERS`, `CSRF_PROTECTION`, `SIGNATURE_TOLERANCE`. **Observability** : `METRICS_ENABLED`, `METRICS_PORT`, `PROMETHEUS_NAMESPACE`, `JAEGER_ENDPOINT`, `HEALTH_CHECK_INTERVAL`, `LOG_DIR`, `LOG_LEVEL`, `LOG_FORMAT`. Documentation complète beside le stream server (`veza-stream-server/.env.example`) — délibérément séparé de ce catalogue (audience différente : opérateur stream vs. dev backend). ## 26. Security headers (rappel) Appliqués globalement via `middleware.SecurityHeaders()` (`veza-backend-api/internal/middleware/security_headers.go`). Non configurables par env var — config dans le code. | Header | Valeur | Rôle | | --- | --- | --- | | `Strict-Transport-Security` | `max-age=31536000; includeSubDomains; preload` (prod only) | HSTS | | `X-Content-Type-Options` | `nosniff` | Anti MIME sniffing | | `X-Frame-Options` | `DENY` (`SAMEORIGIN` pour Swagger) | Anti clickjacking | | `X-XSS-Protection` | `1; mode=block` | XSS legacy | | `Referrer-Policy` | `strict-origin-when-cross-origin` | Referrer | | `Permissions-Policy` | `geolocation=(), microphone=(), camera=()...` | Restriction features | | `Content-Security-Policy` | `default-src 'none'; ...` | CSP strict hors Swagger | | `X-Permitted-Cross-Domain-Policies` | `none` | Flash/PDF | | `Cross-Origin-Embedder-Policy` | `require-corp` | COEP | | `Cross-Origin-Opener-Policy` | `same-origin` | COOP | | `Cross-Origin-Resource-Policy` | `same-origin` | CORP | ## 27. Variables dépréciées / legacy Fonctionnent encore avec un warning deprecation. **Supprimées en v1.1.0** — migrer maintenant. | Déprécié | Canonique | Notes | | --- | --- | --- | | `REDIS_ADDR` | `REDIS_URL` | URL supersede host/port/db split. | | `REDIS_PASSWORD` | `REDIS_URL` | Embedded dans URL. | | `REDIS_DB` | `REDIS_URL` | Path segment `/0`. | | `SMTP_USER` | `SMTP_USERNAME` | Renommé v1.0.6. | | `FROM_EMAIL` | `SMTP_FROM` | Unifié v1.0.6. | | `FROM_NAME` | `SMTP_FROM_NAME` | Unifié v1.0.6. | | `GO_ENV`, `NODE_ENV` | `APP_ENV` | — | | `VITE_FRONTEND_URL` | `FRONTEND_URL` | Frontend lit depuis backend. | | `JWT_ACCESS_TOKEN_DURATION` | (hardcoded) | Hardcodé dans le service JWT. | | `JWT_REFRESH_TOKEN_DURATION` | (hardcoded) | — | | `RATE_LIMIT_ENABLED` | (implicite) | Toujours on si Redis dispo. | | `DATABASE_MAX_OPEN_CONNS` | `DB_MAX_OPEN_CONNS` | Bug template — renommé v1.1.0. | | `DATABASE_MAX_IDLE_CONNS` | `DB_MAX_IDLE_CONNS` | — | | `DATABASE_CONN_MAX_LIFETIME` | `DB_MAX_LIFETIME` | — | ## 28. Règles de validation production `Config.Validate()` dans `veza-backend-api/internal/config/validation.go` applique : | Règle | Fichier:ligne | | --- | --- | | `LOG_LEVEL=DEBUG` refusé en prod | `validation.go:876` | | `CORS_ALLOWED_ORIGINS` sans wildcards en prod | `validation.go:869` | | `CHAT_JWT_SECRET` doit différer de `JWT_SECRET` en prod | `validation.go:891` | | `CLAMAV_REQUIRED=false` refusé en prod | `validation.go:886` | | `OAUTH_ENCRYPTION_KEY` ≥32 bytes requis en prod | `validation.go:896` | | `HYPERSWITCH_ENABLED=true` requiert `HYPERSWITCH_API_KEY` + webhook secret | `validation.go:908` | | `REDIS_URL` doit être explicite en prod (pas de fallback default) | `validation.go:916` | | `TRACK_STORAGE_BACKEND` must be `local` ou `s3` (règle 11, v1.0.8) | `config.go` `ValidateForEnvironment` | | `TRACK_STORAGE_BACKEND=s3` requiert `AWS_S3_ENABLED=true` en prod | `config.go` `ValidateForEnvironment` | | `BYPASS_CONTENT_CREATOR_ROLE=true` refusé en prod | `validation.go:1018` | Si une règle est violée, boot échoue avec message humain pointant la variable fautive. Validation manuelle : `./scripts/validate-env.sh development` (ou `production`, `test`). Intégré dans `make doctor`. ## 29. Drift template ↔ code Survey 2026-04-23 a identifié des incohérences entre `.env.template` et le code. Non-bloquants, cleanup v1.1.0. **Manquant dans template** (le code les lit, le dev peut s'en sortir sans les définir) : `HLS_STREAMING` (ajouté 2026-04-23), `TRACK_STORAGE_BACKEND` (ajouté 2026-04-23), `SLOW_REQUEST_THRESHOLD_MS`, `CONFIG_WATCH`, `HANDLER_TIMEOUT`, `MAX_JSON_BODY_SIZE`, `GEOIP_DB_PATH`, `VAPID_PRIVATE_KEY` / `VAPID_PUBLIC_KEY`, `RTMP_CALLBACK_SECRET`, `NGINX_RTMP_*`, `HARD_DELETE_CRON_ENABLED`, `ELASTICSEARCH_AUTO_INDEX`. **Dans template mais non lus** : `REDIS_ADDR`, `REDIS_PASSWORD`, `REDIS_DB`, `JWT_ACCESS_TOKEN_DURATION`, `JWT_REFRESH_TOKEN_DURATION`, `RATE_LIMIT_ENABLED`, `DATABASE_MAX_OPEN_CONNS` / `DATABASE_MAX_IDLE_CONNS` / `DATABASE_CONN_MAX_LIFETIME` (mauvais noms). **Incohérence de nommage** : `SMTP_USERNAME` canonique vs `SMTP_USER` legacy ; `DB_MAX_*` code vs `DATABASE_MAX_*` template. ## 30. Checklist de démarrage 1. Copier `veza-backend-api/.env.template` vers `veza-backend-api/.env` et configurer. 2. Pour RS256 prod : exécuter `scripts/generate-jwt-keys.sh` et configurer `JWT_PRIVATE_KEY_PATH`, `JWT_PUBLIC_KEY_PATH`. Sinon `JWT_SECRET` ≥32 chars. 3. Configurer les **5 minimums** : `DATABASE_URL`, `REDIS_URL`, `JWT_SECRET` (ou keys RS256), `CORS_ALLOWED_ORIGINS`, `FRONTEND_URL`. 4. Si paiements : `HYPERSWITCH_ENABLED=true` + `HYPERSWITCH_API_KEY` + `HYPERSWITCH_WEBHOOK_SECRET`. 5. Valider : `./scripts/validate-env.sh development` (ou `production`). 6. Démarrer : `make dev`. 7. En prod : ne jamais committer `.env` ni fichiers `.pem`. --- *Dernière sync 2026-04-23 — `v1.0.7-rc1` HEAD `778c8550`. Quand tu ajoutes une variable dans le code, mets à jour ce fichier + `.env.template` dans le même commit. Cross-référence `AUDIT_REPORT.md` §9 item #15.*