Chapitre 02Comprendre
Comment ça marche
Deux flux, deux points de couplage, onze règles qui ne se discutent pas. De quoi lire le code sans se perdre, et savoir tout de suite où une modification a sa place.
Le flux de lecture
C'est le flux du visiteur, et il est volontairement court : tout se passe au build, rien à l'exécution.
Un visiteur reçoit un fichier HTML déjà complet. Aucun appel réseau ne va chercher le contenu, aucune base de données n'est interrogée, et le JavaScript n'est nécessaire à aucun mot affiché. Une page publique embarque environ 175 octets de script en ligne, qui lisent un cookie et ne font rien d'autre — voir « l'amorce d'édition » plus bas.
Le flux d'écriture
C'est le flux du client qui modifie sa page. Il est plus long, et chaque étape a un rôle.
-
L'overlay envoie le contenu complet de la page
Pas un champ, pas un diff : le fichier JSON entier, tel qu'il devrait être après modification, accompagné de la version lue à l'ouverture.
-
La fonction refuse dans un ordre précis
Débit, puis taille annoncée, puis identité, puis taille reçue, puis forme, chemin, schéma, verrou, écriture. L'ordre n'est pas cosmétique : un plafond de taille placé après la lecture du corps ne protège de rien, et refuser un appelant qui insiste ne demande pas de savoir qui il est.
-
Le même schéma Zod qu'au build valide le contenu
Pas une copie du schéma : le même objet, importé du même fichier. Un contenu qui ferait échouer le build n'entre jamais dans le dépôt.
-
Le verrou optimiste compare les versions
Si le fichier a changé dans le dépôt depuis l'ouverture de la page, l'écriture est refusée avec un conflit explicite — jamais un écrasement silencieux.
-
Un commit, puis une reconstruction
Le commit est attribué à
EDITOR_NAME/EDITOR_EMAIL. L'hébergeur détecte la poussée et reconstruit le site : trente à soixante secondes.
Le dépôt est séparé en deux
C'est la décision qui rend l'exploitation tenable au-delà de trois clients. Ce qui est identique d'un site à l'autre est partagé et versionné ; ce qui appartient au site est copié puis adapté.
| Où | Par site | |
|---|---|---|
| Overlay, modèle de contenu, composants, routes serveur, sécurité | packages/inline-core |
dépendance versionnée |
| Contenu, charte, mise en page, langues, adaptateurs de routes | le projet du site | créé une fois, puis adapté |
Ce qui est dans inline-core ne se recopie jamais dans un site.
Une règle métier qui apparaît dans /functions/api est une règle au mauvais
endroit : le jour d'un correctif, il faudra la retrouver dans dix dépôts.
Les deux points de couplage
Deux choses, et deux seulement, dépendent d'un fournisseur extérieur. Elles sont isolées derrière une interface, dans deux fichiers :
packages/inline-core/src/server/git-provider.ts readFile / writeFile (GitHub | GitLab)
packages/inline-core/src/server/auth.ts verifyAuth / createSession (clé de site | délégué)Aucune vérification d'identité et aucun appel à une API Git n'existe ailleurs dans le code. C'est ce qui permet de changer d'hébergeur en quelques heures, de passer de GitHub à GitLab en écrivant une implémentation, ou de basculer vers une authentification multi-utilisateurs sans toucher au reste.
Les onze règles absolues
Elles priment sur toute autre considération. Elles sont reprises ici parce qu'on les enfreint rarement par conviction, et souvent par réflexe.
| # | Règle | Pourquoi |
|---|---|---|
| 1 | output: 'static' — jamais SSR, jamais hybride | C'est ce qui garantit que tout le contenu est dans le HTML servi. |
| 2 | Aucune zone éditable dans un composant hydraté | client:only sort le contenu de l'index ; client:load le réaffiche au chargement et efface les modifications en cours. |
| 3 | Le jeton Git ne quitte jamais le serveur | Il appartient à l'agence, pas au client. Jamais dans une réponse, un fichier servi ou un stockage du navigateur. |
| 3 bis | La clé de site est vérifiée exclusivement côté serveur | Un hash servi au navigateur est attaquable hors ligne. |
| 4 | Aucun secret dans le dépôt | Uniquement en variables d'environnement de la fonction serveur. |
| 5 | Aucun style libre | Les styles passent par les enums Zod. Pas d'hexadécimal, pas de pixels dans le contenu. |
| 6 | Ne pas utiliser src/pages/api/* | En sortie statique, ces routes s'exécutent au build, pas à la requête. Tout va dans /functions. |
| 7 | Toute écriture est validée côté serveur | Identité, schéma, chemin, taille, assainissement. La validation client ne compte pour rien. |
| 8 | Aucun jargon technique dans l'interface | « Publier » et non « Commit ». Le client ne verra jamais le code. |
| 9 | Aucune arborescence de contenu dans l'overlay | C'est le moment où l'outil redevient un CMS et où le client décroche. |
| 10 | Les vidéos ne sont jamais téléversées | Un fichier lourd dans Git casse le dépôt et les builds. Fournisseur + identifiant, rien d'autre. |
| 11 | Ne pas élargir le périmètre | Les demandes hors périmètre se signalent, elles ne s'implémentent pas. |
Quatre mécanismes qui méritent d'être connus
Le schéma vit dans un fichier neutre
La fonction d'écriture ne peut pas importer astro:content : elle s'exécute hors
du contexte Astro. Le schéma Zod est donc isolé dans
inline-core/src/schema.ts, qui n'importe rien d'Astro. Le build et l'écriture
valident avec le même objet, pas avec deux copies qui finiraient par diverger.
L'amorce d'édition, et pourquoi elle ne donne aucun droit
Chaque page porte environ 175 octets de JavaScript en ligne : ils lisent le cookie
inline_edit et, s'il est présent, ajoutent une balise <script>
vers l'overlay. Une page publique ne télécharge donc jamais l'éditeur.
Le cookie inline_edit ne contient aucun secret. Le forger n'affiche qu'une
interface dont toutes les écritures seront refusées côté serveur : les routes
vérifient le cookie de session signé, jamais le témoin. Modifier le DOM sans clé n'a pas
plus de conséquence qu'avec les outils de développement du navigateur.
Le verrou optimiste
À l'ouverture, l'overlay lit le fichier via /api/content et reçoit une
version — chez GitHub, le SHA du blob. Cette version repart au moment de publier.
Si elle ne correspond plus, la publication est refusée et un message invite à recharger.
Rien n'est écrasé, et le brouillon local n'est pas perdu.
Vérifier une clé coûte volontairement cher
Environ 350 ms : c'est le prix d'argon2id aux paramètres recommandés, et c'est ce qui rend
la force brute impraticable. Ce coût n'est payé qu'à la connexion, une fois par tranche de
8 heures. Sur un hébergement facturant le temps processeur, vérifiez que le forfait couvre
cette durée sur /api/auth.
Les quatre routes
| Route | Rôle | Identité | Débit |
|---|---|---|---|
POST /api/auth | Ouvre une session à partir de la clé | — | 5 / 15 min |
GET /api/content | Lit l'état réel du dépôt et la version | oui | 60 / 5 min |
POST /api/save | Valide et publie une page | oui | 30 / 5 min |
POST /api/upload | Range une image dans le dépôt | oui | 30 / 15 min |
Détail complet des requêtes, réponses et codes d'erreur : Référence des routes.
Ce qui n'existe pas
- Pas de base de données. Le dépôt Git tient ce rôle.
- Pas de serveur applicatif. Quatre fonctions sans état, appelées à l'écriture seulement.
- Pas de session côté serveur. Le cookie est signé ; rien n'est stocké.
- Pas de compte utilisateur. Une clé par site.
- Pas de cache à invalider. Le HTML est reconstruit, donc remplacé.
La suite, concrètement : Démarrage local.