Configuration
Runtime configuration, secrets, and provider-specific SMTP settings.
Use this page to decide which files belong to local development, which values must come from secrets, and what must change when moving the service to production.
Configuration Model
The service has three configuration layers: static userver component configuration, startup/runtime variables, and userver dynamic config. Static configuration defines which components, task processors, listeners, handlers, and config-service clients exist. Runtime variables provide environment-specific values such as ports, database URLs, JWT keys, SMTP credentials, and config-service endpoints. Dynamic config controls runtime policy such as auth limits and outbox retry behavior.
| File | Use | Production rule |
|---|---|---|
configs/static_config.yaml |
Local development and testsuite-enabled workflows. | Do not use for internet-facing production if it enables test handlers. |
configs/static_config.prod.yaml |
Production component graph and HTTP handlers. | Use this file for production containers. |
configs/config_vars.yaml |
Runtime values loaded by the static config. | Ignored locally; generate or mount it from a secret store in production. |
configs/config_vars.docker.example.yaml |
Tracked example that documents the runtime config shape. | Keep demo-safe; copy it as a starting point, never as a production secret source. |
configs/config_vars.docker.yaml |
Ignored local Docker/devcontainer runtime values. | Create it from the tracked example; local real credentials may live here because it is not committed. |
.env |
Docker Compose variables for local Postgres and migration jobs. | Keep it untracked and environment-specific. |
Secret Delivery Model
Production should treat the Docker image as immutable and secret-free. The compose defaults point at ignored local prod-style paths for manual smoke runs, while the deploy layer reads a secret source
such as a platform secret store, Vault, Yandex Lockbox, Kubernetes Secret, or Docker secret, renders
config_vars.yaml, and mounts it read-only into the container. JWT key files are mounted through
a separate secrets directory referenced by AUTH_JWT_PRIVATE_KEY_PATH and
AUTH_JWT_PUBLIC_KEY_PATH.
SMIRKLY_CONFIG_VARS_PATH=/etc/smirkly-auth/config_vars.yaml \
SMIRKLY_SECRETS_PATH=/run/secrets/smirkly-auth \
docker compose --profile prod up --build -d smirkly-auth
SMTP passwords, database passwords, migration database URLs, refresh token pepper, and JWT private keys must not be committed, baked into the image, or stored in tracked demo configs. Changing these values requires regenerating the mounted config or secret and restarting the service instances.
Dynamic Config
Runtime policy is stored in typed userver dynamic configs so the code reads snapshots instead of
scattered hard-coded values. Local development and Docker run from the C++ defaults defined in
dynamic_config::Key. Production connects dynamic-config-client and
dynamic-config-client-updater to config-service and keeps a filesystem cache for restart fallback.
| Key | Controls | Change model |
|---|---|---|
SMIRKLY_AUTH_RUNTIME_CONFIG |
Sign-up and sign-in rate limits, verified-email sign-in requirement, session activity write threshold, verification code TTL, and verification attempt limits. | Safe runtime policy; validate values before rollout. |
SMIRKLY_EMAIL_OUTBOX_RUNTIME_CONFIG |
Outbox processing kill switch, batch size, max attempts, stuck timeout, and retry backoff. | Safe for incidents, for example disabling delivery or lowering batch size. |
Do not put secrets, DSNs, JWT private keys, SMTP credentials, listener ports, config-service endpoints, or task processor thread
counts into the auth/outbox dynamic config keys. Those values shape the process or contain secret material and require deploy-time
configuration plus rolling restart. In production, dynamic-config-updates-enabled should be true,
dynamic-config-cache-path should be writable and persistent, and config updates should be monitored through userver metrics.
The repository contains tracked examples for the production dynamic config handoff. Use
configs/dynamic_config/values.example.json as a seed for config-service storage. A userver-compatible
config-service returns the same object wrapped under configs, with updated_at metadata, as shown in
configs/dynamic_config/config_service_response.example.json. The auth process reads neither file directly in
production; it receives the HTTP response through dynamic-config-client-updater. Local development can stay on
C++ defaults by keeping dynamic-config-updates-enabled: false.
Runtime Variables
| Setting | Required | Notes |
|---|---|---|
event-threads |
Yes | Userver event loop threads. Startup config; change with a rolling restart. |
worker-threads |
Yes | Main userver task processor size for request handling. |
worker-fs-threads |
Yes | Filesystem/blocking utility processor size. |
worker-monitor-threads |
Yes | Dedicated processor for monitor/admin handlers. |
worker-email-outbox-threads |
Yes | Dedicated SMTP delivery processor. Keep email delivery off the main request path. |
logger-level |
Yes | Use info in production unless debugging a controlled incident. |
is-testing |
Yes | Must be false outside testsuite or local test runs. |
server-port |
Yes | HTTP listen port inside the container. |
monitor-server-port |
Yes | Private monitor listener port for /health/live, /health/ready, and /service/*. Restrict at network level. |
postgres-dbconnection |
Yes | Postgres DSN used by the service. Keep aligned with migration DSN for the same environment. |
AUTH_JWT_AUDIENCE |
Yes | Audience claim expected by downstream services. |
AUTH_JWT_KEY_ID |
Yes | Stable key id exposed as token kid and in JWKS. |
AUTH_JWT_PRIVATE_KEY_PATH |
Yes | Path to the RSA private key mounted inside the runtime container. |
AUTH_JWT_PUBLIC_KEY_PATH |
Yes | Path to the RSA public key served through JWKS. |
AUTH_REFRESH_TOKEN_PEPPER |
Yes | Secret pepper for HMAC-SHA256 refresh token hashes. Rotate only through a planned session invalidation or migration. |
SMTP Variables
Email verification depends on the outbox worker and the SMTP variables below. Values are provider-specific, so production should use the provider documentation and a secret store, not copied local credentials.
| Setting | Example | Production guidance |
|---|---|---|
AUTH_SMTP_HOST |
smtp.gmail.com |
Use a controlled relay or transactional provider for production. |
AUTH_SMTP_PORT |
465 |
Use 465 for implicit TLS or 587 for STARTTLS. |
AUTH_SMTP_TLS_MODE |
tls |
Use tls with port 465, starttls with port 587, and none only for fake local SMTP. |
AUTH_SMTP_USERNAME |
your-account@gmail.com |
Use provider-issued credentials with the narrowest available scope. |
AUTH_SMTP_APP_PASSWORD |
<secret> |
Never commit it. Rotate immediately after exposure in chat, logs, screenshots, or git. |
AUTH_SMTP_FROM_EMAIL |
no-reply@example.com |
Must be authorized by provider policy and aligned with SPF, DKIM, and DMARC. |
AUTH_SMTP_FROM_NAME |
Smirkly |
Human sender name displayed by email clients. |
AUTH_SMTP_HOST: smtp.gmail.com
AUTH_SMTP_PORT: 465
AUTH_SMTP_TLS_MODE: tls
AUTH_SMTP_USERNAME: your-account@gmail.com
AUTH_SMTP_APP_PASSWORD: "<gmail-app-password>"
AUTH_SMTP_FROM_EMAIL: your-account@gmail.com
AUTH_SMTP_FROM_NAME: "Smirkly"
Client IP and Proxy Headers
The service can use forwarded client IP headers only when the immediate peer is a trusted proxy.
Configure trusted proxy CIDRs to match the ingress or load balancer ranges exactly. Do not trust
arbitrary X-Forwarded-For or X-Real-IP values from the public internet.
Change Rules
- Changing source code requires a rebuild and service restart.
- Changing
config_vars.yamlrequires a service restart, not a rebuild. - Changing migrations requires running the migration job before the new service version starts.
- Changing JWT keys requires a planned rotation window and downstream JWKS cache awareness.