/api/v1/settings/scanners), scan schedules, policies, webhook endpoints,
registry credentials and entitlement overrides. The distinction is worth
holding onto — an operator sets what the deployment can do, and a tenant
administrator sets what their organisation does within it.
.env.example in the repository root is the canonical list. If a setting is not
there, it does not exist; if it is there, backend/internal/config reads it.
Keeping one source of truth is what prevents drift: the API port was once
simultaneously 8080 in .env.example, 4000 in the compose file and 4000 in the
Dockerfile, while every OAuth redirect URI assumed 8080.
Precedence
.env is gitignored and must never be committed. A missing .env is normal in
a deployed environment, so its absence is not an error.
The two rules
There is no such thing as a default secret
A shipped default value for a signing key is a published key: anyone who reads the repository can forge credentials with it. TRUSTIVAN had exactly this problem, and it was a documented security finding rather than a hypothetical. So a secret is either supplied by the operator, generated randomly per process in development, or the process refuses to start. There is no fourth option.Configuration fails closed
ENV is a security control, not a label. Anything that is not development —
an unset ENV and ENV=test included — is treated as a deployed environment
and validated strictly, so ENV=prodction, or no ENV at all, fails safe
rather than open.
Missing required configuration is a fatal startup error, never a warning that
scrolls past in a log. A security control that a missing environment variable can
silently disable is not a control.
The same reasoning appears throughout:
WEBHOOK_ALLOW_PRIVATE_DESTINATIONS=trueoutside development is refused at startup, because a deployment that can deliver to its own loopback interface can be pointed at its own instance metadata service by anyone who can create an endpoint.VULN_DB_PROVIDER=mirrorwith no repositories configured is a startup error rather than a quiet fall back to the public internet, which would defeat the air gap the operator was configuring.- A vulnerability database past its hard age limit fails the scan rather than reporting a confidently empty result.
Derived keys, not more settings
TOKEN_ENCRYPTION_KEY is a master key. The signing key for pagination cursors,
the webhook signing secret and the salt for secret findings are all derived
from it rather than configured separately.
This is a deliberate reduction in the number of things a deployment can forget.
A separately configured cursor-signing key that nobody set would fall back to an
empty secret, making every cursor forgeable — and a forged cursor is a tenant
boundary written in JSON. A derived key cannot be unset while the master key is
set.
For the same reason there is no environment variable that mints or seeds an API
key. Keys are created through the API and their secrets returned exactly once: a
credential in a config file is a credential in a backup, a shell history and a
container image layer.
Where the settings are documented
Deployment-shaped settings — metrics, replicas, health probes — are covered in
Deployment topology and
Scaling.
Adding a setting
If you are working on TRUSTIVAN rather than deploying it, the discipline is: add the field toConfig, read it in Load(), add validation to
resolveSecrets() with no default if it is a secret, and document it in
.env.example with why it exists. A setting that exists in code but not in
.env.example is invisible to whoever deploys the product.
