Threat intelligence and the data relay
The relay is one in-cluster service, download-api. It is the only component with a route out, and it
is how your fleet gets threat intelligence.
This is not a choice you make. It is a requirement. See Known limitations.
What it relays
Read-only, upstream-produced content only.
| Class | Routes | Why it is not shipped |
|---|---|---|
| CTI indicators | /v1/indicators/version, /v1/indicators/download | Built by the upstream intelligence pipeline |
| ML models | /v1/models/* and the per-model aliases | Trained and published by Gen0Sec |
| GeoIP | /v1/geoip/manifest, /v1/geoip/{file}/download | Licensed by MaxMind and IPInfo. The IPInfo Lite bundle is the default source |
| IDS rules | /v1/ids-rules/* | Licensed as part of the Emerging Threats ruleset |
It is a relay, not a cache. Nothing is stored on the way through. There is no local artifact store to size, no synchronization job to supervise, and no staleness window. An agent that polls gets whatever upstream has at that moment.
The relayed URLs are identical to the direct ones, so your agents need no configuration change.
What it never relays
| Class | Routes | Why |
|---|---|---|
| Workload identity | /v1/identity/* | Produced in your cluster, describes your workloads |
| Policy edges | /v1/policy-edges/* | Pod IPs, namespaces, NetworkPolicy edges |
| Fleet bans | /v1/fleet-bans/* | Your own agents' ban decisions |
These three are tenant-scoped: they describe your infrastructure or your decisions. Relaying them would push that to a service you do not run. They keep the local object store path behind the local authorization check, in relay mode exactly as without it.
Uploads are never relayed. The relay is read-only, so PUT and POST on the download surface are
served locally or not at all.
How authorization works
Two hops, two credentials, checked in two places.
| Hop | Credential | Checked by |
|---|---|---|
| Agent to relay | The agent's own key, issued by your dashboard | Your cluster. Relayed routes sit behind the local auth middleware and each carries the same permission it has in direct mode |
| Relay to Gen0Sec | Your Gen0Sec API key | Gen0Sec |
A caller's key is never forwarded upstream on a self-hosted deployment. A locally-issued key cannot authenticate to gen0sec. Forwarding it would leak a local credential to a third party without making the request succeed. With no Gen0Sec API key configured, the relay sends no credential at all.
The setting that must be right
downloadProxy:
enabled: true
upstream: "https://api.gen0sec.com"
apiKeySecret:
name: gen0sec-download-proxy # named by default
key: DOWNLOAD_PROXY_API_KEY
Why the key is required
Your agents' keys are issued by your dashboard and exist only in your database, so they cannot authenticate to gen0sec. The relay presents your own Gen0Sec API key instead, and that is the only credential the upstream will accept from your installation.
Without it, the relay sends no credential, Gen0Sec answers 401, and no relayed artifact reaches your fleet.
apiKeySecret.name defaults to gen0sec-download-proxy. If that secret does not exist, the relay pod
never starts: CreateContainerConfigError, visible immediately.
That is deliberate. An empty name renders no environment variable at all, the relay starts, and the only symptom is every relayed class answering 401 while agents report empty rulesets rather than errors. A failed rollout at install time is the cheaper failure.
Blank the name only if you relay through a gateway that supplies its own credential.
Getting the key right
printf '%s' '<your Gen0Sec API key>' > ~/.gen0sec/relay-key
kubectl -n gen0sec create secret generic gen0sec-download-proxy \
--from-file=DOWNLOAD_PROXY_API_KEY=$HOME/.gen0sec/relay-key
The value renders as a secretKeyRef, so it never appears in a manifest or in a values file.
All settings
| Key | Default | Meaning |
|---|---|---|
downloadProxy.enabled | true | Turning it off is not supported |
downloadProxy.upstream | https://api.gen0sec.com | The upstream base URL. A malformed value fails startup rather than degrading |
downloadProxy.external | geoip,ids-rules | Which external classes to relay. An unknown name fails startup |
downloadProxy.timeout | 60s | Upstream request timeout, matching the router's own |
downloadProxy.apiKeySecret.name | "" | Set this. See above |
downloadProxy.apiKeySecret.key | DOWNLOAD_PROXY_API_KEY | The key within the secret |
A malformed upstream is not treated as "fall back to reading from the local object store". That would leave every agent poll erroring against a store this deployment does not populate. Failing fast is the safer wrong answer.
Verifying
kubectl -n gen0sec get --raw \
'/api/v1/namespaces/gen0sec/services/g0s-download-api:http/proxy/status/healthz'
The output is similar to this:
{ "status": "ready", "mode": "proxy", "upstream": "https://api.gen0sec.com",
"external_relays": ["geoip", "ids-rules"] }
That tells you the relay started, not that it is authorized. For that, read the log:
kubectl -n gen0sec logs deploy/g0s-download-api | grep -i 'relayed from upstream' | tail -5
Any status=401 means your Gen0Sec API key is missing or wrong.
Response codes worth knowing
| Code | Means | What an agent does |
|---|---|---|
| 200 | Relayed successfully | Uses the artifact |
| 401 | The upstream rejected the credential | Reports an empty ruleset, not an error. This is the silent failure |
| 502 | The upstream is unreachable | Reads it as "ask again later" and keeps what it holds. Correct degradation |
| 404 | The artifact does not exist upstream | Treats it as withdrawn |
The 401 and 502 rows are the reason this page exists.
Preview a change before applying it
helm template g0s $B/charts/gen0sec-platform-*.tgz -n gen0sec \
-f $B/values/gen0sec-platform-values-onprem.yaml \
--set downloadProxy.apiKeySecret.name=gen0sec-download-proxy \
| grep -A3 DOWNLOAD_PROXY