> ## Documentation Index
> Fetch the complete documentation index at: https://api-documentation.kare-app.fr/llms.txt
> Use this file to discover all available pages before exploring further.

# Infrastructure

> OpenTofu sur Scaleway, workflow de plan revu, bootstrap d'un stage

## Périmètre

`infrastructure/` gère **staging et rien d'autre**. Aucune commande n'accepte d'argument de stage, il
n'y a pas de racine prod ni test. Un futur stage prod clonera la composition staging finie avec son
propre state, ses credentials, ses domaines, son sizing et sa propriété des secrets.

```text theme={null}
infrastructure/
├── bootstrap/          # state/IAM, contrôles de bucket, seed de la première image
├── envs/staging/       # seule racine OpenTofu ; topologie non-secrète figée
├── modules/stage/      # racine de composition réutilisable
├── modules/            # modules Scaleway/AWS ciblés
├── scripts/            # opérations day-2
└── Makefile            # workflow staging à plan revu
```

| Module                   | Rôle                                                      |
| ------------------------ | --------------------------------------------------------- |
| `network`                | VPC / réseau privé (isole base et cache)                  |
| `postgres` / `redis`     | base managée / cache managé                               |
| `object-storage`         | buckets `uploads`, `exports`, `assets`                    |
| `container-registry`     | namespace registry privé                                  |
| `serverless-container`   | containers API et fronts, paramétrés par `kind`           |
| `dns`                    | domaines Scaleway + enregistrements Route 53 optionnels   |
| `secrets` / `iam` / `ci` | Secret Manager, identités runtime / CI / TEM              |
| `transactional-email`    | domaine d'envoi TEM (SPF, DKIM, MX, DMARC)                |
| `observability`          | token d'écriture métriques Cockpit et sync de data source |

OpenTofu possède le VPC, Postgres, Redis, les buckets, le registry, l'IAM, les entrées Secret
Manager, le domaine TEM, les containers, les domaines custom, le token Cockpit et les
enregistrements Route 53 optionnels. **La CI possède l'image** : les ressources container portent
`ignore_changes = [image]`.

<Note>
  L'offre payante TEM (Essential ou Scale) est un prérequis **externe** au niveau projet : le provider
  ne l'expose qu'en data source. Elle survit à un destroy de la stack applicative.
</Note>

## Contrat de sûreté

<Warning>
  `make apply` ne consomme **que** le plan sauvegardé et revu produit par `make plan`. Jamais un plan
  implicite.
</Warning>

* Ne jamais lire, afficher, éditer ou commiter `.envrc`, `terraform.tfvars`, `.terraform/`, le state,
  un plan sauvegardé, ou une valeur de secret.
* Les plans sauvegardés contiennent des secrets en clair : `make clean-plans` après usage.
* Ne jamais appliquer si le chiffrement du state n'est pas vérifié. Les applies foundation, core,
  normal et destroy passent tous par le gate lecture-seule versioning/SSE du bucket de state.
* Postgres n'est public que pour la CIDR fixe du migration runner. `0.0.0.0/0` et `::/0` sont rejetés.
* Route 53 utilise `TF_VAR_route53_aws_access_key` / `TF_VAR_route53_aws_secret_key`. Les `AWS_*`
  ambiants appartiennent au backend S3-compatible du state et ne doivent **jamais** être réutilisés
  pour Route 53.
* Rotation de credential : ajouter une génération dans la map retenue, la sélectionner comme active,
  appliquer, basculer et vérifier les consommateurs, puis supprimer l'ancienne dans un apply revu
  séparé. Jamais `tofu taint`.
* DNS/TLS, activation de l'utilisateur runtime de la base, création de ressource payante, opération de
  state, révocation de credential et suppression de ressource exigent chacun leur propre approbation
  humaine.

## Workflow quotidien

```bash theme={null}
cd infrastructure
make bootstrap-stage-status     # audit de l'identité IaC
make bootstrap-state-status     # depuis le contexte admin qui possède kare-state
make storage-status             # audit versioning / SSE
make init
make plan
make show-plan                  # relire le plan exact que apply consommera
make apply
make sync-secrets               # pousse la clé CI active vers l'Environment GitHub staging
make credential-status          # métadonnées d'expiration, non-secrètes
```

`make help` liste tout. Autres cibles utiles : `make fmt`, `make fmt-check`, `make validate`,
`make verify-stage` (readiness, isolation CORS par audience, frontière de stockage, spoofing de
rate-limit).

## Credentials locaux

Deux paires distinctes, chargées par **direnv** depuis un `.envrc` ignoré par git :

| Variables              | Rôle                                                |
| ---------------------- | --------------------------------------------------- |
| `SCW_*`                | provider Scaleway, projet `kare-staging`            |
| `AWS_*`                | backend S3-compatible du state, projet `kare-state` |
| `TF_VAR_route53_aws_*` | zone Route 53, clé AWS réelle et séparée            |

Le projet `kare-state` est volontairement isolé du projet applicatif : le bucket
`kare-staging-opentofu-state` et son historique survivent à la destruction de la stack.

## Bootstrap d'un stage neuf

<Warning>
  Séquence gardée : un container ne peut pas être créé avant que son image `staging-latest` réelle
  existe, et le DNS externe doit pointer sur les nouveaux endpoints avant l'enregistrement TLS. Pas
  d'image placeholder.
</Warning>

<Steps>
  <Step title="Identités">
    `make bootstrap-stage-apply` dans l'organisation staging, `make bootstrap-state-apply` dans celle
    qui contient `kare-state`. Fusionner les snippets de credentials générés dans le `.envrc` ignoré,
    compléter les entrées staging, puis `make init`.
  </Step>

  <Step title="Foundation : le registry seul">
    `make foundation-plan` → `make foundation-show` → `make foundation-apply`.
  </Step>

  <Step title="Seed des images">
    Depuis un checkout propre et revu, avec `HEROUI_AUTH_TOKEN` et le
    `NEXT_SERVER_ACTIONS_ENCRYPTION_KEY` de l'environment : `make seed-images`. Il construit et pousse
    des images boot-only API, Backoffice, Audit et Kare pour le SHA exact. Il crée un alias
    `staging-latest` manquant, mais ne déplace jamais un alias existant.
  </Step>

  <Step title="TEM">
    Confirmer qu'une offre Scaleway TEM Essential ou Scale est active sur le projet du stage.
  </Step>

  <Step title="Core : la stack sans domaines">
    `make core-plan` → `make core-show` → `make core-apply`. Crée la stack et le domaine TEM sans
    domaines custom ni validation TEM, parce que le DNS manuel survivant pointe encore sur l'ancienne stack.
  </Step>

  <Step title="DNS, à la main">
    Lire les outputs `api_cname_records`, `frontend_cname_records`, `tem_dns_records`. Le propriétaire
    DNS met à jour exactement les six CNAME staging et crée les enregistrements SPF, DKIM, MX et DMARC
    dans Route 53. Vérifier la propagation publique de chaque valeur émise.
  </Step>

  <Step title="Plan complet">
    `make plan` → `make show-plan` → `make apply`. Avec le DNS déjà correct, ce plan crée les six
    domaines custom Scaleway et leurs certificats TLS, et valide le domaine d'envoi TEM.
  </Step>

  <Step title="Chiffrement des buckets">
    Avec approbation séparée : `make storage-apply`, puis `make storage-status` pour prouver
    versioning et SSE-ONE sur les buckets applicatifs neufs.
  </Step>

  <Step title="GitHub + premier déploiement">
    `make sync-secrets`, configurer l'Environment GitHub `staging`, puis lancer un déploiement `all`.
  </Step>

  <Step title="Premier opérateur">
    Sur la base vide : `make bootstrap-bo-admin EMAIL=<operateur>@akord-securite.fr`. Le helper lit
    l'URL publique de la base et le CA directement depuis Scaleway, jamais un `.env` applicatif, et
    affiche le mot de passe initial une seule fois en local.
  </Step>

  <Step title="Données réelles">
    Se connecter au Backoffice, créer l'organisation et l'utilisateur staging par le flux produit,
    synchroniser les credentials E2E dédiés. `STAGING_AUTO_DEPLOY_ENABLED` reste `false` jusqu'à ce
    que tout le flux soit sain.
  </Step>
</Steps>

## Topologie staging actuelle

| Ressource    | Valeur                                                        |
| ------------ | ------------------------------------------------------------- |
| Région       | `fr-par`                                                      |
| Postgres     | `DB-PLAY2-PICO`, 10 GB, pas de HA, accès public restreint     |
| Redis        | `RED1-MICRO`, 1 nœud                                          |
| API          | port `3000`, `min_scale=0`, `max_scale=3`, 1024 MB, 500 mvCPU |
| Registry     | `rg.fr-par.scw.cloud/kare-staging`                            |
| Domaine TEM  | `mail.staging.kare-app.fr`                                    |
| Mail entrant | `inbound.staging.kare-app.fr`                                 |

<Note>
  512 MB de RAM sur l'API meurt en « heap out of memory » au démarrage (416 MB RSS au repos) : ne
  descends pas la limite.
</Note>

## Destruction

Il n'y a pas de commande de destroy directe.

```mermaid theme={null}
flowchart LR
    U["destroy-unlock-plan → show → apply<br/>retire les gardes"] --> P["destroy-plan → destroy-show"]
    P --> A["destroy-apply<br/>phrase de confirmation exacte"]
```

« Repartir de zéro » détruit la stack applicative gérée, **pas** son plan de contrôle : le projet
Scaleway staging, le projet `kare-state`, le bucket de state et son historique, les identités de
bootstrap, la zone et les enregistrements Route 53, les environments GitHub et les projets Sentry
sont préservés.

Avant destruction : `STAGING_AUTO_DEPLOY_ENABLED=false`, `make backup-postgres BACKUP_DIR=<chemin sûr>`
avec dump et checksum conservés hors repo, inventaire des objets retenus et des images du registry.
Redis est éphémère et n'est pas une source de sauvegarde. `make destroy-data-status` puis, seulement
après une approbation de perte de données séparée, `make destroy-data-purge` — sa liste blanche codée
en dur ne peut toucher que `uploads`, `exports` et `assets`, jamais le bucket de state.

<Warning>
  Deux hostnames legacy sont attachés hors OpenTofu. Supprimer les containers Backoffice / Audit peut
  retirer ou invalider ces bindings même si OpenTofu ne les liste pas : à traiter explicitement dans
  l'approbation destructive.
</Warning>
