Skip to content

Configuration (env)

Everything is configured through environment variables. The canonical example is backend/.env.example.

Never commit secrets

backend/internal/config/.env*, the root .env and backend.env are all gitignored. When you add a variable, document it in the READMEs — never its value.

Core

Variable Example Purpose
PORT 8080 API listen port
ENVIRONMENT production Turns on strict production guards
DEBUG false Verbose logging
ALLOWED_ORIGINS https://gerege.mn CORS allow-list (comma-separated; * forbidden)
TRUSTED_PROXIES Reverse-proxy addresses

Database and Redis

Variable Purpose
DB_POSTGRE_DSN / DB_POSTGRE_URL Connection string
DB_MAX_OPEN_CONNS, DB_MAX_IDLE_CONNS, DB_CONN_MAX_LIFE_MINS Pool tuning
REDIS_HOST, REDIS_PASS, REDIS_EXPIRED Redis connection and TTL

Production DSNs must use sslmode=verify-full

The production guard requires it. The Docker Compose stack deliberately runs with ENVIRONMENT=development because its internal database has no TLS.

The API must not connect as a superuser

RLS only enforces when the app connects as a least-privilege role. A superuser or BYPASSRLS role fails the boot in production.

JWT and sessions

Variable Purpose
JWT_SECRET ≥32 characters. Changing it invalidates every session
JWT_EXPIRED, JWT_REFRESH_EXPIRED Access / refresh lifetimes
JWT_ISSUER Usually the app domain. Changing it invalidates all existing tokens

eID (Relying Party)

Variable Purpose
EID_BASE_URL e-ID Gerege /v3 base (or the SSO sign relay)
EID_RP_UUID, EID_RP_SECRET RP credentials
SIGN_RELAY_TOKEN Shared token for signature relay (empty disables it)

OIDC provider side (this app acting as a provider)

Variable Purpose
OAUTH_ISSUER e.g. https://gerege.mn. The provider is enabled only when this is set
SSO_STATE_KEY HMAC key for transient login/consent state (≥32 bytes)
SSO_FIRSTPARTY_CLIENTS First-party clients that skip the consent screen
SSO_ADMIN_API_KEYS, SSO_ADMIN_SUBS Admin API access

Third parties and storage

Variable Purpose
GEMINI_API_KEY AI pipeline. Without it /ai/* returns a genuine 500
GOOGLE_CLIENT_ID / SECRET Google linking (the button hides when empty)
VERIFY_API_BASE, VERIFY_API_KEY, VERIFY_CHANNEL Citizen / organisation verification
XYP_API_BASE, XYP_CLIENT_ID, XYP_CLIENT_SECRET State-registry lookups
GSPACE_* The app's own SFTP storage (per-user quota)
INTEGRATION_ENC_KEY ≥16 bytes. Encrypts OAuth tokens and super admin MFA

INTEGRATION_ENC_KEY is mandatory

Deployments require this key, and once set it must never change — rotating it breaks every previously encrypted value.

Observability

Variable Purpose
OTEL_EXPORTER, OTEL_SAMPLE_RATIO OpenTelemetry tracing
OBSERVABILITY_TOKEN Bearer token gating /metrics and /swagger in production

Frontend

Variable Purpose
BACKEND_URL The internal address the BFF calls (e.g. http://api:8080)

The name api can collide on a shared network

When several stacks share one Docker network, http://api:8080 may resolve to a different container and every /api/v1/* call turns into a 404. Pin BACKEND_URL to your own api container's full name in that case.