Application Settings
๐ Admin only. This page documents administrator features. Configuration lives in
config.yamland the in-app Config editor (/admin/config), which are only available to administrators.
These are the core settings that make Lumen work: database, authentication, and general behavior.
app
| Field | Description |
|---|---|
name |
Display name shown in the app header |
tagline |
Subtitle shown next to the name |
secret_key |
Flask session encryption key (see Security Notes below) |
encryption_key |
Used to hash user API keys and encrypt upstream endpoint API keys stored in the database. See Security Notes |
database.url |
SQLAlchemy connection URL โ PostgreSQL (postgresql://...) in production, or SQLite (sqlite:///lumen.db) for local development |
debug |
Enable debug mode (set to false in production) |
diagnostics |
Optional (default: true) โ stack-capturing DB/context diagnostics (connection-pool checkout tracking and app-context probes, surfaced via /metrics/debug). Set to false to remove the per-request capture overhead. Restart required. |
theme |
Institutional theme. Themes live in themes/<name>/. Built-in: default, illinois, uic, uis. Falls back to default if not found. |
github_url |
Optional โ overrides the default GitHub link in the navbar |
config_editor |
Optional (default: true) โ when false, the /admin/config editor is read-only. Set this for git-managed configs that should only change through version control. The Helm chart sets it to false. |
The global acknowledgement message has moved to
defaults.models.ack_message(see Admin Configuration). The legacyapp.graylist_default_noticeis still accepted as input and feedsdefaults.models.ack_message.
Database Pool
On PostgreSQL the connection pool is auto-sized from the server's
max_connections, divided across all worker processes and Kubernetes replicas so
the combined usage cannot exhaust the server:
pool_sizeper process = 60% ofmax_connectionsรท (workers ร replicas)max_overflowper process = 20% ofmax_connectionsรท (workers ร replicas)- the remaining 20% is reserved for psql, migrations, monitoring, etc.
Worker count is detected from WEB_CONCURRENCY or the uvicorn --workers flag;
replica count comes from the LUMEN_REPLICAS env var (set automatically by the Helm
chart from replicaCount). Pre-ping is always enabled to silently replace stale
connections. SQLite has no connection limit, so pool sizing is skipped for it.
Optional overrides under app.database:
| Field | Description | Default |
|---|---|---|
pool_size |
Override persistent connections per process | auto-sized |
max_overflow |
Override burst connections above pool_size per process |
auto-sized |
max_connections |
Override the detected Postgres max_connections (skips the SHOW max_connections query) |
queried at startup |
pool_timeout |
Seconds to wait before raising a timeout error | 30 |
pool_recycle |
Recycle connections after N seconds to avoid stale-connection errors | N/A (no recycling) |
logging |
SQLAlchemy engine/pool log level, e.g. debug to show per-connection checkout/checkin lines |
info |
Explicit pool_size / max_overflow are honored only if they fit within 80% of
max_connections across all workers ร replicas; otherwise they are ignored and the
auto-sized values are used (a warning is logged).
app:
database:
url: postgresql://lumen:lumen@localhost:5432/lumen
# pool_size / max_overflow omitted โ auto-sized
pool_timeout: 30
pool_recycle: 1800
# logging: debug # SQLAlchemy engine/pool DEBUG lines (checkout/checkin)
Development User
For local development only. When dev_user is set:
app:
dev_user:
email: dev@example.com
groups:
- staff
The specified email logs in directly without going through the OAuth identity provider. It also assigns the listed groups to that user. Should not be used in production.
Logging
Optional settings under app.logs:
| Field | Description | Default |
|---|---|---|
level |
Log level โ debug, info, warning, error |
debug in development, info in production |
access |
Log HTTP request details | true |
model |
Log per-endpoint health check results | false |
app:
logs:
level: debug
access: true
model: true
oauth2
Lumen uses CILogon for authentication. Register your application at CILogon to get a client_id and client_secret:
| Field | Description |
|---|---|
client_id |
CILogon application ID |
client_secret |
CILogon application secret |
server_metadata_url |
Well-known URL for the OAuth2 provider (CILogon example: https://cilogon.org/.well-known/openid-configuration) |
redirect_uri |
Where CILogon sends users after login โ matches the callback URL registered at CILogon |
scopes |
CILogon scopes to request; org.cilogon.userinfo is required for group matching |
params |
Optional extra parameters passed to CILogon (e.g. idphint to restrict login to a specific identity provider) |
allow_unverified_email |
Accept logins whose provider reports the email as unverified (email_verified: false). Defaults to false. A missing email_verified claim is always accepted. |
oauth2:
client_id: cilogon:/client_id/your-id
client_secret: your-secret
server_metadata_url: https://cilogon.org/.well-known/openid-configuration
redirect_uri: https://your-instance/callback
scopes: openid email profile org.cilogon.userinfo
allow_unverified_email: false
params:
idphint: urn:mace:incommon:uiuc.edu
Restart required: Changing any
oauth2field requires a restart, exceptallow_unverified_email, which is hot-reloaded.
chat
Controls behavior of the web chat interface:
chat:
remove: hide
upload:
max_size_mb: 10
max_text_chars: 100000
allowed_extensions:
- txt
- md
- csv
- json
- py
- js
- ts
- html
- css
- xml
- yaml
- yml
- pdf
- png
- jpg
- jpeg
- gif
| Field | Description |
|---|---|
remove |
hide = soft-delete (recoverable) / delete = hard delete |
upload.max_size_mb |
Maximum file upload size in MB |
upload.max_text_chars |
Maximum extracted text characters before truncation |
upload.allowed_extensions |
Allowed file extensions (uncomment to customize; backend uses magic-byte detection to classify each file) |
rate_limiting
Controls how many API requests each user can make:
rate_limiting:
# storage_url: redis://localhost:6379/0 # omit = in-memory (single-process)
limit: "30 per minute" # per authenticated user identity
| Field | Description |
|---|---|
storage_url |
Omit for in-memory (dev only); set to a Redis URL for multi-instance deployments |
limit |
Rate limit in the format "N per timeframe" (e.g. "30 per minute", "1000 per hour") |
Restart required: Changing
storage_urlrequires a restart because the Redis client is initialized at startup.
api
Settings for the OpenAI-compatible API and operational endpoints. Note: monitoring and Prometheus
config live under api: (e.g. api.monitoring.token, api.prometheus.enabled).
api:
consent: true # set to false to exempt API requests from the model acknowledgement requirement
monitoring:
token: "my-secret-token"
prometheus:
enabled: false
token: "" # set to a long random string to require Bearer token auth
debug_token: "" # separate token for /metrics/debug; empty = use `token`
multiproc_dir: "" # path for multi-worker aggregation (e.g. /tmp/prometheus_multiproc)
| Field | Description |
|---|---|
consent |
When true (default), API requests to models with needs_ack: true require recorded acknowledgement, just like the web UI. Set to false to exempt API requests from the acknowledgement requirement. |
api.monitoring
A read-only token for uptime monitoring tools (e.g. Uptime Kuma). Pass it as a Bearer token in the
Authorization header:
Authorization: Bearer my-secret-token
The monitoring token can only access GET /v1/models and GET /v1/models/<id> โ it cannot be used to send chat requests or access any other endpoint. Leave token empty to disable monitoring access.
api.prometheus
Optional Prometheus metrics endpoint at /metrics:
| Field | Description |
|---|---|
enabled |
Enable or disable the metrics endpoint |
token |
Bearer token for auth; empty = no auth required |
debug_token |
Separate, stronger bearer token required for /metrics/debug (it exposes thread dumps and deployment details); empty = /metrics/debug uses token |
multiproc_dir |
Shared directory for multi-worker aggregation; mount a shared volume here in container deployments |
Restart required: Changing
api.prometheus.enabledorapi.prometheus.multiproc_dirrequires a restart.api.prometheus.tokenis read on each request and takes effect immediately.
GET /metrics/debug uses debug_token when set (falling back to the scrape token otherwise) and returns a plain-text diagnostic dump: every outstanding DB connection-pool checkout (with the endpoint, thread and stack that took it) plus a stack dump of every live thread. Use it when lumen_db_pool_connections{state="checked_out"} or {state="stranded"} climbs and never falls back โ whatever is still listed after several minutes is holding a connection it never returned.
Environment Variables
Some settings can be controlled via environment variables. The precedence depends on the setting:
All of the following environment variables take precedence over the corresponding config.yaml values:
| Environment Variable | Overrides |
|---|---|
CONFIG_YAML |
Path to the config file (default: ./config.yaml) |
DATABASE_URL |
app.database.url |
LUMEN_SECRET_KEY |
app.secret_key |
LUMEN_ENCRYPTION_KEY |
app.encryption_key |
OAUTH2_CLIENT_ID |
oauth2.client_id |
OAUTH2_CLIENT_SECRET |
oauth2.client_secret |
OAUTH2_SERVER_METADATA_URL |
oauth2.server_metadata_url |
OAUTH2_REDIRECT_URI |
oauth2.redirect_uri |
OAUTH2_SCOPES |
oauth2.scopes |
app.secret_key and app.encryption_key must each be a random string of at least 32 characters, set either in config.yaml or via their environment variables. Generate separate values (for example, with openssl rand -hex 32); the app will not start with missing or shorter keys.
Security Notes
app.secret_keyis used for Flask session signing. If leaked, an attacker can forge user sessions. In production, inject a randomly generated value of at least 32 characters viaLUMEN_SECRET_KEY.app.encryption_keyis used to hash API keys and encrypt upstream endpoint credentials stored in the database. Rotating this value invalidates all existing user API keys and makes existing encrypted credentials unreadable. Inject a separate randomly generated value of at least 32 characters viaLUMEN_ENCRYPTION_KEY.- Never commit
config.yamlwith real secrets to a shared repository. Useconfig.yaml.exampleas a template and keep your live config file in a private location or inject secrets via environment variables.