Quand j’ai commencé à travailler sur mon “datapod v2”, le “local first” ne faisait pas partie de mes contraintes, je voulais d’abord explorer les problématiques liées à la modération décentralisée et savoir s’il était faisable de construire un index décentralisé sans consensus.
Dans ma première tentative sur les datapods (Hugo Trentesaux / Duniter Datapod · GitLab), je n’utilisais IPFS que pour collectionner les “faits” (c’est-à-dire les données soumises au système) dans un corpus. L’index était reconstruit de zéro pour chaque nœud dans une base Postgres exposant un endpoint Graphql. Cela venait avec plusieurs inconvénients :
- synchronisation entre le corpus de faits et l’index délicate (notamment en cas de bugs cassant la transactionnalité)
- solution monolithique inadaptée à un fonctionnement p2p (difficile d’embarquer postgres dans une app)
- nécessité de découverte réseau et problématique de confiance dans un nœud distant
Ma première “trouvaille” en travaillant sur mon “datapod v2” était qu’il était assez facile de construire un index en utilisant les briques de la donnée décentralisée (ici Iroh plutôt que IPFS mais le principe est le même), et donc de requêter directement la couche de données sans dépendre d’un index secondaire. De plus, la couche d’interprétation SQL implémentée par Claude sur base GlueSQL s’est révélée assez performante. Concrètement, il s’agit simplement d’un Prolly Tree combiné à un choix adapté des clés de stockage clé/valeur.
L’index étant simplement dérivé des faits selon un set de règles communes, deux acteurs qui collectent les mêmes faits peuvent voir leur index converger vers une valeur commune pour peu que ces règles soient indépendantes de l’ordre de réception. Cette contrainte peut paraître anodine, mais elle ne l’est pas. C’est elle qui fait apparaître le plus clairement la différence entre un système avec consensus et sans consensus. La solution “simple” est d’introduire un consensus sous une forme ou une autre, mais j’étais guidé par l’intuition qu’il ne fallait pas introduire de consensus sous peine de réduire la décentralisation du système, j’ai donc continué à réfléchir.
Le consensus au sens algorithmique est ce qui nous permet de trancher entre deux versions des faits. C’est pratique de pouvoir trancher les cas liés à une divergence technique, mais quand cette divergence est humaine, mon avis est qu’il faut laisser les humains décider plutôt que l’algorithme. En l’absence de consensus on se retrouve avec des forks, et il faut savoir si ce fork est d’origine technique ou humaine. L’aspect technique peut être résolu avec des CRDT par exemple, mais les désaccords humains doivent pouvoir vivre leur vie. Je parle alors de “fork social”.
Dans les systèmes cryptographiques, le premier “fork social” est celui de l’identité. En effet, chaque acteur du réseau a un identité déterminée par sa clé cryptographique, chaque donnée du corpus doit être signé par une clé, et on doit décider si l’on accepte cette donnée dans notre corpus (clé de confiance) ou si on la refuse (bot, spammeur…). Accepter toutes les clés n’est pas envisageable, il y en a plus de 2252 pour ed25519, soit de quoi dépasser les capacités de n’importe quel système informatique. Un acteur qui génère une clé localement (donc tire un nombre aléatoire) crée donc nécessairement un “fork social” entre :
- sa version du monde dans laquelle sa clé est légitime
- la version du monde d’un autre qui ne reconnaît pas encore sa clé comme légitime
Ce cas d’usage m’a guidé vers le “local first” comme solution naturelle pour exprimer le fork social. Il me fallait construire un système qui permette de fonctionner complètement même avant que cette clé ne soit reconnue comme légitime par le réseau. Dans mon prototype “Ğeopod” (Ğeopod : mon approche p2p pour les datapods et gchange), l’appli est donc entièrement utilisable hors ligne avec une clé créée localement. Vous pouvez publier des annonces, rechercher parmi ces annonces, liker, commenter… Évidemment ce n’est pas très intéressant en local uniquement, ça le devient plus quand vous vous connectez en réseau local à quelqu’un d’autre qui a créé sa clé et que vous reconnaissez mutuellement vos clés. Vous pouvez alors commencer à explorer les annonces l’un de l’autre. Et ainsi de suite de proche en proche.
Voilà ma conclusion : le “local first” qui me paraissait être une propriété chouette mais non directement liée à la modération décentralisée se trouve en fait être une solution très élégante au problème du fork social. Voilà pourquoi j’y tiens tant, au moins d’un point de vue théorique. C’est la raison principale pour laquelle je m’y intéresse autant, même au vu de la difficulté additionnelle que cela apporte.
Au delà de cet intérêt théorique, une approche “local first” a d’autres avantages :
- Il existe une grande communauté qui s’intéresse à ça, dont https://lofi.so/ (LOcal FIrst SOftware).
- L’expérience utilisateur hors ligne est forcément meilleure qu’un système dépendant de la connexion, c’est le niveau ultime du “cache”.
- Les performances sont généralement meilleures, car une fois la donnée en local, la requête est plus rapide (d’où les “cache”)
- Le coût d’exécution des requêtes est porté localement plutôt que par un acteur distant qui doit se protéger contre les abus (quotas…).
- La sauvegarde des données est native, pas de 404, une donnée supprimée sur le réseau reste disponible et exploitable là où elle a été lue.
et des inconvénients :
- Des applis plus lourdes que si elles déportaient la charge sur un serveur (stockage, CPU…).
- Moins de mutualisation des calculs les plus lourds sur des machines spécialisées.
- …
Mais je préfère quand même avoir une approche “local first” qui est améliorée quand une ressource externe est disponible qu’une approche “server first” qui souffre de fallback en cas d’indisponibilité.
PS : Il faut que je relise sur Nextgraph mentionné ici