Sync Repo-docs-MODULE-ARCHITECTURE from project files

2026-07-07 08:52:20 +02:00
parent f0a0114f78
commit 293753aa5a

@@ -1,4 +1,4 @@
<!-- codex-wiki-sync:bb7422b551349eda3e934059 --> <!-- codex-wiki-sync:b5012b7cacc0ba3fc59eae7f -->
> Mirrored from `/mnt/DATA/git/govoplan-core/docs/MODULE_ARCHITECTURE.md`. > Mirrored from `/mnt/DATA/git/govoplan-core/docs/MODULE_ARCHITECTURE.md`.
> Origin: `repository`. > Origin: `repository`.
@@ -334,6 +334,101 @@ Any future exception is extraction debt and must be temporary, documented in the
script with a reason, and removed when a capability/API/event contract replaces script with a reason, and removed when a capability/API/event contract replaces
it. it.
## Module Lifecycle
Core exposes the installed module catalog through the admin API and WebUI. The
current lifecycle model separates four states:
- installed: the Python/WebUI package is available to the process
- active: the module is present in the running platform registry
- desired: the module should be active on the next server startup
- planned package change: an operator-reviewed package install/uninstall item
saved in system settings but not executed by the running server
The admin module manager can change the desired enabled set and apply it to the
running server. It always keeps `tenancy`, `access`, and `admin` enabled when
saving through the admin UI, and it adds required module dependencies before
saving the desired state. On startup, core always keeps the minimum
authenticated platform set `tenancy`/`access` enabled and keeps `admin` enabled
when the operator configuration includes it. Unknown saved module ids are
ignored when the matching package is no longer installed. The core app factory,
devserver, development bootstrap, background worker registry, and migration
metadata plan all read the saved desired state from `system_settings` before
building their module registry.
Hot enable/disable is a core design principle for every module:
- Core keeps one mutable active `PlatformRegistry` object and swaps its manifest
set through the module lifecycle manager. Modules must read module presence,
optional integrations, permissions, role templates, capabilities, navigation,
and frontend contributions from that registry instead of caching sibling
module availability.
- Core validates install state and dependency closure before activation.
- Core applies configured module migrations before activation. Deactivation
never drops tables or data.
- Core mounts module routers once and guards them by active module state. A
deactivated module's routes remain mounted internally but return a disabled
module response until the module is active again.
- Module route factories must be side-effect-light and idempotent. They may
configure module runtime references, but they must not start workers,
schedulers, or irreversible external subscriptions. Use lifecycle hooks for
those resources.
- Core refreshes the active registry before frontend metadata is returned from
`/api/v1/platform/modules`; the WebUI shell refetches this metadata after
module changes so navigation, routes, and UI capabilities update without a
page reload.
- Modules can provide `on_activate` and `on_deactivate` hooks for worker,
scheduler, cache, or external subscription lifecycle. These hooks must be
idempotent and must not mutate another module directly.
- Package install/uninstall remains a deployment/operator action unless the
runtime provides a trusted package installer and rollback path. The admin UI
can activate/deactivate installed packages; it must not remove package files
or attempt dependency-manager operations in-process.
The package install-plan API records operator intent only:
- `GET /api/v1/admin/system/modules/install-plan` reads the saved plan and
renders shell commands for the operator.
- `PUT /api/v1/admin/system/modules/install-plan` saves planned install or
uninstall rows. Install rows must use tagged package or git references, not
local `file:`/workspace paths.
- `DELETE /api/v1/admin/system/modules/install-plan` clears the plan.
- `govoplan-module-install-plan --format shell` or
`python -m govoplan_core.commands.module_install_plan --format shell` renders
the same commands from a server shell.
The running server intentionally reports `install_uninstall_supported=false`
and `package_mutation_supported=false`. This is by design: mutating the
interpreter environment, npm dependency graph, frontend bundle, migrations, and
worker process set from inside the same running app is not safe without a
separate trusted installer, rollback model, and bundle loader.
## Maintenance Mode
Maintenance mode is the required operating state for package install/uninstall
and other disruptive system maintenance.
Core stores maintenance mode in `system_settings.settings.maintenance_mode`.
The public platform status endpoint exposes only the flag and message so the
WebUI can show a clear login-screen notice. Login remains reachable during
maintenance so an operator can sign in.
Authenticated API access is enforced at the access-principal boundary. When
maintenance mode is enabled, authenticated requests require the system scope
`system:maintenance:access`; otherwise the API returns `503 Service
Unavailable` with a maintenance-mode detail payload. The protected
`system_owner` role grants this through `system:*`. A dedicated
`maintenance_operator` role exists for accounts that should be able to access
the system during maintenance without receiving broad write permissions.
Changing the maintenance-mode flag requires both system settings write access
and `system:maintenance:access`, so an administrator cannot accidentally enable
a mode they cannot use.
The first implementation is a platform access gate. It does not replace
database backups, process supervision, migration checks, or external load
balancer maintenance pages.
## Build And Verification ## Build And Verification
Backend verification from core: Backend verification from core: