Skip to main content

Correctness & Verification

The broker forbids unsafe Rust, uses Creusot-proved decision kernels, and has Stateright models for event sequences within stated bounds. Pull request CI reruns the proofs and models. The ledger below lists what each check covers and the assumptions it requires.

Counts are derived from the broker's verification catalog at build time. One ledger row can hold several related kernels, so rows and functions differ.

Memory safety

Zero unsafe

krabka workspace code forbids unsafe_code as a compiler-enforced lint under Cargo and Bazel. Safe Rust checks memory access and data races at compile time; dependencies remain outside this workspace lint.

How it works

krabka is written in safe Rust. The workspace lint unsafe_code = "forbid" cannot be overridden by an inner attribute, and the Bazel build passes the same -Funsafe_code flag. Clippy runs at the pedantic level with warnings promoted to errors, and no suppression attributes are accepted.

The wire layer in krabka-protocol generates its codecs from the upstream Apache Kafka JSON schemas and is written in safe Rust. Differential suites run digest-pinned Apache Kafka and Confluent Platform brokers beside krabka and compare live responses, covering protocol behavior that falls largely outside both formal tiers.

Formal verification

Creusot proof kernels

Safety-critical decisions live in krabka-verified as pure kernels with pre- and postconditions; Creusot, a deductive verifier for Rust, translates them to Why3 and automated provers discharge the contracts.

How it works

A function carries #[requires] and #[ensures] contracts beside its body, and the proof obligations go to SMT solvers through Why3. A discharged proof establishes the postconditions for inputs that satisfy the preconditions.

krabka extracts each safety-critical decision into a small, total, synchronous kernel in the krabka-verified crate. A host crate maps its state into the kernel's inputs, calls it, and applies the result. There is no second copy of any decision, so the proved body is the body that runs in production.

  • Proof sessions are source. Why3 sessions live under verif/ in the repository. A contract change without a matching proof update fails the build.
  • Re-discharged on every pull request. CI runs a pinned Creusot image against every package in Creusot's scope. A package-discovery check fails if a crate that writes contracts is missing from the proof run.
  • Preconditions are the caller's job. The verification catalog lists what each host must establish before a proof applies. A proved kernel does not prove its caller, and the catalog says so.

The kernels do no I/O, so they also compile to WebAssembly. The verification playground evaluates 17 of them in your browser, with each function's contract beside the result.

Model checking

Stateright models

Where correctness depends on a sequence of crashes, retries and interleavings, a Stateright model explores every reachable state within stated bounds and reports a counterexample trace if a property fails.

How it works

Stateright is a model checker for Rust. A model declares its actors, the actions each may take, and the properties that must hold. The checker performs a breadth-first search over every reachable state within the model's bounds, and when a property fails it prints the exact sequence of actions that broke it.

Some krabka models drive a production decision function directly. Others compose real decision functions with a small environment of crashes, retries, and message reordering. Each model's module comment states its bounds, its properties, and its DRIVEN and MODELED notes, so a reader can tell what the property says about production before relying on it.

  • Pinned state counts. An exhaustive search over a fixed model visits a deterministic set of states. Each runner asserts its unique-state count against a pinned constant, so an action generator that silently drops a branch fails the build instead of passing a smaller search.
  • Never skipped by impact analysis. Pull request CI explicitly runs every Bazel target that contains a model entry point, separately from change-based test selection, so an unrelated edit cannot skip a model check.

The model gallery lists every entry point with what it drives, its bounds, its properties, and its pinned state counts. The model rows below carry the same facts, with the catalog's own paragraphs where it has them.

The evidence, row by row

Every row expands to what is proved or checked, who calls it, and what it assumes.

Creusot kernels 153

What the ledger claims from the broker catalog

The contract audit critiques every original proof module, distinguishes decision-table contracts from aggregate invariants, and records the remaining host assumptions. A proved implication about admission does not establish that valid requests are accepted; admission-completeness contracts must also rule out rejecting everything.

The contract attributes beside each function are the source of truth. A proof applies only when its caller establishes every listed precondition. An empty precondition cell means that the contract accepts all values of the Rust input types.

Creusot also creates sessions for logic functions, lemmas, private helper functions, and generated Clone implementations. These sessions support the contracts in the ledger. They are not additional production kernels. Public logic functions and lemmas, such as kafka_rank, local_retention_model, or lemma_two_sites_never_survive, state a row's rule and are named in its contract cell where the rule matters. The field accessor TransactionSnapshot::identity is proved with the transaction kernels and decides nothing on its own.

Showing 153 of 153

Cross-module theorems 12

published_trim_bounds_remote_breach_cleanup composition/remote_breach.rs 1 proof published_trim_bounds_remote_breach_cleanup composes admitted DeleteRecords, completed WAL/local trim steps, published floor and actual finished ranges. Enabled cleanup with time/size policies disabled selects exactly the oldest whole-row prefix ending below the published floor.
Proves
published_trim_bounds_remote_breach_cleanup composes admitted DeleteRecords, completed WAL/local trim steps, published floor and actual finished ranges. Enabled cleanup with time/size policies disabled selects exactly the oldest whole-row prefix ending below the published floor.
Host caller
Executable cross-module proof witness
Proof sessions
Caller preconditions
Initial stores and previous floor respect committed/delivery bounds. Trace and publication observations mean durable completion; truthful range metadata, I/O and atomic publication remain host obligations. Partial progress preserves the old floor; no checkpoint or disabled deletes selects nothing.
constructed_voter_reconfigurationreconfigured_majorities_overlap composition/reconfiguration/membership.rscomposition/reconfiguration/quorum.rs 2 proofs constructed_voter_reconfiguration constructs exactly the admitted logical membership; reconfigured_majorities_overlap derives actual distinct grant counts and returns a real shared voter whenever both adjacent majorities exist.
Proves
constructed_voter_reconfiguration constructs exactly the admitted logical membership; reconfigured_majorities_overlap derives actual distinct grant counts and returns a real shared voter whenever both adjacent majorities exist.
Host caller
Executable composition of the KRaft reconfiguration policy, unique-ID validator and consensus quorum kernel, mirroring the controller's single set operation
Proof sessions
Caller preconditions
Captured count equals the old ID length; grant slots correspond to those IDs plus one candidate. Empty/duplicate IDs and incoherent target presence reject. Physical directory continuity, truthful epoch/catch-up/grant facts, durable commitment and complete Raft history remain host obligations. Two concurrent additions can have disjoint non-adjacent majorities.
reconfiguration_control_prefix_support composition/reconfiguration/control_support/prepare.rs 1 proof reconfiguration_control_prefix_support derives actual control-record deltas and the exact exclusive end from the admitted plan, builds per-ID grants from reported prefixes, and carries a whole-batch shared-prefix witness through the old/new majorities. A preflight returns no append witness.
Proves
reconfiguration_control_prefix_support derives actual control-record deltas and the exact exclusive end from the admitted plan, builds per-ID grants from reported prefixes, and carries a whole-batch shared-prefix witness through the old/new majorities. A preflight returns no append witness.
Host caller
Executable composition of KRaft admission, metadata batch deltas, local append coordinates and logical quorum overlap
Proof sessions
Caller preconditions
Captured count/IDs and report slots must agree. Reported prefixes, matching log history, control bytes, physical storage continuity, epochs and fsync remain host obligations. This is support qualification across two memberships; the latest-view commitment protocol remains outside the witness.
reconfiguration_control_commit_waiter composition/reconfiguration/control_support/commit.rs 1 proof reconfiguration_control_commit_waiter carries actual supported control rows into half-open history commitment and proves waiter readiness equivalent to commitment of every row. It handles repeated HWM advances and later appends; preflight has no rows or waiter.
Proves
reconfiguration_control_commit_waiter carries actual supported control rows into half-open history commitment and proves waiter readiness equivalent to commitment of every row. It handles repeated HWM advances and later appends; preflight has no rows or waiter.
Host caller
Executable composition of control-prefix support, monotonic HWM advancement, ordered control-history lookup, committed visibility and waiter frontiers; a real controller regression commits finalization's two records separately
Proof sessions
Caller preconditions
The supplied log end and previous/requested HWM must represent the same log. Appended batches require nonnegative previous HWM within that log and an end no later than the log end; invalid geometry rejects, without restricting no-append preflight. The theorem relates truthful HWM observations to control visibility; it does not prove the consensus protocol chooses that HWM safely. Equal voter values alone cannot establish record commitment.
whole_batch_truncation_bounds_controls composition/control_truncation.rs 1 proof whole_batch_truncation_bounds_controls derives the actual whole-batch prefix for any cut at or above the first local offset. The HWM and committed history are bounded by that physical end; all history rows in a discarded batch are removed even when below the requested cut. Waiter presence and commitment use the same end.
Proves
whole_batch_truncation_bounds_controls derives the actual whole-batch prefix for any cut at or above the first local offset. The HWM and committed history are bounded by that physical end; all history rows in a discarded batch are removed even when below the requested cut. Waiter presence and commitment use the same end.
Host caller
The KRaft log truncation and both controller truncation paths use the retained log end; the composition reuses storage retention/clamping and Raft history/waiter kernels.
Proof sessions
Caller preconditions
Complete decoded batch ends, ordered truthful control histories, safe divergence/commit selection, and successful storage I/O remain host facts. Reset-to below the first local offset, rollback of already committed metadata, persistence ordering, and crash durability are outside this composition.
truncated_replay_bounds_first_retry composition/truncated_producer.rs 1 proof truncated_replay_bounds_first_retry derives the latest surviving epoch's last five data batches from an arbitrary-length history and the exact retained physical prefix. It exports the rebuilt window's first matching original history index and batch coordinates; every duplicate acknowledgement fits the retained end, and HWM clamping preserves readiness for that returned frontier.
Proves
truncated_replay_bounds_first_retry derives the latest surviving epoch's last five data batches from an arbitrary-length history and the exact retained physical prefix. It exports the rebuilt window's first matching original history index and batch coordinates; every duplicate acknowledgement fits the retained end, and HWM clamping preserves readiness for that returned frontier.
Host caller
Physical truncation composes with snapshot-row validation, full-window retry classification and the acknowledgement frontier. Real log/tracker regressions cover interior cuts, older-alias reentry and earlier-epoch restoration.
Proof sessions
Caller preconditions
Complete physical ends, faithful same-PID data rows with nondecreasing epochs, and complete data replay remain host facts. Tail deletion can change the first matching alias. Marker-only entries, snapshot retention of earlier slots, serialization, bytes and crash publication are outside this theorem.
loaded_snapshot_bounds_truncated_retry composition/snapshot_tail.rs 2 proofs loaded_snapshot_bounds_truncated_retry derives the exact snapshot seed and local replay history after whole-batch truncation. It exports the bounded retry window, first alias with original seed/tail provenance, batch coordinates, acknowledgement frontier and readiness preserved by HWM clamping.
Proves
loaded_snapshot_bounds_truncated_retry derives the exact snapshot seed and local replay history after whole-batch truncation. It exports the bounded retry window, first alias with original seed/tail provenance, batch coordinates, acknowledgement frontier and readiness preserved by HWM clamping.
Host caller
Snapshot offset/entry admission, replay cursor, physical truncation, the shared rebuilt_data_window_bounds_retry, producer decisions and acknowledgements compose. Real log/tracker regressions cover seed eviction and a snapshot-carried retry below the local floor.
Proof sessions
Caller preconditions
Faithful loaded snapshot identity/coverage, complete local data rows, PID association, marker-free history and serialized replay remain external. Snapshot selection/corruption fallback, bytes, transaction markers and crash publication are outside this boundary.
completed_batches_preserve_first_retry composition/completed_producer.rs 7 proofs completed_batches_preserve_first_retry merges deferred completion origins into a physically ordered five-batch window. It preserves the newest batch after earlier completions, exports the first matching retry's source and coordinates, and bounds its acknowledgement frontier. Marker-only epochs retain fencing and zero-sequence restart.
Proves
completed_batches_preserve_first_retry merges deferred completion origins into a physically ordered five-batch window. It preserves the newest batch after earlier completions, exports the first matching retry's source and coordinates, and bounds its acknowledgement frontier. Marker-only epochs retain fencing and zero-sequence restart.
Host caller
The live tracker and bounded Stateright model use producer_completion_window; the composition consumes row validation, reconstructed retry classification and HWM readiness. Native tests cover all 720 orders of six completions and real-writer marker/restart behavior.
Proof sessions
Caller preconditions
Complete truthful completions, equal-offset batch identity, PID routing, assigned geometry and faithful metadata projection remain host facts. Pending callbacks, transaction-boundary reconstruction, marker snapshot timestamp equality, concurrent truncation and crash durability are outside this theorem.
completed_eviction_bounds_waiters composition/completed_eviction.rs 2 proofs completed_eviction_bounds_waiters proves that evicting an eligible completed source requires five strictly newer frontiers. A ready retained waiter implies that the evicted frontier is HWM-covered; an uncovered evicted frontier leaves all five waiters unready.
Proves
completed_eviction_bounds_waiters proves that evicting an eligible completed source requires five strictly newer frontiers. A ready retained waiter implies that the evicted frontier is HWM-covered; an uncovered evicted frontier leaves all five waiters unready.
Host caller
The strengthened completed-window contract composes with the actual Produce durability-frontier and HWM waiter kernels. Boundary and property checks cover physical eviction, repeated offsets, older epochs, sequence aliases and maximum frontiers.
Proof sessions
Caller preconditions
Complete truthful same-PID batches, ordered old origins, eligible epochs and a common HWM observation remain assumptions. Epoch replacement, pending callbacks, persistence and retained-byte availability are outside this law. An evicted physical source can still have a retained sequence alias.
epoch_handoff_distinguishes_identity_and_data_retry composition/epoch_handoff.rs 2 proofs epoch_handoff_distinguishes_identity_and_data_retry proves that a verified same-PID epoch handoff classifies the old identity as an initialization retry while fencing every old-epoch data retry after higher-epoch data completes. Only the new completion origin survives, regardless of physical offsets or sequence aliases.
Proves
epoch_handoff_distinguishes_identity_and_data_retry proves that a verified same-PID epoch handoff classifies the old identity as an initialization retry while fencing every old-epoch data retry after higher-epoch data completes. Only the new completion origin survives, regardless of physical offsets or sequence aliases.
Host caller
The actual identity-transition and initialization-identity kernels compose with completed-window selection, snapshot-row validity and exact data-epoch fencing.
Proof sessions
Caller preconditions
Verified normal same-PID transitions below the reserved marker-epoch boundary, truthful completed data, PID routing and recorded last epoch are assumed. Full initialization state admission, marker publication, PID rotation, recovery identities and physical persistence are outside the theorem. Rotation alone does not update or fence the old partition entry.
epoch_handoff_survives_delayed_completions composition/epoch_delayed.rs 2 proofs epoch_handoff_survives_delayed_completions proves arbitrary old-epoch callback traces cannot undo a same-PID handoff: every callback is rejected and old data remains fenced, while the new batch retains its duplicate slot, original coordinates and HWM readiness.
Proves
epoch_handoff_survives_delayed_completions proves arbitrary old-epoch callback traces cannot undo a same-PID handoff: every callback is rejected and old data remains fenced, while the new batch retains its duplicate slot, original coordinates and HWM readiness.
Host caller
The actual handoff result and selected origins feed repeated completed-window classification, then the reconstructed-window retry and wrapped-sequence kernels. No physical callback ordering or finite trace bound is assumed.
Proof sessions
Caller preconditions
Truthful same-PID rows, serialized installation, continued presence of the entry and one HWM observation are host facts. Expiry, truncation, new-epoch arrivals, marker publication, full initialization admission and persistence remain outside the trace.
rotation_marker_bounds_identity_retry composition/rotation_marker.rs 1 proof rotation_marker_bounds_identity_retry proves normal exhausted-epoch rotation preserves the old initialization retry while the old PID's final-epoch marker fences all lower-epoch data, including retained aliases. The fresh PID starts data at sequence zero. Its first epoch bump retires the old initialization retry and makes the fresh identity the retry.
Proves
rotation_marker_bounds_identity_retry proves normal exhausted-epoch rotation preserves the old initialization retry while the old PID's final-epoch marker fences all lower-epoch data, including retained aliases. The fresh PID starts data at sequence zero. Its first epoch bump retires the old initialization retry and makes the fresh identity the retry.
Host caller
Two actual identity transitions compose with initialization classification and partition sequence/epoch admission across separate PID entries.
Proof sessions
Caller preconditions
Distinct nonnegative PID allocation, PID-keyed lookup with no fresh entry, installed old-PID marker and faithfully recorded coordinator last/previous identities are host facts. Recovery rotation, full API admission, expiry and physical persistence remain outside. Allocation alone does not install a partition fence.

KRaft consensus 18

election_jitter_ms consensus/log_is_up_to_date.rs 1 proof Runs in browser election_jitter_ms keeps deterministic jitter in [0, base_ms).
Proves
election_jitter_ms keeps deterministic jitter in [0, base_ms).
Host caller
krabka-kraft-core
Proof sessions
Caller preconditions
None. Only the range is proved; that different nodes or epochs get different jitter is not (the hash can collide), and unit tests pin a few values.
log_is_up_to_date consensus/log_is_up_to_date.rs 1 proof Runs in browser log_is_up_to_date implements the KIP-595 candidate-log ordering.
Proves
log_is_up_to_date implements the KIP-595 candidate-log ordering.
Host caller
krabka-kraft-core
Proof sessions
Caller preconditions
Accepts every value of the Rust input types.
vote_wire_decisionvote_encode_decisionvote_admission_decision vote.rs 3 proofs Runs in browser vote_wire_decision accepts exactly when vote_fields_nonnegative holds (no negative KIP-595 voter, candidate, or epoch field; zero is a real value); vote_encode_decision accepts exactly when vote_fields_fit_int32 holds (every unsigned field is at most the signed int32 maximum); vote_admission_decision classifies a VoteTarget and a VoteMembership as ignore (wrong voter ID or directory, decided by the target alone), deny (exact target, but cluster or either voter membership fails), or consider, each variant pinned by an if-and-only-if postcondition.
Proves
vote_wire_decision accepts exactly when vote_fields_nonnegative holds (no negative KIP-595 voter, candidate, or epoch field; zero is a real value); vote_encode_decision accepts exactly when vote_fields_fit_int32 holds (every unsigned field is at most the signed int32 maximum); vote_admission_decision classifies a VoteTarget and a VoteMembership as ignore (wrong voter ID or directory, decided by the target alone), deny (exact target, but cluster or either voter membership fails), or consider, each variant pinned by an if-and-only-if postcondition.
Host caller
The raft Vote codec and kraft-core Vote handler
Proof sessions
Caller preconditions
The decoder also requires exactly one __cluster_metadata partition-0 row and full frame consumption. The host compares KIP-853 directory IDs exactly at kraft.version 1 and retains the version-0 node-ID compatibility rule; later epoch/log and double-vote checks remain in the state machine.
quorum_state_write_decisionquorum_state_load_decision quorum_state.rs 2 proofs quorum_state_write_decision accepts exactly when fits_signed_json holds: schema v0 or v1, and the term and optional leader, vote, and v0 voter identities fit their signed fields without clamping; quorum_state_load_decision restores only the exact no-vote sentinel or a nonnegative voted key from a valid version-specific layout.
Proves
quorum_state_write_decision accepts exactly when fits_signed_json holds: schema v0 or v1, and the term and optional leader, vote, and v0 voter identities fit their signed fields without clamping; quorum_state_load_decision restores only the exact no-vote sentinel or a nonnegative voted key from a valid version-specific layout.
Host caller
KRaft's quorum-state persistence adapter
Proof sessions
Caller preconditions
The host maps optional identities and exact numeric bounds into the write kernel. On load it supplies exact field-presence checks, configured-cluster equality, ordered v0 voter equality, and strict URL-safe 16-byte directory decoding. Checked Rust conversions and JSON, base64, and UUID codecs remain adapter assumptions. Leadership is volatile and is always cleared after admission. File create, write, file sync, and rename remain filesystem assumptions; failures are returned, retries overwrite the temporary file, malformed final files are ignored, and the parent directory is not synced.
voter_wire_decisionvoter_set_wire_decision voter_set.rs 2 proofs voter_wire_decision admits one VotersRecord member only with a nonnegative ID, byte-exact directory identity, a nonempty valid endpoint set, and a non-inverted nonnegative kraft.version range; voter_set_wire_decision admits only record version 0 with a nonempty set of unique voter IDs.
Proves
voter_wire_decision admits one VotersRecord member only with a nonnegative ID, byte-exact directory identity, a nonempty valid endpoint set, and a non-inverted nonnegative kraft.version range; voter_set_wire_decision admits only record version 0 with a nonempty set of unique voter IDs.
Host caller
KRaft's voter_set_from_wire control-record adapter
Proof sessions
Caller preconditions
The host preserves wire order, computes voter-ID uniqueness and per-voter listener-name uniqueness with ordered sets, and marks endpoints valid only when every name and host is nonempty and every port is nonzero. UUID conversion must reproduce all 16 input bytes; Kafka's zero UUID remains the exact legacy no-directory identity. Checked signed-to-unsigned conversions, string cloning, VoterSet construction, control-record decoding, and log replay remain adapter assumptions. The control history is mutated only after the complete record converts successfully, so a malformed record cannot partially install a voter set.
majority_sizeelection_has_quorum consensus/failover.rs 2 proofs Runs in browser majority_size computes floor(voters / 2) + 1; election_has_quorum admits only that many unique grants from the current voter set.
Proves
majority_size computes floor(voters / 2) + 1; election_has_quorum admits only that many unique grants from the current voter set.
Host caller
krabka-kraft-core
Proof sessions
Caller preconditions
The host filters the grant set to unique current voters before passing its count.
failover_action consensus/failover.rs 1 proof failover_action maps a FailoverFacts (leader dead, ISR shrunk, the three-state LiveIsr classification, and the OutOfIsrFacts) to one action class by a fixed precedence for a dead leader -- clean ISR election, then (only when LiveIsr::Empty) an eligible-leader-replica election, then the last known leader (Kafka's canElectLastKnownLeader, an unclean election that neither unclean.leader.election.enable nor unclean.recovery.strategy gates), then the configured offset-aware recovery, then the available KIP-841 election, else unavailable, with a witness-only live ISR unavailable -- and for a live leader shrinks or leaves the ISR; each class is pinned to exactly one fact combination.
Proves
failover_action maps a FailoverFacts (leader dead, ISR shrunk, the three-state LiveIsr classification, and the OutOfIsrFacts) to one action class by a fixed precedence for a dead leader -- clean ISR election, then (only when LiveIsr::Empty) an eligible-leader-replica election, then the last known leader (Kafka's canElectLastKnownLeader, an unclean election that neither unclean.leader.election.enable nor unclean.recovery.strategy gates), then the configured offset-aware recovery, then the available KIP-841 election, else unavailable, with a witness-only live ISR unavailable -- and for a live leader shrinks or leaves the ISR; each class is pinned to exactly one fact combination.
Host caller
The broker's shared leader-failover policy
Proof sessions
Caller preconditions
The host follows Kafka's generateLeaderAndIsrUpdates for one fenced broker: it visits only a partition whose ISR holds that broker, builds the target ISR by removing that broker alone (Replicas.copyWithout), and reports the leader as needing replacement (leader_dead) whenever it is not an acceptable member of that target ISR (isValidNewLeader(partition.leader)), which includes a leader that is down but whose own failover has not run. It classifies the target ISR's live and electable non-witness members, takes every pick -- clean, ELR and KIP-841 -- as the first qualifying replica in assignment order, offers the last-known rung only when the target ELR is empty and the partition's last-known ELR holds exactly one live non-witness replica (the dead broker counts toward the target ELR unless it restarted uncleanly, and the returning last leader reaches the rung through the unfence election), maps the resolved recovery strategy exactly, and constructs the selected ISR after the kernel chooses the action class: the target ISR for a clean election, the winner alone for an ELR, last-known or unclean one. Where no rung can elect and the leader is not the removed broker, the host still commits the target ISR (which keeps the leader); a partition led by the removed broker is left unchanged. Opening the ELR rung on an ISR with no live member, rather than Kafka's literally empty targetIsr, is a host choice justified in the rustdoc of failover_one: in any state Kafka reaches the two coincide.
select_best_recovery_replica consensus/failover.rs 1 proof select_best_recovery_replica returns None exactly for no candidates, and otherwise an in-bounds index that ranks_ge every candidate, where ranks_ge is the lexicographic order: higher last-written leader epoch, then higher log end offset, then lower broker ID.
Proves
select_best_recovery_replica returns None exactly for no candidates, and otherwise an in-bounds index that ranks_ge every candidate, where ranks_ge is the lexicographic order: higher last-written leader epoch, then higher log end offset, then lower broker ID.
Host caller
The broker's KIP-966 unclean-recovery selector
Proof sessions
Caller preconditions
The host preserves response order while adapting each report to a RecoveryCandidate { last_epoch, log_end_offset, broker_id } and maps the proved index back to that same report.
recompute_high_watermark consensus/watermark.rs 1 proof Runs in browser recompute_high_watermark computes the majority offset over the leader's log end (when leader_counts) and follower offsets, applies the Raft/KIP-595 epoch-start gate, and does not regress below current_hwm.
Proves
recompute_high_watermark computes the majority offset over the leader's log end (when leader_counts) and follower offsets, applies the Raft/KIP-595 epoch-start gate, and does not regress below current_hwm.
Host caller
krabka-kraft-core; the krabka-broker WAL live engine, with leader_counts = false and every voter's fsynced offset, the leader's included, as a member, so the leader's unsynced log end never votes; krabka-verified calls it from majority_watermark
Proof sessions
Caller preconditions
1 <= majority <= followers + 1; leader_counts || majority <= followers (a leader that its own VotersRecord removed needs the followers alone to reach the majority); current_hwm <= log_end; each follower offset is at most log_end. The hosts clamp follower offsets and the current HWM to the leader's log end. The live engine passes max(durable watermark, log start), clamped to the leader end, as both the gate and the floor, and advances only when the result passes the stored watermark.
majority_watermark consensus/watermark.rs 1 proof majority_watermark returns the greater of current and the majority frontier (the greatest member of {log_end} U followers that majority members reach, the leader always counted), with no epoch gate.
Proves
majority_watermark returns the greater of current and the majority frontier (the greatest member of {log_end} U followers that majority members reach, the leader always counted), with no epoch gate.
Host caller
The krabka-broker WAL recovery harness, as the length upper bound of the byte-agreed prefix; the diskless crash model
Proof sessions
Caller preconditions
1 <= majority <= followers + 1; current <= log_end; each follower offset is at most log_end. Recovery passes the longest replica end as the leader, every other end as a follower, and current = 0.
exact_epoch_successor epoch.rs 1 proof exact_epoch_successor returns the exact signed successor only below the caller's maximum and rejects the maximum or any value above it.
Proves
exact_epoch_successor returns the exact signed successor only below the caller's maximum and rejects the maximum or any value above it.
Host caller
The broker's shared metadata-epoch adapter, used by ISR updates, leader election and failover, reassignment, unclean recovery, classic and next-generation group coordination, barrier injection, share-state resets, and share snapshots
Proof sessions
Caller preconditions
None. The host passes i32::MAX for Kafka int32 metadata fields and i64::MAX for barrier and snapshot epochs, then rejects None before mutation or persistence. Fetch-session and share-fetch-session epochs are excluded because their protocols intentionally wrap while skipping reserved sentinels.
advance_high_watermarkin_half_open_windowfrontier_reaches raft/metadata_record_offset_deltas.rs 3 proofs advance_high_watermark advances a KRaft HWM monotonically without passing log end; in_half_open_window and frontier_reaches define commit, fetch, apply, and waiter boundaries.
Proves
advance_high_watermark advances a KRaft HWM monotonically without passing log end; in_half_open_window and frontier_reaches define commit, fetch, apply, and waiter boundaries.
Host caller
krabka-raft and its controller offset rules
Proof sessions
Caller preconditions
The HWM bound requires the host invariant previous <= log_end; the predicates accept all integers.
control_history_frontier raft/metadata_record_offset_deltas.rs 1 proof control_history_frontier returns the exact prefix of strictly ordered control-record offsets below a half-open frontier, so the prefix excludes a record exactly at the frontier and its suffix contains every record at or above it.
Proves
control_history_frontier returns the exact prefix of strictly ordered control-record offsets below a half-open frontier, so the prefix excludes a record exactly at the frontier and its suffix contains every record at or above it.
Host caller
The KRaft controller's voter and feature control history, for point lookup, truncation, and commitment
Proof sessions
Caller preconditions
The host copies keys from a BTreeMap, whose unique ascending iteration establishes the kernel's strict-order precondition, and maps the proved last prefix key back into that same map. Truncation splits at the proved first suffix key. Commitment is called with the controller's monotonic HWM and selects only the latest record strictly below it.
metadata_record_offset_deltas raft/metadata_record_offset_deltas.rs 1 proof metadata_record_offset_deltas returns, for a nonempty metadata batch whose final delta fits Kafka's signed lastOffsetDelta, exactly the contiguous record deltas 0..n-1 (one per record, deltas[i] == i), and rejects an empty batch or a count past i32::MAX + 1. The deltas are counted in i32 beside the vector length rather than narrowed from a usize, so the executed body is the proved body; there is no cfg(creusot) split.
Proves
metadata_record_offset_deltas returns, for a nonempty metadata batch whose final delta fits Kafka's signed lastOffsetDelta, exactly the contiguous record deltas 0..n-1 (one per record, deltas[i] == i), and rejects an empty batch or a count past i32::MAX + 1. The deltas are counted in i32 beside the vector length rather than narrowed from a usize, so the executed body is the proved body; there is no cfg(creusot) split.
Host caller
The KRaft controller's metadata_record_batch builder
Proof sessions
Caller preconditions
The host passes the exact slice length, zips the returned deltas with the records in order, and takes the batch's lastOffsetDelta from the last delta; an empty slice produces an empty batch with delta zero before the kernel runs, and a rejection occurs before append.
fetch_response_mutation raft/metadata_record_offset_deltas.rs 1 proof fetch_response_mutation rejects a KRaft Fetch response unless it is a NONE-error answer whose sender, advertised leader, current leader, live fetching role, and epoch all agree, then selects exactly one of snapshot, truncation, append, or HWM advancement. A leaderless observer's same-epoch response that names a leader, whatever its error and whoever sent it, is Discover only: it attaches to that leader and applies nothing (Kafka's maybeHandleCommonResponse). No mutation depends on who the responder is assumed to be.
Proves
fetch_response_mutation rejects a KRaft Fetch response unless it is a NONE-error answer whose sender, advertised leader, current leader, live fetching role, and epoch all agree, then selects exactly one of snapshot, truncation, append, or HWM advancement. A leaderless observer's same-epoch response that names a leader, whatever its error and whoever sent it, is Discover only: it attaches to that leader and applies nothing (Kafka's maybeHandleCommonResponse). No mutation depends on who the responder is assumed to be.
Host caller
The KRaft controller's on_fetch_response adapter, after wire decode and before any endpoint binding or response-derived mutation. A response from an epoch newer than the node's own is not fenced: when it names a leader, the adapter moves the node to it through the BeginQuorumEpoch path, as Kafka's maybeHandleCommonResponse does, and applies nothing else from it; one that names no leader is ignored (Kafka moves to Unattached in that epoch)
Proof sessions
Caller preconditions
The host maps Follower and an Observer with a known leader to the role-leader input, maps every other role to None, and passes the decoded sender, leader (None for -1), epoch, ErrorCode == NONE, and mutation-presence flags unchanged. On Discover and on the newer-epoch path it binds only the leader's own NodeEndpoints entry (the one whose node id is CurrentLeader.LeaderId), never the responder's address; without one, the transport keeps the leader's voter-set listener. Serving replicas answer as Kafka's tryCompleteFetchRequest: validateLeaderOnlyRequest first (FENCED_LEADER_EPOCH, UNKNOWN_LEADER_EPOCH, NOT_LEADER_OR_FOLLOWER), each refusal carrying the known leader, epoch, and the leader's endpoint. Wire decoding itself is outside Creusot.
voter_reconfiguration_decision reconfiguration/voter_reconfiguration_decision.rs 1 proof voter_reconfiguration_decision returns exactly the first rejection of the reference voter_reconfiguration_rejection, which follows the order of Kafka's KafkaRaftClient leader check, AddVoterHandler, RemoveVoterHandler, UpdateVoterHandler, and maybeAppendUpgradedKRaftVersion (not leader; pending change; leader epoch uncommitted; uncommitted control record; then per operation: add needs kraft.version 1, a new voter id, a compatible range, and catch-up; remove needs version 1, the exact stored voter key, and a second voter; update needs a compatible range and a voter that VoterNode.isVoter matches (an unknown or equal stored directory) at both versions -- Kafka matches by id alone at version 0 only to stage upgrade data, while Krabka's version-0 preflight applies the set to the live quorum, so a different known directory is refused; finalization only upgrades 0 to 1 when every voter supports 1), and otherwise admits with the exact next count, version, and control-record shape. Every admitted change satisfies may_reconfigure (KIP-853's one change at a time: leader, committed epoch, no pending change, no uncommitted control record) and keeps a nonempty voter set at version 0 or 1.
Proves
voter_reconfiguration_decision returns exactly the first rejection of the reference voter_reconfiguration_rejection, which follows the order of Kafka's KafkaRaftClient leader check, AddVoterHandler, RemoveVoterHandler, UpdateVoterHandler, and maybeAppendUpgradedKRaftVersion (not leader; pending change; leader epoch uncommitted; uncommitted control record; then per operation: add needs kraft.version 1, a new voter id, a compatible range, and catch-up; remove needs version 1, the exact stored voter key, and a second voter; update needs a compatible range and a voter that VoterNode.isVoter matches (an unknown or equal stored directory) at both versions -- Kafka matches by id alone at version 0 only to stage upgrade data, while Krabka's version-0 preflight applies the set to the live quorum, so a different known directory is refused; finalization only upgrades 0 to 1 when every voter supports 1), and otherwise admits with the exact next count, version, and control-record shape. Every admitted change satisfies may_reconfigure (KIP-853's one change at a time: leader, committed epoch, no pending change, no uncommitted control record) and keeps a nonempty voter set at version 0 or 1.
Host caller
The KRaft controller's on_reconfigure adapter before any voter-state mutation or control-record append
Proof sessions
Caller preconditions
The host takes membership and kraft.version from one committed control-state snapshot, maps "no uncommitted control record" from the latest voter set and version equalling it, "no pending change" from the absent waiter, and epoch commitment from HWM > epoch_start_offset. It classifies the request's voter key against the committed set with a nil stored directory as Kafka's absent directory id. Version ranges come unchanged from decoded voter metadata. Observer catch-up uses the latest recorded fetch offset against the current log end; that liveness observation can become stale after admission, while safety still depends on the current quorum committing the appended voter record. A directory mismatch and the removal of the last voter are both answered with VOTER_NOT_FOUND: the kernel's voter_set_removes (the exact stored key and at least two voters) is Kafka's VoterSet.removeVoter condition, and RemoveVoterHandler answers every refused removal with VOTER_NOT_FOUND. The host checks the proved next count against the constructed set. The voter-request adapter rejects an add, remove, or update without a directory id with INVALID_REQUEST before the kernel, as KafkaRaftClient does, so a request key always carries a known directory.
snapshot_chunk_admission snapshot.rs 1 proof snapshot_chunk_admission admits chunks only for one fixed snapshot identity and declared size, at the exact accumulated byte position, within the configured bound; it completes only when the next position equals the declared size and rejects overflow, duplicates, gaps, overlaps, and changed metadata without advancing.
Proves
snapshot_chunk_admission admits chunks only for one fixed snapshot identity and declared size, at the exact accumulated byte position, within the configured bound; it completes only when the next position equals the declared size and rejects overflow, duplicates, gaps, overlaps, and changed metadata without advancing.
Host caller
KRaft's follower SnapshotFetchState
Proof sessions
Caller preconditions
The host compares the full (end offset, epoch) identity, converts exact buffer and chunk lengths to signed wire coordinates, and mutates the latched size and byte buffer only after admission. BytesMut allocation and copy success, plus the engine's documented discard of state after Restart, remain host and file-I/O assumptions.
snapshot_install_decisionsnapshot_prune_admission snapshot.rs 2 proofs snapshot_install_decision installs only a nonnegative future snapshot identity when no mandatory downgrade snapshot is pending, classifying equal or older boundaries as stale; snapshot_prune_admission permits pruning only at a nonnegative boundary at or below the committed frontier.
Proves
snapshot_install_decision installs only a nonnegative future snapshot identity when no mandatory downgrade snapshot is pending, classifying equal or older boundaries as stale; snapshot_prune_admission permits pruning only at a nonnegative boundary at or below the committed frontier.
Host caller
KRaft's follower snapshot installer and leader checkpoint-prune path
Proof sessions
Caller preconditions
The separately proved chunk state supplies the completed requested identity and exact bytes. Snapshot decoding succeeds before durable install. Checkpoint writing must durably persist the exact snapshot bytes and boundary before pruning; filesystem rename, fsync, log install/prune atomicity, image publication, and checkpoint cleanup remain I/O assumptions. The host re-reads the current log end for install and committed HWM for prune immediately before mutation.

Replication and failover 28

fetch_visibility broker/fetch.rs 1 proof fetch_visibility computes the Fetch read window and response watermarks: a follower reads to the log end, a consumer never reads at or past the high watermark, the KFC-1 delivery watermark, or (under read_committed) the last stable offset, and every fetcher is told the partition's high watermark and min(lso, hw), as Kafka's Partition.readRecords reports them.
Proves
fetch_visibility computes the Fetch read window and response watermarks: a follower reads to the log end, a consumer never reads at or past the high watermark, the KFC-1 delivery watermark, or (under read_committed) the last stable offset, and every fetcher is told the partition's high watermark and min(lso, hw), as Kafka's Partition.readRecords reports them.
Host caller
krabka-broker
Proof sessions
Caller preconditions
None: the kernel has no precondition, and the consumer bound holds for any offsets the caller passes. The host supplies the partition's live high watermark, lso from Log::last_stable_offset(hw), the local log start as the out-of-range floor on a tiered partition, and a delivery watermark it clamps to the high watermark; a wrong value can only narrow a consumer's window or misreport a watermark, never expose an uncommitted or undelivered offset.
replica_fetch_mutation broker/fetch.rs 1 proof replica_fetch_mutation rejects a follower Fetch response row unless its in-flight request epoch, current metadata target, and any reported leader identity all agree (replica_fetch_fenced), then selects exactly one of retry (nonzero error code), truncation (KIP-320 diverging epoch >= 0, Kafka's FetchResponse.isDivergingEpoch), or append; the facts are the row's raw error code and diverging epoch, so the error-before-divergence precedence is proved rather than left to the host.
Proves
replica_fetch_mutation rejects a follower Fetch response row unless its in-flight request epoch, current metadata target, and any reported leader identity all agree (replica_fetch_fenced), then selects exactly one of retry (nonzero error code), truncation (KIP-320 diverging epoch >= 0, Kafka's FetchResponse.isDivergingEpoch), or append; the facts are the row's raw error code and diverging epoch, so the error-before-divergence precedence is proved rather than left to the host.
Host caller
The broker follower replicator's Fetch loop and response adapter, before any response-derived mutation
Proof sessions
Caller preconditions
One request now covers every partition a fetcher follows on one leader, so the host applies the kernel once per response row rather than once per response, with that row's own partition configuration and its own in-flight request epoch. It captures the exact request epoch per partition before sending; indexes the response once and hands the kernel a partition's row only when exactly one row claims it and every populated topic identity field and the partition index match (the kernel does not re-check identity), so a duplicated or contradictory row is side-effect free; accepts current_leader only when both fields are absent sentinels or both exactly match that partition's configured target; and rechecks metadata plus the local replication-target lock before truncation or append. A partition the response does not mention is left untouched, which is the KIP-227 incremental answer for a partition whose state did not change. Wire decoding, record-batch validation, filesystem I/O, producer-state updates, metrics, and separately guarded error recovery remain host assumptions.
preferred_rebalance_admission broker/fetch.rs 1 proof preferred_rebalance_admission admits a preferred-leader batch only when it is nonempty, at most max_changes long, and every change installs the partition's first assigned replica while that replica is alive, in the ISR, and not a witness (preferred_change_eligible).
Proves
preferred_rebalance_admission admits a preferred-leader batch only when it is nonempty, at most max_changes long, and every change installs the partition's first assigned replica while that replica is alive, in the ISR, and not a witness (preferred_change_eligible).
Host caller
The broker's KIP-460 preferred-leader rebalance tick, immediately before controller submission
Proof sessions
Caller preconditions
The host reads each change's facts off the change record it is about to submit (its leader, replicas[0], and ISR) and the tick's liveness and witness snapshots, so the kernel checks the election's output rather than restating it, and passes MAX_ELECTIONS_PER_TICK (Kafka's MAX_ELECTIONS_PER_IMBALANCE) as the cap. The host rejects duplicate topic-partition keys. There is no imbalance-ratio gate to admit against: Kafka's leader.imbalance.per.broker.percentage belongs to the ZooKeeper controller, and the KRaft controller krabka follows restores every partition whose preferred replica is eligible. Liveness can change after the snapshot, and controller submission and metadata-image concurrency remain host assumptions; a later tick retries a failed submission and a later tick picks up whatever the cap left behind.
delete_records_trim_decisiondelete_records_trim_application broker/trim_admission.rs 2 proofs delete_records_trim_decision rejects malformed offset state and out-of-range requests, resolves -1, caps every target at the committed high watermark and optional delivery watermark, and preserves a newer current start. delete_records_trim_application orders WAL before local application, takes the maximum observed frontier on retry, and reports completion only when the WAL and local frontiers are equal.
Proves
delete_records_trim_decision rejects malformed offset state and out-of-range requests, resolves -1, caps every target at the committed high watermark and optional delivery watermark, and preserves a newer current start. delete_records_trim_application orders WAL before local application, takes the maximum observed frontier on retry, and reports completion only when the WAL and local frontiers are equal.
Host caller
The DeleteRecords offset adapter and partition-writer trim reconciliation
Proof sessions
Caller preconditions
After authorization and freeze checks, the caller supplies fresh leader reads of log start, HWM, LEO, and a delivery watermark recomputed under the log mutex; an inconsistent combination fails closed. The writer serializes mutations, applies the WAL first, then advances the local log to the WAL result and checks exact equality before acknowledging. WAL, local-log, object-store, and remote-tier I/O remain host assumptions; failed I/O returns an error, and a retry re-enters the same monotonic decision.
effective_share_backlog broker/trim_admission.rs 1 proof effective_share_backlog computes a nonnegative, saturating backlog.
Proves
effective_share_backlog computes a nonnegative, saturating backlog.
Host caller
krabka-broker
Proof sessions
Caller preconditions
Accepts every value of the Rust input types.
unclean_recovery_commit_admission broker/coordinator_partition.rs 1 proof unclean_recovery_commit_admission admits a polled unclean-election winner only while the partition epoch and ordered replica assignment equal the selection snapshot, the winner remains assigned, and the current leader remains unavailable.
Proves
unclean_recovery_commit_admission admits a polled unclean-election winner only while the partition epoch and ordered replica assignment equal the selection snapshot, the winner remains assigned, and the current leader remains unavailable.
Host caller
The broker's UncleanRecoveryManager post-poll commit path
Proof sessions
Caller preconditions
The host captures the exact partition epoch and ordered replica IDs before polling, maps the re-read record and liveness result immediately before commit, and submits only after kernel admission. Metadata submission remains an asynchronous host boundary.
find_coordinator_admission broker/coordinator_partition.rs 1 proof find_coordinator_admission admits only known GROUP and TRANSACTION keys after their mapped Describe ACL succeeds, and well-formed SHARE keys at API v6+ after ClusterAction succeeds; malformed SHARE keys and unknown wire discriminants fail closed.
Proves
find_coordinator_admission admits only known GROUP and TRANSACTION keys after their mapped Describe ACL succeeds, and well-formed SHARE keys at API v6+ after ClusterAction succeeds; malformed SHARE keys and unknown wire discriminants fail closed.
Host caller
The broker's FindCoordinator authorization adapter, before internal-topic creation or coordinator resolution
Proof sessions
Caller preconditions
The host validates canonical SHARE composite keys and maps SHARE to ClusterAction on the singleton Cluster resource; only kernel allow outcomes enter resolution.
java_string_hash_partition broker/coordinator_partition.rs 1 proof java_string_hash_partition implements Java UTF-16 String.hashCode, Kafka's Utils.abs(Integer.MIN_VALUE) = 0, and positive partition modulo exactly.
Proves
java_string_hash_partition implements Java UTF-16 String.hashCode, Kafka's Utils.abs(Integer.MIN_VALUE) = 0, and positive partition modulo exactly.
Host caller
The broker's group coordinator partitioner, transaction coordinator partitioner, and SHARE coordinator partitioner
Proof sessions
Caller preconditions
All three adapters pass str::encode_utf16() output, and configuration validation requires positive partition counts. The group adapter hashes the group ID and the transaction adapter the transactional ID exactly. The SHARE adapter first canonicalizes the composite key and topic UUID with Kafka's URL-safe unpadded encoding. The decision has no mutable state, so stale input does not apply; invalid counts fail closed, and exact retries are deterministic.
reassignment_action reassignment.rs 1 proof reassignment_action waits until completion_ready holds (Kafka's PartitionReassignmentReplicas.maybeCompleteReassignment predicate: every adding replica is in the ISR, and a replication factor decrease does not shrink the ISR), completes immediately when the leader remains assigned, and otherwise selects the first eligible replacement leader or waits.
Proves
reassignment_action waits until completion_ready holds (Kafka's PartitionReassignmentReplicas.maybeCompleteReassignment predicate: every adding replica is in the ISR, and a replication factor decrease does not shrink the ISR), completes immediately when the leader remains assigned, and otherwise selects the first eligible replacement leader or waits.
Host caller
The broker's partition-reassignment policy
Proof sessions
Caller preconditions
The host constructs the target assignment as current replicas minus removals and marks a handoff candidate eligible exactly when it is both in the ISR and alive.
reassignment_set_membershipreassignment_plan_admission reassignment.rs 2 proofs reassignment_set_membership proves the pointwise union, addition, and removal classification for a start plan; reassignment_plan_admission admits a start or cancel mutation only after its target/RF or approval/in-progress fences, leader eligibility, and checked epoch are established.
Proves
reassignment_set_membership proves the pointwise union, addition, and removal classification for a start plan; reassignment_plan_admission admits a start or cancel mutation only after its target/RF or approval/in-progress fences, leader eligibility, and checked epoch are established.
Host caller
The AlterPartitionReassignments per-row planner, before constructing a controller record
Proof sessions
Caller preconditions
The host rejects empty, duplicate, negative, and unregistered requested targets; enforces the RF policy against the current target; walks each unique current-or-target replica through the set kernel in stable current-then-target order; and selects a leader from the planned replicas intersected with the ISR. The cancel approval is resolved against the same metadata image used for planning. Metadata-image invariants, broker registration changes, break-glass registry correctness, directory remapping, asynchronous controller submission, and races after the snapshot remain host assumptions. The shared checked-epoch adapter fails closed before admission.
broker_heartbeat_decision registration.rs 1 proof broker_heartbeat_decision classifies a heartbeat as Missing exactly when the broker has no registration, Stale exactly when its broker epoch differs from the registered one, and otherwise Current, caught up exactly when its metadata offset reaches the registered epoch (the offset of its registration record).
Proves
broker_heartbeat_decision classifies a heartbeat as Missing exactly when the broker has no registration, Stale exactly when its broker epoch differs from the registered one, and otherwise Current, caught up exactly when its metadata offset reaches the registered epoch (the offset of its registration record).
Host caller
The broker's heartbeat validation
Proof sessions
Caller preconditions
Heartbeat validation reads the stored epoch from one image, answers Missing and Stale with STALE_BROKER_EPOCH as Kafka's ClusterControlManager.checkBrokerEpoch does (a negative broker id is answered the same way before the kernel), and passes the proved catch-up fact into the controller's heartbeat state machine, which decides fencing and shutdown as Kafka's BrokerHeartbeatManager does. Request decoding, authorization, leader routing, metadata validation, liveness timing, log durability, and leadership or image changes after the snapshot remain host and I/O assumptions.
directory_assignment_decisiondirectory_response_decision directory.rs 2 proofs directory_assignment_decision emits a change only for the reporting broker's exact replica slot and makes an already-current assignment a no-op; directory_response_decision accepts controller success only while the responding controller remains the current leader.
Proves
directory_assignment_decision emits a change only for the reporting broker's exact replica slot and makes an already-current assignment a no-op; directory_response_decision accepts controller success only while the responding controller remains the current leader.
Host caller
The AssignReplicasToDirs request-to-delta planner and broker directory-report sender
Proof sessions
Caller preconditions
The host resolves the topic UUID and partition from one metadata image, derives the unique reporting-replica position from the ordered replica vector, and emits the replica ID read back from that proved position. Applying the narrow PartitionDirAssignment delta preserves replica order and all other slots; metadata-image uniqueness and delta-apply semantics remain host assumptions. The sender compares the captured and current controller identities after decoding the response; transport integrity and a leader change after that final check remain outside the proof.
epoch_and_offset_for_entrieskafka_end_offset_for leader_epoch.rs 1 proof Runs in browser epoch_and_offset_for_entries is Kafka's LeaderEpochFileCache.endOffsetFor: its single postcondition equates the answer with the kafka_end_offset_for case table -- an undefined requested epoch, or no recorded epoch above it (empty cache, future epoch), answers (-1, -1); the latest epoch answers the log end; an epoch below every entry answers itself and the first start; an exact older or gap epoch answers the floor epoch and the start of the next recorded epoch.
Proves
epoch_and_offset_for_entries is Kafka's LeaderEpochFileCache.endOffsetFor: its single postcondition equates the answer with the kafka_end_offset_for case table -- an undefined requested epoch, or no recorded epoch above it (empty cache, future epoch), answers (-1, -1); the latest epoch answers the log end; an epoch below every entry answers itself and the first start; an exact older or gap epoch answers the floor epoch and the start of the next recorded epoch.
Host caller
krabka-log, whose epoch_and_offset_for backs the broker's OffsetForLeaderEpoch rows and Fetch diverging_epoch
Proof sessions
Caller preconditions
Entries have strictly increasing leader epochs and start offsets. The host keeps that order through LeaderEpochCheckpoint::assign (Kafka's LeaderEpochFileCache.assign), which drops every trailing entry the new one does not strictly follow before recording it; the log's append paths and Partition::install_local_leadership, which records a new leader epoch at the log end as Partition.makeLeader does, both write through it. Log::truncate_to trims entries at or past the log end even when no record is discarded, as UnifiedLog.truncateTo does. The host maps (-1, -1) to the wire: verbatim on an OffsetForLeaderEpoch row, OFFSET_OUT_OF_RANGE on a Fetch (Partition.readRecords).
leader_high_watermark isr/watermark.rs 1 proof leader_high_watermark is Kafka's Partition.maybeIncrementLeaderHW + UnifiedLog.maybeIncrementHighWatermark: it returns the current watermark unchanged while the ISR (leader counted) is below the effective min.insync.replicas, and otherwise the larger of the current watermark and the lowest log end among the leader, every ISR member and every caught-up ISR-eligible replica outside the ISR (shouldWaitForReplicaToJoinIsr); the contract equates the result with a reference fold of that rule and proves it never falls and never passes the leader's log end.
Proves
leader_high_watermark is Kafka's Partition.maybeIncrementLeaderHW + UnifiedLog.maybeIncrementHighWatermark: it returns the current watermark unchanged while the ISR (leader counted) is below the effective min.insync.replicas, and otherwise the larger of the current watermark and the lowest log end among the leader, every ISR member and every caught-up ISR-eligible replica outside the ISR (shouldWaitForReplicaToJoinIsr); the contract equates the result with a reference fold of that rule and proves it never falls and never passes the leader's log end.
Host caller
krabka-broker
Proof sessions
Caller preconditions
The host passes the committed ISR for Kafka's maximal ISR (an in-flight expansion is not tracked; its replica is caught up and eligible, so it holds the watermark either way), -1 for a replica that has not fetched from this leader, the current watermark clamped to the log end (Kafka's truncateTo), and the LeaderPolicy the ISR scan last installed from the metadata image: effective_min_insync_replicas (the controller's threshold, so the watermark stops exactly where KIP-966 starts naming eligible leaders; the broker's static default_min_insync_replicas fallback is not consulted), replica.lag.time.max.ms, and each replica's fencing and alive broker epoch. Before the first scan the policy is min ISR 1 with no replica eligible. The follower's fetch broker epoch is recorded by ReplicaState::record_follower_broker_epoch; controlled shutdown is not in the image and is left to the controller's AlterPartition eligibility check.
isr_candidate_selectedreplica_isr_eligibleisr_proposal_changedfollower_caught_up_creditisr_maintenance_selected isr/watermark.rsisr/admission.rs 5 proofs Runs in browser isr_candidate_selected keeps the assigned leader, drops an unassigned member, keeps an in-sync follower exactly while Kafka's ReplicaState.isCaughtUp holds (Partition.getOutOfSyncReplicas), and admits an out-of-sync follower exactly when Kafka's Partition.needsExpandIsr would: its log end reaches the leader's high watermark and the leader's epoch start offset (isFollowerInSync) and replica_isr_eligible holds (isReplicaIsrEligible: not fenced, not shutting down, fetch broker epoch -1 or equal to the alive broker epoch, both present); isr_proposal_changed suppresses exactly proposals with no unique additions or removals. follower_caught_up_credit chooses Kafka's Replica.updateFetchStateOrThrow caught-up credit: this fetch when it reaches the leader's log end, the previous fetch when it reaches the log end recorded at that fetch, otherwise none. isr_maintenance_selected, the scan rule that isr_candidate_selected replaced, keeps its proof and has no broker caller; only the leader_failover_model still uses it to re-admit a revived follower.
Proves
isr_candidate_selected keeps the assigned leader, drops an unassigned member, keeps an in-sync follower exactly while Kafka's ReplicaState.isCaughtUp holds (Partition.getOutOfSyncReplicas), and admits an out-of-sync follower exactly when Kafka's Partition.needsExpandIsr would: its log end reaches the leader's high watermark and the leader's epoch start offset (isFollowerInSync) and replica_isr_eligible holds (isReplicaIsrEligible: not fenced, not shutting down, fetch broker epoch -1 or equal to the alive broker epoch, both present); isr_proposal_changed suppresses exactly proposals with no unique additions or removals. follower_caught_up_credit chooses Kafka's Replica.updateFetchStateOrThrow caught-up credit: this fetch when it reaches the leader's log end, the previous fetch when it reaches the log end recorded at that fetch, otherwise none. isr_maintenance_selected, the scan rule that isr_candidate_selected replaced, keeps its proof and has no broker caller; only the leader_failover_model still uses it to re-admit a revived follower.
Host caller
The broker's ISR maintenance proposal builder; leader_high_watermark also calls replica_isr_eligible; the data-path model drives the expansion arm; follower progress in replica_state.rs takes the caught-up credit
Proof sessions
Caller preconditions
The replica-state installation seeds a new ISR member as caught up at installation and not yet fetched (Kafka's resetReplicaState); a replica first heard from outside the ISR starts never caught up. Each follower fetch records the fetch offset, the leader log end at arrival, and Instant::now(), and the host keeps the later of the credited and recorded caught-up times and the maximum leader log end seen. The host makes one decision per sorted BTreeSet candidate, so output is unique and sorted. It reads the leader's log end and epoch start (the leader-epoch checkpoint entry for the image's leader epoch; None admits nobody) once per scan, the high watermark and follower state under the same replica_state lock, and derives "within replica.lag.time.max.ms" from the scan's single Instant. Eligibility facts come from the LeaderPolicy the scan installs and the fetch-recorded broker epoch; the image carries no controlled-shutdown state, so shutting_down is always false on the leader and the controller's INELIGIBLE_REPLICA check covers it.
isr_admission isr/admission.rs 1 proof isr_admission is exactly Kafka's ReplicationControlManager.validateAlterPartitionData order after the unknown-partition check: a leader or partition epoch above the controller's answers NOT_CONTROLLER, an older leader epoch FENCED_LEADER_EPOCH, a requester other than the leader INVALID_REQUEST, an older partition epoch INVALID_UPDATE_VERSION, an invalid ISR, an ISR without the leader, or an illegal recovery state INVALID_REQUEST, an ineligible member INELIGIBLE_REPLICA; it admits exactly when both epochs match and every other fact holds, so a stale proposal never overwrites a newer ISR.
Proves
isr_admission is exactly Kafka's ReplicationControlManager.validateAlterPartitionData order after the unknown-partition check: a leader or partition epoch above the controller's answers NOT_CONTROLLER, an older leader epoch FENCED_LEADER_EPOCH, a requester other than the leader INVALID_REQUEST, an older partition epoch INVALID_UPDATE_VERSION, an invalid ISR, an ISR without the leader, or an illegal recovery state INVALID_REQUEST, an ineligible member INELIGIBLE_REPLICA; it admits exactly when both epochs match and every other fact holds, so a stale proposal never overwrites a newer ISR.
Host caller
The broker's AlterPartition row adapter
Proof sessions
Caller preconditions
The host reads both current epochs and the leader from one image snapshot, compares the request's brokerId with the leader, classifies the ISR as Kafka's Replicas.validateIsr does (non-negative, distinct, assigned) before checking leader membership, folds an unknown recovery-state value into the recovery fact (Kafka throws there; krabka answers INVALID_REQUEST), and validates eligibility against registration and the heartbeat registry's active snapshot with the -1 epoch sentinel for v2 rows. Partition-epoch exhaustion after admission answers INVALID_REQUEST.
wal_reservation_epoch_readywal_reservation_frontierreserve_offsetswal_reservation_response offset_allocator.rs 4 proofs wal_reservation_epoch_ready requires the current leader marker to be committed before allocation; wal_reservation_frontier extends only an exact positive, non-overflowing chain of uncommitted reservations; reserve_offsets admits exactly a nonnegative frontier, a positive count, and an end that fits i64, returning the exact durable base and next offset, and None otherwise; and wal_reservation_response admits only a matching request, valid range, and current leader epoch.
Proves
wal_reservation_epoch_ready requires the current leader marker to be committed before allocation; wal_reservation_frontier extends only an exact positive, non-overflowing chain of uncommitted reservations; reserve_offsets admits exactly a nonnegative frontier, a positive count, and an end that fits i64, returning the exact durable base and next offset, and None otherwise; and wal_reservation_response admits only a matching request, valid range, and current leader epoch.
Host caller
The KRaft controller's reservation submit path and the broker's WAL offset sequencer
Proof sessions
Caller preconditions
The controller actor serializes submissions and preserves append order in its waiter list. A new leader rejects reservations until its epoch marker crosses the HWM, so every inherited entry before that marker is committed and applied or has been truncated; within one ready epoch, the host folds every same-partition waiter in append order before allocating. Negative frontiers, nonpositive counts, and overflowing ends are rejected by the proved allocation itself, and the submit path fails such a record with ChangeRejected. Results are released only after their metadata batch commits and applies; leadership loss fails the old waiter, and a retry waits for the next epoch marker before allocation. The broker binds the single response row to the exact topic, partition, count, and locally observed epoch. A broker-only observer cannot read a controller epoch, so it requires a nonnegative response epoch and relies on the private one-shot RPC to correlate the committed response. Log durability, HWM correctness, metadata encoding and replay, RPC correlation, transport, and I/O remain host assumptions. Malformed, stale, zero-count, overflowing, unready-epoch, and failed responses fail closed.
site_loss_survivors stretch/min_insync_is_site_loss_safe.rs 1 proof Runs in browser site_loss_survivors returns what the loss of a fullest site leaves under the round-robin placement model round_robin_load (replica r on site r % sites): the loss of site 0 leaves exactly the result and no site loss leaves fewer; the proof derives the closed form rf - ceil(rf / sites) from the model (lemma_round_robin_load, lemma_site_load_at_most_first).
Proves
site_loss_survivors returns what the loss of a fullest site leaves under the round-robin placement model round_robin_load (replica r on site r % sites): the loss of site 0 leaves exactly the result and no site loss leaves fewer; the proof derives the closed form rf - ceil(rf / sites) from the model (lemma_round_robin_load, lemma_site_load_at_most_first).
Host caller
krabka-verified calls it from min_insync_is_site_loss_safe; the krabka-broker stretch model calls it directly.
Proof sessions
Caller preconditions
1 <= rf <= 1024; 1 <= sites <= 1024. That the broker's replica placer places replicas round-robin over sites is a host assumption, not proved.
min_insync_is_site_loss_safe stretch/min_insync_is_site_loss_safe.rs 1 proof Runs in browser min_insync_is_site_loss_safe returns true exactly when, under the round-robin placement model, no site holds min_insync replicas and every single-site loss leaves at least min_insync.
Proves
min_insync_is_site_loss_safe returns true exactly when, under the round-robin placement model, no site holds min_insync replicas and every single-site loss leaves at least min_insync.
Host caller
krabka-broker
Proof sessions
Caller preconditions
1 <= rf <= 1024; 1 <= sites <= 1024. Round-robin placement is a host assumption, as above.
quorum_survives_any_single_site_loss stretch/quorum_survives_any_single_site_loss.rs 1 proof Runs in browser quorum_survives_any_single_site_loss checks that each one-site loss leaves a strict voter majority.
Proves
quorum_survives_any_single_site_loss checks that each one-site loss leaves a strict voter majority.
Host caller
The krabka-broker stretch model; no production caller.
Proof sessions
Caller preconditions
At most 1024 sites; each site has between 0 and 1024 voters. The companion lemma_two_sites_never_survive proves that no two-site split ever passes.
wal_fetch_admission wal/select_wal_voters.rs 1 proof wal_fetch_admission fails closed unless authenticated and claimed nodes agree, the local node leads the placement, the claimant is a voter, and the requested epoch is current or unset.
Proves
wal_fetch_admission fails closed unless authenticated and claimed nodes agree, the local node leads the placement, the claimant is a voter, and the requested epoch is current or unset.
Host caller
krabka-broker
Proof sessions
Caller preconditions
The host maps the authenticated identity and current placement exactly into primitive node IDs and voter order.
wal_batch_equal wal/batch_equal.rs 1 proof wal_batch_equal is true exactly when both inclusive offset coordinates and every encoded batch byte agree; equal lengths or offset frontiers alone are insufficient.
Proves
wal_batch_equal is true exactly when both inclusive offset coordinates and every encoded batch byte agree; equal lengths or offset frontiers alone are insufficient.
Host caller
Production WAL follower promotion; the local byte-majority recovery harness
Proof sessions
Caller preconditions
The caller supplies the observed bytes and faithfully decoded batch coordinates. Promotion’s outer length/zip fold compares the complete validated overlap; filesystem reads and durability remain outside the kernel.
exact_wal_batch_range wal/batch_equal.rs 1 proof exact_wal_batch_range accepts only an empty exact range or decoded batches whose first base equals the requested start, every checked last + 1 equals the next base, and final successor equals the target; gaps, overlaps, reordering, inverted batches, and successor overflow fail closed.
Proves
exact_wal_batch_range accepts only an empty exact range or decoded batches whose first base equals the requested start, every checked last + 1 equals the next base, and final successor equals the target; gaps, overlaps, reordering, inverted batches, and successor overflow fail closed.
Host caller
The diskless WAL exact_batches gate used by source and replica readers
Proof sessions
Caller preconditions
The host decodes and validates each record batch first, then passes parallel base/last arrays in the original byte order plus the exact requested half-open bounds.
wal_covering_batch_range wal/batch_equal.rs 2 proofs wal_covering_batch_range admits exactly whole-batch physical ranges whose first batch contains the logical read start and whose last successor equals the requested end. It returns the physical start, rejects gaps, overlaps, missing or partial batches, and admits only an empty batch list for an empty logical window. exact_wal_batch_range additionally exports an independent global layout and per-batch interval bounds.
Proves
wal_covering_batch_range admits exactly whole-batch physical ranges whose first batch contains the logical read start and whose last successor equals the requested end. It returns the physical start, rejects gaps, overlaps, missing or partial batches, and admits only an empty batch list for an empty logical window. exact_wal_batch_range additionally exports an independent global layout and per-batch interval bounds.
Host caller
The promotion covering reader and physical-copy/logical-trim adapter
Proof sessions
Caller preconditions
The decoded metadata faithfully describes the complete bytes returned by the raw reader. Empty destinations reset at the first physical batch base and restore the reconciled logical floor. The follower checkpoints a higher canonical floor before that reset. Filesystem durability and concurrent leadership changes remain host assumptions.
wal_checkpoint_range_valid wal/batch_equal.rs 2 proofs wal_checkpoint_range_valid requires a nonnegative recovered range containing the checkpoint and, for a nonempty checkpoint, an observed whole-batch successor equal to its exclusive end. Interior starts and empty ranges are valid.
Proves
wal_checkpoint_range_valid requires a nonnegative recovered range containing the checkpoint and, for a nonempty checkpoint, an observed whole-batch successor equal to its exclusive end. Interior starts and empty ranges are valid.
Host caller
WAL follower checkpoint recovery
Proof sessions
Caller preconditions
The observed last offset comes from the actual recovered end or the raw read containing the checkpoint's final offset. Empty ranges reset at their floor; nonempty ranges truncate whole batches and then trim their logical start. Accurate headers, fsync, and checkpoint publication remain host assumptions.
select_wal_voter_index wal/select_wal_voters.rs 1 proof select_wal_voter_index returns the first candidate, in the caller's order, that the voter_blocked rule does not block (node already used, rack already used, or, for the first placement, not the local node), and None exactly when every candidate is blocked.
Proves
select_wal_voter_index returns the first candidate, in the caller's order, that the voter_blocked rule does not block (node already used, rack already used, or, for the first placement, not the local node), and None exactly when every candidate is blocked.
Host caller
The verified select_wal_voters fold, called by the broker's diskless WAL placement policy
Proof sessions
Caller preconditions
The host sorts brokers by node ID, maps exact rack-string equality to compact IDs, and maps the proved index back to the same candidate list.
select_wal_voters wal/select_wal_voters.rs 1 proof select_wal_voters folds the actual local-first selector into a placement of distinct nodes and racks, no longer than requested, using only supplied candidates. Empty results occur exactly for a zero request or absent local node; a nonempty incomplete result blocks every remaining candidate by node or rack.
Proves
select_wal_voters folds the actual local-first selector into a placement of distinct nodes and racks, no longer than requested, using only supplied candidates. Empty results occur exactly for a zero request or absent local node; a nonempty incomplete result blocks every remaining candidate by node or rack.
Host caller
The broker's WAL placement adapter
Proof sessions
Caller preconditions
The host sorts broker records by node ID and interns exact rack-string equality; records without a rack are absent. This is greedy maximal placement, not global maximum placement on conflicting duplicate metadata rows. Full placement and configured rack loss compose with installation and quorum arithmetic.
wal_voter_set_valid wal/select_wal_voters.rs 1 proof wal_voter_set_valid admits exactly a nonempty set of the expected size, headed by the local node, with pairwise distinct node IDs. Duplicate IDs cannot contribute the same durable-offset map entry twice.
Proves
wal_voter_set_valid admits exactly a nonempty set of the expected size, headed by the local node, with pairwise distinct node IDs. Duplicate IDs cannot contribute the same durable-offset map entry twice.
Host caller
The WAL engine's quorum installation boundary
Proof sessions
Caller preconditions
The adapter maps the same node IDs as the installed vector. Rack diversity comes from the placement constructor; this guard validates identity/shape, not physical topology, fsync acknowledgements, or byte equality.

Storage and log 24

compute_horizon compaction/retain_decision.rs 1 proof compute_horizon equals compute_horizon_model, the sum of the timestamp and the retention time clamped to the i64 bounds.
Proves
compute_horizon equals compute_horizon_model, the sum of the timestamp and the retention time clamped to the i64 bounds.
Host caller
krabka-verified calls it from retain_decision; krabka-log also calls it in tests.
Proof sessions
Caller preconditions
Accepts every value of the Rust input types.
retain_decision compaction/retain_decision.rs 1 proof Runs in browser retain_decision implements the KIP-534 record and control-marker retention rules.
Proves
retain_decision implements the KIP-534 record and control-marker retention rules.
Host caller
krabka-log
Proof sessions
Caller preconditions
Accepts every value of the Rust input types.
compaction_decode_step compaction/retain_decision.rs 1 proof compaction_decode_step treats zero remaining input as the only clean end-of-stream and requires every successful batch decode to consume bytes.
Proves
compaction_decode_step treats zero remaining input as the only clean end-of-stream and requires every successful batch decode to consume bytes.
Host caller
krabka-log, shared by compaction's map and rewrite passes
Proof sessions
Caller preconditions
The host passes exact cursor lengths and the RecordBatch::decode result; corruption is surfaced before swap promotion.
remote_covered_through retention/coverage.rs 1 proof remote_covered_through returns the greatest contiguous inclusive remote coverage containing the local anchor: every offset in the returned prefix belongs to an actual range, the endpoint is attained, and no adjacent range can extend it. It rejects malformed ranges anywhere in the listing and returns no coverage exactly when no valid range contains the anchor.
Proves
remote_covered_through returns the greatest contiguous inclusive remote coverage containing the local anchor: every offset in the returned prefix belongs to an actual range, the endpoint is attained, and no adjacent range can extend it. It rejects malformed ranges anywhere in the listing and returns no coverage exactly when no valid range contains the anchor.
Host caller
The broker's shared coverage adapter, used by local physical retention and copy resumption
Proof sessions
Caller preconditions
The adapter sorts ranges by start. Overlap, duplicate starts, obsolete disconnected prefixes and signed extrema are supported. An obsolete range cannot move copying below the local start or suppress a later anchored component. Metadata truth, completed object copies, coherent snapshots and subsequent physical deletion remain host or I/O assumptions.
local_retention_prefixremote_retention_prefixretention_delete_target retention/remote_retention_model.rsretention/delete_target.rs 3 proofs local_retention_prefix returns exactly the length that the reference fold local_retention_model gives. That fold is Kafka's UnifiedLog.deleteOldSegments: a retention.bytes pass deletes an oldest segment only while the remaining debt covers the whole segment (diff - size >= 0), and it runs only when the log is at least its budget. A retention.ms pass then continues from where the size pass stopped, with the log-start breach folded into its flag. The walk covers every local segment with the active one last, as Kafka's deletableSegments does, and never takes a newest segment that is empty (isLastSegmentAndEmpty, local_retention_limit); every pass stops at a blocked segment. Independently of reference equality, the first kept segment before the deletion limit is blocked or not expired; if unblocked, its prefix byte cost exceeds the initial size debt. Each selected non-expired row fits that debt together with all older rows. remote_retention_prefix returns exactly remote_retention_model, which is Kafka's RemoteLogRetentionHandler per-segment rule: a log-start breach leaves the debt alone, a time breach lowers it but not below zero, and a size breach needs a positive debt that covers the whole segment. Independent charged-prefix bounds justify each selected row and require the first kept row to fail floor, time and size eligibility, even when cumulative bytes exceed u64::MAX. A tier that accepts no delete selects nothing. retention_delete_target advances an inclusive last offset without signed overflow and pins every result variant.
Proves
local_retention_prefix returns exactly the length that the reference fold local_retention_model gives. That fold is Kafka's UnifiedLog.deleteOldSegments: a retention.bytes pass deletes an oldest segment only while the remaining debt covers the whole segment (diff - size >= 0), and it runs only when the log is at least its budget. A retention.ms pass then continues from where the size pass stopped, with the log-start breach folded into its flag. The walk covers every local segment with the active one last, as Kafka's deletableSegments does, and never takes a newest segment that is empty (isLastSegmentAndEmpty, local_retention_limit); every pass stops at a blocked segment. Independently of reference equality, the first kept segment before the deletion limit is blocked or not expired; if unblocked, its prefix byte cost exceeds the initial size debt. Each selected non-expired row fits that debt together with all older rows. remote_retention_prefix returns exactly remote_retention_model, which is Kafka's RemoteLogRetentionHandler per-segment rule: a log-start breach leaves the debt alone, a time breach lowers it but not below zero, and a size breach needs a positive debt that covers the whole segment. Independent charged-prefix bounds justify each selected row and require the first kept row to fail floor, time and size eligibility, even when cumulative bytes exceed u64::MAX. A tier that accepts no delete selects nothing. retention_delete_target advances an inclusive last offset without signed overflow and pins every result variant.
Host caller
The log's local retention tick and the broker's local and remote KIP-405 retention walks
Proof sessions
Caller preconditions
The local tick passes one LocalRetentionSegment per local segment in oldest-first order with the active segment last, with exact sizes, the log-start breach (next segment's base at or below the log start; never for the active segment) OR-ed with the retention.ms breach measured from Kafka's largestTimestamp() (newest record timestamp, or the .log file's modification time when no record carries a non-negative one), and blocked from a freshly scanned delivery watermark. Its size debt is None unless the policy deletes, retention.bytes is set, and the whole log, active segment included, is at least the budget. When the prefix covers every segment it rolls first, as Kafka's deleteSegments does, then removes only the selected bases from memory and indexes, attempts their required and optional files and their producer snapshots (deleteProducerSnapshots); file-delete errors leave recoverable orphan files but cannot expand the selected prefix. Log does not know the high watermark, so Kafka's highWatermark >= upperBoundOffset bound is the caller's responsibility and is not enforced today. The tiered local host sets blocked for a segment that the remote tier does not cover whole, appends a blocked fact for the active segment (never copied), ages a segment whose records claim a future timestamp by its file's modification time, and takes its debt from the whole local log size; unlike Kafka it does not roll a breaching active segment. The remote host passes filtered and sorted CopySegmentFinished segments; its time axis is Kafka's maxTimestampMs < now - retention.ms (absent while that cutoff is negative), and its debt is Kafka's buildRetentionSizeData total: remote bytes plus the local bytes above the highest remote offset (onlyLocalLogSegmentsSize).
remote_retention_floor_step retention/remote_floor.rs 2 proofs remote_retention_floor_step extends a logical floor only through a completed range reaching that floor, using the checked inclusive-to-exclusive successor. Gaps, malformed coordinates and successor exhaustion close the run permanently; every crossed offset lies inside the supplied range.
Proves
remote_retention_floor_step extends a logical floor only through a completed range reaching that floor, using the checked inclusive-to-exclusive successor. Gaps, malformed coordinates and successor exhaustion close the run permanently; every crossed offset lies inside the supplied range.
Host caller
The broker's remote retention completion loop
Proof sessions
Caller preconditions
The composition derives all three policy facts from actual ranges and proves maximal policy selection, stopping at the first failed/missing completion observation, offset coverage by completed deletions and progress for connected representable prefixes. Expiry flags, byte debt, truthful metadata, real lifecycle completion, concurrent I/O and durable floor publication remain host obligations.
barrier_cut_expired retention/remote_retention_model.rs 1 proof barrier_cut_expired expires a held barrier cut only when a positive retained-cut count yields a representable cutoff and the held epoch is at or below that exact cutoff.
Proves
barrier_cut_expired expires a held barrier cut only when a positive retained-cut count yields a representable cutoff and the held epoch is at or below that exact cutoff.
Host caller
The barrier coordinator's retention selector; barrier epoch allocation uses the separately proved exact_epoch_successor adapter above
Proof sessions
Caller preconditions
Barrier retention is count-based, as persisted in GroupValue.retained_cuts; no timestamp, duration, or wall clock participates. The host supplies the newly published epoch, persisted count, and exact held epochs. Persistence appends tombstones for precisely the returned epochs, and recovery may replay the prior state until those records commit.
offset_index_lookup log_index.rs 1 proof Runs in browser offset_index_lookup returns the position for the greatest relative offset at or below the target.
Proves
offset_index_lookup returns the position for the greatest relative offset at or below the target.
Host caller
krabka-log
Proof sessions
Caller preconditions
Entries have strictly increasing relative offsets.
time_index_scan_start log_index.rs 1 proof time_index_scan_start returns the last indexed offset with a timestamp strictly below the target, or zero when none exists, including i64::MIN. It reuses the binary search above.
Proves
time_index_scan_start returns the last indexed offset with a timestamp strictly below the target, or zero when none exists, including i64::MIN. It reuses the binary search above.
Host caller
TimeIndex::scan_start, used by both segment timestamp searches
Proof sessions
Caller preconditions
Entries have nondecreasing timestamps. Global earliest-match correctness additionally requires accurate prefix maxima and a complete forward scan; the composition theorems establish the semantic link from actual records, while batch-header decoding and windowed I/O remain external.
offset_index_position_at_or_after log_index.rs 1 proof offset_index_position_at_or_after returns the position for the least relative offset at or above the target.
Proves
offset_index_position_at_or_after returns the position for the least relative offset at or above the target.
Host caller
krabka-log
Proof sessions
Caller preconditions
Entries have strictly increasing relative offsets.
time_index_lookup log_index.rs 1 proof time_index_lookup returns the relative offset for the last timestamp at or below the target.
Proves
time_index_lookup returns the relative offset for the last timestamp at or below the target.
Host caller
The strict scan-start kernel below
Proof sessions
Caller preconditions
Entries have nondecreasing timestamps.
timestamp_record_time timestamp.rs 2 proofs timestamp_record_time chooses an append stamp verbatim or checked producer base-plus-delta time. Ignored producer overflow never rejects append time.
Proves
timestamp_record_time chooses an append stamp verbatim or checked producer base-plus-delta time. Ignored producer overflow never rejects append time.
Host caller
Local batch timestamp decoding and remote encoded-record scanning
Proof sessions
Caller preconditions
The host projects Some(batch.max_timestamp) exactly for LogAppendTime and None for CreateTime. The composition carries actual checked offset coordinates through floor-aware selection and exclusive visibility; it assumes a complete ordered data batch, valid offsets/CreateTime sums, and coherent frontiers. Binary decoding, header truthfulness and object enumeration remain external.
first_timestamp_record_indexearliest_max_timestamp_indextimestamp_record_coordinatestimestamp_scan_nexttimestamp_scan_window timestamp.rs 5 proofs first_timestamp_record_index selects the first decoded record at or after both the logical floor and target timestamp; earliest_max_timestamp_index selects the earliest maximum timestamp; timestamp_record_coordinates derives absolute record coordinates without signed overflow; timestamp_scan_next advances strictly past a fresh decoded batch; and timestamp_scan_window doubles only a positive representable retry window.
Proves
first_timestamp_record_index selects the first decoded record at or after both the logical floor and target timestamp; earliest_max_timestamp_index selects the earliest maximum timestamp; timestamp_record_coordinates derives absolute record coordinates without signed overflow; timestamp_scan_next advances strictly past a fresh decoded batch; and timestamp_scan_window doubles only a positive representable retry window.
Host caller
The local segment timestamp scan, for indexed lookup and maximum-timestamp recovery
Proof sessions
Caller preconditions
The host supplies CRC-validated batches and records in on-disk order. The time-index floor does not skip the earliest qualifying record, and a segment's stored maximum timestamp equals the maximum decoded record timestamp, so the first record at or above it is an exact earliest maximum. Each read returns whole batches in order. Decode and file I/O remain host assumptions; read, coordinate, stale-cursor, and retry-window failures return no result.
stamp_ranges_validstamp_range_insertion_indexexact_stamp_range_indexcovering_stamp_range_index stamp.rs 4 proofs stamp_ranges_valid accepts exactly sorted, nonoverlapping, non-inverted inclusive ranges; stamp_range_insertion_index finds a boundary-preserving insertion point or rejects overlap; exact_stamp_range_index selects only an exact pair of range boundaries for idempotent replacement or removal; and covering_stamp_range_index is equivalent to inclusive coverage.
Proves
stamp_ranges_valid accepts exactly sorted, nonoverlapping, non-inverted inclusive ranges; stamp_range_insertion_index finds a boundary-preserving insertion point or rejects overlap; exact_stamp_range_index selects only an exact pair of range boundaries for idempotent replacement or removal; and covering_stamp_range_index is equivalent to inclusive coverage.
Host caller
The local log's commit-stamp sidecar, across open, append, transactional upsert, startup removal, and point lookup
Proof sessions
Caller preconditions
The host decodes complete fixed-width big-endian entries, sorts them by both boundaries and stamp, removes only fully identical retry records, and validates the result before installation. Successful mutations keep the in-memory ranges in canonical order; exact upsert and removal rewrite the sidecar before swapping memory. File reads, appends, rewrites, and synchronization remain I/O assumptions. A partial fixed-width record or conflicting persisted range fails startup closed; an interrupted in-place rewrite may require log recovery, while an uncertain append may leave an exact duplicate that startup safely canonicalizes.
producer_snapshot_reload_log_startproducer_snapshot_reload_keepsproducer_snapshot_strayproducer_snapshot_latest_indexproducer_snapshot_entry_validproducer_snapshot_replay_start producer_snapshot/snapshot_transaction_valid.rsproducer_snapshot/replay_start.rs 7 proofs producer_snapshot_reload_log_start picks Kafka LogLoader.load's reload log start (the checkpointed floor, 0 when absent, under remote storage; otherwise the local log start); producer_snapshot_reload_keeps keeps exactly the snapshots in (log start, log end], Kafka truncateAndReload's rule (the snapshot at the log start is deleted); producer_snapshot_stray deletes exactly what Kafka removeStraySnapshots deletes: a snapshot at no segment base unless it is the newest and above every base; producer_snapshot_latest_index selects the greatest offset the reload keeps without relying on directory order; producer_snapshot_entry_valid equals snapshot_entry_valid_model over a named ProducerSnapshotEntryFacts (Kafka's snapshot entry schema): a real identity, a coordinator epoch of at least -1, last-record fields that are the marker-only sentinel (-1, -1, 0) or a batch ending before the snapshot offset, and an open transaction that is absent or starts before the snapshot offset and no later than the last record; producer_snapshot_replay_start equals Kafka rebuildProducerState's max(mapEndOffset, first local base, logStartOffset) and rejects a snapshot the reload would have deleted.
Proves
producer_snapshot_reload_log_start picks Kafka LogLoader.load's reload log start (the checkpointed floor, 0 when absent, under remote storage; otherwise the local log start); producer_snapshot_reload_keeps keeps exactly the snapshots in (log start, log end], Kafka truncateAndReload's rule (the snapshot at the log start is deleted); producer_snapshot_stray deletes exactly what Kafka removeStraySnapshots deletes: a snapshot at no segment base unless it is the newest and above every base; producer_snapshot_latest_index selects the greatest offset the reload keeps without relying on directory order; producer_snapshot_entry_valid equals snapshot_entry_valid_model over a named ProducerSnapshotEntryFacts (Kafka's snapshot entry schema): a real identity, a coordinator epoch of at least -1, last-record fields that are the marker-only sentinel (-1, -1, 0) or a batch ending before the snapshot offset, and an open transaction that is absent or starts before the snapshot offset and no later than the last record; producer_snapshot_replay_start equals Kafka rebuildProducerState's max(mapEndOffset, first local base, logStartOffset) and rejects a snapshot the reload would have deleted.
Host caller
The log's snapshot codec, reload, and stray removal, open-time recovery, and tail truncation and head trim; recovered batches also use the bounded replay_batch_cursor_decision.
Proof sessions
Caller preconditions
The host supplies offsets parsed from canonical fixed-width filenames (distinct by construction), every local segment base including the active one, and every decoded entry field unchanged. Open runs stray removal and then the reload after the log-start checkpoint is restored, and every reload ends with a snapshot at the log end when that is above the log start (Kafka rebuildProducerState then takeSnapshot); truncation deletes out-of-range snapshots before removing any segment and replays only when producer or unreplicated-transaction state existed; a head trim, a retention deletion, and a tiered local eviction each delete the snapshot at every dropped segment's base and leave in-memory producers alone. When the replay cursor lands inside a batch, the host replays that whole batch (Kafka LogSegment.read). CRC, byte decoding, directory enumeration, and HashMap duplicate detection remain host assumptions; malformed or future-state snapshots are removed and recovery retries the next proved candidate. The captured log end bounds every replay batch, and the admitted exclusive cursor is threaded into the next read. Snapshot create, file sync, rename, deletion, and crash atomicity remain filesystem assumptions; the parent directory is not synced.
earliest_timestamp_candidatefirst_timestamp_record_index list_offsets/timestamp_candidate.rstimestamp.rs 3 proofs earliest_timestamp_candidate selects the least candidate offset, preserves its attached timestamp, prefers remote on ties, and returns no candidate exactly when both tiers found none. first_timestamp_record_index finds the first decoded record meeting both the logical offset floor and timestamp.
Proves
earliest_timestamp_candidate selects the least candidate offset, preserves its attached timestamp, prefers remote on ties, and returns no candidate exactly when both tiers found none. first_timestamp_record_index finds the first decoded record meeting both the logical offset floor and timestamp.
Host caller
The broker’s timestamp merger and the log’s windowed timestamp scan; remote reads pass the same logical floor to the record decoder
Proof sessions
Caller preconditions
Candidate arrays are complete, ordered by offset, and accurately decoded. Objects may have gaps and tier windows may overlap; remote hits cannot substitute for checking earlier local matches. Successful remote hits below the local start need no local read. Remote timeout and oversized-record errors retain their response policy. Logical-floor capture, metadata enumeration, decoded bytes, epoch projection, and overlapping-copy consistency remain host obligations.
list_offsets_kindlist_offsets_epoch_decisionlist_offsets_bound_decisionlist_offsets_earliestlist_offsets_selection_decision list_offsets/selection_model.rslist_offsets/selection_decision.rs 6 proofs list_offsets_kind classifies versioned timestamp sentinels; list_offsets_epoch_decision applies the request leader-epoch fence; list_offsets_bound_decision selects exactly the log end for a replica, the high watermark for a read-uncommitted consumer, and min(LSO, HWM) for a read-committed one; list_offsets_earliest selects the minimum available local, remote, and diskless start; and list_offsets_selection_decision equals the reference rule list_offsets_selection_model on every variant: EARLIEST/EARLIEST_LOCAL resolve to their start unclamped, LATEST to min(candidate, last_fetchable), and every record-derived kind to its candidate only when strictly below last_fetchable (otherwise unknown), as Partition.fetchOffsetForTimestamp does.
Proves
list_offsets_kind classifies versioned timestamp sentinels; list_offsets_epoch_decision applies the request leader-epoch fence; list_offsets_bound_decision selects exactly the log end for a replica, the high watermark for a read-uncommitted consumer, and min(LSO, HWM) for a read-committed one; list_offsets_earliest selects the minimum available local, remote, and diskless start; and list_offsets_selection_decision equals the reference rule list_offsets_selection_model on every variant: EARLIEST/EARLIEST_LOCAL resolve to their start unclamped, LATEST to min(candidate, last_fetchable), and every record-derived kind to its candidate only when strictly below last_fetchable (otherwise unknown), as Partition.fetchOffsetForTimestamp does.
Host caller
The broker's end-to-end ListOffsets partition resolver, legacy v0 resolver, and leader-epoch adapter
Proof sessions
Caller preconditions
The host supplies the decoded wire version and sentinel, the current partition epoch, and exact local visibility offsets. Only the offset selected by the replica and isolation path must be valid; unused local or transaction facts cannot reject a valid tiered HWM. Remote metadata and the diskless index supply optional earliest candidates, and negative candidates fail closed. Tier-specific timestamp, maximum, and frontier lookups run before the final kernel. A remote timeout returns a per-partition timeout. A remote-storage error follows the existing sentinel-specific fallback or unknown-result path. An absent result remains unknown, and a pending-upload successor uses checked arithmetic. The new composition establishes first-match existence and exclusive isolation from complete decoded arrays and truthful sparse bounds. Remote/object I/O, cross-tier enumeration, epoch projection, metadata snapshot consistency, and changes after the gathered snapshot remain host or I/O assumptions.
latest_checkpoint_indexcheckpoint_id_newercheckpoint_id_retained checkpoint.rs 3 proofs latest_checkpoint_index selects a maximum (end offset, epoch) id; checkpoint_id_newer proves the lexicographic order and checkpoint_id_retained retains exactly the checkpoints whose full id equals one of the two ids it is given, and no partial match of either.
Proves
latest_checkpoint_index selects a maximum (end offset, epoch) id; checkpoint_id_newer proves the lexicographic order and checkpoint_id_retained retains exactly the checkpoints whose full id equals one of the two ids it is given, and no partial match of either.
Host caller
KRaft's checkpoint directory scanner and cleanup
Proof sessions
Caller preconditions
The host's recent_checkpoint_ids supplies the two newest ids by applying latest_checkpoint_index twice (the selected id, then the runner-up among the rest), so a FetchSnapshot reader can finish the id it started on across a snapshot roll; a directory holding a single checkpoint passes the selected id as both. That these are the two newest is a host fact, not part of the retention contract. The host accepts only UTF-8 filenames that parse and round-trip to the canonical fixed-width <end offset>-<epoch>.checkpoint form. Directory enumeration, file reads, atomic temp-file rename, and best-effort deletion remain filesystem assumptions. Malformed names are ignored and never selected or deleted.
future_log_swap_admission storage/future_log_swap_admission.rs 1 proof future_log_swap_admission permits a future-log swap only when the current and future log-end offsets are exactly equal, preventing both truncation and unreplicated suffix promotion.
Proves
future_log_swap_admission permits a future-log swap only when the current and future log-end offsets are exactly equal, preventing both truncation and unreplicated suffix promotion.
Host caller
The broker's future-log swap guard
Proof sessions
Caller preconditions
The host holds both log locks and supplies their exact log-end offsets.
local_truncation_plantruncation_relative_offsettruncation_batch_retainedtruncation_frontier storage/future_log_swap_admission.rs 4 proofs local_truncation_plan selects the exact sealed-segment prefix and active-segment disposition at an exclusive cut; truncation_relative_offset rejects negative, reversed, and u32-overflowing relative offsets; truncation_batch_retained keeps exactly decoded batches ending before the cut; truncation_frontier clamps dependent frontiers to the actual retained log end.
Proves
local_truncation_plan selects the exact sealed-segment prefix and active-segment disposition at an exclusive cut; truncation_relative_offset rejects negative, reversed, and u32-overflowing relative offsets; truncation_batch_retained keeps exactly decoded batches ending before the cut; truncation_frontier clamps dependent frontiers to the actual retained log end.
Host caller
The local log's truncation coordinator, segment truncation walk, and transaction-index rewrite
Proof sessions
Caller preconditions
The loaded log supplies strictly increasing, unique segment bases and valid, ordered, nonoverlapping decoded batches. The host retains one active segment, replays producer state, rewrites the transaction, stamp, and offset indexes, and clamps the last stable offset, delivery watermark, and epoch checkpoint to the actual retained end. Filesystem deletion, rewrite, synchronization, and crash atomicity remain I/O assumptions. The independently reconciled diskless frontier is not local-log state and is not clamped here.
local_append_coordinates storage/future_log_swap_admission.rs 1 proof local_append_coordinates admits a batch at the expected logical frontier or beyond it, and computes its inclusive last offset and exclusive successor with checked arithmetic, excluding duplicates (a base below the frontier), negative deltas, and signed overflow. A base above the frontier is a hole in the offsets, as a compacted leader's log has, which a follower takes as Kafka's appendAsFollower does.
Proves
local_append_coordinates admits a batch at the expected logical frontier or beyond it, and computes its inclusive last offset and exclusive successor with checked arithmetic, excluding duplicates (a base below the frontier), negative deltas, and signed overflow. A base above the frontier is a hole in the offsets, as a compacted leader's log has, which a follower takes as Kafka's appendAsFollower does.
Host caller
The local log's owned append coordinator, verbatim append coordinator, segment write path, and rollback path
Proof sessions
Caller preconditions
The host supplies max(local log end, reconciled diskless frontier) as the expected logical base; a frontier above the local end represents offsets already reserved by the diskless WAL. Batch encoding and CRC validation precede mutation. Segment bytes and sparse indexes are flushed before a synchronized stamp or transaction sidecar. Any later append or checkpoint failure invokes full tail truncation to restore bytes, the file cursor, indexes, producer and transaction state, visibility frontiers, and epoch state before retry. Individual filesystem calls and rollback can still fail; atomic persistence across separate files and process or host crashes remains an I/O assumption.
remote_segment_transitionremote_partition_delete_transition storage/remote_partition_delete_transition.rs 2 proofs remote_segment_transition and remote_partition_delete_transition equal Kafka's RemoteLogSegmentState.isValidTransition and RemotePartitionDeleteState.isValidTransition: a forward lifecycle edge or an idempotent self transition, and, for partition deletion with no prior state, only DELETE_PARTITION_MARKED.
Proves
remote_segment_transition and remote_partition_delete_transition equal Kafka's RemoteLogSegmentState.isValidTransition and RemotePartitionDeleteState.isValidTransition: a forward lifecycle edge or an idempotent self transition, and, for partition deletion with no prior state, only DELETE_PARTITION_MARKED.
Host caller
The remote-storage segment state and partition-delete state adapters
Proof sessions
Caller preconditions
The host maps its Rust enums onto the shared RemoteSegmentLifecycle and RemotePartitionDeleteLifecycle kernel enums; no numeric tags remain. Exhaustive production tests cover every source and destination pair.
remote_cache_action storage/remote_partition_delete_transition.rs 1 proof remote_cache_action admits only legal remote-segment cache transitions, makes exact same-state updates and already-absent delete-finished tombstones no-ops, permits epoch indexing only for copy-finished state, hides delete-started state, removes delete-finished state, and rejects every missing-state resurrection attempt. The cache is deliberately stricter than Kafka's RemoteLogMetadataCache, which admits every self transition: here a self transition is a no-op only for an exact retry and is otherwise rejected.
Proves
remote_cache_action admits only legal remote-segment cache transitions, makes exact same-state updates and already-absent delete-finished tombstones no-ops, permits epoch indexing only for copy-finished state, hides delete-started state, removes delete-finished state, and rejects every missing-state resurrection attempt. The cache is deliberately stricter than Kafka's RemoteLogMetadataCache, which admits every self transition: here a self transition is a no-op only for an exact retry and is otherwise rejected.
Host caller
The remote-metadata primary cache and derived epoch-index adapter
Proof sessions
Caller preconditions
The host passes the cached state as Some(RemoteSegmentLifecycle), None only for a missing UUID, and classifies a same-state retry as exact only when state, event timestamp, broker, and any supplied custom metadata agree. After every admitted state change, it rebuilds the derived epoch map from copy-finished primary entries; deterministic (epoch, start, UUID) ordering resolves malformed slot collisions and exposes the next finished candidate after deletion. Seed clears both maps, folds duplicate UUIDs monotonically, and retains delete-finished UUIDs as tombstones for the whole seed pass before one rebuild. Segment construction, snapshot decoding, primary-map locking, and durability remain host assumptions. Lookup additionally rechecks finished state, epoch membership, offset span, and relative-offset width before returning metadata.
scheduled_delivery_visibledelivery_watermark_advancecoalesce_delivery_range delivery.rs 3 proofs scheduled_delivery_visible exposes a scheduled batch only when its checked activation deadline is at or before the broker clock and fails closed on invalid uncertainty or overflow; delivery_watermark_advance returns exactly min(max(candidate, clamp(current, log_start, log_end)), log_end): the cached watermark clamped into live log bounds, advanced to the scanned candidate when it is ahead, and never past the log end; coalesce_delivery_range merges exactly overlapping or adjacent inclusive ranges and returns their maximum end without successor overflow.
Proves
scheduled_delivery_visible exposes a scheduled batch only when its checked activation deadline is at or before the broker clock and fails closed on invalid uncertainty or overflow; delivery_watermark_advance returns exactly min(max(candidate, clamp(current, log_start, log_end)), log_end): the cached watermark clamped into live log bounds, advanced to the scanned candidate when it is ahead, and never past the log end; coalesce_delivery_range merges exactly overlapping or adjacent inclusive ranges and returns their maximum end without successor overflow.
Host caller
The log's local activation walk and watermark, range fold, and the broker's remote fetch gate
Proof sessions
Caller preconditions
Configuration supplies a nonnegative millisecond clock-uncertainty bound; local and remote paths use the same broker clock reading and decoded batch max_timestamp. Storage supplies valid ordered batch ranges and log_start <= log_end. Retention or truncation may first clamp a stale watermark down to the new live range; without a range change, each proved step is monotonic.

Recovery and restore 7

local_recovery_segment_chainlocal_recovery_sealed_lastlocal_recovery_batch_steplocal_recovery_index_frontierlocal_recovery_swap_actionlocal_recovery_swap_replaces local_recovery/index_frontier.rs 6 proofs local_recovery_segment_chain accepts only nonnegative strictly ordered segment bases; local_recovery_sealed_last closes each sealed range exactly before the next base; local_recovery_batch_step advances one maximal valid byte-and-offset prefix without overlap or overflow; local_recovery_index_frontier admits a recovered segment only when its exact exclusive frontier, relative to the segment base, lies in [0, u32::MAX]; local_recovery_swap_action aborts a compaction swap exactly when its .log.cleaned still exists, completes it exactly when its .log.swap exists without one (the swap_committed rule of Kafka's LogLoader.load: a committed swap is completed whether or not the original base log survives), resumes sidecar promotion exactly when only the final log exists, and rejects sidecars with no log; local_recovery_swap_replaces selects exactly the segments a completed swap deletes: its own base and every base in [swap base, swap next offset).
Proves
local_recovery_segment_chain accepts only nonnegative strictly ordered segment bases; local_recovery_sealed_last closes each sealed range exactly before the next base; local_recovery_batch_step advances one maximal valid byte-and-offset prefix without overlap or overflow; local_recovery_index_frontier admits a recovered segment only when its exact exclusive frontier, relative to the segment base, lies in [0, u32::MAX]; local_recovery_swap_action aborts a compaction swap exactly when its .log.cleaned still exists, completes it exactly when its .log.swap exists without one (the swap_committed rule of Kafka's LogLoader.load: a committed swap is completed whether or not the original base log survives), resumes sidecar promotion exactly when only the final log exists, and rejects sidecars with no log; local_recovery_swap_replaces selects exactly the segments a completed swap deletes: its own base and every base in [swap base, swap next offset).
Host caller
The log's compaction swap and swap recovery, segment-chain open, and active-segment torn-tail scan and sparse-index rebuild
Proof sessions
Caller preconditions
The host parses canonical fixed-width segment and swap names, sorts and deduplicates their bases, and passes decoded batch bases, deltas, and exact consumed byte lengths unchanged. With open-time validation enabled, it scans the active segment from byte zero, stops at the first framing, CRC, overlap, or arithmetic failure, truncates only after every byte and offset frontier is representable, and rebuilds both sparse indexes from that same retained prefix at the configured interval. Disabling open-time validation opts out of this guarantee. Compaction follows Kafka's LocalLog.replaceSegments: the rewrite writes .cleaned files, atomic_swap fsyncs them, renames the sidecars and then the log to .swap with a directory fsync after each, deletes every consumed segment (propagating every unlink failure but a missing file) and fsyncs, then renames the log and then the sidecars to their final names with a directory fsync after each. No consumed segment is deleted before the .log.swap rename is durable, which is the host fact that makes AbortSwap safe. Recovery computes a committed swap's next offset by walking its batch headers with local_recovery_batch_step and fails closed on any torn or overlapping batch; it fsyncs the directory after its deletions and before it renames, and after aborting before it deletes .cleaned files. Crash-point tests crash atomic_swap at every I/O operation, lose every subset of the directory operations since the last directory fsync, and require the reopened directory to equal the pre- or post-compaction directory, and the post-compaction one once the commit is durable. Directory enumeration, record decoding, file truncation, index rewrite, rename and unlink atomicity, fsync semantics, and concurrent directory stability remain filesystem assumptions.
replay_record_decisionreplay_cursor_decisionreplay_batch_cursor_decisionshould_capture_first_downgrade recovery/replay_cursor_decision.rsrecovery/should_capture_first_downgrade.rs 4 proofs replay_record_decision applies a record, at offset base + delta, exactly when the replay_record_admitted rule holds: a nonnegative delta, a batch of the kind the pass replays (data or control), and an offset inside [from, end); every other record is skipped; replay_cursor_decision advances only on strict progress; replay_batch_cursor_decision accepts a decoded batch only when its base does not overlap the cursor and its exclusive successor is representable, strictly advancing, and at or below the captured end; should_capture_first_downgrade retains only the first downgrade.
Proves
replay_record_decision applies a record, at offset base + delta, exactly when the replay_record_admitted rule holds: a nonnegative delta, a batch of the kind the pass replays (data or control), and an offset inside [from, end); every other record is skipped; replay_cursor_decision advances only on strict progress; replay_batch_cursor_decision accepts a decoded batch only when its base does not overlap the cursor and its exclusive successor is representable, strictly advancing, and at or below the captured end; should_capture_first_downgrade retains only the first downgrade.
Host caller
The KRaft controller's committed data, control-state, and control-record recovery walks, the group coordinator's __consumer_offsets replay, and the barrier coordinator's __barrier_state replay
Proof sessions
Caller preconditions
Batch framing, CRC checking, and record/control decoding remain in the host. Each caller passes the decoded batch base and signed last delta unchanged, threads the admitted exclusive end into the next call, and captures the log end before replay. Kafka compaction may leave forward gaps, which the kernel permits. The group coordinator returns a startup error for a rejected batch; barrier recovery warns and stops that partition before applying the rejected batch.
barrier_recovery_fold_actionbarrier_recovery_finalize_decision recovery/replay_cursor_decision.rs 2 proofs barrier_recovery_fold_action selects the one keyed state change allowed for each barrier record. A group tombstone removes the full entry. A cut or injection tombstone retires a pending injection only when its epoch matches, and a later retry cannot reopen an epoch already represented by a cut or the group's durable epoch. barrier_recovery_finalize_decision pins every outcome with an if-and-only-if postcondition: no pending injection; a malformed one (negative frozen coordinator epoch or invalid targets); an unknown current coordinator; a fenced one (current epoch below the frozen epoch); and otherwise a partial cut, the only admitted finalization.
Proves
barrier_recovery_fold_action selects the one keyed state change allowed for each barrier record. A group tombstone removes the full entry. A cut or injection tombstone retires a pending injection only when its epoch matches, and a later retry cannot reopen an epoch already represented by a cut or the group's durable epoch. barrier_recovery_finalize_decision pins every outcome with an if-and-only-if postcondition: no pending injection; a malformed one (negative frozen coordinator epoch or invalid targets); an unknown current coordinator; a fenced one (current epoch below the frozen epoch); and otherwise a partial cut, the only admitted finalization.
Host caller
The barrier coordinator's ordered __barrier_state fold and interrupted-injection recovery
Proof sessions
Caller preconditions
The host decodes each record, selects the BTreeMap entry named by its exact group key, and applies records in log order. All records for one group map to one state partition. Recovery treats an empty target set, an empty topic, a nonpositive partition count, or a negative frozen coordinator epoch as malformed and leaves the epoch pending. Unknown or stale coordinator epochs also leave it pending. For an admitted finalization, the host supplies no marker offsets and checks that build_cut returned Partial before append. The in-memory entry changes only after the cut, group record, and injection tombstone append succeeds. Record decoding, log reads, metadata-image freshness, append durability, and process-crash atomicity remain host or I/O assumptions.
restore_batch_steprestore_record_coordinatesrestore_record_selectedrestore_batch_filter_decisionrestore_batch_past_offset_bound restore/record_coordinates.rs 5 proofs restore_batch_step accepts only a non-overlapping archived batch base and computes its exclusive end without overflow, while permitting gaps preserved by Kafka compaction; restore_record_coordinates validates each record delta and derives its absolute offset and its Kafka timestamp (kafka_record_timestamp: base_timestamp + timestamp_delta under CreateTime, the batch max_timestamp under LogAppendTime, as DefaultRecord.readFrom substitutes logAppendTime) without overflow; restore_record_selected keeps exactly records at or below an inclusive offset bound and strictly before an exclusive timestamp bound when no OR-combined exclusion (restore_excluded over producer, offset-range, key, and header matches) holds; restore_batch_filter_decision makes keep, empty, and filter equivalent to the folded record decisions; and restore_batch_past_offset_bound stops an ordered batch walk exactly when the next base is past its partition bound.
Proves
restore_batch_step accepts only a non-overlapping archived batch base and computes its exclusive end without overflow, while permitting gaps preserved by Kafka compaction; restore_record_coordinates validates each record delta and derives its absolute offset and its Kafka timestamp (kafka_record_timestamp: base_timestamp + timestamp_delta under CreateTime, the batch max_timestamp under LogAppendTime, as DefaultRecord.readFrom substitutes logAppendTime) without overflow; restore_record_selected keeps exactly records at or below an inclusive offset bound and strictly before an exclusive timestamp bound when no OR-combined exclusion (restore_excluded over producer, offset-range, key, and header matches) holds; restore_batch_filter_decision makes keep, empty, and filter equivalent to the folded record decisions; and restore_batch_past_offset_bound stops an ordered batch walk exactly when the next base is past its partition bound.
Host caller
The restore verifier's record-batch walk, the compiled bound and record filter, and the materializer's batch preparation and ordered segment walk
Proof sessions
Caller preconditions
Batch framing and CRC are validated first. The host maps each partition's parsed inclusive offset bound and the global exclusive timestamp bound exactly, maps the batch attributes' timestamp-type bit onto RestoreTimestampType, computes producer, half-open offset-range, UTF-8 key-regex, and header-regex matches into RestoreExclusions before calling the record kernel, and scans verified batches in archive order. A straddling data batch is decoded and filtered rather than skipped. Control batches bypass all per-record predicates, including timestamp, to preserve transaction markers; the host relies on Kafka's one-marker-at-base control-batch shape, and a control batch whose base is past an offset bound is skipped with the ordered tail. During an applicable predicate walk, record parsing and coordinate failures are archive-integrity errors before selection or rewriting; untouched data uses the CRC/frame-validated verbatim fast path. Complete decoded-row truthfulness, target-log append, synchronization, and retry remain host assumptions.
restore_rewritten_batch_headerrestore_rewritten_record restore/rewritten_record.rs 4 proofs restore_rewritten_batch_header admits exactly the legal_header and legal_producer rewrites: nonnegative base, span, and retained count with an exactly representable archived offset frontier; a control batch spans one offset and holds at most its one marker (zero after Kafka's RETAIN_EMPTY compaction of a discarded marker); and a non-idempotent sentinel, idempotent data, or transaction-marker producer tuple. restore_rewritten_record admits exactly the restore_rewrite_record_legal records: every retained record delta advances strictly inside that span, and its Kafka timestamp (kafka_record_timestamp, so max_timestamp itself under LogAppendTime, where the producer's deltas are never read) is representable and at most the preserved maximum timestamp.
Proves
restore_rewritten_batch_header admits exactly the legal_header and legal_producer rewrites: nonnegative base, span, and retained count with an exactly representable archived offset frontier; a control batch spans one offset and holds at most its one marker (zero after Kafka's RETAIN_EMPTY compaction of a discarded marker); and a non-idempotent sentinel, idempotent data, or transaction-marker producer tuple. restore_rewritten_record admits exactly the restore_rewrite_record_legal records: every retained record delta advances strictly inside that span, and its Kafka timestamp (kafka_record_timestamp, so max_timestamp itself under LogAppendTime, where the producer's deltas are never read) is representable and at most the preserved maximum timestamp.
Host caller
The restore materializer's owned-batch preflight and segment accounting and append
Proof sessions
Caller preconditions
The host fully decodes a filtered data batch, preserves survivor order and the archived offset span, derives records_count from the retained vector, maps the timestamp-type attribute bit exactly, and checks every retained delta and Kafka timestamp. Control batches bypass selection but take this owned path so the log reads the marker and applies transaction or barrier semantics; data batches untouched by predicates retain their previously CRC-validated verbatim bytes. The protocol encoder recomputes the count and CRC; preflight validates that fresh framing and compares every admitted header field before either dry-run accounting or mutation, so compressed sizes are counted from exact encoded bytes. The target log reuses the same owned encoder and rolls back failed appends; allocator, compression-codec, filesystem, synchronization, rollback, and crash atomicity remain host assumptions.
restore_index_frontierrestore_offset_index_entry_validrestore_time_index_entry_validrestore_txn_index_entry_validrestore_leader_epoch_entry_validrestore_producer_ids_strictproducer_snapshot_entry_valid restore_sidecar/restore_producer_ids_strict.rsproducer_snapshot/replay_start.rs 7 proofs restore_index_frontier derives the exact inclusive relative-offset bound and rejects a segment whose span does not fit Kafka's 32-bit relative offset (returned widened as i64, so no narrowing conversion is executed or trusted); restore_offset_index_entry_valid and restore_time_index_entry_valid require strict sparse-index coordinates and verified log bounds; restore_txn_index_entry_valid requires Kafka's TransactionIndex order: each abort marker's last_offset inside the segment and strictly increasing across entries, with 0 <= start_offset <= last_offset and a nonnegative producer ID, while start_offset may precede the segment base (a transaction spanning a roll) and need not increase (interleaved transactions); restore_leader_epoch_entry_valid requires nonnegative strictly ordered segment-scoped epochs and starts; and restore_producer_ids_strict makes sorted producer IDs unique. Each producer state also passes producer_snapshot_entry_valid at the segment's exact exclusive frontier.
Proves
restore_index_frontier derives the exact inclusive relative-offset bound and rejects a segment whose span does not fit Kafka's 32-bit relative offset (returned widened as i64, so no narrowing conversion is executed or trusted); restore_offset_index_entry_valid and restore_time_index_entry_valid require strict sparse-index coordinates and verified log bounds; restore_txn_index_entry_valid requires Kafka's TransactionIndex order: each abort marker's last_offset inside the segment and strictly increasing across entries, with 0 <= start_offset <= last_offset and a nonnegative producer ID, while start_offset may precede the segment base (a transaction spanning a roll) and need not increase (interleaved transactions); restore_leader_epoch_entry_valid requires nonnegative strictly ordered segment-scoped epochs and starts; and restore_producer_ids_strict makes sorted producer IDs unique. Each producer state also passes producer_snapshot_entry_valid at the segment's exact exclusive frontier.
Host caller
The restore verifier's index, leader-epoch, and producer-snapshot adapters
Proof sessions
Caller preconditions
The host requires complete fixed-width big-endian index entries, preserves each decoded field, and supplies the log byte length and inclusive offset extent established by the verified batch walk. Offset-index coordinates and transaction-index last_offsets strictly increase; time-index offsets strictly increase while timestamps may repeat, because krabka's segment writer appends the running maximum timestamp at every index point (Kafka's TimeIndex.maybeAppend writes only strictly greater timestamps, which non-decreasing also admits). The strict text parser enforces the exact declared leader-epoch row count and the export path's clamped segment bounds. Snapshot framing and CRC are checked before fixed-width decoding; its filename-independent frontier is verified end + 1. Duplicate producer IDs are rejected by krabka-log's shared producer-snapshot decoder as it decodes, so arbitrary entry order is accepted while duplicates are not; that duplicate check is host code, and restore_producer_ids_strict is no longer on this path. Producer timestamps are persisted but are not part of producer sequencing validity. Object listing, fetch stability, byte decoding, duplicate producer-ID detection, CRC implementation, allocation, and target-side I/O remain host assumptions. Malformed, stale, overflowing, duplicate, or out-of-range sidecars fail before materialization; pure validation is repeatable after uncertain reads.
restore_archive_reconcile restore/record_coordinates.rs 1 proof restore_archive_reconcile classifies each archive-scan observation against a RestoreSnapshotState (missing, live, delete-started, delete-finished) into RestoreReconcileDecision: keep scanned live bytes, exclude unscanned non-live and delete-started bytes, and report every other pair (an unscanned live segment, or a scanned segment the snapshot omits or reports fully deleted) as a disagreement. Each variant is pinned by an if-and-only-if postcondition.
Proves
restore_archive_reconcile classifies each archive-scan observation against a RestoreSnapshotState (missing, live, delete-started, delete-finished) into RestoreReconcileDecision: keep scanned live bytes, exclude unscanned non-live and delete-started bytes, and report every other pair (an unscanned live segment, or a scanned segment the snapshot omits or reports fully deleted) as a disagreement. Each variant is pinned by an if-and-only-if postcondition.
Host caller
The restore inventory's archive-source reconciliation
Proof sessions
Caller preconditions
The host maps the four RLMM lifecycle states and snapshot absence exactly onto the enum (both copy states are live) and keys segments by (UUID, start offset). Duplicate partition or segment keys are disagreements instead of retries that overwrite prior metadata. Snapshot file I/O and decoding and archive listing consistency remain assumptions. Missing and corrupt snapshots fail before reconciliation. The scan and snapshot must remain stable while this read-only comparison runs.

Diskless and remote storage 11

diskless_trim_decision diskless/trim_decision.rs 1 proof diskless_trim_decision advances local log start only behind both the committed object projection and the lagged high watermark, without subtraction overflow or regression.
Proves
diskless_trim_decision advances local log start only behind both the committed object projection and the lagged high watermark, without subtraction overflow or regression.
Host caller
The diskless WAL object flusher
Proof sessions
Caller preconditions
None; negative offsets and a safety lag beyond the high watermark fail closed, while a negative configured lag preserves the host's existing zero-lag behavior.
diskless_logical_rangediskless_span_extensiondiskless_batch_step diskless/logical_range.rsdiskless/trim_decision.rs 3 proofs diskless_logical_range selects the indexed range covering a requested offset or the immediate successor after a gap; diskless_span_extension extends exactly when the next range is in the same object, contiguous, and fits the response cap; diskless_batch_step is Invalid exactly for an empty, negative-delta, coordinate-overflowing, or out-of-run batch, and otherwise skips, starts, continues (each advancing by the encoded length) or stops, without splitting a batch.
Proves
diskless_logical_range selects the indexed range covering a requested offset or the immediate successor after a gap; diskless_span_extension extends exactly when the next range is in the same object, contiguous, and fits the response cap; diskless_batch_step is Invalid exactly for an empty, negative-delta, coordinate-overflowing, or out-of-run batch, and otherwise skips, starts, continues (each advancing by the encoded length) or stops, without splitting a batch.
Host caller
The diskless WAL cold-read index lookup and batch selector
Proof sessions
Caller preconditions
The host copies ranges in BTreeMap order and rejects invalid, overlapping, or unordered logical ranges before calling the selection kernel. Object-key equality is exact. Object reads, index completeness, decoded RecordBatch framing and CRC, and actual decode consumption remain host and I/O assumptions. Kafka permits the first returned batch to exceed the cap; every later batch must fit without splitting.
diskless_retention_prefixexpirable diskless/logical_range.rs 1 proof diskless_retention_prefix returns the longest oldest-first prefix of one diskless partition's WAL-index ranges on which every range is expirable, capped one short of the newest range, and stops only at a range that is not: Kafka's UnifiedLog.deleteOldSegments log-start-offset, size (diff - segmentSize >= 0, so retention.bytes never deletes past its budget) and time (now - largestTimestamp > retention.ms) predicates.
Proves
diskless_retention_prefix returns the longest oldest-first prefix of one diskless partition's WAL-index ranges on which every range is expirable, capped one short of the newest range, and stops only at a range that is not: Kafka's UnifiedLog.deleteOldSegments log-start-offset, size (diff - segmentSize >= 0, so retention.bytes never deletes past its budget) and time (now - largestTimestamp > retention.ms) predicates.
Host caller
The diskless WAL flusher's retention pass through the WAL-index projection
Proof sessions
Caller preconditions
The host passes the projection's ranges in BTreeMap (first-offset) order, which makes the floor predicate hold on a prefix; together with nonnegative sizes this makes the single union walk equal to Kafka's three sequential walks. retention.ms and retention.bytes are None for Kafka's unlimited sentinel, and the floor is the partition's DeleteRecords floor, not the local log start. A horizon now - retention.ms that i64 cannot represent expires nothing by time. The byte sum is taken in u128, so no length slice overflows it. Tombstoning the returned keys, and the clock, remain host assumptions.
remote_metadata_partition remote_metadata.rs 1 proof remote_metadata_partition implements Kafka's KIP-405 metadata partitioner exactly: Java UUID-half hashing, big-endian int serialization, Murmur2, positive masking, and modulo; nonpositive partition counts are rejected.
Proves
remote_metadata_partition implements Kafka's KIP-405 metadata partitioner exactly: Java UUID-half hashing, big-endian int serialization, Murmur2, positive masking, and modulo; nonpositive partition counts are rejected.
Host caller
krabka-remote-storage-topic
Proof sessions
Caller preconditions
Uuid::as_u128 preserves the canonical most-significant and least-significant 64-bit images consumed by the kernel. Java int values are carried as their u32 two's-complement images with wrapping u32 arithmetic, which is Java's int arithmetic modulo 2^32.
remote_metadata_resume_cursor remote_metadata.rs 1 proof remote_metadata_resume_cursor maps the sole no-event sentinel -1 to zero, advances every real committed offset by exactly one, and rejects offsets below the sentinel or at i64::MAX.
Proves
remote_metadata_resume_cursor maps the sole no-event sentinel -1 to zero, advances every real committed offset by exactly one, and rejects offsets below the sentinel or at i64::MAX.
Host caller
The topic-backed remote-metadata manager's snapshot adapter, used by both startup restoration and dynamic assignment
Proof sessions
Caller preconditions
Snapshot framing and cache contents remain host assumptions. The host validates every retained per-partition cursor before importing the dump; one invalid cursor discards the whole dump and restores empty-cache/full-replay state. Missing snapshot partitions are padded with -1, surplus partitions are ignored, and dynamic assignment calls the same adapter over the validated committed vector. Kafka HWM fetch, assignment delivery, snapshot file I/O, and the cache-before-committed capture ordering remain I/O and concurrency assumptions; failed HWM reads continue to gate NotReady and retry through the existing reconciler.
tiered_earliest_finished_indextiered_latest_finished_indextiered_owning_epoch_index remote_read/remote_time_index_candidate_count.rs 3 proofs tiered_earliest_finished_index selects a valid finished remote segment with the minimum start; tiered_latest_finished_index selects one with the maximum inclusive end; and tiered_owning_epoch_index selects the nonnegative leader epoch with the greatest in-segment start at or below that end.
Proves
tiered_earliest_finished_index selects a valid finished remote segment with the minimum start; tiered_latest_finished_index selects one with the maximum inclusive end; and tiered_owning_epoch_index selects the nonnegative leader epoch with the greatest in-segment start at or below that end.
Host caller
The broker remote reader's tiered ListOffsets adapter
Proof sessions
Caller preconditions
The host takes one RLMM listing and constructs equal-length parallel arrays without reordering, then maps each proved index back to that same immutable vector. A candidate is valid only when the copy finished and 0 <= start <= end; negative or inverted metadata is invisible. Epoch keys and starts come from the selected segment's one map, and an absent valid owner fails closed to no tiered latest offset. Equal minimum starts or maximum ends may select either tied segment; correctness assumes tied maximum-end segments describe the same leader lineage. RLMM readiness and listing consistency, lifecycle durability, and concurrent metadata changes after the returned snapshot remain host and I/O assumptions.
remote_read_relative_offset remote_read/relative_offset.rs 1 proof remote_read_relative_offset admits only finished remote segments whose inclusive range contains the request, whose requested leader epoch owns that exact [epoch start, next epoch start) subrange, and whose relative offset fits the sparse index.
Proves
remote_read_relative_offset admits only finished remote segments whose inclusive range contains the request, whose requested leader epoch owns that exact [epoch start, next epoch start) subrange, and whose relative offset fits the sparse index.
Host caller
The remote-storage metadata cache and broker remote Fetch reader
Proof sessions
Caller preconditions
The host derives the requested and next epoch starts from each segment's ordered epoch map; every primary and fallback candidate delegates before index or object access.
remote_fetch_end_position remote_read/relative_offset.rs 1 proof remote_fetch_end_position computes an inclusive bounded-fetch end without wrapping and uses an open end for zero, exact-segment, or oversized caps.
Proves
remote_fetch_end_position computes an inclusive bounded-fetch end without wrapping and uses an open end for zero, exact-segment, or oversized caps.
Host caller
The remote-storage OffsetIndex range adapter
Proof sessions
Caller preconditions
The host saturates usize caps above u32::MAX before delegation, which preserves the open-ended result.
remote_time_index_candidate_count remote_read/remote_time_index_candidate_count.rs 1 proof remote_time_index_candidate_count counts the prefix of strictly increasing relative offsets whose timestamps are all strictly below the target, and stops exactly at the first padding entry (a non-increasing offset) or the first entry at or above the target. Every counted entry is a safe scan floor when its timestamp bounds earlier records.
Proves
remote_time_index_candidate_count counts the prefix of strictly increasing relative offsets whose timestamps are all strictly below the target, and stops exactly at the first padding entry (a non-increasing offset) or the first entry at or above the target. Every counted entry is a safe scan floor when its timestamp bounds earlier records.
Host caller
The remote-storage TimeIndex floor adapter
Proof sessions
Caller preconditions
The zero-copy parser exposes only complete 12-byte big-endian entries and ignores a trailing partial entry. The adapter decodes every entry and passes them all; the kernel ends the usable index at the padding itself. Nothing assumes the remote timestamps are sorted: a time-index entry holds the largest timestamp at or before its offset, so any entry strictly below the target is a safe floor, and for a Kafka-written index (nondecreasing timestamps) the last counted entry is the latest one. The remote composition proves the resulting global first match from those record bounds, and a real-segment archive round trip exercises both adapters against a record oracle; structural validation alone cannot establish truthful running maxima.
remote_txn_overlap_decision remote_txn.rs 1 proof remote_txn_overlap_decision classifies inclusive aborted-transaction and Fetch ranges exactly, rejecting either inverted interval.
Proves
remote_txn_overlap_decision classifies inclusive aborted-transaction and Fetch ranges exactly, rejecting either inverted interval.
Host caller
The remote-storage transaction-index reader, which also rejects payloads not divisible by the 34-byte entry size and any entry whose version prefix is unsupported
Proof sessions
Caller preconditions
The host decodes each big-endian entry field exactly and rejects a torn partial entry before calling the kernel.
diskless_object_reclaimable diskless/logical_range.rs 1 proof diskless_object_reclaimable permits deletion only after grace has elapsed and the locked projection has no reference.
Proves
diskless_object_reclaimable permits deletion only after grace has elapsed and the locked projection has no reference.
Host caller
The diskless object reclaimer
Proof sessions
Caller preconditions
The reclaimer takes a fresh reference check while holding the projection lock through deletion. Every WAL-index record is keyed by its range, so the WAL-index projection and the remote-storage capture projection keep the latest keyed value per range and remove it on its tombstone; equal keys share a Kafka partition and retain their order, and an unkeyed record makes the projection invalid. Object-store listing, Kafka delivery and compaction, and object-store I/O remain host assumptions.

Producers and transactions 15

producer_decisionincrement_sequencedecrement_sequence producer/decision.rsproducer/sequence_decision.rs 3 proofs Runs in browser producer_decision is Kafka's client-append producer check (UnifiedLog.analyzeAndValidateProducerState): at the entry's epoch it first searches the retained batches' sequence ranges (ProducerStateEntry.findDuplicateBatch) and returns the first exact match as a duplicate; only a batch that repeats no retained batch is then classified as append, out of order, or fenced per checkProducerEpoch/checkSequence (no entry accepts any sequence, except that under the host's trunk_rules flag a partition whose log is empty requires sequence 0 there, Kafka trunk's KAFKA-15591 rule; a lower epoch is fenced, a higher epoch must start at 0, the same epoch must continue at last sequence + 1 modulo 2^31). Every variant, including the duplicate's index, is pinned by one postcondition. increment_sequence and decrement_sequence return the operands' sum or difference reduced modulo 2^31 for every i32 input, which is Kafka's DefaultRecordBatch.incrementSequence/decrementSequence on its nonnegative domain.
Proves
producer_decision is Kafka's client-append producer check (UnifiedLog.analyzeAndValidateProducerState): at the entry's epoch it first searches the retained batches' sequence ranges (ProducerStateEntry.findDuplicateBatch) and returns the first exact match as a duplicate; only a batch that repeats no retained batch is then classified as append, out of order, or fenced per checkProducerEpoch/checkSequence (no entry accepts any sequence, except that under the host's trunk_rules flag a partition whose log is empty requires sequence 0 there, Kafka trunk's KAFKA-15591 rule; a lower epoch is fenced, a higher epoch must start at 0, the same epoch must continue at last sequence + 1 modulo 2^31). Every variant, including the duplicate's index, is pinned by one postcondition. increment_sequence and decrement_sequence return the operands' sum or difference reduced modulo 2^31 for every i32 input, which is Kafka's DefaultRecordBatch.incrementSequence/decrementSequence on its nonnegative domain.
Host caller
krabka-broker, which maps a duplicate's index back to the retained batch it names; increment_sequence is also called by krabka-log and the broker tracker, and decrement_sequence by the tracker entry.
Proof sessions
Caller preconditions
The host passes the entry's epoch and last sequence and its up-to-five retained batches' sequence ranges unchanged, all at the entry's epoch (the tracker clears them on an epoch change, as Kafka's maybeUpdateProducerEpoch does). log_empty is the partition's log end offset being 0, which stands for Kafka's mapEndOffset() == 0: both advance with every append, neither moves back when the log start moves, and both return to 0 only when the whole log is truncated away, and trunk_rules is unstable.api.versions.enable.
produce_batch_admission produce.rs 1 proof produce_batch_admission accepts exactly a nonempty data batch with base offset zero, consistent positive record count, a valid producer sequence when idempotent, and CreateTime timestamps; malformed headers take precedence over timestamp rejection.
Proves
produce_batch_admission accepts exactly a nonempty data batch with base offset zero, consistent positive record count, a valid producer sequence when idempotent, and CreateTime timestamps; malformed headers take precedence over timestamp rejection.
Host caller
The broker's Produce batch preparation
Proof sessions
Caller preconditions
CRC validation runs first; the host maps the decoded control and timestamp-type attributes exactly.
producer_id_block_allocation producer_id.rs 3 proofs producer_id_block_allocation admits only a registered broker with the exact nonnegative epoch and current nonnegative frontier, then constructs one positive contiguous block whose durable successor is exactly first + len without signed overflow.
Proves
producer_id_block_allocation admits only a registered broker with the exact nonnegative epoch and current nonnegative frontier, then constructs one positive contiguous block whose durable successor is exactly first + len without signed overflow.
Host caller
The broker's shared producer-ID allocator, called by AllocateProducerIds and local producer identity allocation
Proof sessions
Caller preconditions
The host maps the request broker ID and epoch, the current registered epoch, and the committed next_producer_id exactly. This adapter is the broker's only production producer of ProducerIdsRecord and submits the proved plan.next; success is returned only after the controller accepts that record. The controller serializes competing records, rechecks the broker epoch and a strictly advancing frontier, and the loser retries from the refreshed committed image. Raft durability, image freshness after a rejected concurrent proposal, controller failover, and response delivery remain host or I/O assumptions. An uncertain success can leave an unused block, but no block is reused or overlapped.
produce_durability_frontier produce.rs 1 proof produce_durability_frontier returns exactly base offset + last offset delta + 1, rejects negative inputs, and fails closed on signed overflow.
Proves
produce_durability_frontier returns exactly base offset + last offset delta + 1, rejects negative inputs, and fails closed on signed overflow.
Host caller
The broker's shared Produce durability adapter, used by the fresh-append acknowledgement path and idempotent duplicate path
Proof sessions
Caller preconditions
Batch admission establishes a nonnegative delta, the writer supplies the assigned nonnegative base, and producer-state duplicates preserve that same base and span. An invalid or overflowing frontier returns INVALID_RECORD without starting a durability wait or recording a dedup commit. High-watermark publication, waiter notification, deadlines, leadership changes, and producer-state concurrency remain asynchronous host assumptions.
unique_aborted_transaction_rows transaction/abort_rows.rs 2 proofs unique_aborted_transaction_rows retains every supplied wire (producer_id, first_offset) pair exactly once, introduces no pairs, and never grows the list.
Proves
unique_aborted_transaction_rows retains every supplied wire (producer_id, first_offset) pair exactly once, introduces no pairs, and never grows the list.
Host caller
Broker remote Fetch, after merging overlapping remote-tail and local transaction indexes
Proof sessions
Caller preconditions
The caller selects all qualifying source rows. The composition carries complete admitted index witnesses through inclusive remote and half-open local selection. Enumeration, lineage, index truthfulness, coherent frontiers and client-side filtering remain host obligations.
transaction_completion_decision transaction/producer_identity.rs 1 proof transaction_completion_decision answers AlreadyComplete exactly when the live entry holds the intended completion identity and state, Proceed exactly when it holds the prepared identity and state, RejectStaleIdentity exactly when it is neither complete nor holds the prepared identity, and RejectState exactly when it holds the prepared identity in any other state.
Proves
transaction_completion_decision answers AlreadyComplete exactly when the live entry holds the intended completion identity and state, Proceed exactly when it holds the prepared identity and state, RejectStaleIdentity exactly when it is neither complete nor holds the prepared identity, and RejectState exactly when it holds the prepared identity in any other state.
Host caller
The broker's EndTxn reacquisition guard and completion decision; the reaper and completion-task kernel transaction_reaper_completion_decision also calls it
Proof sessions
Caller preconditions
prepare_state != complete_state (#[requires]): the host passes one Kafka Prepare* -> Complete* pairing, from decide_phase1_transition or completion_for. The host maps Kafka transaction states and producer identities exactly into primitives.
transaction_reaper_completion_decision transaction/reaper.rs 2 proofs transaction_reaper_completion_decision pins all five outcomes: RejectMalformed exactly when a snapshot names a negative PID or epoch; otherwise AlreadyComplete exactly when the live entry equals the intended completion, RejectStaleIdentity exactly when it is not complete and no longer holds the prepared identity, Proceed exactly when it equals the prepared snapshot and the host's full-entry comparison holds, and RejectChangedPreparedState in every remaining case. It delegates to transaction_completion_decision rather than restating it.
Proves
transaction_reaper_completion_decision pins all five outcomes: RejectMalformed exactly when a snapshot names a negative PID or epoch; otherwise AlreadyComplete exactly when the live entry equals the intended completion, RejectStaleIdentity exactly when it is not complete and no longer holds the prepared identity, Proceed exactly when it equals the prepared snapshot and the host's full-entry comparison holds, and RejectChangedPreparedState in every remaining case. It delegates to transaction_completion_decision rather than restating it.
Host caller
The transaction coordinator's idle-reaper completion adapter, the prepared-transaction completion task, and serialized transaction-state persistence
Proof sessions
Caller preconditions
prepared.state != completion.state (#[requires]): the host passes a completion_for pairing or the constant (PrepareAbort, CompleteAbort). The host compares the complete persisted TxnEntry, including transaction ID, current and staged identities, state, partition set, timeout, and timestamps. It serializes all writes for the transaction-state partition, rejects an entry handle replaced while waiting, and holds the current entry's lock from the recheck through the durable completion append. Prepare and complete changes are staged on clones, so allocation or append failure does not change the live entry. An exact completed retry writes nothing. Existing PrepareAbort retries also reject the persisted 2PC no-timeout sentinel. Log append durability, producer-ID allocation, crash atomicity after an append, marker transport, and coordinator ownership remain host or I/O assumptions.
transaction_marker_materialization_decisionreplay_batch_cursor_decision transaction/marker.rsrecovery/should_capture_first_downgrade.rs 3 proofs transaction_marker_materialization_decision rejects malformed identities and stale producer or coordinator generations, suppresses an exact completed retry, and selects committed-offset publication only for a pending COMMIT on __consumer_offsets; replay_batch_cursor_decision bounds every transactional-offset scan step and rejects overlap, negative deltas, range escape, and signed overflow.
Proves
transaction_marker_materialization_decision rejects malformed identities and stale producer or coordinator generations, suppresses an exact completed retry, and selects committed-offset publication only for a pending COMMIT on __consumer_offsets; replay_batch_cursor_decision bounds every transactional-offset scan step and rejects overlap, negative deltas, range escape, and signed overflow.
Host caller
Both local and inter-broker marker fan-out use the broker's shared marker materialization adapter, which reads generations from the log's producer state and scans offsets through the bounded adapter.
Proof sessions
Caller preconditions
The host supplies the request PID and epochs, the exact latest producer and coordinator epochs, whether that PID is pending, the decoded marker type, and exact topic identity. A per-partition lock spans admission, append acknowledgement, and any committed-offset actor publication, so conflicting markers cannot both observe one pending generation; an exact completed retry appends nothing. Missing producer state for a pending transaction fails closed. Offset records are decoded only from bounded transactional batches for the admitted PID, aborts and non-offsets partitions never publish, and an append error returns before publication. After a successful COMMIT append, decoded offsets remain in the locked retry backlog until the group actor acknowledges them; a delivery failure leaves that backlog intact, and an exact marker retry drains it without appending another marker. Log append durability, actor delivery, process-crash atomicity, request decoding, and producer-state recovery remain host or I/O assumptions; startup replay independently republishes only transactions closed by durable COMMIT markers.
transaction_pid_install_decision transaction/reaper.rs 2 proofs transaction_pid_install_decision admits a recovered or newly persisted transaction identity only from its hash-selected state partition, with a nonnegative current PID and epoch, an absent or well-formed staged identity, and no PID owned by another transactional ID.
Proves
transaction_pid_install_decision admits a recovered or newly persisted transaction identity only from its hash-selected state partition, with a nonnegative current PID and epoch, an absent or well-formed staged identity, and no PID owned by another transactional ID.
Host caller
The transaction coordinator's staged log replay and publication and shared PID-index adapter
Proof sessions
Caller preconditions
The host decodes complete Kafka transaction keys and values, checks the key's exact hash partition, preserves record order inside each sorted local partition, and supplies exact current PID owners. Replay folds into a temporary image: a later value evicts every older PID for only its transactional ID, an exact retry is idempotent, and a tombstone removes both state and all its PID mappings. Missing partitions, read or decode failures, regressing or overflowing batch coordinates, malformed identities, and cross-transaction PID collisions discard the temporary image, latch recovery invalid, and clear coordinator leadership before returning an error. Live publication rechecks PID ownership under one short lock after the durable append. Startup publishes before concurrent readers; producer-ID allocator uniqueness, log append and read durability, decoding, allocation, locking, and process-crash atomicity remain host and I/O assumptions.
transaction_partition_registration transaction/reaper.rs 1 proof transaction_partition_registration admits a new or idempotent partition registration only at the broker that coordinates the exact transactional ID and for the matching nonnegative producer ID and epoch, with no staged identity and a state that can enter Ongoing.
Proves
transaction_partition_registration admits a new or idempotent partition registration only at the broker that coordinates the exact transactional ID and for the matching nonnegative producer ID and epoch, with no staged identity and a state that can enter Ongoing.
Host caller
The transaction coordinator's shared partition-registration adapter, called by AddPartitionsToTxn, TxnOffsetCommit, and the transactional Produce fence
Proof sessions
Caller preconditions
The host obtains coordinator ownership from the current leader-partition set, looks the entry up by the request transactional ID, compares the stored transactional ID and full producer generation, and checks exact topic-partition set membership. Produce derives the transactional ID from the persisted PID index and persists the exact produced partition before append when this broker coordinates the transaction. Because Produce carries no transactional ID, a data-partition leader cannot resolve a transaction coordinated by another broker; an absent local PID mapping is therefore treated as remote coordinator authority, while malformed producer IDs still fail closed. An exact local retry is persisted again, so a prior uncertain registration write cannot turn into an unpersisted append. Remote coordinator authorization and registration, transaction-log durability, PID-index integrity, metadata changes after the check, and concurrent registration serialization remain host and I/O assumptions.
init_producer_id_identity_decision transaction/producer_identity.rs 1 proof init_producer_id_identity_decision bumps the epoch for a caller that supplies no producer identity, bumps it for the entry's live identity, answers the retry of a bump that already happened when the caller names the epoch before it, admits the producer ID from before a rotation only at an exhausted epoch, and fences every other supplied identity.
Proves
init_producer_id_identity_decision bumps the epoch for a caller that supplies no producer identity, bumps it for the entry's live identity, answers the retry of a bump that already happened when the caller names the epoch before it, admits the producer ID from before a rotation only at an exhausted epoch, and fences every other supplied identity.
Host caller
The transactional InitProducerId sub-path, under the same entry lock as each recovery, abort, or epoch bump it admits
Proof sessions
Caller preconditions
The host passes the wire producer_id and producer_epoch unchanged, where -1 is the sentinel every request below v3 and every first initialisation carries, and compares them against the client-visible identity of the persisted entry, which is the staged recovery identity when one exists. last_producer_epoch and prev_producer_id are what the host records on every bump and rotation, as Kafka's prepareIncrementProducerEpoch and prepareProducerIdRotation do, and the abort path that fails its marker fan-out records the epoch the producer still holds. last_producer_epoch is persisted in TransactionLogValue (#892) only under unstable.api.versions.enable, as Kafka trunk does (KAFKA-20357); Kafka 4.3.1 keeps it in memory and reloads -1. Where it is persisted, a replayed entry carries the epoch it held before the fence, and a producer that already holds it may retry its InitProducerId. At TV_2 the abort path records the epoch the producer held as the last epoch, and the completion bump is the only epoch bump. A fenced caller changes nothing, and a retry writes nothing. The kernel does not model Kafka's third isValidProducerId clause, txnMetadata.producerEpoch == NO_PRODUCER_EPOCH, which holds only for metadata handleInitProducerId has just created; the host covers it by answering a transactional ID with no entry with a fresh allocation and never calling the kernel, and every entry it does pass has a nonnegative epoch (allocation hands out epoch 0, and transaction_pid_install_decision rejects a replayed negative current or staged epoch). Entry-lock serialization, transaction-log durability, and marker fan-out remain host and I/O assumptions.
first_unstable_offsettransaction_marker_closesaborted_transaction_intervalaborted_transaction_overlaps transaction/next_producer_identity.rs 4 proofs first_unstable_offset returns Some exactly when every pending start is at or below the log end, and then selects the minimum pending start, or the log end when none is pending; transaction_marker_closes closes state only for an unambiguous COMMIT or ABORT belonging to a pending producer; aborted_transaction_interval admits only an ordered inclusive interval for a live producer; and aborted_transaction_overlaps is equivalent to intersection with a nonempty half-open Fetch range.
Proves
first_unstable_offset returns Some exactly when every pending start is at or below the log end, and then selects the minimum pending start, or the log end when none is pending; transaction_marker_closes closes state only for an unambiguous COMMIT or ABORT belonging to a pending producer; aborted_transaction_interval admits only an ordered inclusive interval for a live producer; and aborted_transaction_overlaps is equivalent to intersection with a nonempty half-open Fetch range.
Host caller
The local log's owned transaction marker and LSO path, append path, verbatim replication path, recovery replay, and local aborted-transaction index
Proof sessions
Caller preconditions
The host keeps one first offset per nonnegative producer and supplies the exact checked log end. It parses the first control-record key exactly; malformed or unknown control types do not clear pending state, and only the same producer ID can close its range. Broker marker admission supplies producer and coordinator fencing through transaction_marker_materialization_decision. The local index decodes only complete 34-byte, version-prefixed big-endian entries, rejects an unsupported version and invalid intervals, and canonicalizes only fully identical retries. Log, index, snapshot, append, rewrite, and synchronization I/O remain host assumptions; append rollback retains pending state on a sidecar failure, while an uncertain index sync may leave an exact retry record that startup safely canonicalizes.
next_producer_identity transaction/next_producer_identity.rs 1 proof next_producer_identity preserves unverified identities, increments normal and recovery epochs within their distinct bounds, and returns the supplied producer ID at the rotation boundary.
Proves
next_producer_identity preserves unverified identities, increments normal and recovery epochs within their distinct bounds, and returns the supplied producer ID at the rotation boundary.
Host caller
The broker's EndTxn producer-identity adapter
Proof sessions
Caller preconditions
The host selects recovery mode only for staged recovery identities and supplies a nonnegative, globally fresh producer ID when rotation is required. The kernel neither establishes allocation freshness nor publishes a partition fence.
should_abort_idle_transaction transaction/next_producer_identity.rs 1 proof should_abort_idle_transaction is exactly Kafka's TransactionStateManager.timedOutTransactions rule: it aborts if and only if the transaction is ongoing, its timeout is not the KIP-939 2PC sentinel i32::MAX, and start + timeout < now in exact arithmetic (strict, as Kafka's comparison is). A proved corollary: a backwards or stopped clock (now <= start) never aborts a transaction with a nonnegative timeout, which is every timeout InitProducerId persists.
Proves
should_abort_idle_transaction is exactly Kafka's TransactionStateManager.timedOutTransactions rule: it aborts if and only if the transaction is ongoing, its timeout is not the KIP-939 2PC sentinel i32::MAX, and start + timeout < now in exact arithmetic (strict, as Kafka's comparison is). A proved corollary: a backwards or stopped clock (now <= start) never aborts a transaction with a nonnegative timeout, which is every timeout InitProducerId persists.
Host caller
The broker's shared 2PC timeout and reaper adapter, called by the idle reaper
Proof sessions
Caller preconditions
None: the contract accepts every timeout, start, and clock value, including a zero or negative timeout replayed from the transaction log, which Kafka's TransactionLog.read also accepts unchecked. The host maps only TxnState::Ongoing to the kernel's ongoing state.
log_batch_kind transaction/marker.rs 2 proofs log_batch_kind distinguishes data, ABORT, COMMIT, barrier, and unknown control batches from the actual control flag and first record key.
Proves
log_batch_kind distinguishes data, ABORT, COMMIT, barrier, and unknown control batches from the actual control flag and first record key.
Host caller
The log's shared control classifier, used by native append and recovery; replicated controls enter shared bookkeeping through verbatim append.
Proof sessions
Caller preconditions
The caller faithfully decodes the first record and attributes. Version bytes remain ignored by the existing parser. Native transaction application, sidecar I/O, coordinator fencing, and concurrency remain host obligations.

Groups and coordination 7

classic_upgrade_epochconsumer_downgrade_epochgroup_migration_record_plan group_migration.rs 3 proofs classic_upgrade_epoch admits a classic-to-consumer migration exactly for the consumer protocol when every member subscription decodes; consumer_downgrade_epoch admits the reverse exactly when every member has a classic facade. Both keep each valid source epoch and clamp only a negative source to zero. group_migration_record_plan makes an upgrade tombstone classic state and write all next-gen key families, while a downgrade writes classic state and tombstones every next-gen key family for every member.
Proves
classic_upgrade_epoch admits a classic-to-consumer migration exactly for the consumer protocol when every member subscription decodes; consumer_downgrade_epoch admits the reverse exactly when every member has a classic facade. Both keep each valid source epoch and clamp only a negative source to zero. group_migration_record_plan makes an upgrade tombstone classic state and write all next-gen key families, while a downgrade writes classic state and tombstones every next-gen key family for every member.
Host caller
The unified coordinator's upgrade adapter, downgrade adapter, upgrade trigger, and downgrade trigger
Proof sessions
Caller preconditions
The upgrade adapter requires exact protocol_type == "consumer" and successful schema decoding of every selected subscription. It copies member IDs, static identities, clients, hosts, protocol lists, timeouts, subscriptions, and last synced assignments. The downgrade trigger separately requires a nonempty group and an enabled policy, then maps each server target through the current topic-ID image before conversion. Deleted topic IDs are omitted. Both adapters use the proved record action and assert one k5, k7, and k8 record per member. The offsets log appends the full flip as one batch; actor state changes only after that append succeeds. Bootstrap replay and compacted-log ordering remain host assumptions, covered by upgrade/downgrade replay tests. Failed appends publish no partial batch, and exact conversion retries emit the same keyed records. Request decoding, metadata-image consistency, record encoding, append durability, cache publication, and process-crash atomicity remain host or I/O assumptions.
uniform_quota_split uniform_assignor/select_least_loaded.rs 1 proof uniform_quota_split returns Kafka's minimumMemberQuota and remainingMembersToGetAnExtraPartition as the Euclidean floor and remainder: minimum * members + extras == total and extras < members.
Proves
uniform_quota_split returns Kafka's minimumMemberQuota and remainingMembersToGetAnExtraPartition as the Euclidean floor and remainder: minimum * members + extras == total and extras < members.
Host caller
The unified coordinator's uniform group assignor, once for each homogeneous assignment
Proof sessions
Caller preconditions
The host passes the partition total of the subscribed topics that exist in the metadata snapshot and a nonzero member count; it calls the kernel only when the group has at least one member.
homogeneous_member_quotas uniform_assignor/select_least_loaded.rs 1 proof homogeneous_member_quotas states Kafka's UniformHomogeneousAssignmentBuilder.maybeRevokePartitions quota rule for every member in order. Each member's target is minimum or minimum + 1; it keeps min(owned, target) of its current partitions and receives the rest; the extra slot goes by keeps_extra_quota (keep it when already above the minimum, or when the members left are no more than the extras left); and no extra slot is left after the last member, so exactly extras members get the larger target.
Proves
homogeneous_member_quotas states Kafka's UniformHomogeneousAssignmentBuilder.maybeRevokePartitions quota rule for every member in order. Each member's target is minimum or minimum + 1; it keeps min(owned, target) of its current partitions and receives the rest; the extra slot goes by keeps_extra_quota (keep it when already above the minimum, or when the members left are no more than the extras left); and no extra slot is left after the last member, so exactly extras members get the larger target.
Host caller
The unified coordinator's uniform group assignor, once for each homogeneous assignment
Proof sessions
Caller preconditions
The host passes the split from uniform_quota_split (so extras < members holds) and, for each member in ascending member-ID order, how many partitions of the subscribed topics it owns in its sanitized current target assignment (in-range, subscribed, first claimant wins). minimum < usize::MAX holds because the host materializes one owner slot per partition. The host keeps the first retain owned partitions in (Kafka Uuid topic order, partition order), appends the rest after the never-assigned partitions, and hands the list out in member order by fill; because the targets sum to the total, every partition is placed exactly once. It runs this builder only when the group spec says homogeneous and every member's resolved topic IDs agree.
select_least_loaded uniform_assignor/select_least_loaded.rs 1 proof select_least_loaded returns no index exactly for an empty candidate list. Otherwise it returns an in-bounds candidate that no other candidate precedes under load_precedes (smaller current load, then smaller load at the start of the topic), and that strictly precedes every earlier candidate, so the first least-loaded candidate wins. This is the order of Kafka's MemberAssignmentBalancer.nextLeastLoadedMember when no partition is taken away.
Proves
select_least_loaded returns no index exactly for an empty candidate list. Otherwise it returns an in-bounds candidate that no other candidate precedes under load_precedes (smaller current load, then smaller load at the start of the topic), and that strictly precedes every earlier candidate, so the first least-loaded candidate wins. This is the order of Kafka's MemberAssignmentBalancer.nextLeastLoadedMember when no partition is taken away.
Host caller
The unified coordinator's uniform group assignor, once for each unassigned partition in the heterogeneous builder's assignRemainingPartitions
Proof sessions
Caller preconditions
The host passes the topic's subscribers in ascending member order with their exact current total load and their load when the topic started, visits topics in Kafka's sortTopicIds order, and assigns the partition only to the chosen subscriber. A property test checks that the kernel's picks equal a port of nextLeastLoadedMember. The final balancing pass (balanceTopics) is an unverified step-for-step port of Kafka's MemberAssignmentBalancer, covered by Kafka's UniformHeterogeneousAssignmentBuilderTest scenarios. Membership, subscription, current-target and partition-count freshness remain snapshot assumptions; the pure assignor performs no I/O. Replica racks are not consulted, as in Kafka trunk.
barrier_target_count_decisionbarrier_marker_fence_decisionbarrier_placement_decisionbarrier_cut_classification barrier/cut_classification.rs 4 proofs barrier_target_count_decision accepts only a positive, exactly representable target-count extension. barrier_marker_fence_decision equals its reference rule: a missing image entry or negative epoch is Malformed, then a different leader in the image or installed partition is NotLeader, then a different epoch is FencedEpoch, and only an exact generation is Append; the receiver maps NotLeader to NOT_LEADER_OR_FOLLOWER and the other two refusals to FENCED_LEADER_EPOCH. barrier_placement_decision admits only requested, nonnegative marker offsets. barrier_cut_classification makes a cut complete exactly when its exact target fold has no missing or malformed target.
Proves
barrier_target_count_decision accepts only a positive, exactly representable target-count extension. barrier_marker_fence_decision equals its reference rule: a missing image entry or negative epoch is Malformed, then a different leader in the image or installed partition is NotLeader, then a different epoch is FencedEpoch, and only an exact generation is Append; the receiver maps NotLeader to NOT_LEADER_OR_FOLLOWER and the other two refusals to FENCED_LEADER_EPOCH. barrier_placement_decision admits only requested, nonnegative marker offsets. barrier_cut_classification makes a cut complete exactly when its exact target fold has no missing or malformed target.
Host caller
The barrier target expansion and cut builder, the local and remote marker fan-out, the linearized local append, and the receiving WriteBarrierMarkers handler
Proof sessions
Caller preconditions
The host freezes topic partition counts from one metadata image and expands each admitted count in index order. A malformed or overflowing count contributes no marker and cannot yield a complete cut. The receiver requires an exact leader epoch; a missing epoch is fenced. The local append holds the partition's metadata-transition read lock through the writer acknowledgement, so an admitted generation cannot change before the append finishes. Remote transport, metadata freshness before an attempt, partition-writer durability, and process-crash atomicity remain host or I/O assumptions.
share_prune_frontier share.rs 1 proof share_prune_frontier returns the minimum latest-snapshot offset across every supplied live share key, proving both lower-bound safety and the maximal safe prune point.
Proves
share_prune_frontier returns the minimum latest-snapshot offset across every supplied live share key, proving both lower-bound safety and the maximal safe prune point.
Host caller
The share coordinator's __share_group_state pruning adapter
Proof sessions
Caller preconditions
The caller freezes every live key mapped to one state partition, then supplies each exact last_snapshot_offset. A key without a folded snapshot carries the default offset 0, which pins pruning at the log start. An empty partition returns no frontier. Concurrent state changes after collection may make pruning more conservative; the selected lower bound remains safe.
share_offset_mutation_decision share.rs 1 proof share_offset_mutation_decision admits an administrative share-offset mutation only on the coordinator for an empty group and a requested partition whose observed data-partition leader epoch is still current. An exact retry changes nothing. Any other admitted mutation advances the state epoch by exactly one and fails closed on overflow.
Proves
share_offset_mutation_decision admits an administrative share-offset mutation only on the coordinator for an empty group and a requested partition whose observed data-partition leader epoch is still current. An exact retry changes nothing. Any other admitted mutation advances the state epoch by exactly one and fails closed on overflow.
Host caller
The share-group actor's serialized reset and delete adapter, called by AlterShareGroupOffsets and DeleteShareGroupOffsets
Proof sessions
Caller preconditions
The handler checks current group-coordinator ownership before it sends the complete requested batch to one group actor. The actor checks membership before persister access and blocks heartbeats until the batch ends. It reads the durable state, reads the current data-partition leader epoch again, and passes those exact facts to the kernel. Persister initialization independently rejects a concurrent state-epoch advance. Reset rewrites only valid requested partitions. Delete replaces each selected state with a durable uninitialized snapshot at the next epoch before it removes the topic from group partition metadata; this snapshot fences older acquisition writes across restart. Metadata removal rolls back in memory if its offsets-log write fails. Request decoding, metadata and persister reads, actor delivery, log durability, cache invalidation, and a leader change after the final check remain host or I/O assumptions.

Security and quotas 20

acl_identity_matchacl_resource_matchacl_operation_matchacl_decision authz/acl_decision.rs 4 proofs Runs in browser acl_identity_match, acl_resource_match, and acl_operation_match prove principal and host wildcards, literal and prefixed resource applicability, exact operations, All, and Kafka's one-way implication arrows gated on is_allow; acl_decision then pins every AclDecision variant to Kafka's AclAuthorizer order: super-user bypass, explicit deny precedence, ACL-backed allow, and allow.everyone.if.no.acl.found's no-ACL-at-all allow, over the named AclFacts (resource applicability takes the named AclResourceFacts).
Proves
acl_identity_match, acl_resource_match, and acl_operation_match prove principal and host wildcards, literal and prefixed resource applicability, exact operations, All, and Kafka's one-way implication arrows gated on is_allow; acl_decision then pins every AclDecision variant to Kafka's AclAuthorizer order: super-user bypass, explicit deny precedence, ACL-backed allow, and allow.everyone.if.no.acl.found's no-ACL-at-all allow, over the named AclFacts (resource applicability takes the named AclResourceFacts).
Host caller
The shared krabka-authz applicability adapter and precedence loop
Proof sessions
Caller preconditions
The host computes exact string equality, the canonical User:*, host and literal-resource * wildcards, str::starts_with, resource-type equality, and exhaustively maps every metadata operation and pattern variant. matches_operation maps is_allow exactly to permission == PermissionType::Allow, so the implication arrows apply only to a stored ALLOW ACL, never a DENY one. Both ACL sources must return every potentially applicable resource entry; the authorizer rechecks applicability before precedence. AclFacts::default_decision is AclDefault::Allow exactly when allow_everyone_if_no_acl_found && !has_resource_acls, where has_resource_acls is true only when the precedence loop saw at least one ACL whose type, name, and pattern apply to the resource -- independent of principal, host, operation, or permission type -- so it only ever widens the "nothing matched" case, never overrides an explicit ALLOW or DENY.
request_auth_admission authz/acl_decision.rs 1 proof request_auth_admission admits only handshake and version discovery before a handshake, only authentication once a handshake (initial or re-authentication) chose a mechanism, nothing after a re-authentication handshake switched mechanisms, and every API afterward.
Proves
request_auth_admission admits only handshake and version discovery before a handshake, only authentication once a handshake (initial or re-authentication) chose a mechanism, nothing after a re-authentication handshake switched mechanisms, and every API afterward.
Host caller
The broker's shared ConnectionAuth adapter
Proof sessions
Caller preconditions
The host maps each connection phase exactly and passes the Kafka API key wire value unchanged.
opa_cache_admissionopa_cache_expiryopa_error_decision opa.rs 3 proofs opa_cache_admission reuses the cached allow or deny unchanged exactly while its deadline is strictly in the future. opa_cache_expiry caches exactly when the TTL is positive and now + ttl is representable in i128, and then sets the deadline to exactly now + ttl, and opa_error_decision maps the configured outage policy exactly.
Proves
opa_cache_admission reuses the cached allow or deny unchanged exactly while its deadline is strictly in the future. opa_cache_expiry caches exactly when the TTL is positive and now + ttl is representable in i128, and then sets the deadline to exactly now + ttl, and opa_error_decision maps the configured outage policy exactly.
Host caller
The broker's OPA authorizer and cache key
Proof sessions
Caller preconditions
The host builds the key from every OPA input axis: principal, operation, resource type, resource name, and source IP. The LRU lookup by that key, using Rust's derived equality and hashing, is what makes every entry the kernel sees an exact-key match. Deadlines are milliseconds since the origin of an injected qubit_clock::MonotonicClock (StdMonotonicClock in production), so a wall-clock step cannot extend a cached decision; a cache hit returns the stored decision unchanged, and the exact deadline is a miss. Nonpositive TTL configuration is rejected, and deadline overflow skips caching. HTTP delivery, OPA policy evaluation, response parsing, mutex and LRU behavior, and process-crash persistence remain host or I/O assumptions. Concurrent misses can call OPA more than once; each caller returns its own response and a later cache hit returns one completed response unchanged.
user_client_quota_precedenceip_quota_precedence quota.rs 2 proofs Runs in browser user_client_quota_precedence selects Kafka's first present exact, default, or combined user/client candidate. ip_quota_precedence selects an exact IP before the IP default.
Proves
user_client_quota_precedence selects Kafka's first present exact, default, or combined user/client candidate. ip_quota_precedence selects an exact IP before the IP default.
Host caller
The broker's shared quota lookup adapter, used by request, produce, fetch, and controller-mutation enforcement
Proof sessions
Caller preconditions
The host builds the eight canonical user/client keys and two canonical IP keys in documented order, tests the requested quota key in each metadata entry, and returns the rate from the one proved slot. The adapter separately constructs the sensor key from the selected level and concrete user/client tags; that key can differ from the configured default entity. Derived EntityKey equality and metadata-image snapshot consistency are host assumptions. Missing, removed, nonmatching, or differently scoped entries remain absent candidates; retries against the same image are exact. Bucket state is keyed by the quota key and returned sensor key; its projection and sharing are checked by production tests, not established by the selector contract. Quota enforcement charges the full amount through record/record_bounded and retains debt. The charge/credit kernels below prove its integer transitions; plan_consume grant conservation and the bounded bucket model alone do not cover debt.
jwks_cache_admissionjwks_on_demand_refresh_decision jwks.rs 2 proofs jwks_cache_admission admits only one stable, fully published cache generation and fails closed for a never-fetched, expired, or future-dated cache when hard expiry is enabled. jwks_on_demand_refresh_decision refreshes exactly when the clock and pause are nonnegative, the clock has not stepped back behind the last refresh, and either no refresh has happened or the pause has elapsed (jwks_on_demand_refresh_allowed), stamping now without signed overflow; every other input is rate-limited.
Proves
jwks_cache_admission admits only one stable, fully published cache generation and fails closed for a never-fetched, expired, or future-dated cache when hard expiry is enabled. jwks_on_demand_refresh_decision refreshes exactly when the clock and pause are nonnegative, the clock has not stepped back behind the last refresh, and either no refresh has happened or the pause has elapsed (jwks_on_demand_refresh_allowed), stamping now without signed overflow; every other input is rate-limited.
Host caller
The broker's JWKS refresher and OAUTHBEARER validator boundary
Proof sessions
Caller preconditions
The refresher publishes keys and their fetch timestamp inside an odd generation and commits the next even generation with release ordering. Both the data listener and controller listener share those publication atomics. The validator reads the generation before and after the timestamp, validates the token, and rechecks the same generation before admission. Generation exhaustion and competing writers preserve the prior cache. HTTP fetch, JWKS parsing, signature verification, wall-clock accuracy, and process-crash persistence remain host, crypto, or I/O assumptions.
controller_request_admissionThe consumer history authz/controller_session.rscomposition/controller_session.rs 2 proofs controller_request_admission maps an expired controller credential to failed authentication before every API. The consumer history connects JWKS publication, completion-time cache/session admission and ACL precedence to the exact handled DescribeQuorum prefix before the first expired observation.
Proves
controller_request_admission maps an expired controller credential to failed authentication before every API. The consumer history connects JWKS publication, completion-time cache/session admission and ACL precedence to the exact handled DescribeQuorum prefix before the first expired observation.
Host caller
The SASL deadline handoff, RaftConnection and controller request gate, before bootstrap/ACL/dispatch
Proof sessions
Caller preconditions
The host preserves the computed deadline, uses the existing epoch clock and captures current request grants. Plain/mTLS/uncapped regular SCRAM retain no deadline. Controller sessions reconnect to reauthenticate. Crypto, accurate clock/claim/ACL projection, frame decoding and actual connection closure remain external; later credential mutation, idle timers and operation completion are outside the captured request history.
sasl_session_expirysession_expired_for_request authz/session.rs 4 proofs sasl_session_expiry selects the earlier credential or positive listener cap, retaining overflowed caps at i64::MAX; session_expired_for_request refuses ordinary APIs at or after that deadline while allowing reauthentication frames.
Proves
sasl_session_expiry selects the earlier credential or positive listener cap, retaining overflowed caps at i64::MAX; session_expired_for_request refuses ordinary APIs at or after that deadline while allowing reauthentication frames.
Host caller
The shared SASL session adapter and connection request gate, used by PLAIN, SCRAM and GSSAPI session creation
Proof sessions
Caller preconditions
The host converts configured Time into milliseconds and projects authenticated connection expiry. Cryptographic success, truthful pending-token expiry/identity transfer and coherent snapshots remain outside; consumer theorems require nonnegative, nondecreasing clock observations.
oauth_validation_admission oauth/completion.rs 2 proofs oauth_validation_admission admits completion exactly for a nonnegative forward clock and a still-current fresh supplied cache snapshot, evaluating its age at completion.
Proves
oauth_validation_admission admits completion exactly for a nonnegative forward clock and a still-current fresh supplied cache snapshot, evaluating its age at completion.
Host caller
The common OAuth validation boundary, called by both initial authentication and reauthentication
Proof sessions
Caller preconditions
The host reads its clock before and after asynchronous validation, captures the final generation, and uses the returned completion time for session admission. Atomic publication, accurate clock/claim observations and cryptographic success remain outside. No cache freshness guarantee follows from an absent snapshot.
oauth_session_admission oauth.rs 1 proof oauth_session_admission admits exactly the sessions oauth_session_admissible names: a present future expiry whose remaining lifetime fits an i64, a positive enabled cap, and a matching reauthentication principal; it rejects every other input. An admitted session carries the exact smaller of the token lifetime and enabled cap (oauth_session_lifetime), plus the corresponding absolute expiry.
Proves
oauth_session_admission admits exactly the sessions oauth_session_admissible names: a present future expiry whose remaining lifetime fits an i64, a positive enabled cap, and a matching reauthentication principal; it rejects every other input. An admitted session carries the exact smaller of the token lifetime and enabled cap (oauth_session_lifetime), plus the corresponding absolute expiry.
Host caller
The OAUTHBEARER handler's initial-authentication and reauthentication transitions
Proof sessions
Caller preconditions
The host maps the validator's optional expiry, the configured Time cap, the authentication phase, and exact string principal equality into the kernel. Only an admitted result enters Authenticated; initial rejection enters the RFC 7628 failure exchange, while reauthentication rejection keeps the prior snapshot for connection closure. Token parsing and validation, principal derivation, wall-clock accuracy, response delivery, and connection closure remain host, crypto, or I/O assumptions.
create_token_deadlinesrenew_token_expiryexpire_token_deadlinetoken_is_active delegation_token/token_is_active.rs 4 proofs create_token_deadlines, renew_token_expiry, expire_token_deadline, and token_is_active implement Kafka's KIP-48 deadline selection, with saturating sums and no resurrection of expired tokens, and authentication admission.
Proves
create_token_deadlines, renew_token_expiry, expire_token_deadline, and token_is_active implement Kafka's KIP-48 deadline selection, with saturating sums and no resurrection of expired tokens, and authentication admission.
Host caller
The broker's create, renew, expire, and SCRAM paths
Proof sessions
Caller preconditions
The handlers supply the configured lifetime, exact image deadlines, and current wall-clock timestamp.
token_mutation_decision delegation_token/token_mutation_decision.rs 2 proofs token_mutation_decision specifies exact Append/Retry admission (ruling out rejecting every request), admits only an exact committed token generation, rejects retained metadata-log tails and stale or malformed replacements, keeps every renewal between now and the maximum timestamp, and treats exact retries as no-ops.
Proves
token_mutation_decision specifies exact Append/Retry admission (ruling out rejecting every request), admits only an exact committed token generation, rejects retained metadata-log tails and stale or malformed replacements, keeps every renewal between now and the maximum timestamp, and treats exact retries as no-ops.
Host caller
The broker's renew, expire, and expiry sweep adapters, plus the controller's atomic admission path
Proof sessions
Caller preconditions
The controller projects the full committed token record and compares it in the same engine turn as append. Private API 1005 carries the precondition unchanged from broker-only nodes to the leader. The consumer derives mutation state from a concrete expiry replacement, distinguishing renewal from authentication at equality; one identity and its immutable fields stay fixed. Commit ordering, faithful metadata/clock projection and cryptographic success remain external.
token_api_admission delegation_token/token_mutation_decision.rs 1 proof token_api_admission requires a non-anonymous authenticated identity for every token API and refuses a token-authenticated session on all four, DescribeDelegationToken included.
Proves
token_api_admission requires a non-anonymous authenticated identity for every token API and refuses a token-authenticated session on all four, DescribeDelegationToken included.
Host caller
The broker's shared ConnectionAuth adapter, called by all four delegation-token handlers before token access or mutation
Proof sessions
Caller preconditions
The host maps only AuthMethod::Anonymous and unestablished connection states to an absent authenticated identity.
token_describe_visible delegation_token/token_mutation_decision.rs 1 proof token_describe_visible takes five booleans (owner_filter_matches, caller_is_owner, caller_is_requester, caller_is_renewer, acl_allows) and returns exactly owner_filter_matches && (caller_is_owner || caller_is_requester || caller_is_renewer || acl_allows), Kafka's DelegationTokenManager.filterToken; a token-authenticated caller never reaches it, having already been refused by token_api_admission.
Proves
token_describe_visible takes five booleans (owner_filter_matches, caller_is_owner, caller_is_requester, caller_is_renewer, acl_allows) and returns exactly owner_filter_matches && (caller_is_owner || caller_is_requester || caller_is_renewer || acl_allows), Kafka's DelegationTokenManager.filterToken; a token-authenticated caller never reaches it, having already been refused by token_api_admission.
Host caller
The broker's DescribeDelegationToken filter
Proof sessions
Caller preconditions
The host compares canonical principals, treats a missing wire owner list as no filter (an empty one returns no tokens before the filter), matches a filter entry against a token's owner, requester OR a listed renewer (TokenInformation.ownerOrRenewer), and passes as acl_allows the authorizer's exact result for either grant Kafka checks: Describe on DelegationToken:<token_id> (authorizeToken) or KIP-373's DescribeTokens on User:<owner> (authorizeRequester).
scram_credential_source delegation_token/token_mutation_decision.rs 1 proof scram_credential_source selects the credential store from the client's tokenauth SCRAM extension alone, as Kafka's ScramSaslServer does: without it only a regular SCRAM credential can authenticate, with it only a live delegation token can (under either SCRAM mechanism, since Kafka's DelegationTokenManager prepares a token credential for every ScramMechanism); a token id that is also a username is checked against the store the client asked for; an expired token and every missing credential fail closed.
Proves
scram_credential_source selects the credential store from the client's tokenauth SCRAM extension alone, as Kafka's ScramSaslServer does: without it only a regular SCRAM credential can authenticate, with it only a live delegation token can (under either SCRAM mechanism, since Kafka's DelegationTokenManager prepares a token credential for every ScramMechanism); a token id that is also a username is checked against the store the client asked for; an expired token and every missing credential fail closed.
Host caller
The broker's SCRAM round-one credential lookup
Proof sessions
Caller preconditions
The host parses tokenauth from the client-first extensions as Kafka's ScramExtensions.tokenAuthenticated (Boolean.parseBoolean, last duplicate wins), performs exact metadata lookups, derives token liveness through the separately proved token_is_active kernel, and synthesizes the token's credential for the negotiated mechanism.
scram_alteration_decision scram.rs 1 proof scram_alteration_decision preserves the first per-user error, rejects a second previously accepted alteration as a duplicate, then applies authorization, nonempty-name, mechanism, iteration-bound, and deletion-target checks in Kafka response precedence. Only its two accepted mechanism variants may produce a record.
Proves
scram_alteration_decision preserves the first per-user error, rejects a second previously accepted alteration as a duplicate, then applies authorization, nonempty-name, mechanism, iteration-bound, and deletion-target checks in Kafka response precedence. Only its two accepted mechanism variants may produce a record.
Host caller
The AlterUserScramCredentials validation adapter and request planner
Proof sessions
Caller preconditions
The adapter passes str::is_empty, the wire mechanism and iteration fields, the fixed Kafka iteration bounds, and exact credential presence from one metadata image. The planner derives prior state from its per-user accepted and rejected maps, removes a pending success on duplicate, and constructs exactly one delete or upsert record only for an accepted decision. Key derivation, allocation, request decoding, controller submission, log durability, and changes after the deletion-target image read remain host, crypto, or I/O assumptions. A retry against the same image produces the same plan; a changed image is validated as a new snapshot.
plan_consume throttle.rs 1 proof Runs in browser plan_consume caps a refill at the burst, grants the smaller of the request and the capped total, and conserves the capped tokens. Its body proves that the saturating u64 total equals the unbounded min(available + refill, burst) for every input, overflow included.
Proves
plan_consume caps a refill at the burst, grants the smaller of the request and the capped total, and conserves the capped tokens. Its body proves that the saturating u64 total equals the unbounded min(available + refill, burst) for every input, overflow included.
Host caller
krabka-throttle
Proof sessions
Caller preconditions
The host holds the bucket lock across the refill claim and the commit, so the kernel's inputs and its stored result belong to one configuration; the Stateright bucket_model checks that protocol.
quota_debt_cap quota/debt_cap.rs 2 proofs quota_debt_cap floors the micro-token credit earned during the converted wait, clipped at the representable debt limit, including wide-product overflow.
Proves
quota_debt_cap floors the micro-token credit earned during the converted wait, clipped at the representable debt limit, including wide-product overflow.
Host caller
BucketState::debt_cap, shared by bounded charges
Proof sessions
Caller preconditions
The host converts the wait to nanoseconds and supplies the effective micro-rate. The composition requires a fixed positive configuration and coherent state; reaching the converted wait clears capped debt. Clock provenance, scheduling and atomic state publication remain external.
quota_whole_request quota/request.rs 2 proofs quota_whole_request selects exactly the minimum of requested whole tokens and whole tokens available, even when multiplication saturates.
Proves
quota_whole_request selects exactly the minimum of requested whole tokens and whole tokens available, even when multiplication saturates.
Host caller
whole_token_request, shared by consumes and the bounded bucket model
Proof sessions
Caller preconditions
Positive storage units per token and a faithfully projected refilled balance. The trace proves conservation and service for fixed positive rate/burst, with a ghost grant sum isolating the additive ledger from token scaling; serialization and clock provenance remain host facts.
quota_refill quota/refill.rs 2 proofs quota_refill converts elapsed nanoseconds at the effective micro-rate into debt-first, burst-capped credit, retaining a fractional numerator below one billion. Wide arithmetic preserves credit beyond u64::MAX through debt repayment.
Proves
quota_refill converts elapsed nanoseconds at the effective micro-rate into debt-first, burst-capped credit, retaining a fractional numerator below one billion. Wide arithmetic preserves credit beyond u64::MAX through debt repayment.
Host caller
BucketState::refilled, used by consumes, charges, and rate changes
Proof sessions
Caller preconditions
Fraction below one billion; available at/below burst; at most one of available/debt nonzero. The host claims the whole elapsed interval once and stores the fraction with the balance; rate and burst stay fixed between the composed intervals. Floating-point rate/wait conversion and lock publication are checked outside Creusot.
quota_creditquota_charge quota.rs 2 proofs quota_credit repays debt before adding refill/refund credit to the burst-capped balance; quota_charge charges the effective request in full and caps the unpaid debt. Both preserve the coherent balance/debt shape.
Proves
quota_credit repays debt before adding refill/refund credit to the burst-capped balance; quota_charge charges the effective request in full and caps the unpaid debt. Both preserve the coherent balance/debt shape.
Host caller
krabka-throttle, for actual refill, record/record_bounded, and refund transitions
Proof sessions
Caller preconditions
The host converts requested whole tokens and elapsed time to effective micro-tokens, computes the debt cap, and stores the whole group under its lock. Exact refund restoration needs no discarded debt and no intervening operation; the proof wrapper rejects unrepresentable debt, whereas the runtime saturates. Repayment is stated over actual credited tokens rather than wall time.

Integrity and operations 11

spool_append_decision audit/spool_append_decision.rs 1 proof spool_append_decision admits an audit frame without overflowing the byte cap and advances the fsync cadence without wrapping.
Proves
spool_append_decision admits an audit frame without overflowing the byte cap and advances the fsync cadence without wrapping.
Host caller
krabka-audit
Proof sessions
Caller preconditions
sync_every > 0; unsynced < sync_every.
settle_loss_batch audit/spool_append_decision.rs 1 proof settle_loss_batch settles the pending fail-open audit losses against a durable records-lost marker. When the marker names the pending generation, it removes exactly the count the marker reports (the settled count plus the batch equals the old count), and moves any remainder to the next generation (the generation changes if and only if a remainder is left, and only by one). A marker of another generation leaves the pending losses unchanged.
Proves
settle_loss_batch settles the pending fail-open audit losses against a durable records-lost marker. When the marker names the pending generation, it removes exactly the count the marker reports (the settled count plus the batch equals the old count), and moves any remainder to the next generation (the generation changes if and only if a remainder is left, and only by one). A marker of another generation leaves the pending losses unchanged.
Host caller
PendingLosses::commit after the writer syncs a marker, and PendingLosses::reconcile at Spool::open, with the batch parsed from the spool's marker whose loss_generation matches the sidecar's; the spool_model drives it for both
Proof sessions
Caller preconditions
The host passes a batch that is a snapshot of the same pending state, so the batch never exceeds the count (the kernel clamps that case to zero rather than conserving it). A generation at u64::MAX saturates instead of advancing; reaching it takes 2^64 settlements. The host holds the pending state under one mutex so a concurrent AuditHandle::emit loss lands either before or after the settlement, persists the result to the audit.losses sidecar, and parses marker JSON; parse failures mean no marker.
audit_checkpoint_admissionaudit_loss_marker_admission audit/spool_append_decision.rs 2 proofs audit_checkpoint_admission admits a signed checkpoint only for the exact head and predecessor sequence of a nonempty verified chain. audit_loss_marker_admission admits exactly a marker with the two-field {records_lost, loss_generation} shape, a positive count, and a generation strictly newer than the last accepted one, and returns that generation.
Proves
audit_checkpoint_admission admits a signed checkpoint only for the exact head and predecessor sequence of a nonempty verified chain. audit_loss_marker_admission admits exactly a marker with the two-field {records_lost, loss_generation} shape, a positive count, and a generation strictly newer than the last accepted one, and returns that generation.
Host caller
The audit partition checkpoint and loss-marker walk
Proof sessions
Caller preconditions
The host parses the exact JSON object, maps a missing or non-u64 count to zero and a missing or non-u64 generation to None, verifies the checkpoint with the externally trusted Ed25519 key, compares the opaque recomputed head exactly, and carries the admitted loss generation across the ordered partition walk. Segment reads, record decoding, hashing, signature verification, trusted-key distribution, and filesystem integrity remain host, crypto, or I/O assumptions.
chain_step chain.rs 1 proof chain_step accepts exactly the expected sequence and opaque previous head, advances by one, and rejects sequence exhaustion instead of wrapping.
Proves
chain_step accepts exactly the expected sequence and opaque previous head, advances by one, and rejects sequence exhaustion instead of wrapping.
Host caller
The audit partition verifier and spool resume walk; the krabka-remote-storage WORM verifier
Proof sessions
Caller preconditions
The host computes head_matches by exact comparison with its independently recomputed chain head. The spool's first unread chained record is the continuation anchor because the durable replay cursor may have removed its prefix; every later unread record must bind to it exactly. Malformed headers, gaps, head mismatches, and sequence exhaustion poison resume, and broker startup disables the audit writer on that error. Hashing, signatures, the replay cursor's integrity, and filesystem reads remain host, crypto, or I/O assumptions.
worm_signature_decisionworm_object_set_decision worm.rs 3 proofs worm_signature_decision admits only a present signature whose named key is externally trusted and whose canonical binding verifies; worm_object_set_decision equals its reference rule, which reports the first failed check in diagnostic order (empty, duplicate key, coordinates, presence, count, size, required digest) and admits only a set that passes all of them.
Proves
worm_signature_decision admits only a present signature whose named key is externally trusted and whose canonical binding verifies; worm_object_set_decision equals its reference rule, which reports the first failed check in diagnostic order (empty, duplicate key, coordinates, presence, count, size, required digest) and admits only a set that passes all of them.
Host caller
The WORM verifier's signature adapter, object adapter, and chain walk
Proof sessions
Caller preconditions
The host reads manifests through the fixed byte cap, rejects unknown JSON fields and format versions, recomputes the chain head from canonical_manifest_bytes, constructs the domain-separated signing bytes, compares the embedded public key with the externally trusted key, and delegates Ed25519 verification to ring. It parses every object filename and binds its base offset, segment UUID, and suffix to the signed segment identity; a one-time directory listing supplies exact same-segment object counts, presence, and sizes. Deep mode streams every current object version through SHA-256; shallow mode deliberately proves only set, coordinate, and size completeness and cannot detect a same-size substitution. Unsigned and untrusted manifests, orphan objects, duplicate keys, wrong coordinates, missing or extra objects, size mismatches, and deep digest mismatches make the programmatic report incomplete. The CLI may still grade unrelated orphan objects as a warning unless --strict-orphans is set. Listing consistency during the walk, object-store reads, canonical encoding, SHA-256, Ed25519, and key distribution remain host, crypto, or I/O assumptions. Tail truncation that removes both manifests and their objects requires an independently stored expected head.
select_chain_tip chain.rs 1 proof select_chain_tip selects the eligible WORM receipt with the greatest (start offset, sequence) rank; chain_step rejects writer-side sequence exhaustion instead of repeating u64::MAX.
Proves
select_chain_tip selects the eligible WORM receipt with the greatest (start offset, sequence) rank; chain_step rejects writer-side sequence exhaustion instead of repeating u64::MAX.
Host caller
The krabka-remote-storage WORM writer and the broker archive copy loop
Proof sessions
Caller preconditions
The host marks a receipt eligible only after excluding delete states, decoding its metadata, and confirming that it carries a produced head; opaque heads remain outside Creusot.
feature_update_decision features/feature_update_decision.rs 1 proof feature_update_decision decides every UpdateFeatures feature row and equals the reference decision feature_update_model: it returns the first failed rule in the order of Kafka 4.3.1's FeatureControlManager.updateFeature (known upgrade type, a nonnegative level, every node supports the level, direction against the current level with an unfinalized feature read as 0), then the per-feature tail: for metadata.version, updateMetadataVersion's refusal of a downgrade across a level whose didMetadataChange is set, with a distinct decision for SAFE_DOWNGRADE and UNSAFE_DOWNGRADE; for kraft.version, an upgrade handed to Raft, a downgrade refused, and a downgrade type at the current level a no-op; for every other feature, Feature.validateVersion's KIP-1022 dependencies. An admitted metadata.version row that crosses a changed level is never a downgrade.
Proves
feature_update_decision decides every UpdateFeatures feature row and equals the reference decision feature_update_model: it returns the first failed rule in the order of Kafka 4.3.1's FeatureControlManager.updateFeature (known upgrade type, a nonnegative level, every node supports the level, direction against the current level with an unfinalized feature read as 0), then the per-feature tail: for metadata.version, updateMetadataVersion's refusal of a downgrade across a level whose didMetadataChange is set, with a distinct decision for SAFE_DOWNGRADE and UNSAFE_DOWNGRADE; for kraft.version, an upgrade handed to Raft, a downgrade refused, and a downgrade type at the current level a no-op; for every other feature, Feature.validateVersion's KIP-1022 dependencies. An admitted metadata.version row that crosses a changed level is never a downgrade.
Host caller
The UpdateFeatures request planner
Proof sessions
Caller preconditions
The host puts the rows into a map keyed by feature name, as Kafka's QuorumController does, so a repeated name keeps its last row, and it walks that map in java.util.HashMap iteration order; the first rejected row fails the whole request and nothing is written. It decodes the wire UpgradeType at every request version (an unknown code is None), reads the current level from the image, folds the local registry range, every registered broker range, and from 3.7-IV0 every other registered controller range and every unregistered voter into node support (a feature a node did not register reads as Kafka's DISABLED range 0..=0), computes checkIfMetadataChanged from the broker-local didMetadataChange table in features.rs, and evaluates dependencies against the finalized levels with every update of the request applied. It maps each rejection to Kafka's INVALID_UPDATE_VERSION message. Feature-registry definitions, the transcribed table, controller submission, durability, and races after the captured image remain host assumptions; the request protocol has no image epoch for stale-client fencing.
break_glass_admissionselect_break_glass_candidatebreak_glass_consumption_decisionbreak_glass_local_action_decision break_glass.rsbreak_glass_persistence.rs 4 proofs break_glass_admission applies the fail-closed withdrawn, consumed, expiry, approval, and signature checks in order over the named BreakGlassProposalFacts; select_break_glass_candidate returns the first index of the lexicographically least (expiry, UUID high, UUID low) key (is_min_by/lex_le), so an exact duplicate key keeps the earlier candidate. break_glass_consumption_decision admits only an exact positive-timestamp mutation of the current pending proposal when no metadata entry awaits commitment, and pins each refusal to its first failing check: missing proposal, nonpositive timestamp, stale proposal, then uncommitted tail. break_glass_local_action_decision permits a gated local action only after its matching consume committed, including an already-committed retry.
Proves
break_glass_admission applies the fail-closed withdrawn, consumed, expiry, approval, and signature checks in order over the named BreakGlassProposalFacts; select_break_glass_candidate returns the first index of the lexicographically least (expiry, UUID high, UUID low) key (is_min_by/lex_le), so an exact duplicate key keeps the earlier candidate. break_glass_consumption_decision admits only an exact positive-timestamp mutation of the current pending proposal when no metadata entry awaits commitment, and pins each refusal to its first failing check: missing proposal, nonpositive timestamp, stale proposal, then uncommitted tail. break_glass_local_action_decision permits a gated local action only after its matching consume committed, including an already-committed retry.
Host caller
The broker's break-glass gate and local-action persistence adapter, used by DeleteRecords and offset-aware unclean recovery; the KRaft submission fence
Proof sessions
Caller preconditions
The gate supplies exact action and target coverage, distinct approvals, the action's signature policy, whether every approval is signed, and canonical UUID halves. The controller compares every consumed record with the stored proposal except for its positive consumption timestamp. Its single-owner loop accepts a consume only when the high watermark covers the full metadata log; this also fences an uncommitted tail retained across leadership changes. After commitment, the stored consumed state rejects retries. Metadata-backed actions still rely on each handler placing the consume first and its exact action records after it in one submit_change. Local actions submit the consume alone and start only after success; a failed submit does not enter the request-local committed set. Metadata encoding, quorum durability, local trim or recovery I/O, and process-crash behavior below a successful commit remain host or I/O assumptions.
freeze_timestamp_in_windowfreeze_signature_decisionfreeze_scope_decisionfreeze_refusesfreeze_mutation_decisionfreeze_replacement_decision freeze/mutation_decision.rsfreeze/replacement_decision.rs 6 proofs freeze_timestamp_in_window checks the symmetric signature-skew interval without machine overflow. freeze_signature_decision returns the first failure from the ordered key, key-principal, connection-principal, timestamp, replacement, and signature rules. freeze_scope_decision makes literal scope beat every prefix and makes the longest matching prefix win. freeze_refuses exhaustively classifies the mutation inventory, and freeze_mutation_decision gives authorization denial precedence over freeze detail before applying that classification. freeze_replacement_decision equals its reference rule: a thaw with no committed entry is Missing and a record no newer than the committed one is Stale (permanent refusals that outrank the tail), an admissible record behind an uncommitted tail is InFlight (the only retryable refusal), and otherwise Append.
Proves
freeze_timestamp_in_window checks the symmetric signature-skew interval without machine overflow. freeze_signature_decision returns the first failure from the ordered key, key-principal, connection-principal, timestamp, replacement, and signature rules. freeze_scope_decision makes literal scope beat every prefix and makes the longest matching prefix win. freeze_refuses exhaustively classifies the mutation inventory, and freeze_mutation_decision gives authorization denial precedence over freeze detail before applying that classification. freeze_replacement_decision equals its reference rule: a thaw with no committed entry is Missing and a record no newer than the committed one is Stale (permanent refusals that outrank the tail), an admissible record behind an uncommitted tail is InFlight (the only retryable refusal), and otherwise Append.
Host caller
The topic-freeze signature verifier, shared scope and mutation resolver, Produce, AddPartitionsToTxn, DeleteRecords, DeleteTopics, reassignment alter and completion, compaction, tier retention, and the KRaft replacement fence
Proof sessions
Caller preconditions
The host maps key lookup and both principal comparisons to one identity state, verifies canonical Ed25519 bytes only after the first five rules pass, and supplies the configured skew in milliseconds. The resolver does not inspect the registry before authorization. It rejects internal topics, maps literal equality and prefix matching exactly, and folds every live registry entry through one precedence kernel. The closed mutation enum refuses Produce, transaction enlistment, deletion, reassignment alter, compaction, and retention. It permits transaction and reassignment completion, offset commits, replication, barrier markers, and tiering copy. A frozen reassignment alter returns before approval consumption or metadata collection; mixed-topic requests can still submit only unfrozen rows. Failed submissions change no freeze decision, and retries resolve against the next immutable image. The mutation kernel has no parsed or numeric input, so malformed, stale, and overflow handling stays in the scope, signature, replacement, and checked-epoch adapters. The controller compares replacement timestamps at the exact pattern-and-scope key and requires the high watermark to cover the full log. It also rejects a second freeze mutation in one batch. A thaw requires a live exact-key entry. String comparison, key loading, canonical-byte construction, cryptographic verification, metadata iteration and encoding, quorum durability, and a clock change after admission remain host or I/O assumptions.
schema_failure_decision schema.rs 1 proof schema_failure_decision permits unvalidated records only when fail-open is configured and the registry failure is transient.
Proves
schema_failure_decision permits unvalidated records only when fail-open is configured and the registry failure is transient.
Host caller
The broker schema validator's registry classifier and admission path
Proof sessions
Caller preconditions
The host classifies transport errors, HTTP 408/429, and 5xx as transient; unknown IDs, other statuses, and malformed successful responses fail closed.
schema_frame_idschema_field_actionschema_batch_admission schema.rs 3 proofs schema_frame_id returns an ID exactly for magic zero, and that ID is the four schema-ID bytes in big-endian order. schema_field_action checks only configured, non-null key or value fields under the matching subject role. schema_batch_admission admits exactly a complete walk in which every applicable field was admitted.
Proves
schema_frame_id returns an ID exactly for magic zero, and that ID is the four schema-ID bytes in big-endian order. schema_field_action checks only configured, non-null key or value fields under the matching subject role. schema_batch_admission admits exactly a complete walk in which every applicable field was admitted.
Host caller
The schema validator's framing and subject adapter and the Produce whole-batch walk
Proof sessions
Caller preconditions
The host supplies the proof kernel only after a slice match establishes the complete five-byte prefix. Empty fields and null fields remain documented exemptions. TopicNameStrategy creates the exact topic-key or topic-value subject from the proved role. The prepared owned or verbatim batch walk decodes every record, counts each selected field and each successful admission, and rejects decode failures before the final decision. Batch CRC, record iteration, schema parsing, registry I/O, cache synchronization, and log append remain host or I/O assumptions. The separate failure classifier is the only path that can count an unvalidated transient lookup as admitted. The field-walk composition derives the counters per position and exports exact ID/action/acceptance witnesses; the Produce composition checks canonical field pairs, derives the actual record count and proves the exact representable acknowledgement frontier. Physical decoding/enumeration and truthful validation observations remain host obligations.

Stateright models 31

What a model check claims from the broker catalog

Stateright enumerates reachable states within explicit bounds. It can find a counterexample in that state space. It does not prove behavior outside the bounds. Each model states its bounds and properties in its module comments.

Some models drive a production kernel directly. Other models compose real decision functions with a small environment. Read the model's DRIVEN and MODELED notes before you treat its property as a production guarantee.

Every unique-state count cited below is asserted by the model's own runner against a PINNED_UNIQUE_STATES constant -- one per config where a runner drives several -- and not merely recorded here. An exhaustive breadth-first search over a fixed model visits a deterministic set of states, so the exact count is a checkable fact rather than a bound. The generated-transition count is not pinned anywhere: it depends on when the search's worker threads deduplicate, and so varies between runs of the same model.

To change a pin, run the model, read the count the runner prints, and write that number into the constant. A changed count is a changed model, never a retuning knob, so a pull request that moves one must say which transition, action, or state field was added or removed to move it. A count that shrinks with no such explanation is the failure this pin exists to catch: an action generator that dropped a branch, a next_state arm that began returning None, or a derived Hash that stopped considering a field, each of which leaves a smaller search still passing every upper bound.

Showing 31 of 31

Broker data and Fetch 5

Data path model krabka-broker data_path_model.rs 3.13M states 6 properties Composes the production high-watermark core with the ISR expansion kernel, the leader-epoch truncation lookup, the controller failover decision, the KIP-966 recovery election and ELR maintenance rule, fetch visibility, and the diskless WAL-durable HWM and offset reservation. It checks that committed records stay durable, that a clean election loses nothing, that an ELR election keeps every record that reached the HWM, and that only an election reported as losing data sets the lost flag.
Summary
Composes the production high-watermark core with the ISR expansion kernel, the leader-epoch truncation lookup, the controller failover decision, the KIP-966 recovery election and ELR maintenance rule, fetch visibility, and the diskless WAL-durable HWM and offset reservation. It checks that committed records stay durable, that a clean election loses nothing, that an ELR election keeps every record that reached the HWM, and that only an election reported as losing data sets the lost flag.
Drives
ReplicaStateisr_candidate_selectedepoch_and_offset_for_entriesfailover_oneselect_leadernext_partition_elrcompute_visibility_windowreserve_offsets
Bounds
Three brokers, one partition, and at most three leader epochs, with log length 4 (data_clean, data_unclean) or 3 (data_elr).
Properties
committed_durableno_loss_when_cleanunclean_losselr_holds_every_committed_recordelr_election_keeps_every_committed_recordhwm_held_by_min_isr
Pinned unique states
ConfigUnique states
data_clean762,237
data_unclean1,471,603
data_elr898,023
data_diskless450
Total3,132,313
Outside the model
Record payloads, transactions, remote storage, multiple partitions, and the last-known-ELR half of KIP-966 remain outside the model.
What the catalog says

The broker catalog's own description of this model, reproduced from its verification document.

The data-path model (data_path_model) composes the production HWM core (ReplicaState), whose high watermark stands still while the ISR is under min.insync.replicas as Kafka's Partition.maybeIncrementLeaderHW does, the leader's ISR expansion kernel (isr_candidate_selected), the leader-epoch truncation lookup (epoch_and_offset_for_entries), the controller failover decision (failover_one), the KIP-966 recovery election (select_leader), the KIP-966 ELR maintenance rule (next_partition_elr), fetch visibility (compute_visibility_window), and, on the diskless path, the WAL-durable HWM and the controller's offset reservation (reserve_offsets over the image's PartitionOffsetAdvance counter). It checks three brokers, one partition, and at most three leader epochs. data_clean (strategy None, unclean disabled, log length 4) reaches exactly 762,237 unique states and requires committed_durable and no_loss_when_clean. data_unclean (Balanced recovery, unclean enabled, log length 4) reaches exactly 1,471,603 and requires committed_durable while witnessing unclean_loss. data_elr (min.insync.replicas 2, log length 3) reaches exactly 898,023; the real ELR set is handed to failover_one, so a partition whose live ISR empties elects a surviving ELR member cleanly, and each election is applied by the rule that chose it: an ISR election truncates nothing and is checked by committed_durable, an ELR member must hold, and an ELR election must keep, every record that reached the HWM (elr_holds_every_committed_record, elr_election_keeps_every_committed_record), and only an election reported as losing data sets lost. Anti-vacuity witnesses require a high watermark held back by min ISR while every ISR member holds a later record (hwm_held_by_min_isr), an ELR to be published, an ELR election to happen, and one to pass over a strictly longer surviving log. data_diskless reaches exactly 450 and requires that a WAL-acknowledged record is never lost, that the HWM is released by WAL sync alone, and that reserved offset ranges are gap-free and unique. All four are pinned by PINNED_UNIQUE_STATES_* in data_path_model/runner.rs, below the depth-70, 60,000,000-generated, and 8,000,000-unique guards. Record payloads, transactions, remote storage, multiple partitions, and the last-known-ELR half of KIP-966 remain outside the model.

Producer state model krabka-broker producer_state_model.rs 4,964 states 1 property Drives the host's retained-batch bookkeeping around the proved producer_decision kernel and compares every answer with an independent classification by Kafka's rules over a ghost window of the five batches Kafka retains. Any disagreement becomes a counterexample, and witnesses require duplicates of the last, an older, and the oldest retained batch, an out-of-order refusal, and an epoch bump.
Summary
Drives the host's retained-batch bookkeeping around the proved producer_decision kernel and compares every answer with an independent classification by Kafka's rules over a ghost window of the five batches Kafka retains. Any disagreement becomes a counterexample, and witnesses require duplicates of the last, an older, and the oldest retained batch, an out-of-order refusal, and an epoch bump.
Drives
ProducerEntry::retained_batchesearlier_after_appendcheck_retainedproducer_decision
Bounds
One producer ID over one- and two-record batches; the basic config covers epochs 0 and 1 with sequences through 6, the wide config epochs through 3 with sequences through 9.
Properties
decision_matches_kafka
Pinned unique states
ConfigUnique states
basic731
wide4,233
Total4,964
Outside the model
The model enumerates submit sequences rather than concurrent requests, and offsets, snapshots, and transactions remain outside it.
What the catalog says

The broker catalog's own description of this model, reproduced from its verification document.

The producer-state model (producer_state_model) drives the host's retained-batch bookkeeping (ProducerEntry::retained_batches and earlier_after_append) and check_retained around the proved producer_decision, for one producer ID over one- and two-record batches. It compares every answer with an independent classification by Kafka's rules over a ghost window of the five batches Kafka retains (NUM_BATCHES_TO_RETAIN), and the decision_matches_kafka property turns any disagreement into a counterexample. Witnesses require a duplicate of the last, of an older, and of the oldest of five retained batches, an out-of-order refusal of a retry below a full window, and an epoch bump. The basic config (epochs 0 and 1, sequences through 6) and the wide config (epochs through 3, sequences through 9) reach exactly 731 and 4,233 unique states, pinned by PINNED_UNIQUE_STATES_BASIC and PINNED_UNIQUE_STATES_WIDE, below the depth-40 and 2,000,000-state truncation guards. The broker serializes one producer's requests, so the model enumerates submit sequences rather than concurrent requests; offsets, snapshots, and transactions remain outside the model.

Replica state model krabka-broker replica_state_model.rs 348 states 1 property Holds the production ReplicaState and explores every interleaving of leader append, follower fetch, and ISR installation. It requires the high watermark to stay nonnegative, inside the leader's log, and nondecreasing, every ISR member to hold every committed record, follower log ends clamped to the leader's, and the leader in the ISR.
Summary
Holds the production ReplicaState and explores every interleaving of leader append, follower fetch, and ISR installation. It requires the high watermark to stay nonnegative, inside the leader's log, and nondecreasing, every ISR member to hold every committed record, follower log ends clamped to the leader's, and the leader in the ISR.
Drives
ReplicaState::install_isrReplicaState::update_follower_leoReplicaState::recompute_hw_for_leader_append
Bounds
One partition with three replicas, a fixed leader, and log ends through 3.
Properties
no_data_loss
Pinned unique states
ConfigUnique states
isr_safety174
isr_overshoot174
Total348
Outside the model
Wall-clock lag, the controller's AlterPartition path, and I/O remain outside the model.
What the catalog says

The broker catalog's own description of this model, reproduced from its verification document.

The replica-state model (replica_state_model) holds the production ReplicaState and drives its install_isr, update_follower_leo, and recompute_hw_for_leader_append for one partition with three replicas, a fixed leader, and log ends through 3. It explores every interleaving of leader append, follower fetch, and ISR installation. A follower's reported log end never regresses, and an ISR expansion admits only a follower whose log end reached the high watermark, the high-watermark half of Kafka's Partition.isFollowerInSync. ISR changes are explicit actions rather than timed decisions, so the lag rule is not checked here. Properties require the high watermark to stay nonnegative and inside the leader's log, every ISR member to hold every committed record (no_data_loss), follower log ends clamped to the leader's, and the leader in the ISR; a transition check requires a nondecreasing high watermark. Witnesses cover an advanced high watermark, one that reaches the leader's log end, one held below it by a lagging member, and an ISR shrink. The isr_safety config and the isr_overshoot config, whose followers may report a log end past the leader's to exercise the clamp, each reach exactly 174 unique states, pinned by PINNED_UNIQUE_STATES_SAFETY and PINNED_UNIQUE_STATES_OVERSHOOT, below the depth-80 and 200,000-state truncation guards. Wall-clock lag, the controller's AlterPartition path, and I/O remain outside the model.

Fetch session model krabka-broker fetch_session_model.rs 166 states 2 properties Drives the production KIP-227 forget-then-merge composition over sequences of incremental fetches whose topic references carry a name only, an ID only, or both, so the search covers a rename and a conflicting ID on one partition. It requires that no two cached keys name one logical partition and that every cached entry carries a requested max_bytes.
Summary
Drives the production KIP-227 forget-then-merge composition over sequences of incremental fetches whose topic references carry a name only, an ID only, or both, so the search covers a rename and a conflicting ID on one partition. It requires that no two cached keys name one logical partition and that every cached entry carries a requested max_bytes.
Drives
apply_incremental
Bounds
Topic references over two names and two IDs; the basic config uses four references and the wide config seven.
Properties
no_shadowno_orphan_default
Pinned unique states
ConfigUnique states
basic18
wide148
Total166
Outside the model
Session epochs, eviction, and the wire codec remain outside the model.
What the catalog says

The broker catalog's own description of this model, reproduced from its verification document.

The fetch-session model (fetch_session_model) drives the production KIP-227 forget-then-merge composition apply_incremental over sequences of incremental fetches, starting from a fully resolved session. Topic references carry a name only (Fetch v12 and below), an ID only (v13 and above), or both, over two names and two IDs, so the search covers a rename (same ID, new name) and a conflicting ID on one partition. The no_shadow property requires that no two cached keys name one logical partition, that is, the same partition and a shared identity half; no_orphan_default requires every cached entry to carry a requested max_bytes, and a transition check requires the cache to reflect each subscribed partition with the requested max_bytes. Witnesses cover a rename, an emptied session, ID-only and name-only keys, and two keys on one partition. The basic config (four references) and the wide config (seven references) reach exactly 18 and 148 unique states, pinned by PINNED_UNIQUE_STATES_BASIC and PINNED_UNIQUE_STATES_WIDE, below the depth-30 and 500,000-unique-state guards. The production merge resolves a request that matches two cached keys in HashMap iteration order; the checked properties hold for every resolution. Session epochs, eviction, and the wire codec remain outside the model.

Fetch visibility model krabka-broker fetch_visibility_model.rs 1,436 states Advances the partition watermarks one at a time and probes every consumer and follower fetch shape at every offset up to one past the log end. Each probe checks the clamp contract (a consumer never reads at or past the high watermark or the delivery watermark, a read-committed consumer stops at min(lso, hw), a follower reads to the log end), that every fetcher is told the partition's high watermark and min(lso, hw), and that neither reported watermark regresses.
Summary
Advances the partition watermarks one at a time and probes every consumer and follower fetch shape at every offset up to one past the log end. Each probe checks the clamp contract (a consumer never reads at or past the high watermark or the delivery watermark, a read-committed consumer stops at min(lso, hw), a follower reads to the log end), that every fetcher is told the partition's high watermark and min(lso, hw), and that neither reported watermark regresses.
Drives
compute_visibility_window
Bounds
Partition watermarks kept in Kafka's order with log ends through 4 (basic) and 7 (wide).
Pinned unique states
ConfigUnique states
basic182
wide1,254
Total1,436
Outside the model
Record reads, remote tiers, and the fetch-session cache remain outside the model.
What the catalog says

The broker catalog's own description of this model, reproduced from its verification document.

The Fetch visibility model (fetch_visibility_model) drives the production compute_visibility_window over advancing partition watermarks, kept in Kafka's order 0 <= log_start <= lso <= hw <= log_end with KFC-1's log_start <= deliverable <= hw. Each advance action raises one watermark by one, and retention carries the delivery watermark along with the log start. A Fetch probe runs every consumer and follower fetch shape at every offset up to one past the log end. Each probe checks the clamp contract: a consumer never reads at or past the high watermark or the delivery watermark, a read-committed consumer stops at min(lso, hw), and a follower reads to the log end. Each probe also checks that every fetcher is told the partition's high watermark and min(lso, hw), as Kafka's Partition.readRecords reports them, and each advance checks that neither reported watermark regresses (KIP-227). Witnesses cover an LSO clamp, a delivery-watermark clamp, a delivery watermark above the LSO, a follower read past the high watermark, and an out-of-range fetch after retention. Log ends through 4 and 7 reach exactly 182 and 1,254 unique states, pinned by PINNED_UNIQUE_STATES_BASIC and PINNED_UNIQUE_STATES_WIDE, below the depth-40 and 200,000-state truncation guards. Record reads, remote tiers, and the fetch-session cache remain outside the model.

Broker failover and placement 5

Leader failover model krabka-broker leader_failover_model.rs 11,892 states 1 property Drives the production controller failover decision and checks each one against Kafka's one-broker rule for a fenced broker and against the rung ladder of PartitionChangeBuilder.electAnyLeader: clean election in assignment order from the target ISR or ELR, offset-aware recovery only with no live ISR and no electable ELR member, the KIP-841 unclean election only when enabled, and Unavailable only when no rung was open. It requires the ISR within the replicas, a non-witness leader inside its ISR, and ELR disjoint from ISR.
Summary
Drives the production controller failover decision and checks each one against Kafka's one-broker rule for a fenced broker and against the rung ladder of PartitionChangeBuilder.electAnyLeader: clean election in assignment order from the target ISR or ELR, offset-aware recovery only with no live ISR and no electable ELR member, the KIP-841 unclean election only when enabled, and Unavailable only when no rung was open. It requires the ISR within the replicas, a non-witness leader inside its ISR, and ELR disjoint from ISR.
Drives
failover_oneisr_maintenance_selectedisr_admissionnext_partition_elrselect_leader
Bounds
One three-replica partition with assignment [1, 3, 2], brokers dying, reviving, and rejoining the ISR, at min.insync.replicas 1 and 2.
Properties
leader_in_isr
Pinned unique states
ConfigUnique states
failover_safe525
failover_unclean525
failover_recover525
three witness variants (each)210
failover_elr_unclean1,470
failover_elr_recover1,246
failover_witness_elr_unclean532
offset_recovery (each of 16 published-ELR and witness combinations)6,859
Total11,892
Outside the model
Logs, and so the completeness of an elected replica, are outside this model; data_path_model checks that.
What the catalog says

The broker catalog's own description of this model, reproduced from its verification document.

The failover model (leader_failover_model) drives the production failover_one over one three-replica partition with brokers dying, reviving, and rejoining the ISR (the leader-side isr_maintenance_selected and controller-side isr_admission kernels), and recomputes the published eligible-leader set after every change with the production next_partition_elr. The assignment is [1, 3, 2], so ISR order and assignment order differ. Each decision is checked in the transition against Kafka's one-broker rule for a fenced broker (handleBrokerFenced): a shrink removes exactly the dead broker and keeps the leader, and a clean election emits exactly that target ISR. It is also checked against the rung ladder of Kafka's PartitionChangeBuilder.electAnyLeader: a clean election takes the first valid replica in assignment order from the target ISR, or with no live ISR member from the first acceptable ELR member in assignment order as a singleton ISR; offset-aware recovery only with no live ISR and no electable ELR member; the KIP-841 election only when additionally the strategy is None and the toggle is on, electing the first acceptable replica; Unavailable only when no rung was open; and no witness ever leads. Properties require the ISR within the replicas, the leader inside its ISR (leader_in_isr), a non-witness leader, and ELR disjoint from ISR. At min.insync.replicas 1, failover_safe, failover_unclean, and failover_recover each reach exactly 525 unique states and the three witness variants 210. At min.insync.replicas 2, failover_elr_unclean reaches 1,470, failover_elr_recover 1,246, and failover_witness_elr_unclean 532, each witnessing a published ELR and an election taken from it. The winner-selection half (offset_recovery) runs select_leader over every response fan-out for each of the 16 published-ELR and witness combinations, 6,859 unique states each. All counts are pinned in leader_failover_model/runner.rs, below the depth-80 and 200,000-state guards. Logs, and so the completeness of an elected replica, are outside this model; data_path_model checks that.

Reassignment model krabka-broker reassignment_model.rs 373 states Drives the production KIP-455 completion step through every interleaving of an adding replica joining the ISR, broker death and revival, and completion ticks. It requires every decision to wait for all adding replicas, a handoff to move leadership to an alive in-ISR target that is not being removed while bumping the epoch by exactly one, and a completion to install exactly the target replica set with the leader inside it.
Summary
Drives the production KIP-455 completion step through every interleaving of an adding replica joining the ISR, broker death and revival, and completion ticks. It requires every decision to wait for all adding replicas, a handoff to move leadership to an alive in-ISR target that is not being removed while bumping the epoch by exactly one, and a completion to install exactly the target replica set with the leader inside it.
Drives
reassign_onereassignment_action
Bounds
One partition with leader epochs through 10; reassign_basic has replicas 1 to 3 adding 3 and removing 2, reassign_leader_handoff removes the leader, and reassign_wide has five replicas adding two and removing two with the leader removed.
Pinned unique states
ConfigUnique states
reassign_basic21
reassign_leader_handoff42
reassign_wide310
Total373
Outside the model
Catch-up is an explicit action rather than a replicated log; the AlterPartitionReassignments planner and controller submission remain outside the model.
What the catalog says

The broker catalog's own description of this model, reproduced from its verification document.

The reassignment model (reassignment_model) drives the production KIP-455 completion step reassign_one, and through it the proved reassignment_action, for one partition with leader epochs through 10. It explores every interleaving of an adding replica joining the ISR, broker death and revival, and completion ticks. A transition check requires every decision to come only after every adding replica is in the ISR; a handoff to move leadership to an alive, in-ISR target replica that is not being removed, keep the replica sets, and bump the leader epoch by exactly one; and a completion to install exactly the target replica set, which still contains the leader. Properties require the ISR, the leader, and the adding and removing sets inside the replica set, and the leader inside the ISR; witnesses cover a completion, a handoff, and a wait. reassign_basic (replicas 1 to 3, add 3, remove 2, leader 1), reassign_leader_handoff (the same change with the removed replica 2 as leader), and reassign_wide (five replicas, add two, remove two, leader removed) reach exactly 21, 42, and 310 unique states, pinned by PINNED_UNIQUE_STATES_BASIC, PINNED_UNIQUE_STATES_LEADER_HANDOFF, and PINNED_UNIQUE_STATES_WIDE, below the depth-80 and 200,000-state guards. Catch-up is an explicit action rather than a replicated log; the AlterPartitionReassignments planner and controller submission remain outside the model.

Stretch cluster model krabka-broker stretch_cluster_model.rs 9,980 states 2 properties Checks KFC-2's witness claim: a witness never leads, an acks=all produce commits whenever at most one site is impaired and the controller has converged, every leader change carries a strictly greater epoch, a site set without a voter majority never commits, and a preferred election keeps the leader in the preferred site when it can. Two RED configs must fail: the pre-witness controller breaks leader_never_witness and min.insync.replicas 1 breaks minority_never_commits.
Summary
Checks KFC-2's witness claim: a witness never leads, an acks=all produce commits whenever at most one site is impaired and the controller has converged, every leader change carries a strictly greater epoch, a site set without a voter majority never commits, and a preferred election keeps the leader in the preferred site when it can. Two RED configs must fail: the pre-witness controller breaks leader_never_witness and min.insync.replicas 1 breaks minority_never_commits.
Drives
stretch_replicasfailover_oneselect_new_leader_for_partitionkrabka_verified::stretch
Bounds
Three sites with one broker each, replication.factor 3, min.insync.replicas 2, a witness replica in the third site, leader epochs through 6, and at most two sites down or isolated at once.
Properties
leader_never_witnessminority_never_commits
Pinned unique states
ConfigUnique states
three sites (PINNED_UNIQUE_STATES_THREE_SITES)1,596
legacy_elect (RED, must fail)5,724
min.insync.replicas 1 (RED, must fail)2,660
Total9,980
Outside the model
The controller may decide in every state, including states without a metadata quorum, which over-approximates the reachable set; KRaft itself, logs, and I/O remain outside the model.
What the catalog says

The broker catalog's own description of this model, reproduced from its verification document.

The stretch-cluster model (stretch_cluster_model) checks KFC-2's witness claim over three sites with one broker each, replication.factor 3, min.insync.replicas 2, a witness replica in the third site, leader epochs through 6, and at most two sites down or isolated at once. It builds the replica list with the production stretch_replicas, takes every failover decision with failover_one and every KIP-460 preferred election with select_new_leader_for_partition, and takes its durability arithmetic from the proved krabka_verified::stretch kernels; ISR rejoin after a site heals and the two acks=all gates of the Produce handler are modeled. Properties require that a witness never leads, that an acks=all produce commits whenever at most one site is impaired and the controller has converged, that every leader change carries a strictly greater epoch, that a site set without a voter majority never commits, and that a preferred election keeps the leader in the preferred site whenever that site holds an alive, in-sync, non-witness replica. Five witnesses include a failover that keeps the witness in the ISR. The run reaches exactly 1,596 unique states, pinned by PINNED_UNIQUE_STATES_THREE_SITES, below the depth-60 and 200,000-state guards. Two #[should_panic] RED configs must fail: the pre-witness controller (legacy_elect, 5,724 states) breaks leader_never_witness, and min.insync.replicas 1 (2,660 states) breaks minority_never_commits. The controller may decide in every state, including states without a metadata quorum, which over-approximates the reachable set; KRaft itself, logs, and I/O remain outside the model.

Diskless crash model krabka-broker diskless_crash_model.rs 6,341 states Composes the diskless WAL design's crash windows with the quorum rule and stateless appenders, with three production kernels deciding the steps. It requires the quorum-acknowledged and producer-acknowledged frontiers to survive a minority WAL loss, a sequencer handoff never to lower the quorum-durable frontier, every trim to stay at or below both the committed index frontier and the quorum-durable frontier, and reservations to be gap-free and unique.
Summary
Composes the diskless WAL design's crash windows with the quorum rule and stateless appenders, with three production kernels deciding the steps. It requires the quorum-acknowledged and producer-acknowledged frontiers to survive a minority WAL loss, a sequencer handoff never to lower the quorum-durable frontier, every trim to stay at or below both the committed index frontier and the quorum-durable frontier, and reservations to be gap-free and unique.
Drives
consensus::majority_watermarkoffset_allocator::wal_reservation_frontieroffset_allocator::reserve_offsetsdiskless::diskless_trim_decision
Bounds
Offsets through two, two appenders, three WAL members with at most one lost, sequencer epochs through two, and every reachable ordering of reservation, fsync, crash, object PUT, index commit, trim, member loss, and sequencer handoff, at depth 15.
Pinned unique states
ConfigUnique states
PINNED_UNIQUE_STATES6,341
Outside the model
WAL members, fsync, PUT, and index commit are counters rather than logs or objects, every reservation stays pending, the trim runs with no safety lag, and the idempotent-producer dedup rebuild is not modeled.
What the catalog says

The broker catalog's own description of this model, reproduced from its verification document.

The diskless crash model (diskless_crash_model) composes the diskless WAL design's crash-window recovery rules with the distributed WAL quorum rule and stateless appenders. Three production kernels decide the steps that carry its properties: consensus::majority_watermark computes the quorum-durable frontier from the live WAL members; offset_allocator::wal_reservation_frontier folds the pending reservation chain and offset_allocator::reserve_offsets places the next reservation, as the controller's V1PartitionOffsetAdvance submit path does, and either kernel refusing fails the run; and diskless::diskless_trim_decision places every trim, as the flusher does. It checks offsets through two, two appenders, three WAL members with at most one lost, sequencer epochs through two, and every reachable ordering of reservation, fsync, crash-before-fsync, crash-mid-fsync, object PUT, index commit, trim, member loss, and sequencer handoff. The run reaches exactly 6,341 unique states at depth 15, pinned by PINNED_UNIQUE_STATES in the model file, below its depth-24 and 100,000-state truncation guards. The safety properties require the quorum-acknowledged and producer-acknowledged frontiers to survive a minority WAL loss, a sequencer handoff never to lower the quorum-durable frontier, every trim to stay at or below both the committed index frontier and the quorum-durable frontier, and the reservations to be gap-free and unique. Reachability witnesses cover a crash between reservation and fsync, a PUT before its index commit, a torn fsync, a trim at the index frontier, a minority WAL loss with acknowledged unflushed records, two appenders interleaving, and a handoff that lowers only the advertised watermark. WAL members, fsync, PUT, and index commit are counters, not logs or objects; every reservation stays pending (the image frontier is 0); the trim runs with no safety lag against the durable frontier as the high watermark. The idempotent-producer dedup rebuild is not modeled; diskless::recovery's unit tests rebuild the dedup state from a real recovered log instead.

Client-server failover model krabka-broker client_server_failover_model.rs 38,131 states Composes a producer client's failover routing and retry bookkeeping with the production idempotent-producer check over every interleaving of send, retry, replication, high-watermark advance, acknowledgement, leader death, clean election, and metadata refresh. Eleven always properties require that an acknowledged batch is committed and stays durable, that no log holds the batch twice, that a reroute never skips a sequence, that every leader holds the committed prefix, and that the attempt and refresh caps hold.
Summary
Composes a producer client's failover routing and retry bookkeeping with the production idempotent-producer check over every interleaving of send, retry, replication, high-watermark advance, acknowledgement, leader death, clean election, and metadata refresh. Eleven always properties require that an acknowledged batch is committed and stays durable, that no log holds the batch twice, that a reroute never skips a sequence, that every leader holds the committed prefix, and that the attempt and refresh caps hold.
Drives
check_pure
Bounds
Three brokers, one producer identity with one batch, logs of at most one batch, a high watermark through 1, at most four send attempts and four metadata refreshes.
Pinned unique states
ConfigUnique states
PINNED_UNIQUE_STATES38,131
Outside the model
The wire client, transactions, and unclean election remain outside the model.
What the catalog says

The broker catalog's own description of this model, reproduced from its verification document.

The client-server failover model (client_server_failover_model) composes a producer client's failover routing and retry bookkeeping with the production idempotent-producer check check_pure. It checks three brokers, one producer identity with one batch, logs of at most one batch, a high watermark through 1, at most four send attempts and four metadata refreshes, and every interleaving of send, retry, replication, high-watermark advance, acknowledgement, leader death, clean election, and metadata refresh. Election is modeled: only a live follower that holds the committed prefix can become leader. Eleven always properties require that an acknowledged batch is committed and stays durable, that no log holds the batch twice, that a reroute never skips a sequence, that a NOT_LEADER_OR_FOLLOWER before append keeps the sequence, that an appended but unacknowledged batch is never reported acknowledged, that a timeout with an unknown result records the acceptance, that every leader holds the committed prefix, and that the attempt and refresh caps hold. Ten witnesses cover each response kind, a duplicate answered after an unknown result, retries across a failover, and an acknowledgement that survives a clean failover. The run reaches exactly 38,131 unique states, pinned by PINNED_UNIQUE_STATES in its runner, below the depth-36 and 120,000-state guards. The wire client, transactions, and unclean election remain outside the model.

Broker quorum WAL 1

Quorum WAL model krabka-broker model.rs 2,996 states Drives the in-process three-replica harness over real Log instances: record-batch append, replica sync with byte verification of each replica's whole retained log, acknowledgement and watermark advancement, and byte-agreement recovery, truncation, and repair. It keeps the watermark inside the leader log, preserves every committed prefix, requires a byte-identical quorum at the committed frontier, and checks every acknowledgement against its rule.
Summary
Drives the in-process three-replica harness over real Log instances: record-batch append, replica sync with byte verification of each replica's whole retained log, acknowledgement and watermark advancement, and byte-agreement recovery, truncation, and repair. It keeps the watermark inside the leader log, preserves every committed prefix, requires a byte-identical quorum at the committed frontier, and checks every acknowledgement against its rule.
Drives
WalShardEngine::newreplicate_and_syncOpenMode::RecoverLog
Bounds
Three voters, at most two one-record batches, leader epochs 0 through 2, and every ordering of up to eight enabled operations, at depth 9.
Pinned unique states
ConfigUnique states
PINNED_UNIQUE_STATES2,996
Outside the model
The production diskless path where remote voters fetch the leader's tail (record_follower_ack, serve_fetch) is covered by unit tests instead, and allocation, fsync, and crash atomicity below successful filesystem calls remain I/O assumptions.
What the catalog says

The broker catalog's own description of this model, reproduced from its verification document.

The quorum WAL model (model.rs) drives the in-process three-replica harness -- the #[cfg(test)] WalShardEngine::new, the local-replica branch of replicate_and_sync, and OpenMode::Recover -- over real Log instances: record-batch append, replica sync with byte verification of each replica's whole retained log, acknowledgement and watermark advancement, and byte-agreement recovery, truncation, and repair. It does not drive the production diskless path, where remote voters fetch the leader's tail and report fsynced offsets (record_follower_ack, serve_fetch); unit tests cover that path. It checks three voters, at most two one-record batches, leader epochs 0 through 2, and every ordering of up to eight enabled operations. The run reaches exactly 2,996 unique states at depth 9, pinned by PINNED_UNIQUE_STATES in the model file, below its depth-10 and 20,000-state truncation guards. The safety properties keep the watermark inside the leader log, preserve every previously committed prefix across transitions, and require a byte-identical quorum at the committed frontier. Every acknowledgement is checked against its rule: it succeeds exactly when at least two live voters hold no record that disagrees with the leader's log. Every recovery must succeed; an OpenMode::Recover error fails the run instead of pruning the transition. Reachability witnesses cover successful acknowledgements, a failed acknowledgement with a live minority, a failed acknowledgement with a live but divergent majority, leader changes, divergent replicas, and a completed recovery. KRaft epoch monotonicity and leader completeness are modeled preconditions: Elect(v) is enabled only when the current leader is down and v is live and holds the whole acknowledged prefix, which KIP-595's vote rule and quorum intersection guarantee in production. Appends come only from the current leader; a deposed leader's append is fenced by the registry's epoch check outside the engine and is not enumerated. Successful filesystem calls are atomic operation boundaries; allocation, fsync, and crash atomicity below those calls remain I/O assumptions.

Broker groups and transactions 8

Classic group model krabka-broker classic_state_model.rs 486,727 states Wraps the production ClassicGroup and explores every interleaving of dynamic, static, and fenced joins, heartbeats, leaves, rebalance completion, sync, and session-timeout expiry over a logical clock. It requires a coherent static-member index, one live owner per group.instance.id, only current members in the round, an empty member set exactly in the Empty state, and expiry exactly when a member is past its session timeout.
Summary
Wraps the production ClassicGroup and explores every interleaving of dynamic, static, and fenced joins, heartbeats, leaves, rebalance completion, sync, and session-timeout expiry over a logical clock. It requires a coherent static-member index, one live owner per group.instance.id, only current members in the round, an empty member set exactly in the Empty state, and expiry exactly when a member is past its session timeout.
Drives
ClassicGroup::add_memberClassicGroup::remove_memberClassicGroup::complete_rebalanceClassicGroup::install_assignmentsClassicGroup::expire_dead_memberscurrent_member_id_for_instance
Bounds
The basic config has two members, one instance ID, and a clock through 4; the wide config three members, two instance IDs, and a clock through 5.
Pinned unique states
ConfigUnique states
basic3,853
wide482,874
Total486,727
Outside the model
Wire decoding, the offsets log, and the actor mailbox remain outside the model.
What the catalog says

The broker catalog's own description of this model, reproduced from its verification document.

The classic group model (classic_state_model) wraps the production ClassicGroup and drives its add_member, remove_member, complete_rebalance, install_assignments, and expire_dead_members, plus the JoinGroup handler's KIP-345 fence pre-check current_member_id_for_instance. It explores every interleaving of dynamic, static, and fenced joins, heartbeats, leaves, rebalance completion, sync, and session-timeout expiry over a logical clock, and checks that a member, static or dynamic, expires exactly when it is past its session timeout. Properties require a coherent static-member index, one live owner for each group.instance.id, only current members in the members joined this round, and an empty member set exactly in the Empty state; witnesses cover a stable group, a pinned static instance, two members, and a generation bump. The basic config (two members, one instance ID, clock through 4) and the wide config (three members, two instance IDs, clock through 5) reach exactly 3,853 and 482,874 unique states, pinned by PINNED_UNIQUE_STATES_BASIC and PINNED_UNIQUE_STATES_WIDE, below the depth-80, 8,000,000-generated, and 600,000-unique guards. A proptest companion drives the same operations over random schedules. Wire decoding, the offsets log, and the actor mailbox remain outside the model.

Consumer group composition model krabka-broker consumer_group_composition_model.rs 35,975 states Reuses the reconciliation model's driving of step_heartbeat and composes it with the production OffsetCommit epoch fence, cross-checking every commit against an independent oracle of the expected epoch comparison and error code. It requires that no two members own one partition and that no member is advertised a partition another member still owns.
Summary
Reuses the reconciliation model's driving of step_heartbeat and composes it with the production OffsetCommit epoch fence, cross-checking every commit against an independent oracle of the expected epoch comparison and error code. It requires that no two members own one partition and that no member is advertised a partition another member still owns.
Drives
step_heartbeatGroupState::validate_commit_decision
Bounds
The basic config has two members, two partitions, and epochs through 6; the wide config three members, two partitions, and epochs through 5.
Pinned unique states
ConfigUnique states
basic6,328
wide29,647
Total35,975
Outside the model
Plain consumer groups deliver at least once, so no-duplicate and no-gap delivery are deliberately not checked; __consumer_offsets persistence and the classic protocol are out of scope.
What the catalog says

The broker catalog's own description of this model, reproduced from its verification document.

The consumer-group composition model (consumer_group_composition_model) reuses the reconciliation model's driving of step_heartbeat and composes it with the production OffsetCommit fence GroupState::validate_offset_commit, including the KIP-1251 per-partition assignment-epoch check. Every commit cross-checks the fence against an independent oracle of the expected epoch comparison, partition assignment epoch and error code, so a divergence fails the run. Properties require that no two members own one partition and that no member is advertised a partition another member still owns; witnesses cover an accepted commit, a member-epoch bump that fences a commit from before a rebalance, a partition held from an epoch older than the member epoch, and a handoff. The basic config (two members, two partitions, epochs through 7) and the wide config (three members, two partitions, epochs through 6) reach exactly 6,328 and 29,647 unique states, pinned by PINNED_UNIQUE_STATES_BASIC and PINNED_UNIQUE_STATES_WIDE, below the depth-80 and 2,000,000-state guards. Plain consumer groups deliver at least once, so no-duplicate and no-gap delivery are deliberately not checked; __consumer_offsets persistence and the classic protocol are out of scope.

KIP-848 reconciliation model krabka-broker reconciler_model.rs 4,365 states Drives the production heartbeat step and the default uniform assignor over join, leave, and heartbeat actions with a faithful client that revokes before it adds and makes no cross-member check, so exclusivity must come from the coordinator's withholding alone. It requires that no two members own one partition at the same time and that no member is advertised a partition another member still owns.
Summary
Drives the production heartbeat step and the default uniform assignor over join, leave, and heartbeat actions with a faithful client that revokes before it adds and makes no cross-member check, so exclusivity must come from the coordinator's withholding alone. It requires that no two members own one partition at the same time and that no member is advertised a partition another member still owns.
Drives
step_heartbeatGroupState::reconcile_memberuniform assignor
Bounds
The basic config has two members, two partitions, and epochs through 8; the wide config three members, two partitions, and epochs through 6.
Pinned unique states
ConfigUnique states
basic552
wide3,813
Total4,365
Outside the model
Offset commits, the offsets log, and the wire protocol remain outside the model.
What the catalog says

The broker catalog's own description of this model, reproduced from its verification document.

The KIP-848 reconciliation model (reconciler_model) drives the production step_heartbeat, and through it GroupState::reconcile_member and the default uniform assignor, over join, leave, and heartbeat actions and a faithful client. The client adds and revokes partitions strictly by the assignment the coordinator returned in that member's last heartbeat, revokes before it adds, and makes no cross-member check, as a real consumer does, so exclusivity must come from the coordinator's withholding alone. Properties require that no two members own one partition at the same time and that no member is advertised a partition another member still owns; witnesses cover a handoff and convergence. The basic config (two members, two partitions, epochs through 9) and the wide config (three members, two partitions, epochs through 7) reach exactly 552 and 3,813 unique states, pinned by PINNED_UNIQUE_STATES_BASIC and PINNED_UNIQUE_STATES_WIDE, below the depth-60 and 200,000-state guards. Offset commits, the offsets log, and the wire protocol remain outside the model.

Coordinator replay model krabka-broker coordinator_replay_model.rs 6,376 states Drives the production replay decisions shared by the consumer, share, and streams adapters. It requires key/value kind agreement, parent-before-child application, nonnegative monotonic epochs, assignment ownership by a present member, and whole-group tombstone dominance until a later group-metadata write, with witnesses for malformed type binding, orphan suppression, stale-epoch suppression, and i32::MAX.
Summary
Drives the production replay decisions shared by the consumer, share, and streams adapters. It requires key/value kind agreement, parent-before-child application, nonnegative monotonic epochs, assignment ownership by a present member, and whole-group tombstone dominance until a later group-metadata write, with witnesses for malformed type binding, orphan suppression, stale-epoch suppression, and i32::MAX.
Drives
replay_mutationreplay_epoch_is_admissiblereplay_batch_cursor_decision
Bounds
One group, two members, every group, member, assignment, topology, partition-metadata, and share-state record class, epochs -1, 0, 1, and i32::MAX, exact retries, mismatched value kinds, and every reachable log ordering through depth 16.
Pinned unique states
ConfigUnique states
PINNED_UNIQUE_STATES6,376
Outside the model
Key parsing, schema decoding, transaction-marker interpretation, log reads, compaction, cache allocation, actor seeding, and process-crash atomicity remain host or I/O assumptions.
What the catalog says

The broker catalog's own description of this model, reproduced from its verification document.

The coordinator replay model drives the production replay_mutation and replay_epoch_is_admissible decisions used by the consumer, share, and streams adapters. It checks one group, two members, every group, member, assignment, topology, partition-metadata, and share-state record class, epochs -1, 0, 1, and i32::MAX, exact retries, mismatched value kinds, and every reachable log ordering. The run reaches exactly 6,376 unique states and converges at depth 14, pinned by PINNED_UNIQUE_STATES in the model file, below its depth-40 and two-million-state truncation guards. Properties require key/value kind agreement, parent-before-child application, nonnegative monotonic epochs, assignment ownership by a present member, and whole-group tombstone dominance until a later group-metadata write. Reachability witnesses cover a group created by a child record, an ignored tombstone for an absent group, a load that fails on an out-of-order tombstone, malformed type binding, stale-epoch suppression, i32::MAX, and member assignment replay. The bootstrap adapter decodes a value before publishing its group-type lock; malformed values return an error without state. As in Kafka's GroupMetadataManager.replay, a child record creates its missing group, a tombstone for a group the log does not hold is ignored, and an out-of-order tombstone fails the load. A member tombstone also removes its target and current assignments. A target-metadata tombstone removes its targets. The outer replay walk supplies record order and bounded cursors through replay_batch_cursor_decision. Key parsing, schema decoding, transaction-marker interpretation, log reads, compaction, cache allocation, actor seeding, and process-crash atomicity remain host or I/O assumptions.

Streams-group reconciliation model krabka-broker streams_group_model.rs 37,960 states Drives the production streams group membership, timeout, target-installation, and cooperative active-task transitions together with the port of Kafka's CurrentAssignmentBuilder and the replay adapters. A task pending revocation counts as still owned, so the properties require exclusive current and target active-task ownership, topology-valid assignments, nonnegative fenced epochs, coherent group phases, and exact durable replay projection.
Summary
Drives the production streams group membership, timeout, target-installation, and cooperative active-task transitions together with the port of Kafka's CurrentAssignmentBuilder and the replay adapters. A task pending revocation counts as still owned, so the properties require exclusive current and target active-task ownership, topology-valid assignments, nonnegative fenced epochs, coherent group phases, and exact durable replay projection.
Drives
StreamsGroupStatestreams assignorStreamsGroupState::reconcile_membermember-epoch fencesnapshot/apply replay adapters
Bounds
Two member IDs, one subtopology with one or two tasks, three logical timeout ticks, topology epochs through two, group epochs through five, and one crash/replay in every reachable ordering.
Pinned unique states
ConfigUnique states
PINNED_UNIQUE_STATES37,960
Outside the model
Request decoding, actor mailbox delivery, offsets-log append durability, cache publication, wall-clock scheduling, metadata-source snapshots, and internal-topic I/O remain outside the model.
What the catalog says

The broker catalog's own description of this model, reproduced from its verification document.

The streams-group reconciliation model drives the production StreamsGroupState membership, timeout, target-installation, and cooperative active-task transitions; the production streams assignor; the port of Kafka's CurrentAssignmentBuilder through StreamsGroupState::reconcile_member; the shared member-epoch fence; and the production snapshot/apply replay adapters. It exhaustively checks two member IDs, one subtopology with one or two tasks, three logical timeout ticks, topology epochs through two, group epochs through six (five bumps from the initial epoch 1), and one crash/replay in every reachable ordering. The run reaches exactly 37,960 unique states, pinned by PINNED_UNIQUE_STATES in the model file, below its depth-64 and one-million-state truncation guards. A task pending revocation counts as still owned, so the properties require exclusive current and target active-task ownership, topology-valid assignments, nonnegative fenced epochs, coherent group phases, and exact durable replay projection. Reachability witnesses cover unknown-member, stale, and forward heartbeat rejection; timeout; topology growth; replay; withheld ownership; release; and two-member membership. Request decoding, actor mailbox delivery, offsets-log append durability, cache publication, wall-clock scheduling, metadata-source snapshots, and internal-topic I/O remain outside the model. Adapter tests cover malformed reports, epoch exhaustion, retry, stale and forward epochs, and failed log appends. A failed append returns COORDINATOR_LOAD_IN_PROGRESS, publishes no partial batch, and leaves recovery to durable replay after the actor restarts.

EndTxn decision model krabka-broker decision_model.rs 7,975 states Runs one transactional id through every interleaving of an EndTxn split at its marker window, InitProducerId, AddPartitionsToTxn, and the completion task. It requires that an EndTxn whose Phase 3 finds the entry moved never writes Complete*, that each generation finalizes at most once, that InitProducerId never moves a Prepare* transaction, and that the epoch never regresses.
Summary
Runs one transactional id through every interleaving of an EndTxn split at its marker window, InitProducerId, AddPartitionsToTxn, and the completion task. It requires that an EndTxn whose Phase 3 finds the entry moved never writes Complete*, that each generation finalizes at most once, that InitProducerId never moves a Prepare* transaction, and that the epoch never regresses.
Drives
decide_phase1_transitionprepare_completion_identities_with_freshcompletion_producer_identitydecide_end_txn_completioncompletion_fornext_producer_identitycompletion_decisionapply_completion
Bounds
One transactional id with producer epochs up to 3 (basic, depth 11) and 6 (wide, depth 20).
Pinned unique states
ConfigUnique states
basic231
wide7,744
Total7,975
Outside the model
The KIP-360 identity classification and keepPreparedTxn recovery are not exercised.
What the catalog says

The broker catalog's own description of this model, reproduced from its verification document.

The EndTxn decision model (decision_model) runs one transactional id through every interleaving of an EndTxn split at its marker window (Phase 1, then Phase 3 after the lock is released for the fan-out), InitProducerId naming no identity, AddPartitionsToTxn, and the completion task. It drives decide_phase1_transition, prepare_completion_identities_with_fresh, completion_producer_identity, decide_end_txn_completion, the Prepare* gate of pending_completion_response (completion_for), the verified next_producer_identity bump, prepare_server_abort_identities_with_fresh for the fence of an Ongoing transaction (the function the InitProducerId handler and the reaper prepare that abort with), and the completion task's completion_for, completion_decision and apply_completion. The PrepareAbort state of that fence and the handler's state table are modeled, and the KIP-360 identity classification and keepPreparedTxn recovery are not exercised. The fence runs at TV_2 and below it. Up to producer epoch 3 and 6 the search visits exactly 231 and 7,744 unique states at depth 11 and 20 at either version, pinned by PINNED_UNIQUE_STATES_BASIC and PINNED_UNIQUE_STATES_WIDE. always properties require that an EndTxn whose Phase 3 finds the entry no longer at its prepared identity, state and generation never writes Complete*; that each generation finalizes at most once, so never both committed and aborted; that InitProducerId never moves a Prepare* transaction (Kafka's CONCURRENT_TRANSACTIONS); that the fence raises the epoch once and records the last epoch and the transaction version Kafka records at the cluster's version; that Phase 3 rejects only an entry that moved; and that the epoch never regresses. Witnesses show a commit, an abort, and a producer overtaken inside the marker window -- reached only through legal transitions: the completion task finishes the prepared transaction and a later InitProducerId or transaction moves the entry on. A table test pins InitProducerId's outcome from every state.

Exactly-once read composition model krabka-broker eos_composition_model.rs 59,752 states 6 properties Runs two transactional producers and an advancing high watermark over one partition, driving the EndTxn cores and the production visibility window while modeling the LSO rule and the abort filter. Properties are checked against oracles derived by producer and control marker: the real window equals min(first unstable offset, hw), every committed record below it is visible, nothing above the HWM is visible, and no offset in an aborted range is visible.
Summary
Runs two transactional producers and an advancing high watermark over one partition, driving the EndTxn cores and the production visibility window while modeling the LSO rule and the abort filter. Properties are checked against oracles derived by producer and control marker: the real window equals min(first unstable offset, hw), every committed record below it is visible, nothing above the HWM is visible, and no offset in an aborted range is visible.
Drives
EndTxn cores (Proceed path)compute_visibility_window
Bounds
Two transactional producers over one partition with one (basic, logs of 5 batches, depth 13) or two (wide, logs of 7 batches, depth 19) generations per producer.
Properties
window_is_min_lso_hwcommitted_prefix_completenothing_visible_above_hwno_visible_abortedonly_committed_visibleno_transition_violation
Pinned unique states
ConfigUnique states
basic1,228
wide58,524
Total59,752
What the catalog says

The broker catalog's own description of this model, reproduced from its verification document.

The exactly-once read composition (eos_composition_model) runs two transactional producers and an advancing high watermark over one partition. It drives the EndTxn cores on their Proceed path and the production compute_visibility_window; the LSO rule and the abort filter of visible() are modeled. Properties are checked against oracles derived by producer and control marker, never by the generation tags the model's own lso() and filter read: window_is_min_lso_hw requires the real window to equal min(first unstable offset, hw), committed_prefix_complete requires every committed record below that window to be visible, nothing_visible_above_hw bounds the visible set by the HWM, and no_visible_aborted excludes every offset in an aborted range as Kafka's aborted-transaction index derives it from the Abort markers. only_committed_visible requires every visible record to be committed; ghost flags fail no_transition_violation if the HWM or LSO regresses or an End does not Proceed. The two configurations (one and two generations per producer, logs of 5 and 7 batches) visit exactly 1,228 and 58,524 unique states at depth 13 and 19, pinned by PINNED_UNIQUE_STATES_BASIC and PINNED_UNIQUE_STATES_WIDE. Witnesses show a visible commit, a filtered abort, a commit held back behind an older open transaction, and the HWM clamp active.

KIP-939 timeout model krabka-broker two_pc_model.rs 102,790 states 3 properties Runs InitProducerId with and without enable2Pc, AddPartitionsToTxn, EndTxn, completion, and the idle reaper over a clock that meets every timeout's start + timeout and start + timeout + 1. The headline property keys on the request's enable2Pc flag so a wrong encoding fails it, and the reaper properties compare every sweep with Kafka's timedOutTransactions rule in exact arithmetic.
Summary
Runs InitProducerId with and without enable2Pc, AddPartitionsToTxn, EndTxn, completion, and the idle reaper over a clock that meets every timeout's start + timeout and start + timeout + 1. The headline property keys on the request's enable2Pc flag so a wrong encoding fails it, and the reaper properties compare every sweep with Kafka's timedOutTransactions rule in exact arithmetic.
Drives
resolve_txn_timeoutshould_abort_idle_txndecide_phase1_transitionprepare_completion_identities_with_freshcompletion_forcompletion_decisionapply_completioncompletion_producer_identitynext_producer_identity
Bounds
One transactional id, requested timeouts 1, 60000, 900000, 900001 and 0, a monotonic clock over 0, 1, 2, 60000, 60001, 900000, 900001, i32::MAX, i32::MAX + 1 and i64::MAX, and producer epochs up to 3 (basic, depth 20) and 5 (wide, depth 22).
Properties
two_pc_never_reapedreaper_never_earlyreaper_never_misses
Pinned unique states
ConfigUnique states
basic21,876
wide80,914
Total102,790
What the catalog says

The broker catalog's own description of this model, reproduced from its verification document.

The KIP-939 timeout model (two_pc_model) runs one transactional id through InitProducerId with and without enable2Pc and with requested timeouts 1, 60000, 900000 (= transaction.max.timeout.ms), 900001 and 0, AddPartitionsToTxn, EndTxn, completion, the idle reaper, and a monotonic clock over 0, 1, 2, 60000, 60001, 900000, 900001, i32::MAX, i32::MAX + 1, i64::MAX, so a transaction opened at 0 meets every timeout's start + timeout and start + timeout + 1. It drives resolve_txn_timeout, should_abort_idle_txn, decide_phase1_transition, prepare_completion_identities_with_fresh, prepare_server_abort_identities_with_fresh (the abort of the reaper and the InitProducerId fence), completion_for, completion_decision, apply_completion, completion_producer_identity and the verified next_producer_identity; the PrepareAbort state of apply_prepare_abort and of the fence, and the start_ms stamp, are modeled, and each Prepare* completes atomically. The cluster's transaction version is TV_2 or below it. Up to producer epoch 3 and 5 the search visits exactly 21,876 and 80,914 unique states at depth 20 and 22 at either version, pinned by PINNED_UNIQUE_STATES_BASIC and PINNED_UNIQUE_STATES_WIDE. A property requires that the abort of an Ongoing transaction raises the epoch once and records the last epoch and transaction version Kafka records at the cluster's version. The headline two_pc_never_reaped keys on the request's enable2Pc flag, not the persisted timeout, so a wrong encoding fails it. reaper_never_early and reaper_never_misses compare every sweep with Kafka's timedOutTransactions rule restated from the request (Ongoing, not 2PC, start + requested < now in exact arithmetic). Generations finalize in strictly increasing order, and the epoch never regresses. Witnesses show a classic reap, a reap exactly one millisecond past the timeout, a sweep that spares a transaction exactly at its timeout, a 2PC transaction open past start + i32::MAX, and a commit.

Broker share and break-glass 4

Share-group membership model krabka-broker share_group_model.rs 54,860 states Drives the production share group join, leave, and timeout transitions, the assignment reconciler and share assignor, the member-epoch fence, and the replay adapters. It requires unique (member, topic, partition) coordinates, bounded assignments for members at the target epoch, nonnegative fenced epochs, and exact durable replay projection; cross-member partition overlap remains intentional KIP-932 behavior when members outnumber partitions.
Summary
Drives the production share group join, leave, and timeout transitions, the assignment reconciler and share assignor, the member-epoch fence, and the replay adapters. It requires unique (member, topic, partition) coordinates, bounded assignments for members at the target epoch, nonnegative fenced epochs, and exact durable replay projection; cross-member partition overlap remains intentional KIP-932 behavior when members outnumber partitions.
Drives
ShareGroupStateassignment reconcilershare assignormember-epoch fencesnapshot/apply replay adapters
Bounds
Two member IDs, one topic with one or two partitions, four logical timeout ticks, epochs through five, and one crash/replay in every reachable ordering.
Pinned unique states
ConfigUnique states
PINNED_UNIQUE_STATES54,860
Outside the model
Request decoding, actor mailbox delivery, offsets-log append durability, cache publication, wall-clock scheduling, metadata snapshot consistency, and share-state persister I/O remain outside the model.
What the catalog says

The broker catalog's own description of this model, reproduced from its verification document.

The share-group membership model (share_group_model) drives the production ShareGroupState join, leave, and timeout transitions; the production assignment reconciler and share assignor; the shared member-epoch fence; and the production snapshot/apply replay adapters. It exhaustively checks two member IDs, one topic with one or two partitions, four logical timeout ticks, epochs through six (five bumps from the initial epoch 1), and one crash/replay in every reachable ordering. A stale heartbeat sends the member epoch minus one, which Kafka accepts when it is the previous member epoch or 0 (a rejoin) and fences otherwise. The run reaches exactly 54,860 unique states, pinned by PINNED_UNIQUE_STATES in the model file, below its depth-64 and one-million-state truncation guards. Properties require unique (member, topic, partition) coordinates, bounded assignments for members at the target epoch, nonnegative fenced epochs, and exact durable replay projection; reachability witnesses cover unknown-member, stale, and forward heartbeat rejection, timeout, metadata reassignment, replay, and two-member membership. Cross-member partition overlap remains intentional KIP-932 behavior when members outnumber partitions. Request decoding, actor mailbox delivery, offsets-log append durability, cache publication, wall-clock scheduling, metadata snapshot consistency, and share-state persister I/O remain outside the model. A failed offsets-log append is covered by an adapter test: the actor returns COORDINATOR_LOAD_IN_PROGRESS, publishes no partial batch, and relies on durable replay after restart.

Share-partition state model krabka-broker state_model.rs 22,720 states Holds the production KIP-932 acquisition state and drives it over every interleaving of consumer operations, time advance, KFC-1 deferral, a leader reload through the persisted batches, and a log-start advance. It requires a sorted, gap-free window covering exactly [start_offset, end_offset), exactly one owner for every acquired batch, lock bookkeeping matching the delivery state, a bounded delivery count, and a nondecreasing start offset.
Summary
Holds the production KIP-932 acquisition state and drives it over every interleaving of consumer operations, time advance, KFC-1 deferral, a leader reload through the persisted batches, and a log-start advance. It requires a sorted, gap-free window covering exactly [start_offset, end_offset), exactly one owner for every acquired batch, lock bookkeeping matching the delivery state, a bounded delivery count, and a nondecreasing start offset.
Drives
AcquisitionState::materializeAcquisitionState::acquireAcquisitionState::acknowledgeAcquisitionState::renewAcquisitionState::expire_locksAcquisitionState::defer_internalAcquisitionState::promote_deferredAcquisitionState::to_persist_batchesAcquisitionState::load_from
Bounds
Two-member concurrency over three offsets with an in-flight limit of 3 or a record lock limit of 2, plus failover with reload, deferral over the failover window, one-member deferral over three offsets, and log-start advance with reload.
Pinned unique states
ConfigUnique states
two members, three offsets, in-flight limit 37,969
two members, three offsets, record lock limit 21,521
failover with reload623
deferral over the failover window1,118
one-member deferral over three offsets5,012
log-start advance with reload6,477
Total22,720
Outside the model
The share-state persister, the fetch path, and wall-clock time remain outside the model.
What the catalog says

The broker catalog's own description of this model, reproduced from its verification document.

The share-partition model (share_partition/state_model) holds the production KIP-932 AcquisitionState and drives its materialize, acquire, acknowledge, renew, expire_locks, defer_internal, promote_deferred, to_persist_batches, and load_from over every interleaving of consumer operations, time advance, KFC-1 deferral, a leader reload through the persisted batches, and a log-start advance. Deferral is over-approximated: any offset can be deferred at any point, which covers every deferral a log and a clock could produce. Properties require a sorted, gap-free window that exactly covers [start_offset, end_offset), exactly one owner for every acquired batch (so no offset is held by two members), lock bookkeeping that matches the delivery state, a bounded delivery count, and the share-partition start offset inside its range, plus the log-start and deferral properties in the configs that enable those actions. A transition check requires a nondecreasing start offset and delivery count, keeps an acknowledged offset terminal, and leaves promote_deferred as the only route out of Deferred. Two-member concurrency over three offsets with an in-flight limit of 3, or a record lock limit of 2, reaches exactly 7,969 or 1,521 unique states; failover with reload reaches 623; deferral over the failover window 1,118; one-member deferral over three offsets 5,012; and log-start advance with reload 6,477. Each count is pinned by its PINNED_UNIQUE_STATES_* constant in the runner, below the depth-80 and 200,000-state guards. The share-state persister, the fetch path, and wall-clock time remain outside the model.

Break-glass lifecycle model krabka-broker state_model.rs 150 states Drives one break-glass proposal through every interleaving of approve, withdraw, expire, and consume, building a real proposal record and calling the production approval and gate decisions against a real MetadataImage. It requires that no interleaving consumes the proposal twice, none consumes it with fewer than required_approvals distinct approvers, and a withdrawn proposal is never consumed.
Summary
Drives one break-glass proposal through every interleaving of approve, withdraw, expire, and consume, building a real proposal record and calling the production approval and gate decisions against a real MetadataImage. It requires that no interleaving consumes the proposal twice, none consumes it with fewer than required_approvals distinct approvers, and a withdrawn proposal is never consumed.
Drives
approve::decidegate::authorize
Bounds
One proposal over a small set of principals with a logical clock that expires it at 2 ms, in a two-of-three-approvers config and a three-of-four config.
Pinned unique states
ConfigUnique states
two-of-three approvers36
three-of-four approvers114
Total150
Outside the model
Signature verification, controller submission, and metadata durability remain outside the model.
What the catalog says

The broker catalog's own description of this model, reproduced from its verification document.

The break-glass lifecycle model (break_glass/state_model) drives one proposal through every interleaving of approve, withdraw, expire, and consume over a small set of principals and a logical clock that expires the proposal at 2 ms. Each transition builds a BreakGlassProposalRecord from the state and calls the production approve::decide for an approval or withdrawal and gate::authorize for a consume, against a real MetadataImage. Properties require that no interleaving consumes the proposal twice, that none consumes it with fewer than required_approvals distinct approvers, and that a withdrawn proposal is never consumed; witnesses cover a consume, a withdrawal, an expiry, full approval, and a fully approved proposal that expired unspent. The two-of-three-approvers config, whose principals include the proposer, who cannot approve, and one principal outside the approver set, and the three-of-four config reach exactly 36 and 114 unique states, pinned by PINNED_UNIQUE_STATES_TWO_OF_THREE and PINNED_UNIQUE_STATES_THREE_OF_FOUR, below the depth-20 and 200,000-unique guards. Signature verification, controller submission, and metadata durability remain outside the model.

Break-glass cross-spend model krabka-broker cross_spend_model.rs 1,296 states 2 properties Puts two open proposals in one image and runs every interleaving of approve, withdraw, expire, and consume against them. Each request carries a hand-written covered_by set taken from KFC-9's target-matching rule rather than from the broker's own covers, so the headline no_cross_spend property is not a tautology; removing the partition-target limit fails only the lookalike config and removing the consumed check fails all three.
Summary
Puts two open proposals in one image and runs every interleaving of approve, withdraw, expire, and consume against them. Each request carries a hand-written covered_by set taken from KFC-9's target-matching rule rather than from the broker's own covers, so the headline no_cross_spend property is not a tautology; removing the partition-target limit fails only the lookalike config and removing the consumed check fails all three.
Drives
approve::decidegate::authorize
Bounds
Two open proposals in one MetadataImage under the same clock bound as the lifecycle model, in three configs: one action on two topics, a topic that looks like a partition of another, and two proposals that both cover one partition.
Properties
no_cross_spendno_under_approved
Pinned unique states
ConfigUnique states
two topics432
partition lookalike432
both cover one partition432
Total1,296
What the catalog says

The broker catalog's own description of this model, reproduced from its verification document.

The break-glass cross-spend model (break_glass/cross_spend_model) puts two open proposals in one MetadataImage and runs every interleaving of approve, withdraw, expire, and consume against them, with the same production approve::decide and gate::authorize calls and the same clock bound as the lifecycle model. Each request carries a hand-written covered_by set, taken from KFC-9's target-matching rule rather than from the broker's own covers, so the headline property is not a tautology. Properties require that no proposal is spent on a request it does not cover (no_cross_spend), that each proposal is spent at most once, that no spend is under-approved, and that a withdrawn proposal is never consumed; witnesses cover each proposal spent, both spent, and one approved while the other is requested. Three configs, one action on two topics, a topic that looks like a partition of another, and two proposals that both cover one partition, each reach exactly 432 unique states, pinned by PINNED_UNIQUE_STATES_TWO_TOPICS, PINNED_UNIQUE_STATES_PARTITION_LOOKALIKE, and PINNED_UNIQUE_STATES_BOTH_COVER, below the depth-12 and 1,000,000-unique guards. Removing the partition-target limit from gate::covers fails only the lookalike config, and removing the consumed check fails all three. no_under_approved is a guard here, not an independent check, because approve::decide already refuses a second approval from one principal.

Log 2

Compaction model krabka-broker compact_model.rs 1.25M states 5 properties Runs one pass of the cleaner over an abstract log per step, using the production key-indexing and retain decisions, and checks each pass's output against its input with rules computed from the two logs alone. The legacy cleaner, which indexes control keys and dedups markers by them, is run through the same checker and reports exactly control-not-deduped and marker-data-precedence as broken.
Summary
Runs one pass of the cleaner over an abstract log per step, using the production key-indexing and retain decisions, and checks each pass's output against its input with rules computed from the two logs alone. The legacy cleaner, which indexes control keys and dedups markers by them, is run through the same checker and reports exactly control-not-deduped and marker-data-precedence as broken.
Drives
should_index_keyretain_decision
Bounds
Abstract logs of up to 4 entries (basic) and up to 5 entries (wide).
Properties
control-not-dedupedmarker-data-precedencetombstone-agingidempotent-stampno-data-loss
Pinned unique states
ConfigUnique states
basic144,128
wide1,102,770
Total1,246,898
What the catalog says

The broker catalog's own description of this model, reproduced from its verification document.

The compaction model (compact_model) runs one pass of a cleaner over an abstract log per Compact step, the production should_index_key and retain_decision by default, and records the log the pass consumed in the next state. Five always properties check each pass's output against its input with rules computed from the two logs alone: control-not-deduped (a marker whose horizon has not elapsed survives), marker-data-precedence (a marker survives while the pass reads data of its own transaction in front of it, as Kafka decides per transaction rather than per producer), tombstone-aging (no elapsed tombstone survives; an unexpired newest tombstone does), idempotent-stamp (the output is the input with deletions and None -> now + delete.retention.ms stamps only), and no-data-loss (the set of keys with a live newest value is unchanged). The basic run (logs up to 4 entries) and the wide run (up to 5) reach exactly 144,128 and 1,102,770 unique states, pinned by PINNED_UNIQUE_STATES_BASIC and PINNED_UNIQUE_STATES_WIDE in compact_model/runner.rs, below the depth-40 and 1,500,000-unique guards. The legacy cleaner, which indexes control keys and dedups markers by them, is run through the same pass and checker, which reports exactly control-not-deduped and marker-data-precedence as broken, with the recorded five-action counterexample.

Leader-epoch reconciliation model krabka-broker leader_epoch_model.rs 66,351 states 1 property Drives the production leader-epoch lookup on both sides of a fetch and the checkpoint truncation, with Kafka's LeaderEpochFileCache.assign, readRecords divergence gate, and fetcher truncation rules as the environment. It requires an accepted follower to be a prefix of the leader's log, no truncation to drop a shared record, every diverging_epoch to change the follower, and the leader never to answer OFFSET_OUT_OF_RANGE; a third config drops assign-at-election and must find the counterexample the assignment prevents.
Summary
Drives the production leader-epoch lookup on both sides of a fetch and the checkpoint truncation, with Kafka's LeaderEpochFileCache.assign, readRecords divergence gate, and fetcher truncation rules as the environment. It requires an accepted follower to be a prefix of the leader's log, no truncation to drop a shared record, every diverging_epoch to change the follower, and the leader never to answer OFFSET_OUT_OF_RANGE; a third config drops assign-at-election and must find the counterexample the assignment prevents.
Drives
epoch_and_offset_for_entriestruncate_to
Bounds
reconciliation_two_replicas has two replicas, epochs through 5, and three records per leader log; reconciliation_three_replicas has three replicas, epochs through 3, and two records; the regression config has two replicas, epochs through 4, and three records.
Properties
leader_places_every_follower_epoch
Pinned unique states
ConfigUnique states
reconciliation_two_replicas35,274
reconciliation_three_replicas20,212
no-assign regression (PINNED_UNIQUE_STATES_NO_ASSIGN)10,865
Total66,351
Outside the model
Log start above 0, tiered storage, the below-log-start OFFSET_OUT_OF_RANGE, the follower's ListOffsets-based out-of-range recovery, wire encoding, and I/O remain outside the model.
What the catalog says

The broker catalog's own description of this model, reproduced from its verification document.

The leader-epoch reconciliation model drives the production epoch_and_offset_for_entries lookup on both sides of a fetch and the production checkpoint truncation (truncate_to), with Kafka's LeaderEpochFileCache.assign, Partition.readRecords divergence gate, and AbstractFetcherThread.getOffsetTruncationState follower rules as the environment. Replicas hold logs of per-offset leader epochs; elections are clean or unclean, and the new leader assigns its epoch at its log end. The reconciliation_two_replicas config (two replicas, epochs through 5, three records per leader log) reaches exactly 35,274 unique states, and reconciliation_three_replicas (three replicas, epochs through 3, two records) exactly 20,212, pinned by PINNED_UNIQUE_STATES_TWO and PINNED_UNIQUE_STATES_THREE in leader_epoch_model, below its depth-64 and two-million-state truncation guards. Properties require a follower the leader has accepted to be a prefix of the leader's log, no truncation to drop a record the follower shares with the leader, every diverging_epoch to change the follower (no livelock), the leader never to answer OFFSET_OUT_OF_RANGE, and every checkpoint to stay strictly increasing. Reachability witnesses cover a divergent suffix really truncated, a gap epoch resolved to the leader's floor epoch, a KIP-279 step-back round, and full convergence after a divergence. A third config (two replicas, epochs through 4, three records; 10,865 unique states, PINNED_UNIQUE_STATES_NO_ASSIGN) drops assign-at-election and must find a counterexample to leader_places_every_follower_epoch while the safety properties still hold: without it a newly elected leader that has not written cannot place a follower's newer epoch. The broker assigns the epoch at promotion, as Partition::install_local_leadership records it at the log end, so the first two configs describe production and the third is the regression the assignment prevents. Replacing the gap branch's floor epoch with the requested epoch fails the proptest driver described next. A proptest driver runs the same step function over random 400-step schedules on four replicas. Log start above 0, tiered storage, the below-log-start OFFSET_OUT_OF_RANGE, the follower's ListOffsets-based out-of-range recovery, wire encoding, and I/O remain outside the model.

KRaft 1

KRaft model krabka-broker kraft_model.rs 2.28M states 3 properties Runs the production quorum state machine for every node over an unordered network, with replication as the one abstracted exchange. It requires every node that believes it leads epoch e to hold each entry committed in an epoch at or below e, and its stale_candidate_refused witness requires a vote refused for log recency alone; removing the log-recency conjunct from handle_vote_request makes the config fail.
Summary
Runs the production quorum state machine for every node over an unordered network, with replication as the one abstracted exchange. It requires every node that believes it leads epoch e to hold each entry committed in an epoch at or below e, and its stale_candidate_refused witness requires a vote refused for log recency alone; removing the log-recency conjunct from handle_vote_request makes the config fail.
Drives
QuorumStateMachinehandle_fetchhandle_fetch_responsehandle_vote_request
Bounds
three_voters_append checks three voters, one client append, leader epochs through two, at most two in-flight messages, and at most one crashed node at a time; other configs cover election safety, two-voter linearizability, message loss and duplication, and stateless appenders.
Properties
leader_completenessdivergent_log_truncatedstale_candidate_refused
Pinned unique states
ConfigUnique states
three_voters_append839,339
three_voters_election_safety10,750
two_voters_linearizable46,521
three_voters_faults1,128,704
two_voters_append_via_linearizable256,973
Total2,282,287
Outside the model
Wire encoding, disk durability, wall-clock timing, volatile-state loss on restart, and behavior beyond the stated bounds remain outside the model.
What the catalog says

The broker catalog's own description of this model, reproduced from its verification document.

The KRaft model (kraft_model) runs the production QuorumStateMachine for every node over an unordered network. Replication is the one abstracted exchange: the records a fetch returns are applied when the follower sends it, and only when the leader's log extends the follower's (the high watermark reaches a follower on the same terms). A follower whose log disagrees with the leader's is never overwritten; the production handle_fetch answers its fetch with a diverging epoch, the production handle_fetch_response truncates it, fetches again at once from the truncated end, and re-arms the fetch timer, as Kafka's KafkaRaftClient.handleFetchResponse and pollFollowerAsVoter do, and the divergent_log_truncated witness requires that path to run in both crash configs. Its three_voters_append config checks leader completeness under a stale majority: three voters, one client append, leader epochs through two, at most two in-flight messages, and at most one crashed node at a time, whose omission model drops that node's traffic and so leaves it behind the committed prefix. The pinned run reaches 839,339 unique states, asserted by PINNED_UNIQUE_STATES_THREE_VOTERS_APPEND, below its depth-60 and 8,000,000-state truncation guards. The leader_completeness property requires every node that believes it leads epoch e to hold each entry committed in an epoch at or below e, stamped with the epoch it committed under. The stale_candidate_refused witness requires a vote refused for log recency alone: the transition that delivers the request also runs a copy of the voter's real machine on the same request with the candidate's log end replaced by the voter's own, and only a refusal that copy grants counts. It would disappear if the config stopped reaching a stale-majority election. Removing the log-recency conjunct from handle_vote_request makes this config fail. The other configs cover election and log-matching safety with three voters and no appends (three_voters_election_safety, 10,750 states), committed-log linearizability with two voters (two_voters_linearizable, 46,521), election safety under message loss, duplication, and one crash at a time (three_voters_faults, 1,128,704), and stateless appenders (two_voters_append_via_linearizable, 256,973), each pinned by its PINNED_UNIQUE_STATES_* constant. Wire encoding, disk durability, wall-clock timing, volatile-state loss on restart, and behavior beyond the stated bounds remain outside the model.

Throttle 1

Throttle bucket model krabka-broker bucket_model.rs 462,427 states Drives the proved cap-and-grant arithmetic at every commit and steps the token bucket protocol one shared-memory access at a time. It requires available <= burst and exact token conservation per configuration, so a claimed refill can neither vanish nor be granted twice, and RED witnesses must find both bugs of the former lock-free seqlock design.
Summary
Drives the proved cap-and-grant arithmetic at every commit and steps the token bucket protocol one shared-memory access at a time. It requires available <= burst and exact token conservation per configuration, so a claimed refill can neither vanish nor be granted twice, and RED witnesses must find both bugs of the former lock-free seqlock design.
Drives
plan_consumeTokenBucket
Bounds
bucket_basic checks two consumers, one reset between bursts 2 and 1, two clock units, and requests up to two; bucket_wide checks two consumers, two resets among (rate 1, burst 3), (2, 1), (0, 0) and (1, 0), two clock units, and requests up to two; bucket_fractional meters two storage units to a token and checks two consumers, one reset from (rate 1, burst 3) to (1, 1), two clock units, and requests up to two whole tokens.
Pinned unique states
ConfigUnique states
bucket_basic56,259
bucket_wide372,204
bucket_fractional33,964
Total462,427
Outside the model
The clock is modeled in whole seconds with whole-token rates and memory as sequentially consistent; the part-token remainder is covered by unit tests.
What the catalog says

The broker catalog's own description of this model, reproduced from its verification document.

The throttle bucket model drives the production plan_consume cap-and-grant arithmetic at every commit and steps the TokenBucket protocol one shared-memory access at a time: the lock-free rate-mirror read of the unthrottled fast path, the lock acquire, the clock read, the last_refill claim, the available commit, and the release for a consume; the lock acquire, the group store, the mirror publish, and the release for a reset, with a separate burst per configuration. A reset keeps the balance: it refills the old configuration to now, caps the balance at the old and then at the new burst, and starts a bucket that had no rate, or gets none, full. Its bucket_basic config checks two consumers, one reset between bursts 2 and 1, two clock units, and requests up to two; the run reaches exactly 56,259 unique states, pinned by PINNED_UNIQUE_STATES_BASIC. Its bucket_wide config checks two consumers, two resets among (rate 1, burst 3), (2, 1), (0, 0) and (1, 0), two clock units, and requests up to two; the run reaches exactly 372,204 unique states, pinned by PINNED_UNIQUE_STATES_WIDE. Its bucket_fractional config meters two storage units to a token, as production meters micro-tokens, and checks two consumers, one reset from (rate 1, burst 3) to (1, 1), two clock units, and requests up to two whole tokens, so its rates are half a token per second and the second burst holds half a token; the run reaches exactly 33,964 unique states, pinned by PINNED_UNIQUE_STATES_FRACTIONAL. Each commit also drives the production whole-token request cut, whole_token_request. All three stay below the depth-80 and 4,000,000-state truncation guards. Properties require available <= burst and exact token conservation per configuration: available + granted + capped + in-flight claimed refill == base + rate * (last_refill - t0), where base is the balance the reset started the configuration with, so a claimed refill can neither vanish nor be granted twice, and every grant is a whole number of tokens. Reachability witnesses cover overlapping consumers, a reset overlapping a consume, an in-flight claim, a granted and a capped refill, a shrinking reset, a drained bucket, and a reset that keeps a balance under the new burst. Debt is outside the model: try_consume never creates it, and record differs from a consume only in what it does with the part it cannot grant, which it keeps as debt, at most what the refill repays in max_wait for record_bounded; the unit tests in consume.rs cover both. RED witnesses step the former lock-free seqlock design (a generation re-check followed by two compare-and-exchanges) and must find both of its bugs: a whole reset between the generation re-check and the available commit that lets available exceed the new burst, and a failed commit that drops the refill its claim took. Two scripted schedules replay each bug concretely against the seqlock and show that the lock makes the same interleaving impossible. The clock is modeled in whole seconds with rates in whole storage units and memory as sequentially consistent; the part-unit remainder is covered by unit tests in consume.rs.

Audit 1

Audit spool model krabka-broker spool_model.rs 71,351 states 3 properties Drives the production spool append decision, the pending-loss helpers, the loss-batch settlement kernel, and the replay-recovery classifier, with a loss or a crash able to fall between any two of the writer's steps. It requires every durably admitted record to remain delivered or durably pending, at-most-once automatic delivery, exact loss accounting, distinct marker generations, and every durable marker settled exactly once.
Summary
Drives the production spool append decision, the pending-loss helpers, the loss-batch settlement kernel, and the replay-recovery classifier, with a loss or a crash able to fall between any two of the writer's steps. It requires every durably admitted record to remain delivered or durably pending, at-most-once automatic delivery, exact loss accounting, distinct marker generations, and every durable marker settled exactly once.
Drives
spool_append_decisionadd_loss_statesettle_loss_batchSpool::open replay-recovery classifier
Bounds
Three record IDs, two loss events, a two-record spool, sync cadence two, and at most two crashes, in every reachable ordering.
Properties
losses_accountedmarker_generations_uniquemarkers_settled_once
Pinned unique states
ConfigUnique states
PINNED_UNIQUE_STATES71,351
Outside the model
Filesystem and directory-sync semantics, storage hardware, sink durability, explicit operator recovery of poisoned replay, and behavior beyond the stated bounds remain outside the model.
What the catalog says

The broker catalog's own description of this model, reproduced from its verification document.

The audit spool model (spool_model) drives the production spool_append_decision for every spool append, a record's or a loss marker's, the add_loss_state helper behind PendingLosses::add, the settle_loss_batch kernel that PendingLosses::commit and PendingLosses::reconcile apply, and the replay-recovery classifier behind Spool::open. The loss path is PendingLosses as the serial writer drives it: a concurrent AuditHandle::emit loss or a crash can fall between any two of persist_with's steps (snapshot, sidecar write, marker append subject to spool capacity, sync, in-memory commit, commit write), and a poisoned reopen stops before reconciliation as Spool::open does. It checks three record IDs, two loss events, a two-record spool, sync cadence two, and at most two crashes, in every reachable ordering. The run reaches exactly 71,351 unique states, pinned by PINNED_UNIQUE_STATES in the model file, below its depth-64 and 4,000,000-state truncation guards. Properties require every durably admitted record to remain delivered or durably pending, at-most-once automatic delivery, exact loss accounting (every loss is reported by exactly one durable marker, is still pending, or was held only in memory at a crash), distinct marker generations, and every durable marker settled exactly once. Reachability witnesses cover torn append recovery, retry after a definite sink failure, fail-stop poison after an uncertain delivery, cleanup after a committed cursor, a marker refused by a full spool, loss-marker reconciliation after reopen, and a concurrent loss carried past a marker. The test superseded_settlement_loses_and_rereports_losses keeps the settlement that PendingLosses applied before settle_loss_batch, in which a commit left the remainder in the marker's generation and reconciliation zeroed the whole count, and requires the checker to find losses_accounted, marker_generations_unique, and markers_settled_once counterexamples for it. Filesystem and directory-sync semantics, storage hardware, sink durability, explicit operator recovery of poisoned replay, and behavior beyond the stated bounds remain outside the model. Adapter tests cover malformed, missing, stale, and committed poison plus loss-counter saturation.

Rust client 3

Role succession model krabka-client-rs model.rs 8 properties An exhaustive model of role succession that runs the production evaluate function itself rather than restating its rules: on every step it builds a role state from the model, asks evaluate what each member does, and applies that decision against a coordinator and a broker fence. A crashed candidate loses its producer session but keeps its registration until the coordinator expires it, so the model covers both a short pause that keeps the rank and a long outage that loses it.
Summary
An exhaustive model of role succession that runs the production evaluate function itself rather than restating its rules: on every step it builds a role state from the model, asks evaluate what each member does, and applies that decision against a coordinator and a broker fence. A crashed candidate loses its producer session but keeps its registration until the coordinator expires it, so the model covers both a short pause that keeps the rank and a long outage that loses it.
Drives
succession::evaluate
Bounds
Three candidates, a transaction coordinator that mints strictly increasing epochs, and a broker that rejects a write from a member whose epoch is not the newest minted one; a candidate can crash at any point.
Properties
at_most_one_unfenced_epochat_most_one_live_authorityhold_needs_a_live_lease_of_the_memberno_challenge_while_the_lease_is_livea_later_joiner_never_outranks_an_earlier_onea_registered_member_always_has_a_rankthe_lease_epoch_only_growsno_epoch_passes_the_minted_mark
Consumer lock-order model krabka-client-rs lock_order_model.rs 1 property An exhaustive model of the classic consumer's async-Mutex lock protocol, built to settle whether the poll loop and the background coordinator task can deadlock through a lock-order cycle. Collecting every hold-one-while-acquiring-another edge from the real code gives an acyclic partial order, and the model proves the protocol deadlock-free across all task interleavings.
Summary
An exhaustive model of the classic consumer's async-Mutex lock protocol, built to settle whether the poll loop and the background coordinator task can deadlock through a lock-order cycle. Collecting every hold-one-while-acquiring-another edge from the real code gives an acyclic partial order, and the model proves the protocol deadlock-free across all task interleavings.
Bounds
The nine shared tokio::sync::Mutex fields of the classic Consumer and the lock-holding regions of the poll, seek, coordinator, and commit tasks, each cited by function and lock variable from the real source.
Properties
no_deadlock
Outside the model
The model abstracts away values such as offsets, partitions, and RPC payloads, and the network, keeping only which task holds which mutex and where each task is suspended.
Producer client failover model krabka-client-rs client_failover_model.rs 5 properties An exhaustive model of producer-side failover routing at the client and server seam. It checks that stale metadata and a dead cached leader cannot make a prepared batch wedge forever: the batch either reaches the freshly elected leader quickly or fails within a bounded retry budget, and a retry preserves producer identity.
Summary
An exhaustive model of producer-side failover routing at the client and server seam. It checks that stale metadata and a dead cached leader cannot make a prepared batch wedge forever: the batch either reaches the freshly elected leader quickly or fails within a bounded retry budget, and a retry preserves producer identity.
Properties
identity_preservedsend_attempts_cappedrefreshes_cappedquick_recovery_or_failurebounded_dead_leader_churn

Other evidence 7

Showing 7 of 7

Independent failure signals 3

Mutation testing Mutation testing with cargo-mutants 3 links cargo-mutants inserts faults into production code and the suite must detect each one; sweeps run weekly per crate, and a skip is accepted only with a stated reason that a lint enforces.

cargo-mutants inserts faults into production code and the suite must detect each one; sweeps run weekly per crate, and a skip is accepted only with a stated reason that a lint enforces.

Mutation testing inserts faults into production code and checks whether the test suite detects them. A surviving mutant is a change the suite did not detect.

The mutation sweep baseline records the last run in which each swept crate had no surviving mutants.

Differential testing Differential and JVM acceptance suites 2 links Digest-pinned Apache Kafka and Confluent Platform brokers run beside krabka, real kafka-clients drive both, and responses are compared byte for byte.

Digest-pinned Apache Kafka and Confluent Platform brokers run beside krabka, real kafka-clients drive both, and responses are compared byte for byte.

Digest-pinned Apache Kafka and Confluent Platform brokers run beside krabka. Real kafka-clients and the standard admin tools drive both, and responses are compared byte for byte.

The wire layer in krabka-protocol generates its codecs from the upstream Apache Kafka JSON schemas. These codecs are written in safe Rust; differential suites check their behavior against live Kafka brokers because protocol encoding and decoding sit largely outside both formal tiers.

Property and crash testing Crash-recovery and property tests 1 link Unit, property-based, integration and crash-recovery tests cover the boundaries that neither formal tier reaches.

Unit, property-based, integration and crash-recovery tests cover the boundaries that neither formal tier reaches.

Unit, property-based, integration, and crash-recovery tests cover the boundaries that neither formal tier reaches, including protocol codecs, I/O, and async orchestration.

Those tests are evidence, but they are not a Creusot proof or a Stateright model check. The catalog's Outside both tiers section names what they cover on their own.

From the catalog 4

Process Development evidence 2 links Human and AI changes pass the same build, test, proof, model-check and compatibility gates, and the checks have independent failure signals.

Human and AI changes pass the same build, test, proof, model-check and compatibility gates, and the checks have independent failure signals.

A human maintainer and AI coding agents codeveloped krabka-broker. The development process does not use authorship as evidence of correctness. Pull requests apply the same build, test, proof, model-check, and compatibility gates to human and AI changes.

An AI-generated test can repeat the implementation's mistake. krabka therefore uses checks with independent failure signals. Mutation testing inserts faults that the tests must detect. Creusot sends contracts to automated provers, and Stateright explores bounded event orderings. Differential and acceptance suites also run live, digest-pinned Kafka brokers as reference systems.

The checks do not prove the whole broker. This catalog states the formal boundary in detail. The repository README describes the mutation and container tiers. Mutation testing stands in for authorship only while it is actually running, so the sweeps are scheduled weekly per crate and the mutation sweep baseline records the last run in which each swept crate came back with no survivor.

A mutation skip narrows that evidence, so the README's skipping a mutant rule bounds it. A function-level #[cfg_attr(test, mutants::skip)] is accepted for an I/O-only wrapper with no in-process signal, or for orchestration whose every branch calls into a separately mutation-tested kernel, and it must carry a // cargo-mutants: <reason> line saying which — aspect check-mutants-skip fails the build otherwise. An equivalent mutant of a single expression belongs in .cargo/mutants.toml's exclude_re instead, so the function's other mutants stay in the sweep. Integration coverage is explicitly not a reason: the sweep runs unit tests and ordinary tests/*.rs only, so a skip resting on the container suites removes precisely the code this tier would otherwise measure.

Scope Catalog boundary 1 link What belongs in the Creusot ledger, what belongs in the Stateright inventory, and what the closure audit found.

What belongs in the Creusot ledger, what belongs in the Stateright inventory, and what the closure audit found.

This catalog covers safety-critical, deterministic decisions implemented in this repository. These decisions admit, reject, select, fence, order, or recover state in KRaft, storage, security, and protocol handling. A decision belongs in the Creusot ledger when a contract can describe its safety rule. It belongs in the Stateright inventory when the rule depends on a bounded sequence of events, crashes, or interleavings. Host orchestration and external systems remain outside both tiers as described below.

The closure audit enumerated the workspace crates, traced every ledger caller and model entry point to production code, checked its proof artifact or bounded model and adapter tests, and searched the remaining mutation boundaries. Apart from the barrier coordinator partitioner named under Outside Both Tiers, the audit found no unlisted decision inside this boundary. A cross-check of the kernel source against the ledger found every public executable function of krabka-verified in a ledger row, and every kernel that a row names in the source. The tables below are the authoritative inventory; counts are not used because one row can contain several related kernels.

Every pull request runs the Creusot package-discovery check and all discovered proof-bearing packages. The same CI workflow explicitly runs the five Bazel test targets that contain every Stateright entry point in this inventory. The explicit run is separate from impact-based test selection, so an unrelated change cannot skip the model checks. A new proof-bearing package must pass the discovery check. A new Stateright entry point must be added to this inventory and to one of the explicit model-bearing targets, or that target list must be extended.

Reproduce Run the proofs yourself 2 links A pinned Creusot release, the commands that re-run every proof, and an unproved goal that fails CI.

A pinned Creusot release, the commands that re-run every proof, and an unproved goal that fails CI.

The .creusot-version file pins the Creusot release. That release pins the Rust nightly that creusot-rustc needs. Its creusot-setup/src/tools_versions_urls.rs file pins the prover binaries and checksums. Its creusot-deps.opam file pins Why3 and Why3find. The local why3find.json file selects the provers and sets the time limits, search depth, tactics, and proof-search profile. It does not set a memory limit.

Install the pinned Creusot release and its provers as the proofs job does. Then run these commands from the repository root:

aspect check-creusot-packages
export PATH="${HOME}/.local/share/creusot/bin:${PATH}"
packages=(krabka-verified)
for package in "${packages[@]}"; do
  cargo creusot --package "${package}"
done

The package check fails if a workspace package that depends on creusot-std or contains a Creusot contract is absent from the explicit proof list. It runs outside the proof image, which carries no Aspect CLI, and it also asserts that tools/creusot/prove.sh -- the image's entrypoint, and the loop above -- still names the same packages, so the list and the proofs cannot drift apart. Each cargo creusot command updates its package directory under verif/.

cargo creusot exits with a nonzero status when any goal is unproved. A deliberately false #[ensures] makes the run end with Error: <n> unproved files and exit status 1, and prove.sh runs under set -euo pipefail. An unproved goal therefore fails the CI proof job. CI checks that proof result, not a clean Git diff. Generated .coma files contain an absolute source path, so another checkout can change them without a change to the proof result.

Boundary Outside both tiers 1 link Protocol codecs, network and async orchestration, I/O and operational configuration are covered by tests, not by proofs or models.

Protocol codecs, network and async orchestration, I/O and operational configuration are covered by tests, not by proofs or models.

Code that is not in the Creusot ledger or the Stateright inventory is in neither tier. This includes most protocol encoding and decoding, authorization, network and async orchestration, file and object-store I/O, and operational configuration. Unit, property, integration, differential, and container tests cover those areas. Those tests are evidence, but they are not a Creusot proof or a Stateright model check.

The barrier coordinator's __barrier_state partitioner is the one coordinator routing decision without a proof. It applies Kafka's Utils.abs(murmur2(bytes)) % numPartitions over the group name through the broker's own murmur2, and JVM golden vectors test it. The group, transaction, and SHARE coordinator partitioners route through the proved java_string_hash_partition, and the KIP-405 metadata partitioner through remote_metadata_partition.

A proved kernel does not prove its caller. The proof starts after the caller establishes the preconditions in the ledger. A Stateright model does not prove the I/O around the decisions that it drives.

The shared SASL session kernels bind credential and positive listener deadlines and enforce expiry on ordinary requests, allowing reauthentication frames through the separate phase gate. An overflowing positive listener cap saturates instead of disappearing. Created-token composition derives coherent deadlines from creation, carries the captured expiry through both SCRAM rounds, and proves exact request admission plus token-API isolation. Credential-free composition proves that PLAIN, regular SCRAM and GSSAPI retain a finite listener ceiling, including clock overflow. Cryptographic success, faithful host snapshots and nondecreasing clocks are explicit external obligations; later token mutation is outside the captured snapshot.

OAuth validation completion binds the finishing clock observation to a still-current and fresh supplied JWKS snapshot. The common host validation path passes its returned completion time into session admission, so asynchronous validation consumes the token's advertised remaining lifetime. The consumer theorem composes initial cache admission, completion admission, session deadlines and reauthentication principal binding, with exact rejection and service conditions. Successful cryptographic validation, faithful claim/cache projection, accurate clocks and absence of generation ABA remain external obligations; absent cache observations carry no JWKS freshness claim.

Cache publication and OAuth admission connects the production publication-generation kernel to the cache guard, completion clock and initial controller session admission. For any length of exclusive-writer fetch outcomes, each installed key set advances the committed generation by two; failed fetches and exhaustion preserve keys. The final generation equals the initial generation exactly when no keys were installed. An unfinished writer enters an odd generation. This derives the absence of generation ABA for this publisher, instead of assuming it.

Given a stable starting snapshot and a valid unexpired initial credential with no broker cap, the composition authenticates exactly when no keys changed, no writer is in flight and hard cache expiry has not elapsed at completion. It also proves that the computed lifetime ends at the token expiry. A counter at its last even generation safely preserves the old cache and may still serve it while fresh. The controller now supplies the same publication atomics as the data listener; a signed-token wire fixture distinguishes a fresh cache from stale, never-published and in-flight metadata, and a validation fixture changes the observed generation after cryptographic success.

Exclusive CAS ownership, the complete ordered publication trace, faithful atomic/clock/claim observations, actual key replacement and cryptographic correctness remain host obligations. Failed or competing publishers cannot be omitted if they modify keys. This proves admission at authentication; the controller hands the stream to the raft engine and currently has no subsequent credential-expiry gate, despite OAuth advertising a finite lifetime.

Where the formal boundary ends

This is not a whole-system formal proof: Creusot proves kernels, and a proof applies only when the caller establishes the listed preconditions, while Stateright checks a model within its bounds and says nothing beyond them. Protocol encoding and decoding, network and async orchestration, file and object-store I/O, and operational configuration are covered by tests, not by proofs. The verification catalog in the broker repository states the boundary in detail and lists every proof and every model.