ADR-0002: Technology stack¶
- Status: Accepted
- Date: 2026-04-22
Context¶
kafkito is a single-binary Kafka management UI (see ADR-0001). We need a stack that:
- Produces a small, fast, statically linked binary.
- Provides first-class, actively maintained Kafka client support.
- Enables a protobuf-first API layer suitable for both backend and web/mobile clients.
- Ships a modern, maintainable SPA frontend that we can embed via
//go:embed. - Is Cloud Foundry / Kubernetes / container-friendly out of the box (see ADR-0003).
Decision¶
Backend¶
| Concern | Choice | Rationale |
|---|---|---|
| Language | Go 1.26+ | Single-binary, static linking, strong stdlib, mature Kafka client ecosystem. |
| HTTP router | go-chi/chi v5 |
Idiomatic, stdlib-compatible, excellent middleware story. |
| RPC layer | Connect-RPC with buf (protobuf-first) |
HTTP/1.1, HTTP/2, gRPC-compatible, generated TypeScript client, API contract versioning. |
| Kafka client | twmb/franz-go + kadm + kmsg + sr |
Best-of-breed pure-Go Kafka client; supports KIPs promptly; wire-protocol-first. |
| Scripting | dop251/goja |
Sandboxed JS engine for message-filter DSL. |
| Config | knadh/koanf |
YAML + env + VCAP_SERVICES layering. |
| Logging | log/slog (stdlib) with optional go.uber.org/zap handler later if perf demands it |
Structured JSON logs to stdout, Cloud Foundry loggregator-friendly. |
| Observability | OpenTelemetry + Prometheus exporter | Industry standard. |
Frontend¶
| Concern | Choice | Rationale |
|---|---|---|
| Runtime | Bun | Fast install/build, first-class TypeScript, matches modern React tooling. |
| Framework | React 19 | Mainstream, large talent pool, stable ecosystem. |
| Build | Vite | Fast dev server, lean production bundles. |
| Routing | TanStack Router | Type-safe, data-loading built in, no server-side rendering required. |
| UI kit | shadcn/ui + Tailwind CSS | Copy-in component library (no vendor lock-in), accessible defaults. |
| State / data | TanStack Query + Connect-RPC generated client | Declarative data-fetching; protobuf types end-to-end. |
Distribution¶
- Single Go binary with the SPA embedded via
//go:embed. - Distroless multi-arch container image (
gcr.io/distroless/static-debian12,linux/amd64+linux/arm64).
Consequences¶
Positive
- One deployment artifact. No JVM, no sidecar node_modules.
- Protobuf-first API ensures end-to-end type safety and easy contract evolution.
Negative
- Bun is less mainstream in some enterprise environments than Node.js. We mitigate by producing plain static assets — the runtime is only a build-time dependency.
- Connect-RPC is newer than plain REST + OpenAPI. Team members need to learn
bufand the Connect generator.
Alternatives considered¶
- Echo / Gin instead of Chi — rejected, Chi has the cleanest middleware story and is stdlib-compatible.
- Sarama or IBM/sarama instead of franz-go — rejected, franz-go is more modern, has better performance and KIP coverage.
- Next.js or Remix instead of Vite SPA — rejected, we do not need SSR; a SPA simplifies the
//go:embeddistribution model. - gRPC-Web / grpc-gateway instead of Connect-RPC — rejected, Connect-RPC offers a simpler protocol with identical tooling benefits.