Components

Data flow
A user’s browser request to a cluster
- Browser →
https://radar.acme.example/c/{cluster-id}/api/... - nginx (
radar-hub-webPod) reverse-proxies to theradar-hubService. radar-hub’s auth middleware validates the session cookie (sealed withHUB_COOKIE_PASSWORD).- The control plane looks up the cluster’s session in the in-memory registry (one entry per connected in-cluster Radar).
- The control plane opens a yamux stream on that cluster’s long-lived WebSocket.
- The request body streams over the yamux stream to the in-cluster Radar.
- The in-cluster Radar serves the request out of its existing chi router and streams the response back.
- nginx + the control plane forward each chunk to the browser.
A cluster connecting
- In-cluster
skyhook-connectorrunshelm install ... cloud.url=wss://radar.acme.example/agent cloud.token=rhc_.... - The connector dials
/agentwithAuthorization: Bearer rhc_.... - The control plane looks up the SHA-256 hash of the token in
clustersand validates the cluster id. - The control plane upgrades the connection to WebSocket, wraps it as a
net.Conn, and starts a yamux server-side session. - Connector registers with its cluster id; the control plane stores the session in the in-memory registry.
- Tunnel stays open until the connector process dies or the network drops.
Trust boundaries
| Boundary | What’s true |
|---|---|
| Browser ↔ control plane | Sealed session cookie (nacl/secretbox) sealed with HUB_COOKIE_PASSWORD. Cookie is HttpOnly + Secure + SameSite=Lax. Spoofing the cookie requires possessing the cookie key. |
| Cluster ↔ control plane | SHA-256-hashed bearer token (rhc_*). Per-cluster - revoking via the web app cuts that one cluster’s tunnel without affecting others. Token rotation via POST /api/clusters/{id}/rotate-token. |
| Control plane ↔ in-cluster Radar (over yamux) | Implicit via the cluster-token check at tunnel-establish time. The tunnel IS the trust boundary - any X-Forwarded-User header injected by the control plane is trusted by Radar BECAUSE the tunnel was already authenticated. |
| Control plane ↔ Postgres | Standard libpq auth via HUB_DB_DSN. We recommend sslmode=require or stricter for managed databases. |
| Control plane ↔ IdP (OIDC) | TLS-validated via the IdP’s published certificate. JWKS fetched lazily from discovery; refreshed on signature failure. |
| Control plane ↔ Skyhook (license heartbeat) | Outbound HTTPS only; can be disabled via chart values. |
What we don’t have access to
In a self-hosted install, Skyhook has access to:- Whatever metadata you choose to send in the license heartbeat (license id, version, cluster count, audit-event volume - shape documented in Licensing).
- Nothing else.
- Customer kubernetes-cluster data (Pods, Deployments, secrets, etc.) - that lives in your kube-apiserver and Radar’s per-cluster cache.
- User identities (emails, OIDC subjects) - they’re in your Postgres.
- Audit log content - same.
- Tunnel traffic - never crosses your VPC.
Single-replica deployment
The control plane runs as a single replica. The tunnel-session registry is in-memory, so a second replica without sticky routing would split tunnels across pods. Practical guidance:- Single replica handles hundreds of clusters; the control plane is mostly wait-on-network.
- Tune
hub.resources.limitsif CPU pressure shows up under fleet calls. Each fleet endpoint fans out to every connected cluster in parallel with bounded timeouts. - If you outgrow this, talk to us — multi-replica needs a sticky-routing layer we’ll build to your shape.