Architecture¶
Here you see what pieces kubelatch is made of, where its state lives, and how it connects to your clusters and to GitHub. It helps you understand what happens if a piece fails and why the deployment is so small.
One binary, three pieces¶
kubelatch is a single Go binary with the web interface embedded. Three pieces run inside the same process:
flowchart LR
dev["People<br/>browser and kubectl"] -->|HTTPS| kl
ci["CI<br/>GitHub Actions"] -->|HTTPS| kl
subgraph kl["kubelatch (1-2 replicas)"]
cp["Control plane<br/>+ SPA"]
px["Impersonation<br/>proxy"]
rc["RBAC<br/>reconciler"]
end
kl --> pg[("Postgres")]
px -->|"SA kubelatch-proxy"| api["API servers<br/>EKS · GKE · AKS · k3s…"]
rc -->|"SA kubelatch-reconciler"| api
cp <-->|"login, members,<br/>webhook"| gh["GitHub"]
| Piece | What it does | Entry point |
|---|---|---|
| Control plane | The JSON API and the web interface (SPA in React). Accounts, clusters, permissions, credentials, trust rules and audit. | /api/... and / |
| Impersonation proxy | Receives requests from kubectl, k9s, Lens, Helm or CI, validates the token, logs the request and forwards it to the API server acting as whoever made it. | /clusters/<id>/... |
| RBAC reconciler | Maintains the ClusterRoles for each tier on every cluster, a binding for each tier-and-scope combination that has active permissions, and the exact list of users and groups the proxy is allowed to impersonate. | Outbound: talks to each API server |
There are also two routes outside /api with no session: the GitHub Actions token exchange (/v1/ci/github-actions/token) and the GitHub webhook (/v1/github/webhook). They're described in the API reference.
Alongside these pieces, background jobs run on every replica: the reconciler's periodic pass (every 10 minutes), the audit retention job (every hour) and, with GitHub configured, organization member synchronization (every hour).
Where the state lives¶
All state is in Postgres: subjects, clusters (with their encrypted ServiceAccount tokens), permissions, credentials (only their hash), trust rules and the two audit tables. kubelatch applies its migrations on startup.
Replicas don't keep anything of their own. That's why you can run one or two behind the same Service. With two, the reconciler, retention and member synchronization take turns using Postgres advisory locks. The only thing not shared is the in-memory per-IP rate limiters: each replica counts its own.
Without Postgres, kubelatch doesn't validate any token: the proxy responds with 503 and no one gets in. It's a deliberately closed failure.
How it fits with everything else¶
kubelatch talks to each cluster with two distinct ServiceAccounts, created by the bootstrap manifest in the kubelatch-system namespace:
kubelatch-proxycan only impersonate (impersonate) the users and groups the reconciler puts in its list (resourceNames). It's the only identity that travels in client requests.kubelatch-reconcilerwrites RBAC, but only over the fixed names kubelatch manages. It's never in the path of a client request.
GitHub appears twice. For people, as the login provider (a GitHub App belonging to your organization, see Identity). For CI, as the issuer of the GitHub Actions OIDC tokens that kubelatch exchanges for short-lived credentials.
Why it works on any cluster¶
The proxy and the reconciler only use the standard Kubernetes and RBAC API. There's no need to touch API server flags, install anything inside the nodes, or run a per-cluster agent. That's why the same mechanism works on managed clusters (EKS, GKE, AKS) and self-managed ones (kubeadm, k3s, RKE2, Talos). The page about the proxy explains which alternatives were discarded.
The cluster still decides with its native RBAC. kubelatch doesn't authorize each verb: it tells the API server who the person is and which groups they belong to, and the API server applies the bindings the reconciler created.
What happens if kubelatch goes down¶
Your clusters' workloads don't depend on kubelatch. If it goes down, only people's and CI's access through it is lost. That's why kubelatch must never be the only path to a cluster: keep a native emergency access route (see Accounts and recovery).
A restart or a rollout cuts open exec, port-forward, watch and logs -f connections. kubelatch gives normal requests a 3 s grace period, cuts the streams, and finishes writing its audit rows before exiting. The deployment manifest uses terminationGracePeriodSeconds: 60 and a rollout with maxSurge: 1 and maxUnavailable: 0.
Reference deployment¶
The default deployment (deploy/k8s/base) is a single-replica Deployment behind a Service of type LoadBalancer L4, with TLS terminated in the binary itself. The ha overlay scales up to two replicas with a PodDisruptionBudget. The ingress overlay puts kubelatch behind an Ingress, with the settings that long-lived streams need. The steps are in Install.