# ADR-001 — Backend : SvelteKit natif (fichiers plats), pas de Strapi **Statut** : accepté · **Date** : 2026-07-10 · **Décideur** : migration OKI d'exitchatcontrol.org **Doctrine de référence** : `ui-ux-souveraine-oki.md` (citée « doctrine §x.y » ci-dessous) ## Contexte exitchatcontrol.org est un annuaire de ~55 outils libres répartis en 23 sections thématiques, actuellement généré statiquement par Astro à partir de fichiers versionnés (`src/data/*.json`, `src/content/sections/*/*.mdx`). Le déploiement cible est un conteneur nginx auto-hébergé (YunoHost/Docker), sans backend. Deux options étaient sur la table (mission §2.1) : - **Option A** : Strapi v5 headless, base relationnelle, admin web, API, i18n plugin. - **Option B** : SvelteKit full-stack, contenu en fichiers Markdown/JSON versionnés, parsés au build, site prérendu. ## Décision **Option B — SvelteKit 2 + Svelte 5, contenu en fichiers plats prérendus.** ## Justification par la règle de décision La règle imposée : minimiser la charge cognitive **extrinsèque** (doctrine §1.2, Sweller) pour l'utilisateur final ET l'administrateur, dans le budget performance (doctrine §4.4). 1. **Les données sont déjà statiques et versionnées.** 55 outils, mises à jour par pull request espacées de plusieurs jours/semaines (historique git). Le seuil « le volume de données justifie une base relationnelle » posé comme condition de l'option A n'est pas atteint — 55 fiches tiennent dans un dossier de fichiers Markdown lisibles à l'œil nu. 2. **Charge d'administration.** Strapi = un service Node persistant + une base + des migrations de schéma + des mises à jour de sécurité + des sauvegardes. Pour l'admin OKI, c'est de la charge extrinsèque pure : la tâche réelle (« corriger une fiche ») devient « se connecter à un admin, naviguer un CMS, publier, invalider un cache ». En fichiers plats : éditer un fichier, commit. Le git devient l'historique éditorial, la revue par PR devient la modération. 3. **Budget performance (doctrine §4.4).** Site 100 % prérendu : premier rendu = HTML servi par nginx, zéro requête API, JS initial minimal (hydratation sélective). Strapi n'aiderait pas mais ajouterait un point de latence et de panne si l'on cédait à la tentation du rendu à la demande. 4. **Souveraineté et surface d'attaque.** Un site statique n'a pas de CVE de CMS, pas de compte admin à voler, pas de base à exfiltrer. C'est la posture cohérente avec le sujet même du site (OPSEC, résistance à la censure) : le site doit être **mirrorable** (un tarball du build suffit), ce qu'un Strapi rend impossible. 5. **Loi de Tesler (doctrine §1.3)** : la complexité éditoriale n'est pas supprimée, elle est déplacée vers le format de fichier. On l'absorbe par un frontmatter strict + validation au build (le build échoue si une fiche est invalide — équivalent des types du CMS, sans le CMS). ### Ce qui ferait rebasculer vers Strapi (critères de réversibilité) - Une équipe éditoriale non-technique de plusieurs personnes publiant chaque semaine ; - Des soumissions communautaires en volume nécessitant un workflow draft/publish ; - Plus de ~500 fiches ou des relations réellement complexes. Dans ce cas, le modèle de données de la mission §3 s'applique tel quel ; le frontend SvelteKit resterait inchangé (il consommerait l'API au build). Étape intermédiaire possible sans backend : Decap CMS (git-based) sur le dossier `content/` — écarté pour l'instant, « préférer la solution la plus simple ». ## Décisions techniques associées - **adapter-static + prerender total** : parité avec le déploiement nginx actuel (Dockerfile conservé dans l'esprit). Un lien fonctionne sans JS (doctrine §4.4). - **Markdown + frontmatter maison** : parseur frontmatter minimal (~40 lignes, zéro dépendance) + `marked` (MIT) pour le corps, exécuté **au build uniquement** — rien de tout cela n'est expédié au client. - **i18n maison typé** (pattern repris du projet source) plutôt que Paraglide/inlang : ~40 lignes, dictionnaires JSON `fr`/`gcf` vérifiés par le compilateur TypeScript (une clé manquante = erreur de build). Paraglide reste la piste si le nombre de locales croît. - **Langues** : `fr` (défaut) + `gcf` (créole guadeloupéen, code ISO 639-3 enregistré BCP 47 valide). Micro-textes créoles d'abord (doctrine §4.2 : impact affectif disproportionné pour un coût faible) ; traduction intégrale des fiches = chantier éditorial séparé (voir `docs/CONTENT_GUIDE.md`). - **CSS natif en `@layer`** : `reset → tokens → base → layouts → components → utilities` (doctrine §5.2), tokens `oklch()`, container queries, `clamp()`. Pas de Tailwind (doctrine §5.2 option 4 : écarté en connaissance de cause). - **Licence du code** : MIT, comme le projet source (continuité).