Configuration
ReadyA deployment is configured entirely through environment variables — no config-file format to learn, and no secrets compiled into the binary. ydfi bootstrap writes a richly-commented .env with every variable documented inline; ydfi validate-env checks it before you boot. The surface is deliberately small — around 45 variables in total: a small set you must set (database, secrets, PKI, license, deployment URL), and the rest have sensible defaults you reach for only when a deployment genuinely needs them (mode, ports, logging, attestation policy). Anything more granular than that — rate-limit windows, cache sizes, background-worker cadence — is a compiled constant, not a variable, so there is nothing to misconfigure there.
Who this is for: whoever stands up or tunes a deployment. You should not need to write any of this by hand — Deploy and ydfi bootstrap generate it. This page is the map for when you want to know what a variable does.
How it works#
One mechanism, start to finish: environment variables.
There is no YAML, no TOML, no config file with its own syntax and its own bugs. Everything the server reads — connection strings, ports, key material, feature flags, every tuning constant — comes from the environment. That is a deliberate choice: it is the one configuration surface every container runtime, orchestrator, and secrets manager already knows how to fill, and it keeps secrets out of the binary and out of any file you might commit. The three cryptographic secrets are just variables like the rest; you generate them with ydfi secret generate-* and hand them in the same way.
The path from nothing to a running deployment is two commands and a check:
YDFI_SERVER_ENV takes production, development, or test. Production turns the safety checks on — strict TLS, no insecure bypasses, no dev fallbacks. development and test relax them for local work and are the only modes where the loud *_TEST_BYPASS escape hatches are honoured. A live deployment runs production; anything else is validated with a warning.
What you must set#
These have no safe default — the server refuses to start without them. ydfi bootstrap fills every one; the table is here so you know what each is for.
| Variable | What | Form |
|---|---|---|
| YDFI_SERVER_DB_URI | Postgres connection for the deployment. | postgres://… |
| YDFI_PRIMARY_ENCRYPTION_KEY | AES-256 key that encrypts TOTP secrets at rest. Supports a key-id so you can rotate. | ID:KEY (6+64 hex) |
| YDFI_SERVER_LICENSE_PATH | The license JWT the deployment runs on. See Get a license. | file path |
| YDFI_SERVER_DEPLOYMENT_URL | The public URL this deployment serves at. Enrollment and challenge QR links are built from it. | https://… |
| YDFI_SERVER_TLS_CERT / _TLS_KEY | The mTLS leaf the RP-facing (identity) listener presents. | file paths |
| YDFI_SERVER_CLIENT_CA | The CA the identity listener trusts to authenticate relying parties. | cert file |
| YDFI_SERVER_PHONE_TLS_CERT / _TLS_KEY / _CLIENT_CA | The same three, for the unified phone listener (enrollment + auth-challenge delivery). | file paths |
| YDFI_SERVER_ROOT_CA_CERT_PATH / _ROOT_CA_KEY_PATH | Signs RotateCertificate responses when a relying party rotates its own cert. | file paths |
YDFI_SERVER_LICENSE_VERIFY_KEYS_PATH is not required on a production build — the Ydentifi root every license chains to is compiled into it, and it refuses to start while the variable is set, so it must be left unset there. Only a development build reads it. See Get a license and Releases.
The phone-listener mTLS trio auto-generates from an in-process PKI fixture outside production — only a production build forces you to supply it. The root CA pair must be the same root your mTLS verifier already trusts, or a relying party that rotates its cert ends up with one that chains to nothing the server accepts.
A minimal .env#
ydfi bootstrap writes the full, richly-commented file; once the required core above is filled in, this is the shape it reduces to — a single .env a production deployment boots on. The secrets shown are placeholders (generate real ones with ydfi secret generate-*); the paths point at the PKI bootstrap emitted.
There is deliberately no YDFI_SERVER_LICENSE_VERIFY_KEYS_PATH here — the Ydentifi root is compiled into a production binary, which refuses to start while the variable is set, so it stays unset. The two listener ports keep their defaults (50051 and 50052), as does everything under the tuning knobs. Run ydfi validate-env before boot.
Commonly set#
These have defaults, so they are optional — but they are the ones you actually reach for.
| Variable | What | Default |
|---|---|---|
| YDFI_SERVER_ENV | Mode: production / development / test. Selects which safety checks apply. | — |
| YDFI_SERVER_PORT | The RP-facing (identity) gRPC listener. | 50051 |
| YDFI_SERVER_PHONE_PORT | The unified device listener (onboard + gateway on one port). | 50052 |
| YDFI_ADMIN_SERVER_PORT | The admin gRPC listener, when the admin server runs. | 50060 |
| YDFI_ADMIN_SERVER_BIND_ADDR | Loopback by default; set 0.0.0.0 deliberately to expose admin remotely (pair with a firewall). | 127.0.0.1 |
| YDFI_SERVER_MANAGEMENT_PORT | pprof + the public Prometheus scrape. | 6060 |
| YDFI_SERVER_INTERNAL_METRICS_PORT | The full metric surface, behind mTLS, for internal-only cardinality. The public scrape above works either way. | 0 (off) |
| YDFI_SERVER_LOG_LEVEL / _LOG_FORMAT | Verbosity, and text vs JSON for log aggregation. Security events are written at any level. | — |
| YDFI_SERVER_ENABLE_REFLECTION | gRPC reflection on the relying-party listener, so tools can explore the API without proto files. The phone listener never serves it. Off in production unless you want it. | false |
| YDFI_SERVER_SERVICE_NAME | OTel resource name — matters once multiple instances feed one collector. | ydentifi-server |
| YDFI_SERVER_OTEL_COLLECTOR_URL / _OTEL_SAMPLE_RATE | OpenTelemetry tracing, if you run a collector. | — |
The tuning knobs#
A short, deliberate list — everything else is fixed.
Per-RPC rate-limit windows, cache sizes, and background-worker cadences are not environment variables. They're calibrated as a set — each one sized relative to its neighbors — and letting a deployment override one in isolation risks silently weakening the anti-abuse posture between them without anything catching it. What's left is what's genuinely deployment-specific:
…_DB_MAX_IDLE_TIME
…_ANDROID_PACKAGE_NAME · _ANDROID_SIGNING_CERT_SHA256
…_STREAM_MAX_CONNECTION_AGE
The attestation profile, in full:
That's the complete list. Rate-limit windows, cache sizes, and reaper cadences ship as fixed, load-tested defaults — there is no environment variable for them.
There is no setting for how long the deployment keeps a device’s history or the record of what the server did, because it keeps neither: both are log lines on your log pipeline, and their retention is whatever that pipeline already does. A period chosen here would be Ydentifi deciding when your records expire, and a default of any length is wrong for someone — PCI DSS wants twelve months, GDPR pushes the other way, and SOC 2 tests only that you honour whatever you wrote down. Capture standard output, or there is no record.
Validate before you boot#
Configuration mistakes should fail on your terminal, not at start-up in production. ydfi validate-env reads a .env and checks every variable — required ones present, secrets the right length, ports in range, cert files that parse, the deployment URL well-formed — and tells you, per line, what passed, what is a warning, and what would fail.
It is a static check of the file, so it is safe to run anywhere and worth wiring into CI. It does not connect to the database or contact the licensing server — it confirms the shape of the configuration, so the deployment that boots on it boots clean.