llmp2p : BitTorrent est de retour et il distribue vos LLM
C’est exactement le problème des modèles LLM locaux : un GGUF fait de 1 à 100 Go, servi par du HTTP tout nu. Chaque téléchargement repart de zéro sur le Hub de Hugging Face. Votre homelab de trois machines avec Ollama ? Trois téléchargements complets du même blob, trois fois la facture de bande passante.
Avant d’écrire quoi que ce soit, j’ai regardé l’étagère. Le CLI du Hub reprend mais retape l’origine à chaque fois. Les pulls Ollama retéléchargent le blob complet par machine. Les clients torrent auraient volontiers distribué les fichiers, mais rien ne reliait l’essaim aux hashes que le Hub publie, rien ne signait les métadonnées et rien ne retombait sur HTTPS quand l’essaim était vide. Rien sur l’étagère ne faisait le travail entier, alors j’ai écrit le travail entier.
Donc j’ai écrit llmp2p : les modèles deviennent ce qu’ils sont, des blobs immuables géants qu’un essaim sert mieux qu’une origine unique. Premier pull d’un modèle que personne ne seede ? Lent et on retombe proprement sur HTTPS. Mais dès que deux personnes l’ont, ça accélère. Plus le modèle est populaire, plus la distribution est rapide. C’est le contrat des essaims depuis vingt ans.

Le protocole : manifest et verification
llmp2p ne télécharge pas des fichiers en aveugle. Le protocole :
- Le manifest : un JSON qui décrit le modèle (nom, taille, SHA-256 du blob), signé ed25519 par l’éditeur. La clé publique du publisher est dans une allowlist.
- Le torrent : le manifest devient un torrent dont les pièces correspondent aux chunks du blob GGUF. L’essaim se forme autour du torrent.
- La vérification : à la fin du téléchargement, le SHA-256 du blob est recalculé et comparé au manifest. Pas de match, le blob est rejeté et re-téléchargé depuis HTTPS.
- Le fallback HTTPS : si l’essaim est vide (personne ne seede), llmp2p télécharge directement depuis le Hub de Hugging Face, comme le ferait n’importe quel client. Personne n’attend un essaim qui n’arrivera pas.
Le HTTPS fallback garantit que le premier téléchargement fonctionne. Le swarm garantit que les suivants sont plus rapides. Et la vérification garantit que le blob téléchargé est exactement celui que le publisher a signé.
Le daemon et le CLI
Deux binaires en Go (1.25) :
llmp2p: le CLI.llmp2p pull <model>télécharge, vérifie et seede.llmp2p verify <model>re-vérifie l’intégrité.llmp2p keygengénère une paire ed25519 pour signer des manifests.llmp2pd: le daemon. Il écoute sur:8347, expose une API REST pour lister les modèles disponibles, leur état de téléchargement, les stats de l’essaim.
Le daemon est fait pour tourner en service, toujours actif, toujours en train de seeder les modèles téléchargés. Chaque pull devient un seed pour les machines suivantes.
Le container : FROM scratch
L’image Docker est construite FROM scratch (pas de base, pas de shell, pas de libc) : le binaire Go statique est le seul contenu. Multi-arch (amd64, arm64), cosign-signé et le SBOM est généré au build. L’image pèse quelques Mo.
Le GitLab incident : quand l’infra se souvient de vos morts
[edit: octobre 2026] Début octobre, un fichier dans mon dépôt GitLab a déclenché une alerte : un pipeline de 2022 référençait un runner qui n’existe plus. Le runner en question avait été supprimé, mais la référence survivait dans un fichier de config et GitLab envoyait un mail d’erreur à chaque push, deux ans plus tard.
Le problème n’était pas le mail. C’était la découverte : en fouillant les pipelines orphelins, j’ai trouvé des artefacts de CI qui contenaient des clés API, des secrets de déploiement et des tokens d’accès, dans des jobs qui avaient tourné il y a deux ans, sur des branches supprimées depuis. GitLab garde les artefacts de CI même après la suppression de la branche et ces artefacts étaient accessibles depuis l’API du projet.
La leçon : les secrets que vous avez utilisés il y a deux ans sont probablement encore dans vos artefacts de CI et le nettoyage des artefacts est aussi important que le nettoyage des secrets dans le code. llmp2p
nettoie les siens: le .gitlab-ci.yml expire les artefacts après 24h et les variables d’environnement sont scoped par environnement, pas globales.
Vivre avec : essaims privés, Ollama, units de déploiement
Trois choses qui l’ont transformé de démo en outil que je fais tourner tous les jours :
- Mode essaim privé (
pull --swarm-key) : la découverte passe en DHT seul sous une clé partagée. Pas d’origines bootstrap, pas de repli Hub : vos dépôts de modèles internes restent internes, seedés entre vos propres machines. - Intégration Ollama : après un pull vérifié, llmp2p enregistre le GGUF via
ollama createavec un Modelfile généré. Toucher directement le blob storage d’Ollama dépend de sa version et n’est pas supporté, donc l’intégration passe par le CLI au lieu de fouiner dans le dossier de données. - Units de déploiement : des units systemd, launchd et NSSM sont livrées dans le repo, parce qu’un seeder qu’on démarre à la main cesse d’être un seeder la semaine où on l’oublie.
Le repo est sur un train de releases depuis le début de septembre ; l’outil est jeune, le contrat des essaims est vieux.
Le code applicatif est sur github.com/log0u7/llmp2p
. L’image conteneur, FROM scratch, multi-arch et signée cosign
, se construit dans un dépôt dédié
et se tire depuis registry.gitlab.com/6admin.io/docker/llmp2p/llmp2p:v0.3.2.