> ## Documentation Index
> Fetch the complete documentation index at: https://radarhq.io/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Architecture

> Component diagram, data flow, trust boundaries.

## Components

<img src="https://mintcdn.com/skyhook/MO3tZsRgyb2MPuuB/images/cloud/self-hosted/architecture.png?fit=max&auto=format&n=MO3tZsRgyb2MPuuB&q=85&s=fb5061f6ece0c3c8551c9296c8f5e89d" alt="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." width="2720" height="1848" data-path="images/cloud/self-hosted/architecture.png" />

## 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

| 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](/docs/cloud/self-hosted/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.
