@startuml failure_native_one_down
title Native failure, with one still alive

participant "Indexer A" as IA
participant "Indexer B" as IB
participant "Native A (crashed)" as NA
participant "Native B (alive)" as NB
participant "Node" as N

note over IA, NB: Native State : IA, IB heartbeats to NA & NB

== Native A Failure ==
NA ->x IA: stream reset
NA ->x IB: stream reset
NA ->x N: stream reset (heartbeat Node → NA)

== Indexers side : replenishNativesFromPeers ==
note over IA: SendHeartbeat(NA) détecte reset\\nAfterDelete(NA)\\nStaticNatives = [NB] (still 1)

IA -> IA: replenishNativesFromPeers()\\nphase 1 : fetchNativeFromNatives

IA -> NB: GET /opencloud/native/peers/1.0
NB --> IA: GetPeersResponse{Peers:[NC]} /' new native if one known '/

alt NC disponible
    IA -> NC: SendHeartbeat /opencloud/heartbeat/1.0\\nSubscribe /opencloud/native/subscribe/1.0
    note over IA: StaticNatives = [NB, NC]\\nNative Pool restored.
else Aucun peer natif
    IA -> IA: fetchNativeFromIndexers()\\nAsk to any indexers their natives
    IB --> IA: GetNativesResponse{Natives:[]} /' IB also only got NB '/
    note over IA: Impossible to find a 2e native.\\nStaticNatives = [NB] (degraded but alive).
end

== Node side : alive indexers pool ==
note over N: Node heartbeats to IA & IB.\\nNA Failure does not affect indexers pool.\\nFuture Consensus did not use NB (1/1 vote = quorum OK).

N -> NB: /opencloud/native/consensus/1.0\\nConsensusRequest{Candidates:[IA,IB]}
NB --> N: ConsensusResponse{Trusted:[IA,IB]}
note over N: Consensus 1/1 alive natif → admit.\\nAuto downgrade of the consensus floor (alive majority).

== NB side : heartbeat to NA fails ==
note over NB: EnsureNativePeers / SendHeartbeat to NA\\nfail (sendHeartbeat err)\\n→ delete(StaticNatives[NA])\\nreplenishNativesFromPeers(NA) triggers

note over NB: Mesh natif downgraded to NB alone.\\Downgraded but functionnal.

@enduml
