Release v0.1.6
This commit is contained in:
@@ -1,19 +1,147 @@
|
||||
# GovOPlaN Module Architecture
|
||||
|
||||
GovOPlaN is structured as a core platform runner plus installable feature modules. Core owns the runtime shell and cross-cutting primitives. Modules own feature behavior and contribute backend routes, database metadata, permissions, WebUI routes, and navigation metadata.
|
||||
GovOPlaN is structured as a platform kernel plus installable modules. The kernel starts and composes the platform. Modules own product behavior and contribute backend routes, database metadata, permissions, WebUI routes, navigation metadata, capabilities, and events.
|
||||
|
||||
## Core Responsibilities
|
||||
The current package name is still `govoplan-core`, but the architecture target is a smaller kernel. Access, tenancy, policy, audit, and admin semantics are platform-module responsibilities and should be extracted in stages.
|
||||
|
||||
Core owns:
|
||||
The concrete access/auth/RBAC extraction path is tracked in
|
||||
[`ACCESS_EXTRACTION_PLAN.md`](ACCESS_EXTRACTION_PLAN.md).
|
||||
|
||||
## Layer Model
|
||||
|
||||
| Layer | Purpose | Examples |
|
||||
| --- | --- | --- |
|
||||
| Kernel | Bootstraps and composes the platform | app factory, module registry, route aggregation, migration orchestration, capability/event contracts, health metadata |
|
||||
| Platform modules | Cross-cutting governance capabilities | access, tenancy, policy, audit, admin, ops |
|
||||
| Service modules | Reusable operational capabilities | files, mail, templates, recipients, notifications |
|
||||
| Business modules | Public-sector workflows | campaigns, cases, forms, approvals, appointments |
|
||||
| Connector modules | External system integration | FIT-Connect, XÖV/XTA, DMS/eAkte, ERP, IDM |
|
||||
|
||||
## Kernel Responsibilities
|
||||
|
||||
The kernel target owns:
|
||||
|
||||
- the server entry point and platform configuration
|
||||
- module discovery, registry validation, route aggregation, and platform metadata APIs
|
||||
- database engine/session primitives and migration orchestration
|
||||
- auth, tenants, RBAC, governance, audit, CSRF/API helpers, and secret helpers
|
||||
- shared WebUI shell components such as `AppShell`, `IconRail`, `DataGrid`, `ExplorerTree`, `MessageDisplayPanel`, dialogs, loading frames, access boundaries, and form primitives
|
||||
- module discovery, manifest validation, registry validation, route aggregation, and platform metadata APIs
|
||||
- database engine/session lifecycle and module migration orchestration
|
||||
- module install-plan validation, signed catalog verification, license entitlement checks, and installer request orchestration
|
||||
- capability registry, command/event contracts, and lifecycle hooks
|
||||
- shared WebUI shell contracts, generic WebUI components, and module route/nav rendering
|
||||
- centralized mapping from serializable icon names to renderable frontend icons
|
||||
- health, OpenAPI aggregation, and runtime diagnostics
|
||||
|
||||
Core must not import module feature pages or module business logic directly. It should interact with modules through manifests, entry points, metadata, and route contributions.
|
||||
The kernel must not own product semantics such as users, tenants, RBAC decisions, governance policies, audit storage, mail behavior, file behavior, or campaign behavior. Those belong to platform, service, or business modules.
|
||||
|
||||
## Current Compatibility Responsibilities
|
||||
|
||||
During the staged split, `govoplan-core` still contains compatibility surfaces
|
||||
for access, auth, tenancy, RBAC, governance, audit, CSRF/API helpers, and
|
||||
secret helpers. The extracted access implementation now lives in
|
||||
`govoplan-access`; live legacy ORM table definitions have been split across
|
||||
their platform owners while retaining historical table names. The old core
|
||||
model, route, admin-service, and access-security import shims have been
|
||||
removed; callers must use module-owned imports or kernel capabilities. The
|
||||
remaining compatibility surfaces are temporary until the matching platform
|
||||
modules are fully self-contained:
|
||||
|
||||
- `govoplan-access`
|
||||
- `govoplan-tenancy`
|
||||
- `govoplan-policy`
|
||||
- `govoplan-audit`
|
||||
- `govoplan-admin`
|
||||
|
||||
New code should avoid deepening these compatibility dependencies. Prefer explicit kernel contracts and module capabilities over direct imports.
|
||||
|
||||
Core must not import module feature pages or module business logic directly. It should interact with modules through manifests, entry points, metadata, capabilities, events, and route contributions.
|
||||
|
||||
## Stable Kernel Contracts
|
||||
|
||||
The following contracts are the baseline API that modules can rely on:
|
||||
|
||||
- `ModuleManifest`
|
||||
- `ModuleCompatibility`
|
||||
- module uninstall guard provider contract
|
||||
- `MigrationSpec`
|
||||
- route factory contract
|
||||
- capability factory contract
|
||||
- access DTO/protocol contracts in `govoplan_core.core.access`
|
||||
- resource ACL provider contract
|
||||
- tenant summary provider contract
|
||||
- tenant delete-veto provider contract
|
||||
- WebUI module contribution contract
|
||||
- navigation metadata contract
|
||||
- command/event envelope contract
|
||||
|
||||
Changes to these contracts must be versioned or accompanied by compatibility shims.
|
||||
|
||||
Known access-related capability names are defined in
|
||||
`govoplan_core.core.access`, including:
|
||||
|
||||
- `access.principalResolver`
|
||||
- `access.directory`
|
||||
- `access.permissionEvaluator`
|
||||
- `access.resourceAccess`
|
||||
- `access.tenantProvisioner`
|
||||
- `access.administration`
|
||||
- `access.governanceMaterializer`
|
||||
- `tenancy.tenantResolver`
|
||||
- `security.secretProvider`
|
||||
- `audit.sink`
|
||||
|
||||
`govoplan-access` currently registers `access.principalResolver`,
|
||||
`access.permissionEvaluator`, `access.directory`, `access.tenantProvisioner`,
|
||||
`access.administration`, and `access.governanceMaterializer`.
|
||||
`govoplan-tenancy` registers `tenancy.tenantResolver`. The minimal
|
||||
authenticated platform set is now `tenancy` plus `access`; the registry
|
||||
inserts `tenancy` before `access` when only feature modules are requested.
|
||||
Feature modules should prefer these capabilities over direct reads of
|
||||
access/tenant ORM models when they need labels, group membership, default
|
||||
access provisioning, counts, audit actor labels, or tenant metadata.
|
||||
|
||||
FastAPI route dependencies for authenticated endpoints are access-owned and
|
||||
published from `govoplan_access.backend.auth.dependencies`. Routers may import
|
||||
that dependency module directly until a more generic request-principal adapter
|
||||
exists; they must not import access ORM models or other access implementation
|
||||
internals.
|
||||
|
||||
Current live table ownership:
|
||||
|
||||
- `govoplan-tenancy`: `tenants`
|
||||
- `govoplan-access`: `accounts`, `users`, `groups`, `roles`,
|
||||
`system_role_assignments`, `user_group_memberships`,
|
||||
`user_role_assignments`, `group_role_assignments`, `api_keys`,
|
||||
`auth_sessions`
|
||||
- `govoplan-admin`: `governance_templates`,
|
||||
`governance_template_assignments`
|
||||
- `govoplan-audit`: `audit_log`
|
||||
- `govoplan-core`: `system_settings`
|
||||
|
||||
Current admin route ownership follows the same boundary: access contributes
|
||||
users, groups, roles, system accounts/roles, auth, sessions, and API-key
|
||||
administration; tenancy contributes tenant registry/settings routes; admin
|
||||
contributes system settings, overview, and governance-template routes; audit
|
||||
contributes audit-log routes. Governance template metadata and assignment
|
||||
routes live in `govoplan-admin`; materializing those templates into
|
||||
access-owned groups and roles is performed by the
|
||||
`access.governanceMaterializer` capability.
|
||||
|
||||
Current admin WebUI ownership mirrors that route split. `govoplan-access`
|
||||
contributes the `/admin` route shell and admin nav item. Other platform modules
|
||||
contribute individual admin sections through the `admin.sections` UI capability.
|
||||
`govoplan-admin` contributes the overview, system settings, and governance
|
||||
template sections through that capability. Access-owned tenant/user/group/role
|
||||
sections remain in the access package until their owning platform modules take
|
||||
them over.
|
||||
|
||||
Cross-module feature contracts live under focused kernel contract modules. For
|
||||
example, `govoplan_core.core.campaigns` defines
|
||||
`campaigns.access`, `campaigns.mailPolicyContext`,
|
||||
`campaigns.policyContext`, `campaigns.deliveryTasks`, and
|
||||
`campaigns.retention`. The campaign module registers these capabilities so mail
|
||||
can resolve campaign owner/policy context and delivery tasks, files can validate
|
||||
campaign file-share access, and core retention can call campaign-owned cleanup
|
||||
logic without importing campaign ORM models. Keep these contracts small
|
||||
DTO/protocol surfaces and register concrete behavior from the owning module.
|
||||
|
||||
## Module Responsibilities
|
||||
|
||||
@@ -26,6 +154,7 @@ A module owns:
|
||||
- module migrations and migration metadata
|
||||
- module permissions and role templates
|
||||
- module-specific schemas, policies, and domain rules
|
||||
- module package metadata and retirement providers used by install/uninstall preflight
|
||||
- WebUI pages, feature-specific components, API clients, route contributions, and navigation metadata
|
||||
|
||||
A module should not own generic platform UI. If a component is useful outside one module, move it to `@govoplan/core-webui` and parameterize it there before reusing it.
|
||||
@@ -44,12 +173,15 @@ files = "govoplan_files.backend.manifest:get_manifest"
|
||||
The manifest should declare:
|
||||
|
||||
- `id`, `name`, `version`
|
||||
- `compatibility` when the module needs a minimum/maximum core version or a
|
||||
newer manifest contract
|
||||
- required `dependencies` and `optional_dependencies`
|
||||
- permissions and role templates
|
||||
- router factory
|
||||
- migration metadata and script location
|
||||
- frontend package metadata
|
||||
- navigation metadata using serializable icon names
|
||||
- uninstall guard providers for data, migration, worker, or scheduler vetoes
|
||||
|
||||
Backend nav metadata must use icon-name strings, not frontend components:
|
||||
|
||||
@@ -74,6 +206,37 @@ Rules:
|
||||
- Keep module-owned tables and migrations in the module repository.
|
||||
- Keep cross-module foreign-key assumptions explicit and conservative.
|
||||
- Register module metadata in `MigrationSpec` so core can discover it.
|
||||
- Optional module migrations may create multiple Alembic heads. Verification
|
||||
should compare the database heads to the configured script heads instead of
|
||||
assuming one linear revision when multiple modules are enabled.
|
||||
|
||||
## Install, Uninstall, And Catalogs
|
||||
|
||||
Core owns the install plan, signed catalog validation, license entitlement
|
||||
check, maintenance-mode guard, replay state, and installer request queue.
|
||||
Package mutation is performed by `govoplan-module-installer` outside the
|
||||
FastAPI request process.
|
||||
|
||||
Official catalogs can be served as static JSON from `govoplan-web`, but core
|
||||
does not trust the website by location alone. A catalog must pass the configured
|
||||
signature, channel, freshness, and replay rules before a catalog entry can be
|
||||
planned. Catalog entries may declare `license_features`; core checks those
|
||||
against the configured offline license before adding the entry to the install
|
||||
plan.
|
||||
|
||||
Modules should provide:
|
||||
|
||||
- pinned backend and WebUI package refs for official catalog entries
|
||||
- compatibility metadata in the module manifest
|
||||
- lifecycle hooks when a runtime enable/disable action needs module-specific
|
||||
work
|
||||
- uninstall guards for persistent data, active workers, schedulers, or external
|
||||
bindings
|
||||
- retirement providers when destructive uninstall can safely drop or retire
|
||||
module-owned data
|
||||
|
||||
Uninstall remains non-destructive unless the operator explicitly requests
|
||||
`destroy_data` and the module provides a retirement provider that supports it.
|
||||
|
||||
## WebUI Contract
|
||||
|
||||
@@ -103,6 +266,30 @@ WebUI modules receive only the core route context:
|
||||
|
||||
A module should call its own API client and module-owned backend routes. Shared API helpers should live in core only when they are truly platform-level concerns.
|
||||
|
||||
Modules can also contribute named UI capabilities for explicit extension
|
||||
points. Capability values must be narrow, typed contracts, not imports from a
|
||||
sibling feature package. For admin pages, modules contribute:
|
||||
|
||||
```ts
|
||||
const adminSections: AdminSectionsUiCapability = {
|
||||
sections: [
|
||||
{
|
||||
id: "system-settings",
|
||||
label: "General",
|
||||
group: "SYSTEM",
|
||||
order: 10,
|
||||
allOf: ["system:settings:read"],
|
||||
render: ({ settings, auth }) => createElement(SystemSettingsPanel, { settings, auth })
|
||||
}
|
||||
]
|
||||
};
|
||||
```
|
||||
|
||||
The access admin route shell collects all installed `admin.sections`
|
||||
capabilities with `usePlatformUiCapabilities("admin.sections")`, filters them
|
||||
by `anyOf`/`allOf`, and renders them without importing the contributing module's
|
||||
components directly.
|
||||
|
||||
## Icon Rules
|
||||
|
||||
Icons are resolved centrally by core.
|
||||
@@ -112,6 +299,7 @@ Modules must provide icon names with `iconName` in frontend nav contributions an
|
||||
Current core icon names include:
|
||||
|
||||
- `activity`
|
||||
- `admin`
|
||||
- `campaign`
|
||||
- `dashboard`
|
||||
- `file`
|
||||
@@ -124,6 +312,9 @@ Current core icon names include:
|
||||
|
||||
If a module needs a new navigation icon, add the name-to-component mapping in core first, then use the name in backend and frontend metadata.
|
||||
|
||||
The access module uses the `admin` icon for its `/admin` route. Core only
|
||||
resolves that icon name; it does not hard-code the admin route in the rail.
|
||||
|
||||
## Shared Component Rules
|
||||
|
||||
Use this rule of thumb:
|
||||
@@ -137,6 +328,9 @@ Examples:
|
||||
|
||||
- `ExplorerTree` is core because files, mailboxes, and future modules can all render hierarchical navigation.
|
||||
- `MessageDisplayPanel` is core because mail, campaign sending, and later audit/review surfaces can display message-like content.
|
||||
- `AdminPageLayout`, `AdminIconButton`, and `AdminSelectionList` are core
|
||||
because access, admin, tenancy, policy, and audit panels share the same admin
|
||||
shell language.
|
||||
- `MailProfileManagement` remains in the mail module because it is specific to mail transport policies and profiles.
|
||||
|
||||
## Cross-Module Integration
|
||||
@@ -147,16 +341,298 @@ Rules:
|
||||
|
||||
- Use core module metadata to check whether another module is installed.
|
||||
- Use backend APIs/events/service contracts for runtime cooperation.
|
||||
- If a sibling module needs owner-specific data, expose a narrow DTO/protocol
|
||||
capability from the owning module instead of importing its ORM models.
|
||||
- Keep UI integration declarative where possible: nav items, route contributions, context actions, and explicit extension points.
|
||||
- Avoid direct imports from one feature module into another feature module unless the imported package is a published API contract designed for that purpose. UI components should be promoted to core instead.
|
||||
|
||||
### Dependency Boundary Enforcement
|
||||
|
||||
The repository includes `scripts/check_dependency_boundaries.py`. It enforces the current baseline:
|
||||
|
||||
- kernel/core source may not add new direct imports of files/mail/campaign internals
|
||||
- access source may not import files/mail/campaign internals
|
||||
- feature modules may not import access implementation internals
|
||||
- feature modules may not add new direct imports of sibling feature modules
|
||||
- FastAPI routers may import the published
|
||||
`govoplan_access.backend.auth.dependencies` dependency API
|
||||
- the transitional allowlist is expected to stay empty
|
||||
|
||||
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
|
||||
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.
|
||||
- Modules that own persistent data, background jobs, schedulers, external
|
||||
subscriptions, or irreversible migration state must expose uninstall guard
|
||||
providers through their manifest. Guards return `blocker`, `warning`, or
|
||||
`info` results and may inspect live state through the core-owned DB session.
|
||||
Default package uninstall is non-destructive, so ordinary persistent data
|
||||
should warn that data will remain dormant. Guards should block only when
|
||||
removing the package would corrupt other active modules, workers, external
|
||||
subscriptions, or deployment state. A guard failure is treated as a blocker.
|
||||
- Modules that can destroy their own data must also expose a migration
|
||||
retirement provider. Destructive retirement is opt-in per uninstall plan row
|
||||
through `destroy_data: true`; the installer then snapshots the database,
|
||||
invokes the module-owned retirement executor while the package is still
|
||||
installed, and only then removes Python/WebUI packages. Without that flag,
|
||||
the same provider is used for preflight reporting only and module tables/data
|
||||
remain dormant.
|
||||
- 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 is performed by the trusted operator installer, not
|
||||
directly inside FastAPI request handlers. The admin UI can save install plans,
|
||||
show preflight blockers, and activate/deactivate installed packages.
|
||||
|
||||
The package install-plan API records operator intent only:
|
||||
|
||||
- `GET /api/v1/admin/system/modules/install-plan` reads the saved plan,
|
||||
renders shell commands, and returns installer preflight status.
|
||||
- `GET /api/v1/admin/system/modules/install-runs` returns recent installer run
|
||||
summaries and the current installer lock status.
|
||||
- `GET /api/v1/admin/system/modules/install-runs/{run_id}` returns the raw run
|
||||
record for diagnosis.
|
||||
- `GET /api/v1/admin/system/modules/install-requests` returns daemon handoff
|
||||
requests queued from the admin UI or CLI plus the current daemon heartbeat.
|
||||
- `POST /api/v1/admin/system/modules/install-requests` queues a supervised
|
||||
installer request. It requires maintenance mode and maintenance access. The
|
||||
FastAPI request writes only the request record; it does not run package
|
||||
commands.
|
||||
- `POST /api/v1/admin/system/modules/install-requests/{request_id}/cancel`
|
||||
cancels a queued request. Running requests are not interrupted by the API;
|
||||
they remain owned by the installer daemon.
|
||||
- `POST /api/v1/admin/system/modules/install-requests/{request_id}/retry`
|
||||
queues a new request using the options from a failed or cancelled request.
|
||||
- `GET /api/v1/admin/system/modules/package-catalog` reads approved package
|
||||
references from `GOVOPLAN_MODULE_PACKAGE_CATALOG` so operators can add known
|
||||
module refs to the install plan without typing them manually. The endpoint
|
||||
also reports catalog validity, channel, signature, trust state, and the
|
||||
configured path.
|
||||
- `POST /api/v1/admin/system/modules/install-plan/catalog/{module_id}` saves
|
||||
a planned install row from a validated catalog entry. Catalog signature and
|
||||
approved-channel policy are enforced before the row is saved.
|
||||
- `POST /api/v1/admin/system/modules/{module_id}/uninstall-plan` saves a
|
||||
planned non-destructive uninstall row for an installed module after it has
|
||||
been disabled. The Python distribution name is resolved from the installed
|
||||
`govoplan.modules` entry point; the WebUI package name comes from the module
|
||||
manifest. Operators can then edit the saved plan row and set `destroy_data`
|
||||
when they explicitly want module-owned tables/data retired before package
|
||||
removal.
|
||||
- `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. Python install rows must also include the
|
||||
distribution package name so rollback can uninstall newly added packages.
|
||||
- `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.
|
||||
- `govoplan-module-installer --format shell` runs the same preflight checks from
|
||||
the server shell.
|
||||
- `govoplan-module-installer --apply --build-webui` executes the saved plan
|
||||
after preflight passes, snapshots `pip freeze` and WebUI package files, writes
|
||||
a run record under the runtime installer directory, and marks planned rows as
|
||||
applied after success. Successful installs are added to saved startup state by
|
||||
default; successful uninstalls are removed from saved startup state by default.
|
||||
Use `--no-activate-installed-modules` or
|
||||
`--keep-uninstalled-modules-in-desired` only for staged rollout workflows.
|
||||
- `govoplan-module-installer --supervise --migrate --health-url http://127.0.0.1:8000/health --restart-command '<restart govoplan server>'`
|
||||
is the preferred disruptive-change path. It applies the plan, optionally runs
|
||||
migrations in a fresh Python process after a fresh-process manifest
|
||||
verification, runs the restart command if provided, polls health, and
|
||||
automatically rolls packages back from the run snapshot if commands,
|
||||
migrations, restart, or health recovery fail.
|
||||
- `govoplan-module-installer --daemon` runs the request executor. It polls the
|
||||
runtime request queue, claims one request at a time, and executes the same
|
||||
supervised installer flow. `--daemon-once` processes at most one queued
|
||||
request and exits, which is useful for tests or process-manager one-shot
|
||||
units. The daemon writes `daemon.status.json` under the installer runtime
|
||||
directory so the admin UI and CLI can report heartbeat/status.
|
||||
- `govoplan-module-installer --enqueue-supervised` creates the same request
|
||||
record from a shell instead of from the admin UI.
|
||||
- `govoplan-module-installer --daemon-status --format json` reports the daemon
|
||||
heartbeat. `--cancel-request <request-id>` and `--retry-request <request-id>`
|
||||
provide shell equivalents for the admin UI request controls.
|
||||
- `govoplan-module-installer --validate-package-catalog [path] --format json`
|
||||
validates a catalog file or the catalog configured through
|
||||
`GOVOPLAN_MODULE_PACKAGE_CATALOG`.
|
||||
- `--sign-package-catalog <path> --catalog-signing-key-id <key-id> --catalog-signing-private-key <pem>`
|
||||
signs a catalog with Ed25519. `--require-signed-catalog`,
|
||||
`--approved-catalog-channel <channel>`, and
|
||||
`--catalog-trusted-key <key-id>=<base64-public-key>` enforce the approved
|
||||
release-channel path from an operator shell.
|
||||
- `govoplan-module-installer --rollback <run-id>` restores the saved package
|
||||
snapshots, restores the captured SQLite or external database snapshot when
|
||||
present, restores the previous desired module state when the database was not
|
||||
restored wholesale, and reruns package installation from the previous freeze
|
||||
file.
|
||||
- `--database-backup-command '<command>'` and
|
||||
`--database-restore-command '<command>'` provide non-SQLite backup/restore
|
||||
hooks for migrated installer runs. The backup hook runs before migrations;
|
||||
the restore hook runs during rollback and can be overridden on the rollback
|
||||
command line.
|
||||
- `govoplan-module-installer --list-runs --format json`,
|
||||
`--show-run <run-id> --format json`, and `--lock-status --format json`
|
||||
expose the same run-history and lock information from the operator shell.
|
||||
- `--list-requests --format json` and `--show-request <request-id> --format json`
|
||||
expose daemon handoff records from the operator shell.
|
||||
|
||||
The supervisor accepts multiple restart commands and health URLs. This is the
|
||||
process boundary for web, worker, scheduler, and auxiliary service restarts:
|
||||
each restart command is executed, each health URL is polled, and rollback uses
|
||||
the same restart/health set after restoring package and database snapshots.
|
||||
|
||||
The installer preflight is intentionally conservative:
|
||||
|
||||
- maintenance mode must be active;
|
||||
- installed module manifests must be compatible with the supported manifest
|
||||
contract and current core version;
|
||||
- uninstalling `tenancy`, `access`, or `admin` is blocked;
|
||||
- uninstalling an active module is blocked;
|
||||
- uninstalling a module still present in desired startup state is blocked;
|
||||
- uninstalling a module with active/desired dependents is blocked;
|
||||
- uninstalling a module that owns migrations is non-destructive by default:
|
||||
schema/data remain dormant and preflight emits a warning;
|
||||
- modules that declare explicit migration retirement support should also
|
||||
register a retirement provider. Without a provider, preflight emits a
|
||||
manual-review warning. Providers may return blockers, warnings, and an
|
||||
explanatory summary, but core does not drop schema or data on behalf of
|
||||
modules.
|
||||
- module-owned uninstall guard providers can veto data/migration/worker unsafe
|
||||
removals;
|
||||
- install refs must be exact versions or tagged git refs;
|
||||
- Python install rows must include the package distribution name for rollback;
|
||||
- WebUI package changes require a WebUI root and trigger rebuild/reload status.
|
||||
|
||||
The installer supervisor must run outside the FastAPI server process. A server
|
||||
request handler cannot reliably restart or roll back the process that is
|
||||
currently executing the request. The admin UI therefore remains an operator
|
||||
planning and request-submission surface; the trusted daemon/CLI is the executor.
|
||||
|
||||
Automatic rollback covers Python and WebUI package state. The manual/shell
|
||||
daemon can run `npm install` and `npm run build`; this remains the supported
|
||||
WebUI package path. Browser-loaded remote module bundles are experimental and
|
||||
reserved for controlled deployments with integrity/signature policy.
|
||||
|
||||
For `sqlite:///` database URLs, `--migrate` also captures a SQLite backup and
|
||||
rollback restores it before the supervisor restarts the server. For non-SQLite
|
||||
database URLs, `--migrate` requires deployment-specific backup and restore
|
||||
commands. A `--database-restore-check-command` can validate the created backup
|
||||
artifact before migrations proceed. Hook commands run in the installer run
|
||||
directory with:
|
||||
|
||||
- `GOVOPLAN_INSTALLER_RUN_DIR`
|
||||
- `GOVOPLAN_DATABASE_URL`
|
||||
- `GOVOPLAN_DATABASE_BACKUP_PATH`
|
||||
- `GOVOPLAN_DATABASE_BACKUP_METADATA`
|
||||
|
||||
Module uninstall does not retire data by default. Package removal leaves
|
||||
module-owned schema/data dormant. Explicit retirement is a later module-owned
|
||||
operation guarded by retirement providers.
|
||||
|
||||
The running FastAPI server still reports `package_mutation_supported=false`
|
||||
because dependency-manager operations are not executed inside request handlers.
|
||||
The trusted mutation boundary is the operator CLI/daemon. This keeps the
|
||||
interpreter, npm dependency graph, frontend bundle, migrations, and worker
|
||||
process set under process-supervisor control.
|
||||
|
||||
Frontend module loading primarily uses the build-time package graph generated by
|
||||
the core WebUI host. Installing or uninstalling a WebUI package therefore still
|
||||
uses `npm install` plus a WebUI rebuild/reload by default. Core also exposes an
|
||||
experimental hot remote-bundle path for modules that are enabled by the backend
|
||||
but absent from the local WebUI package graph:
|
||||
|
||||
- `FrontendModule.asset_manifest` points at a JSON remote asset manifest.
|
||||
- `asset_manifest_integrity` is an SRI-style hash for that manifest.
|
||||
- `asset_manifest_signature` and `asset_manifest_public_key_id` allow the shell
|
||||
to verify the manifest when a trusted browser key is registered on
|
||||
`window.__GOVOPLAN_REMOTE_MODULE_KEYS__`.
|
||||
- The remote asset manifest contract version is `1` and contains `moduleId`,
|
||||
`entry`, `entryIntegrity`, and optional `moduleExport`.
|
||||
- The browser fetches and verifies the manifest, fetches and verifies the entry
|
||||
bundle, imports it dynamically, and then applies backend metadata before
|
||||
adding the module's routes/nav/capabilities.
|
||||
|
||||
Unsigned/unhashed remote bundles are skipped. This keeps remote loading a
|
||||
controlled deployment option rather than a replacement for release package
|
||||
builds.
|
||||
|
||||
## 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
|
||||
|
||||
Backend verification from core:
|
||||
|
||||
```bash
|
||||
cd /mnt/DATA/git/govoplan-core
|
||||
./.venv/bin/python -m compileall src/govoplan_core ../govoplan-files/src/govoplan_files ../govoplan-mail/src/govoplan_mail ../govoplan-campaign/src/govoplan_campaign
|
||||
./.venv/bin/python -m compileall src/govoplan_core ../govoplan-access/src/govoplan_access ../govoplan-admin/src/govoplan_admin ../govoplan-tenancy/src/govoplan_tenancy ../govoplan-policy/src/govoplan_policy ../govoplan-audit/src/govoplan_audit ../govoplan-files/src/govoplan_files ../govoplan-mail/src/govoplan_mail ../govoplan-campaign/src/govoplan_campaign
|
||||
./.venv/bin/python scripts/check_dependency_boundaries.py
|
||||
```
|
||||
|
||||
Core WebUI host verification:
|
||||
|
||||
Reference in New Issue
Block a user