Format
A self-hosted license is an Ed25519-signed JWT:- Signature scheme:
EdDSA(RFC 8037 Ed25519). - The
radar-hubbinary embeds the Skyhook-controlled public key at build time. The matching private key never leaves Skyhook’s secret store. - Verified offline at boot - no network call required for the signature check itself.
Claims
iss != "skyhook" or aud doesn’t include "radar-hub". Required claims: sub, org, exp. Everything else is optional.
How verification works
At boot
The control plane reads the JWT from one of:RADAR_HUB_LICENSE_KEYenv var (preferred)./etc/radar-hub/licensefile (Helm Secret mount).
radar-hub: in the log line - grep for these strings):
License-set path (the most common case):
radar-hub: license verified- happy path. Same log line carries license id, org, tier, max_clusters, expiry.radar-hub: license is expired (warn-only - hub keeps serving)- signature OK, pastexp. Continues serving.radar-hub: license verification failed- signature mismatch or missing required claim. Continues serving in warn-only mode for v1. Investigate immediately - this almost always means the wrong build talking to a real key, or vice versa.
radar-hub: self-hosted mode but no RADAR_HUB_LICENSE_KEY (or /etc/radar-hub/license) - running in unlicensed mode (warn-only banner)- the JWT file/env wasn’t supplied. Setlicense.keyin your Helm values or mount a Secret to/etc/radar-hub/license.
ghcr.io/skyhook-dev/radar-hub images):
radar-hub: embedded license public key is the all-zero placeholder; rebuild with the real key before shipping to a customer- the build wasn’t cut against the real Skyhook public key, so license verification cannot succeed against any real customer JWT. Only happens on a locally-built image - the officialghcr.io/skyhook-dev/radar-hub:<version>images embed the production public key. If you see this in production, you’re running an unofficial build; pull the published tag instead.
Warn-only enforcement
The control plane does NOT block writes when the license is invalid or expired. Enforcement is contractual via the Subscription Agreement, not technical. The user-visible behavior is:- A persistent yellow banner in the web app when verification has failed.
- Boot log lines visible to the operator.
- Heartbeat reports the issue back to Skyhook.
License heartbeat
The control plane posts a heartbeat every 6 hours. On by default; disable with--set license.heartbeat.enabled=false if your environment forbids outbound HTTPS.
- Endpoint:
POST https://api.radarhq.io/v1/license/heartbeat - Payload:
license_id,hub_version,hub_mode,org_name,cluster_count,audit_event_count_24h,started_at. - Response: optional renewed JWT, the latest published
hub_version, optional message string.
- No cluster names, no user emails, no audit-event content.
- No customer Postgres data.
- No tunnel traffic.
Failure ladder
When the control plane can’t reach the heartbeat endpoint:| Days without success | Behavior |
|---|---|
| 0-7 | Silent. |
| 7-14 | Yellow “Cannot reach license server” banner in the web app. |
| 14-30 | Red banner; admin gets warning on each login. |
| 30+ | Same red banner. No write blocking. |
Rotation
Licenses are issued for a fixed term (typically 12 months) matching the contract. Renewal is either:- Heartbeat-renewed: the renewal response carries a new JWT. The control plane stores it in Postgres and uses it on next boot.
- Manual (fallback when heartbeat is disabled, or when your account team needs to push an out-of-cycle renewal): your account team emails a new JWT; you update the Helm value or Secret + roll the
radar-hubDeployment.
Key rotation (Skyhook-side)
The embedded public key is append-only. Skyhook will publish a new chartappVersion with both the old and new public keys when the signing key rotates; existing deployments keep working until they upgrade past the cutover release.
You don’t need to do anything for a public-key rotation - just stay on supported chart versions and run helm upgrade at your normal cadence.
What you control
- The license JWT itself (rotate by upgrading the chart with a new value).
- Cookie sealing key (
hub.cookiePassword) - independent from the license; rotating it just signs everyone out. - The break-glass admin (always available, regardless of license state).
What Skyhook controls
- The signing private key.
- The license-server URL.
- The terms of the Subscription Agreement that backs warn-only enforcement.