Atproto, le protocole social décentralisé
Écrit le
Pour Louminot, une application que je développe, j’ai exploré un protocole décentralisé pour gérer les données utilisateurs.
Ce document a pour but de mettre sur papier “numérique” ma compréhension de Atproto.
Avant-propos
Je n’ai jamais aimé les réseaux sociaux… Ce qui me dérange, c’est qu’on n’a pas le contrôle sur nos données. C’est pourquoi quand j’ai entendu parler de Atproto, j’ai été intrigué.
Nos données sur nos propres serveurs et les supprimer quand bon nous semble ? Trop beau pour être vrai ?
Photo par Denys Nevozhai sur Unsplash
Le protocole Atproto
On interagit avec le PDS (Personal Data Server) directement pour toutes les opérations. Si je souhaite consulter le post d’un compte, j’envoie une requête à mon PDS qui va l’envoyer sur le “réseau Atproto” et le récupérer.
C’est un schéma simplifié. On va voir plus bas qu’il y a plusieurs concepts à comprendre pour bien appréhender le protocole.
Composition d’un post
Avant de rentrer dans les détails, il faut comprendre comment un post est structuré. Voici donc un post typique sur un réseau social :
Dans une base de données relationnelle, on aurait ici des tables posts, authors et likes. Et pour le nombre de “like”, commentaires et partages, on aurait des requêtes SQL pour les statistiques.
| posts | authors | likes |
|---|---|---|
| id | id | id_author |
| parent_post | name | id_post |
| author_id | avatar | |
| text | ||
| createdAt |
Dans Atproto, les données sont stockées dans des fichiers.
Records
Les records sont des fichiers sous format JSON. Le contenu du post précédent a cette structure :
{
"$type": "app.bsky.feed.post", // le lexicon utilisé pour ce record
"text": "Hello world", // le contenu du post
"createdAt": "2026-06-18T08:10:57.856Z" // la date de création du post
}
Tout comme dans une base de données relationnelle, les informations sur l’auteur du post ne sont pas stockées dans le même record que le post :
{
"$type": "app.bsky.actor.profile",
"avatar": {
"ref": {
"$link": "bafkreiha3bnz4knckg6tgdgawvvgtc27bmoykfe2dpe5fq7suxclijgjdm"
},
"size": 685201,
"$type": "blob",
"mimeType": "image/jpeg"
},
"description": "Freelance 🐪 Dev and Engineering Manager",
"displayName": "Joris"
}
Le lien se fait naturellement. Ils sont tous deux sur le même repository, ils appartiennent donc au même compte.
Les records key
Comment faire pour avoir plusieurs posts d’un même auteur ? Il faut que chaque post ait un identifiant unique. Pour cela, on utilise une record key ou en abrégé rkey. Tous les records n’ont pas besoin d’une rkey. Par exemple, l’auteur d’un post est unique dans un repository.
On aura donc une arborescence de fichiers sur le repository qui ressemble à ceci :
app.bsky
|- actor.profile
|- self.json
|- feed.post
|- 3mqhjuttizs22.json
|- 3mqhjqwvflc22.json
Repository
Un repository est un ensemble de records. Chaque utilisateur a son propre repository. Un PDS peut contenir plusieurs repositories. Comme il s’agit d’un espace personnel, il peut y avoir plusieurs applications qui y ajoutent des records. Tous les records ne sont pas compatibles entre eux. Du coup, on peut avoir un namespace pour chaque application.
PDS
|- repository de Joris
|- app.bsky
|- actor.profile
|- self.json
|- feed.post
|- 3mqhjuttizs22.json
|- 3mqhjqwvflc22.json
|- com.twitter
|- actor.profile
|- self.json
|- feed.post
|- 3mqhjuttizs34.json
Lexicons
Les lexicons sont les schémas de données qui définissent la structure et les interactions dans le protocole. Voici la définition de notre post :
{
"id": "app.bsky.feed.post",
"defs": {
"main": {
"key": "tid",
"type": "record",
"record": {
"type": "object",
"required": [
"text",
"createdAt"
],
"properties": {
"text": {
"type": "string",
"description": "The primary post content. May be an empty string, if there are embeds.",
"maxLength": 3000,
"maxGraphemes": 300
},
"createdAt": {
"type": "string",
"description": "Client-declared timestamp when this post was originally created.",
"format": "datetime"
}
}
}
}
},
"$type": "com.atproto.lexicon.schema",
"lexicon": 1
}
Dans ce lexicon, il y a trois éléments à retenir :
- type : qu’est-ce que le PDS doit faire de ce schéma. Ici, c’est un record. Le PDS doit donc faire une opération sur un fichier.
- key : c’est la rkey utilisée pour identifier un post. Pour un singleton, on a par exemple
literal:self. - properties : ce sont les propriétés du post.
Le schéma nous offre également de l’interopérabilité. Si un autre développeur souhaite créer une application qui interagit avec le protocole Atproto, il peut utiliser le même lexicon pour créer des posts. Ça permet d’avoir des applications qui interagissent entre elles sans être couplées.
Bon, il reste quand même un problème. Comment je fais pour avoir les likes, les commentaires et les partages d’un post ?
C’est là que les Appviews interviennent.
Appviews et relays
Voici un record app.bsky.feed.like :
{
"$type": "app.bsky.feed.like",
"subject": {
"cid": "bafyreigraigbderlnhnwsw4j42qhklrsu4urbwvadu2bihf4kwycelvpzq",
"uri": "at://did:plc:ns7i7bp55lay2bx75k5jcnbh/app.bsky.feed.post/3mqp3psb2js2w"
},
"createdAt": "2026-07-15T19:06:35.954Z"
}
Nous avons donc un record qui contient une référence vers le post qui a été liké. Mais comment je fais pour récupérer tous les likes d’un post ?
Il faudrait en théorie scanner tous les PDS du réseau pour trouver les records qui ont app.bsky.feed.like et qui ont pour subject le post. Cela n’est pas très efficace.
Pour cela, il y a les relays. Ce sont des serveurs qui reçoivent des notifications de tous les PDS. Ainsi, une application, disons bsky.app, peut interroger un relay pour récupérer un flux de likes et créer une projection personnalisée dans une base de données locale. Ces applications sont appelées appviews. Les appviews ont une application sidecar qui est le tap.
Je ne rentre pas dans les détails. Je pense que c’est suffisant pour comprendre le fonctionnement global de Atproto. Si vous souhaitez en savoir plus, je vous invite à lire la documentation officielle.
Mes impressions
Ce protocole est très cool, mais il est complexe. Il y a beaucoup de concepts à comprendre et, comme dans les architectures d’Event sourcing, il y a de la consistance éventuelle. Il faut donc faire attention au read after write. Un autre avantage que je vois à ce protocole, c’est que les environnements de développement peuvent dépendre de données de production sans risque de les corrompre. On peut donc faire des tests sur des données réelles sans risque.