Skip to main content

Components

Self-hosted Radar Cloud architecture: nginx + web app in front of the radar-hub Go API, with Postgres and in-cluster Radars connected to the API. Optional license heartbeat to api.radarhq.io.

Data flow

A user’s browser request to a cluster

  1. Browser → https://radar.acme.example/c/{cluster-id}/api/...
  2. nginx (radar-hub-web Pod) reverse-proxies to the radar-hub Service.
  3. radar-hub’s auth middleware validates the session cookie (sealed with HUB_COOKIE_PASSWORD).
  4. The control plane looks up the cluster’s session in the in-memory registry (one entry per connected in-cluster Radar).
  5. The control plane opens a yamux stream on that cluster’s long-lived WebSocket.
  6. The request body streams over the yamux stream to the in-cluster Radar.
  7. The in-cluster Radar serves the request out of its existing chi router and streams the response back.
  8. nginx + the control plane forward each chunk to the browser.
Per-request overhead: one yamux stream (~1KB), no DB roundtrip beyond the auth check.

A cluster connecting

  1. In-cluster skyhook-connector runs helm install ... cloud.url=wss://radar.acme.example/agent cloud.token=rhc_....
  2. The connector dials /agent with Authorization: Bearer rhc_....
  3. The control plane looks up the SHA-256 hash of the token in clusters and validates the cluster id.
  4. The control plane upgrades the connection to WebSocket, wraps it as a net.Conn, and starts a yamux server-side session.
  5. Connector registers with its cluster id; the control plane stores the session in the in-memory registry.
  6. Tunnel stays open until the connector process dies or the network drops.
The cluster always initiates - the control plane never makes outbound calls into customer infrastructure. This is the firewall-friendly property that lets self-hosted radar-hub connect clusters in different VPCs / accounts / providers without poking holes.

Trust boundaries

BoundaryWhat’s true
Browser ↔ control planeSealed 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 planeSHA-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 ↔ PostgresStandard 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.
We don’t have:
  • 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.limits if 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.