Tagged enum on type, mirroring Strimzi. The wire shape is flat (Strimzi-compatible): type is the discriminator and per-variant config fields are siblings of type. The custom schema_with hand-rolls a structural schema because kube-rs 3.x's StructuralSchemaRewriter panics when oneOf branches share a type property with differing enum values (the default schemars output for tagged-union enums). Same workaround as Storage in kafka_node_pool.rs. Cross-variant constraints (e.g. "iterations only valid when type=scram-sha-512") are enforced by the operator at reconcile time, not by the apiserver.
authentication.iterations
integer
no
authentication.maxLifetimeMs
integer
no
authentication.passwordLength
integer
no
authentication.renewBeforeExpiryMs
integer
no
authentication.renewalDays
integer
no
authentication.renewers
array
no
authentication.type
string
yes
authentication.validityDays
integer
no
authorization
object
no
Authorization is optional — a user with no ACLs can still authenticate. When absent, ACL reconciliation is skipped.
quotas
object
no
Optional per-user client quotas (KIP-13/124/257). Maps onto Kafka's (user) quota entity via AlterClientQuotas (api_key 49). Absent → operator does not touch the broker's quota state; present → the operator drives the broker's quota keys for User:<name> toward the spec (sets configured fields, tombstones omitted ones).
quotas.consumerByteRate
integer
no
Maximum consume-side bytes/sec. Backed by consumer_byte_rate.
quotas.controllerMutationRate
number
no
KIP-599 controller-mutation rate (creates/deletes/sec). Backed by controller_mutation_rate.
quotas.producerByteRate
integer
no
Maximum produce-side bytes/sec. Backed by producer_byte_rate.
quotas.requestPercentage
integer
no
Maximum percentage of a request-handler thread's time the user may consume (0..=100). Backed by request_percentage.
Status
Field
Type
Required
Default
Description
conditions
array
no
[]
Standard Kubernetes-style condition list. Surfaces Ready.
delegationTokenExpiryTimestampMs
integer
no
Current expiry_timestamp_ms of the operator-managed delegation token (extended on each successful renew). Compared against now to decide when to renew per spec.authentication.renewBeforeExpiryMs.
delegationTokenId
string
no
Persisted token_id (UUID) of the operator-managed delegation token for this user. Used across reconciles to find the same token via DescribeDelegationToken. Present once the operator has successfully issued a token via CreateDelegationToken.
delegationTokenMaxTimestampMs
integer
no
Token's absolute upper bound (max_timestamp_ms). Renew can never push expiry_timestamp_ms past this — the operator stops renewing and surfaces TokenExpiring once further extension is impossible.
external
boolean
no
false
true once a credential-less user (type: tls-external) has been reconciled. Surfaces in kubectl describe ku so operators can tell at a glance that the operator does not own this user's credentials.
observedGeneration
integer
no
metadata.generation of the last successfully-reconciled spec.
quotasInSync
boolean
no
false
True once the spec's quotas (if any) have been reflected in the broker's (user) client-quota state. False when spec.quotas is None (the operator does not touch broker quotas).
scramSha256
boolean
no
false
True once SCRAM-SHA-256 credentials are provisioned.
scramSha512
boolean
no
false
True once SCRAM-SHA-512 credentials are provisioned.
secret
string
no
Name of the Kubernetes Secret holding the user's password.
tls
boolean
no
false
true once a TLS user has a current cert Secret. Mirrors scram_sha512.
tlsCertNotAfter
string
no
RFC3339 timestamp of the user cert's notAfter. Present when tls == true.
tlsPrincipal
string
no
The principal string the operator pinned in ACLs (e.g. User:CN=alice for TLS users, User:alice for SCRAM and tls-external users). Always populated when the user is provisioned. Load-bearing for debugging "why isn't my ACL matching" issues.
username
string
no
Effective Kafka principal name (matches metadata.name).