Skip to content
GitHub

Architecture decisions

Code comments cite decisions as ADR-NNN. New decisions get their own file under docs/adr/ in the repository, with Context, Decision and Consequences. This page is the index.

ADR Decision Status Where it shows
001 Kubernetes holds desired state; SQL holds identity and audit superseded by 015
002 One binary with roles (modular monolith) decided kuben serve --roles, kuben_core::config::Role
003 Two Tokio runtimes as a bulkhead, a supervisor, panic = "unwind" superseded by 013 (supervisor and unwind kept) kuben_platform::supervise
004 Projections (small read models) instead of raw reflectors decided kuben_platform::projection
005 Opaque cookie sessions and opaque API tokens; no JWT in the browser decided kuben_api::auth
006 sqlx with two backends (SQLite, PostgreSQL) and a test matrix decided kuben-store, tests/matrix.rs
007 OpenAPI-first: the TypeScript client is generated, CI fails on drift decided kuben_api::openapi, packages/api-client
008 Gateway API as the networking layer decided controller::gateway
009 BuildKit + Railpack/CNB, deploy by digest, immutable releases superseded by 016
010 CEL and ValidatingAdmissionPolicy instead of admission webhooks decided CRD schemas in kuben-crd
011 React 19 + TanStack for the console decided apps/console
012 Licensing and a clean-room boundary to the system being replaced decided
013 One runtime in phase 0; a second one only behind runtime.bulkhead decided kuben::serve
014 One SSE stream per browser tab; WebSocket only for terminals decided kuben_api::stream
015 Data ownership: SQL references CRDs only by uid decided kuben-store, invariant I-17
016 Persistent buildkitd plus a light buildctl Job; external registry in the MVP decided, phase 1
017 The binary applies its CRDs at boot (server-side apply) decided controller::crd_apply
018 Environments are soft-deleted with a grace period decided controller::environment
019 Explicit bootstrap order; polling fallback for webhooks decided deployment docs
020 Append-only audit in the core; signed anchoring later decided kuben_api::audit
021 Six crates in phase 0, split only for a stated reason decided Cargo.toml
022 Threat model; Kuben’s service account is treated like cluster-admin decided, threat model pending charts/kuben/templates/rbac.yaml
023 Lease-based leader election for the controllers decided kuben_platform::leader
024 Turborepo runs every task; Bun replaces pnpm and Node; Cargo stays the Rust build graph decided turbo.json, package.json, bunfig.toml

ADR-023 in brief: leader election without trusting clocks

Section titled “ADR-023 in brief: leader election without trusting clocks”

The chart allowed several replicas on PostgreSQL, but every replica ran the full controller set and server-side-applied the same objects with force. The decision: only the holder of the Lease kuben-controller runs the controllers; every replica still serves the API from its projections. Every Lease write is a compare-and-swap on resourceVersion; a candidate treats the Lease as expired only after seeing it unchanged for the lease duration (15 s), never by comparing renewTime with its own clock, so skew cannot produce two leaders. The leader renews every 2 s and stops its controllers if renewal fails for 10 s. On shutdown it releases the Lease. Login-throttle windows moved to the database so all replicas share one budget. Read the blog post for the full reasoning.

ADR-024 in brief: one task runner for two languages

Section titled “ADR-024 in brief: one task runner for two languages”

Turborepo 2.10 added native Cargo workspace support, which removed the original objection to using it for Rust. Every crate is a Turborepo package and kuben-cargo stands for the whole workspace; Cargo still schedules and caches compilation. The two cross-language edges are explicit tasks: kuben-api#gen@kuben/api-client#gen, and @kuben/console#buildkuben#build:release. Bun 1.4 is the package manager, JS runtime and test runner; Node is not needed. Releases never reuse a cache.

The full texts, with context and consequences, are in the repository under docs/adr/.