diff --git a/docs/adr/023-architecture-moderation.md b/docs/adr/023-architecture-moderation.md
index 3383c93..055ed80 100644
--- a/docs/adr/023-architecture-moderation.md
+++ b/docs/adr/023-architecture-moderation.md
@@ -28,79 +28,35 @@ Architecture hybride **humain + IA** avec file d'attente intelligente.
### Architecture
-```mermaid
-graph TB
- subgraph Client["App Mobile/Web"]
- Report["Signalement utilisateur"]
- end
-
- subgraph Backend["Backend Go"]
- API["API Fiber
/moderation/report"]
- Queue["PostgreSQL Queue
LISTEN/NOTIFY"]
- Worker["Worker Go
(transcription + NLP)"]
- end
-
- subgraph AI["IA Self-hosted"]
- Whisper["Whisper large-v3
(transcription)"]
- NLP["distilbert
(sentiment + haine)"]
- end
-
- subgraph Moderation["Modération Dashboard"]
- Dashboard["React Dashboard"]
- Player["Wavesurfer.js
(lecture audio)"]
- end
-
- subgraph Storage["Stockage"]
- DB["PostgreSQL
(signalements + logs)"]
- Redis["Redis
(priorisation + cache)"]
- end
-
- Report --> API
- API --> Queue
- Queue --> Worker
- Worker --> Whisper
- Whisper --> NLP
- NLP --> Redis
- Worker --> DB
- Dashboard --> Player
- Dashboard --> Redis
- Dashboard --> DB
-
- classDef clientStyle fill:#e3f2fd,stroke:#1565c0
- classDef backendStyle fill:#fff3e0,stroke:#e65100
- classDef aiStyle fill:#f3e5f5,stroke:#6a1b9a
- classDef storageStyle fill:#e8f5e9,stroke:#2e7d32
-
- class Client,Report clientStyle
- class Backend,API,Queue,Worker backendStyle
- class AI,Whisper,NLP aiStyle
- class Storage,DB,Redis storageStyle
-```
+**Flux de traitement** :
+1. **Client** (App Mobile/Web) → Signalement utilisateur
+2. **API Backend** (Fiber) → Endpoint `/moderation/report`
+3. **Queue PostgreSQL** → LISTEN/NOTIFY pour dispatch asynchrone
+4. **Worker Go** → Goroutine de traitement (transcription + analyse)
+5. **IA Self-hosted** → Whisper large-v3 (transcription) + distilbert/roberta (NLP)
+6. **Cache Redis** → Sorted Sets pour priorisation temps réel
+7. **Dashboard React** → Interface modérateurs avec Wavesurfer.js (player audio)
+8. **Stockage** → PostgreSQL (signalements + logs audit) + Redis (cache priorisation)
### Workflow de Traitement
1. **Réception signalement** :
- ```sql
- INSERT INTO moderation_reports (content_id, user_id, category, comment)
- VALUES ($1, $2, $3, $4)
- RETURNING id;
-
- NOTIFY moderation_queue, 'report_id:{id}';
- ```
+ - Insertion en base PostgreSQL (table `moderation_reports`)
+ - Notification asynchrone via PostgreSQL NOTIFY
2. **Worker asynchrone** (goroutine) :
- - Écoute `LISTEN moderation_queue`
- - Télécharge audio depuis stockage S3/local
- - Transcription Whisper (1-10 min selon durée)
- - Analyse NLP : score confiance 0-100%
- - Calcul priorité : `(score_IA × 0.7) + (nb_signalements × 0.2) + (fiabilité_signaleur × 0.1)`
- - Insertion Redis Sorted Set : `ZADD moderation:priority {priority} {report_id}`
+ - Écoute queue PostgreSQL (LISTEN/NOTIFY)
+ - Téléchargement audio depuis stockage S3/local
+ - Transcription audio via Whisper large-v3 (1-10 min selon durée)
+ - Analyse NLP : score confiance 0-100% (distilbert + roberta)
+ - Calcul priorité selon formule : `(score_IA × 0.7) + (nb_signalements × 0.2) + (fiabilité_signaleur × 0.1)`
+ - Insertion dans Redis Sorted Set pour priorisation
3. **Dashboard modérateurs** :
- - Poll Redis Sorted Set : `ZREVRANGE moderation:priority 0 19` (top 20)
- - Affichage liste priorisée avec transcription, waveform, historique créateur
- - Actions : Approuver, Rejeter, Escalade (shortcuts clavier A/R/E)
- - Logs audit PostgreSQL (conformité DSA)
+ - Récupération signalements priorisés depuis Redis (top 20 par page)
+ - Affichage : transcription, waveform audio, historique créateur
+ - Actions disponibles : Approuver, Rejeter, Escalade (shortcuts clavier A/R/E)
+ - Logs audit PostgreSQL pour traçabilité (conformité DSA)
## Alternatives considérées
@@ -136,14 +92,7 @@ graph TB
- **Performance MVP** : Suffisant jusqu'à 1000 signalements/jour (~0.7/min)
- **Simplicité** : Pas de broker externe, transactions ACID
-- **Migration facile** : Abstraction interface `ModerationQueue` → swap vers Redis Streams si besoin
-
-```go
-type ModerationQueue interface {
- Enqueue(ctx context.Context, reportID int64) error
- Listen(ctx context.Context) (<-chan int64, error)
-}
-```
+- **Migration facile** : Abstraction via interface `ModerationQueue` → swap vers Redis Streams si besoin (méthodes : Enqueue, Listen)
### Whisper large-v3 self-hosted
@@ -176,24 +125,16 @@ type ModerationQueue interface {
### Dépendances
-```go
-// backend/go.mod
-require (
- github.com/gofiber/fiber/v3 latest // API Dashboard
- github.com/jackc/pgx/v5 latest // PostgreSQL + LISTEN/NOTIFY
- github.com/redis/rueidis latest // Cache priorisation
- // Whisper via Python subprocess ou go-whisper bindings
-)
-```
+**Backend Go** :
+- `gofiber/fiber/v3` : API Dashboard
+- `jackc/pgx/v5` : PostgreSQL + LISTEN/NOTIFY
+- `redis/rueidis` : Cache priorisation
+- Whisper : via Python subprocess ou go-whisper bindings
**Frontend Dashboard** :
-```json
-{
- "react": "^18.3.0",
- "@tanstack/react-table": "^8.10.0",
- "wavesurfer.js": "^7.0.0"
-}
-```
+- `react` : Framework UI
+- `@tanstack/react-table` : Tables performantes
+- `wavesurfer.js` : Player audio avec waveform
## Métriques de Succès
diff --git a/docs/adr/024-monitoring-observabilite.md b/docs/adr/024-monitoring-observabilite.md
index bbd6c3e..530d5ac 100644
--- a/docs/adr/024-monitoring-observabilite.md
+++ b/docs/adr/024-monitoring-observabilite.md
@@ -31,86 +31,42 @@ Stack **Prometheus + Grafana + Loki** self-hosted avec alerting multi-canal.
### Architecture
-```mermaid
-graph TB
- subgraph Services["Services RoadWave"]
- API["Backend Go API
(Fiber metrics)"]
- DB["PostgreSQL
(pg_exporter)"]
- Redis["Redis
(redis_exporter)"]
- Zitadel["Zitadel
(metrics endpoint)"]
- end
+**Services surveillés** :
+- Backend Go API (métriques Fiber)
+- PostgreSQL (pg_exporter)
+- Redis (redis_exporter)
+- Zitadel (endpoint metrics)
- subgraph Monitoring["Stack Monitoring"]
- Prom["Prometheus
(scrape + TSDB)"]
- Grafana["Grafana
(dashboards)"]
- Loki["Loki
(logs aggregation)"]
- Alert["Alertmanager
(routing)"]
- Uptime["Uptime Kuma
(external checks)"]
- end
+**Stack Monitoring** :
+- **Prometheus** : Collecte métriques (scrape), stockage TSDB 15j rétention
+- **Grafana** : Visualisation dashboards
+- **Loki** : Agrégation logs (chunks compressés, 7j rétention)
+- **Alertmanager** : Routing alertes multi-canal
+- **Uptime Kuma** : Checks HTTP externes, SSL monitoring
- subgraph Notifications["Alerting"]
- Email["Email (Brevo)"]
- Slack["Webhook Slack/Discord"]
- end
+**Alerting** :
+- Email (Brevo) : asynchrone, faible intrusivité
+- Webhook (Slack/Discord) : temps réel, on-call
- subgraph Storage["Stockage"]
- PromStorage["Prometheus TSDB
(15j retention)"]
- LokiStorage["Loki Chunks
(7j retention)"]
- Backups["Backups PostgreSQL
(S3 OVH)"]
- end
-
- API --> Prom
- DB --> Prom
- Redis --> Prom
- Zitadel --> Prom
-
- API -.->|logs stdout| Loki
- Prom --> Grafana
- Loki --> Grafana
- Prom --> Alert
-
- Alert --> Email
- Alert --> Slack
-
- Uptime -.->|external HTTP checks| API
- Uptime --> Alert
-
- Prom --> PromStorage
- Loki --> LokiStorage
- DB -.->|WAL-E continuous| Backups
-
- classDef serviceStyle fill:#e3f2fd,stroke:#1565c0
- classDef monitoringStyle fill:#fff3e0,stroke:#e65100
- classDef notifStyle fill:#f3e5f5,stroke:#6a1b9a
- classDef storageStyle fill:#e8f5e9,stroke:#2e7d32
-
- class Services,API,DB,Redis,Zitadel serviceStyle
- class Monitoring,Prom,Grafana,Loki,Alert,Uptime monitoringStyle
- class Notifications,Email,Slack notifStyle
- class Storage,PromStorage,LokiStorage,Backups storageStyle
-```
+**Stockage** :
+- Prometheus TSDB : métriques 15j
+- Loki chunks : logs 7j
+- Backups PostgreSQL : WAL-E continuous vers S3 OVH
### Métriques Clés
-**API Performance** (Prometheus PromQL) :
-```promql
-# Latency p99
-histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m]))
-
-# Error rate
-rate(http_requests_total{status=~"5.."}[5m]) / rate(http_requests_total[5m])
-
-# Throughput
-rate(http_requests_total[5m])
-```
+**API Performance** (requêtes PromQL) :
+- Latency p99 : histogramme quantile 99e percentile sur durée requêtes HTTP (fenêtre 5 min)
+- Error rate : ratio requêtes 5xx / total requêtes (fenêtre 5 min)
+- Throughput : taux de requêtes par seconde (fenêtre 5 min)
**Infrastructure** :
-- CPU usage : `rate(node_cpu_seconds_total{mode!="idle"}[5m])`
-- Memory usage : `node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes`
-- Disk I/O : `rate(node_disk_io_time_seconds_total[5m])`
+- CPU usage : taux utilisation CPU (mode non-idle, fenêtre 5 min)
+- Memory usage : ratio mémoire disponible / totale
+- Disk I/O : temps I/O disque (fenêtre 5 min)
-**Business** :
-- Active users (DAU) : compteur custom `roadwave_active_users_total`
+**Business** (compteurs custom) :
+- Active users (DAU) : `roadwave_active_users_total`
- Audio streams actifs : `roadwave_hls_streams_active`
- Signalements modération : `roadwave_moderation_reports_total`
@@ -209,60 +165,29 @@ rate(http_requests_total[5m])
### Alerting Rules
-**Critiques** (Slack + Email immédiat) :
-```yaml
-- alert: APIDown
- expr: up{job="roadwave-api"} == 0
- for: 1m
- severity: critical
- message: "API indisponible depuis 1 min"
+**Alertes critiques** (Slack + Email immédiat) :
+- **API Down** : Job API indisponible pendant >1 min → Notification immédiate
+- **High Error Rate** : Taux erreurs 5xx >1% pendant >5 min → Notification immédiate
+- **Database Down** : PostgreSQL indisponible pendant >1 min → Notification immédiate
-- alert: HighErrorRate
- expr: rate(http_requests_total{status=~"5.."}[5m]) > 0.01
- for: 5m
- severity: critical
- message: "Error rate >1% depuis 5 min"
-
-- alert: DatabaseDown
- expr: up{job="postgresql"} == 0
- for: 1m
- severity: critical
- message: "PostgreSQL indisponible"
-```
-
-**Warnings** (Email uniquement) :
-```yaml
-- alert: HighLatency
- expr: histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m])) > 0.1
- for: 10m
- severity: warning
- message: "Latency p99 >100ms depuis 10 min"
-
-- alert: DiskSpaceRunningOut
- expr: node_filesystem_avail_bytes / node_filesystem_size_bytes < 0.1
- for: 30m
- severity: warning
- message: "Espace disque <10%"
-```
+**Alertes warnings** (Email uniquement) :
+- **High Latency** : Latency p99 >100ms pendant >10 min → Investigation requise
+- **Disk Space Running Out** : Espace disque <10% pendant >30 min → Nettoyage requis
### Backup & Disaster Recovery
**PostgreSQL WAL-E** :
-```bash
-# Backup continu WAL (Write-Ahead Log)
-wal-e backup-push /var/lib/postgresql/data
-
-# Rétention : 7 jours full + WAL
-# Stockage : S3 OVH (région GRA, France)
-# Chiffrement : AES-256 server-side
-```
+- Méthode : Backup continu Write-Ahead Log (WAL)
+- Rétention : 7 jours full + WAL incrémentaux
+- Stockage : S3 OVH région GRA (France)
+- Chiffrement : AES-256 server-side
**RTO (Recovery Time Objective)** : 1h
-- Temps de restore depuis S3 : ~30 min (DB 10 GB)
-- Temps validation + relance services : ~30 min
+- Restore depuis S3 : ~30 min (DB 10 GB)
+- Validation + relance services : ~30 min
**RPO (Recovery Point Objective)** : 15 min
-- WAL archivage toutes les 15 min
+- Fréquence archivage WAL : toutes les 15 min
- Perte maximale : 15 min de transactions
**Tests DR** : Mensuel (restore backup sur environnement staging)
@@ -272,7 +197,7 @@ wal-e backup-push /var/lib/postgresql/data
### API Down (5xx errors spike)
1. **Vérifier** : Grafana dashboard → onglet Errors
-2. **Logs** : Loki query `{app="roadwave-api"} |= "error"`
+2. **Logs** : Requête Loki filtrée sur app roadwave-api + niveau error
3. **Actions** :
- Si OOM : restart container + augmenter RAM
- Si DB connexions saturées : vérifier slow queries
@@ -282,7 +207,7 @@ wal-e backup-push /var/lib/postgresql/data
### Database Slow Queries
1. **Identifier** : Grafana → PostgreSQL dashboard → Top slow queries
-2. **Analyser** : `EXPLAIN ANALYZE` sur query problématique
+2. **Analyser** : Utiliser EXPLAIN ANALYZE sur query problématique
3. **Actions** :
- Index manquant : créer index (migration rapide)
- Lock contention : identifier transaction longue et kill si bloquante
@@ -291,7 +216,7 @@ wal-e backup-push /var/lib/postgresql/data
### High Load (CPU >80%)
1. **Vérifier** : Grafana → Node Exporter → CPU usage
-2. **Top processus** : `htop` ou `docker stats`
+2. **Top processus** : Consulter htop ou docker stats
3. **Actions** :
- Si Whisper (modération) : réduire concurrence workers
- Si API : scale horizontal (ajouter instance)
diff --git a/docs/adr/025-securite-secrets.md b/docs/adr/025-securite-secrets.md
index f0b25d1..d3360de 100644
--- a/docs/adr/025-securite-secrets.md
+++ b/docs/adr/025-securite-secrets.md
@@ -30,184 +30,62 @@ Stratégie **secrets management + encryption at rest + HTTPS** avec stack self-h
### Architecture Secrets
-```mermaid
-graph TB
- subgraph Dev["Environnement Dev"]
- EnvFile[".env file
(local uniquement)"]
- end
+**Environnements** :
+- **Développement** : Fichier .env local (non versionné)
+- **Production** : HashiCorp Vault (self-hosted)
- subgraph Prod["Production"]
- Vault["HashiCorp Vault
(secrets storage)"]
- API["Backend Go API"]
- DB["PostgreSQL
(encrypted at rest)"]
- Redis["Redis
(TLS enabled)"]
- end
-
- subgraph Encryption["Encryption Layer"]
- AES["AES-256-GCM
(PII encryption)"]
- TLS["TLS 1.3
(transport)"]
- end
-
- subgraph Secrets["Secrets Stockés"]
- JWT["JWT Signing Key
(RS256 private key)"]
- DBCreds["DB Credentials
(user/pass)"]
- Mangopay["Mangopay API Key
(sandbox + prod)"]
- EncKey["Encryption Master Key
(AES-256)"]
- end
-
- EnvFile -.->|dev only| API
- Vault --> API
-
- Vault --- JWT
- Vault --- DBCreds
- Vault --- Mangopay
- Vault --- EncKey
-
- API --> AES
- API --> TLS
- AES --> DB
- TLS --> DB
- TLS --> Redis
-
- classDef devStyle fill:#fff3e0,stroke:#e65100
- classDef prodStyle fill:#e3f2fd,stroke:#1565c0
- classDef encStyle fill:#f3e5f5,stroke:#6a1b9a
- classDef secretStyle fill:#ffebee,stroke:#c62828
-
- class Dev,EnvFile devStyle
- class Prod,Vault,API,DB,Redis prodStyle
- class Encryption,AES,TLS encStyle
- class Secrets,JWT,DBCreds,Mangopay,EncKey secretStyle
-```
+**Flux** :
+1. **Vault** stocke secrets sensibles (JWT signing key, DB credentials, Mangopay API key, encryption master key)
+2. **Backend API** récupère secrets depuis Vault au démarrage
+3. **Encryption layer** : AES-256-GCM pour PII, TLS 1.3 pour transport
+4. **Stockage** : PostgreSQL (data encrypted at rest), Redis (TLS enabled)
### Secrets Management avec Vault
**Initialisation Vault** (one-time setup) :
-```bash
-# 1. Init Vault (génère unseal keys + root token)
-vault operator init -key-shares=5 -key-threshold=3
+1. Init Vault : génération 5 unseal keys + root token (Shamir secret sharing)
+2. Unseal : 3 clés parmi 5 requises pour déverrouiller Vault
+3. Login root + activation KV-v2 engine (path : `roadwave/`)
-# 2. Unseal (3 clés requises parmi 5)
-vault operator unseal
-vault operator unseal
-vault operator unseal
+**Secrets stockés** :
+- **JWT signing key** : Paire RS256 privée/publique
+- **Database credentials** : Host, port, user, password (généré aléatoire 32 caractères)
+- **Mangopay API** : Client ID, API key, webhook secret
-# 3. Login root + création secrets
-vault login
-vault secrets enable -path=roadwave kv-v2
-```
-
-**Stockage secrets** :
-```bash
-# JWT signing key (RS256 private key)
-vault kv put roadwave/jwt private_key=@jwt-private.pem public_key=@jwt-public.pem
-
-# Database credentials
-vault kv put roadwave/database \
- host=localhost \
- port=5432 \
- user=roadwave \
- password=
-
-# Mangopay API
-vault kv put roadwave/mangopay \
- client_id= \
- api_key= \
- webhook_secret=
-```
-
-**Récupération depuis Go** :
-```go
-import vault "github.com/hashicorp/vault/api"
-
-client, _ := vault.NewClient(&vault.Config{
- Address: "http://vault:8200",
-})
-client.SetToken(os.Getenv("VAULT_TOKEN"))
-
-secret, _ := client.KVv2("roadwave").Get(context.Background(), "database")
-dbPassword := secret.Data["password"].(string)
-```
+**Récupération depuis Backend Go** :
+- Utilisation SDK `hashicorp/vault/api`
+- Authentification via token Vault (variable env VAULT_TOKEN)
+- Récupération secrets via KVv2 engine
### Encryption PII (Field-level)
**Données chiffrées** (AES-256-GCM) :
-- **GPS précis** : lat/lon (24h), puis geohash-5 seulement ([Règle 02](../regles-metier/02-conformite-rgpd.md))
-- **Email** : chiffré en base, déchiffré à l'envoi
+- **GPS précis** : lat/lon conservés 24h puis réduits à geohash-5 (~5km²) ([Règle 02](../regles-metier/02-conformite-rgpd.md))
+- **Email** : chiffré en base, déchiffré uniquement à l'envoi
- **Numéro téléphone** : si ajouté (Phase 2)
**Architecture encryption** :
-```go
-type Encryptor struct {
- masterKey []byte // 256 bits (32 bytes) depuis Vault
-}
+- Utilisation bibliothèque standard Go `crypto/aes` avec mode GCM (AEAD)
+- Master key 256 bits (32 bytes) récupérée depuis Vault
+- Chiffrement : génération nonce aléatoire + seal GCM → encodage base64
+- Stockage : colonne `email_encrypted` en base PostgreSQL
-func (e *Encryptor) Encrypt(plaintext string) (string, error) {
- block, _ := aes.NewCipher(e.masterKey)
- gcm, _ := cipher.NewGCM(block)
-
- nonce := make([]byte, gcm.NonceSize())
- rand.Read(nonce)
-
- ciphertext := gcm.Seal(nonce, nonce, []byte(plaintext), nil)
- return base64.StdEncoding.EncodeToString(ciphertext), nil
-}
-
-// Usage
-email := "user@example.com"
-encryptedEmail, _ := encryptor.Encrypt(email)
-// Store in DB: "Ae3xK9... (base64 ciphertext)"
-```
-
-**Schema PostgreSQL** :
-```sql
-CREATE TABLE users (
- id UUID PRIMARY KEY,
- email_encrypted TEXT NOT NULL, -- AES-256-GCM chiffré
- created_at TIMESTAMPTZ NOT NULL
-);
-
--- Index sur email chiffré IMPOSSIBLE → utiliser hash pour recherche
-CREATE INDEX idx_email_hash ON users(sha256(email_encrypted));
-```
+**Contraintes** :
+- Index direct sur champ chiffré impossible
+- Solution : index sur hash SHA-256 de l'email chiffré pour recherche
### HTTPS/TLS Configuration
**Let's Encrypt wildcard certificate** :
-```bash
-# Certbot avec DNS challenge (OVH API)
-certbot certonly \
- --dns-ovh \
- --dns-ovh-credentials ~/.secrets/ovh.ini \
- -d roadwave.fr \
- -d *.roadwave.fr
+- Méthode : Certbot avec DNS-01 challenge (API OVH)
+- Domaines couverts : `roadwave.fr` + `*.roadwave.fr` (wildcard)
+- Renouvellement : automatique via cron quotidien (30j avant expiration)
-# Renouvellement auto (cron)
-0 0 * * * certbot renew --post-hook "systemctl reload nginx"
-```
-
-**Nginx TLS config** :
-```nginx
-server {
- listen 443 ssl http2;
- server_name api.roadwave.fr;
-
- ssl_certificate /etc/letsencrypt/live/roadwave.fr/fullchain.pem;
- ssl_certificate_key /etc/letsencrypt/live/roadwave.fr/privkey.pem;
-
- # TLS 1.3 uniquement
- ssl_protocols TLSv1.3;
- ssl_ciphers 'TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384';
-
- # HSTS (force HTTPS)
- add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
-
- # Security headers
- add_header X-Frame-Options "DENY" always;
- add_header X-Content-Type-Options "nosniff" always;
- add_header Referrer-Policy "strict-origin-when-cross-origin" always;
-}
-```
+**Nginx TLS configuration** :
+- Protocol : TLS 1.3 uniquement (pas de TLS 1.2 ou inférieur)
+- Ciphers : TLS_AES_128_GCM_SHA256, TLS_AES_256_GCM_SHA384
+- HSTS : max-age 1 an, includeSubDomains
+- Security headers : X-Frame-Options DENY, X-Content-Type-Options nosniff, Referrer-Policy strict-origin-when-cross-origin
## Alternatives considérées
@@ -295,24 +173,11 @@ server {
### Rate Limiting (Protection DDoS/Brute-force)
-**Configuration Fiber** :
-```go
-import "github.com/gofiber/fiber/v3/middleware/limiter"
-
-app.Use(limiter.New(limiter.Config{
- Max: 100, // 100 requêtes
- Expiration: 1 * time.Minute, // par minute
- Storage: redisStorage, // Redis backend
- KeyGenerator: func(c fiber.Ctx) string {
- return c.IP() // Par IP
- },
- LimitReached: func(c fiber.Ctx) error {
- return c.Status(429).JSON(fiber.Map{
- "error": "Too many requests",
- })
- },
-}))
-```
+**Configuration** :
+- Middleware Fiber `limiter` avec backend Redis
+- Limite : 100 requêtes par minute par IP (global)
+- Clé de limitation : adresse IP client
+- Réponse limitation : HTTP 429 "Too many requests"
**Rate limits par endpoint** :
- `/auth/login` : 5 req/min/IP (protection brute-force)
@@ -330,14 +195,11 @@ app.Use(limiter.New(limiter.Config{
| **Mangopay API key** | À la demande | Rotation manuelle si compromission |
| **Encryption master key** | Jamais (re-encryption massive) | Backup sécurisé uniquement |
-**Process rotation DB credentials (Vault)** :
-```bash
-# Vault génère nouveau password + update PostgreSQL
-vault write database/rotate-root/roadwave
-
-# Application récupère nouveau password automatiquement
-# Ancien password invalide après 1h grace period
-```
+**Process rotation DB credentials** :
+- Vault génère automatiquement nouveau password
+- Vault met à jour PostgreSQL avec nouveau password
+- Application récupère nouveau password au prochain accès Vault
+- Ancien password invalide après grace period de 1h
## Métriques de Succès