Settings Management
Micboard exposes one runtime read interface and one authorized write workflow. Application code must not import the internal registry or read MICBOARD_* values directly from Django settings.
Runtime reads
from micboard.services.settings.settings_service import settings as micboard_settings
threshold = micboard_settings.get( "battery_low_threshold", 20, organization=organization, site=site, manufacturer=manufacturer,)For ordinary configurable keys, the definition selects exactly one database scope. A manufacturer definition consults only the supplied manufacturer, a site definition only the supplied site, an organization definition only the supplied organization, and a global definition only the global row. Missing values then fall through MICBOARD_CONFIG, package defaults, the active SettingDefinition default, and the supplied default. Values never cascade across scopes.
Keys beginning with MICBOARD_ are deployment controls. They resolve only from Django host settings so an administrator cannot weaken a process, transport, or security ceiling with a database row. Named feature properties such as msp_enabled follow the same host-owned rule.
Runtime writes
Web forms and admin actions submit validated DTOs through micboard.services.settings.persistence_service.SettingsPersistenceService. That service owns definition lookup, authorization, type serialization, upsert, and cache invalidation. Do not write Setting rows directly from views, forms, commands, or unrelated services.
The supported user interfaces are:
/admin/micboard/settingdefinition/for definitions./admin/micboard/setting/for scoped values./settings/for the redacted overview./settings/bulk/for authorized bulk changes./settings/manufacturer/for manufacturer-scoped changes.
Definitions and scopes
SettingDefinition declares a key, value type, allowed scope, default, and optional choices. Setting stores one serialized value for a specific global, organization, site, or manufacturer scope. Database constraints reject duplicate scope rows; persistence DTO validation rejects incompatible scope combinations before the write.
Initialize the standard definitions with:
uv run --no-sync python manage.py init_settingsUse --reset only when intentionally replacing existing definitions in a controlled environment.
Caching
SettingsService owns cache invalidation. Normal writes invalidate automatically. Infrastructure code inside the settings domain may call:
micboard_settings.invalidate_value_cache("battery_low_threshold")micboard_settings.invalidate_definition_cache("battery_low_threshold")Callers outside settings administration should not invalidate caches as part of ordinary reads.
Adding a setting
- Add or update the definition in
micboard/management/commands/init_settings.py. - Choose the narrowest valid scope and a serialized default matching the declared type.
- Read it through
micboard_settings.get(...)with explicit scope objects. - Add resolution, scope-isolation, authorization, and redaction tests.
- Run the settings architecture test to confirm no alternate read seam was introduced.
Verification
uv run --no-sync pytest tests/test_settings.py tests/test_settings_service.py \ tests/services/settings tests/test_settings_architecture.pyuv run --no-sync ruff check micboard/services/settings tests/services/settingsuv run --no-sync python -m mypy micboard/services/settingsTroubleshooting
- Unexpected deployment value: inspect the matching Django
MICBOARD_*host setting. - Unexpected configurable value: verify the requested scope objects and the most-specific active database row.
- Stale value after a supported write: treat it as a persistence-service defect; supported writes invalidate caches automatically.
- Value hidden in the UI: review the settings presentation allowlist and sensitivity metadata.
