CVE Radar reads sensitive values from process.env at startup. For development, a local .env file is fine. For production, do not bake secrets into image layers or commit them in Compose files.
Supported keys:
| Direct env | File env (*_FILE) |
|---|---|
NVD_API_KEY |
NVD_API_KEY_FILE |
GITHUB_TOKEN |
GITHUB_TOKEN_FILE |
DEEPL_API_KEY |
DEEPL_API_KEY_FILE |
ALERT_WEBHOOK_URL |
ALERT_WEBHOOK_URL_FILE |
API_SECRET |
API_SECRET_FILE |
Watch notification channels (NOTIFICATION_*) do not support *_FILE yet — use platform env or a gitignored Compose env_file. See NOTIFICATIONS.md.
| Direct env only | Notes |
|---|---|
NOTIFICATION_SLACK_WEBHOOK_URL |
Preferred Slack channel |
NOTIFICATION_DISCORD_WEBHOOK_URL |
Discord incoming webhook |
NOTIFICATION_TELEGRAM_BOT_TOKEN |
Pair with NOTIFICATION_TELEGRAM_CHAT_ID |
NOTIFICATION_SMTP_PASS |
With NOTIFICATION_SMTP_HOST, FROM, TO |
NOTIFICATION_WEBHOOK_URL |
Generic JSON webhook |
When *_FILE is set, the server reads the file path (trimmed UTF-8) and hydrates the direct env var before routes load. File wins over an empty direct env; if both exist, the file content is used when *_FILE is present.
Implementation: server/lib/secrets.ts.
Example: docker-compose.secrets.example.yml
mkdir -p secrets
printf '%s' 'your-nvd-key' > secrets/nvd_api_key.txt
printf '%s' 'ghp_xxx' > secrets/github_token.txt
chmod 600 secrets/*.txt
docker compose -f docker-compose.secrets.example.yml up -dSecrets mount under /run/secrets/ inside the container. Never add secrets/*.txt to git (see .gitignore).
Typical pattern:
- Store values in HashiCorp Vault, AWS Secrets Manager, or your platform vault.
- Use External Secrets Operator to sync into a Kubernetes
Secret. - Mount as files (preferred) or env vars.
File mount (recommended):
apiVersion: apps/v1
kind: Deployment
metadata:
name: cve-radar
spec:
template:
spec:
containers:
- name: cve-radar
image: raminnietzsche/cve-radar:latest
env:
- name: NVD_API_KEY_FILE
value: /etc/cve-radar-secrets/nvd_api_key
- name: GITHUB_TOKEN_FILE
value: /etc/cve-radar-secrets/github_token
volumeMounts:
- name: cve-radar-secrets
mountPath: /etc/cve-radar-secrets
readOnly: true
volumes:
- name: cve-radar-secrets
secret:
secretName: cve-radar-secrets
optional: trueExternalSecret (illustrative — adjust to your provider):
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: cve-radar-secrets
spec:
refreshInterval: 1h
secretStoreRef:
name: vault-backend
kind: ClusterSecretStore
target:
name: cve-radar-secrets
creationPolicy: Owner
data:
- secretKey: nvd_api_key
remoteRef:
key: cve-radar/nvd
property: api_key
- secretKey: github_token
remoteRef:
key: cve-radar/github
property: tokenDo not put secrets in Dockerfile ENV/ARG or image labels — they remain in layer history.
Spot-check that no key material appears in build history:
docker history --no-trunc raminnietzsche/cve-radar:latest | head -20
docker inspect raminnietzsche/cve-radar:latest --format '{{json .Config.Env}}'Only non-secret defaults (e.g. PORT, NODE_ENV) should appear.
GitHub Actions should continue using repository secrets / environments — never commit .env or secrets/ contents. See .github/workflows/ci-app.yml.
docs/DOCKER.md— run and publishSECURITY.md— reporting and deployment hygiene