Verification
krabka uses two formal test tiers. Creusot proves contracts for small, executable kernels. Stateright checks all reachable states inside each model’s stated bounds. Neither tier proves the whole broker.
Development evidence
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.
Catalog Boundary
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.
Creusot Proof Ledger
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.
Cross-module theorems
The proof/test-only composition module
calls the existing kernels through their contracts and checks relationships across module boundaries.
All compositions export usable relational guarantees or witnesses. A historical
control replacing all fifteen then-remaining bodies with true proved those
files and passed all composition tests. Append, reservation, audit-loss settlement, scheduled delivery and read-committed Fetch compositions now return actual
computed witnesses, with exact admission and relational contracts plus
independent oracles. The
audit
records the historical weakness and repairs. The preceding thirteen-entry
control proved all affected files and passed all 53 then-existing composition
tests; read-committed Fetch has since been repaired. The twelve-entry checkpoint
control proved all twelve replacements and passed all 57 composition tests;
audit-loss settlement has since been repaired and consumed by marker admission.
Scheduled delivery now exports its greatest frontier to a transaction-stability
consumer proving the greatest prefix allowed by both gates and HWM; inherited
delivery/LSO values cannot replace those derived frontiers. Placement now exports
actual voter identities and installer admission into the exact rack-loss survivor
witness, which proves capacity for the original majority after one rack loss.
Offset-index validation exports exact byte cursors to a complete-batch seek
composition proving the indexed scan finds the same first match as a full scan;
structural row validity alone cannot establish truthful batch positions.
Validated epoch reconciliation exports its actual cut to retained snapshot
selection and replay; neither discarded-tail snapshots nor invented earlier
floors can suppress reconstruction. Constructed sparse rows now export attained
prefix maxima into retained-record selection and then cross-tier ListOffsets
visibility; a pruned match cannot suppress a later retained one. Validated remote
and local time scans now export actual cursors into a consumer that checks
prefix bounds against decoded records and returns the first retained match.
Completed logical trim now supplies the read floor independently of producer
replay progress; physical eviction supplies the cache frontier used to reroute
the same retained match to committed remote coverage. Validated time cursors now
compose with constructed prefix maxima into complete closed-interval scans;
equal maxima require a strict lower start, and later regressions prevent using
the upper cursor to bound the scan tail. The derived transaction/HW/delivery
offset prefix now gates this interval scan, preserving the first retained match
strictly below its maximal limit while ignoring inherited LSO. A minimum-equivalent
transaction projection preserves visibility but cannot validate omitted entries.
Remote interval coverage now derives whole-segment blocking for local retention
and composes with the size/time prefix and checked deletion successor. Its target
bounds every offset selected for eviction by an actual supplied remote interval,
including gaps between local segments. An independent progress guarantee selects
every unblocked prefix eligible by size or expiry, including their mixed handoff.
Independent prefix-byte bounds justify each selected non-expired row, excluding
paired selectors and reference models that skip the size pass or ignore its debt.
Selected segments remain covered at successor exhaustion. Complete truthful
metadata and durable physical deletion remain external.
Witness-returning compositions carry their aggregate guarantees in the exported
contracts. The connections are:
allocation, append, acknowledgement, recovery, and scan frontiers agree;
sequential reservations are adjacent and disjoint; consuming both frontier
contracts makes successive batches recover and acknowledge strictly in order;
transaction-derived LSO returns the greatest read-committed prefix bounded by
every unstable start, HW and delivery, and that prefix determines the complete
remote/local abort-row union; a newly advanced
consensus HWM gives the Fetch limit quorum support; archived offset-index row
validation establishes global ordering and bounded floor/ceiling lookups; and
replaying an audit settlement returns the exact conserved pending state before
generation exhaustion; its witness and marker admission preserve concurrent
losses, reject duplicate admission and permit the fresh remainder marker;
restored time-index cursors are monotone and remain within their segment;
validated epoch reconciliation cuts bound truncated consumer Fetch;
snapshot selection after truncation yields a replay cursor inside the surviving
log; narrowing Fetch returns witnesses for exactly all overlapping restored aborts;
remote/local source union preserves every qualifying wire abort row exactly once;
the actual maximum activation time is due exactly when every batch is due;
that aggregate theorem composes with a complete batch walk, delivery watermark,
and Fetch cap to hide waiting batches while replication remains ungated;
replica fencing, append/truncation, and HWM advancement preserve bounded Fetch;
and restored record selection respects batch spans and agrees with whole-batch
classification; actual prefix maxima construct sparse time-index rows; strict
predecessor scanning returns the global first matching record; and arbitrary
sparse-index construction agrees with a full scan even with timestamp regressions
and offset gaps; remote candidate selection preserves the same first-record
result with padding and unsorted conservative bounds; and validated remote and
local scan starts agree; completed trim steps converge without frontier
regression; admitted trim reconciliation preserves its caps, makes retry a
no-op, and bounds snapshot reload cursors; and diskless eviction reconciliation
preserves committed object coverage and HWM safety lag; the full WAL placement
fold establishes the quorum installer’s distinct identity invariant; and a
complete placement of at least three voters retains a majority after one
configured rack fails; installed voter validation, explicit durable votes, quorum
advancement, and consumer Fetch produce a concrete majority of distinct supporting
nodes whenever an advance exposes retained records. The unsynced leader end is
excluded as an extra vote. Watermark maximality and the exact HWM/LSO/delivery
minimum exclude stalled advancement or an artificially empty Fetch. The proved
explicit-vote count equivalence lemma
connects the host’s offset clamp to the same count model; floor-only/inherited-watermark
cases remain explicit. Checking actual copied batch bytes also composes with live
append, Produce acknowledgement, and recovery over an arbitrary-length prefix:
logical frontiers agree, byte progress equals the actual encoded lengths, and
complete valid copies that fit their file extent cannot be rejected wholesale.
Checkpoint admission also composes with whole-batch truncation and exact Fetch
clamping, preserving valid interior floors and treating empty recovery as a reset.
Covering-read validation and the trim planner then connect physical copying to
logical Fetch: every offset in the retained window has a concrete copied batch
with identical bytes, and every offset before either floor is excluded. Promotion
checkpoints a higher canonical floor in the follower before rebasing; an injected
first-append failure and reopen regression checks that ordering.
The byte-quorum composition
connects identity/epoch admission, validated actual WAL copies, observed durable
completion, installed identities, explicit quorum votes and consumer Fetch.
A fresh advance above the retained floor exports a distinct matching-copy
majority; complete valid copies must reach log end. The canonical reference is
validated through the covering-copy theorem, including an interior logical
floor. That theorem and its layout validator now export arbitrary batch order
and checked extents; WAL authorization and byte-count definitions are available
to crate-local proof consumers. Independent sorted-ledger tests and consistent
wrong-helper contracts reject mismatched or unfinished copies. Accurate decoded
bytes, current lineage, actual fsync/checkpoint completion, identity provenance
and inherited watermark evidence remain host obligations. The network leader
continues to trust authenticated offset reports; the model consumes inspected
copy evidence and does not prove that authentication makes those reports true.
The append/reservation witness composition
returns the actual computed append, reservation, recovery, acknowledgement and
scan results with exact admission and equality guarantees. Its pair consumer
calls those exported contracts to establish adjacent non-overlapping batch
ranges and strictly increasing acknowledgement frontiers. No batch bytes,
controller serialization or fsync/quorum completion are inferred from the
coordinate witnesses.
Complete transaction-index admission now returns the original selected row indices,
with exact overlap membership and invalid-index rejection. The
abort-source union composition
joins those witnesses to inclusive remote overlap, half-open local selection,
Fetch visibility, and the production
wire-row deduplicator.
It proves that every qualifying pair from either supplied source survives exactly
once, even when the marker owner lies beyond the data window. The
derived-stability composition
first obtains actual LSO and Fetch witnesses from the
transaction-state composition.
It replaces the inherited scalar LSO before selecting abort rows, so the same
maximum prefix bounded by every unstable start, HW, delivery and the requested
cut determines all qualifying unique rows from both indexes. Complete or
minimum-equivalent transaction projection, coherent lineage, index enumeration,
requested-floor authorization, bytes and client filtering remain host obligations.
RemoteReader therefore scans later finished indexes; remote Fetch also merges local index rows.
The native archive/eviction regression checks remote-only, local-only and duplicated
markers. Complete current-lineage enumeration, truthful objects, coherent frontiers,
and client-side record/control filtering remain external.
Exact restore selection now returns original row indices and a complete archived
frontier instead of a success flag. The
filtered-restore retry composition
connects those witnesses to actual rewrite-header/record admission, modulo
producer sequence arithmetic and snapshot retry reconstruction. Its returned
coordinates name every qualifying original record exactly once in source order.
The original retry sequence and acknowledgement frontier survive both partial
and empty rewrites, including wrap. The materializer keeps the original offset
span independently of survivor count; a native encoded restore/reopen fixture
checks recovered producer metadata and retry classification. This projects one
admitted last non-transactional data batch, with truthful ordered decoded rows;
encoding, snapshot durability, complete replay and PID routing remain external.
Actual control-key classification then composes with matching-producer closure,
abort intervals, strict marker/HWM release, and the maximal read-committed Fetch
prefix. Replicated control batches share native append’s semantic bookkeeping
while retaining their original wire bytes; the promotion regression compares
transaction and producer state before and after reopen.
The marker-admission composition
connects the production transaction-version fence and producer/coordinator
generation admission to that same control-marker pipeline. Rejected pending
markers preserve the previous Fetch limit and produce no abort interval.
Admitted markers retain their unstable start until HW passes the marker’s last
offset; afterward the prefix is maximal under all remaining starts and caps,
and strictly advances when no competing start or delivery cap blocks it.
COMMIT publishes offsets only on the offsets partition; ABORT returns its exact
interval and never publishes offsets. Complete PID-keyed pending state,
serialized application, successful durable append and actual offset publication
remain host obligations.
A completed trim now syncs retained bytes and publishes its WAL range before
native prefix removal or log-floor advancement. The publication-stage composition
then connects each old/new checkpoint to whole-batch recovery and exact Fetch
membership, including partial native floor advancement and a previously
uncheckpointed tail. A 24-case fault table checks the actual adapter.
Snapshot admission also composes with modulo-2^31 sequence reconstruction,
retained-batch retry classification, append coordinates, and acknowledgement
frontiers. For a snapshot-only reload, a same-epoch retry names the original
last batch and offset; older epochs fence and the successor sequence appends.
A real snapshot write/reopen test exercises that broker projection, including
sequence wraparound. PID lookup and decoding remain host obligations.
The truncation/snapshot composition now returns the selection and replay
cursor as witnesses. Its contracts state newest-survivor completeness and the
exact replay cursor, so bounds alone cannot justify losing a valid snapshot or
skipping the replayed tail.
Corrupt-snapshot fallback also composes with newest selection and exact replay
construction. It preserves original candidate identities across removals, skips
only corruption, stops on read I/O failure, and returns the exact fallback cursor
when no non-corrupt survivor exists. Read classifications and directory effects
remain host obligations; production tests distinguish corruption from I/O errors.
Faithful batch decoding and filesystem durability remain external.
Debt-first quota credit and full-request charge now compose with the grant kernel:
a charge and full refund restore the refilled signed balance and next consume
budget exactly when the debt is representable. Saturated or capped-away debt
makes unconditional restoration false. The bounded-debt composition proves the
exact balance after charge and repayment, debt clearance once credit reaches the
cap, and a guaranteed consume budget from credit beyond it. These are integer
credit guarantees; effective micro-token conversion, clock claims, rate/wait
rounding, sensor identity, and serialized publication remain host obligations.
The production refill now uses quota_refill to retain fractional micro-token
credit while claiming each nanosecond interval once. Its composition with
plan_consume proves that splitting an interval cannot create or lose a
consume budget, even while repaying debt or reaching the burst. A rational-time
ledger also fixes the exact final debt and fraction. The host preserves that
fraction across nonzero rate changes and discards it when capped; its native
regressions cover repeated clocks, rate changes, and large refills. Rate/wait
floating-point conversion, clock provenance, and atomic publication remain
outside the pure proof.
The production bounded-charge adapter now derives its integer debt cap with
quota_debt_cap. Rounding floating
micro-tokens upward could retain debt beyond the requested wait: one micro-token
per second retained one at a half-second bound, while the clock earned zero.
The cap now floors the credit earned by the wait converted to nanoseconds.
The elapsed-time composition
connects that cap to charge, partitioned refill, whole-token selection and grant.
It exports exact retained debt and final ledger, proves debt clears at the
converted deadline, and proves enough extra time grants the full probe. Exact
retention rejects a forget-all-debt implementation; deadline repayment rejects
rounding upward. Matching local contracts for each wrong helper still prove,
while the composition and independent native oracle fail. The real bucket
regression checks both retained debt and deadline clearance at fractional rates.
Rate, burst and quantum remain fixed, with no intervening operation; host time
conversion, clock provenance, serialized claims and publication stay external.
The arbitrary consume-trace composition joins that refill with the actual
whole-token request selector and plan_consume. Its scaled ledger accounts for
every grant, debt repayment, fractional credit, and credit discarded at the
burst across repeated or backward clocks. A final request covering the burst
must drain every whole token: conservation alone would allow permanent
starvation. Independent signed-ledger tests and a zero-grant negative control
check both properties. The trace uses a fixed positive configuration and
serialized steps; unlimited zero-rate grants, rate changes, and intervening
charges/refunds are outside its scope.
The timestamp/ListOffsets composition connects the actual sparse scan and
complete pending-transaction fold to isolation selection and the final response
clamp. It resolves exactly when a matching record exists in the visible window,
preserves global first-match identity through timestamp regressions and offset
gaps, and strictly excludes every pending transaction start for read-committed
consumers. Scanning the complete window before clamping agrees with finding
the first match inside the visible prefix: once the global first match lies
outside that prefix, no later record can be visible. This needs ordered offsets,
not ordered timestamps. Replica/debug requests retain their log-end bound. Complete window
decoding, truthful sparse bounds, cross-tier enumeration, epoch lookup, and
coherent captured state remain host obligations; scheduled-delivery Fetch/LATEST
caps are separate from positive-timestamp ListOffsets.
The retained-tier union composition then combines floor-aware first matches
with the production candidate merger and exclusive response clamp. It proves
the global first retained visible match across overlapping local/remote windows,
preserving the selected record timestamp rather than returning the request time.
A real upload-gap regression catches the former remote-first short circuit.
Both readers enforce the logical floor, including a cut inside one batch;
per-tier object enumeration, byte decoding, and coherent capture remain external.
Their sessions are under
verif/krabka_verified_rlib/composition.
They add no production runtime work. The audit states each theorem’s remaining
identity, serialization, completeness, and durability assumptions; none proves
the surrounding I/O or extends a bounded model to arbitrary executions.
A coherent recovered data window composes that snapshot-row theorem with the five-slot producer decision and acknowledgement arithmetic. It returns the first matching row’s original physical span and exclusive frontier, even with sequence wrap aliases, and derives non-duplicate policy from the newest row. A real sparse log spans an entire sequence wrap and checks the older alias after snapshot-plus-tail replay. Complete replay, window generation/eviction, faithful metadata projection, timestamps, and persisted bytes remain host facts; a snapshot stores only the last batch, so arbitrary restart equivalence is not claimed.
Token-description isolation composes a complete User-owner ACL scan with connection admission and token visibility. Matching permission rows grant only All/DescribeTokens, a matching deny wins, and any resource ACL suppresses the no-ACL fallback even when its principal or operation does not match. Token authentication and an excluding owner filter refuse visibility despite owner, requester, renewer, superuser, or ACL privilege. Faithful matching facts, complete enumeration, token-service configuration, the independent Describe grant for this token, HMAC generation, and response encoding remain host obligations.
Production kernels
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.
| Kernel and contract | Host caller | Proof session | Caller preconditions |
|---|---|---|---|
acl_, acl_, and acl_ prove principal and host wildcards, literal and prefixed resource applicability, exact operations, All, and Kafka’s one-way implication arrows gated on is_; acl_ then pins every Acl variant to Kafka’s Acl order: super-user bypass, explicit deny precedence, ACL-backed allow, and allow.’s no-ACL-at-all allow, over the named Acl (resource applicability takes the named Acl). |
The shared krabka-authz applicability adapter and precedence loop |
identity proof, resource proof, operation proof, precedence proof |
The host computes exact string equality, the canonical User:*, host and literal-resource * wildcards, str::, resource-type equality, and exhaustively maps every metadata operation and pattern variant. matches_ maps is_ exactly to permission == Permission, 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. Acl is Acl exactly when allow_, where has_ 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_ 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. |
The broker’s shared Connection adapter |
proof. |
The host maps each connection phase exactly and passes the Kafka API key wire value unchanged. |
opa_ reuses the cached allow or deny unchanged exactly while its deadline is strictly in the future. opa_ 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_ maps the configured outage policy exactly. |
The broker’s OPA authorizer and cache key | admission proof, expiry proof, and error-policy proof |
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_ (Std 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_ selects Kafka’s first present exact, default, or combined user/client candidate. ip_ selects an exact IP before the IP default. |
The broker’s shared quota lookup adapter, used by request, produce, fetch, and controller-mutation enforcement | user/ and IP proof |
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 Entity 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/ |
spool_ admits an audit frame without overflowing the byte cap and advances the fsync cadence without wrapping. |
krabka-audit |
proof. |
sync_; unsynced < sync_. |
settle_ 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. |
Pending after the writer syncs a marker, and Pending at Spool::, with the batch parsed from the spool’s marker whose loss_ matches the sidecar’s; the spool_ drives it for both |
proof. |
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:: saturates instead of advancing; reaching it takes 2^64 settlements. The host holds the pending state under one mutex so a concurrent Audit loss lands either before or after the settlement, persists the result to the audit. sidecar, and parses marker JSON; parse failures mean no marker. |
audit_ admits a signed checkpoint only for the exact head and predecessor sequence of a nonempty verified chain. audit_ admits exactly a marker with the two-field {records_ shape, a positive count, and a generation strictly newer than the last accepted one, and returns that generation. |
The audit partition checkpoint and loss-marker walk | checkpoint proof and loss-marker proof |
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. |
fetch_ 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_) the last stable offset, and every fetcher is told the partition’s high watermark and min(lso, hw), as Kafka’s Partition. reports them. |
krabka-broker |
proof. |
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::, 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_ rejects a follower Fetch response row unless its in-flight request epoch, current metadata target, and any reported leader identity all agree (replica_), then selects exactly one of retry (nonzero error code), truncation (KIP-320 diverging epoch >= 0, Kafka’s Fetch), 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. |
The broker follower replicator’s Fetch loop and response adapter, before any response-derived mutation | proof. |
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_ 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_ admits a preferred-leader batch only when it is nonempty, at most max_ long, and every change installs the partition’s first assigned replica while that replica is alive, in the ISR, and not a witness (preferred_). |
The broker’s KIP-460 preferred-leader rebalance tick, immediately before controller submission | proof. |
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_ (Kafka’s MAX_) as the cap. The host rejects duplicate topic-partition keys. There is no imbalance-ratio gate to admit against: Kafka’s leader. 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_ 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_ orders WAL before local application, takes the maximum observed frontier on retry, and reports completion only when the WAL and local frontiers are equal. |
The Delete offset adapter and partition-writer trim reconciliation |
admission proof, application proof |
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_ computes a nonnegative, saturating backlog. |
krabka-broker |
proof. |
None. |
unclean_ 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. |
The broker’s Unclean post-poll commit path |
proof. |
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_ 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. |
The broker’s Find authorization adapter, before internal-topic creation or coordinator resolution |
proof. |
The host validates canonical SHARE composite keys and maps SHARE to Cluster on the singleton Cluster resource; only kernel allow outcomes enter resolution. |
java_ implements Java UTF-16 String., Kafka’s Utils., and positive partition modulo exactly. |
The broker’s group coordinator partitioner, transaction coordinator partitioner, and SHARE coordinator partitioner | proof. |
All three adapters pass str:: 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. |
classic_ admits a classic-to-consumer migration exactly for the consumer protocol when every member subscription decodes; consumer_ 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_ 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. |
The unified coordinator’s upgrade adapter, downgrade adapter, upgrade trigger, and downgrade trigger | upgrade proof, downgrade proof, and record-plan proof |
The upgrade adapter requires exact protocol_ 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/ |
jwks_ 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_ 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_), stamping now without signed overflow; every other input is rate-limited. |
The broker’s JWKS refresher and OAUTHBEARER validator boundary | cache proof and rate-limit proof |
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_ 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. |
The SASL deadline handoff, Raft and controller request gate, before bootstrap/ |
guard proof and history proof |
The host preserves the computed deadline, uses the existing epoch clock and captures current request grants. Plain/ |
sasl_ selects the earlier credential or positive listener cap, retaining overflowed caps at i64::; session_ refuses ordinary APIs at or after that deadline while allowing reauthentication frames. |
The shared SASL session adapter and connection request gate, used by PLAIN, SCRAM and GSSAPI session creation | deadline proof, request proof, token consumer, and credential-free consumer |
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_ admits completion exactly for a nonnegative forward clock and a still-current fresh supplied cache snapshot, evaluating its age at completion. |
The common OAuth validation boundary, called by both initial authentication and reauthentication | completion proof and consumer proof |
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_ admits exactly the sessions oauth_ 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_), plus the corresponding absolute expiry. |
The OAUTHBEARER handler’s initial-authentication and reauthentication transitions | proof. |
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. |
uniform_ returns Kafka’s minimum and remaining as the Euclidean floor and remainder: minimum * members + extras == total and extras < members. |
The unified coordinator’s uniform group assignor, once for each homogeneous assignment | proof. |
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_ states Kafka’s Uniform 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_ (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. |
The unified coordinator’s uniform group assignor, once for each homogeneous assignment | proof. |
The host passes the split from uniform_ (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:: 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_ returns no index exactly for an empty candidate list. Otherwise it returns an in-bounds candidate that no other candidate precedes under load_ (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 Member when no partition is taken away. |
The unified coordinator’s uniform group assignor, once for each unassigned partition in the heterogeneous builder’s assign |
proof. |
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 sort order, and assigns the partition only to the chosen subscriber. A property test checks that the kernel’s picks equal a port of next. The final balancing pass (balance) is an unverified step-for-step port of Kafka’s Member, covered by Kafka’s Uniform 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. |
chain_ accepts exactly the expected sequence and opaque previous head, advances by one, and rejects sequence exhaustion instead of wrapping. |
The audit partition verifier and spool resume walk; the krabka-remote-storage WORM verifier |
proof. |
The host computes head_ 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_ admits only a present signature whose named key is externally trusted and whose canonical binding verifies; worm_ 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. |
The WORM verifier’s signature adapter, object adapter, and chain walk | signature proof, object-set proof, and the shared chain-step proof |
The host reads manifests through the fixed byte cap, rejects unknown JSON fields and format versions, recomputes the chain head from canonical_, 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_ selects the eligible WORM receipt with the greatest (start offset, sequence) rank; chain_ rejects writer-side sequence exhaustion instead of repeating u64::. |
The krabka-remote-storage WORM writer and the broker archive copy loop |
proof. |
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. |
compute_ equals compute_, the sum of the timestamp and the retention time clamped to the i64 bounds. |
krabka-verified calls it from retain_; krabka-log also calls it in tests. |
proof. |
None. |
retain_ implements the KIP-534 record and control-marker retention rules. |
krabka-log |
proof. |
None. |
compaction_ treats zero remaining input as the only clean end-of-stream and requires every successful batch decode to consume bytes. |
krabka-log, shared by compaction’s map and rewrite passes |
proof. |
The host passes exact cursor lengths and the Record result; corruption is surfaced before swap promotion. |
remote_ 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. |
The broker’s shared coverage adapter, used by local physical retention and copy resumption | proof. |
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_ returns exactly the length that the reference fold local_ gives. That fold is Kafka’s Unified: a retention. 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. 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 deletable does, and never takes a newest segment that is empty (is, local_); 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_ returns exactly remote_, which is Kafka’s Remote 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::. A tier that accepts no delete selects nothing. retention_ advances an inclusive last offset without signed overflow and pins every result variant. |
The log’s local retention tick and the broker’s local and remote KIP-405 retention walks | local prefix proof, remote prefix proof, target proof |
The local tick passes one Local 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. breach measured from Kafka’s largest (newest record timestamp, or the . 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. 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 delete does, then removes only the selected bases from memory and indexes, attempts their required and optional files and their producer snapshots (delete); file-delete errors leave recoverable orphan files but cannot expand the selected prefix. Log does not know the high watermark, so Kafka’s high 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 Copy segments; its time axis is Kafka’s max (absent while that cutoff is negative), and its debt is Kafka’s build total: remote bytes plus the local bytes above the highest remote offset (only). |
published_ 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. |
Executable cross-module proof witness | proof. |
Initial stores and previous floor respect committed/ |
remote_ 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. |
The broker’s remote retention completion loop | step proof, selection-to-completion composition |
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. |
election_ keeps deterministic jitter in [0, base_. |
krabka-kraft-core |
proof. |
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_ implements the KIP-595 candidate-log ordering. |
krabka-kraft-core |
proof. |
None. |
vote_ accepts exactly when vote_ holds (no negative KIP-595 voter, candidate, or epoch field; zero is a real value); vote_ accepts exactly when vote_ holds (every unsigned field is at most the signed int32 maximum); vote_ classifies a Vote and a Vote 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. |
The raft Vote codec and kraft-core Vote handler | decode proof, encode proof, admission proof |
The decoder also requires exactly one _ 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_ accepts exactly when fits_ holds: schema v0 or v1, and the term and optional leader, vote, and v0 voter identities fit their signed fields without clamping; quorum_ restores only the exact no-vote sentinel or a nonnegative voted key from a valid version-specific layout. |
KRaft’s quorum-state persistence adapter | write proof, load proof |
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_ admits one Voters member only with a nonnegative ID, byte-exact directory identity, a nonempty valid endpoint set, and a non-inverted nonnegative kraft. range; voter_ admits only record version 0 with a nonempty set of unique voter IDs. |
KRaft’s voter_ control-record adapter |
voter proof, set proof |
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, Voter 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_ computes floor(voters / 2) + 1; election_ admits only that many unique grants from the current voter set. |
krabka-kraft-core |
majority_, election_ |
The host filters the grant set to unique current voters before passing its count. |
failover_ maps a Failover (leader dead, ISR shrunk, the three-state Live classification, and the Out) to one action class by a fixed precedence for a dead leader – clean ISR election, then (only when Live) an eligible-leader-replica election, then the last known leader (Kafka’s can, an unclean election that neither unclean. nor unclean. 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. |
The broker’s shared leader-failover policy | proof. |
The host follows Kafka’s generate for one fenced broker: it visits only a partition whose ISR holds that broker, builds the target ISR by removing that broker alone (Replicas.), and reports the leader as needing replacement (leader_) whenever it is not an acceptable member of that target ISR (is), 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 target, is a host choice justified in the rustdoc of failover_: in any state Kafka reaches the two coincide. |
feature_ decides every Update feature row and equals the reference decision feature_: it returns the first failed rule in the order of Kafka 4.3.1’s Feature (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., update’s refusal of a downgrade across a level whose did is set, with a distinct decision for SAFE_ and UNSAFE_; for kraft., an upgrade handed to Raft, a downgrade refused, and a downgrade type at the current level a no-op; for every other feature, Feature.’s KIP-1022 dependencies. An admitted metadata. row that crosses a changed level is never a downgrade. |
The Update request planner |
proof. |
The host puts the rows into a map keyed by feature name, as Kafka’s Quorum does, so a repeated name keeps its last row, and it walks that map in java. iteration order; the first rejected row fails the whole request and nothing is written. It decodes the wire Upgrade 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. 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.), computes check from the broker-local did table in features., and evaluates dependencies against the finalized levels with every update of the request applied. It maps each rejection to Kafka’s INVALID_ 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. |
select_ returns None exactly for no candidates, and otherwise an in-bounds index that ranks_ every candidate, where ranks_ is the lexicographic order: higher last-written leader epoch, then higher log end offset, then lower broker ID. |
The broker’s KIP-966 unclean-recovery selector | proof. |
The host preserves response order while adapting each report to a Recovery and maps the proved index back to that same report. |
reassignment_ waits until completion_ holds (Kafka’s Partition 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. |
The broker’s partition-reassignment policy | proof. |
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_ proves the pointwise union, addition, and removal classification for a start plan; reassignment_ admits a start or cancel mutation only after its target/RF or approval/ |
The Alter per-row planner, before constructing a controller record |
set proof, admission proof |
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_ 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). |
The broker’s heartbeat validation | heartbeat proof |
Heartbeat validation reads the stored epoch from one image, answers Missing and Stale with STALE_ as Kafka’s Cluster 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 Broker 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. |
recompute_ computes the majority offset over the leader’s log end (when leader_) and follower offsets, applies the Raft/KIP-595 epoch-start gate, and does not regress below current_. |
krabka-kraft-core; the krabka-broker WAL live engine, with leader_ 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_ |
proof. |
1 <= majority <= followers + 1; `leader_counts |
majority_ returns the greater of current and the majority frontier (the greatest member of {log_ that majority members reach, the leader always counted), with no epoch gate. |
The krabka-broker WAL recovery harness, as the length upper bound of the byte-agreed prefix; the diskless crash model |
proof. |
1 <= majority <= followers + 1; current <= log_; each follower offset is at most log_. Recovery passes the longest replica end as the leader, every other end as a follower, and current = 0. |
break_ applies the fail-closed withdrawn, consumed, expiry, approval, and signature checks in order over the named Break; select_ returns the first index of the lexicographically least (expiry, UUID high, UUID low) key (is_/lex_), so an exact duplicate key keeps the earlier candidate. break_ 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_ permits a gated local action only after its matching consume committed, including an already-committed retry. |
The broker’s break-glass gate and local-action persistence adapter, used by Delete and offset-aware unclean recovery; the KRaft submission fence |
admission proof, selection proof, consumption proof, local-action proof |
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_. 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_ checks the symmetric signature-skew interval without machine overflow. freeze_ returns the first failure from the ordered key, key-principal, connection-principal, timestamp, replacement, and signature rules. freeze_ makes literal scope beat every prefix and makes the longest matching prefix win. freeze_ exhaustively classifies the mutation inventory, and freeze_ gives authorization denial precedence over freeze detail before applying that classification. freeze_ 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 In (the only retryable refusal), and otherwise Append. |
The topic-freeze signature verifier, shared scope and mutation resolver, Produce, Add, Delete, Delete, reassignment alter and completion, compaction, tier retention, and the KRaft replacement fence |
skew proof, signature proof, scope proof, inventory proof, mutation proof, replacement proof |
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. |
directory_ emits a change only for the reporting broker’s exact replica slot and makes an already-current assignment a no-op; directory_ accepts controller success only while the responding controller remains the current leader. |
The Assign request-to-delta planner and broker directory-report sender |
planning proof, response proof |
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 Partition 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. |
diskless_ advances local log start only behind both the committed object projection and the lagged high watermark, without subtraction overflow or regression. |
The diskless WAL object flusher | proof. |
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_ selects the indexed range covering a requested offset or the immediate successor after a gap; diskless_ extends exactly when the next range is in the same object, contiguous, and fits the response cap; diskless_ 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. |
The diskless WAL cold-read index lookup and batch selector | logical range proof, span proof, batch proof |
The host copies ranges in BTree order and rejects invalid, overlapping, or unordered logical ranges before calling the selection kernel. Object-key equality is exact. Object reads, index completeness, decoded Record 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_ 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 Unified log-start-offset, size (diff - segment, so retention. never deletes past its budget) and time (now - largest) predicates. |
The diskless WAL flusher’s retention pass through the WAL-index projection | proof. |
The host passes the projection’s ranges in BTree (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. and retention. are None for Kafka’s unlimited sentinel, and the floor is the partition’s Delete floor, not the local log start. A horizon now - retention. 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. |
exact_ returns the exact signed successor only below the caller’s maximum and rejects the maximum or any value above it. |
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. |
None. The host passes i32:: for Kafka int32 metadata fields and i64:: 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. |
barrier_ 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. |
The barrier coordinator’s retention selector; barrier epoch allocation uses the separately proved exact_ adapter above |
proof. |
Barrier retention is count-based, as persisted in Group; 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. |
create_, renew_, expire_, and token_ implement Kafka’s KIP-48 deadline selection, with saturating sums and no resurrection of expired tokens, and authentication admission. |
The broker’s create, renew, expire, and SCRAM paths | create proof, renew proof, expire proof, authentication proof |
The handlers supply the configured lifetime, exact image deadlines, and current wall-clock timestamp. |
token_ 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. |
The broker’s renew, expire, and expiry sweep adapters, plus the controller’s atomic admission path | proof., and boundary renewal/ |
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_ requires a non-anonymous authenticated identity for every token API and refuses a token-authenticated session on all four, Describe included. |
The broker’s shared Connection adapter, called by all four delegation-token handlers before token access or mutation |
proof. |
The host maps only Auth and unestablished connection states to an absent authenticated identity. |
token_ takes five booleans (owner_, caller_, caller_, caller_, acl_) and returns exactly `owner_ |
caller_ |
||
scram_ selects the credential store from the client’s tokenauth SCRAM extension alone, as Kafka’s Scram 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 Delegation prepares a token credential for every Scram); 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. |
The broker’s SCRAM round-one credential lookup | proof. |
The host parses tokenauth from the client-first extensions as Kafka’s Scram (Boolean., last duplicate wins), performs exact metadata lookups, derives token liveness through the separately proved token_ kernel, and synthesizes the token’s credential for the negotiated mechanism. |
epoch_ is Kafka’s Leader: its single postcondition equates the answer with the kafka_ 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. |
krabka-log, whose epoch_ backs the broker’s Offset rows and Fetch diverging_ |
proof. |
Entries have strictly increasing leader epochs and start offsets. The host keeps that order through Leader (Kafka’s Leader), which drops every trailing entry the new one does not strictly follow before recording it; the log’s append paths and Partition::, which records a new leader epoch at the log end as Partition. does, both write through it. Log:: trims entries at or past the log end even when no record is discarded, as Unified does. The host maps (-1, -1) to the wire: verbatim on an Offset row, OFFSET_ on a Fetch (Partition.). |
offset_ returns the position for the greatest relative offset at or below the target. |
krabka-log |
proof. |
Entries have strictly increasing relative offsets. |
time_ returns the last indexed offset with a timestamp strictly below the target, or zero when none exists, including i64::. It reuses the binary search above. |
Time, used by both segment timestamp searches |
proof. |
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_ returns the position for the least relative offset at or above the target. |
krabka-log |
proof. |
Entries have strictly increasing relative offsets. |
time_ returns the relative offset for the last timestamp at or below the target. |
The strict scan-start kernel below | proof. |
Entries have nondecreasing timestamps. |
timestamp_ chooses an append stamp verbatim or checked producer base-plus-delta time. Ignored producer overflow never rejects append time. |
Local batch timestamp decoding and remote encoded-record scanning | timestamp-type proof, raw-record visibility composition |
The host projects Some(batch. 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/ |
first_ selects the first decoded record at or after both the logical floor and target timestamp; earliest_ selects the earliest maximum timestamp; timestamp_ derives absolute record coordinates without signed overflow; timestamp_ advances strictly past a fresh decoded batch; and timestamp_ doubles only a positive representable retry window. |
The local segment timestamp scan, for indexed lookup and maximum-timestamp recovery | first-match proof, earliest-maximum proof, coordinate proof, cursor proof, window proof |
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_ accepts exactly sorted, nonoverlapping, non-inverted inclusive ranges; stamp_ finds a boundary-preserving insertion point or rejects overlap; exact_ selects only an exact pair of range boundaries for idempotent replacement or removal; and covering_ is equivalent to inclusive coverage. |
The local log’s commit-stamp sidecar, across open, append, transactional upsert, startup removal, and point lookup | validation proof, insertion proof, exact-range proof, coverage proof |
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. |
leader_ is Kafka’s Partition. + Unified: it returns the current watermark unchanged while the ISR (leader counted) is below the effective min., 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 (should); 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. |
krabka-broker |
proof. |
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 truncate), and the Leader the ISR scan last installed from the metadata image: effective_ (the controller’s threshold, so the watermark stops exactly where KIP-966 starts naming eligible leaders; the broker’s static default_ fallback is not consulted), replica., 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 Replica; controlled shutdown is not in the image and is left to the controller’s Alter eligibility check. |
isr_ keeps the assigned leader, drops an unassigned member, keeps an in-sync follower exactly while Kafka’s Replica holds (Partition.), and admits an out-of-sync follower exactly when Kafka’s Partition. would: its log end reaches the leader’s high watermark and the leader’s epoch start offset (is) and replica_ holds (is: not fenced, not shutting down, fetch broker epoch -1 or equal to the alive broker epoch, both present); isr_ suppresses exactly proposals with no unique additions or removals. follower_ chooses Kafka’s Replica. 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_, the scan rule that isr_ replaced, keeps its proof and has no broker caller; only the leader_ still uses it to re-admit a revived follower. |
The broker’s ISR maintenance proposal builder; leader_ also calls replica_; the data-path model drives the expansion arm; follower progress in replica_ takes the caught-up credit |
selection proof, eligibility proof, credit proof, superseded-rule proof, no-op proof |
The replica-state installation seeds a new ISR member as caught up at installation and not yet fetched (Kafka’s reset); 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::, 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 BTree 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_ lock, and derives “within replica.” from the scan’s single Instant. Eligibility facts come from the Leader the scan installs and the fetch-recorded broker epoch; the image carries no controlled-shutdown state, so shutting_ is always false on the leader and the controller’s INELIGIBLE_ check covers it. |
isr_ is exactly Kafka’s Replication order after the unknown-partition check: a leader or partition epoch above the controller’s answers NOT_, an older leader epoch FENCED_, a requester other than the leader INVALID_, an older partition epoch INVALID_, an invalid ISR, an ISR without the leader, or an illegal recovery state INVALID_, an ineligible member INELIGIBLE_; it admits exactly when both epochs match and every other fact holds, so a stale proposal never overwrites a newer ISR. |
The broker’s Alter row adapter |
proof. |
The host reads both current epochs and the leader from one image snapshot, compares the request’s broker with the leader, classifies the ISR as Kafka’s Replicas. 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_), 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_. |
wal_ requires the current leader marker to be committed before allocation; wal_ extends only an exact positive, non-overflowing chain of uncommitted reservations; reserve_ 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_ admits only a matching request, valid range, and current leader epoch. |
The KRaft controller’s reservation submit path and the broker’s WAL offset sequencer | epoch-readiness proof, pending-frontier proof, allocation proof, and response proof |
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 Change. 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. |
producer_ is Kafka’s client-append producer check (Unified): at the entry’s epoch it first searches the retained batches’ sequence ranges (Producer) 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 check/check (no entry accepts any sequence, except that under the host’s trunk_ 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_ and decrement_ return the operands’ sum or difference reduced modulo 2^31 for every i32 input, which is Kafka’s Default/decrement on its nonnegative domain. |
krabka-broker, which maps a duplicate’s index back to the retained batch it names; increment_ is also called by krabka-log and the broker tracker, and decrement_ by the tracker entry. |
decision proof, increment proof, decrement proof |
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 maybe does). log_ is the partition’s log end offset being 0, which stands for Kafka’s map: 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_ is unstable.. |
producer_ picks Kafka Log’s reload log start (the checkpointed floor, 0 when absent, under remote storage; otherwise the local log start); producer_ keeps exactly the snapshots in (log start, log end], Kafka truncate’s rule (the snapshot at the log start is deleted); producer_ deletes exactly what Kafka remove deletes: a snapshot at no segment base unless it is the newest and above every base; producer_ selects the greatest offset the reload keeps without relying on directory order; producer_ equals snapshot_ over a named Producer (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_ equals Kafka rebuild’s max(map and rejects a snapshot the reload would have deleted. |
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_. |
reload-log-start proof, reload-range proof, stray proof, selection proof, entry proof, replay-start proof, and batch-cursor proof |
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 rebuild then take); 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 Log). CRC, byte decoding, directory enumeration, and Hash 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. |
local_ accepts only nonnegative strictly ordered segment bases; local_ closes each sealed range exactly before the next base; local_ advances one maximal valid byte-and-offset prefix without overlap or overflow; local_ admits a recovered segment only when its exact exclusive frontier, relative to the segment base, lies in [0, u32::; local_ aborts a compaction swap exactly when its . still exists, completes it exactly when its . exists without one (the swap_ rule of Kafka’s Log: 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_ selects exactly the segments a completed swap deletes: its own base and every base in [swap base, swap next offset). |
The log’s compaction swap and swap recovery, segment-chain open, and active-segment torn-tail scan and sparse-index rebuild | chain proof, sealed-boundary proof, batch-step proof, index-frontier proof, swap-action proof, and swap-replaces proof |
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 Local: the rewrite writes . files, atomic_ fsyncs them, renames the sidecars and then the log to . 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 . rename is durable, which is the host fact that makes Abort safe. Recovery computes a committed swap’s next offset by walking its batch headers with local_ 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 . files. Crash-point tests crash atomic_ 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. |
produce_ 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. |
The broker’s Produce batch preparation | proof. |
CRC validation runs first; the host maps the decoded control and timestamp-type attributes exactly. |
producer_ 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. |
The broker’s shared producer-ID allocator, called by Allocate and local producer identity allocation |
decision proof, decision clone proof, and plan clone proof |
The host maps the request broker ID and epoch, the current registered epoch, and the committed next_ exactly. This adapter is the broker’s only production producer of Producer and submits the proved plan.; 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_ returns exactly base offset + last offset delta + 1, rejects negative inputs, and fails closed on signed overflow. |
The broker’s shared Produce durability adapter, used by the fresh-append acknowledgement path and idempotent duplicate path | proof. |
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_ 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. |
advance_ advances a KRaft HWM monotonically without passing log end; in_ and frontier_ define commit, fetch, apply, and waiter boundaries. |
krabka-raft and its controller offset rules |
advance_, in_, frontier_ |
The HWM bound requires the host invariant previous <= log_; the predicates accept all integers. |
control_ 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. |
The KRaft controller’s voter and feature control history, for point lookup, truncation, and commitment | proof. |
The host copies keys from a BTree, 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_ returns, for a nonempty metadata batch whose final delta fits Kafka’s signed last, exactly the contiguous record deltas 0. (one per record, deltas[i] == i), and rejects an empty batch or a count past i32::. 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. |
The KRaft controller’s metadata_ builder |
proof. |
The host passes the exact slice length, zips the returned deltas with the records in order, and takes the batch’s last 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_ 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 maybe). No mutation depends on who the responder is assumed to be. |
The KRaft controller’s on_ 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 Begin path, as Kafka’s maybe does, and applies nothing else from it; one that names no leader is ignored (Kafka moves to Unattached in that epoch) |
proof. |
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, Error, and mutation-presence flags unchanged. On Discover and on the newer-epoch path it binds only the leader’s own Node entry (the one whose node id is Current), never the responder’s address; without one, the transport keeps the leader’s voter-set listener. Serving replicas answer as Kafka’s try: validate first (FENCED_, UNKNOWN_, NOT_), each refusal carrying the known leader, epoch, and the leader’s endpoint. Wire decoding itself is outside Creusot. |
voter_ returns exactly the first rejection of the reference voter_, which follows the order of Kafka’s Kafka leader check, Add, Remove, Update, and maybe (not leader; pending change; leader epoch uncommitted; uncommitted control record; then per operation: add needs kraft. 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 Voter 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_ (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. |
The KRaft controller’s on_ adapter before any voter-state mutation or control-record append |
proof. |
The host takes membership and kraft. 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_. 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_: the kernel’s voter_ (the exact stored key and at least two voters) is Kafka’s Voter condition, and Remove answers every refused removal with VOTER_. 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_ before the kernel, as Kafka does, so a request key always carries a known directory. |
constructed_ constructs exactly the admitted logical membership; reconfigured_ derives actual distinct grant counts and returns a real shared voter whenever both adjacent majorities exist. |
Executable composition of the KRaft reconfiguration policy, unique-ID validator and consensus quorum kernel, mirroring the controller’s single set operation | membership proof and overlap proof | 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/ |
reconfiguration_ 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. |
Executable composition of KRaft admission, metadata batch deltas, local append coordinates and logical quorum overlap | control-prefix proof | 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_ 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. |
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 | control commitment proof | The supplied log end and previous/ |
whole_ 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. |
The KRaft log truncation and both controller truncation paths use the retained log end; the composition reuses storage retention/ |
proof. |
Complete decoded batch ends, ordered truthful control histories, safe divergence/ |
truncated_ 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. |
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. |
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_ 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. |
Snapshot offset/entry admission, replay cursor, physical truncation, the shared rebuilt_, producer decisions and acknowledgements compose. Real log/tracker regressions cover seed eviction and a snapshot-carried retry below the local floor. |
composition proof and window proof |
Faithful loaded snapshot identity/ |
completed_ 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. |
The live tracker and bounded Stateright model use producer_; 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. |
selection proof, insertion lemma, pair lemma, slot witness, source membership, unchanged-window coverage, composition proof | 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_ 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. |
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. | eviction proof, completion proof |
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_ 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. |
The actual identity-transition and initialization-identity kernels compose with completed-window selection, snapshot-row validity and exact data-epoch fencing. | handoff proof, completion proof |
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_ 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. |
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. | trace proof, handoff proof |
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_ 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. |
Two actual identity transitions compose with initialization classification and partition sequence/epoch admission across separate PID entries. | rotation proof |
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. |
replay_ applies a record, at offset base + delta, exactly when the replay_ 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_ advances only on strict progress; replay_ 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_ retains only the first downgrade. |
The KRaft controller’s committed data, control-state, and control-record recovery walks, the group coordinator’s _ replay, and the barrier coordinator’s _ replay |
record proof, cursor proof, batch cursor proof, downgrade proof |
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_ 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_ 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. |
The barrier coordinator’s ordered _ fold and interrupted-injection recovery |
fold proof, finalization proof |
The host decodes each record, selects the BTree 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_ 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. |
barrier_ accepts only a positive, exactly representable target-count extension. barrier_ equals its reference rule: a missing image entry or negative epoch is Malformed, then a different leader in the image or installed partition is Not, then a different epoch is Fenced, and only an exact generation is Append; the receiver maps Not to NOT_ and the other two refusals to FENCED_. barrier_ admits only requested, nonnegative marker offsets. barrier_ makes a cut complete exactly when its exact target fold has no missing or malformed target. |
The barrier target expansion and cut builder, the local and remote marker fan-out, the linearized local append, and the receiving Write handler |
target-count proof, marker-fence proof, placement proof, classification proof |
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. |
remote_ 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. |
krabka-remote-storage-topic |
proof. |
Uuid:: 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_ 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::. |
The topic-backed remote-metadata manager’s snapshot adapter, used by both startup restoration and dynamic assignment | proof. |
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/-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 Not and retry through the existing reconciler. |
tiered_ selects a valid finished remote segment with the minimum start; tiered_ selects one with the maximum inclusive end; and tiered_ selects the nonnegative leader epoch with the greatest in-segment start at or below that end. |
The broker remote reader’s tiered List adapter |
earliest proof, latest proof, and epoch proof |
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. |
earliest_ selects the least candidate offset, preserves its attached timestamp, prefers remote on ties, and returns no candidate exactly when both tiers found none. first_ finds the first decoded record meeting both the logical offset floor and timestamp. |
The broker’s timestamp merger and the log’s windowed timestamp scan; remote reads pass the same logical floor to the record decoder | candidate proof, floor proof, and tier-union composition |
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_ classifies versioned timestamp sentinels; list_ applies the request leader-epoch fence; list_ 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_ selects the minimum available local, remote, and diskless start; and list_ equals the reference rule list_ on every variant: EARLIEST/EARLIEST_ resolve to their start unclamped, LATEST to min(candidate, last_, and every record-derived kind to its candidate only when strictly below last_ (otherwise unknown), as Partition. does. |
The broker’s end-to-end List partition resolver, legacy v0 resolver, and leader-epoch adapter |
kind proof, epoch proof, bound proof, earliest proof, and selection proof, with the timestamp visibility composition |
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. |
remote_ 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. |
The remote-storage metadata cache and broker remote Fetch reader | proof. |
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_ computes an inclusive bounded-fetch end without wrapping and uses an open end for zero, exact-segment, or oversized caps. |
The remote-storage Offset range adapter |
proof. |
The host saturates usize caps above u32:: before delegation, which preserves the open-ended result. |
remote_ 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. |
The remote-storage Time floor adapter |
proof. |
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_ classifies inclusive aborted-transaction and Fetch ranges exactly, rejecting either inverted interval. |
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. |
The host decodes each big-endian entry field exactly and rejects a torn partial entry before calling the kernel. |
unique_ retains every supplied wire (producer_ pair exactly once, introduces no pairs, and never grows the list. |
Broker remote Fetch, after merging overlapping remote-tail and local transaction indexes | deduplication proof, complete source-union proof |
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. |
restore_ accepts only a non-overlapping archived batch base and computes its exclusive end without overflow, while permitting gaps preserved by Kafka compaction; restore_ validates each record delta and derives its absolute offset and its Kafka timestamp (kafka_: base_ under Create, the batch max_ under Log, as Default substitutes log) without overflow; restore_ keeps exactly records at or below an inclusive offset bound and strictly before an exclusive timestamp bound when no OR-combined exclusion (restore_ over producer, offset-range, key, and header matches) holds; restore_ makes keep, empty, and filter equivalent to the folded record decisions; and restore_ stops an ordered batch walk exactly when the next base is past its partition bound. |
The restore verifier’s record-batch walk, the compiled bound and record filter, and the materializer’s batch preparation and ordered segment walk | batch proof, coordinate proof, record-selection proof, batch-fold proof, and batch-skip proof |
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 Restore, computes producer, half-open offset-range, UTF-8 key-regex, and header-regex matches into Restore 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/ |
restore_ admits exactly the legal_ and legal_ 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_ compaction of a discarded marker); and a non-idempotent sentinel, idempotent data, or transaction-marker producer tuple. restore_ admits exactly the restore_ records: every retained record delta advances strictly inside that span, and its Kafka timestamp (kafka_, so max_ itself under Log, where the producer’s deltas are never read) is representable and at most the preserved maximum timestamp. |
The restore materializer’s owned-batch preflight and segment accounting and append | header proof and record proof, complete selection witnesses, and filtered-restore retry composition |
The host fully decodes a filtered data batch, preserves survivor order and the archived offset span, derives records_ 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_ 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_ and restore_ require strict sparse-index coordinates and verified log bounds; restore_ requires Kafka’s Transaction order: each abort marker’s last_ inside the segment and strictly increasing across entries, with 0 <= start_ and a nonnegative producer ID, while start_ may precede the segment base (a transaction spanning a roll) and need not increase (interleaved transactions); restore_ requires nonnegative strictly ordered segment-scoped epochs and starts; and restore_ makes sorted producer IDs unique. Each producer state also passes producer_ at the segment’s exact exclusive frontier. |
The restore verifier’s index, leader-epoch, and producer-snapshot adapters | frontier proof, offset-index proof, time-index proof, transaction-index proof, leader-epoch proof, producer-ID proof, and the shared producer-state proof |
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_s 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 Time 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_ 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_ classifies each archive-scan observation against a Restore (missing, live, delete-started, delete-finished) into Restore: 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. |
The restore inventory’s archive-source reconciliation | proof. |
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. |
schema_ permits unvalidated records only when fail-open is configured and the registry failure is transient. |
The broker schema validator’s registry classifier and admission path | proof. |
The host classifies transport errors, HTTP 408/429, and 5xx as transient; unknown IDs, other statuses, and malformed successful responses fail closed. |
schema_ returns an ID exactly for magic zero, and that ID is the four schema-ID bytes in big-endian order. schema_ checks only configured, non-null key or value fields under the matching subject role. schema_ admits exactly a complete walk in which every applicable field was admitted. |
The schema validator’s framing and subject adapter and the Produce whole-batch walk | framing proof, field proof, and batch proof |
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. Topic 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/ |
scram_ 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. |
The Alter validation adapter and request planner |
proof. |
The adapter passes str::, 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. |
snapshot_ 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. |
KRaft’s follower Snapshot |
proof. |
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. Bytes allocation and copy success, plus the engine’s documented discard of state after Restart, remain host and file-I/O assumptions. |
snapshot_ installs only a nonnegative future snapshot identity when no mandatory downgrade snapshot is pending, classifying equal or older boundaries as stale; snapshot_ permits pruning only at a nonnegative boundary at or below the committed frontier. |
KRaft’s follower snapshot installer and leader checkpoint-prune path | install proof, prune proof |
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. |
latest_ selects a maximum (end offset, epoch) id; checkpoint_ proves the lexicographic order and checkpoint_ retains exactly the checkpoints whose full id equals one of the two ids it is given, and no partial match of either. |
KRaft’s checkpoint directory scanner and cleanup | selection proof, ordering proof, retention proof |
The host’s recent_ supplies the two newest ids by applying latest_ twice (the selected id, then the runner-up among the rest), so a Fetch 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>. 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. |
share_ returns the minimum latest-snapshot offset across every supplied live share key, proving both lower-bound safety and the maximal safe prune point. |
The share coordinator’s _ pruning adapter |
proof. |
The caller freezes every live key mapped to one state partition, then supplies each exact last_. 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_ 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. |
The share-group actor’s serialized reset and delete adapter, called by Alter and Delete |
proof. |
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. |
site_ returns what the loss of a fullest site leaves under the round-robin placement model round_ (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_, lemma_). |
krabka-verified calls it from min_; the krabka-broker stretch model calls it directly. |
proof. |
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_ returns true exactly when, under the round-robin placement model, no site holds min_ replicas and every single-site loss leaves at least min_. |
krabka-broker |
proof. |
1 <= rf <= 1024; 1 <= sites <= 1024. Round-robin placement is a host assumption, as above. |
quorum_ checks that each one-site loss leaves a strict voter majority. |
The krabka-broker stretch model; no production caller. |
proof. |
At most 1024 sites; each site has between 0 and 1024 voters. The companion lemma_ proves that no two-site split ever passes. |
plan_ 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. |
krabka-throttle |
proof. |
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_ checks that protocol. |
quota_ floors the micro-token credit earned during the converted wait, clipped at the representable debt limit, including wide-product overflow. |
Bucket, shared by bounded charges |
proof. and elapsed-time composition |
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_ selects exactly the minimum of requested whole tokens and whole tokens available, even when multiplication saturates. |
whole_, shared by consumes and the bounded bucket model |
proof. and consume-trace composition |
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_ 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:: through debt repayment. |
Bucket, used by consumes, charges, and rate changes |
proof. and refill-partition composition |
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_ repays debt before adding refill/refund credit to the burst-capped balance; quota_ charges the effective request in full and caps the unpaid debt. Both preserve the coherent balance/debt shape. |
krabka-throttle, for actual refill, record/ |
credit proof, charge proof, and the charge/refund and bounded-repayment compositions |
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. |
transaction_ answers Already exactly when the live entry holds the intended completion identity and state, Proceed exactly when it holds the prepared identity and state, Reject exactly when it is neither complete nor holds the prepared identity, and Reject exactly when it holds the prepared identity in any other state. |
The broker’s End reacquisition guard and completion decision; the reaper and completion-task kernel transaction_ also calls it |
proof. |
prepare_ (#[requires]): the host passes one Kafka Prepare* -> Complete* pairing, from decide_ or completion_. The host maps Kafka transaction states and producer identities exactly into primitives. |
transaction_ pins all five outcomes: Reject exactly when a snapshot names a negative PID or epoch; otherwise Already exactly when the live entry equals the intended completion, Reject 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 Reject in every remaining case. It delegates to transaction_ rather than restating it. |
The transaction coordinator’s idle-reaper completion adapter, the prepared-transaction completion task, and serialized transaction-state persistence | decision proof and clone proof |
prepared. (#[requires]): the host passes a completion_ pairing or the constant (Prepare. The host compares the complete persisted Txn, 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 Prepare 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_ 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 _; replay_ bounds every transactional-offset scan step and rejects overlap, negative deltas, range escape, and signed overflow. |
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. | marker decision proof, clone proof, and scan-step proof |
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_ 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. |
The transaction coordinator’s staged log replay and publication and shared PID-index adapter | decision proof and clone proof |
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_ 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. |
The transaction coordinator’s shared partition-registration adapter, called by Add, Txn, and the transactional Produce fence |
proof. |
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_ 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. |
The transactional Init sub-path, under the same entry lock as each recovery, abort, or epoch bump it admits |
proof. |
The host passes the wire producer_ and producer_ 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_ and prev_ are what the host records on every bump and rotation, as Kafka’s prepare and prepare do, and the abort path that fails its marker fan-out records the epoch the producer still holds. last_ is persisted in Transaction (#892) only under unstable., 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 Init. At TV_ 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 is clause, txn, which holds only for metadata handle 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_ 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_ 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_ closes state only for an unambiguous COMMIT or ABORT belonging to a pending producer; aborted_ admits only an ordered inclusive interval for a live producer; and aborted_ is equivalent to intersection with a nonempty half-open Fetch range. |
The local log’s owned transaction marker and LSO path, append path, verbatim replication path, recovery replay, and local aborted-transaction index | LSO proof, marker proof, interval proof, overlap proof |
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_. 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_ preserves unverified identities, increments normal and recovery epochs within their distinct bounds, and returns the supplied producer ID at the rotation boundary. |
The broker’s End producer-identity adapter |
proof. |
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_ is exactly Kafka’s Transaction rule: it aborts if and only if the transaction is ongoing, its timeout is not the KIP-939 2PC sentinel i32::, 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 Init persists. |
The broker’s shared 2PC timeout and reaper adapter, called by the idle reaper | proof. |
None: the contract accepts every timeout, start, and clock value, including a zero or negative timeout replayed from the transaction log, which Kafka’s Transaction also accepts unchecked. The host maps only Txn to the kernel’s ongoing state. |
future_ permits a future-log swap only when the current and future log-end offsets are exactly equal, preventing both truncation and unreplicated suffix promotion. |
The broker’s future-log swap guard | proof. |
The host holds both log locks and supplies their exact log-end offsets. |
local_ selects the exact sealed-segment prefix and active-segment disposition at an exclusive cut; truncation_ rejects negative, reversed, and u32-overflowing relative offsets; truncation_ keeps exactly decoded batches ending before the cut; truncation_ clamps dependent frontiers to the actual retained log end. |
The local log’s truncation coordinator, segment truncation walk, and transaction-index rewrite | plan proof, relative-offset proof, batch-boundary proof, frontier proof |
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_ 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 append does. |
The local log’s owned append coordinator, verbatim append coordinator, segment write path, and rollback path | proof. |
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_ and remote_ equal Kafka’s Remote and Remote: a forward lifecycle edge or an idempotent self transition, and, for partition deletion with no prior state, only DELETE_. |
The remote-storage segment state and partition-delete state adapters | segment proof, partition-delete proof |
The host maps its Rust enums onto the shared Remote and Remote kernel enums; no numeric tags remain. Exhaustive production tests cover every source and destination pair. |
remote_ 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 Remote, which admits every self transition: here a self transition is a no-op only for an exact retry and is otherwise rejected. |
The remote-metadata primary cache and derived epoch-index adapter | proof. |
The host passes the cached state as Some(Remote, 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. |
diskless_ permits deletion only after grace has elapsed and the locked projection has no reference. |
The diskless object reclaimer | reclaim proof |
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. |
wal_ 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. |
krabka-broker |
proof. |
The host maps the authenticated identity and current placement exactly into primitive node IDs and voter order. |
wal_ is true exactly when both inclusive offset coordinates and every encoded batch byte agree; equal lengths or offset frontiers alone are insufficient. |
Production WAL follower promotion; the local byte-majority recovery harness | proof. |
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_ 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. |
The diskless WAL exact_ gate used by source and replica readers |
proof. |
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. |
log_ distinguishes data, ABORT, COMMIT, barrier, and unknown control batches from the actual control flag and first record key. |
The log’s shared control classifier, used by native append and recovery; replicated controls enter shared bookkeeping through verbatim append. | classifier proof and marker/Fetch composition | 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. |
wal_ 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_ additionally exports an independent global layout and per-batch interval bounds. |
The promotion covering reader and physical-copy/ |
covering proof and copy/trim/Fetch composition | 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_ 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. |
WAL follower checkpoint recovery | checkpoint guard and checkpoint/ |
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. |
scheduled_ 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_ returns exactly min(max(candidate, clamp(current, log_: the cached watermark clamped into live log bounds, advanced to the scanned candidate when it is ahead, and never past the log end; coalesce_ merges exactly overlapping or adjacent inclusive ranges and returns their maximum end without successor overflow. |
The log’s local activation walk and watermark, range fold, and the broker’s remote fetch gate | visibility proof, watermark proof, coalescing proof |
Configuration supplies a nonnegative millisecond clock-uncertainty bound; local and remote paths use the same broker clock reading and decoded batch max_. Storage supplies valid ordered batch ranges and log_. Retention or truncation may first clamp a stale watermark down to the new live range; without a range change, each proved step is monotonic. |
select_ returns the first candidate, in the caller’s order, that the voter_ 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. |
The verified select_ fold, called by the broker’s diskless WAL placement policy |
proof. |
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_ 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. |
The broker’s WAL placement adapter | proof. |
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_ 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. |
The WAL engine’s quorum installation boundary | proof. |
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. |
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.
Run the Creusot Proofs
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.
Stateright Model-Check Tier
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.
The current model entry points are:
| Area | Model entry points |
|---|---|
| Broker data and Fetch | data_, producer_, replica_, fetch_, and fetch_. |
| Broker failover and placement | leader_, reassignment_, stretch_, diskless_, and client_. |
| Broker quorum WAL | quorum WAL model. |
| Broker groups and transactions | classic_, consumer_, reconciler_, coordinator replay model, streams-group reconciliation model, decision_, eos_, and two_. |
| Broker share and break-glass | share-group membership model, share_, break_, and break_. |
| Log | compact_ and leader_. |
| KRaft | kraft_. |
| Throttle | bucket_. |
| Audit | spool_. |
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.
The paragraphs below take the entry points in table order. Each one names the production code the model drives, what it models instead, its bounds, its properties and reachability witnesses, and its pinned counts, and it ends with what stays outside the model.
Broker Data and Fetch
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.
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.
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.
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.
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
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.
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.
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.
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.
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
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
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.
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.
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.
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.
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.
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.
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.
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.
Broker Share and Break-Glass
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.
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.
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.
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
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.
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
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
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
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.
Outside Both Tiers
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.