The Open Source Kafka Control Plane
Secure, vendor-neutral, operations-first visibility for Apache Kafka.
Getting Started · Features · Configuration · Security · Contributing · Support
Kafdeck is a self-hosted control plane for Apache Kafka designed for operators who need useful Kafka visibility without turning the management UI into another reliability or security risk.
It runs outside the Kafka data path, supports local/on-premise and air-gapped deployments, uses bounded read operations, and keeps metadata access, record access, export, identity, and masking as separate security concerns.
Important
v0.7 is the current release line. Read-only remains the default operating posture. v0.7 adds governed Schema Registry lifecycle and developer tooling, multi-profile Kafka Connect with bounded auto-restart, controlled SerDe and finite data jobs/generation, bounded ksqlDB and Streams/lineage evidence, plus a keyboard-first Command Palette. Governed mutations remain opt-in, and unsupported or unsafe provider capabilities remain explicit Blocked/Unsupported states rather than falling through to a generic provider/CLI escape path.
- Safe by default — bounded work, cancellation, deadlines, rate/byte limits, per-cluster isolation, and fail-closed behavior.
- Read access is not data access — metadata permissions never automatically grant Kafka payload visibility.
- Server-side masking — sensitive record data is redacted before it reaches the browser, API client, or export path.
- No mandatory data-plane proxy — Kafdeck stays out of the Kafka message path.
- Zero-write observation — read-only Kafdeck does not create internal Kafka topics or need Kafka write access to store its own state.
- Vendor-neutral core — Apache Kafka semantics define the baseline; ecosystem/provider integrations are capability-gated.
- On-premise and air-gapped friendly — core runtime functionality has no required SaaS dependency.
- Governed OSS engineering — architecture, security, compatibility, release evidence, and project decisions are versioned in the repository.
Stable releases are published through GitHub Releases. v0.7 is the active/current release identity selected by the governed release manifest. The protected-main publication workflow owns the immutable source tag, GitHub Release and GHCR promotion for that identity. The main branch can also contain capabilities that have passed implementation gates but are not part of a later published release.
| Current capability | Introduced | Current v0.7 posture |
|---|---|---|
| Cluster & broker explorer | v0.1 | Available; bounded, cancellable, evidence-based |
| Operator Identity / OIDC / RBAC | v0.2 | Available; OIDC is the recommended multi-user production mode |
| Safe Data Explorer + server-side masking | v0.3 | Available; payload access/export remain separately authorized |
| Consumers / Schemas / Ecosystem observations | v0.4 | Available and extended by later releases |
| Governed Kafka administration | v0.5 | Available when mutation mode and durable prerequisites are explicitly enabled |
| Fleet capability boundary / secure remote UI | v0.6 | Capability-driven; unsupported or unsafe operations remain explicit Blocked/Unsupported |
| Developer & Streaming Ecosystem Platform | v0.7 | Active release; governed Schema/Connect/data tooling, bounded streaming integrations, Command Palette and evidence-driven status truth |
See ROADMAP.md for the capability roadmap and docs/releases/ for exact release evidence.
The release table above records when capability families first appeared. The sections below describe the current v0.7 behavior.
- multiple immutable configuration-driven Kafka clusters,
- broker/controller metadata,
- topics, partitions, leaders, replicas and ISR,
- topic and broker configuration inspection where authorized/supported,
- evidence-based cluster/partition health,
- bounded snapshots, deadlines, cancellation, stale/partial semantics and per-cluster isolation.
- Local, deployment-token and OIDC access modes,
- OIDC Authorization Code + PKCE with server-side sessions,
- immutable default-deny RBAC in OIDC mode,
- subject/group bindings,
- action-, cluster- and resource-scoped authorization,
- backend-authoritative enforcement for UI and API clients,
- structured security audit events,
- deployment access tokens held in page memory only after URL-fragment bootstrap.
- bounded record browsing by topic/partition,
- earliest/latest/offset/timestamp navigation and previous-page reads,
- bounded live tail,
- key/value/header inspection,
- raw, UTF-8, binary/hex and structured projections,
- Schema Registry-assisted Avro / Protobuf / JSON Schema decoding,
- controlled CBOR / XML / MessagePack SerDe,
- bounded filtering,
- explicit record/byte/time/rate/concurrency budgets,
- server-side masking before UI/API/export,
- separate
record.readandrecord.exportauthorization, - bounded JSON / NDJSON / CSV export,
- no payload persistence by default.
- consumer groups, states, members and assignments,
- committed/end offsets and per-partition/aggregate lag,
- explicit missing / unauthorized / out-of-range states,
- evidence-based inactive/stalled diagnostics,
- metrics/history only when a trustworthy provider exists — unknown data is never fabricated as zero.
- subjects, versions, schema IDs, formats, content and references,
- Avro, Protobuf and JSON Schema,
- bounded reference graph,
- deterministic schema diff,
- compatibility inspection and explanation,
- bounded deterministic mock examples,
- governed registration, compatibility changes and delete lifecycle where the configured provider supports them,
- explicit provider capability truth: Confluent-compatible baseline, tested Karapace-compatible profile, and Apicurio reported Unsupported until its distinct typed adapter is admitted,
- lifecycle writes require mutation mode and the exact authorization/risk/approval path; read access never implies schema mutation.
- multiple stable Connect profiles per Kafka cluster,
- worker, connector, task and bounded trace observations,
- plugin discovery and typed configuration validation,
- fail-closed secret/configuration projection,
- governed create/update/delete/pause/resume/restart/task-restart where provider capability and mutation policy allow it,
- legacy single
Connectconfiguration remains backward compatible as profiledefault; new deployments should useConnectProfiles[], - optional bounded auto-restart with durable attempts/backoff/lifetime/circuit state, disabled by default,
- shared MirrorMaker/replication guards prevent a generic Connect lifecycle route from bypassing Kafdeck policy.
State-changing operations are opt-in and use the same server-owned pipeline:
Request -> Authorization -> Validation -> Risk Classification -> Preview -> Confirmation/Approval -> Execute -> Verify -> Audit
Current governed families include:
- topic create/alter/delete and partition increases,
- bounded record production,
- consumer offset/group administration,
- Schema Registry lifecycle mutations,
- Kafka Connect lifecycle mutations,
- controlled DeleteRecords purge,
- finite replay/reprocess/DLQ/forwarding jobs,
- bounded Data Generator execution.
Safety properties include server-owned LOW / MODERATE / HIGH / CRITICAL risk floors, distinct-principal approval for CRITICAL operations, durable idempotency and operation state, SQLite standalone persistence, PostgreSQL multi-instance/HA persistence, cluster-wide execution slots, restart-safe leases and explicit no-blind-retry semantics for ambiguous external effects.
- finite replay, reprocess, DLQ forwarding and cross-topic/cross-cluster forwarding,
- exact frozen source/destination identity and finite count/byte/rate/duration budgets,
- durable checkpoints/fencing without durable raw-payload staging,
- deterministic generator seed,
- schema-backed or closed built-in generation sources,
- explicit destination enable policy,
- hard server-side volume/rate/time ceilings,
- one unresolved external-write batch maximum,
- ambiguous writes require explicit reconciliation and are never blindly replayed.
- bounded single-statement read-only ksqlDB
SELECTexecution, - row/byte/time/concurrency limits and cancellation,
- DDL, DML, persistent-query creation and generic SQL forwarding blocked before provider I/O,
- registered Kafka Streams application/topology evidence,
- state-store/RocksDB metrics only when registered telemetry exposes them,
- lineage edges carry provenance, confidence, observed time and
Observed/Inferredevidence kind, - inferred lineage never satisfies authorization.
Kafdeck exposes fleet capability state explicitly instead of simulating missing provider primitives. A capability may be Supported, Blocked, Unsupported, Unconfigured, Unavailable or Unknown. Kafdeck does not substitute CLI, reflection, raw protocol, sidecars or arbitrary provider HTTP calls to hide a capability gap.
- topic description, owner/team, domain, tags, documentation reference and classification,
- catalog metadata is descriptive and never grants Kafka permissions,
- keyboard-first Command Palette with Ctrl/Cmd+K,
- unified explicit status semantics for denied/unsupported/blocked/partial/stale/unavailable/unknown/approval states,
- locally bundled Tabler/frontend assets with no runtime CDN dependency,
- reduced-motion and keyboard/focus-management support.
flowchart LR
B[Browser / API client] --> A[Kafdeck HTTP API]
A --> I[Access boundary<br/>Local / Token / OIDC]
I --> R[Authorization + Risk + Audit]
R --> S[Application services]
S --> K[Kafka typed adapters]
S --> SR[Schema Registry typed adapter]
S --> C[Kafka Connect typed adapter]
S --> Q[Bounded ksqlDB adapter]
S --> T[Registered Streams telemetry]
S --> J[Durable mutation / job coordinator]
K --> KF[(Apache Kafka)]
SR --> REG[(Schema Registry)]
C --> CON[(Kafka Connect)]
Q --> KSQL[(ksqlDB)]
T --> APP[(Registered app telemetry)]
J --> K
J --> SR
J --> C
S --> M[Server-side masking / bounded projection]
M --> A
The important part is what is not in this diagram: Kafdeck is not a generic Kafka producer proxy, broker plugin, arbitrary provider HTTP/SQL console, consumer-group member for normal browsing, or mandatory data-plane gateway.
When administration is disabled, state-changing routes fail closed and Kafdeck remains observational. When administration is enabled, every admitted effect still passes through typed authorization, server-owned risk classification, preview/confirmation/approval, durable idempotency/fencing, verification and audit. Long-running data jobs and generator work remain finite and budgeted; ambiguous provider effects are not blindly retried.
A typical request flows like this:
- configuration is loaded and validated at startup;
- the deployment access boundary authenticates the caller;
- OIDC deployments apply Kafdeck RBAC before upstream I/O;
- the application invokes an admitted typed read, mutation, job or ecosystem port;
- server-owned bounds, deadlines, capabilities and authorization are enforced before provider I/O;
- record payloads are decoded and masked server-side where required;
- only a safe projection or governed operation state is returned to the client;
- security-sensitive activity is audited without persisting secrets, raw generated payloads or query-result rows.
Kafdeck releases are published as a single non-root OCI image. The current release is:
ghcr.io/araditc/kafdeck:v0.7
For production, pin the immutable v0.7 digest:
ghcr.io/araditc/kafdeck@sha256:2e089bfe4788d93e7f9b5d03fac117001daccd06c244f1017648d5ccf57535c8
Do not rely on latest.
For the Docker quick start, put Kafka and Kafdeck on the same user-defined network and advertise a Kafka listener that is reachable from the Kafdeck container:
docker network create kafdeck-demo
docker run -d --name kafdeck-kafka --network kafdeck-demo -p 9092:9092 -e KAFKA_NODE_ID=1 -e KAFKA_PROCESS_ROLES=broker,controller -e KAFKA_CONTROLLER_QUORUM_VOTERS=1@kafdeck-kafka:29093 -e KAFKA_CONTROLLER_LISTENER_NAMES=CONTROLLER -e KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,INTERNAL:PLAINTEXT,HOST:PLAINTEXT -e KAFKA_LISTENERS=CONTROLLER://:29093,INTERNAL://:19092,HOST://:9092 -e KAFKA_ADVERTISED_LISTENERS=INTERNAL://kafdeck-kafka:19092,HOST://localhost:9092 -e KAFKA_INTER_BROKER_LISTENER_NAME=INTERNAL -e KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=1 -e KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS=0 -e KAFKA_TRANSACTION_STATE_LOG_MIN_ISR=1 -e KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR=1 -e CLUSTER_ID=4L6g3nShT-eMCtK--X86sw apache/kafka:4.3.1The host can use localhost:9092; containers on kafdeck-demo use kafdeck-kafka:19092. This distinction matters because Kafka clients follow broker-advertised listeners after bootstrap.
The repository's simpler deploy/dev/docker-compose.kafka.yml advertises localhost:9092 and is intended for host-native source development, not for a second Docker container connecting to it.
This evaluation broker is intentionally PLAINTEXT and single-node. Do not copy its security posture into production.
Create appsettings.Production.json:
{
"Kafdeck": {
"Deployment": {
"ListenUrls": [
"http://0.0.0.0:8080"
],
"AccessMode": "Token",
"AccessToken": "env:KAFDECK_DEPLOYMENT_TOKEN"
},
"Clusters": [
{
"Id": "local",
"BootstrapServers": [
"kafdeck-kafka:19092"
],
"SecurityProtocol": "Plaintext"
}
]
}
}ListenUrls is the preferred form and accepts multiple Kestrel endpoints. The legacy single ListenUrl key remains supported for existing deployments. When a wildcard bind such as 0.0.0.0 or [::] is used, Kafdeck derives a wildcard Host filter only after the deployment has passed the existing non-local Token/OIDC security checks. Concrete IP/hostname bindings derive exact allowed hosts automatically.
The examples below use the current released tag v0.7. For production, prefer the immutable digest shown above.
$env:KAFDECK_DEPLOYMENT_TOKEN = "change-this-local-token"
docker run --rm --name kafdeck --network kafdeck-demo -p 8080:8080 -e KAFDECK_DEPLOYMENT_TOKEN=$env:KAFDECK_DEPLOYMENT_TOKEN -v "$PWD\appsettings.Production.json:/app/appsettings.Production.json:ro" ghcr.io/araditc/kafdeck:v0.7export KAFDECK_DEPLOYMENT_TOKEN='change-this-local-token'
docker run --rm \
--name kafdeck \
--network kafdeck-demo \
-p 8080:8080 \
-e KAFDECK_DEPLOYMENT_TOKEN \
-v "$PWD/appsettings.Production.json:/app/appsettings.Production.json:ro" \
ghcr.io/araditc/kafdeck:v0.7Then open Kafdeck from another machine by using the server's reachable address, for example:
http://192.168.10.20:8080/#access_token=change-this-local-token
For local access on the server itself, http://127.0.0.1:8080/ remains valid when loopback is also included in ListenUrls.
The token is supplied through the URL fragment, not a query string. The UI immediately removes it from the address bar/history, keeps it only in page memory, and sends it as X-Kafdeck-Access-Token for API calls. It is not written to localStorage or sessionStorage; after a full page reload, bootstrap Token mode again with the fragment or use OIDC for persistent multi-user sessions.
Warning
Token mode is a deployment access boundary, not a multi-user identity system. For multi-user production deployments, use OIDC/RBAC.
podman run --rm \
--name kafdeck \
-p 8080:8080 \
-e KAFDECK_DEPLOYMENT_TOKEN \
-v "$PWD/appsettings.Production.json:/app/appsettings.Production.json:ro,Z" \
ghcr.io/araditc/kafdeck:v0.7For Podman, create a Podman network and ensure Kafka advertises a broker hostname reachable on that network. The Docker-specific kafdeck-demo recipe above is not automatically shared with Podman.
Building from source is the best path for contributors and for testing unreleased main capabilities.
- Git,
- .NET SDK 10,
- Node.js 24+ and npm,
- Docker + Compose for the integration Kafka environment.
git clone https://github.com/araditc/Kafdeck.git
cd Kafdeck
dotnet restore Kafdeck.slnx --locked-mode
dotnet build Kafdeck.slnx -c Release --no-restore
npm --prefix src/frontend ci
npm --prefix src/frontend run build
rm -rf src/backend/Kafdeck.Api/wwwroot
mkdir -p src/backend/Kafdeck.Api/wwwroot
cp -R src/frontend/dist/. src/backend/Kafdeck.Api/wwwroot/
dotnet run --project src/backend/Kafdeck.Api/Kafdeck.Api.csprojThe default application configuration remains intentionally loopback-only at http://127.0.0.1:8080 and contains no cluster profiles. To expose a source run to another machine, explicitly select Token or OIDC mode and configure one or more ListenUrls. For a useful source run, either add src/backend/Kafdeck.Api/appsettings.Development.json or set environment variables before starting the API:
export Kafdeck__Deployment__ListenUrls__0=http://0.0.0.0:8080
export Kafdeck__Deployment__AccessMode=Token
export Kafdeck__Deployment__AccessToken=env:KAFDECK_DEPLOYMENT_TOKEN
export KAFDECK_DEPLOYMENT_TOKEN='change-this-local-token'
export Kafdeck__Clusters__0__Id=local
export Kafdeck__Clusters__0__BootstrapServers__0=localhost:9092
export Kafdeck__Clusters__0__SecurityProtocol=Plaintext
dotnet run --project src/backend/Kafdeck.Api/Kafdeck.Api.csprojgit clone https://github.com/araditc/Kafdeck.git
Set-Location Kafdeck
dotnet restore Kafdeck.slnx --locked-mode
dotnet build Kafdeck.slnx -c Release --no-restore
npm --prefix src/frontend ci
npm --prefix src/frontend run build
Remove-Item -Recurse -Force src/backend/Kafdeck.Api/wwwroot -ErrorAction SilentlyContinue
New-Item -ItemType Directory -Force src/backend/Kafdeck.Api/wwwroot | Out-Null
Copy-Item -Recurse src/frontend/dist/* src/backend/Kafdeck.Api/wwwroot/
dotnet run --project src/backend/Kafdeck.Api/Kafdeck.Api.csprojFor a local Kafka:
docker compose -f deploy/dev/docker-compose.kafka.yml up -dTo point a native Windows source run at that broker:
$env:Kafdeck__Deployment__ListenUrls__0 = "http://0.0.0.0:8080"
$env:Kafdeck__Deployment__AccessMode = "Token"
$env:Kafdeck__Deployment__AccessToken = "env:KAFDECK_DEPLOYMENT_TOKEN"
$env:KAFDECK_DEPLOYMENT_TOKEN = "change-this-local-token"
$env:Kafdeck__Clusters__0__Id = "local"
$env:Kafdeck__Clusters__0__BootstrapServers__0 = "localhost:9092"
$env:Kafdeck__Clusters__0__SecurityProtocol = "Plaintext"
dotnet run --project src/backend/Kafdeck.Api/Kafdeck.Api.csprojdocker build -t kafdeck:dev .The runtime image serves both API and UI, runs as the image-defined non-root user, exposes port 8080, and uses ASP.NET Core 10.
Kafdeck uses the standard ASP.NET Core configuration model. JSON configuration and environment variables can be combined. Nested environment keys use double underscores.
Kafdeck__Clusters__0__Id=prod
Kafdeck__Clusters__0__BootstrapServers__0=kafka-1.example:9093
Kafdeck__Clusters__0__SecurityProtocol=SaslSsl| Mode | Intended use | Important behavior |
|---|---|---|
| Local | Single-user local development | Must bind to loopback; no deployment token or OIDC |
| Token | Controlled management boundary / simple deployment | Requires a secret-referenced access token |
| Oidc | Multi-user production operation | Operator identity + Kafdeck RBAC; non-loopback listen URL must use HTTPS |
Local and Token modes are deployment-boundary modes and do not provide identity-scoped Kafdeck RBAC. Kafka-side ACLs and Kafdeck record masking still apply.
Secret fields accept:
env:VARIABLE_NAME
file:/absolute/mounted/path
Examples:
{
"AccessToken": "env:KAFDECK_DEPLOYMENT_TOKEN",
"ClientSecret": "file:/run/secrets/oidc-client-secret"
}Do not commit actual passwords, access tokens, PEM private keys, or client secrets.
Supported Kafka transport/authentication modes:
- Plaintext,
- Ssl,
- SaslPlaintext,
- SaslSsl.
Supported SASL mechanisms:
- Plain,
- ScramSha256,
- ScramSha512.
Production-oriented example:
{
"Id": "prod",
"BootstrapServers": [
"kafka-1.example:9093",
"kafka-2.example:9093",
"kafka-3.example:9093"
],
"SecurityProtocol": "SaslSsl",
"Tls": {
"VerifyServerCertificate": true,
"CaCertificate": "file:/run/secrets/kafka-ca.pem"
},
"Sasl": {
"Mechanism": "ScramSha512",
"Username": "env:KAFDECK_KAFKA_USER",
"Password": "file:/run/secrets/kafka-password"
}
}TLS server-certificate verification cannot be silently disabled.
SchemaRegistry is a property of an individual object inside Kafdeck:Clusters[]. Example cluster object:
{
"Id": "prod",
"BootstrapServers": [ "kafka-1.example:9093" ],
"SecurityProtocol": "SaslSsl",
"SchemaRegistry": {
"Url": "https://schema-registry.example",
"ProviderProfile": "ConfluentCompatibleV1",
"Username": "env:KAFDECK_SR_USER",
"Password": "file:/run/secrets/schema-registry-password"
}
}Remote basic authentication requires HTTPS.
ProviderProfile defaults to ConfluentCompatibleV1 for backward compatibility. v0.7 also admits KarapaceCompatibleV1 through the tested Confluent-compatible lifecycle contract. ApicurioV3 is a distinct capability profile and is currently reported as unsupported for lifecycle/read operations until a typed Apicurio adapter has its own compatibility evidence; Kafdeck does not send Confluent-compatible paths to that profile.
The capability endpoint GET /api/v1/clusters/{clusterId}/schemas/capabilities reports provider support separately from mutation-mode activation. Subject/version/reference reads and developer tooling remain available when authorized; governed registration, compatibility changes and delete lifecycle require mutation mode plus the exact server-derived authorization/risk/approval path.
v0.7 supports multiple stable Connect profiles per Kafka cluster. New deployments should use ConnectProfiles[]:
{
"Id": "prod",
"BootstrapServers": [ "kafka-1.example:9093" ],
"SecurityProtocol": "SaslSsl",
"ConnectProfiles": [
{
"Id": "default",
"Url": "https://connect-default.example:8083",
"MutationProviderProfile": "ConfluentCompatibleV1",
"Username": "env:KAFDECK_CONNECT_USER",
"Password": "file:/run/secrets/connect-password"
},
{
"Id": "analytics",
"Url": "https://connect-analytics.example:8083",
"MutationProviderProfile": "ConfluentCompatibleV1"
}
]
}Profile IDs are stable product identities. Kafdeck rejects duplicate profile IDs, duplicate HTTP origins and mixed legacy/new configuration. The legacy singular Connect object is still accepted for backward compatibility and normalizes to profile default, but it must not be configured together with ConnectProfiles[].
Read operations include worker/connector/task/plugin observations. When mutation mode, authorization and the typed provider profile permit it, Kafdeck can govern connector create/update/delete, pause/resume, connector restart and task restart. Secret-like configuration remains write-only/redacted, and generic Connect HTTP forwarding is not exposed.
Optional Connect auto-restart is disabled by default. If enabled under Kafdeck:Administration:ConnectAutoRestart, durable mutation persistence and the normal authorization/provider-policy prerequisites are mandatory; invalid or unbounded policy values fail closed.
KsqlDb is configured inside the relevant Kafdeck:Clusters[] object:
{
"Id": "prod",
"BootstrapServers": [ "kafka-1.example:9093" ],
"SecurityProtocol": "SaslSsl",
"KsqlDb": {
"Url": "https://ksql.example",
"Username": "env:KAFDECK_KSQL_USER",
"Password": "file:/run/secrets/ksql-password"
}
}v0.7 admits bounded single-statement read-only SELECT execution through the typed ksqlDB adapter. Row, byte, duration and concurrency ceilings are server-owned. DDL/DML, persistent-query creation, multi-statement/ambiguous input and generic SQL/HTTP forwarding fail closed before provider I/O.
Kafdeck never probes arbitrary application URLs. Streams topology/state-store evidence comes only from an explicitly registered telemetry profile:
{
"Id": "prod",
"BootstrapServers": [ "kafka-1.example:9093" ],
"SecurityProtocol": "SaslSsl",
"StreamsTelemetry": {
"Url": "https://streams-telemetry.example",
"ProviderProfile": "KafdeckTelemetryV1",
"Username": "env:KAFDECK_STREAMS_USER",
"Password": "env:KAFDECK_STREAMS_PASSWORD"
}
}Remote basic authentication requires HTTPS. Missing telemetry is reported as unavailable/unconfigured rather than fabricated as an empty or healthy topology. Lineage preserves provenance/confidence and distinguishes Observed from Inferred edges; inferred edges never become authorization evidence.
Generator execution is disabled for clusters that are not explicitly allowlisted by deployment configuration:
{
"Kafdeck": {
"Generator": {
"EnabledClusterIds": [ "dev" ]
}
}
}Allowlisting a cluster does not bypass RBAC, mutation risk/approval, destination validation or hard count/rate/byte/duration caps. Generated payloads are bounded in memory and are not durably staged.
{
"Kafdeck": {
"Records": {
"Masking": {
"PolicyId": "prod-default",
"Version": 1,
"MaskKey": true,
"KeyReplacement": "[REDACTED]",
"StructuredRules": [
{
"Path": "/customer/cardNumber",
"Replacement": "[REDACTED]"
}
],
"HeaderRules": [
{
"Name": "authorization",
"Replacement": "[REDACTED]"
}
]
}
}
}
}An active structured masking rule fails closed when Kafdeck cannot safely apply it.
OIDC deployments support roles with action-, cluster-, and resource-scoped permissions.
A non-loopback OIDC deployment must listen on HTTPS. Because Kafdeck calls Kestrel directly, the process/container needs a server certificate; configuring only ListenUrl=https://... is not enough. For the released container, mount a PKCS#12/PFX certificate and configure Kestrel, for example:
docker run --rm \
--name kafdeck \
-p 8443:8443 \
-e ASPNETCORE_Kestrel__Certificates__Default__Path=/run/secrets/kafdeck-https.pfx \
-e ASPNETCORE_Kestrel__Certificates__Default__Password="$KAFDECK_HTTPS_CERT_PASSWORD" \
-e KAFDECK_OIDC_CLIENT_SECRET \
-v "$PWD/kafdeck-https.pfx:/run/secrets/kafdeck-https.pfx:ro" \
-v "$PWD/appsettings.Production.json:/app/appsettings.Production.json:ro" \
ghcr.io/araditc/kafdeck:v0.7Inject KAFDECK_HTTPS_CERT_PASSWORD through your orchestrator/secret manager; it is an ASP.NET/Kestrel setting and does not use Kafdeck's env: secret-reference syntax. In production, use a certificate whose SAN matches the hostname operators use.
The corresponding Kafdeck/OIDC configuration can then use an HTTPS listen URL:
{
"Kafdeck": {
"Deployment": {
"ListenUrls": [ "https://0.0.0.0:8443" ],
"AccessMode": "Oidc",
"Oidc": {
"Issuer": "https://idp.example",
"ClientId": "kafdeck",
"ClientSecret": "env:KAFDECK_OIDC_CLIENT_SECRET",
"GroupClaim": "groups",
"Scopes": [ "openid", "profile" ]
}
},
"Authorization": {
"Roles": [
{
"Id": "kafka-readers",
"Permissions": [
{ "Action": "ClusterRead", "ClusterIds": [ "prod" ] },
{ "Action": "TopicList", "ClusterIds": [ "prod" ] },
{ "Action": "TopicRead", "ClusterIds": [ "prod" ], "ResourcePatterns": [ "*" ] },
{ "Action": "ConsumerRead", "ClusterIds": [ "prod" ], "ResourcePatterns": [ "*" ] },
{ "Action": "SchemaRead", "ClusterIds": [ "prod" ], "ResourcePatterns": [ "*" ] }
]
}
],
"GroupBindings": [
{
"ExternalGroup": "kafka-operators",
"RoleIds": [ "kafka-readers" ]
}
]
}
}
}Grant RecordRead and RecordExport only where payload access is explicitly required.
Core rules include:
- authorization is deny-by-default in OIDC mode;
- governed administration is opt-in; read access never implies write/admin access;
- state-changing execution requires durable operation state; SQLite is standalone-only and PostgreSQL is required for HA mutation execution;
- CRITICAL operations require a distinct eligible approver and fail closed when that property cannot be established;
- mutation/job concurrency is bounded across HA replicas through durable execution slots, leases and fencing;
- ambiguous post-dispatch outcomes are retained as unresolved/unknown external effects rather than blindly retried;
- replay/forwarding jobs and Data Generator execution are finite, budgeted and cancellation-aware;
- metadata access does not imply record access, and record export is separate from record read;
- masking is server-side and fail-closed;
- secrets are referenced rather than serialized into diagnostics;
- deployment access tokens are memory-only in the browser after URL-fragment bootstrap;
- record/generated payloads and ksqlDB result rows are not durably persisted by default;
- audit output excludes record payloads, generated payloads, provider credentials and ksqlDB result rows;
- ecosystem integrations expose typed configured origins, not a generic HTTP proxy;
- Kafka Connect secret-like configuration is redacted fail-closed;
- ksqlDB permits only bounded read-oriented queries; DDL/DML/persistent-query forms are blocked;
- inferred lineage is never used as authorization evidence;
- release images are built with exact-source SBOM, High/Critical vulnerability scanning, keyless signing and immutable digest promotion.
Read SECURITY.md before production deployment.
Give Kafdeck only the Kafka permissions required for the capabilities you intentionally enable. Visibility alone does not require produce, topic mutation, ACL mutation or offset-alteration rights. If you enable governed administration, replay/forwarding or generation, grant only the exact Kafka privileges required by those admitted operations and keep Kafdeck RBAC/risk/approval controls independently enforced.
- pin a released OCI digest;
- run the container as the image-defined non-root user;
- mount configuration and secret files read-only;
- use TLS/mTLS or SASL over TLS for Kafka;
- use a distinct least-privilege Kafka principal per environment;
- use OIDC/RBAC for multi-user production access;
- keep Kafdeck on a management network;
- configure an approved HTTPS boundary;
- prefer concrete
ListenUrlsfor production host filtering; wildcard binds require Token/OIDC and intentionally derive wildcard Host acceptance; - do not expose development PLAINTEXT listeners to untrusted networks;
- monitor /healthz;
- retain release provenance/SBOM/security evidence according to your environment policy.
GET /healthz
curl http://127.0.0.1:8080/healthzKafdeck product capabilities are exposed through versioned HTTP routes under /api/v1. The UI uses the same backend-authoritative contracts rather than bypassing authorization.
The current v0.7 OpenAPI contract is available both at runtime and in the repository:
- runtime:
GET /api/v1/openapi/v0.7.json - checked in: docs/api/openapi-v0.7.json
Earlier versioned API documents remain in docs/api/ as historical release contracts. Treat the API contract from the published release you deploy as authoritative.
Compatibility is evidence-based rather than assumed from a Kafka-compatible endpoint.
The v0.7 release validation matrix retains:
| Kafka | Tier |
|---|---|
| 4.3.1 | Tier 1 |
| 4.2.1 | Tier 1 |
| 4.1.2 | Tier 1 |
| 3.9.2 | Tier 2 |
Provider-specific services can expose different administrative capabilities. Kafdeck records those as capability profiles instead of claiming blanket equivalence.
Kafdeck has no mandatory public SaaS dependency at runtime.
For an air-gapped environment:
- mirror the exact released OCI digest into the controlled registry;
- mirror Kafka CA/client certificate material and external ecosystem credentials separately;
- transfer configuration independently from secrets;
- verify image digest before import/run;
- keep secret material outside the image;
- if rebuilding inside the air gap, mirror .NET/npm/base-image dependencies and preserve lockfiles/provenance.
| Symptom | What to check |
|---|---|
| /healthz is unreachable | Listen URL, port mapping, container process, firewall |
| Container port is published but UI does not load | Kafdeck may still be bound to container loopback; use an explicit non-loopback listen URL with Token/OIDC mode |
| UI loads but API returns 401 in Token mode | Deployment token secret reference and fragment bootstrap |
| Kafka cluster is unavailable | DNS/routing, advertised listeners, bootstrap address, operation deadlines |
| TLS errors | CA trust, SAN/hostname, certificate validity, mounted file permissions |
| SASL errors | Security protocol, mechanism, username/password secret references |
| Configuration read returns authorization errors | Kafka principal may lack read/describe permission; do not solve this by granting mutation rights |
| Record decode unavailable | Schema Registry profile, network/TLS/auth, schema references |
| Connect data unavailable | Connect profile, endpoint reachability, TLS/basic-auth configuration |
| ksql metadata says unsupported | Provider requires statement execution; this is intentionally unsupported |
| Data appears partial | Check Kafdeck RBAC, Kafka ACLs, upstream capability/timeout limitations |
| Response is marked stale | A bounded cached observation is being served after a retryable upstream failure |
src/backend/ .NET application, modules, infrastructure adapters
src/frontend/ React operator UI
tests/Kafdeck.Architecture.Tests/
architecture/security/behavior tests
tests/integration/ containerized integration harnesses
deploy/dev/ local Kafka development infrastructure
docs/architecture/ architecture documentation
docs/security/ security boundaries and threat models
docs/operator/ operator and upgrade guides
docs/releases/ release notes and exact release evidence
docs/rfcs/ capability RFCs
docs/decisions/ approval and decision records
.github/workflows/ CI, security and release-supply-chain automation
dotnet restore Kafdeck.slnx --locked-mode
dotnet build Kafdeck.slnx -c Release --no-restore
dotnet test tests/Kafdeck.Architecture.Tests/Kafdeck.Architecture.Tests.csproj -c Release --no-buildnpm --prefix src/frontend ci
npm --prefix src/frontend run lint
npm --prefix src/frontend test
npm --prefix src/frontend run builddocker compose -f deploy/dev/docker-compose.kafka.yml up -dCI also validates Kafka compatibility, CodeQL, dependency review, repository policy, supply-chain evidence, bounded benchmarks, SBOM generation, and vulnerability scanning where applicable.
Contributions are welcome.
Start with:
The short version:
- start from the latest main;
- keep the change focused;
- add/update tests and docs;
- use Conventional Commits;
- sign off commits for DCO compliance;
- preserve the safety and vendor-neutrality boundaries;
- open a PR and resolve review findings;
- let required exact-head checks and protected-branch governance complete.
DCO example:
git commit -s -m "feat(topics): add safe topic capability"Large architecture/security/public-contract changes require an ADR or RFC rather than being hidden inside an implementation PR.
The repository's official engineering/documentation language is English.
Useful contribution areas include:
- documentation and examples,
- accessibility improvements,
- test coverage,
- safe provider compatibility fixtures,
- Kafka/version compatibility evidence,
- UX improvements that preserve backend-authoritative security,
- performance measurements and regression detection,
- issue reproduction and diagnostics,
- security hardening.
Before implementing a roadmap feature, check whether its scope is already approved and whether an RFC is required.
For normal bugs and feature requests, use GitHub Issues.
A good bug report includes:
- Kafdeck release tag or exact commit SHA,
- operating system / container runtime,
- Kafka version and deployment type,
- Kafdeck access mode,
- minimal reproducible configuration with secrets removed,
- reproduction steps,
- expected vs actual behavior,
- relevant sanitized logs.
See SUPPORT.md for project support policy.
Do not report exploitable vulnerabilities in a public issue.
Follow SECURITY.md and use GitHub private vulnerability reporting when available, or another private channel explicitly published by the maintainers.
Kafdeck uses protected-branch PR governance, DCO, exact-head validation, versioned ADR/RFC decisions, and release gates.
See:
Kafdeck's release pipeline is designed around exact-source evidence:
- locked dependency restore,
- quality gates,
- CodeQL,
- dependency review,
- Kafka compatibility tests,
- OCI build,
- SPDX SBOM generation,
- High/Critical vulnerability scanning,
- keyless signing for approved published candidates,
- immutable image digest promotion,
- GitHub Release bound to the approved source revision.
Release notes and evidence live in docs/releases/.
The roadmap is public and capability-driven:
With v0.7 released, the next planned themes start at v0.8: observability, automation and platform APIs; then governance/enterprise hardening and the v1.0 stability/compatibility milestone.
Nothing in the roadmap should be interpreted as currently available until it is implemented, validated, and released.
Kafdeck is licensed under the Apache License 2.0.
Copyright and contribution rights are governed by the license and the project's DCO-based contribution model.
Kafdeck is built for operators who want powerful Kafka visibility without casually handing a web UI the keys to the cluster.
If the project is useful to you, consider starring the repository, opening high-quality issues, testing it against real Kafka environments, and contributing evidence-backed improvements.