Skip to main content
Celum is a Next.js dashboard backed by a Go API. The backend owns authentication, evaluates IAM, talks to each supervisor’s Kubernetes API, and stores control-plane data in PostgreSQL and network-flow analytics in ClickHouse.

Components

Frontend

Next.js 16 (App Router) + React 19, TypeScript, Tailwind, shadcn/ui.

Backend

Go with the Chi router; a dynamic Kubernetes client per supervisor.

PostgreSQL

Control-plane state: supervisors, cluster templates, addons, IAM policies/ groups, audit logs, cached data.

ClickHouse

Network-flow analytics powering the security topology and forensics views.

Request pipeline

Every API request passes through an ordered middleware chain before reaching a handler:
1

Tracing + access log

An OpenTelemetry span (named after the route pattern) and a structured access log line.
2

Recoverer

Panics are caught and turned into 500s instead of crashing the process.
3

CORS

Cross-origin handling scoped to the configured frontend origin.
4

Session

Validates the k8s-gate-session cookie (or a Bearer API token) and puts the user’s identity and groups into the request context.
5

Audit

Mutating requests (POST/PUT/DELETE) are recorded with user, action, resource, status, and duration.
6

Authorization (IAM)

The route is resolved to an action + KRN and checked against the user’s policies. See Permissions model.

Multi-supervisor model

Celum manages many supervisor clusters at once. Each supervisor is a kubeconfig — provided as a file (filename = supervisor name) or stored in the database. An empty supervisor name resolves to a synthesized __default__. Kubernetes clients are cached per supervisor, and every supervisor call is tagged with its name in traces. See Supervisors & clusters for the full model.

Provider detection

A cluster’s provider is read from its Cluster API spec.infrastructureRef.kind and mapped to a friendly name — for example: Unmapped kinds fall back to the kind name with the Cluster suffix stripped.

Data fetching

The frontend uses two helpers, never raw fetch: The client API base URL is injected at request time via a runtime-config script (window.__APP_CONFIG__), so the same build runs against any environment.

Observability

The backend emits OpenTelemetry traces over OTLP/HTTP when OTEL_EXPORTER_OTLP_ENDPOINT is set (a no-op tracer otherwise, so there’s no overhead when unconfigured). Spans cover HTTP handlers, per-supervisor Kubernetes calls, and PostgreSQL/ClickHouse queries, and structured logs carry the active trace ID for correlation.

Performance patterns

Read-only endpoints that hit a supervisor’s API use a stale-while-revalidate cache: fresh values are served immediately, stale values are returned while a refresh runs in the background, and a cold miss is bounded by a short timeout with an empty fallback — so one unreachable supervisor can’t hang a page. Helm release listings use a fast path that reads release Secrets directly, avoiding the Kubernetes API discovery cost.
A consequence worth knowing: a page can show data from a supervisor that has since become unreachable. Values are served stale while a refresh runs behind them, and a cold miss returns empty rather than an error.

Backend layout

The Go backend is organized under backend/internal/: Plus integration packages for DNS, IPAM, GitOps, and related services.

Supervisors & clusters

How supervisors are discovered, named, and cached.

Permissions model

How a request becomes an action + KRN, and how it is evaluated.