@startuml indexer_heartbeat title Indexer — Heartbeat node → indexer (score on 5 metrics) participant "Node A" as NodeA participant "Node B" as NodeB participant "IndexerService" as Indexer note over NodeA,NodeB: Every node tick every 20s (SendHeartbeat) par Node A heartbeat NodeA -> Indexer: NewStream /opencloud/heartbeat/1.0 NodeA -> Indexer: stream.Encode(Heartbeat{Name, PeerID_A, IndexersBinded, Record}) Indexer -> Indexer: CheckHeartbeat(host, stream, dec, streams, mu, maxNodes) note over Indexer: len(h.Network().Peers()) >= maxNodes → reject Indexer -> Indexer: getBandwidthChallengeRate(host, remotePeer, 512-2048B) Indexer -> Indexer: getOwnDiversityRate(host)\\nh.Network().Peers() + Peerstore.Addrs()\\n→ ratio /24 subnets distincts Indexer -> Indexer: fillRate = len(h.Network().Peers()) / maxNodes Indexer -> Indexer: Retrieve existing UptimeTracker\\noldTracker.RecordHeartbeat()\\n→ TotalOnline += gap si gap ≤ 120s\\nuptimeRatio = TotalOnline / time.Since(FirstSeen) Indexer -> Indexer: ComputeIndexerScore(\\n uptimeRatio, bpms, diversity,\\n latencyScore, fillRate\\n)\\nScore = (0.20×U + 0.20×B + 0.20×D + 0.15×L + 0.25×F) × 100 Indexer -> Indexer: dynamicMinScore(age)\\n= 20 + 60×(hours/24), max 80 alt Score A < dynamicMinScore(age) Indexer -> NodeA: (close stream — "not enough trusting value") else Score A >= dynamicMinScore(age) Indexer -> Indexer: streams[PeerID_A].HeartbeatStream = hb.Stream\\nstreams[PeerID_A].HeartbeatStream.UptimeTracker = oldTracker\\nstreams[PeerID_A].LastScore = hb.Score note over Indexer: AfterHeartbeat → republish PeerRecord on DHT end else Node B heartbeat NodeB -> Indexer: NewStream /opencloud/heartbeat/1.0 NodeB -> Indexer: stream.Encode(Heartbeat{Name, PeerID_B, IndexersBinded, Record}) Indexer -> Indexer: CheckHeartbeat → getBandwidthChallengeRate\\n→ getOwnDiversityRate → ComputeIndexerScore(5 composants) alt Score B >= dynamicMinScore(age) Indexer -> Indexer: streams[PeerID_B] subscribed + LastScore updated end end par note over Indexer: GC ticker 30s — gc()\\nnow.After(Expiry) où Expiry = lastHBTime + 2min\\n→ AfterDelete(pid, name, did) @enduml