Un fork expérimental de YaCy : meilleur classement, résultats signés, pairs derrière un NAT
Un fork de YaCy qui corrige des faiblesses mesurées sur le réseau public, ajoute une couche de confiance permettant de filtrer les résultats falsifiés et le spam, et permet aux pairs situés derrière un NAT de participer via un relais. Chaque affirmation de cette page est mesurée dans des réseaux pair-à-pair fermés que chacun peut reconstruire avec docker compose.
Statut : expérience. Ce fork n’est pas une version publiée par le projet YaCy et n’y est pas affilié. Il rompt délibérément la compatibilité avec le réseau YaCy public (identifiants de pairs, seeds, hachages de mots CJK). Il est partagé pour montrer, données à l’appui, ce que font les changements, afin que les idées puissent être discutées et, là où elles conviennent, proposées en amont par petits morceaux.
Pourquoi
Mesuré sur le réseau YaCy public (freeworld) avec 16 requêtes, seuls 11% des 10 premiers résultats contenaient tous les termes de la requête. En examinant les causes :
- Les requêtes à plusieurs termes étaient envoyées à Solr avec un minimum match de 1 (une requête OR), à l’index local et à chaque pair distant.
- Les scores Solr de chaque pair sont normalisés par rapport à son meilleur résultat, si bien qu’un pair qui n’a que des correspondances sur un seul terme place son meilleur au même niveau que les correspondances exactes des autres pairs.
- Le texte japonais et chinois n’est découpé qu’aux espaces et à la ponctuation, si bien qu’une phrase entière devient un seul « mot » et que l’index de mots (RWI) ne peut pas la trouver.
- Dans un réseau de nouveaux pairs, les pairs âgés de moins de 3 jours ne sont jamais interrogés lors d’une recherche dans l’index de mots, et les définitions de petits réseaux n’envoient les requêtes Solr distantes à personne.
- Les résultats ne portent aucune preuve d’origine. Un pair peut renvoyer n’importe quelle URL avec n’importe quel titre pour n’importe quelle requête, et des seeds peuvent être rejoués ou modifiés par des tiers.
- Les pairs derrière un NAT ne peuvent être que « junior » : ils ne sont pas joignables, donc ce qu’ils indexent est invisible pour les autres.
Ce qui a changé
Classement
Un minimum match plus strict, une pondération par couverture des termes après la normalisation par pair, une pondération contre les pages pauvres et la recherche sur tous les pairs des petits réseaux.
CJK
Le texte chinois, japonais et coréen est indexé et recherché sous forme de bigrammes chevauchants, dans Solr et dans l’index de mots.
Confiance
Des clés de pair Ed25519, des seeds signés, des listes de confiance signées par des coordinateurs avec des tags déclarés, et une signature d’auteur sur chaque document que le pair crawle.
Traversée de NAT
Un petit sidecar go-libp2p réserve un emplacement sur un circuit relay, pour que les pairs derrière un NAT répondent aux recherches.
Qualité de recherche
| Symptôme | Cause | Changement |
|---|---|---|
| Les pages qui correspondent à un seul terme (bourrage de mots-clés) sont classées en tête | Solr mm=1 pour les requêtes à plusieurs termes | Minimum match 2<-1 5<80% : avec deux termes, les deux doivent correspondre ; avec 3 à 5 termes, un peut manquer (search.ranking.solr.mm, .mm.cjk) |
| La meilleure correspondance partielle d’un pair est classée comme les correspondances exactes des autres pairs | Normalisation des scores par pair | Multiplier le score normalisé par (termes trouvés / termes de la requête)², au moins 0.05, et jamais en dessous de ce que garantit le minimum match (search.ranking.coverage.exponent) |
| Japonais / chinois introuvables dans l’index de mots | Pas de segmentation en mots pour le CJK | Bigrammes chevauchants dans l’index de mots et dans la requête ; CJKWidthFilter + CJKBigramFilter dans le schéma Solr |
| Les pages pauvres contenant toute la requête dans leur titre (listes de tags) sont classées en tête | Le qf par défaut pondère title^5 et h1^5 (et host ^6, nom de fichier de l’URL ^4, chemin ^3) face à text^1 | Les résultats de moins de 100 mots sont pondérés par mots / 100, au moins 0.1 (search.ranking.thin.words) ; comptage des mots CJK corrigé (il comptait les espaces) |
| Un réseau de nouveaux pairs ne cherche jamais dans les index de mots des autres pairs | La recherche DHT exige des pairs âgés de plus de 3 jours | Configurable (remotesearch.dht.minage, 3 par défaut) |
| Les petits réseaux n’envoient les requêtes Solr distantes à personne, ou ignorent les cibles DHT | La formule du nombre de cibles donne 0 ; les cibles DHT étaient exclues de Solr | Les réseaux jusqu’à 32 pairs interrogent chaque pair de confiance connecté (chaque pair en mode ouvert), cibles DHT comprises |
Couche de confiance
- Identité des pairs. Chaque pair possède une clé Ed25519. Son hachage de pair de 12 caractères est dérivé de la clé publique, et le cœur de son seed (nom, ports, clé, joignabilité, tags déclarés) est signé. Les seeds non signés sont rejetés par défaut. Un challenge hello prouve que le détenteur de la clé répond à une adresse.
- Listes de confiance. Les utilisateurs configurent des clés de coordinateurs. Un coordinateur délègue à des opérateurs, et les opérateurs signent des listes de pairs de confiance avec une priorité et des tags déclarés comme
adsouproxy:<engine>. Les listes sont versionnées au lieu d’expirer et se propagent de pair en pair. - Signatures d’auteur. Le pair qui crawle une page signe son URL, son titre et un filtre de Bloom de ses mots. Par défaut, un résultat n’est affiché que si sa signature est valide et que son auteur fait partie de l’ensemble de confiance (exceptions : les documents non signés de l’index propre du pair qui ne sont pas arrivés par la DHT comptent comme les siens, et les résultats de moteurs de recherche externes sont marqués « externe »). Un pair qui stocke un document pour la DHT ne peut pas le falsifier. Il peut seulement le retenir.
- Mode ouvert. Les résultats non signés ou non fiables peuvent être affichés, marqués « non vérifié » et toujours classés sous les résultats vérifiés. Les résultats portant une signature falsifiée ne sont jamais affichés.
Traversée de NAT
Un processus sidecar (Go, go-libp2p) tourne à côté de YaCy avec la même clé. Derrière un NAT, il réserve un emplacement sur un circuit relay v2 et annonce l’adresse du circuit dans le seed signé (Reach=relay). Les autres pairs ouvrent vers lui un port de tunnel local et utilisent du HTTP ordinaire, si bien que les clients existants de YaCy fonctionnent sans modification. Par défaut, ces pairs ne font que répondre aux recherches. Ils ne stockent pas de données DHT, sauf s’ils l’activent.
Voir la conception confiance et NAT (en anglais) pour les détails.
Résultats
Deux expériences dans yacy-lab. Toutes deux exécutent des réseaux fermés dans docker compose, avec un corpus et un jeu de requêtes déterministes.
Qualité de recherche : upstream contre fork, 3 pairs chacun
Chaque pair crawle un site. Les requêtes sont envoyées au pair 1 avec resource=global, et la plupart des pages pertinentes se trouvent sur les autres pairs. Le corpus contient deux sortes de leurres : des pages bourrées d’un seul terme de la requête, et des pages pauvres d’« archive de tags » contenant toute la requête dans leur titre. Moyenne sur 11 requêtes (6 en anglais, 4 en japonais, 1 en chinois), 2 exécutions au résultat identique sauf mention contraire.
| Cluster / chemin | R-précision ↑ | Recall@10 ↑ | Leurres dans le top R ↓ | Tous les termes dans le top 10 ↑ |
|---|---|---|---|---|
| upstream, par défaut | 0.52 | 0.96 | 0.48 | 0.42 |
| fork, par défaut | 0.79–0.86 | 1.00 | 0.14–0.21 | 0.75 |
| upstream, index de mots seul | 0.02 | 0.02 | 0.00 | 0.09 |
| fork, index de mots seul | 0.93 | 0.95 | 0.07 | 0.77 |
- Pages bourrées de mots-clés dans le top 5 (somme sur les 11 requêtes) : upstream 14, fork 0.
- Les pages pauvres d’archive de tags descendent mais restent dans le top 5. Leur rang moyen est de 4.5 dans la recherche par défaut et de 5.0 avec l’index de mots seul. Avec Solr seul, il passe de 2.0 sans la pondération des pages pauvres à 3.1 avec celle-ci. Elles restent classées au-dessus de pages pertinentes dont le titre ne contient pas les termes de la requête. Une pondération plus forte les ferait descendre davantage mais pénaliserait aussi les pages courtes légitimes, c’est pourquoi la valeur par défaut reste modérée.
- L’upstream ne peut pas utiliser les index de mots des autres pairs dans un réseau de nouveaux pairs, et ne peut pas du tout y trouver de mots japonais ou chinois.
- La valeur par défaut du minimum match a été choisie sur ce corpus. Reste à mesurer sur le réseau public si elle y convient.
Confiance et NAT : 6 pairs du fork, un relais et un NAT
Trois pairs de confiance, un pair de confiance qui déclare ads, un pair signé mais non fiable qui crawle du spam et implante des documents avec une signature empruntée, et un pair derrière un routeur MASQUERADE. Les 26 vérifications passent toutes, entre autres :
- Les identifiants de pairs dérivent des clés. Le spam du pair non fiable n’est pas affiché par défaut, pas même lorsqu’un pair de confiance en détient une copie.
- En mode ouvert, le spam apparaît marqué non vérifié et sous chaque résultat vérifié. Le document à la signature empruntée n’apparaît jamais.
- Les résultats du pair
adsportent le tag, etexcludeTags=adsles retire. - Le pair derrière le NAT n’est pas joignable directement, mais ses pages sont trouvées via le relais, et il reste connecté.
- Une nouvelle version de liste se propage par le seul échange entre pairs. Révoquer un opérateur retire les résultats de ses pairs.
Essayer
Sans Docker : ouvrez la démo dans votre navigateur. Elle fonctionne en mode simulé et rejoue des réponses enregistrées sur la vraie démo (la page est en japonais).
La démo démarre les deux réseaux (3 pairs upstream, et la configuration confiance et NAT du fork) sur une seule machine et place une page de recherche devant eux. Il vous faut Docker avec environ 7 Go de mémoire.
git clone https://github.com/pad01g/yacy_search_server.git yacy
git clone https://github.com/pad01g/yacy-lab.git
cd yacy
git checkout baseline && docker build -t yacy-lab/upstream:baseline -f docker/Dockerfile .
git checkout improved-search && docker build -t yacy-lab/fork:latest -f docker/Dockerfile .
docker build -t yacy-lab/sidecar:latest sidecar/
cd ../yacy-lab
docker compose -f compose.demo.yaml -p yacydemo up -d
# ouvrir http://localhost:8800 (la mise en place prend environ 10 minutes et affiche sa progression)
La page permet aussi de modifier la confiance : choisir à quels coordinateurs le pair qui cherche fait confiance (un second coordinateur ne liste que le pair de spam, donc lui faire confiance rend le spam « vérifié »), modifier et re-signer la liste de confiance, remettre la nouvelle version à un pair et la regarder se propager, et révoquer ou rétablir la délégation de l’opérateur.
Les expériences elles-mêmes : docker compose -p yacylab up -d && docker compose -p yacylab run --rm runner (qualité de recherche) et docker compose -f compose.trust.yaml -p yacytrust up -d && docker compose -f compose.trust.yaml -p yacytrust run --rm runner (confiance et NAT). Voir le README du labo.
Rejoindre : aucune autorisation nécessaire
N’importe qui, personne ou agent, peut faire tourner un pair, le connecter avec l’URL d’un membre (p2p.bootstrap.peers, y compris à travers un réseau Tailscale), indexer et signer ses propres pages et services, et gérer son propre coordinateur ou opérateur : un coordinateur n’est qu’une clé et un fichier signé à n’importe quelle URL, sans serveur. Les publicités déclarées (tag ads) sont autorisées ; les utilisateurs choisissent à quelles listes ils font confiance. Comment rejoindre · pull requests bienvenues.
Pour les agents IA
Un agent peut faire tourner son propre pair et l’utiliser comme outil de recherche, sans API de recherche ni clé d’API :
- Serveur MCP (
io.github.pad01g/yacy-search) :docker run -i --rm --network yacy -e YACY_URL=http://yacy:8090 -e YACY_ADMIN_PASSWORD=<votre mot de passe> ghcr.io/pad01g/yacy-search-mcp:0.2.1(avec le pair démarré pardocker run -d --name yacy --network yacy -p 127.0.0.1:8090:8090 -v yacy_data:/opt/yacy_search_server/DATA ghcr.io/pad01g/yacy-improved-search:latest; changez d’abord le mot de passe par défautyacy). Outils :search,crawl,index_status,peers,get_ranking_settings,set_ranking_setting,evaluate_ranking,trust_status. Il tourne à côté de l’agent et dialogue avec le pair de l’agent ; il n’y a aucun service central. - Skill d’agent :
npx skills add pad01g/yacy-labinstalle yacy-p2p-search (démarrer un pair, crawler, chercher, évaluer et ajuster le classement). - Registre de confiance : pad01g/yacy-trust. Une pull request fusionnée liste votre pair ou fait de vous un opérateur qui se porte garant de pairs.
- Résumé lisible par machine : llms.txt.
Limites
- Incompatible par défaut avec le réseau YaCy public (seeds non signés rejetés, hachages de mots CJK modifiés). Le mode ouvert peut interroger les anciens pairs, mais uniquement en tant que résultats non vérifiés.
- Sans coordinateurs configurés, un pair ne fait confiance qu’à ses propres documents. Les réseaux fermés peuvent utiliser
trust.signedOnly=true. Un réseau public a besoin de quelqu’un qui joue le rôle de coordinateur. - Le corpus est synthétique et petit (160 pages). Il reproduit les faiblesses constatées sur le réseau public. Il ne prédit pas l’ampleur de l’amélioration sur celui-ci.
- Le test NAT utilise un seul routeur MASQUERADE, pas de vrais routeurs domestiques. Le hole punching (DCUtR) est activé mais pas mesuré.
- Un auteur de confiance peut toujours signer un contenu faux (les tags et l’audit sont la réponse). Un pair qui stocke des documents peut toujours retenir des résultats.
Liens
- Code : pad01g/yacy_search_server, branche
improved-search(changements listés dans FORK.md) - Expériences et démo : pad01g/yacy-lab
- Document de conception complet (en japonais) : docs/trust-and-nat.md
- Upstream : yacy/yacy_search_server · forum YaCy