KIP coverage and conformance tests

Per-KIP status, conformance test inventory, cross-client matrix, and known gaps. The Kafka API matrix breaks the coverage down per RPC; this page is the higher-level KIP view.

KIP coverage

KIP Title Status Evidence
KIP-8 Replica Fetcher Done InterBrokerApiHandler.UpdateMetadata
KIP-98 Exactly-Once Delivery (idempotent + txns) Done TransactionCoordinator, Kip892TransactionDefenseTests, six TransactionTests (commit, abort, isolation, mixed, fencing, consume-process-produce) round-trip via Confluent.Kafka. The five EOS RPCs (22, 24, 25, 26, 28) dispatch through the SurgewaveBroker.ProcessRequestAsync fast-path switch.
KIP-113 Replicas to log dirs Wired (rejects) Surgewave has a single data dir per broker, so AlterReplicaLogDirs rejects every partition with LogDirNotFound. DescribeLogDirs returns the single LogDirResult with per-partition sizes.
KIP-455 Partition reassignment Done PartitionReassignmentManager with full 4-state machine (Pending → Adding → Syncing → Completing → Completed); wired through ClusterAdminHandler for AlterPartitionReassignments / ListPartitionReassignments.
KIP-460 Admin leader-election RPC Done ClusterAdminHandlerClusterController.ElectLeaderAsync with three-stage fallback (Preferred → ISR → Unclean).
KIP-496 Allow consumers to delete offsets Done ConsumerGroupCoordinator.HandleOffsetDelete — conservative read of GROUP_SUBSCRIBED_TO_TOPIC for groups with active members.
KIP-516 Topic Identifiers Done Metadata/Fetch/Produce carry Uuid topic IDs
KIP-554 SCRAM credential admin RPCs Done SecurityApiHandler + ScramCredentialStore. Two stores (SHA-256 + SHA-512) stand up in-memory when SCRAM is in SaslMechanisms. The wire RPC is the canonical provisioning path.
KIP-595 KRaft (ZK-less consensus) Done RaftApiHandler, 5 quorum APIs (52–55, 59)
KIP-664 DescribeProducers Done TransactionCoordinator.HandleDescribeProducers projects ProducerStateManager state per (topic, partition).
KIP-714 Client Telemetry Reporting Done (wire + log/meter ingestor + topic sink) TelemetryApiHandler + ITelemetryIngestor. Default LoggingTelemetryIngestor logs and meters every push. Optional TelemetryTopicSink mirrors raw OTLP MetricsData blobs to an internal topic. Server-side decode of the OTLP proto into per-metric Meter instruments remains a future enhancement.
KIP-848 Consumer Group v2 Done ConsumerGroupV2*, Kip848WireTests, Kip848ConsumerProtocolTests
KIP-853 Dynamic Raft Voter Changes Wire bound + foundation in place — semantics not implemented RPCs 80/81/82 advertise and bind to a polite-rejection handler. Pre-validation rejects malformed requests with InvalidRequest (42). Foundation building blocks: RaftConfiguration immutable record + monotonic ConfigurationSequence, reserved MetadataCommandType.VoterChange enum slot. Implementation plan: internal voter-changes design doc.
KIP-892 Transaction Coordinator Schemas Done Kip892TransactionDefenseTests
KIP-894 Tiered Storage (Remote Log) Partial RemoteLogSegmentState, IRemoteStorageProvider plug-points exist; remote fetch path is fenced behind config
KIP-895 KRaft broker registration v2 Done ClusterMembershipHandler (62, 63, 64)
KIP-903 Fetch v15 (replicaId removed) Done FetchRequest.Reader flexible-version parsing
KIP-932 Share Groups (queue-style) Done ShareGroupApiHandler, ShareGroupCoordinator, Kip932WireTests
KIP-936 SASL/OAUTHBEARER on the wire Done OAuthBearerAuthenticator + JwksTokenValidator, wired into SaslAuthenticator; OIDC discovery + direct JWKS, audience/issuer/principal-claim configurable; 12 unit tests + 5 integration tests with an in-process IdP fixture.
KIP-966 Paginated DescribeTopicPartitions Done MetadataApiHandler.HandleDescribeTopicPartitions honours ResponsePartitionLimit + StartingCursor; emits NextCursor when truncated.
KIP-985 Reverse range iteration Done IReadOnlyKeyValueStore.ReverseAll() / .ReverseRange()
KIP-994 Transactional ListTransactions filters Done Kip994ListTransactionsFiltersTests (8 semantic) + Kip994ListTransactionsWireTests (4 wire-roundtrip across v0/v1/v2). Pathological-regex defence ((a+)+$ Cox bait) caught by MatchTimeout plus a RegexMatchTimeoutException swallow.
KIP-1071 Streams Groups (topology-aware) Done StreamsGroupApiHandler, StreamsGroupCoordinator
KIP-1106 ListConfigResources for KIP-714 Done TelemetryApiHandler.HandleListConfigResources returns the configured metric subscriptions when telemetry is enabled.
KIP-1152 TransactionalIdPattern filter Done Wire-parsed at v2+; the regex DoS defence (timeout + exception swallow) fires on this code path too.
KIP-1242 ApiVersions v5 (ClusterId/NodeId + REBOOTSTRAP_REQUIRED) Done ApiVersionsRequest.cs reads + writes the v5 ClusterId (nullable compact string) + NodeId (int32) fields so the trailing tagged-fields varint stays aligned. MetadataApiHandler.HandleApiVersions triggers REBOOTSTRAP_REQUIRED (129) when a v5+ request carries a non-null ClusterId that doesn't match the broker's actual cluster ID OR a NodeId (!= -1) that doesn't match the broker's BrokerId. v4 requests don't carry these fields and are never rejected. Kip1242ApiVersionsV5Tests pins the wire framing + error-code value; Kip1242RebootstrapTriggerTests (5) pins the handler-side trigger (match passes, null/-1 passes, ClusterId mismatch → Rebootstrap, NodeId mismatch → Rebootstrap, v4 ignores the fields).
KIP-1226 Share-partition lag in DescribeShareGroupOffsets v1 Done DescribeShareGroupOffsetsResponse.DescribePartitionResult.Lag (int64, default -1) on the wire, v1-gated in both WriteTo and ReadFrom. ShareGroupCoordinator.HandleDescribeShareGroupOffsets populates it via log.HighWatermark - group.StartOffsets[key] (floor 0) in both code paths — explicit topic list AND "all subscribed topics" fallback. Kip1226ShareGroupLagTests pins v0 drops the field (no framing drift) and v1 round-trips the lag value.
KIP-1222 Renew acknowledgements in ShareAcknowledge v2 Wire pinned; lease-renew semantics deferred ShareAcknowledgeRequest carries the v2 IsRenewAck bool plus the new AcknowledgeType = 4 (Renew) value in the int8 array. ShareAcknowledgeResponse carries AcquisitionLockTimeoutMs (int32, v2+, ignorable). Both are v2-gated in WriteTo and ReadFrom. The actual lease-renew handling inside ShareGroupCoordinator is a follow-up — Surgewave currently treats a Renew ack as a no-op pass-through, which is wire-safe but doesn't actually extend the in-flight lock. Kip1222ShareAckRenewTests pins the v2 round-trip (IsRenewAck + AcknowledgeType=4) and a v1 regression that the v2 fields stay off the wire.
KIP-1319 Topic IDs in TxnOffsetCommit v6 Done TxnOffsetCommitRequest/Response model refactored from name-keyed Dictionary to List<TxnOffsetCommitTopic> with both Name? (v0-5) and TopicId (v6+). WriteTo/ReadFrom pick the right wire field per version. Both TransactionCoordinator and ClusteredTransactionCoordinator resolve TopicId → Name via LogManager.GetTopicMetadataById at v6 entry and return UNKNOWN_TOPIC_ID per partition when unresolvable; pre-v6 paths back-fill TopicId so the response can carry it at v6 if needed. Advertised version bumped to 6. Kip1319TxnOffsetCommitV6Tests pins v5 (Name) and v6 (TopicId) wire framing for both request and response.
KIP-1251 Per-partition assignment epochs + per-partition fence-check (CGv2 OffsetCommit) Done TopicPartitionAssignment carries AssignmentEpochs: List<int>? aligned with Partitions; TargetAssignmentComputer.ApplyAssignment snapshots the previous (TopicId, Partition) → epoch map before recomputing so partitions that stay with the same member keep their stable per-partition epoch (newly assigned ones get the current GroupEpoch). On the commit-fence side, ConsumerGroupV2Coordinator.IsPartitionAssignmentValid does the per-partition check; CommitOffsetsForV2Group calls it per partition and emits STALE_MEMBER_EPOCH for partitions where the request's member epoch is below the partition's assignment epoch. The pre-flight ValidateMemberForOffsetOperation no longer fences on memberEpoch < member.MemberEpoch — that decision is deferred to the per-partition check so the KIP's motivating case (old member commits for partitions it still holds across a rebalance) actually works. Future-epoch claims still get FencedMemberEpoch at the pre-flight. Legacy persisted state without AssignmentEpochs falls back to strict group-level equality. Kip1251PerPartitionEpochTests (4) pins the assignor population; Kip1251FenceCheckTests (8) pins the fence-check (current/older/future epochs, owned/non-owned partitions, unknown group/member, legacy-state fallback, pre-flight loosening). All 869 broker tests continue to pass.
KIP-1240 Share-group configs (share.delivery.count.limit, share.partition.max.record.locks, share.renew.acknowledge.enable) Renew-gate enforced + admin-wired; delivery-count / max-locks enforcement still follow-up ShareGroupState carries the three configs with the upstream defaults (5 / 2000 / true). share.renew.acknowledge.enable is enforced inline in ShareGroupCoordinator.ProcessAcknowledgementBatches: any RENEW (AckType=4) in an incoming batch returns INVALID_REQUEST per partition when the gate is closed, checked up-front so the rejection takes precedence over QueueView absence. MaxDeliveryCount and MaxRecordLocks are forward-compat state today — Surgewave's QueueView has no per-partition lock-cap or archive-on-overflow logic yet. Admin-side wire-up landed in a follow-up commit: IncrementalAlterConfigsRequest with ResourceType.Group (32) routes through ConfigApiHandlerShareGroupCoordinator.SetShareGroupConfig, with upstream clamps enforced (delivery.count.limit ∈ [2,10], max.record.locks ∈ [100,10000]). DELETE op restores the upstream default. Kip1240ShareGroupConfigTests (5) pins the state + renew-gate behaviour; Kip1240IncrementalAlterConfigsTests (6) pins the admin path (set persists, clamps enforced, DELETE restores default, unknown config name → InvalidConfig, ValidateOnly is non-mutating).
KIP-1196 Coordinator-internal buffer-pool caps (group.coordinator.cached.buffer.max.bytes, share.coordinator.cached.buffer.max.bytes) Config captured; buffer-reuse pool follow-up BrokerConfig.GroupCoordinatorCachedBufferMaxBytes and BrokerConfig.ShareCoordinatorCachedBufferMaxBytes carry the two new broker-level caps with the upstream defaults (1 MiB + 12-byte log-record overhead) and a [Range(524288, int.MaxValue)] validator matching the upstream atLeast(512 * 1024) lower bound. Buffer-reuse pool wiring inside the coordinators is a follow-up — Surgewave allocates per-write today, which is fine for the current throughput envelope but leaves a few % on the table at sustained high-rate group-metadata churn. Kip1196CoordinatorBufferConfigTests (8) pins the property names by reflection (so a future rename can't silently detach the test from KIP-1196) plus the defaults, the RangeAttribute lower bound, custom-value acceptance, and below-bound validation failure for both properties.
KIP-1263 Coordinator background threads + assignment intervals (group.coordinator.background.threads, group.{consumer,share,streams}.assignment.interval.ms) Config captured; assignment-throttle + background-pool wiring follow-up BrokerConfig.GroupCoordinatorBackgroundThreads (default 2, atLeast(1)) plus three matching assignment-interval properties (one per group type, default 1000 ms, atLeast(0)). Surgewave's TargetAssignmentComputer already runs only on material membership/subscription change, so the time-based assignment-interval gate is a follow-up rather than a critical gap — Surgewave is less prone to the upstream thrash that motivated this KIP. Background-thread pool wiring for regex-subscription updates is also a follow-up: today the regex path runs inline on the heartbeat thread. Kip1263CoordinatorTuningConfigTests (11) pins property names by reflection, the upstream defaults (2 / 1000 / 1000 / 1000), the lower bounds (1 / 0 / 0 / 0), and that below-bound values fail Validator.TryValidateProperty.
KIP-1161 Stricter LIST-type config validation + num.replica.fetchers >= 1 Done Two pieces: (a) DynamicBrokerConfig.ValidateConfigValue enforces num.replica.fetchers >= 1 (the new lower bound — pre-1161 a value of 0 silently disabled replica fetching). Surgewave promotes the config to DynamicConfigKeys so admins can tune it via IncrementalAlterConfigs with the bound checked at set-time. (b) BrokerConfig.Validate ships a generic ValidateListConfig helper that runs against the four LIST-type properties (Security.SaslMechanisms, Security.Users, Security.SuperUsers, Security.OAuth2.AllowedAlgorithms): null/blank entries reject outright; duplicate entries are deduplicated in-place with the dropped count surfaced as a warning-shaped error. The per-config comparer matters — SaslMechanisms/AllowedAlgorithms are OrdinalIgnoreCase (PLAIN == plain, RS256 == rs256), Users/SuperUsers are Ordinal (distinct credentials per case-distinct username). Kip1161ListConfigValidationTests (8) pins all six branches: num.replica.fetchers accepts/rejects 0/-3/4, blank entry rejected, duplicates deduplicated, no-duplicates passes clean, case-sensitivity preserved per-config.

Conformance test infrastructure

Suite Location What it pins down
Wire encoder / decoder tests/Kuestenlogik.Surgewave.Protocol.Kafka.Tests/Kip848WireTests.cs, Kip932WireTests.cs, Kip994ListTransactionsWireTests.cs, KafkaProtocolHandlerTests.cs Frame parsing, flexible-version tagged fields, byte-level equivalence for KIP-848 / KIP-932 / KIP-994 messages.
API coverage matrix tests/Kuestenlogik.Surgewave.IntegrationTests/KafkaProtocolApiCoverageTests.cs Confluent.Kafka.Admin exercises Metadata, Produce, Fetch, ApiVersions against an embedded broker; verifies advertised-versions match what the broker actually services.
Confluent.Kafka round-trip tests/Kuestenlogik.Surgewave.IntegrationTests/ConfluentKafkaCompatibilityTests.cs End-to-end producer + consumer using Confluent.Kafka 2.x against an embedded Surgewave broker.
KIP-848 client interop tests/Kuestenlogik.Surgewave.IntegrationTests/Kip848ConsumerProtocolTests.cs, Kip848DiagnosticTests.cs librdkafka 2.14 next-gen consumer flow; CGv2 group join, server-side assignment, rebalance.
KIP-892 transaction defense tests/Kuestenlogik.Surgewave.Broker.Tests/Transactions/Kip892TransactionDefenseTests.cs Producer epoch fencing, abort-on-timeout, two-phase commit.
KIP-994 list-transactions filters tests/Kuestenlogik.Surgewave.Broker.Tests/Transactions/Kip994ListTransactionsFiltersTests.cs producerIdFilter + statesFilter + DurationFilter + TransactionalIdPattern semantics, pathological-regex DoS defence.
KIP-936 OAUTHBEARER e2e tests/Kuestenlogik.Surgewave.IntegrationTests/OAuthBearerSaslIntegrationTests.cs, OAuthBearerValidatorProbeTests.cs, Fixtures/OAuthBearerBrokerFixture.cs In-process IdP serves OIDC discovery + JWKS over HttpListener; embedded broker validates a freshly-signed RS256 JWT presented through Confluent.Kafka's OAuthBearerTokenRefreshHandler. Asserts produce + consume round-trip and that a wrong-issuer token is rejected.
Schema Registry contract tests/Kuestenlogik.Surgewave.Schema.Registry.Tests/ConfluentSchemaRegistryContractTests.cs 28 tests pin the JSON-shape contract — error_code (snake_case), is_compatible (snake_case), compatibilityLevel (camelCase), schema-type uppercase mapping.

Cross-client matrix

Client Producer Consumer (classic) Consumer (CGv2) Transactions Share Groups Notes
Confluent.Kafka 2.x (.NET) Tested Tested Tested Tested Not tested e2e Primary in-tree integration suite.
librdkafka 2.14 (C / native) Tested Tested Tested Tested Not tested e2e Driven through Confluent.Kafka.
Kafka Java client Not tested Not tested Not tested Not tested Not tested On the follow-up backlog.
Sarama / kgo (Go) Not tested Not tested Not tested Not tested Not tested On the follow-up backlog.
kafka-python / aiokafka Not tested Not tested Not tested Not tested Not tested On the follow-up backlog.

Known gaps

  1. KIP-714 server-side OTLP decode. The wire is bound, payloads are logged + metered, and the optional topic sink mirrors raw OTLP bytes to an internal topic. Decoding the proto into per-metric Meter instruments on the broker would surface client metrics in the broker's own dashboards without an external collector — this is a future enhancement; the topic-sink path already covers the common operator workflow (collector reads the topic, feeds Prometheus/Grafana).
  2. All admin RPCs are now wired. Previous editions of this list called out ElectLeaders, AlterPartitionReassignments, ListPartitionReassignments, DescribeLogDirs, AlterReplicaLogDirs, DescribeUserScramCredentials, AlterUserScramCredentials, ListConfigResources as missing — those are now bound to either a real handler (most of the list) or a polite-reject handler (AlterReplicaLogDirs rejects every partition with LogDirNotFound because Surgewave has no JBOD; KIP-853 voter-change RPCs reject with UnsupportedVersion because Surgewave's Raft has no online reconfiguration yet).
  3. KIP-853 online voter changes are not implemented. RPCs 80/81/82 are wire-bound but reject with a documented UnsupportedVersion. Operators reconfigure the static voter set and restart. Implementing the joint-consensus / single-server-change protocol is tracked separately — see the internal voter-changes design doc.
  4. Cross-client conformance matrix currently covers only the librdkafka / Confluent.Kafka path. Java, Go, and Python client e2e suites are scheduled.