@startuml title Indexer — Heartbeat double (Pair A + Pair B → Indexer partagé) participant "Node A" as NodeA participant "Node B" as NodeB participant "IndexerService (partagé)" as Indexer note over NodeA,NodeB: Chaque pair tick toutes les 20s par Pair A heartbeat NodeA -> Indexer: NewStream /opencloud/heartbeat/1.0 NodeA -> Indexer: json.Encode(Heartbeat A {Name, DID_A, PeerID_A, IndexersBinded}) Indexer -> Indexer: CheckHeartbeat(host, stream, streams, mu, maxNodes) note over Indexer: len(peers) < maxNodes ? Indexer -> Indexer: getBandwidthChallenge(512-2048 bytes, stream) Indexer -> NodeA: Write(random payload) NodeA -> Indexer: Echo(same payload) Indexer -> Indexer: Mesure round-trip → Mbps A Indexer -> Indexer: getDiversityRate(host, IndexersBinded_A) note over Indexer: /24 subnet diversity des indexeurs liés Indexer -> Indexer: ComputeIndexerScore(uptimeA%, MbpsA%, diversityA%) note over Indexer: Score = 0.4×uptime + 0.4×bpms + 0.2×diversity alt Score A < 75 Indexer -> NodeA: (close stream) else Score A >= 75 Indexer -> Indexer: StreamRecord[PeerID_A] = {DID_A, Heartbeat, UptimeTracker} end else Pair B heartbeat NodeB -> Indexer: NewStream /opencloud/heartbeat/1.0 NodeB -> Indexer: json.Encode(Heartbeat B {Name, DID_B, PeerID_B, IndexersBinded}) Indexer -> Indexer: CheckHeartbeat → getBandwidthChallenge Indexer -> NodeB: Write(random payload) NodeB -> Indexer: Echo(same payload) Indexer -> Indexer: ComputeIndexerScore(uptimeB%, MbpsB%, diversityB%) alt Score B >= 75 Indexer -> Indexer: StreamRecord[PeerID_B] = {DID_B, Heartbeat, UptimeTracker} end end par note over Indexer: Les deux pairs sont désormais\nenregistrés avec leurs streams actifs @enduml