# 01 — Architecture de production sur Hetzner

---

## Vue d'ensemble

```
┌─────────────────────────────────────────────────────────────────────────┐
│                           INTERNET                                       │
└───────────────────────────────┬─────────────────────────────────────────┘
                                │
                    ┌───────────▼───────────┐
                    │   Cloudflare          │
                    │   WAF + CDN + DNS     │
                    │   TLS termination     │
                    └───────────┬───────────┘
                                │ HTTPS (443)
                    ┌───────────▼───────────┐
                    │  Hetzner Load         │
                    │  Balancer (LB11)      │
                    │  €7.49/mois           │
                    └─────┬─────────┬───────┘
                          │         │
           ┌──────────────▼──┐  ┌───▼──────────────┐
           │  Worker Node 1  │  │  Worker Node 2   │
           │  CX42 (8c/16G)  │  │  CX42 (8c/16G)  │
           │  €21.49/mois    │  │  €21.49/mois     │
           │                 │  │                  │
           │ ┌─────────────┐ │  │ ┌─────────────┐ │
           │ │ NestJS Pod  │ │  │ │ NestJS Pod  │ │
           │ │ (backend)   │ │  │ │ (backend)   │ │
           │ └─────────────┘ │  │ └─────────────┘ │
           │ ┌─────────────┐ │  │ ┌─────────────┐ │
           │ │ Next.js Pod │ │  │ │ Next.js Pod │ │
           │ │ (frontend)  │ │  │ │ (frontend)  │ │
           │ └─────────────┘ │  │ └─────────────┘ │
           └────────┬────────┘  └────────┬─────────┘
                    │  Réseau privé Hetzner (10.0.0.0/16)
           ┌────────▼────────────────────▼─────────┐
           │         Control Plane Node             │
           │         CX22 (2c/4G) €5.11/mois       │
           │         k3s server (etcd)              │
           └────────────────────────────────────────┘
                                │
        ┌───────────────────────┼───────────────────────┐
        │                       │                       │
┌───────▼───────┐   ┌───────────▼──────────┐   ┌───────▼───────┐
│  PostgreSQL   │   │       Redis           │   │  Hetzner      │
│  Neon.tech    │   │  Upstash (serverless) │   │  Object       │
│  ou self-host │   │  ou self-hosted       │   │  Storage      │
│  $19/mois     │   │  $0-10/mois          │   │  S3-compat.   │
│               │   │                      │   │  €4.99/mois   │
└───────────────┘   └──────────────────────┘   └───────────────┘
```

---

## Composants et rôles

### Couche réseau

| Composant | Rôle | Port |
|---|---|---|
| **Cloudflare** | DNS, WAF, CDN, protection DDoS, cache assets | 443 (HTTPS) |
| **Hetzner Load Balancer** | Distribution trafic entre workers, sticky sessions WebSocket | 80, 443 |
| **Réseau privé Hetzner** | Communication inter-nœuds (10.0.0.0/16), base de données | Interne |

### Couche Kubernetes (k3s)

| Composant | Rôle | Ressource |
|---|---|---|
| **Control Plane (CX22)** | etcd, kube-apiserver, scheduler, controller-manager | 2 vCPU / 4 GB |
| **Worker Nodes (CX42)** | Exécution des pods NestJS + Next.js | 8 vCPU / 16 GB chacun |
| **Traefik Ingress** | Routage HTTP/HTTPS, sticky sessions Socket.IO | Built-in k3s |
| **cert-manager** | TLS automatique via Let's Encrypt | In-cluster |
| **Hetzner Cloud Controller** | Provisionne LB + volumes depuis Kubernetes | In-cluster |
| **Hetzner CSI Driver** | PersistentVolumes sur Hetzner Volumes | In-cluster |

### Couche application

| Pod | Image | Replicas | Ports |
|---|---|---|---|
| **xpeditis-backend** | `ghcr.io/<org>/xpeditis-backend:latest` | 2–15 | 4000 |
| **xpeditis-frontend** | `ghcr.io/<org>/xpeditis-frontend:latest` | 1–8 | 3000 |

### Couche données

| Service | Option MVP | Option Production | Protocole |
|---|---|---|---|
| **PostgreSQL 15** | Neon.tech Pro ($19/mois) | Self-hosted sur CX32 | 5432 |
| **Redis 7** | Upstash free ($0-10/mois) | Self-hosted StatefulSet | 6379 |
| **Stockage fichiers** | Hetzner Object Storage (€4.99/mois) | Idem (scale automatique) | HTTPS/S3 API |

---

## Flux réseau détaillé

### Requête API standard (rate search)

```
Client Browser
    │ HTTPS
    ▼
Cloudflare (cache miss → forward)
    │ HTTPS, header CF-Connecting-IP
    ▼
Hetzner Load Balancer :443
    │ HTTP (TLS terminé par Cloudflare ou cert-manager)
    ▼
Traefik Ingress (api.xpeditis.com)
    │ HTTP :80 interne
    ▼
NestJS Pod (port 4000)
    ├── Redis (cache rate:FSN:HAM:20ft) → HIT → retour direct
    └── MISS → 5× appels APIs carriers (Maersk/MSC/etc.)
              └── Réponse → Store Redis TTL 15min
                         └── Réponse client
```

### Connexion WebSocket (notifications temps réel)

```
Client Browser
    │ wss:// upgrade
    ▼
Cloudflare (WebSocket proxy activé)
    │
    ▼
Hetzner LB (sticky session cookie activé)
    │ Même backend pod à chaque reconnexion
    ▼
Traefik (annotation sticky cookie)
    │
    ▼
NestJS Pod /notifications namespace (Socket.IO)
    ├── Auth: JWT validation on connect
    ├── Join room: user:{userId}
    └── Redis pub/sub → broadcast cross-pods
```

### Upload de document (carrier portal)

```
Carrier Browser
    │
    ▼
NestJS POST /api/v1/csv-bookings/{id}/documents
    │ Validation: type (PDF/XLS/IMG), taille max 10 MB
    ▼
S3StorageAdapter.upload()
    │ AWS SDK v3, forcePathStyle: true
    ▼
Hetzner Object Storage
    │ Endpoint: https://fsn1.your-objectstorage.com
    └── Stocké: xpeditis-docs/{orgId}/{bookingId}/{filename}
```

---

## Ports et protocoles

### Ports externes (ouverts sur Hetzner Firewall)

| Port | Protocole | Source | Destination | Usage |
|---|---|---|---|---|
| 22 | TCP | Votre IP uniquement | Tous nœuds | SSH administration |
| 80 | TCP | 0.0.0.0/0 | LB | Redirection HTTP → HTTPS |
| 443 | TCP | 0.0.0.0/0 | LB | HTTPS + WebSocket |
| 6443 | TCP | Votre IP + workers | Control plane | Kubernetes API |

### Ports internes (réseau privé 10.0.0.0/16 uniquement)

| Port | Protocole | Usage |
|---|---|---|
| 5432 | TCP | PostgreSQL (si self-hosted) |
| 6379 | TCP | Redis (si self-hosted) |
| 4000 | TCP | NestJS API (pod → pod) |
| 3000 | TCP | Next.js (pod → pod) |
| 10250 | TCP | kubelet API |
| 2379-2380 | TCP | etcd (control plane) |

---

## Namespaces Kubernetes

```
cluster
├── xpeditis-prod          # Application principale
│   ├── Deployments: backend, frontend
│   ├── Services: backend-svc, frontend-svc
│   ├── ConfigMaps: backend-config, frontend-config
│   ├── Secrets: backend-secrets, frontend-secrets
│   ├── HPA: backend-hpa, frontend-hpa
│   └── Ingress: xpeditis-ingress
│
├── cert-manager           # Gestion certificats TLS
│   └── ClusterIssuer: letsencrypt-prod, letsencrypt-staging
│
├── monitoring             # Observabilité
│   ├── Prometheus
│   ├── Grafana
│   └── Loki
│
└── kube-system            # Système k3s
    ├── Traefik (Ingress Controller)
    ├── Hetzner Cloud Controller Manager
    └── Hetzner CSI Driver
```

---

## Pourquoi k3s plutôt que k8s complet

| Critère | k3s (choisi) | k8s complet |
|---|---|---|
| **RAM control plane** | 512 MB | 2-4 GB |
| **CPU control plane** | 1 vCPU | 2-4 vCPU → serveur plus cher |
| **Temps install** | 5 min (hetzner-k3s) | 30-60 min |
| **Maintenance** | System Upgrade Controller inclus | Manuelle |
| **Compatibilité** | 100% compatible kubectl/helm | — |
| **Traefik** | Inclus par défaut | Installation séparée |
| **Coût** | CX22 (€5.11/mois) comme control plane | Minimum CX42 (€21.49) |
| **Production** | Oui (utilisé par des milliers de startups) | Oui |

---

## Stratégie de scaling

### Horizontal Pod Autoscaler (HPA)

```
Métriques surveillées :
- CPU > 70% → Scale up
- CPU < 30% (5 min) → Scale down
- Mémoire > 80% → Scale up (custom metric)

Backend : min 2 → max 15 pods
Frontend : min 1 → max 8 pods
```

### Cluster Autoscaler

```
Worker nodes : min 2 → max 8
Déclenché par : pods en état "Pending" (pas assez de ressources)
Délai scale-down : 10 min d'utilisation < 50%
```

---

## Décisions d'architecture

### Pourquoi Hetzner Object Storage plutôt que MinIO self-hosted

Le code utilise déjà `AWS SDK v3` avec `forcePathStyle: true` et un endpoint configurable. Hetzner Object Storage est 100% compatible S3 → **zéro modification de code**, juste les variables d'environnement :

```bash
# Avant (MinIO local)
AWS_S3_ENDPOINT=http://localhost:9000

# Après (Hetzner Object Storage)
AWS_S3_ENDPOINT=https://fsn1.your-objectstorage.com
AWS_ACCESS_KEY_ID=<hetzner_access_key>
AWS_SECRET_ACCESS_KEY=<hetzner_secret_key>
AWS_REGION=eu-central-1
AWS_S3_BUCKET=xpeditis-prod
```

### Pourquoi Neon.tech pour PostgreSQL (MVP)

- PostgreSQL 15 managé, compatible TypeORM
- Extensions `uuid-ossp` et `pg_trgm` disponibles (requis par Xpeditis)
- Backups automatiques inclus
- Connection pooling built-in (via PgBouncer)
- Pas de gestion de HA à faire manuellement
- Free tier pour le dev, $19/mois pour la prod
- Migration vers self-hosted possible à tout moment

### Pourquoi Cloudflare devant Hetzner LB

- CDN mondial (cache des assets Next.js)
- Protection DDoS free
- WAF avec règles OWASP
- DNS avec failover automatique
- Certificats TLS optionnels (on peut laisser cert-manager gérer le TLS)
- Cache des PDFs générés → économise les appels S3
