### **7. Distribution, Cache et Environnements**

#### **7.1. Noms de domaine et distribution du SDK**

Le produit s'appuie sur le domaine `circuy.net`, réparti en deux hôtes selon la seule distinction qui compte ici, celle du cachable et du non-cachable :

| Hôte | Contenu | Cache |
| --- | --- | --- |
| `cdn.circuy.net` | Script du SDK, manifestes, versions publiées | Oui |
| `app.circuy.net` | Dashboard, API d'administration, flux d'authentification | Jamais |

Cette séparation n'est pas cosmétique : elle permet d'intercaler un jour un cache en périphérie devant `cdn.circuy.net` sans toucher ni à l'intégration déjà déployée chez les clients, ni à la surface d'administration (ADR-11).

Le script est servi à une adresse stable, que les clients inscrivent une fois pour toutes dans leurs pages :

```
https://cdn.circuy.net/v1/sdk.js
```

Cette URL ne peut pas être versionnée finement, puisque le client ne modifiera pas son intégration à chaque correctif. Elle est donc mise en cache brièvement, avec réutilisation du contenu périmé pendant le rafraîchissement :

```
Cache-Control: public, max-age=600, stale-while-revalidate=86400
```

Les visiteurs reçoivent toujours une réponse immédiate depuis le cache, tandis que la mise à jour se propage en arrière-plan. Un correctif atteint l'ensemble du parc en quelques minutes, sans qu'aucune requête n'attende jamais l'origine.

**Compatibilité ascendante.** Le chiffre du chemin `/v1/` ne change que sur rupture. Trois règles la garantissent :

1. Les évolutions du contrat n'ajoutent que des champs facultatifs (section 5.2).
2. Un champ inconnu est ignoré silencieusement.
3. Un type d'étape inconnu fait sauter l'étape, jamais échouer le parcours.

Un SDK resté longtemps dans un cache navigateur reste ainsi capable d'exécuter un parcours publié récemment, en ignorant simplement ce qu'il ne sait pas rendre.

#### **7.2. Distribution des parcours**

C'est la mise en œuvre de l'ADR-03 : deux natures de documents, deux politiques de cache opposées.

| Document | Cache-Control | Justification |
| --- | --- | --- |
| `manifest.json` | `public, max-age=60, stale-while-revalidate=600` | Petit, change à chaque publication |
| `tours/{tourId}/{version}.json` | `public, max-age=31536000, immutable` | Ne change jamais, par construction |

Le manifeste énumère les parcours actifs avec leur déclencheur et leur version courante. Il pèse quelques kilooctets et constitue le seul document dont la fraîcheur importe. Les contenus, eux, vivent à des adresses qui n'existaient pas avant leur publication : ils sont mis en cache pour un an sans le moindre risque d'obsolescence.

```mermaid
flowchart LR
    Pub[Publication] -->|cree| V5[tours/tr_7f3a9c/5.json]
    Pub -->|met a jour| M[manifest.json]
    M -->|cache 60 s| SDK[SDK]
    V5 -->|cache 1 an immutable| SDK
```

**Délai de propagation, engagement contractuel.** L'absence de purge (ADR-03) se paie par un délai, qui doit être annoncé plutôt que subi :

| Situation | Délai avant qu'une publication soit visible |
| --- | --- |
| Site à trafic régulier | 60 secondes |
| Visiteur isolé sur un manifeste périmé | jusqu'à 10 minutes, son passage déclenchant le rafraîchissement |
| Nouveau visiteur, cache vide | immédiat |

Ce délai remplace l'instantanéité annoncée par le cahier fonctionnel. Il est borné et connu, là où l'échec d'un appel de purge produirait une incohérence non bornée et silencieuse.

**Il est précisé à trois endroits, et cette obligation fait partie de la définition de terminé du lot 2 :** dans la documentation d'intégration remise aux clients, dans les conditions de service au titre de la publication, et dans l'interface du dashboard au moment même de la publication (section 6.4). Un administrateur qui publie doit savoir, à la seconde où il clique, quand ses utilisateurs verront le changement.

L'extension, elle, interroge l'API d'administration sans cache : un administrateur qui publie voit immédiatement le résultat dans son outil, et ce sont ses utilisateurs finaux qui bénéficient du cache.

#### **7.3. En-têtes et clés de cache**

Le manifeste porte un `ETag` calculé sur son contenu, ce qui rend la revalidation quasi gratuite : la grande majorité des vérifications se soldent par une réponse 304 sans corps.

**Les lectures publiques répondent `Access-Control-Allow-Origin: *`, sans identifiants.** Ce point mérite une clarification, car le cahier fonctionnel présente la restriction par domaine comme le mécanisme de sécurité de la clé publique. Ce n'est pas le cas : le contrôle d'origine est appliqué par le navigateur, pas par le serveur, et n'empêche donc nullement la lecture de ces documents par un outil en ligne de commande. Renvoyer une origine spécifique imposerait par ailleurs un `Vary: Origin` qui fragmenterait le cache autant qu'il y a de clients.

La restriction par domaine conserve son rôle, mais là où il est réel : sur l'**écriture** d'événements de télémétrie (section 5.5), où elle empêche qu'une clé publique récupérée sur un site serve à polluer les statistiques depuis un autre. Les documents de lecture, eux, sont traités pour ce qu'ils sont : du contenu public, dont la protection ne doit jamais reposer sur l'obscurité (section 8.2).

#### **7.4. Hébergement souverain**

**Hébergeur retenu : Greenshift**, prestataire déjà en relation avec l'équipe. Le critère décisif est celui de la doctrine : ce qui est déjà exploité l'emporte sur ce qui serait comparé sur fiche technique. Greenshift fournit des serveurs managés avec PostgreSQL et sauvegardes quotidiennes incluses, ce qui couvre exactement les besoins de l'ADR-10.

**Les domaines `circuy.fr`, `circuy.com` et `circuy.net` sont enregistrés chez OVH**, qui intervient comme bureau d'enregistrement et pour la gestion des zones DNS. OVH n'héberge ni l'application, ni les données.

**Localisation réelle des données.** Greenshift a son siège à Paris mais héberge chez Iron Mountain, dans la région d'Amsterdam. Les données résident donc **aux Pays-Bas**, et non en France. Cette précision n'est pas un détail rédactionnel : la communication commerciale doit annoncer un hébergement européen, jamais français.

**Point d'attention sur l'extraterritorialité.** Iron Mountain est une société de droit américain. Le contrat étant un contrat de colocation, Iron Mountain fournit le bâtiment, l'énergie et la sécurité physique, sans accès logique aux données, qui restent sous le seul contrôle de Greenshift. Le risque au regard des lois extraterritoriales est donc faible mais non nul, et il est moins net qu'avec un hébergeur intégralement européen. Le cahier fonctionnel plaçant cette protection parmi ses arguments, la formulation retenue vis-à-vis des clients doit rester exacte : hébergement en Union européenne, sous contrôle d'un prestataire européen, dans un centre exploité en colocation.

**Topologie, conforme aux ADR-10 et ADR-11 :**

```mermaid
flowchart TB
    Visiteur -->|cdn.circuy.net| App[Instance unique: Hono + dashboard statique]
    Admin[Dashboard et extension] -->|app.circuy.net| App
    App --> PG[(PostgreSQL)]
    PG --> Backup[Sauvegardes quotidiennes]
```

**Sauvegardes.** Sauvegarde quotidienne incluse dans l'offre de l'hébergeur, avec sept jours de rétention, complétée par une restauration d'essai trimestrielle sur l'environnement de recette. Cette restauration périodique n'est pas une formalité : une sauvegarde qui n'a jamais été restaurée n'est pas une sauvegarde, c'est une hypothèse.

#### **7.5. Environnements**

| Environnement | Composition |
| --- | --- |
| Local | PostgreSQL en conteneur, backend et dashboard en mode développement, extension en rechargement à chaud |
| Recette | Copie réduite de la production, données synthétiques, cible des restaurations d'essai |
| Production | Topologie décrite en 7.4 |

La promotion se fait par image de conteneur étiquetée : l'image validée en recette est exactement celle déployée en production. Les migrations Drizzle sont appliquées au démarrage, avant que l'instance n'accepte du trafic.

**Pas d'environnement éphémère par branche.** Le dispositif coûte une automatisation permanente et une facture continue, pour une équipe qui n'a pas encore de production. Il sera envisagé quand plusieurs personnes se gêneront effectivement sur l'environnement de recette.

#### **7.6. Observabilité**

**Journaux** structurés en JSON sur la sortie standard, collectés par l'hébergeur. Ils ne contiennent jamais d'adresse IP, de jeton, ni de contenu de parcours.

**Métriques suivies :** taux de réponses en erreur serveur, latence et débit des endpoints publics, part de réponses 304 sur le manifeste, et taux d'événements `target_lost` rapporté aux `step_view`.

Les deux dernières méritent une explication. La part de réponses 304 mesure l'efficacité réelle de la stratégie de cache : c'est elle, et non un taux de succès de réseau de diffusion, qui indique si l'instance tient sans intermédiaire (ADR-11). Sa dégradation, ou le franchissement du seuil de 500 requêtes par seconde, est le signal qui rouvre la question du cache en périphérie. Le taux de `target_lost` est quant à lui autant un indicateur produit que technique : sa montée signale que les sites de nos clients évoluent plus vite que la résilience de nos sélecteurs.

**Alertes qui réveillent un humain**, et elles seules :

- Indisponibilité des endpoints publics de lecture.
- Échec d'une sauvegarde.
- Taux d'erreur serveur supérieur à 1 % sur cinq minutes.

Tout le reste est consultable mais ne déclenche rien. Une alerte qui sonne sans exiger d'action immédiate apprend à l'équipe à ignorer les alertes, ce qui coûte plus cher que l'incident qu'elle prétendait prévenir.
