Skip to main content

Kafka KIP compatibility matrix

This matrix records what the tree claims about Kafka compatibility and where the evidence for each claim is: one row per KIP that the roster in kip_inventory lists or that any file under crates/ names, then the repository’s JVM differential evidence, which is the Kafka on-disk log contract and the ApiVersions table every client negotiates against.

KIP status

Every row comes from KIP_ANNOTATIONS in api_catalog, and two sources decide which rows have to be there. The generator scans crates/ for KIP-<n> mentions, except the crate CHANGELOGs, and fails when a KIP is named without a row here, so a compatibility claim cannot enter src/ without evidence beside it. It also reads kip_inventory, the checked-in roster of the KIPs the compatibility target covers, and fails when one of them has no row – the direction the scan cannot see, because code that mentions its KIP in no comment would otherwise go unadvertised here. A row for a KIP that neither the roster nor crates/ names fails as well, and so does a row whose owner or test does not exist, or a path::function entry naming a function the file does not define.

  • Status is Implemented, Partial (the note says what is missing), or Out of scope (the note cites the decision).
  • Owner is the module that holds the behavior.
  • Tests establish the status. A test under src/ is a unit or model test.
  • Kafka image is what the listed suites run against, read from each crate’s BUILD.bazel docker map and pinned below; in process means no suite in the row starts a Kafka container.
  • librdkafka is the non-JVM client evidence, and it is read from the row’s own tests: a row is credited with a client exactly when a test it lists in librdkafka_conformance drives that client. The suite’s CLIENTS table names each one, and the cell names the test that ran it, so a row citing the test that drives only the librdkafka 2.x image is credited with that image and not the older one.
KIP Status Contract Owner Tests Kafka image librdkafka Notes
KIP-13 Implemented Producer and consumer byte-rate quotas broker/src/quota/mod.rs broker/tests/client_quotas.rs
broker/tests/client_quotas/throttling.rs
in process none
KIP-32 Implemented Message timestamps in the v1 message set and the v2 batch records-legacy/src/lib.rs log/tests/integration.rs::read_jvm_produced_log_dir
log/tests/integration.rs::jvm_consumes_rust_written_log_dir
broker/tests/legacy_fetch.rs
cp_kafka_6_1_1 none The v0 and v1 message sets reach the wire only through Fetch v0-v3 and Produce v0-v2, which Kafka 4.x refuses and krabka serves only under the [runtime] key legacy_request_versions_enable (default off). Produce v3 and up still up-converts a legacy message set it carries.
KIP-48 Implemented Delegation tokens: create, renew, expire, describe and SCRAM token auth broker/src/handlers/create_delegation_token.rs broker/tests/delegation_tokens.rs
broker/tests/jvm_acceptance_quotas/delegation_tokens.rs
cp_kafka_6_1_1, cp_kafka_7_5_0 none
KIP-62 Implemented Classic group state machine with the AwaitingSync stage broker/src/coordinator/unified/classic_state/group.rs broker/tests/group_protocol_negotiation.rs
broker/tests/unit/consumer_group.rs
in process none
KIP-73 Implemented Replication throttling through the leader and follower throttle keys broker/src/throttle/mod.rs broker/tests/throttle.rs in process none The measured throttled-replication rate is published as krabka_broker_replication_throttled_bytes_out and krabka_broker_replication_throttled_bytes_in, which stand for Kafka’s kafka.server:type=LeaderReplication,name=byte-rate and its FollowerReplication twin, with krabka_broker_replication_throttle_sleeps for the rounds the throttle held back entirely. Kafka delays a throttled fetch; krabka drops the partition from the round and the follower re-asks, so there is no throttle_time_ms to attribute and the byte-rate is what says whether the throttle is biting.
KIP-74 Implemented Fetch response size limits: max_bytes and partition_max_bytes log/src/log/read.rs log/src/log/read/tests.rs::a_read_never_skips_past_a_segment_its_budget_clipped
log/src/log/read/tests.rs::a_descriptor_read_never_skips_past_a_segment_its_budget_clipped
broker/tests/replication.rs::a_follower_that_catches_up_across_segments_copies_every_batch
in process none Only the first batch of a read can be larger than the budget, so a fetch always makes progress, and a read that the budget stops inside a segment does not go on into the next one.
KIP-98 Implemented Transactions and idempotent producers, with transactional-id expiry broker/src/txn/coordinator.rs broker/tests/transactions.rs
broker/tests/transactions/txn_fencing.rs
broker/tests/jvm_streams_app.rs
broker/tests/jvm_connect_distributed.rs
broker/tests/librdkafka_conformance.rs::next_gen_group_topic_ids_and_telemetry_with_librdkafka_2x
broker/src/handlers/produce/producer_checks.rs::a_producer_with_no_state_on_a_never_appended_partition_starts_at_zero_under_trunk
apache_kafka_4_3_1, cp_kafka_7_5_0 kcat librdkafka-2.15.0 (librdkafka 2.15.0): next_gen_group_topic_ids_and_telemetry_with_librdkafka_2x The idempotent-producer sequence check is Kafka 4.3.1’s: a producer with no state may start at any sequence. Kafka trunk’s KAFKA-15591 rule, which answers OUT_OF_ORDER_SEQUENCE_NUMBER to a non-zero first sequence from a producer with no state on a partition that has never held a record, applies only under unstable.api.versions.enable.
KIP-101 Implemented Leader-epoch based truncation for followers broker/src/replicator/truncation.rs broker/tests/leader_epoch.rs
broker/tests/leader_epoch/epoch_diverge_leader.rs
broker/tests/leader_epoch/epoch_fencing.rs
in process none
KIP-107 Implemented DeleteRecords admin request broker/src/handlers/delete_records.rs broker/tests/client_admin_delete_records.rs
broker/tests/admin_handlers/admin_delete_records.rs
in process none
KIP-108 Implemented A create-topic policy refusing a topic with POLICY_VIOLATION broker/src/topic_policy.rs broker/src/topic_policy.rs
broker/tests/admin_handlers/admin_topic_policy.rs
broker/tests/topic_freeze/wire.rs
in process none The policy is the declared [topic_policy] rule set rather than a Java class named by create.topic.policy.class.name: a replication-factor floor, a partition ceiling, a min.insync.replicas floor, and required / forbidden config values. It runs where Kafka calls CreateTopicPolicy.validate — after config validation, before the records are generated — on validate-only requests too. A frozen topic answers with the same error 44.
KIP-110 Implemented Zstandard compression in the v2 record batch broker/src/handlers/fetch_downconvert.rs broker/tests/recompression.rs
broker/tests/legacy_fetch.rs
in process none A zstd batch is re-compressed as snappy for a v0 or v1 fetch, because those formats never carried zstd. Those fetch versions are served only under legacy_request_versions_enable.
KIP-112 Implemented JBOD: a broker with an offline log directory stays registered broker/src/handlers/broker_heartbeat/failover.rs broker/tests/jbod_disk_failure.rs
broker/tests/offline_replicas.rs
in process none
KIP-113 Implemented AlterReplicaLogDirs and DescribeLogDirs: replica moves between log directories broker/src/future_log.rs broker/tests/alter_replica_log_dirs.rs
broker/tests/jbod.rs
broker/tests/jvm_acceptance_quotas/log_dirs.rs
cp_kafka_6_1_1, cp_kafka_7_5_0 none
KIP-124 Implemented Request-rate quotas as a percentage of handler time broker/src/quota/request.rs broker/tests/client_quotas/throttling.rs
broker/src/network/dispatch/tests/throttle_mute.rs
in process none
KIP-133 Implemented An alter-config policy refusing a topic config change with POLICY_VIOLATION broker/src/topic_policy.rs broker/src/handlers/alter_configs/topic_configs.rs
broker/src/handlers/incremental_alter_configs/topic_scope.rs
broker/tests/admin_handlers/admin_topic_policy.rs
in process none The same [topic_policy] rule set stands in for the class named by alter.config.policy.class.name. Both alter paths check the resolved post-change config map, as AlterConfigPolicy.validate does; its RequestMetadata carries no partition count and no replication factor, so those two rules apply to CreateTopics alone.
KIP-207 Implemented The high watermark a new leader reports may regress after an election broker/src/handlers/list_offsets/resolve.rs broker/src/data_path_model/model.rs
broker/src/handlers/list_offsets/resolve.rs::offset_not_available_follows_partition_fetch_offset_for_timestamp
in process none The exhaustive data-path model checks durability without a watermark monotonicity assertion, which is what the KIP allows. ListOffsets fences a client lookup while the leader’s epoch start is above its high watermark, as Partition.fetchOffsetForTimestamp does: OFFSET_NOT_AVAILABLE from v5, LEADER_NOT_AVAILABLE below.
KIP-211 Implemented Committed-offset retention measured from the group’s last activity broker/src/coordinator/retention.rs broker/tests/offsets_retention.rs in process none
KIP-219 Implemented Respond first, then mute the channel for the throttle time broker/src/network/dispatch/response.rs broker/tests/client_quotas/throttling.rs
broker/src/network/dispatch/throttle_audit.rs::throttle_echo_divergences_are_the_recorded_ones
in process none Every API a request quota can hold on the ordinary dispatch path reports the delay it was held for: the dispatch loop patches a leading ThrottleTimeMs, and Produce, Fetch and ApiVersions – whose schemas bury the field behind an array – charge the quota in the handler and set it on the typed response instead. The dispatch loop decodes and encodes again the other buried-field responses (the delegation-token APIs and OffsetDelete) to set the field. The throttle-echo section below lists the buried-field APIs and what each one’s RequestQuotaPolicy costs.
KIP-226 Implemented DescribeConfigs reports the source of every value broker/src/handlers/describe_configs.rs broker/tests/jvm_acceptance_cli/configs.rs apache_kafka_4_3_1, cp_kafka_6_1_1, cp_kafka_7_5_0 none A broker resource is checked against Kafka 4.3.1’s whole KafkaConfig key roster: a key that is not dynamic is refused, and every dynamic key is parsed and range-checked. Dynamic keys with no krabka behaviour behind them (thread pool sizes, the log cleaner, connection limits, SSL and SASL listener settings, listeners) are stored and reported and do not change how the broker runs, and a CLASS value is accepted without loading the class.
KIP-227 Implemented Incremental fetch sessions broker/src/fetch_session.rs broker/tests/fetch_session.rs in process none
KIP-255 Implemented SASL/OAUTHBEARER broker/src/network/auth/oauthbearer.rs broker/tests/auth_handlers/oauthbearer.rs in process none
KIP-257 Implemented Quota entities keyed by user, client id, or both broker/src/quota/lookup.rs broker/tests/client_quotas.rs
broker/tests/tuple_quota_enforcement.rs
in process none
KIP-279 Implemented Leader-epoch truncation that converges after several leader changes log/src/leader_epoch_checkpoint/lookup.rs log/src/leader_epoch_model.rs
broker/tests/leader_epoch.rs
in process none
KIP-290 Implemented Prefixed ACL patterns and the MATCH filter broker/src/handlers/acl_wire.rs broker/tests/acl_handlers.rs in process none
KIP-320 Implemented Leader epochs in Fetch and ListOffsets, and OffsetForLeaderEpoch for consumers broker/src/handlers/offset_for_leader_epoch.rs broker/tests/jvm_kip320_divergence.rs
broker/tests/jvm_kip320_divergence/wire_conformance.rs
broker/tests/consumer_proactive_validation.rs
apache_kafka_4_0_0, apache_kafka_4_3_1, cp_kafka_6_1_1, cp_kafka_7_5_0 none
KIP-345 Implemented Static consumer-group membership broker/src/coordinator/unified/classic_state/membership.rs broker/tests/static_membership.rs
broker/tests/jvm_acceptance_cli/console_groups.rs
apache_kafka_4_3_1, cp_kafka_6_1_1, cp_kafka_7_5_0 none
KIP-360 Implemented Epoch bump when a transactional producer re-initialises broker/src/handlers/init_producer_id/transactional.rs broker/tests/transactions/txn_fencing.rs::init_producer_id_fences_a_stale_producer_identity
broker/tests/jvm_acceptance_durability/transactional_eos.rs::transactional_console_producer_eos
apache_kafka_4_3_1, cp_kafka_6_1_1, cp_kafka_7_5_0 none
KIP-368 Implemented SASL re-authentication and session expiry broker/src/network/dispatch/session.rs broker/tests/auth_handlers/oauthbearer_sessions.rs
broker/tests/auth_handlers/plain.rs::plain_session_capped_by_connections_max_reauth_then_closes
broker/tests/auth_handlers/scram.rs::scram_session_capped_by_connections_max_reauth_then_closes
broker/tests/jvm_acceptance_sasl/scram.rs::jvm_sasl_scram_sha512_in_band_reauth_under_max_reauth_window
apache_kafka_4_3_1, cp_kafka_6_1_1, cp_kafka_7_5_0 none connections.max.reauth.ms bounds every mechanism, and PLAIN, SCRAM and GSSAPI all re-authenticate in band under it. GSSAPI carries no automated re-auth case, because the suite has no KDC.
KIP-371 Implemented ssl.principal.mapping.rules maps an mTLS Subject DN to a principal broker/src/network/auth/ssl_principal_mapper.rs broker/src/network/auth/ssl_principal_mapper.rs::kafka_documented_rules_map_the_subject_dn
broker/src/file_config/listener.rs::apply_to_listener_parses_principal_mapping_rules
broker/tests/jvm_acceptance_tls/mtls_principal_mapping.rs
cp_kafka_6_1_1, cp_kafka_7_5_0 none The rules are per listener, under [listeners.tls_config]. Kafka’s broker-wide ssl.principal.mapping.rules and its listener.name.<name>. prefixed form are not read from server_properties. As in Kafka, the rules map the Subject DN on SSL listeners only, and patterns and replacements follow java.util.regex; a SASL_SSL listener takes its principal from the SASL exchange. The patterns are read as java.util.regex reads them through a rewrite for fancy_regex: (?i) folds ASCII case only, (?iu) folds by Character.toLowerCase of Character.toUpperCase, in a character class as well, and the POSIX classes such as Alpha and Punct are ASCII unless (?U) is on. One residue remains, documented in the java_fold module: a backreference under a case flag compares as fancy_regex does. Under (?i) it matches a non-ASCII letter in the other case, where Java’s does not, and under (?iu) it does not match i with İ or ı, or a letter with a case that takes another number of bytes, such as s with ſ, where Java’s does.
KIP-373 Implemented Delegation tokens for other users: the USER resource type and the CREATE_TOKENS and DESCRIBE_TOKENS operations broker/src/handlers/create_delegation_token.rs broker/src/handlers/create_delegation_token/tests.rs::create_tokens_acl_admits_minting_for_that_owner_only
broker/src/handlers/describe_delegation_token/tests.rs::describe_tokens_acl_on_the_owner_grants_all_of_their_tokens
broker/src/handlers/acl_wire/binding_filter/tests.rs::exact_user_token_filter_matches_the_stored_binding
in process none
KIP-382 Implemented MirrorMaker 2 replicates a Kafka cluster onto krabka broker/src/handlers/offset_commit.rs broker/tests/mirror_maker2.rs::mirror_maker2_migrates_a_kafka_cluster_onto_krabka apache_kafka_4_3_1 none The stock connect-mirror-maker.sh of apache/kafka:4.3.1 mirrors a broker of that release onto krabka: records with their headers, MM2’s compacted heartbeats, checkpoints and offset-syncs topics, a consumer group’s translated position, and a retention.ms carried over by sync.topic.configs. sync.topic.acls is left at its default; because neither cluster in the suite has an authorizer, MM2 skips the sync at the source, and the target-side CreateAcls krabka would answer SECURITY_DISABLED is asserted directly. docs/operations/migrate-from-kafka.md is the cutover procedure. Kafka trunk’s four newest topic keys (remote.copy.lag.ms, remote.copy.lag.bytes, max.decompressed.message.bytes, errors.deadletterqueue.group.enable) are unknown topic configs by default, as they are on 4.3.1, so a replay from a trunk cluster that sets one fails as it does against a 4.3.1 broker; unstable.api.versions.enable accepts and describes them. With the flag on, remote.copy.lag.ms and remote.copy.lag.bytes hold a sealed segment back from the remote tier (KIP-1241), and max.decompressed.message.bytes refuses a compressed record above it wherever the broker decompresses one, as trunk does: on produce with INVALID_RECORD; in log compaction, where it fails the pass and leaves the partition uncleanable until the limit changes, where trunk holds it until a broker restart; and in a by-timestamp or MAX_TIMESTAMP ListOffsets on the local log or the remote tier, which answers INVALID_RECORD for a record it has to read. Follower replication is not checked, as in trunk. errors.deadletterqueue.topic.name, errors.deadletterqueue.copy.record.enable and the three *.assignor.offload.enable group keys are accepted on a group with trunk’s validation and stored, and nothing reads them yet.
KIP-392 Implemented Fetch from the closest replica broker/src/replica_selector.rs broker/tests/kip_392_fetch_from_follower.rs in process none
KIP-394 Implemented JoinGroup member-id bootstrap with MEMBER_ID_REQUIRED broker/src/coordinator/unified/classic_ops/join.rs broker/tests/group_protocol_negotiation.rs in process none
KIP-405 Implemented Tiered storage: remote segment copy, read, retention and metadata broker/src/remote_log_manager.rs broker/tests/jvm_acceptance_tiered.rs
broker/tests/tiered_storage_multi_broker.rs
remote-storage/tests/gcs_emulator.rs
remote-storage/tests/jvm_tiered_storage.rs
restore/tests/roundtrip.rs
restore/tests/roundtrip/consume.rs
cp_kafka_6_1_1, cp_kafka_7_8_8, apache_kafka_4_0_0 none The tier’s traffic and lag are published per topic under the krabka_broker_remote_* names, which stand for Kafka’s BrokerTopicMetrics RemoteCopyBytesPerSec, RemoteFetchBytesPerSec, the three Remote*RequestsPerSec and Remote*ErrorsPerSec meters, and the four Remote*Lag* gauges. The bounded reader pool and the on-disk index cache report krabka_broker_remote_log_reader_task_queue_size, _avg_idle_percent and _fetch_duration_seconds for Kafka’s RemoteLogManager gauges, plus rejection and cache hit / miss counters Kafka has no counterpart for. The GCS lane of the evidence, gcs_emulator.rs, covers the native [remote_storage.gcs] backend’s reads, deletes and WORM startup gate against an emulator; it does not cover a GCS copy, because object_store writes an object with the Cloud Storage XML API and no GCS emulator serves that PUT. The copy path is covered against MinIO and InMemory instead.
KIP-412 Implemented Dynamic broker log levels through the BROKER_LOGGER resource broker/src/handlers/incremental_alter_configs.rs broker/tests/jvm_broker_loggers.rs
broker/tests/broker_logger_config.rs
apache_kafka_4_3_1 none
KIP-429 Implemented Cooperative rebalance protocol negotiation in JoinGroup broker/src/coordinator/unified/classic_ops/join.rs broker/tests/group_protocol_negotiation.rs
broker/tests/jvm_acceptance_cli/console_groups.rs
apache_kafka_4_3_1, cp_kafka_6_1_1, cp_kafka_7_5_0 none
KIP-430 Implemented Authorized-operations bitfields in Metadata, DescribeGroups and DescribeCluster broker/src/handlers/authorized_operations.rs broker/tests/authorized_operations.rs in process none
KIP-447 Implemented OffsetFetch require_stable and the UNSTABLE_OFFSET_COMMIT answer broker/src/handlers/offset_fetch/unstable.rs broker/tests/txn_offset_commit_materialize.rs
broker/src/handlers/offset_fetch/tests.rs
in process none
KIP-455 Implemented AlterPartitionReassignments and ListPartitionReassignments broker/src/reassignment.rs broker/tests/partition_reassignment.rs
broker/tests/jvm_acceptance_reassign.rs
broker/tests/jvm_acceptance_reassign/cancel_gate.rs
apache_kafka_4_3_1, cp_kafka_7_5_0 none
KIP-460 Implemented ElectLeaders with unclean election, and automatic preferred-leader rebalance broker/src/leader_rebalance.rs broker/tests/elect_leaders.rs
broker/tests/elect_leaders/auto_rebalance.rs
in process none
KIP-464 Implemented CreateTopics num_partitions and replication_factor -1 take the broker defaults broker/src/handlers/create_topics.rs broker/src/handlers/create_topics/tests.rs in process none
KIP-467 Implemented Per-record error indices and messages in the Produce response broker/src/handlers/produce/schema.rs broker/tests/schema_validation/rejected.rs in process none
KIP-482 Implemented Flexible versions and tagged fields, with the v2 request header broker/src/network/dispatch.rs broker/src/network/dispatch/tests.rs
broker/tests/librdkafka_conformance.rs::round_trip_group_join_and_api_versions_with_kcat
in process kcat 1.7.1 (librdkafka 1.8.2): round_trip_group_join_and_api_versions_with_kcat
kcat librdkafka-2.15.0 (librdkafka 2.15.0): round_trip_group_join_and_api_versions_with_kcat
KIP-496 Implemented OffsetDelete for consumer groups broker/src/handlers/offset_delete.rs broker/tests/offset_delete.rs
broker/tests/jvm_acceptance_cli/consumer_groups.rs
apache_kafka_4_3_1, cp_kafka_6_1_1, cp_kafka_7_5_0 none
KIP-500 Implemented KRaft mode: broker heartbeats, fencing and controlled shutdown broker/src/handlers/broker_heartbeat.rs broker/tests/controlled_shutdown.rs
broker/tests/advertised_controller_liveness.rs
in process none
KIP-511 Implemented Client software name and version in ApiVersions v3 raft/src/server/api_versions/client_software.rs broker/tests/client_software_versions.rs
broker/tests/librdkafka_conformance.rs::round_trip_group_join_and_api_versions_with_kcat
in process kcat 1.7.1 (librdkafka 1.8.2): round_trip_group_join_and_api_versions_with_kcat
kcat librdkafka-2.15.0 (librdkafka 2.15.0): round_trip_group_join_and_api_versions_with_kcat
KIP-516 Implemented Topic identifiers in Metadata, Produce, Fetch, offsets and DeleteTopics broker/src/topic_resolve.rs broker/tests/kip516_metadata.rs
broker/tests/kip516_produce.rs
broker/tests/kip516_fetch.rs
broker/tests/kip516_offsets.rs
broker/tests/kip516_delete_topics.rs
broker/tests/librdkafka_conformance.rs::next_gen_group_topic_ids_and_telemetry_with_librdkafka_2x
in process kcat librdkafka-2.15.0 (librdkafka 2.15.0): next_gen_group_topic_ids_and_telemetry_with_librdkafka_2x
KIP-525 Implemented CreateTopics v5 returns the created topic’s configuration broker/src/handlers/create_topics.rs broker/tests/admin_handlers.rs in process none
KIP-534 Implemented Compaction retains the last tombstone and transaction marker for a delay log/src/compact.rs log/src/compact/retention_fuzz.rs
log/src/compact_model/pass.rs
broker/tests/compaction.rs
in process none
KIP-546 Implemented DescribeClientQuotas and AlterClientQuotas broker/src/handlers/describe_client_quotas.rs broker/tests/client_quotas.rs
broker/tests/jvm_acceptance_quotas/client_quotas.rs
cp_kafka_6_1_1, cp_kafka_7_5_0 none
KIP-554 Implemented SCRAM credentials through AlterUserScramCredentials and DescribeUserScramCredentials broker/src/handlers/alter_user_scram_credentials.rs broker/tests/describe_user_scram_credentials.rs
broker/tests/auth_handlers/alter_scram.rs
broker/tests/jvm_acceptance_sasl/scram.rs
apache_kafka_4_3_1, cp_kafka_6_1_1, cp_kafka_7_5_0 none
KIP-559 Implemented protocol_type and protocol_name in JoinGroup v7 and SyncGroup v5 broker/src/handlers/sync_group.rs broker/tests/kip559_l7_proxy_fields.rs in process none
KIP-584 Implemented Feature versioning: UpdateFeatures and the finalized features in ApiVersions broker/src/handlers/update_features.rs broker/tests/feature_finalization.rs
broker/tests/api_versions_features.rs
broker/tests/jvm_features.rs
apache_kafka_4_3_1 none The finalizable features are metadata.version, group.version, transaction.version, share.version, streams.version, eligible.leader.replicas.version and kraft.version, the last finalized by a KRaft control record rather than by UpdateFeatures. metadata.version is supported up to 4.3.1’s latest production level, 4.3-IV0 (30), unless Kafka’s unstable.feature.versions.enable is set, which raises it to trunk’s 4.5-IV0 (34), Kafka’s latestTesting, in ApiVersions, node registration, UpdateFeatures and krabka format alike.
KIP-590 Implemented Envelope: the controller listener serves a forwarded admin write broker/src/envelope.rs broker/tests/kip590_envelope.rs
broker/tests/jvm_role_separated_admin.rs
apache_kafka_4_3_1 none Metadata-dependent admin operations are forwarded in an Envelope carrying the client identity and planned on the active Krabka controller. JVM controller interoperation remains outside the compatibility target (crates/raft/src/lib.rs:54).
KIP-595 Implemented The KRaft controller quorum: Vote, BeginQuorumEpoch, EndQuorumEpoch, Fetch and DescribeQuorum kraft-core/src/core.rs raft/tests/kraft_sim.rs
raft/tests/kraft_engine_sim.rs
broker/tests/jvm_static_quorum_spike.rs
broker/tests/admin_handlers/admin_describe_quorum.rs
apache_kafka_4_0_0 none
KIP-599 Implemented Controller mutation quotas on CreateTopics, CreatePartitions and DeleteTopics broker/src/quota/controller_mutation.rs broker/tests/controller_mutation_quota.rs in process none
KIP-612 Implemented Per-IP connection creation rate quotas broker/src/broker/accept.rs broker/tests/ip_quotas.rs in process none
KIP-630 Implemented Metadata snapshots: the checkpoint file and FetchSnapshot raft/src/snapshot.rs raft/tests/snapshot.rs
raft/tests/kraft_checkpoint_jvm.rs
broker/tests/fetch_snapshot.rs
apache_kafka_4_0_0 none
KIP-631 Implemented The KRaft metadata records and broker registration broker/src/handlers/broker_registration.rs raft/tests/kraft_checkpoint_jvm.rs
broker/tests/unregister_broker.rs
apache_kafka_4_0_0 none
KIP-642 Out of scope Multi-node quorum reassignment in one operation raft/src/controller/membership.rs none in process none Voter changes go one node at a time through KIP-853. change_membership rejects a batch that adds or removes more than one voter.
KIP-664 Implemented DescribeProducers, DescribeTransactions and ListTransactions broker/src/handlers/describe_producers.rs broker/tests/describe_producers.rs
broker/tests/list_describe_transactions.rs
in process none
KIP-704 Implemented AlterPartition’s leader recovery state after an unclean election broker/src/handlers/alter_partition/isr_update.rs broker/src/handlers/alter_partition/isr_update.rs::error_response_matches_kafkas_default_fields
broker/src/handlers/alter_partition/isr_update.rs::recovering_partition_cannot_expand_until_leader_reports_recovered
broker/src/handlers/alter_partition/tests.rs
broker/tests/leader_election.rs::unclean_failover_recovers_after_a_real_broker_restart
in process none Unclean election commits RECOVERING with the leader change. The controller fences illegal RECOVERED-to-RECOVERING transitions and multi-member ISR proposals that still report RECOVERING; the elected leader’s next maintenance proposal reports RECOVERED. Recovery state is stored in standard KRaft partition metadata and survives replay and snapshot restore.
KIP-714 Implemented Client metrics push: GetTelemetrySubscriptions and PushTelemetry broker/src/client_metrics/mod.rs broker/tests/client_telemetry.rs
broker/tests/client_metrics_config.rs
broker/tests/librdkafka_conformance.rs::next_gen_group_topic_ids_and_telemetry_with_librdkafka_2x
in process kcat librdkafka-2.15.0 (librdkafka 2.15.0): next_gen_group_topic_ids_and_telemetry_with_librdkafka_2x GetTelemetrySubscriptions (71) and PushTelemetry (72) are advertised only when the broker has a client-metrics receiver: the [runtime] key client_metrics_enable, or a configured client_metrics_otlp_endpoint, which implies it. The default is off, which is what a stock Kafka broker advertises when metric.reporters holds no ClientTelemetry implementation, so a modern Java or librdkafka client opens no telemetry handshake it has nowhere to push to. api_catalog::ClientMetricsReceiver names the gate and BrokerConfig::client_metrics_receiver reads it. Both handlers stay registered either way and answer a client that sends one anyway. A closed connection drops the instance it created, and an idle instance expires after the larger of one minute and three push intervals, as in Kafka. PushTelemetry follows Kafka 4.3.1 in two places where trunk has since changed: every push sets the instance’s terminating flag, a rejected one included, and a payload that decompresses past telemetry.max.bytes is answered INVALID_RECORD. unstable.api.versions.enable selects trunk’s behavior for both, which records the flag only for an accepted push (KAFKA-20898) and answers TELEMETRY_TOO_LARGE (KAFKA-21076). The match patterns of a subscription are java.util.regex patterns, read through the same rewrite, and the same residue remains, documented in the java_fold module: a backreference under a case flag compares as fancy_regex does. Under (?i) it matches a non-ASCII letter in the other case, where Java’s does not, and under (?iu) it does not match i with İ or ı, or a letter with a case that takes another number of bytes, such as s with ſ, where Java’s does.
KIP-734 Implemented ListOffsets MAX_TIMESTAMP broker/src/handlers/list_offsets/timestamp.rs broker/tests/list_offsets_isolation/timestamp_sentinels.rs in process none
KIP-768 Implemented SASL/OAUTHBEARER against an OIDC provider’s JWKS endpoint broker/src/oauth_jwks.rs broker/tests/auth_handlers/oauthbearer_tokens.rs::sasl_oauthbearer_signed_token_happy_path
broker/src/oauth_jwks/refresher/tests.rs
broker/src/oauth_jwks/fetch.rs::fetch_jwks_parses_served_keyset
verified/src/jwks.rs::cache_requires_one_fresh_stable_generation
in process none The broker half of the KIP: crates/broker/src/oauth_jwks/ GETs the provider’s JWKS document over HTTP or HTTPS, parses it, and swaps the key set into the shared JwksHandle a SignedJwsValidator reads, so rotated keys are picked up with no restart. It refreshes on a cadence and on a validator’s unknown-kid signal, rate-limits the on-demand path, keeps the previous key set when a fetch fails, and fences readers with the even/odd generation counter that crates/verified/src/jwks.rs:42 proves admission against; the same file’s crates/verified/src/jwks.rs:84 keeps the on-demand limiter monotonic across a wall-clock rollback. Cache expiry, issuer and audience checks, the principal and groups claims, the typ check, clock skew, an operator-supplied IdP TLS trust bundle and the use=enc filter are configured from the [oauthbearer] TOML table rather than Kafka’s sasl.oauthbearer.* JAAS options. The KIP’s client half – the login callback that retrieves a token with an OAuth client_credentials grant – is a client concern and lives in krabka-client-rs, not in this repository.
KIP-778 Implemented metadata.version as a finalized feature that krabka format bootstraps format/src/format/features.rs format/tests/format_smoke.rs
broker/tests/format_features.rs
in process none
KIP-814 Implemented A static leader that rejoins a stable group gets skip_assignment and keeps the current assignment broker/src/coordinator/unified/classic_ops/join.rs broker/tests/static_membership.rs::static_rejoin_preserves_assignment_and_generation in process none
KIP-827 Implemented DescribeLogDirs v4 reports total and usable bytes per directory broker/src/handlers/describe_log_dirs/dirs.rs broker/tests/jvm_acceptance_quotas/log_dirs.rs cp_kafka_6_1_1, cp_kafka_7_5_0 none
KIP-835 Implemented The KRaft leader appends a NoOpRecord every metadata.max.idle.interval.ms raft/src/kraft/controller/idle.rs raft/src/kraft/controller/tests_cleaning.rs::a_leader_appends_kip835_no_ops_that_change_nothing
raft/src/kraft/controller/tests_cleaning.rs::the_no_op_timer_runs_only_on_a_leader_with_an_interval
raft/src/kraft/controller/tests_apply.rs::quorum_snapshot_timestamp_tracks_applied_not_newer_committed_records
broker/src/metadata_observer/fetch.rs::apply_fetch_records_advances_data_batch_offset_and_publishes
broker/src/metadata_observer.rs::pending_record_processing_time_tracks_average_and_max
in process none The NoOpRecord heartbeat is in the tree. Controller and broker metrics publish the last applied record offset and append timestamp, and time since that timestamp. Pending-record processing gauges report the process-lifetime average and maximum microseconds spent on completed apply passes that advanced across pending committed records. Gauges refresh on the broker polling interval.
KIP-841 Implemented Unclean leader election when the ISR is empty and the topic allows it broker/src/leader_election.rs broker/tests/leader_election.rs
broker/src/leader_election/scan/dead_broker_tests.rs
in process none
KIP-848 Implemented The next-generation consumer group protocol broker/src/coordinator/unified/consumer_state.rs broker/tests/consumer_group_next_gen.rs
broker/tests/jvm_consumer_group_next_gen.rs
broker/tests/group_version.rs
broker/tests/jvm_acceptance_cli/consumer_groups.rs
broker/tests/librdkafka_conformance.rs::next_gen_group_topic_ids_and_telemetry_with_librdkafka_2x
apache_kafka_4_0_0, cp_kafka_7_4_0, apache_kafka_4_3_1, cp_kafka_6_1_1, cp_kafka_7_5_0 kcat librdkafka-2.15.0 (librdkafka 2.15.0): next_gen_group_topic_ids_and_telemetry_with_librdkafka_2x A ConsumerGroupHeartbeat whose SubscribedTopicRegex does not compile is answered INVALID_REGULAR_EXPRESSION (128) before any member record is written, and the member is not admitted, as Kafka does. The pattern is compiled with Rust regex in Unicode mode, which accepts RE2J’s Unicode character classes; topic names are ASCII, so RE2J’s ASCII-only perl classes cannot diverge on a match. An inline flag group naming a flag RE2J has no equivalent for (x, u, R) is rejected ahead of the compile with RE2J’s own message, since regex would take it. The re2j wrapper translates RE2J’s \\Q...\\E literal quoting and character-class rules before compiling, while keeping Rust regex’s bounded non-backtracking matching. A repetition large enough to exceed regex’s compiled-size limit remains a narrow acceptance difference. No JVM-lane case covers the refusal: KafkaConsumer.subscribe(Pattern) and kafka-console-consumer --include compile the pattern locally with java.util.regex, so a stock JVM client never sends an invalid one to the broker. The group resolves each subscribed regex once for all its members, as Kafka’s TopicRegexResolver does: against the metadata image, keeping the topics that the principal of the heartbeat that resolves it may Describe. It records the result as a ConsumerGroupRegularExpression record, so a coordinator failover restores the topics of every regex subscription without a heartbeat that carries the pattern. A heartbeat of any member resolves the patterns again when a member’s pattern is not resolved yet, when a topic was created since the last resolution, or when the last one is older than group.consumer.regex.refresh.interval.ms (10 minutes), and never within 10 seconds of the last one. A change of the ACLs counts as a new topic here, where Kafka picks it up at the refresh interval. A group’s consumer.session.timeout.ms, consumer.heartbeat.interval.ms and consumer.assignment.interval.ms are accepted, validated against the group.consumer.min.* and group.consumer.max.* bounds and stored by AlterConfigs and IncrementalAlterConfigs on a GROUP resource, as Kafka does, and the consumer coordinator applies them to that group over the broker’s group.consumer.* values. A next target assignment waits for consumer.assignment.interval.ms (1 s by default) since the last one, as in Kafka’s canComputeNextTargetAssignment.
KIP-853 Implemented Dynamic controller quorum: AddRaftVoter, RemoveRaftVoter, UpdateRaftVoter and auto-join raft/src/kraft/controller/reconfiguration.rs broker/tests/dynamic_voters.rs
raft/tests/reconfig.rs
broker/tests/jvm_features.rs
apache_kafka_4_3_1 none
KIP-858 Implemented Directory identifiers: AssignReplicasToDirs and directory ids in heartbeats broker/src/assign_dirs.rs broker/tests/jbod_disk_failure.rs
broker/tests/offline_replicas.rs
in process none
KIP-860 Implemented AlterPartitionReassignments refuses a replication-factor change unless the request allows it broker/src/handlers/alter_partition_reassignments/plan.rs broker/src/handlers/alter_partition_reassignments/plan.rs::the_replication_factor_check_counts_the_set_the_partition_is_headed_for in process none
KIP-890 Implemented Transactions v2: verify-only AddPartitionsToTxn and epoch bumps on completion broker/src/txn/version.rs broker/tests/transaction_version.rs
broker/tests/transaction_version/txnver_verify_only.rs
in process none What a request may do follows its API version, as in Kafka, whatever transaction.version the cluster finalized: EndTxn v5 and AddPartitionsToTxn v4 and later are TV_2 and bump the epoch, and older versions and AddOffsetsToTxn are TV_0. The cluster level picks the __transaction_state value format and the rules of a server-initiated abort. Two answers follow Kafka trunk only under unstable.api.versions.enable: an EndTxn v5 commit at the pre-abort epoch after CompleteAbort answers PRODUCER_FENCED (KAFKA-20785, where 4.3.1 answers INVALID_TXN_STATE), and LastProducerEpoch (tag 4) is written to and read from __transaction_state (KAFKA-20357, where 4.3.1 keeps it in memory). A transactional offset commit records the topic id only from TxnOffsetCommit v6 and under that flag.
KIP-903 Implemented Broker epochs fence stale replicas in AlterPartition broker/src/handlers/alter_partition/isr_update.rs raft/src/kraft/controller/tests_broker_registration.rs
broker/src/elr/tests.rs
in process none
KIP-919 Implemented Admin clients bootstrap from controllers: ControllerRegistration, DescribeCluster and UnregisterBroker broker/src/controller_admin.rs broker/tests/jvm_bootstrap_controller.rs
broker/tests/client_admin_controller_bootstrap.rs
broker/tests/unregister_broker.rs
apache_kafka_4_3_1 none
KIP-932 Implemented Share groups: membership, ShareFetch, ShareAcknowledge, the share coordinator and admin offsets broker/src/share_partition/mod.rs broker/tests/share_groups.rs
broker/tests/share_consume.rs
broker/tests/share_admin_offsets.rs
broker/tests/jvm_share_groups.rs
apache_kafka_4_1_0 none Group-level configs are accepted, validated and stored for every key of Kafka 4.3.1’s GroupConfig, by AlterConfigs and IncrementalAlterConfigs on a GROUP resource, as Kafka does. The share partitions apply share.auto.offset.reset, share.record.lock.duration.ms, share.delivery.count.limit, share.partition.max.record.locks, share.isolation.level and share.renew.acknowledge.enable. The share coordinator applies share.session.timeout.ms, share.heartbeat.interval.ms and share.assignment.interval.ms to that group over the broker’s group.share.* values, and a next target assignment waits for the assignment interval (1 s by default) since the last one, as in Kafka’s canComputeNextTargetAssignment.
KIP-939 Implemented Two-phase commit transactions with the KIP-939 timeout rules broker/src/txn/two_pc.rs broker/tests/transactions_2pc.rs
broker/src/txn/two_pc_model.rs
in process none InitProducerId v6 is latestVersionUnstable in Kafka 4.3.1, so it is served only under unstable.api.versions.enable, and 2PC itself only under transaction.two.phase.commit.enable. keepPreparedTxn is answered UNSUPPORTED_VERSION, as 4.3.1’s TransactionCoordinator.handleInitProducerId answers it, unless both switches are on; then krabka’s prepared-transaction recovery serves it, which no Kafka release implements.
KIP-950 Implemented Tiered storage disablement: remote.log.copy.disable and remote.log.delete.on.disable broker/src/remote_log_manager.rs broker/src/config_keys/validation/tests.rs
broker/tests/jvm_acceptance_tiered.rs
cp_kafka_6_1_1, cp_kafka_7_8_8 none remote.storage.enable going true -> false is refused unless remote.log.delete.on.disable=true comes with it, and the flip then erases the partition’s remote segments and raises its log start offset to the local log start. remote.log.copy.disable=true is the read-only tier: no new copies, reads and remote retention unchanged. Under a WORM archive the cascade clears the partition’s remote metadata and removes nothing from the archive, as a DeleteTopics cascade does.
KIP-951 Implemented Leader hints in the Produce and Fetch responses broker/src/handlers/produce/pipeline.rs broker/tests/produce_leader_gate.rs
broker/tests/producer_leader_routing.rs
in process none
KIP-966 Implemented Eligible leader replicas, unclean recovery and DescribeTopicPartitions broker/src/elr.rs broker/tests/unclean_recovery.rs
broker/tests/describe_topic_partitions.rs
broker/tests/jvm_acceptance_cli/elr_columns.rs
broker/tests/jvm_features.rs
apache_kafka_4_3_1, cp_kafka_6_1_1, cp_kafka_7_5_0 none ELR maintenance is gated on the eligible.leader.replicas.version feature, as Kafka gates it on FeatureControlManager.isElrFeatureEnabled(): at level 0 the controller publishes no eligible or last-known-eligible set, and a downgrade to 0 clears what an earlier level 1 published. The release default is level 1 from metadata.version 4.1-IV0, where ELRV_1 bootstraps, and 0 below it; level 1 declares Kafka’s KIP-1022 dependency on metadata.version at 4.0-IV1. ELR is carried by standard per-partition KRaft metadata and is consumed during JVM-compatible replay. The last-known ELR follows Kafka 4.3.1’s PartitionChangeBuilder (useLastKnownLeaderInBalancedRecovery is always on): it holds the single last leader of a partition that has no leader and is empty otherwise, and when that leader returns it is elected as an unclean leader (RECOVERING, a singleton ISR) with no unclean.leader.election.enable or unclean.recovery.strategy consulted, unless an ELR member is there to lead cleanly first. krabka’s partition record always names a leader, so a partition with no leader keeps its last leader in the record, marks it with that one-member last-known ELR, and Metadata and DescribeTopicPartitions report it as leader = -1; the election runs when a broker unfences (handleBrokerUnfenced). The operator election in ElectLeaders does not take the last-known rung.
KIP-996 Implemented Pre-vote before a KRaft election kraft-core/src/core/election.rs kraft-core/src/core/election/tests.rs
raft/tests/kraft_engine_sim/failover.rs
broker/tests/jvm_static_quorum_spike/contested_election.rs
apache_kafka_4_0_0 none
KIP-1005 Implemented ListOffsets LATEST_TIERED_TIMESTAMP broker/src/handlers/list_offsets/sentinels.rs broker/tests/list_offsets_isolation/timestamp_sentinels.rs in process none
KIP-1022 Implemented Feature flags with dependency checks at format and upgrade time format/src/format/features.rs broker/tests/format_features.rs
broker/tests/jvm_features.rs
apache_kafka_4_3_1 none
KIP-1023 Implemented ListOffsets EARLIEST_PENDING_UPLOAD_TIMESTAMP broker/src/handlers/list_offsets/resolve.rs broker/tests/list_offsets_isolation/timestamp_sentinels.rs in process none
KIP-1038 Implemented ListTransactions filters transactional ids by an RE2J-compiled pattern broker/src/handlers/list_transactions.rs broker/src/handlers/list_transactions.rs
broker/src/re2j.rs
in process none
KIP-1066 Implemented cordoned.log.dirs: cordoned log directories take no new replica broker/src/cordoned_log_dirs.rs broker/src/cordoned_log_dirs.rs
broker/src/handlers/broker_heartbeat/tests.rs::a_heartbeat_stores_its_cordoned_dirs_from_4_3_iv0
broker/src/handlers/create_topics/placement/tests.rs::fully_cordoned_brokers_are_not_automatic_placement_candidates
broker/src/handlers/incremental_alter_configs/broker_scope.rs
broker/tests/alter_replica_log_dirs.rs
in process none Matches Kafka 4.3.1. A manual CreateTopics or CreatePartitions assignment, or an AlterPartitionReassignments target, that names a broker whose log directories are all cordoned is accepted, as a live apache/kafka:4.3.1 accepts it: the INVALID_REPLICA_ASSIGNMENT refusal The manual partition assignment includes broker N, but all its log directories are cordoned. is KAFKA-20832, which is on Kafka trunk and the 4.3 branch after 4.3.1 only. The controller does not tell a forwarded write of cordoned.log.dirs from a direct one, so it does not apply Kafka’s rule that a direct controller write may only remove entries.
KIP-1071 Implemented Streams groups: StreamsGroupHeartbeat and StreamsGroupDescribe broker/src/coordinator/unified/streams/mod.rs broker/tests/streams_groups.rs
broker/tests/streams_classic_upgrade.rs
broker/tests/jvm_streams_groups.rs
broker/tests/jvm_streams_app.rs
apache_kafka_4_1_0, apache_kafka_4_3_1, cp_kafka_7_5_0 none A group’s streams.* overrides, streams.initial.rebalance.delay.ms and streams.assignment.interval.ms included, apply per group, and a key of another coordinator stored beside them leaves them intact.
KIP-1073 Implemented DescribeCluster hides fenced brokers from clients broker/src/handlers/describe_cluster.rs broker/tests/role_separation_observer.rs in process none
KIP-1075 Implemented A server-side timeout for remote ListOffsets work broker/src/handlers/list_offsets/remote.rs broker/src/config_keys/registry/tests.rs in process none
KIP-1101 Out of scope The MetadataHash tagged field on the group-metadata records, and the streams partition-metadata record it retires broker/src/coordinator/unified/persistence_next_gen/epochs.rs none in process none The hash is how Kafka decides a group must rebalance because its subscribed topics changed shape. The consumer and share groups of krabka decide that from the metadata image instead and write the hash as 0, which is what Kafka writes for a group whose hash is unset, and Kafka’s own reader accepts it. The streams group keeps Kafka’s hash, because KIP-1071 configures the topology again when it changes, and writes it in its group metadata record. krabka keeps the streams partition-metadata snapshot this KIP retired, on the key version Kafka no longer assigns, where Kafka’s serde skips it as an unknown type rather than mis-reading it.
KIP-1142 Implemented ListConfigResources broker/src/handlers/list_config_resources.rs broker/tests/admin_handlers/admin_listings.rs in process none
KIP-1155 Implemented A checkpoint at every metadata.version downgrade raft/src/kraft/controller/snapshotting.rs raft/src/kraft/controller/tests_downgrade.rs in process none
KIP-1186 Implemented kraft.version upgrade and the last-voter check that the Kafka 4.3 quorum tools drive raft/src/server/voter_admin.rs broker/tests/jvm_features.rs apache_kafka_4_3_1 none
KIP-1191 Partial Share-group dead-letter queue: a record that a member rejects, or that uses up the delivery count limit, is written to the group’s dead-letter topic before it is archived broker/src/share_partition/dlq.rs broker/src/features.rs::share_version_two_is_advertised_only_under_unstable_feature_versions
broker/src/handlers/update_features/preconditions.rs::share_version_two_needs_unstable_feature_versions
format/src/format/features.rs::share_version_two_is_trunks_level
broker/src/share_partition/state/dlq.rs
broker/src/share_partition/dlq/record.rs
broker/src/share_partition/dlq/validate.rs
broker/src/share_partition/dlq/coalesce.rs
broker/src/share_partition/dlq/writer.rs
broker/src/metrics/share_dlq.rs
broker/src/share_partition/manager/dead_letter.rs
broker/tests/share_consume/dead_letter_queue.rs
in process none Kafka trunk’s share.version 2, which a node supports only under unstable.feature.versions.enable; the topic key errors.deadletterqueue.group.enable and the group keys errors.deadletterqueue.topic.name and errors.deadletterqueue.copy.record.enable need unstable.api.versions.enable too, so trunk mode is both switches. At share.version 1, the level a default node runs, a group’s dead-letter keys change nothing and a reject archives at once. A group with a topic name gets one record for each offset that is rejected or reaches share.delivery.count.limit, with Kafka’s six __dlq.errors.* headers and, with errors.deadletterqueue.copy.record.enable, the source key and value. It is an acks=all produce to the leader of the topic partition that the source partition maps to, as Kafka’s ShareGroupDLQStateManager sends it, over the inter-broker listener, and it retries five times from 1 s to 30 s. The writes of a broker share one connection to each destination leader, neighbouring runs with the same delivery count and cause are one write, and at most eight writes run at once. As in Kafka’s ShareGroupDLQStateManager, the rounds of every write that waits for one destination leader go out in as few produce requests as max.message.bytes allows, across runs and partitions: one request is in flight to a leader, the rounds that arrive meanwhile go out together in the next, the rounds for one partition are one batch, and each write archives its run when the response holds the row for its own partition, and retries on its own. The eight writers are the most runs that share a request. The record is persisted as delivery state ARCHIVING (3) first and as ARCHIVED (4) when the write ends, whatever its result, and a leader that finds ARCHIVING after a restart writes the record again, as SharePartition.initiateDLQAndArchive and maybeResumeDlqArchiving do. The topic has to have errors.deadletterqueue.group.enable=true, a name that starts with errors.deadletterqueue.topic.name.prefix (default dlq.) and not with __, and a missing topic is created only when errors.deadletterqueue.auto.create.topics.enable is true. Both cluster keys are read from the dynamic broker config, per broker and then cluster-wide, and neither is in the DescribeConfigs key roster of a broker yet. The DeadLetterQueue* meters of Kafka’s ShareGroupMetrics are exported for each group as the counters krabka_broker_share_group_dlq_records_total (DeadLetterQueueRecordCount), krabka_broker_share_group_dlq_produce_requests_total (DeadLetterQueueTotalProduceRequestsPerSec, one for each round of each write for each attempt, as Kafka counts it) and krabka_broker_share_group_dlq_failed_produce_requests_total (DeadLetterQueueFailedProduceRequestsPerSec), and group.share.dlq.manager.class.name, a JVM class name, has no counterpart. A copied record that the six headers and the batch header push over max.message.bytes is written with its headers alone, where trunk’s writer sends it whole and loses it to MESSAGE_TOO_LARGE.
KIP-1222 Implemented Share acquisition lock renewal: the Renew acknowledge type and IsRenewAck on ShareFetch and ShareAcknowledge v2 broker/src/handlers/share_fetch/acknowledge.rs broker/src/handlers/share_fetch/renew_tests.rs::renew_acknowledgements_renew_only_the_renew_offsets
broker/src/handlers/share_fetch/renew_tests.rs::a_renew_fetch_answers_a_denied_topic_as_an_acknowledge_error
broker/tests/share_consume/lock_lifetime.rs::renew_extends_lock_not_redelivered
in process none share.renew.acknowledge.enable (default true) is a GroupConfig key that AlterConfigs and IncrementalAlterConfigs accept on a GROUP resource and that the share partitions apply: a group that sets it to false answers a Renew acknowledgement with INVALID_RECORD_STATE, and a Renew type without IsRenewAck is INVALID_REQUEST.
KIP-1241 Implemented Delayed remote copy: remote.copy.lag.ms and remote.copy.lag.bytes hold a sealed segment back from the remote tier broker/src/remote_log_manager/copy.rs broker/src/remote_log_manager/copy.rs::a_sealed_segment_waits_out_its_copy_lag
broker/src/remote_log_manager/copy.rs::a_derived_copy_lag_is_the_effective_local_retention
broker/src/remote_log_manager.rs::tick_all_honours_the_remote_copy_lag_on_a_trunk_broker
in process none Kafka trunk’s topic keys and their log.remote.copy.lag.ms and log.remote.copy.lag.bytes broker defaults, served only under unstable.api.versions.enable. By default they are unknown configs, as on 4.3.1, and a sealed segment is copied as soon as it is sealed. With the flag on, the copy pass stops at the first sealed segment that is neither old enough by its newest record nor far enough behind newer local data, as RLMCopyTask.candidateLogSegments does.
KIP-1242 Implemented ApiVersions v5 routing identity and REBOOTSTRAP_REQUIRED broker/src/handlers/api_versions.rs broker/src/handlers/api_versions/tests.rs in process none Kafka trunk’s ApiVersions v5, served only under unstable.api.versions.enable. By default both listeners serve 4.3.1’s v0-v4 and answer v5 UNSUPPORTED_VERSION with that range, so REBOOTSTRAP_REQUIRED (129) is never sent. On a SASL listener the first ApiVersions, which arrives before authentication, is answered as Kafka’s SaslServerAuthenticator answers it: INVALID_REQUEST for an invalid request and otherwise the table, with no routing check.
KIP-1251 Implemented OffsetCommit accepts an older member epoch for a partition assigned at or before that epoch broker/src/coordinator/unified/consumer_state/group.rs broker/src/handlers/offset_commit/group_validation_tests.rs::consumer_group_commit_follows_kip_1251
broker/src/coordinator/unified/consumer_state/group.rs::offset_commit_follows_kafka_consumer_group_rule
in process none
KIP-1263 Out of scope The AssignmentTimestamp tagged field on the target-assignment metadata records broker/src/coordinator/unified/persistence_next_gen/epochs.rs none in process none Kafka stamps each target assignment with the time it was computed, for its own assignment metrics. krabka does not measure assignment latency from the log, so it writes the tagged field’s default of 0, which is what Kafka writes when it has no timestamp to record.
KIP-1276 Implemented CIDR-range ACL hosts (10.0.0.0/8, 2001:db8::/32) in CreateAcls and host matching authz/src/cidr.rs authz/src/cidr.rs
authz/src/simple/matching.rs
broker/src/handlers/create_acls/validate.rs
in process none Trunk behavior, behind unstable.feature.versions.enable. By default CreateAcls and the authorizer behave as Kafka 4.3.1 does: any host, an empty one or one containing / included, is stored and compared as text. Under the flag CreateAcls applies trunk’s validateHostPattern, which accepts a CIDR host from metadata.version 4.4-IV1 (level 32) and answers UNSUPPORTED_VERSION below it, and the authorizer reads a stored host containing / as a range from that version.
KIP-1312 Implemented UnregisterController drops a controller registration broker/src/handlers/unregister_controller.rs broker/src/handlers/unregister_controller/tests.rs::refusals_follow_trunks_order
broker/src/handlers/unregister_controller/tests.rs::handle_unregisters_a_registered_controller_as_trunk_does
broker/tests/controller_admin_surface.rs::controller_listener_serves_unregister_controller
broker/tests/unregister_controller_forward.rs
in process none Matches Kafka trunk: no Kafka release has api key 94. The controller listener answers it and a broker listener forwards it in an Envelope, as trunk’s ControllerApis and KafkaApis do. It is advertised and accepted only under unstable.api.versions.enable; by default a request for key 94 closes the connection, as it does against a 4.3.1 broker or controller, and an Envelope carrying one is answered UNSUPPORTED_VERSION. It needs metadata.version 4.4-IV2 (level 33), which a node supports only under unstable.feature.versions.enable, and answers trunk’s UNSUPPORTED_VERSION below it.
KIP-1319 Implemented TxnOffsetCommit v6: topics by id, GROUP_ID_NOT_FOUND and STALE_MEMBER_EPOCH broker/src/txn/handlers/txn_offset_commit.rs broker/src/txn/handlers/txn_offset_commit/integration_tests.rs::v6_resolves_topic_ids_before_the_read_gate_and_the_existence_check
broker/src/txn/handlers/txn_offset_commit/integration_tests.rs::v6_answers_group_id_not_found_where_older_versions_answer_illegal_generation
broker/tests/transactions/txn_offset_commit_topic_ids.rs::send_offsets_to_transaction_commits_by_topic_id
in process none Kafka trunk’s TxnOffsetCommit v6, which Kafka 4.3.1 predates, served only under unstable.api.versions.enable. By default the broker advertises and accepts 4.3.1’s v0-v5.
KIP-1331 Partial Streams topology descriptions: StreamsGroupHeartbeat, StreamsGroupDescribe v1 and StreamsGroupTopologyDescriptionUpdate broker/src/coordinator/unified/streams/description.rs broker/src/coordinator/unified/streams/actor/tests.rs::heartbeat_response_carries_the_recovery_lag_at_version_1_only
broker/src/handlers/streams_group_heartbeat.rs::handle_answers_v1_with_the_recovery_lag_and_no_topology_description_request
broker/src/handlers/streams_group_describe/tests.rs::version_1_names_the_assignor_and_the_topology_description_status
broker/src/coordinator/unified/streams/actor/tests.rs::a_member_missing_a_rack_aware_tag_gets_missing_client_tags_at_version_1
broker/src/handlers/streams_group_topology_description_update/tests.rs::handle_answers_as_a_trunk_broker_without_a_plugin
broker/src/handlers/streams_group_topology_description_update/tests.rs::handle_refuses_what_a_trunk_broker_with_a_plugin_refuses
broker/src/coordinator/unified/streams/actor/description/tests.rs::one_member_of_a_group_is_asked_for_the_description
broker/src/coordinator/unified/streams/actor/description/tests.rs::a_loaded_group_is_not_asked_for_a_stored_epoch
broker/tests/streams_groups/streams_topology_description.rs::one_member_is_asked_and_its_push_is_described
broker/tests/streams_groups/streams_topology_description.rs::a_deleted_and_recreated_group_is_asked_again
in process none Matches Kafka trunk, and served only under unstable.api.versions.enable: by default StreamsGroupHeartbeat and StreamsGroupDescribe are 4.3.1’s v0, api key 93 is not advertised and closes the connection, and trunk’s streams.* group keys are unknown group configs. Without group.streams.topology.description.plugin.class, as a Kafka broker runs by default, a heartbeat never sets TopologyDescriptionRequired, a describe that asks for the description answers NOT_STORED, and StreamsGroupTopologyDescriptionUpdate (93) answers UNSUPPORTED_VERSION with trunk’s The broker has no streams group topology description plugin configured. once the streams protocol and group Read gates pass. krabka cannot load a JVM class, so the key accepts only Kafka’s reference plugin, org.apache.kafka.server.streams.InMemoryTopologyDescriptionPlugin, which krabka builds in, and any other class stops the broker at startup. With that plugin, a version 1 heartbeat asks one member for the description under Kafka’s back-off of 30 seconds that doubles to one hour. The broker keeps the pushed description in memory and its epoch in the StoredDescriptionTopologyEpoch of the group metadata record, a describe answers it as AVAILABLE, and DeleteGroups drops it. As with Kafka’s in-memory plugin, a broker restart or a coordinator move loses the description but keeps the stored epoch, so the group is not asked again until its topology epoch changes. Kafka writes the UNCERTAIN epoch before it calls the plugin, and krabka, whose plugin stores in the same step, writes only the pushed epoch. The built-in plugin never fails, so FailedDescriptionTopologyEpoch stays -1 and there is no cleanup cycle for a plugin to retry. Heartbeat v1 carries MISSING_CLIENT_TAGS when a tag key named by the group’s streams.rack.aware.assignment.tags, whose default is the broker’s group.streams.rack.aware.assignment.tags, is missing from the member’s client tags. DeleteGroups v3, which adds a per-group ErrorMessage, is trunk’s too and is advertised only under the same flag. krabka never answers GROUP_DELETION_FAILED, since its plugin cannot fail a delete, so the message is always null.
KIP-1357 Implemented StreamsGroupDescribe v1 names the group’s task assignor broker/src/handlers/streams_group_describe/render.rs broker/src/handlers/streams_group_describe/tests.rs::version_1_names_the_assignor_and_the_topology_description_status in process none Kafka trunk’s StreamsGroupDescribe v1, served only under unstable.api.versions.enable.
SASL/GSSAPI Implemented SASL/Kerberos authentication broker/src/network/auth/gssapi.rs broker/tests/gssapi_e2e.rs
broker/tests/auth_handlers/gssapi.rs
in process none Kerberos predates the KIP process (KAFKA-1686, Kafka 0.9), so no KIP number. Both suites run in the scheduled container gssapi CI job: the KDC fixture writes keytabs through a bind mount, so the lane is schedule and workflow_dispatch only.
mixed-quorum Out of scope A controller quorum with both JVM and Krabka voters raft/src/lib.rs none in process none Outside the raft crate’s compatibility target: crates/raft/src/lib.rs:54.

Kafka images

Key Image
apache_kafka_4_0_0 mirror.gcr.io/apache/kafka:4.0.0@sha256:01b9a4030e54c6068e66eb3ba4cb82c0d89238629ef1c30d79b86036bf89b1b7
apache_kafka_4_1_0 mirror.gcr.io/apache/kafka:4.1.0@sha256:bce21a1a56707320ce239d743143bf4359ba45da27d31a5236f2169001eec751
apache_kafka_4_3_1 mirror.gcr.io/apache/kafka:4.3.1@sha256:ccd1314e47ec76909e01f86308b4dcf2064f19f7c89759234322314b0e319e26
cp_kafka_6_1_1 mirror.gcr.io/confluentinc/cp-kafka:6.1.1@sha256:537c303f182ff6ba665f324520105c44970c97d7ef94b306709a5d68b8c1aed7
cp_kafka_7_4_0 mirror.gcr.io/confluentinc/cp-kafka:7.4.0@sha256:9080235b1e58a57ca16d480cba6c9cbde72619f2bd43b77d70bada5aecf7811a
cp_kafka_7_5_0 mirror.gcr.io/confluentinc/cp-kafka:7.5.0@sha256:69022c46b7f4166ecf21689ab4c20d030b0a62f2d744c20633abfc7c0040fa80
cp_kafka_7_8_8 mirror.gcr.io/confluentinc/cp-kafka:7.8.8@sha256:a8c5e3c481e58b130e1673e201c364fa23a9c6ff0f6ab017a31c326285822bf3

On-disk log contract

The suite covers timestamped record batches (KIP-32), segment and index discovery, and record key/value recovery in both directions. It ran against mirror.gcr.io/confluentinc/cp-kafka:6.1.1@sha256:537c303f182ff6ba665f324520105c44970c97d7ef94b306709a5d68b8c1aed7.

Direction Status Contract Owner Differential test Kafka image
JVM -> krabka Implemented Open and read a JVM-written Kafka log directory crates/log read_jvm_produced_log_dir mirror.gcr.io/confluentinc/cp-kafka:6.1.1@sha256:537c303f182ff6ba665f324520105c44970c97d7ef94b306709a5d68b8c1aed7
krabka -> JVM Implemented Restart Kafka over a krabka-written log directory and consume it crates/log jvm_consumes_rust_written_log_dir mirror.gcr.io/confluentinc/cp-kafka:6.1.1@sha256:537c303f182ff6ba665f324520105c44970c97d7ef94b306709a5d68b8c1aed7

Advertised API versions

kafka-broker-api-versions read both tables below. api_versions_differential asserts krabka’s against api_catalog and records the join in divergence.json, which is where these two version columns come from. A range that moves, or a key that appears on one side only, changes that file and so changes this table.

The oracle is mirror.gcr.io/apache/kafka:4.3.1@sha256:ccd1314e47ec76909e01f86308b4dcf2064f19f7c89759234322314b0e319e26.

It is a stock single-node KRaft broker, read on its PLAINTEXT broker listener. krabka is read the same way: one client listener, no client-telemetry receiver, and both version switches at their defaults. unstable.api.versions.enable is Kafka’s own; legacy_request_versions_enable is krabka’s. With those defaults krabka advertises exactly Kafka 4.3.1’s table, so every row below should match. A krabka only or Kafka only verdict would mean the two brokers disagree on which keys exist.

What krabka serves past 4.3.1 is opt-in, and so it does not appear here. Kafka trunk’s ApiVersions v5, TxnOffsetCommit v6, the streams v1 pair, and api keys 93 and 94 need unstable.api.versions.enable. The pre-4.0 Fetch, ListOffsets and Produce versions need legacy_request_versions_enable. The KIP rows above name each one.

A row whose ranges differ carries the reason krabka means to differ, recorded beside the ranges themselves in RANGE_DIVERGENCE_INTENTS (divergence.rs). The suite panics on a range divergence with no entry there, so a range that moves cannot reach this page unexplained. Every other verdict leaves the column empty.

API Key krabka Kafka Verdict Intent
Produce 0 0-13 0-13 match –
Fetch 1 4-18 4-18 match –
ListOffsets 2 1-11 1-11 match –
Metadata 3 0-13 0-13 match –
OffsetCommit 8 2-10 2-10 match –
OffsetFetch 9 1-10 1-10 match –
FindCoordinator 10 0-6 0-6 match –
JoinGroup 11 0-9 0-9 match –
Heartbeat 12 0-4 0-4 match –
LeaveGroup 13 0-5 0-5 match –
SyncGroup 14 0-5 0-5 match –
DescribeGroups 15 0-6 0-6 match –
ListGroups 16 0-5 0-5 match –
SaslHandshake 17 0-1 0-1 match –
ApiVersions 18 0-4 0-4 match –
CreateTopics 19 2-7 2-7 match –
DeleteTopics 20 1-6 1-6 match –
DeleteRecords 21 0-2 0-2 match –
InitProducerId 22 0-5 0-5 match –
OffsetForLeaderEpoch 23 2-4 2-4 match –
AddPartitionsToTxn 24 0-5 0-5 match –
AddOffsetsToTxn 25 0-4 0-4 match –
EndTxn 26 0-5 0-5 match –
WriteTxnMarkers 27 1-2 1-2 match –
TxnOffsetCommit 28 0-5 0-5 match –
DescribeAcls 29 1-3 1-3 match –
CreateAcls 30 1-3 1-3 match –
DeleteAcls 31 1-3 1-3 match –
DescribeConfigs 32 1-4 1-4 match –
AlterConfigs 33 0-2 0-2 match –
AlterReplicaLogDirs 34 1-2 1-2 match –
DescribeLogDirs 35 1-5 1-5 match –
SaslAuthenticate 36 0-2 0-2 match –
CreatePartitions 37 0-3 0-3 match –
CreateDelegationToken 38 1-3 1-3 match –
RenewDelegationToken 39 1-2 1-2 match –
ExpireDelegationToken 40 1-2 1-2 match –
DescribeDelegationToken 41 1-3 1-3 match –
DeleteGroups 42 0-2 0-2 match –
ElectLeaders 43 0-2 0-2 match –
IncrementalAlterConfigs 44 0-1 0-1 match –
AlterPartitionReassignments 45 0-1 0-1 match –
ListPartitionReassignments 46 0-0 0-0 match –
OffsetDelete 47 0-0 0-0 match –
DescribeClientQuotas 48 0-1 0-1 match –
AlterClientQuotas 49 0-1 0-1 match –
DescribeUserScramCredentials 50 0-0 0-0 match –
AlterUserScramCredentials 51 0-0 0-0 match –
DescribeQuorum 55 0-2 0-2 match –
UpdateFeatures 57 0-2 0-2 match –
DescribeCluster 60 0-2 0-2 match –
DescribeProducers 61 0-0 0-0 match –
UnregisterBroker 64 0-0 0-0 match –
DescribeTransactions 65 0-0 0-0 match –
ListTransactions 66 0-2 0-2 match –
ConsumerGroupHeartbeat 68 0-1 0-1 match –
ConsumerGroupDescribe 69 0-1 0-1 match –
ListConfigResources 74 0-1 0-1 match –
DescribeTopicPartitions 75 0-0 0-0 match –
ShareGroupHeartbeat 76 1-1 1-1 match –
ShareGroupDescribe 77 1-1 1-1 match –
ShareFetch 78 1-2 1-2 match –
ShareAcknowledge 79 1-2 1-2 match –
AddRaftVoter 80 0-1 0-1 match –
RemoveRaftVoter 81 0-0 0-0 match –
InitializeShareGroupState 83 0-0 0-0 match –
ReadShareGroupState 84 0-0 0-0 match –
WriteShareGroupState 85 0-1 0-1 match –
DeleteShareGroupState 86 0-0 0-0 match –
ReadShareGroupStateSummary 87 0-1 0-1 match –
StreamsGroupHeartbeat 88 0-0 0-0 match –
StreamsGroupDescribe 89 0-0 0-0 match –
DescribeShareGroupOffsets 90 0-1 0-1 match –
AlterShareGroupOffsets 91 0-0 0-0 match –
DeleteShareGroupOffsets 92 0-0 0-0 match –

The Bazel Docker lane supplies each image from a digest-pinned OCI repository. CI regenerates this page and fails when the test names, lane, image tag, digest, recorded API versions, or checked-in output drift.

KIP-219 throttle-echo divergences

The broker reports a request-quota delay by patching the leading ThrottleTimeMs int32 of an already-encoded response, so it can only echo the delay on responses whose schema puts that field first. Every advertised API is audited by throttle_audit, which encodes each response at each advertised version and compares where ThrottleTimeMs lands against the broker’s table.

The APIs below carry ThrottleTimeMs behind another field, so the patch cannot reach it. That is a statement about the schema, not about what a client observes: whether a request-quota delay is ever applied to one of these APIs is decided separately, by its dispatch entry’s RequestQuotaPolicy. The two columns are kept apart for that reason.

  • SelfAccounted entries charge the quota in the handler and set ThrottleTimeMs on the typed response before encoding, so the buried field costs nothing and the client does see the delay.
  • ApplyFallbackAccounting entries are the ones the dispatch loop charges and delays. For the buried-field APIs below, the dispatch loop decodes the response, sets ThrottleTimeMs and encodes it again, so the client does see the delay.
  • InlineExempt entries are the APIs Kafka answers through sendResponseExemptThrottle (WriteTxnMarkers, AlterPartition, FetchSnapshot, UpdateRaftVoter) and the SASL handshake APIs, which Kafka answers in the authenticator. They are never delayed by the request quota on the ordinary path.

recorded_reach_matches_the_dispatch_registry in the audit pins the last column against the assembled dispatch registry, so a policy change on any of these APIs fails the build until this page is regenerated.

API api_key Versions Why the field cannot be patched Runtime effect
Produce 0 1-13 Sits behind the Responses array, at an offset the response header does not fix None. The handler charges its own quota and sets ThrottleTimeMs on the typed response before encoding, so the client does see the delay
ApiVersions 18 1-5 Sits behind the ApiKeys array, at an offset the response header does not fix None. The handler charges its own quota and sets ThrottleTimeMs on the typed response before encoding, so the client does see the delay
CreateDelegationToken 38 1-3 Last field, behind the principal strings, the token timestamps and the HMAC None. The dispatch loop charges the quota, then decodes the response, sets ThrottleTimeMs and encodes it again, so the client does see the delay
RenewDelegationToken 39 1-2 Last field, behind ErrorCode and the new expiry timestamp None. The dispatch loop charges the quota, then decodes the response, sets ThrottleTimeMs and encodes it again, so the client does see the delay
ExpireDelegationToken 40 1-2 Last field, behind ErrorCode and the new expiry timestamp None. The dispatch loop charges the quota, then decodes the response, sets ThrottleTimeMs and encodes it again, so the client does see the delay
DescribeDelegationToken 41 1-3 Last field, behind ErrorCode and the variable-length token list None. The dispatch loop charges the quota, then decodes the response, sets ThrottleTimeMs and encodes it again, so the client does see the delay
OffsetDelete 47 0 Leads with ErrorCode; the field is at a fixed offset of 2, but the dispatch loop patches leading fields only None. The dispatch loop charges the quota, then decodes the response, sets ThrottleTimeMs and encodes it again, so the client does see the delay