Documentation · Configuration

Configuration reference

Every YAML option consumed by the config loader, with defaults and constraints.

The loader accepts one YAML file via --config <path>. Omit optional sections unless they are needed. Relative password_file, cert_file, key_file, and ca_file paths resolve against the config file’s directory.

Structure

proxy:
  listener_sets:
    - name: plaintext
      auth:
        mode: required
      port_range:
        bind_host: "0.0.0.0"
        advertised_host: "proxy.example.com"
        bootstrap_port: 9092
        broker_port_range: { start: 9093, end: 9192 }
        broker_id_base: 1
    - name: tls-sni
      auth: { mode: required }
      tls:
        cert_file: "certs/server.pem"
        key_file: "certs/server-key.pem"
      sni:
        bind: "0.0.0.0:9443"
        bootstrap_host: "bootstrap.kafka.example.com"
        broker_host_template: "broker-{broker_id}.kafka.example.com"

admin:
  bind: "127.0.0.1:28080"

acl_admin:
  mode: disabled

control_plane:
  discovery:
    refresh_interval_ms: 5000
    deadline_ms: 1000
    stale_grace_period_ms: 300000
  probe:
    refresh_interval_ms: 5000
    deadline_ms: 1000
    suspect_backoff_initial_ms: 250
    suspect_backoff_max_ms: 5000
    suspect_backoff_jitter_ms: 100
    healthy_recycle_interval_ms: 300000
    authenticated_unbound_timeout_ms: 30000

upstream:
  bootstrap:
    - "broker-1.kafka.example.com:9092"
    - "broker-2.kafka.example.com:9092"
  connect_timeout_ms: 5000
  max_unauthenticated_upstreams: 64
  tls:
    ca_file: "certs/upstream-ca.pem"
  discovery:
    sasl:
      mechanism: PLAIN
      username: "klamm-discovery"
      password_file: "secrets/discovery.password"

tenants:
  - name: tenant-a
    allowed_topics: [orders]
    topic_deletion: { enabled: false }
    limits:
      max_connections: 100
      max_inflight: 500
    auth:
      credentials:
        - username: tenant-a
          password_file: "secrets/tenant-a.password"
          upstream:
            sasl:
              mechanism: PLAIN
              username: "__vt_tenant-a_tenant-a"
              password_file: "secrets/tenant-a-upstream.password"

Core rules

  • proxy.listener_sets is the public listener model. Each listener set must choose exactly one allocation strategy: port_range or sni.
  • proxy.listener_sets[].port_range creates one bootstrap listener and a deterministic broker route range. Broker route port is broker_port_range.start + (broker_id - broker_id_base). Discovered broker IDs outside the configured range are not exposed and protocol rewrites fail closed instead of leaking upstream addresses.
  • proxy.listener_sets[].sni creates one TLS listener whose bootstrap and broker routes are distinguished by TLS Server Name Indication. It requires tls on the same listener set.
  • proxy.listener_sets[].auth.mode defaults to required. mode: none requires auth.tenant and binds every downstream connection on that listener set to the configured tenant as user anonymous; the tenant must exist. Setting auth.tenant while mode is required is rejected.
  • admin is optional. If present, admin.bind must be a socket address and must not conflict with a proxy listener bind address.
  • acl_admin.mode defaults to disabled. In this mode Klamm does not advertise CreateAcls, DescribeAcls, or DeleteAcls, and rejects those requests if a client sends them anyway.
  • acl_admin.mode: broker-delegated enables ACL Admin API virtualization through the upstream Kafka broker. It requires credential-bound downstream authentication on every listener set, requires every tenant credential to define upstream.sasl, and requires upstream.sasl.username to equal __vt_{tenant}_{username} so broker runtime authorization uses the same physical principal namespace as ACL Admin API rewrites.
  • upstream.bootstrap must contain at least one upstream broker bootstrap address. Broker membership and broker IDs are discovered through Kafka Metadata responses; do not list the upstream broker inventory in Klamm config.
  • upstream.tls.ca_file enables TLS to upstream brokers.
  • proxy.listener_sets[].sni.bootstrap_host and every generated broker_host_template hostname must resolve to the proxy and be covered by the downstream TLS certificate SANs.
  • control_plane can be omitted; defaults are listed in the control plane table below.
  • tenants can be omitted or empty, but no downstream credential can authenticate until a matching tenant credential is configured.
  • Tenant names must use only lowercase ASCII letters, digits, and -, must be at most 40 characters, and cannot contain _.
  • allowed_topics omitted means dynamic namespace mode. allowed_topics: [] means static deny-all mode. A non-empty list means static allow-list mode. allowed_topics: null is rejected.
  • Logical topic names in allowed_topics must be non-empty, must not start with __, and may only use ASCII letters, digits, ., _, and -. Prefixed physical topic names must fit Kafka’s 249-character topic-name limit.
  • topic_deletion.enabled defaults to false and must be true for DeleteTopics to pass, regardless of topic mode.
  • Downstream tenant credentials support SASL/PLAIN, SCRAM-SHA-256, and SCRAM-SHA-512. SCRAM uses password_file by default, or the per-mechanism override fields when configured.
  • Credential-bound upstream SASL is optional unless acl_admin.mode: broker-delegated is enabled.
  • upstream.discovery.sasl is optional control-plane authentication for Metadata discovery. It is not a tenant impersonation mechanism and is not used for forwarded tenant traffic.
  • SIGHUP reloads tenant and credential changes. Changes to listener sets, admin, ACL Admin mode, control-plane settings, upstream bootstrap, upstream TLS, and upstream connection limits require a restart.

Listener sets

OptionDefaultNotes
proxy.listener_sets[].namerequiredStable listener-set name used in route status and diagnostics. Must be unique.
proxy.listener_sets[].auth.moderequiredDownstream authentication mode. required uses SASL/PLAIN, SCRAM-SHA-256, or SCRAM-SHA-512. none skips downstream SASL and binds the listener set to auth.tenant.
proxy.listener_sets[].auth.tenantunsetRequired only for auth.mode: none; rejected for required-auth listener sets.
proxy.listener_sets[].tls.cert_fileunsetEnables TLS for a downstream listener set. Required for sni listener sets.
proxy.listener_sets[].tls.key_fileunsetPrivate key for the downstream TLS listener set. Required for sni listener sets.
proxy.listener_sets[].port_range.bind_hostunsetLocal interface used for both the bootstrap listener and broker route listeners in the port-range set.
proxy.listener_sets[].port_range.advertised_hostunsetHost advertised to clients for the bootstrap listener and all broker route listeners in the port-range set.
proxy.listener_sets[].port_range.bootstrap_portunsetClient-facing bootstrap port for this listener set. Must not overlap the broker route range or other proxy/admin binds.
proxy.listener_sets[].port_range.broker_port_range.startunsetFirst client-facing broker route port. Broker route port is start + (broker_id - broker_id_base).
proxy.listener_sets[].port_range.broker_port_range.endunsetInclusive last client-facing broker route port. Broker IDs beyond this range are intentionally unmapped.
proxy.listener_sets[].port_range.broker_id_base0Broker ID mapped to broker_port_range.start. Use 1 for clusters whose broker IDs start at 1.
proxy.listener_sets[].sni.bindunsetTLS bind address for SNI routing. Must include host and port.
proxy.listener_sets[].sni.bootstrap_hostunsetBootstrap hostname for SNI routing. Must be non-empty, resolve to the proxy, and be covered by the downstream TLS certificate.
proxy.listener_sets[].sni.broker_host_templateunsetBroker hostname template for SNI routing. Must contain exactly one {broker_id} placeholder; generated names must resolve to the proxy and be covered by the downstream TLS certificate.

Upstream

OptionDefaultNotes
upstream.bootstrap[]requiredOne or more upstream Kafka bootstrap addresses. Klamm discovers broker membership and broker IDs from Metadata.
upstream.connect_timeout_ms5000TCP/TLS/SASL upstream connection timeout in milliseconds. Must be greater than zero.
upstream.max_unauthenticated_upstreams64Limit for concurrent pre-auth upstream connections used while handling bootstrap ApiVersions. Must be greater than zero.
upstream.tls.ca_fileunsetCA bundle for server-auth TLS to upstream brokers.
upstream.discovery.sasl.mechanismunsetOptional control-plane Metadata discovery SASL mechanism. Supports PLAIN, SCRAM-SHA-256, and SCRAM-SHA-512.
upstream.discovery.sasl.usernameunsetUpstream Kafka username used only by control-plane Metadata discovery.
upstream.discovery.sasl.password_fileunsetPassword file for the control-plane Metadata discovery SASL user.

Tenants

OptionDefaultNotes
tenants[].limits.max_connections100Per-tenant downstream connection limit for authenticated sessions.
tenants[].limits.max_inflight500Per-tenant maximum in-flight requests per authenticated session.
tenants[].auth.credentials[].scram_sha256_password_fileunsetOptional SCRAM-SHA-256 password file for this username. Defaults to password_file when unset.
tenants[].auth.credentials[].scram_sha512_password_fileunsetOptional SCRAM-SHA-512 password file for this username. Defaults to password_file when unset.
tenants[].auth.credentials[].upstream.sasl.mechanismunsetCredential-bound upstream SASL. Supports PLAIN, SCRAM-SHA-256, and SCRAM-SHA-512.
tenants[].auth.credentials[].upstream.sasl.usernameunsetUpstream Kafka username used after this downstream credential authenticates.
tenants[].auth.credentials[].upstream.sasl.password_fileunsetUpstream Kafka password file for the credential-bound SASL user.

ACL Admin

OptionDefaultNotes
acl_admin.modedisableddisabled hides and rejects ACL Admin APIs. broker-delegated enables broker-backed ACL Admin API virtualization and requires required-auth listener sets plus credential-bound upstream SASL usernames matching __vt_{tenant}_{username}.

Control plane

OptionDefaultNotes
control_plane.discovery.refresh_interval_ms5000Interval between upstream Metadata discovery refreshes. Must be greater than zero.
control_plane.discovery.deadline_ms1000Per-refresh Metadata discovery deadline. Must be greater than zero.
control_plane.discovery.stale_grace_period_ms300000Grace period before brokers absent from successful Metadata refreshes are retired. Must be greater than zero.
control_plane.probe.refresh_interval_ms5000Interval between upstream broker health probes. Must be greater than zero.
control_plane.probe.deadline_ms1000Per-probe deadline. Must be greater than zero.
control_plane.probe.suspect_backoff_initial_ms250Initial backoff for suspect brokers. Must be greater than zero.
control_plane.probe.suspect_backoff_max_ms5000Maximum suspect-broker backoff. Must be at least suspect_backoff_initial_ms.
control_plane.probe.suspect_backoff_jitter_ms100Probe backoff jitter in milliseconds.
control_plane.probe.healthy_recycle_interval_ms300000Interval for recycling healthy probe connections. Must be greater than zero.
control_plane.probe.authenticated_unbound_timeout_ms30000How long an authenticated downstream session may remain without a bound upstream connection. Must be greater than zero.