Aller au contenu principal

Clepit

En bref

Catégorie
Plateforme pour développeurs
Site web
clepit.com
Console
app.clepit.com
Documentation
clepit.com/en/docs
API GraphQL
api.clepit.com/graphql
Temps réel
ws.clepit.com
Surface MCP
api.clepit.com/mcp
Pages publiées
clepit.space

La plupart des textes enrichis finissent dans une base de données sous la forme d’un bloc de HTML. Cela convient tant que vous ne voulez rien en faire d’autre que le réafficher : retrouver chaque page qui mentionne un client, restituer le même document dans une page web, un e-mail et une application mobile, ou laisser deux personnes le modifier en même temps sans que l’une perde un paragraphe. À ce stade, les mots et leur mise en forme sont emmêlés, et la seule chose capable de lire le document de façon fiable reste l’éditeur qui l’a écrit.

Clepit sépare les deux. Une page est une liste de blocs, chacun un petit objet typé, et le document est du JSON : aucun balisage à analyser, aucun éditeur nécessaire pour le lire. C’est aussi pourquoi l’éditeur tient debout tout seul. @clepit/core est publié sur npm sous licence MIT et ignore tout de la partie hébergée, tandis que l’espace de travail sur app.clepit.com est ce que deviennent ces mêmes documents une fois dotés de collaborateurs, de permissions, d’un historique et d’une adresse sur le web public.

Une page est une liste de blocs

Un bloc, ce sont quatre champs : un identifiant, un type, les données que ce type définit, et les réglages qui lui sont appliqués. Les données d’un paragraphe n’ont pas la même forme que celles d’un tableau, et c’est le type qui indique quelle forme attendre, si bien qu’un document stocké peut être vérifié et pas seulement analysé. Une page entière, c’est un horodatage, une version et les blocs dans l’ordre : assez petit pour être lu à l’œil nu et relu dans une pull request. Rien là-dedans ne décrit l’apparence de la page : cela appartient à ce qui la dessine, et c’est ce qui permet à un même document de devenir une page web, un e-mail et un écran mobile sans qu’il en existe trois copies.

Dessiner un document sans navigateur

Le moteur de rendu n’écrit jamais directement dans le navigateur. Il dessine à travers une couche mince derrière laquelle se trouvent deux supports : l’un construit de vrais éléments de page, l’autre construit une chaîne de caractères, et le même code de bloc s’exécute sur les deux. C’est ainsi qu’une page publiée est dessinée sur un serveur où aucun navigateur n’existe, et c’est pourquoi ce que produit le serveur est le document même que l’éditeur aurait affiché, et non une seconde implémentation laissée à dériver. Le support chaîne exige un assainisseur en argument obligatoire et n’a délibérément aucune valeur par défaut : l’assainisseur habituel du paquet construit une page pour analyser, il ne peut donc pas s’exécuter là, et se rabattre en silence sur l’échappement dépouillerait chaque document de sa mise en forme interne sans le dire. La faille reste donc visible à l’endroit où l’on s’en sert, plutôt que dissimulée dans une valeur par défaut.

Lire ne coûte au lecteur aucun JavaScript

L’adaptateur React livre ses deux moitiés séparément, parce qu’elles veulent des choses opposées. Le composant de contenu s’exécute sur le serveur et produit un balisage fini pendant la construction de la page : le lecteur reçoit donc le document dès la première réponse. Le composant éditeur, lui, n’existe que côté client, puisqu’il prend en charge le cycle de vie de l’éditeur et qu’il n’y a rien à prendre en charge tant qu’il n’y a pas de navigateur. Lire un document Clepit ne demande donc aucun JavaScript. C’est en écrivant que le moteur d’exécution arrive.

Ce qu’une page peut contenir

Vingt-six sortes de blocs sont livrées avec le paquet. La plupart sont celles dont tout éditeur a besoin : titres, paragraphes, listes, listes de contrôle, citations, code, tableaux, images, audio, vidéo, fichiers, encadrés et séparateurs. Les autres existent parce que la documentation réclame des choses qu’un outil d’écriture ignore d’ordinaire. Une table des matières qui se construit toute seule à partir des titres du document et renvoie à chacun. Des sections repliables et des colonnes. Une carte qui tient lieu d’une autre page. Un croquis à main levée. Et un bloc d’activité qui stocke quel document surveiller, non une copie de son activité : il continue donc de montrer ce qui se passe maintenant au lieu de se figer au jour où on l’a inséré. La mise en forme à l’intérieur d’un bloc couvre les marques habituelles, gras, italique, souligné, barré, code en ligne, surlignage et liens, ainsi que les infobulles, les étiquettes d’état et les mentions. Le paquet lui-même n’a aucune dépendance à l’exécution.

Formules et diagrammes, dessinés dans le paquet

Deux de ces blocs rendent LaTeX et Mermaid, et tous deux font tout le travail dans le paquet : analyser la source, calculer la mise en page, dessiner le résultat. Il n’y a aucune bibliothèque de rendu en dessous, ni aucun appel à un service pour transformer une formule ou un organigramme en image. C’est moins une préférence en matière de dépendances qu’une conséquence du support chaîne. Un bloc qui irait chercher quelque chose que seul un navigateur fournit, ou qui irait chercher le réseau, ne pourrait pas être dessiné sur le serveur qui sert les pages publiées, et une même page aurait alors une allure différente selon qui la demande.

Une référence d’API qui vit dans la page

Donnez une spécification au bloc OpenAPI, collée ou désignée par une adresse, et il dessine ce que cette spécification décrit : les opérations, leurs chemins et paramètres, les schémas de requête et de réponse, et le mode d’authentification. Pour chaque opération, il construit aussi un extrait de requête en cURL, TypeScript, Dart et Python, engendré à partir de la spécification plutôt que tapé par un auteur qui oubliera de le mettre à jour. Le bloc d’intégration porte le même regard sur le monde extérieur : il reconnaît une poignée de services qu’il sait réellement afficher, traite pour tout le reste un simple lien comme un aboutissement valable et non comme un échec, et refuse tout net d’afficher une adresse en laquelle il n’a pas confiance.

Deux personnes dans le même paragraphe

Une page en cours d’édition est tenue par une seule tâche sur le serveur, une par page, et chaque mise à jour y passe dans l’ordre. C’est ce qui rend l’édition simultanée raisonnable à se représenter : aucun deuxième rédacteur ne court après le premier. Le document lui-même est un CRDT, si bien que deux personnes qui écrivent dans le même paragraphe fusionnent au lieu de s’effacer, et qu’un client resté en arrière rattrape son retard en échangeant ce qui manque de part et d’autre. Chaque mise à jour est ajoutée à un journal d’écriture anticipée avant d’être diffusée à quiconque : ce que voient les autres personnes présentes dans le document a donc déjà été consigné durablement, et non simplement relayé. L’autorisation est appliquée sur le serveur et non dans l’interface : un pair qui rejoint sans droit d’édition est rétrogradé en lecture, et la session revérifie ce droit périodiquement tant que le document est ouvert, de sorte qu’un accès retiré atteint quelqu’un en train d’écrire plutôt que d’attendre qu’il recharge la page.

Toute écriture passe par une seule porte

Un document peut être modifié par une personne qui y écrit et par un programme qui appelle l’API, et ces deux chemins pouvaient autrefois écrire dans la même page indépendamment. Ils ne le peuvent plus. Une écriture venue de l’API est transmise à la session même qui détient le document vivant, où elle est appliquée comme une seule transaction aux côtés des modifications en cours : il n’existe donc qu’un ordre d’événements, et non deux rédacteurs avec chacun son avis sur ce que dit la page. Les identifiants de blocs sont conservés lors de la réécriture du document, car les commentaires y sont ancrés et une réconciliation qui créerait de nouveaux identifiants laisserait chaque commentaire pointer vers le vide. Et lorsque l’ensemble de blocs obtenu est identique à celui déjà stocké, rien n’est écrit du tout.

Le seul transport qu’une clé machine ne peut atteindre

Une clé d’API personnelle fonctionne avec REST, GraphQL, le socket d’abonnement GraphQL et MCP. Elle ne fonctionne pas avec le socket de collaboration, et c’est délibéré. Chaque modification collaborative est estampillée de la personne qui l’a faite, et ces estampilles deviennent la paternité consignée dans l’historique de la page. Un principal machine qui écrirait là inscrirait un auteur qu’aucune personne n’a écrit, et revenir dessus plus tard revient à réécrire l’histoire, non à supprimer une ligne. La frontière ne passe pas entre les websockets et HTTP : elle passe par le fait que le transport écrit ou non un historique attribué à un auteur. La règle est imposée par la forme du code et non par la mémoire, puisque accepter une clé exige de basculer délibérément vers un autre appel d’authentification, et qu’un test échoue lorsqu’un transport le fait.

Un espace de travail à sa propre adresse

Chaque espace de travail est un locataire doté de son propre sous-domaine dès sa création, et le locataire est déterminé à partir de l’adresse par laquelle la requête est arrivée. Savoir dans quel locataire vous êtes est donc tranché avant qu’aucune de vos données ne soit lue, et non par un filtre ajouté après coup que quelqu’un pourrait oublier. En dessous, c’est la base de données elle-même qui fait respecter la frontière, par la sécurité au niveau des lignes : chaque requête emprunte une connexion, y appose l’identité de l’appelant, et le pool efface cet état au retour de la connexion, si bien que l’identité d’une requête ne peut fuir dans les interrogations de la suivante.

Revendiquer un domaine n’est pas le prouver

Un espace de travail sur un forfait Entreprise peut servir ses pages depuis un domaine à lui. Revendiquer un domaine et servir depuis ce domaine sont délibérément deux étapes distinctes : le domaine est enregistré non vérifié, et le résolveur l’ignore entièrement tant qu’un enregistrement de vérification n’apparaît pas dans le DNS. N’importe qui peut taper l’adresse d’une autre entreprise dans un formulaire. Seule la personne qui contrôle ce domaine peut publier l’enregistrement qui le rend effectif.

Chaque version qu’a connue la page

Clepit conserve des révisions plutôt qu’un unique état courant. Un instantané est pris automatiquement pendant que les gens travaillent, bridé pour qu’une frappe ordinaire n’en produise pas des centaines : un nouveau est écrit dès que dix minutes se sont écoulées ou que dix blocs ont changé, selon ce qui arrive en premier. La restauration est une seule transaction : l’ancien instantané est appliqué, les blocs sont réconciliés, et la restauration elle-même est écrite comme une nouvelle révision, si bien que revenir en arrière est consigné et non défait en silence. La réconciliation conserve délibérément les identifiants de blocs de la source, car les commentaires sont ancrés aux blocs et restaurer une page avec des identifiants neufs laisserait chacun de ses commentaires sans ancrage.

Le retrouver

La recherche s’exécute sur une projection des pages, et la requête passe par l’analyseur de recherche web de Postgres plutôt que d’être assemblée en SQL à la main : on peut donc taper des guillemets et des signes moins sans que rien de tout cela ne constitue une surface d’injection. Ce qui compte dans un espace partagé, toutefois, c’est l’endroit où se trouve le contrôle des droits. La recherche joint la table des pages, et la sécurité au niveau des lignes de cette table s’applique à la jointure : les résultats sont donc déjà restreints aux pages que la personne qui interroge a le droit de voir. Les filtres (un sous-arbre de l’arborescence, qui a révisé en dernier, quand la page a été modifiée pour la dernière fois) s’y ajoutent comme conditions supplémentaires. Chacun rétrécit ; aucun ne peut élargir, car tous se situent derrière le même contrôle.

Publier fige, partager non

Ce sont deux choses différentes, et Clepit les traite différemment à dessein. Publier une page fige le document courant sous forme de révision, fait pointer la page vers elle et la rend publique : ce qu’un visiteur lit sur clepit.space, à une adresse du type acme.clepit.space/handbook, c’est cette révision figée, et non les modifications faites depuis. Dépublier efface ces pointeurs mais conserve l’adresse publique : republier plus tard revient donc à la même URL au lieu de casser tous les liens qui y menaient. Le lien de partage, lui, fait l’inverse : il sert le document vivant, si bien que ce que voit le destinataire change à mesure que la page change.

Un lien que vous pouvez reprendre

Un lien de partage est un jeton que vous pouvez révoquer, et on peut lui donner une date d’expiration au moment de le créer. Seule une empreinte du jeton est conservée : le lien n’est donc montré qu’une fois, à la création, et ne peut plus être retrouvé dans la base de données ensuite, ni par nous ni par quiconque y accéderait. Ces liens accordent la lecture, pas le commentaire : un commentaire exige un auteur, et le porteur d’un lien n’en est pas un.

Se connecter depuis votre propre annuaire

Un espace de travail peut confier l’authentification à son propre fournisseur d’identité : OpenID Connect avec le forfait Business, SAML avec le forfait Entreprise, et SCIM à côté pour la synchronisation d’annuaire. SCIM couvre la ressource utilisateur que pilotent réellement Okta et Entra, et s’écarte délibérément de la lecture évidente de la norme sur un point : une suppression désactive le membre au lieu de l’effacer. La spécification l’autorise, et l’autre solution serait une synchronisation d’annuaire capable de détruire le contenu d’un espace de travail en retirant simplement quelqu’un d’un groupe.

Quatre façons de lui parler

REST couvre quarante-quatre opérations sous /v1, décrites par un document OpenAPI engendré à partir des routes elles-mêmes plutôt qu’écrit à côté d’elles, et l’intégration continue compare cette sortie à la copie versionnée : un changement de route qui contournerait le registre ne peut donc pas passer en silence. GraphQL couvre le modèle applicatif et porte les abonnements sur son propre socket. Le temps réel est un processus distinct, ce qui explique qu’un redémarrage de la passerelle n’entraîne pas la surface de requêtes avec lui, et il autorise par socket plutôt que par salle : pour chaque événement, toutes les connexions du locataire sont évaluées en une seule vérification groupée, et seules celles qui ont le droit de le voir le reçoivent. MCP expose les mêmes opérations aux agents d’IA sous forme d’outils, et aucun outil ne se fie à un identifiant fourni par l’appelant pour décider dans quel espace de travail il agit ; les écritures passent par les mêmes services que ceux de l’interface, si bien que les contrôles de droits et la piste d’audit sont les mêmes.

À qui cela s’adresse

Les équipes qui veulent un éditeur qu’elles maîtrisent, et les développeurs qui intègrent du contenu structuré dans leur propre produit.

Visiter Clepit: clepit.com