Configuration Health
A module can be installed and still do nothing: an empty setting, a missing database row or a dummy API key turns a feature off without an error until a background task trips over it. Configuration health makes that state visible before it costs anything (in the admin CMS, in the API and on the command line).
Where you see it
Section titled “Where you see it”- Admin CMS: a warning triangle next to the notification bell, only while a check fails. The panel lists each problem with a How to fix link; the user menu opens it any time, with the passing checks as a green grid. A screen that depends on a check (for example Leads → Review) shows the same text as a banner.
- API:
GET /api/munin/v2/health/(admin) returns the rows;POST /api/munin/v2/health/check/adds live probes such as an SMTP login. - CLI / CI gate:
python manage.py check --database default --tag entirius_config --fail-level WARNINGpython manage.py check --deploy --tag entirius_probe # live probes, on demandConfiguration health is state, not an event: nothing is marked read, and a fixed setting clears on the next read. Runtime failures (a send that bounced, an outage) still go to the notification bar.
The contract
Section titled “The contract”A check is a plain Django system check; django-munin aggregates them.
| Rule | Why |
|---|---|
Register with the tag entirius_config and a second tag that is the check code, <module>.<check> | the code names the row in the CMS and works as check --tag <code> |
Return [] when healthy; one checks.Warning per failing subject otherwise | a passing code shows as a green tick |
Never checks.Error | runserver and migrate abort on errors. A config gap must never stop a boot |
Warning(title, hint=detail, id=code, obj={"scope", "state", "severity", "fix_url"}) | scope = channel idx or ""; state = unconfigured, recording (a source reads recorded answers instead of the real API), unreachable or auth_failed (munin adds error when a check itself raises); severity = high, medium or low; fix_url = the module docs anchor or a CMS path (/…) |
Read the database only when databases is passed and your table exists | boot passes none; the endpoint and --database default do, and so does migrate, before it has created anything |
| No network in a config check | a probe is a separate check: tag entirius_probe, register(..., deploy=True), never raises; its failures show until the next “Check again”. One exception: toolbox.status keeps the toolbox client’s cached 60 s probe on every read, so it also runs at boot and in manage.py check |
Adding a check to a module
Section titled “Adding a check to a module”# django_<module>/checks.pyfrom django.conf import settingsfrom django.core import checks
CODE = "mymodule.api_key"FIX_URL = "https://github.com/entirius/entirius-django-mymodule/blob/master/docs/configuration.md#api-key"
@checks.register("entirius_config", CODE)def api_key_set(app_configs=None, **kwargs): if settings.MYMODULE_API_KEY: return [] return [ checks.Warning( "API key not set", hint="Set MYMODULE_API_KEY; until then the sync task skips every run.", id=CODE, obj={"scope": "", "state": "unconfigured", "severity": "high", "fix_url": FIX_URL}, ) ]Import the module from AppConfig.ready(). Then:
- Document the setting and the check command in the module’s own
docs/. Thefix_urllands there. - Add the CMS text:
config_health.checks.<code with _>.<state>andconfig_health.names.<code with _>in entirius-pwa-cmsen.json/pl.json. Without it the CMS shows your Englishtitle. - Test the check function directly: healthy →
[], each failing state → one warning withscopeandfix_url.
Checks shipped so far: toolbox.status (django-munin), email.smtp (django-email), communicator.smtp
(django-communicator), siteintel.sources and the siteintel.keys probe (django-siteintel). Each module’s docs/
describes its own.