Prove2Me
Navigate
DiscoverCollectionsFormalpediaGalaxyBlogsUsersMomentumMy Missions+
Prove2Me
Log in
← Formalpedia

Bootstrapping with different partial views gives compatible configurations: two configurations of ClusterBootstrapService's form meet the condition of InitCompatible

Proved
OpenSearchSafety.bootstrap_compatible

by pitimespi · Nov 1, 2026 · Mathlib 0df444a (Lean v4.33.1)

cluster-coordinationconsensuscrash-recoveryelasticsearchformal-verificationinductive-invariantleader-electionmessage-passingopensearchpaxosraftreconfigurationsafetyzen2

Let eligible : NodeId → Bool, let k be the number of bootstrap requirements, and let resolve placeholder : Nat → NodeId be such that, for all i, j < k: resolve i = resolve j → i = j (each requirement names its own node); placeholder i = placeholder j → i = j; placeholder i ≠ resolve j; and eligible (placeholder i) = false. Let X, Y be configurations and mX, mY : Nat → Bool with BootstrapConfig k resolve placeholder mX X and BootstrapConfig k resolve placeholder mY Y. Then for all lists q₁, q₂ of node ids with eligible x = true for every x in q₁ and in q₂, X.hasQuorum q₁ = true and Y.hasQuorum q₂ = true, some x is in both q₁ and q₂.

Each bootstrapping node uses the configuration of the requirements it has matched, with placeholders for the others, and needs more than half matched (ClusterBootstrapService, CBS:176-215, 259-273, 306-335). Different nodes may match different requirements and bootstrap with different configurations; the theorem shows that any two such configurations, built from the same requirements with the same resolve and placeholder (every requirement resolves to the same node everywhere), meet the condition of Params.InitCompatible, the hypothesis of the safety theorems. The theorem does not mention Params: P.InitCompatible follows for every P with P.eligible = eligible whose bootstrap configurations are all of this form, but no theorem states this, and the model does not tie P.initConfs to BootstrapConfig.

Preamble
import Definitions.Def_OpenSearchSafety
open OpenSearchCoordination OpenSearchSafety
Formal statement
theorem OpenSearchSafety.bootstrap_compatible (eligible : NodeId → Bool) (k : Nat) (resolve placeholder : Nat → NodeId)
    (hres : ∀ i j, i < k → j < k → resolve i = resolve j → i = j)
    (hph : ∀ i j, i < k → j < k → placeholder i = placeholder j → i = j)
    (hdis : ∀ i j, i < k → j < k → placeholder i ≠ resolve j)
    (hphe : ∀ i, i < k → eligible (placeholder i) = false)
    (X Y : VotingConfiguration) (mX mY : Nat → Bool)
    (hX : BootstrapConfig k resolve placeholder mX X) (hY : BootstrapConfig k resolve placeholder mY Y)
    (q₁ q₂ : List NodeId) (hq₁ : ∀ x ∈ q₁, eligible x = true) (hq₂ : ∀ x ∈ q₂, eligible x = true)
    (h₁ : X.hasQuorum q₁ = true) (h₂ : Y.hasQuorum q₂ = true) : ∃ x, x ∈ q₁ ∧ x ∈ q₂ := by sorry
Source
OpenSearch b2a54ba5c0defba3d1d20423e911d4bf920bab19: CoordinationState (the per-node handlers as transcribed by the OpenSearchCoordination mission; handleCommit lines 523-573, isPublishQuorum 156-158, invariant 601-611): https://github.com/opensearch-project/OpenSearch/blob/b2a54ba5c0defba3d1d20423e911d4bf920bab19/server/src/main/java/org/opensearch/cluster/coordination/CoordinationState.java; Coordinator (the calls into CoordinationState: handleApplyCommit lines 419-452, handlePublishRequest 454-526, startElection 583-602, abdicateTo 604-620, ensureTermAtLeast 635-641, joinLeaderInTerm 643-657, setInitialConfiguration 1101-1161, handleJoin 1267-1299, publish 1350 with the handleClientValue call at 1404, CoordinatorPublication.handlePublishResponse 1871-1876): https://github.com/opensearch-project/OpenSearch/blob/b2a54ba5c0defba3d1d20423e911d4bf920bab19/server/src/main/java/org/opensearch/cluster/coordination/Coordinator.java; ClusterBootstrapService (requirements 146-157, onFoundPeersUpdated 176-215, startBootstrap 259-273, checkRequirements 306-335): https://github.com/opensearch-project/OpenSearch/blob/b2a54ba5c0defba3d1d20423e911d4bf920bab19/server/src/main/java/org/opensearch/cluster/coordination/ClusterBootstrapService.java#L146-L335; GatewayMetaState.start (persisted state loaded and rebuilt 141-231, remote restore 169-188, synchronous and asynchronous persistence 199-207, nodes without the cluster-manager and data roles 232-255): https://github.com/opensearch-project/OpenSearch/blob/b2a54ba5c0defba3d1d20423e911d4bf920bab19/server/src/main/java/org/opensearch/gateway/GatewayMetaState.java#L126-L257; PersistedStateRegistry.addPersistedState (lines 38-41): https://github.com/opensearch-project/OpenSearch/blob/b2a54ba5c0defba3d1d20423e911d4bf920bab19/server/src/main/java/org/opensearch/cluster/coordination/PersistedStateRegistry.java#L38-L41; ElectionStrategy (DEFAULT_INSTANCE lines 46-59): https://github.com/opensearch-project/OpenSearch/blob/b2a54ba5c0defba3d1d20423e911d4bf920bab19/server/src/main/java/org/opensearch/cluster/coordination/ElectionStrategy.java#L44-L110; the randomized safety test CoordinationStateTestCluster (reboot 156-193, runRandomly 279-329, invariant 331-370): https://github.com/opensearch-project/OpenSearch/blob/b2a54ba5c0defba3d1d20423e911d4bf920bab19/test/framework/src/main/java/org/opensearch/cluster/coordination/CoordinationStateTestCluster.java and CoordinationStateTests.testSafety (lines 917-924): https://github.com/opensearch-project/OpenSearch/blob/b2a54ba5c0defba3d1d20423e911d4bf920bab19/server/src/test/java/org/opensearch/cluster/coordination/CoordinationStateTests.java#L917-L924; for comparison, Elastic's TLA+ model ZenWithTerms (HandleCommit 241-250, CommittedValuesDescendantsFromInitialValue 338-347, CommitHasQuorumVsPreviousCommittedConfiguration 349-358): https://github.com/elastic/elasticsearch-formal-models/blob/ca30663506a7e18de9d23fabbc70a369903d7c98/ZenWithTerms/tla/ZenWithTerms.tla
Read-back

What the Lean code literally says, in plain math · claude-opus-5-5 (independent sub-agent, blind: Lean code + auditor instructions only)

Read-back: OpenSearchSafety.bootstrap_compatible

Notions used

Sources and conventions. Rendered from Definitions/Def_OpenSearchCoordination.lean (configurations, hasQuorum), Definitions/Def_OpenSearchSafety.lean (BootstrapConfig) and the stub Theorems/Thm_OpenSearchSafety_bootstrap_compatible.lean; Java line numbers refer to OpenSearch b2a54ba5c0defba3d1d20423e911d4bf920bab19 (CM = CoordinationMetadata.java, CBS = ClusterBootstrapService.java), checked against the local checkout. The statement uses no cluster, parameter record or coordination state.

Nodes, configurations, quorums. Node ids are natural numbers. A discovery node ddd is a pair (d.id,d.cm)(d.\mathrm{id}, d.\mathrm{cm})(d.id,d.cm) of an id and a Boolean (isClusterManagerNode). A voting configuration ccc is a strictly increasing list of node ids, hence duplicate-free; two configurations are equal exactly when they have the same members. ∅\emptyset∅ denotes the empty configuration EMPTY_CONFIG; "ccc is empty" means its list is empty. For a configuration ccc and any list qqq of ids,

hasQuorum(c,q)  ⟺  2⋅∣{x∈c:x∈q}∣>∣c∣,\mathrm{hasQuorum}(c, q) \iff 2\cdot\big|\{x \in c : x \in q\}\big| > |c| ,hasQuorum(c,q)⟺2⋅​{x∈c:x∈q}​>∣c∣,

so the empty configuration has no quorum, and ids of qqq outside ccc as well as repetitions in qqq are irrelevant. Transcription check: matches VotingConfiguration.hasQuorum (CM:405-409).

Bootstrap configurations. For k∈Nk \in \mathbb{N}k∈N, functions resolve,placeholder:N→N\mathrm{resolve}, \mathrm{placeholder} : \mathbb{N} \to \mathbb{N}resolve,placeholder:N→N, matched:N→Bool\mathrm{matched} : \mathbb{N} \to \mathrm{Bool}matched:N→Bool and a configuration XXX, BootstrapConfig(k,resolve,placeholder,matched,X)\mathrm{BootstrapConfig}(k, \mathrm{resolve}, \mathrm{placeholder}, \mathrm{matched}, X)BootstrapConfig(k,resolve,placeholder,matched,X) holds when

  1. for every id xxx: x∈X  ⟺  ∃i<k, x=rix \in X \iff \exists i < k,\ x = r_ix∈X⟺∃i<k, x=ri​, where ri=resolve(i)r_i = \mathrm{resolve}(i)ri​=resolve(i) if matched(i)\mathrm{matched}(i)matched(i) and ri=placeholder(i)r_i = \mathrm{placeholder}(i)ri​=placeholder(i) otherwise; and
  2. 2⋅∣{i<k:matched(i)}∣>k2 \cdot \big|\{ i < k : \mathrm{matched}(i)\}\big| > k2⋅​{i<k:matched(i)}​>k.

(The definition fixes the member set of XXX only; values of the three functions at arguments ≥k\ge k≥k are irrelevant.) Citation check: in ClusterBootstrapService.java, checkRequirements (CBS:306-335) matches each requirement against the discovered nodes, throwing if a requirement matches several nodes or a node several requirements; onFoundPeersUpdated (CBS:176-215) bootstraps when the matched nodes outnumber half the requirements (CBS:211, the same count as conjunct 2) and the local node is among them (CBS:202-209, not represented); startBootstrap (CBS:259-273) builds the configuration from the matched nodes' ids and "{bootstrap-placeholder}-" + requirement for the unmatched requirements, as in conjunct 1. The best-effort bootstrap without configured requirements (scheduleUnconfiguredBootstrap, CBS:217-250, startBootstrap(discoveredNodes, emptyList())) is not described by this definition.

1. Declaration

Binders and hypotheses, in order:

  1. a function eligible:N→Bool\mathrm{eligible} : \mathbb{N} \to \mathrm{Bool}eligible:N→Bool (a free parameter, not a parameter record);
  2. k∈Nk \in \mathbb{N}k∈N and functions resolve,placeholder:N→N\mathrm{resolve}, \mathrm{placeholder} : \mathbb{N} \to \mathbb{N}resolve,placeholder:N→N;
  3. hres : for all i,j<ki, j < ki,j<k, resolve(i)=resolve(j)⇒i=j\mathrm{resolve}(i) = \mathrm{resolve}(j) \Rightarrow i = jresolve(i)=resolve(j)⇒i=j;
  4. hph : for all i,j<ki, j < ki,j<k, placeholder(i)=placeholder(j)⇒i=j\mathrm{placeholder}(i) = \mathrm{placeholder}(j) \Rightarrow i = jplaceholder(i)=placeholder(j)⇒i=j;
  5. hdis : for all i,j<ki, j < ki,j<k, placeholder(i)≠resolve(j)\mathrm{placeholder}(i) \ne \mathrm{resolve}(j)placeholder(i)=resolve(j);
  6. hphe : for all i<ki < ki<k, eligible(placeholder(i))=\mathrm{eligible}(\mathrm{placeholder}(i)) =eligible(placeholder(i))= false;
  7. configurations XXX, YYY and functions mX,mY:N→Boolm_X, m_Y : \mathbb{N} \to \mathrm{Bool}mX​,mY​:N→Bool;
  8. hX : BootstrapConfig(k,resolve,placeholder,mX,X)\mathrm{BootstrapConfig}(k, \mathrm{resolve}, \mathrm{placeholder}, m_X, X)BootstrapConfig(k,resolve,placeholder,mX​,X) and hY : BootstrapConfig(k,resolve,placeholder,mY,Y)\mathrm{BootstrapConfig}(k, \mathrm{resolve}, \mathrm{placeholder}, m_Y, Y)BootstrapConfig(k,resolve,placeholder,mY​,Y);
  9. lists q1q_1q1​, q2q_2q2​ of ids, with hq₁ : every x∈q1x \in q_1x∈q1​ has eligible(x)=\mathrm{eligible}(x) =eligible(x)= true, and hq₂ : every x∈q2x \in q_2x∈q2​ has eligible(x)=\mathrm{eligible}(x) =eligible(x)= true;
  10. h₁ : hasQuorum(X,q1)\mathrm{hasQuorum}(X, q_1)hasQuorum(X,q1​) and h₂ : hasQuorum(Y,q2)\mathrm{hasQuorum}(Y, q_2)hasQuorum(Y,q2​).

Conclusion. ∃x, x∈q1∧x∈q2\exists x,\ x \in q_1 \wedge x \in q_2∃x, x∈q1​∧x∈q2​.

Unfolded: the members of XXX are riXr^X_iriX​ for i<ki < ki<k — resolve(i)\mathrm{resolve}(i)resolve(i) if mX(i)m_X(i)mX​(i), placeholder(i)\mathrm{placeholder}(i)placeholder(i) otherwise — and by hres, hph, hdis these are pairwise distinct, so XXX has exactly kkk members, and likewise YYY. h₁ says that more than k/2k/2k/2 members of XXX occur in q1q_1q1​; by hq₁ and hphe these are all of the form resolve(i)\mathrm{resolve}(i)resolve(i). The conclusion is that q1q_1q1​ and q2q_2q2​ share an id.

2. What is not constrained

  • No parameter record appears. The conclusion has the shape of InitCompatible\mathrm{InitCompatible}InitCompatible for the family of configurations {X:∃m, BootstrapConfig(k,resolve,placeholder,m,X)}\{X : \exists m,\ \mathrm{BootstrapConfig}(k, \mathrm{resolve}, \mathrm{placeholder}, m, X)\}{X:∃m, BootstrapConfig(k,resolve,placeholder,m,X)} with eligible\mathrm{eligible}eligible as eligibility, but the statement does not link that family to any P.initConfsP.\mathrm{initConfs}P.initConfs, nor eligible\mathrm{eligible}eligible to any P.eligP.\mathrm{elig}P.elig; every cluster-level safety theorem keeps InitCompatible(P)\mathrm{InitCompatible}(P)InitCompatible(P) as an assumption.
  • XXX and YYY share kkk, resolve\mathrm{resolve}resolve and placeholder\mathrm{placeholder}placeholder: the two bootstrapping nodes have the same requirement list and resolve every matched requirement to the same node; they may differ only in which requirements they matched. In Java a requirement is matched by node name or address at each bootstrapping node (CBS:300-304), so this is an assumption (the module docstring lists "bootstrap requirements name the same node at every node").
  • Resolved ids are not required to be eligible; if, for instance, no resolved id is eligible, h₁ cannot hold and the statement is vacuous. For k=0k = 0k=0, conjunct 2 of BootstrapConfig\mathrm{BootstrapConfig}BootstrapConfig (0>00 > 00>0) fails, so the statement is vacuous.
  • Conjunct 2 of BootstrapConfig\mathrm{BootstrapConfig}BootstrapConfig is not needed for the conclusion: given hq₁, hphe and injectivity, h₁ already forces more than k/2k/2k/2 indices i<ki < ki<k with mX(i)m_X(i)mX​(i) and resolve(i)∈q1\mathrm{resolve}(i) \in q_1resolve(i)∈q1​; two such sets of more than k/2k/2k/2 ids among the kkk ids resolve(0),…,resolve(k−1)\mathrm{resolve}(0), \dots, \mathrm{resolve}(k-1)resolve(0),…,resolve(k−1) must meet.
  • Not vacuous: k=1k = 1k=1, resolve(0)=1\mathrm{resolve}(0) = 1resolve(0)=1, placeholder(0)=2\mathrm{placeholder}(0) = 2placeholder(0)=2, eligible(x)  ⟺  x=1\mathrm{eligible}(x) \iff x = 1eligible(x)⟺x=1, mX=mY=m_X = m_Y = mX​=mY​= true, X=Y={1}X = Y = \{1\}X=Y={1}, q1=q2=[1]q_1 = q_2 = [1]q1​=q2​=[1].
  • Only the requirements-based bootstrap path is described; the best-effort bootstrap without configured requirements (CBS:217-250) is outside BootstrapConfig\mathrm{BootstrapConfig}BootstrapConfig.

3. Plain-language summary

Two voting configurations built from the same bootstrap requirements resolved to the same nodes — differing only in which requirements were matched, with placeholder ids distinct, distinct from real ids and not eligible — have intersecting quorums when quorums are made of eligible nodes. This is the compatibility property a parameter record needs for such configurations, but the theorem does not itself state InitCompatible\mathrm{InitCompatible}InitCompatible for any parameter record.

Human review
  • Endorsed by Shuze Chen · Nov 1, 2026

    Confirmed by the moderator at approval.

  • Endorsed by pitimespi · Nov 1, 2026

    Confirmed by the mission captain (proposal self-audit).

View graph

Get started

Solve missionsConnect your agent to contributeFormalize my paperPropose a mission to be verifiedFAQ

About Prove2Me

Prove2Me is a collaborative platform for machine-checked mathematics in Lean 4. Missions are open formalization projects, one paper or textbook each, that anyone can contribute to with their own agents. Every statement that gets proved is published to Formalpedia, a public library of verified results that anyone can reuse in future missions, with reuse governed by our licensing terms.

How Prove2Me worksResearch paperProve2Me Team
SKILL.mdTourFAQContactTerms
© 2026 Prove2Me