porchlight/docs/plans/2026-02-18-config-file-design.md
2026-02-18 12:09:43 +01:00

84 lines
2.7 KiB
Markdown

# TOML Configuration File Design
**Date:** 2026-02-18
**Status:** Approved
## Goal
Add a TOML configuration file to Porchlight so that server settings and OIDC
client registrations can be defined in a file. Environment variables retain
highest priority and override any file-based values.
## Decisions
| Decision | Choice | Rationale |
|---|---|---|
| File format | TOML | Human-friendly, supports comments, stdlib `tomllib` on 3.11+ |
| Integration | pydantic-settings `TomlConfigSettingsSource` | Already available in pydantic-settings 2.12, handles precedence natively |
| Default path | `porchlight.toml` in CWD | Simple convention, overridable via `OIDC_OP_CONFIG_FILE` |
| Missing file | Silently skip | Env vars and defaults still work — file is optional |
| Scope | Server settings + client registrations in one file | Single source of truth |
## Precedence
```
env vars > TOML file > defaults
```
pydantic-settings handles this via `settings_customise_sources()`.
## Example porchlight.toml
```toml
issuer = "https://auth.example.com"
debug = false
session_secret = "a-long-random-string"
sqlite_path = "data/porchlight.db"
signing_key_path = "data/keys"
invite_ttl = 86400
[clients.my-webapp]
client_secret = "super-secret-value"
redirect_uris = ["https://app.example.com/callback"]
response_types = ["code"]
scope = ["openid", "profile", "email"]
token_endpoint_auth_method = "client_secret_basic"
[clients.another-app]
client_secret = "another-secret"
redirect_uris = ["https://other.example.com/oidc/callback"]
response_types = ["code"]
scope = ["openid", "profile"]
token_endpoint_auth_method = "client_secret_basic"
```
## Code Changes
### config.py
- Add `ClientConfig(BaseModel)` with fields: `client_secret`, `redirect_uris`,
`response_types` (default `["code"]`), `scope` (default `["openid"]`),
`token_endpoint_auth_method` (default `"client_secret_basic"`).
- Add `config_file: str = "porchlight.toml"` field to `Settings`.
- Add `clients: dict[str, ClientConfig] = {}` field to `Settings`.
- Override `settings_customise_sources()` to insert `TomlConfigSettingsSource`
between env and defaults. Use `config_file` field as the TOML path.
### app.py
- After creating the OIDC server, loop over `settings.clients` and register
each client in `oidc_server.context.cdb`.
- Keep the internal `manage-app` client registration as-is (always registered
regardless of config file).
### cli.py
- No changes. `Settings()` picks up the TOML file automatically.
## What Stays the Same
- All existing `OIDC_OP_*` env vars work identically.
- `Settings()` call sites unchanged.
- Docker/compose env var configuration unchanged.
- No new dependencies (pydantic-settings 2.12 already has TOML support,
Python 3.13 has `tomllib` in stdlib).