Architecture
Go backend with informer caches, a bundled VictoriaMetrics for history, and an outbound-only agent. React frontend with live WebSocket updates.
System Diagram
(Go) (React 18 · TS · Vite 5 · Tailwind) Go Workspace
Monorepo with go.work containing four modules:
apps/api— Main backend server (entry:cmd/server/main.go)packages/agent— The cluster agent (its own release line,1.x)packages/proto— Generated gRPC/protobuf types shared by API and agentpackages/shared— Shared Go utilities
The two halves
Backend (per install): reads the Kubernetes API through shared informers,
evaluates insights, serves the REST/WebSocket API, and keeps state — users,
settings, insight episodes, security findings, the audit trail, Kobi
conversations — in an embedded BoltDB. Historical metrics go to
VictoriaMetrics: the Helm chart and the Compose stack run a single-node
instance next to the API; the single binary and single-container image don’t
bundle one, and read any VictoriaMetrics-compatible endpoint you point
KUBEBOLT_METRICS_STORAGE_URL at.
Agent (per cluster, optional): connects outbound over gRPC
(AgentChannel) and ships metric samples — from its built-in kubelet/node
collectors, a bundled vmagent scrape sidecar, or by reading your existing
Prometheus (promRead). It also carries Hubble network flows for the
Reliability tab and, in reader or operator mode, proxies Kubernetes API
calls for clusters the backend can’t reach directly — including exec,
port-forward and file-browser sessions in operator mode. See
Connecting clusters.
Key Backend Packages
| Package | Purpose |
|---|---|
cluster/manager.go | Cluster registry + lifecycle: kubeconfig contexts, agent-proxy and metrics-only clusters, display names, context switching |
cluster/connector.go | Shared informers + dynamic client, 45s cache sync timeout, 15s rest timeout |
cluster/permissions.go | RBAC probing via SSAR, cluster-wide then namespace fallback, semaphore of 10 |
cluster/nslister.go | Multi-namespace lister wrappers for namespace-scoped ServiceAccounts |
cluster/graph.go | In-memory topology graph with debounced rebuild (2s) |
cluster/relationships.go | Edge detection: ownerRefs, selectors, Gateway parentRefs, volumes |
agent/channel | gRPC AgentChannel: sample ingest, tunnels for exec/port-forward/files |
metrics/collector.go | Metrics Server polling, per-namespace fallback, graceful degradation |
insights/engine.go | 24-rule evaluation engine |
insights/episodestore_bolt.go | Episode lifecycle on BoltDB: episodes, transitions, mutes, presence, rule policies |
findings/ | Security & Compliance store — Trivy, Kyverno and Falco findings, persisted so they answer without a live connector |
copilot/ | Kobi: 39 tools, providers, conversations, memory/compact, usage analytics |
mcp/ | Read-only MCP server over Streamable HTTP and stdio |
auth/store.go, auth/jwt.go, auth/middleware.go | Users and refresh tokens on BoltDB, JWT issue/verify, role enforcement (viewer < editor < admin) |
auth/api_tokens_store.go | Long-lived REST tokens (kbs_ service, kbk_ key) with path scopes and the public-edge rejection |
websocket/hub.go | Broadcast hub, 4096 buffer, silent drops when no clients; broadcasts carry a reference (kind, namespace, name), never the object |
api/router.go | Chi router with requireConnector middleware |
Data Flow
- Manager reads kubeconfig contexts + the persistent registry → async connection (HTTP server binds immediately, returns 503 until connected)
- Permission probe: SSAR calls, cluster-wide then namespace fallback, ~2-5s
- Informers start only for permitted resources; namespace-scoped SAs get per-namespace informer factories with multi-lister aggregation
- Dynamic client discovers Gateway API CRDs (5s timeout, gracefully skipped)
- Metrics Server polls every 30s → in-memory cache; agent samples land in
VictoriaMetrics and serve
query/query_range(PromQL) - REST API serves enriched resources with metrics injection, paginated (50/page); WebSocket broadcasts changes with debounced topology rebuilds
Deployment topology
The web UI can be served from a different origin than the API (split
UI/API, with KUBEBOLT_CORS_ORIGINS listing the UI’s origin), and the chart supports fronting the API with either a classic
Ingress or Gateway API (gatewayAPI.enabled) — with separate listeners
for the HTTP/WebSocket API and the agent’s gRPC channel.