363 lines
16 KiB
Plaintext
363 lines
16 KiB
Plaintext
|
|
================================================================================
|
|||
|
|
OC-DISCOVERY : ARCHITECTURE CIBLE — RÉSEAU DHT SANS NATIFS
|
|||
|
|
Vision d'évolution long terme, issue d'une analyse comparative
|
|||
|
|
================================================================================
|
|||
|
|
|
|||
|
|
Rédigé à partir de l'analyse de l'architecture actuelle et de la discussion
|
|||
|
|
comparative avec Tapestry, Kademlia, EigenTrust et les systèmes de réputation
|
|||
|
|
distribués.
|
|||
|
|
|
|||
|
|
Référence : DECENTRALIZED_SYSTEMS_COMPARISON.txt §9
|
|||
|
|
|
|||
|
|
|
|||
|
|
================================================================================
|
|||
|
|
1. MOTIVATION
|
|||
|
|
================================================================================
|
|||
|
|
|
|||
|
|
L'architecture actuelle (node → indexer → native indexer) est robuste et bien
|
|||
|
|
adaptée à une phase précoce du réseau. Ses limites à l'échelle sont :
|
|||
|
|
|
|||
|
|
- Pool de natives statique au démarrage → dépendance à la configuration
|
|||
|
|
- Cache local des natives = point de défaillance unique (perte = pool vide)
|
|||
|
|
- Consensus inter-natives bloquant (~7s) déclenché à chaque bootstrap node
|
|||
|
|
- État O(N indexers) par native → croît linéairement avec le réseau
|
|||
|
|
- Nœuds privilégiés structurellement → SPOFs relatifs
|
|||
|
|
|
|||
|
|
La cible décrite ici supprime la notion de native indexer en tant que tier
|
|||
|
|
architectural. Le réseau devient plat : indexers et nodes sont des acteurs
|
|||
|
|
de même nature, différenciés uniquement par leur rôle volontaire.
|
|||
|
|
|
|||
|
|
|
|||
|
|
================================================================================
|
|||
|
|
2. PRINCIPES FONDAMENTAUX
|
|||
|
|
================================================================================
|
|||
|
|
|
|||
|
|
P1. Aucun nœud n'est structurellement privilégié.
|
|||
|
|
P2. La confiance est un produit du temps et de la vérification, pas d'un arbitre.
|
|||
|
|
P3. Les claims d'un acteur sont vérifiables indépendamment par tout pair.
|
|||
|
|
P4. La réputation émerge du comportement collectif, pas d'un signalement central.
|
|||
|
|
P5. La DHT est une infrastructure neutre — elle stocke des faits, pas des jugements.
|
|||
|
|
P6. La configuration statique n'existe plus au runtime — seulement au bootstrap.
|
|||
|
|
|
|||
|
|
|
|||
|
|
================================================================================
|
|||
|
|
3. RÔLES
|
|||
|
|
================================================================================
|
|||
|
|
|
|||
|
|
3.1 Node
|
|||
|
|
--------
|
|||
|
|
Consommateur du réseau. Démarre, sélectionne un pool d'indexers via DHT,
|
|||
|
|
heartbeat ses indexers, accumule des scores localement. Ne publie rien en
|
|||
|
|
routine. Participe aux challenges de consensus à la demande.
|
|||
|
|
|
|||
|
|
3.2 Indexer
|
|||
|
|
-----------
|
|||
|
|
Acteur volontaire. S'inscrit dans la DHT à la naissance, maintient son record,
|
|||
|
|
sert le trafic des nodes (heartbeat, Publish, Get). Déclare ses métriques dans
|
|||
|
|
chaque réponse heartbeat. Maintient un score agrégé depuis ses nodes connectés.
|
|||
|
|
|
|||
|
|
Différence avec l'actuel : l'indexer n'a plus de lien avec une native.
|
|||
|
|
Il est autonome. Son existence dans le réseau est prouvée par son record DHT
|
|||
|
|
et par les nodes qui le contactent directement.
|
|||
|
|
|
|||
|
|
3.3 Nœud DHT infrastructure (ex-native)
|
|||
|
|
----------------------------------------
|
|||
|
|
N'importe quel nœud suffisamment stable peut maintenir la DHT sans être un
|
|||
|
|
indexer. C'est une configuration, pas un type architectural : `dht_mode: server`.
|
|||
|
|
Ces nœuds maintiennent les k-buckets Kademlia et stockent les records des
|
|||
|
|
indexers. Ils ne connaissent pas le trafic node↔indexer et ne l'orchestrent pas.
|
|||
|
|
|
|||
|
|
|
|||
|
|
================================================================================
|
|||
|
|
4. BOOTSTRAP D'UN NODE
|
|||
|
|
================================================================================
|
|||
|
|
|
|||
|
|
4.1 Entrée dans le réseau
|
|||
|
|
-------------------------
|
|||
|
|
Le node démarre avec 1 à 3 adresses de nœuds DHT connus (bootstrap peers).
|
|||
|
|
Ce sont les seules informations statiques nécessaires. Ces peers n'ont pas de
|
|||
|
|
rôle sémantique — ils servent uniquement à entrer dans l'overlay DHT.
|
|||
|
|
|
|||
|
|
4.2 Découverte du pool d'indexers
|
|||
|
|
----------------------------------
|
|||
|
|
|
|||
|
|
Node → DHT.FindProviders(hash("/opencloud/indexers"))
|
|||
|
|
→ reçoit une liste de N candidats avec leurs records
|
|||
|
|
|
|||
|
|
Sélection du pool initial :
|
|||
|
|
|
|||
|
|
1. Filtre latence : ping < seuil → proximité réseau réelle
|
|||
|
|
2. Filtre fill rate : préférer les indexers moins chargés
|
|||
|
|
3. Tirage pondéré : probabilité ∝ (1 - fill_rate), courbe w(F) = F×(1-F)
|
|||
|
|
indexer à 20% charge → très probable
|
|||
|
|
indexer à 80% charge → peu probable
|
|||
|
|
4. Filtre diversité : subnet /24 différent pour chaque entrée du pool
|
|||
|
|
|
|||
|
|
Aucun consensus nécessaire à cette étape. Le node démarre avec une tolérance
|
|||
|
|
basse (voir §7) — il accepte des indexers imparfaits et les évalue au fil du temps.
|
|||
|
|
|
|||
|
|
|
|||
|
|
================================================================================
|
|||
|
|
5. REGISTRATION D'UN INDEXER DANS LA DHT
|
|||
|
|
================================================================================
|
|||
|
|
|
|||
|
|
À la naissance, l'indexer publie son record DHT :
|
|||
|
|
|
|||
|
|
clé : hash("/opencloud/indexers") ← clé fixe, connue de tous
|
|||
|
|
valeur: {
|
|||
|
|
multiaddr : <adresse réseau>,
|
|||
|
|
region : <subnet /24>,
|
|||
|
|
capacity : <maxNodesConn>,
|
|||
|
|
fill_rate : <float 0-1>, ← auto-déclaré, vérifiable
|
|||
|
|
peer_count : <int>, ← auto-déclaré, vérifiable
|
|||
|
|
peers : [hash(nodeID1), ...], ← liste hashée des nodes connectés
|
|||
|
|
born_at : <timestamp>,
|
|||
|
|
sig : <signature clé indexer>, ← non-forgeable (PSK context)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Le record est rafraîchi toutes les ~60s (avant expiration du TTL).
|
|||
|
|
Si l'indexer tombe : TTL expire → disparaît de la DHT automatiquement.
|
|||
|
|
|
|||
|
|
La peer list est hashée pour la confidentialité mais reste vérifiable :
|
|||
|
|
un challenger peut demander directement à un node s'il est connecté à cet indexer.
|
|||
|
|
|
|||
|
|
|
|||
|
|
================================================================================
|
|||
|
|
6. PROTOCOLE HEARTBEAT — QUESTION ET RÉPONSE
|
|||
|
|
================================================================================
|
|||
|
|
|
|||
|
|
Le heartbeat devient bidirectionnel : le node pose des questions, l'indexer
|
|||
|
|
répond avec ses déclarations courantes.
|
|||
|
|
|
|||
|
|
6.1 Structure
|
|||
|
|
-------------
|
|||
|
|
|
|||
|
|
Node → Indexer :
|
|||
|
|
{
|
|||
|
|
ts : now,
|
|||
|
|
challenge : <optionnel, voir §8>
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Indexer → Node :
|
|||
|
|
{
|
|||
|
|
ts : now,
|
|||
|
|
fill_rate : 0.42,
|
|||
|
|
peer_count : 87,
|
|||
|
|
cached_score : 0.74, ← score agrégé depuis tous ses nodes connectés
|
|||
|
|
challenge_response : {...} ← si challenge présent dans la requête
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Le heartbeat normal (sans challenge) est quasi-identique à l'actuel en poids.
|
|||
|
|
Le cached_score indexer est mis à jour progressivement par les feedbacks reçus.
|
|||
|
|
|
|||
|
|
6.2 Le cached_score de l'indexer
|
|||
|
|
---------------------------------
|
|||
|
|
L'indexer agrège les scores que ses nodes connectés lui communiquent
|
|||
|
|
(implicitement via le fait qu'ils restent connectés, ou explicitement lors
|
|||
|
|
d'un consensus). Ce score lui donne une vision de sa propre qualité réseau.
|
|||
|
|
|
|||
|
|
Un node peut comparer son score local de l'indexer avec le cached_score déclaré.
|
|||
|
|
Une forte divergence est un signal d'alerte.
|
|||
|
|
|
|||
|
|
Score local node : 0.40 ← cet indexer est médiocre pour moi
|
|||
|
|
Cached score : 0.91 ← il se prétend excellent globalement
|
|||
|
|
→ déclenche un challenge de vérification
|
|||
|
|
|
|||
|
|
|
|||
|
|
================================================================================
|
|||
|
|
7. MODÈLE DE CONFIANCE PROGRESSIVE
|
|||
|
|
================================================================================
|
|||
|
|
|
|||
|
|
7.1 Cycle de vie d'un node
|
|||
|
|
---------------------------
|
|||
|
|
|
|||
|
|
Naissance
|
|||
|
|
→ tolérance basse : accepte presque n'importe quel indexer du DHT
|
|||
|
|
→ switching cost faible : peu de contexte accumulé
|
|||
|
|
→ minScore ≈ 20% (dynamicMinScore existant, conservé)
|
|||
|
|
|
|||
|
|
Quelques heures
|
|||
|
|
→ uptime s'accumule sur chaque indexer connu
|
|||
|
|
→ scores se stabilisent
|
|||
|
|
→ seuil de remplacement qui monte progressivement
|
|||
|
|
|
|||
|
|
Long terme (jours)
|
|||
|
|
→ pool stable, confiance élevée sur les indexers connus
|
|||
|
|
→ switching coûteux mais déclenché sur déception franche
|
|||
|
|
→ minScore ≈ 80% (maturité)
|
|||
|
|
|
|||
|
|
7.2 Modèle sous-jacent : beta distribution implicite
|
|||
|
|
------------------------------------------------------
|
|||
|
|
|
|||
|
|
α = succès cumulés (heartbeats OK, probes OK, challenges réussis)
|
|||
|
|
β = échecs cumulés (timeouts, probes échoués, challenges ratés)
|
|||
|
|
|
|||
|
|
confiance = α / (α + β)
|
|||
|
|
|
|||
|
|
Nouveau indexer : α=0, β=0 → prior neutre, tolérance basse
|
|||
|
|
Après 10 jours : α élevé → confiance stable, seuil de switch élevé
|
|||
|
|
Déception franche : β monte → confiance chute → switch déclenché
|
|||
|
|
|
|||
|
|
7.3 Ce que "décevoir" signifie
|
|||
|
|
--------------------------------
|
|||
|
|
|
|||
|
|
Heartbeat rate → trop de timeouts → fiabilité en baisse
|
|||
|
|
Bandwidth probe → chute sous déclaré → dégradation ou mensonge
|
|||
|
|
Fill rate réel → supérieur au déclaré → indexer surchargé ou malhonnête
|
|||
|
|
Challenge échoué → peer déclaré absent du réseau → claim invalide
|
|||
|
|
Latence → dérive progressive → qualité réseau dégradée
|
|||
|
|
Cached_score gonflé → divergence forte avec score local → suspicion
|
|||
|
|
|
|||
|
|
|
|||
|
|
================================================================================
|
|||
|
|
8. VÉRIFICATION DES CLAIMS — TROIS COUCHES
|
|||
|
|
================================================================================
|
|||
|
|
|
|||
|
|
8.1 Couche 1 : passive (chaque heartbeat, 60s)
|
|||
|
|
-----------------------------------------------
|
|||
|
|
Mesures automatiques, zéro coût supplémentaire.
|
|||
|
|
|
|||
|
|
- RTT du heartbeat → latence directe
|
|||
|
|
- fill_rate déclaré → tiny payload dans la réponse
|
|||
|
|
- peer_count déclaré → tiny payload
|
|||
|
|
- cached_score indexer → comparé au score local
|
|||
|
|
|
|||
|
|
8.2 Couche 2 : sampling actif (1 heartbeat sur N)
|
|||
|
|
--------------------------------------------------
|
|||
|
|
Vérifications périodiques, asynchrones, légères.
|
|||
|
|
|
|||
|
|
Tous les 5 HB (~5min) : spot-check 1 peer aléatoire (voir §8.4)
|
|||
|
|
Tous les 10 HB (~10min): vérification diversité subnet (lookups DHT légers)
|
|||
|
|
Tous les 15 HB (~15min): bandwidth probe (transfert réel, protocole dédié)
|
|||
|
|
|
|||
|
|
8.3 Couche 3 : consensus (événementiel)
|
|||
|
|
-----------------------------------------
|
|||
|
|
Déclenché sur : admission d'un nouvel indexer dans le pool, ou suspicion détectée.
|
|||
|
|
|
|||
|
|
Node sélectionne une claim vérifiable de l'indexer cible X
|
|||
|
|
Node vérifie lui-même
|
|||
|
|
Node demande à ses indexers de confiance : "vérifiez cette claim sur X"
|
|||
|
|
Chaque indexer vérifie indépendamment
|
|||
|
|
Convergence des résultats → X est honnête → admission
|
|||
|
|
Divergence → X est suspect → rejet ou probation
|
|||
|
|
|
|||
|
|
Le consensus est léger : quelques contacts out-of-band, pas de round bloquant.
|
|||
|
|
Il n'est pas continu — il est événementiel.
|
|||
|
|
|
|||
|
|
8.4 Vérification out-of-band (pas de DHT writes par les nodes)
|
|||
|
|
----------------------------------------------------------------
|
|||
|
|
Les nodes ne publient PAS de contact records continus dans la DHT.
|
|||
|
|
Cela éviterait N×M records à rafraîchir (coût DHT élevé à l'échelle).
|
|||
|
|
|
|||
|
|
À la place, lors d'un challenge :
|
|||
|
|
|
|||
|
|
Challenger sélectionne 2-3 peers dans la peer list déclarée par X
|
|||
|
|
→ contacte ces peers directement : "es-tu connecté à indexer X ?"
|
|||
|
|
→ réponse directe (out-of-band, pas via DHT)
|
|||
|
|
→ vérification sans écriture DHT
|
|||
|
|
|
|||
|
|
L'indexer ne peut pas faire répondre "oui" à des peers qui ne lui sont pas
|
|||
|
|
connectés. La vérification est non-falsifiable et sans coût DHT.
|
|||
|
|
|
|||
|
|
8.5 Pourquoi X ne peut pas tricher
|
|||
|
|
------------------------------------
|
|||
|
|
X ne peut pas coordonner des réponses différentes vers des challengers
|
|||
|
|
simultanés. Chaque challenger contacte indépendamment les mêmes peers.
|
|||
|
|
Si X ment sur sa peer list :
|
|||
|
|
|
|||
|
|
- Challenger A contacte peer P → "non, pas connecté à X"
|
|||
|
|
- Challenger B contacte peer P → "non, pas connecté à X"
|
|||
|
|
- Consensus : X ment → score chute chez tous les challengers
|
|||
|
|
- Effet réseau : progressivement, X perd ses connections
|
|||
|
|
- Peer list DHT se vide → claims futures encore moins crédibles
|
|||
|
|
|
|||
|
|
|
|||
|
|
================================================================================
|
|||
|
|
9. EFFET RÉSEAU SANS SIGNALEMENT CENTRAL
|
|||
|
|
================================================================================
|
|||
|
|
|
|||
|
|
Un node qui pénalise un indexer n'envoie aucun "rapport" à quiconque.
|
|||
|
|
Ses actions locales produisent l'effet réseau par agrégation :
|
|||
|
|
|
|||
|
|
Node baisse le score de X → X reçoit moins de trafic de ce node
|
|||
|
|
Node switche vers Y → X perd un client
|
|||
|
|
Node refuse les challenges X → X ne peut plus participer aux consensus
|
|||
|
|
|
|||
|
|
Si 200 nodes font pareil :
|
|||
|
|
|
|||
|
|
X perd la majorité de ses connections
|
|||
|
|
Sa peer list DHT se vide (peers contactés directement disent "non")
|
|||
|
|
Son cached_score s'effondre (peu de nodes restent)
|
|||
|
|
Les nouveaux nodes qui voient X dans la DHT obtiennent des challenges échoués
|
|||
|
|
X est naturellement exclu sans aucune décision centrale
|
|||
|
|
|
|||
|
|
Inversement, un indexer honnête voit ses scores monter sur tous ses nodes
|
|||
|
|
connectés, sa peer list se densifier, ses challenges réussis systématiquement.
|
|||
|
|
Sa réputation est un produit observable et vérifiable.
|
|||
|
|
|
|||
|
|
|
|||
|
|
================================================================================
|
|||
|
|
10. RÉSUMÉ DE L'ARCHITECTURE
|
|||
|
|
================================================================================
|
|||
|
|
|
|||
|
|
DHT → annuaire neutre, vérité des records indexers
|
|||
|
|
maintenu par tout nœud stable (dht_mode: server)
|
|||
|
|
|
|||
|
|
Indexer → acteur volontaire, s'inscrit, maintient ses claims,
|
|||
|
|
sert le trafic, accumule son propre score agrégé
|
|||
|
|
|
|||
|
|
Node → consommateur, score passif + sampling + consensus léger,
|
|||
|
|
confiance progressive, switching adaptatif
|
|||
|
|
|
|||
|
|
Heartbeat → métronome 60s + vecteur de déclarations légères + challenge optionnel
|
|||
|
|
|
|||
|
|
Consensus → événementiel, multi-challengers indépendants,
|
|||
|
|
vérification out-of-band sur claims DHT
|
|||
|
|
|
|||
|
|
Confiance → beta implicite, progressive, switching cost croissant avec l'âge
|
|||
|
|
|
|||
|
|
Réputation → émerge du comportement collectif, aucun arbitre central
|
|||
|
|
|
|||
|
|
Bootstrap → 1-3 peers DHT connus → seule configuration statique nécessaire
|
|||
|
|
|
|||
|
|
|
|||
|
|
================================================================================
|
|||
|
|
11. TRAJECTOIRE DE MIGRATION
|
|||
|
|
================================================================================
|
|||
|
|
|
|||
|
|
Phase 1 (actuel)
|
|||
|
|
Natives statiques, pool indexers dynamique, consensus inter-natives
|
|||
|
|
→ robuste, adapté à la phase précoce
|
|||
|
|
|
|||
|
|
Phase 2 (intermédiaire)
|
|||
|
|
Pool de natives dynamique via DHT (bootstrap + gossip)
|
|||
|
|
Même protocole natif, juste la découverte devient dynamique
|
|||
|
|
→ supprime la dépendance à la configuration statique des natives
|
|||
|
|
→ voir DECENTRALIZED_SYSTEMS_COMPARISON.txt §9.2
|
|||
|
|
|
|||
|
|
Phase 3 (cible)
|
|||
|
|
Architecture décrite dans ce document
|
|||
|
|
Natives disparaissent en tant que tier architectural
|
|||
|
|
DHT = infrastructure, indexers = acteurs autonomes
|
|||
|
|
Scoring et consensus entièrement côté node
|
|||
|
|
→ aucun nœud privilégié, scalabilité O(log N)
|
|||
|
|
|
|||
|
|
La migration Phase 2 → Phase 3 est une refonte du plan de contrôle.
|
|||
|
|
Le plan de données (heartbeat node↔indexer, Publish, Get) est inchangé.
|
|||
|
|
Les primitives libp2p (Kademlia DHT, GossipSub) sont déjà présentes.
|
|||
|
|
|
|||
|
|
|
|||
|
|
================================================================================
|
|||
|
|
12. PROPRIÉTÉS DU SYSTÈME CIBLE
|
|||
|
|
================================================================================
|
|||
|
|
|
|||
|
|
Scalabilité O(log N) — routage DHT Kademlia
|
|||
|
|
Résilience Pas de SPOF structurel, TTL = seule source de vérité
|
|||
|
|
Confiance Progressive, vérifiable, émergente
|
|||
|
|
Sybil resistance PSK — seuls les nœuds avec la clé peuvent publier
|
|||
|
|
Cold start Tolérance basse initiale, montée progressive (existant)
|
|||
|
|
Honnêteté Claims vérifiables out-of-band, non-falsifiables
|
|||
|
|
Décentralisation Aucun nœud ne connaît l'état global complet
|
|||
|
|
|
|||
|
|
================================================================================
|