From 81ccbf79e61cf0e1631ee643ea2a0b7ec9c8081b Mon Sep 17 00:00:00 2001 From: jpgiannetti Date: Sun, 1 Feb 2026 17:12:07 +0100 Subject: [PATCH] refactor(adr-023/024/025): retirer exemples de code et scripts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Suppression de tous les exemples de code pour garder uniquement les descriptions techniques : ADR-023 (Architecture Modération) : - Diagramme Mermaid → description flux textuelle - Exemples SQL/Redis → description workflow - Interface Go → description abstraction - Dépendances → liste concise ADR-024 (Monitoring et Observabilité) : - Diagramme Mermaid → architecture textuelle - Exemples PromQL → description métriques - Config YAML alertes → liste alertes avec seuils - Commandes bash WAL-E → description backup - Runbooks → étapes sans commandes ADR-025 (Sécurité et Secrets) : - Diagramme Mermaid → flux secrets textuel - Commandes bash Vault → description process - Code Go encryption → architecture encryption - Schéma SQL → contraintes textuelles - Config Nginx → configuration TLS - Code Go rate limiting → paramètres middleware ADR restent 100% techniques et complets sans code concret. Cohérence avec ADR-022 (même approche). Co-Authored-By: Claude Sonnet 4.5 --- docs/adr/023-architecture-moderation.md | 119 +++--------- docs/adr/024-monitoring-observabilite.md | 163 +++++----------- docs/adr/025-securite-secrets.md | 230 +++++------------------ 3 files changed, 120 insertions(+), 392 deletions(-) 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