Accueil / Informatique / ActivityPub

ActivityPub

Cette article est une critique d'un point de vue technique d'ActivityPub.

Les trucs qui sont cools :

  • la fédération est un bon modèle pour plein de raisons :
    • ça permet de décentraliser
    • tout en mutualisant la modération
    • on peut espérer qu'une suppression soit bien une supression (contrairement à la plupart (toutes ?) les technos P2P)
    • c'est facile de créer des communautés autour d'une thématique

Les trucs nuls

Le souci c'est que ActivityPub est pas génial d'un point de vu technique.

En gros, c'est très compliqué et très mal spécifié alors qu'on pourrait faire plus simple.

JSON-LD y est pour beaucoup. C'est le format de données qu'utilise ActivityPub, et ça ressemble à du JSON avec des URLs partout (pour la faire très simple) :

// copié depuis la spec (exemple n°11)
{
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    {"@language": "en"}
  ],
  "type": "Like",
  "actor": "https://dustycloud.org/chris/",
  "name": "Chris liked 'Minimal ActivityPub update client'",
  "object": "https://rhiaro.co.uk/2016/05/minimal-activitypub",
  "to": [
    "https://rhiaro.co.uk/#amy",
    "https://dustycloud.org/followers",
    "https://rhiaro.co.uk/followers/"
  ],
  "cc": "https://e14n.com/evan"
}

L'avantage de ces URLs c'est qu'on peut y aller si on a besoin d'information supplémentaire. Par exemple si une instance reçoit le message précédent (qui correspond à un like / favori) et qu'elle ne sait pas qui est https://dustycloud.org/chris (la personne qui a fav), elle peut aller à cette URL pour récupérer son profil et l'ajouter dans sa base de données.

C'est notamment pour ça que copier l'adresse d'un toot dans la barre de recherche Mastodon permet de le charger sur votre instance : Mastodon va faire une requête ActivityPub à cette adresse, voit que c'est un toot et l'ajoute à la base de donnée avant de l'afficher comme résultat de la recherche.

L'idée de lier les différentes ressources avec des URLs semble donc bien, c'est simple, ça permet de pas faire des messages trop lourds puisqu'on peut envoyer un tout petit peu d'information et laisser les URL guider les instances qui ont besoin de plus.

Le truc c'est que c'est allé un peu trop loin : l'exemple de message que j'ai donné au dessus est une version « compactée ». Avant de pouvoir le traiter correctement, un serveur/instance devrait normalement le transformer pour que tout ce qui n'est pas une URL en devienne une. On se retrouve avec ce genre de trucs (j'ai fait l'algo à la main, il y sans doute des erreurs) :

{
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    {"@language": "en"}
  ],
  "https://www.w3.org/ns/activitystreams#type": "https://www.w3.org/ns/activitystreams#Like",
  "https://www.w3.org/ns/activitystreams#actor": "https://dustycloud.org/chris/",
  "https://www.w3.org/ns/activitystreams#name": "Chris liked 'Minimal ActivityPub update client'",
  "https://www.w3.org/ns/activitystreams#object": "https://rhiaro.co.uk/2016/05/minimal-activitypub",
  "https://www.w3.org/ns/activitystreams#to": [
    "https://rhiaro.co.uk/#amy",
    "https://dustycloud.org/followers",
    "https://rhiaro.co.uk/followers/"
  ],
  "https://www.w3.org/ns/activitystreams#cc": "https://e14n.com/evan"
}

Cette expansion est utile dans le cas où on a deux propriétés qui peuvent avoir le même nom : tant qu'elles n'ont pas la même URL au final, tout va bien.

Cette expansion d'URL n'est qu'une toute petite partie de l'algorithme d'expansion. Heureusement, il est bien documenté et expliqué, donc l'implémenter pourrait sembler assez simple : on suit la spécification pas à pas, c'est un peu long, mais au final on arrive à ce qu'on veut.

Sauf que la spécification a été pensée pour des langages faiblement typés : si vous l'implémentez en JavaScript ou en Elixir ça devrait pas trop mal se passer, mais en Haskell ou même en Java ça va être beaucoup plus complexe (à moins d'utiliser des structures de données « flexibles » comme des Map, mais c'est pas très idiomatique et ça rend la réfléxion plus complexe parce que le type ne renseigne pas du tout sur le format des données).

Donc comme c'est super relou de suivre la spécification JSON-LD, au final personne ne le fait.

Deuxième truc nul : comme la spec est pas claire, personne n'implémente vraiment les mêmes trucs, et on est obligé de copier les autres implémentations pour espérer pouvoir fédérer avec elles. La sécurité par exemple est assez peu prise en compte dans le protocole (qui dit qu'on « peut » implémenter les signatures HTTP si je me souviens bien).

Comment « réparer » ActivityPub

Du coup on pourrait se dire qu'il faudrait réparer ActivityPub pour avoir un truc qui marche mieux.

Ce qu'on peut garder :

  • l'idée de lier différents objets avec des URLs

Ce qu'on peut jeter :

  • JSON-LD et sa complexité qui est pas utile 99% du temps

Ce qu'il faut ajouter :

  • une spécification claire de comment est-ce qu'on doit faire niveau sécurité
  • une spécification claire de comment se comporter quand on reçoit certains types d'activités / d'objets : sans ça un blocage peut être interprêté de beaucoup de façons différentes selon le logiciel qui fait tourner une instance

Et aussi si on pouvait passer à un modèle serveur/client découplé (comme le protocole de base le prévoit) au lieu d'avoir des trucs très spécifiques et très couplés comme on a actuellement ça serait bien, mais c'est plus un problème d'implémentation de la spec que de la spec en elle-même.