Module Registry & SDK — How it works
Overview
The module engine is the platform's extension registry. It records the modules known to a deployment, their versions, dependencies, declared permissions and menu items, and which tenants/facilities have each module activated. It also exposes the module SDK: a self-describing manifest that a future domain plug-in ships, validated read-only before registration.
Data model & ownership
| Table | Purpose |
|---|---|
module | Registered module; stable unique module_key. |
module_version | Versions of a module. |
module_dependency | Declares one module depends on another. |
module_permission | Permissions a module contributes. |
module_menu_item | Navigation contributed by a module. |
tenant_module_activation / facility_module_activation | Which tenants/facilities have the module on. |
Key rules & invariants
-
activation_statusis theModuleActivationStatusenum, not a string (TERM-005). It was free text written straight from the request body and then compared two different ways on the same column:ACTIVE.equals(...)guarded whether dependency validation ran, whileACTIVE.equalsIgnoreCase(...)decided what was served. So POSTing"active"in the wrong case skipped dependency validation entirely and the module was still served as active — menus and permissions included. One lowercase letter activated a module in the UI while bypassing the gate that exists to prevent exactly that. The enum removes the string, so the two-comparison mismatch cannot recur.The values are
ACTIVEandINACTIVEbecause the frontend already declared that contract; this types what was in use rather than inventing a vocabulary.⚠ Not the same column:
RegisteredModule.statusis the separate registry lifecycle field (LifecycleStatus:ACTIVE/SUSPENDED, TERM-008D). Jackson rejects unrecognised values with 400; migrationmodule/006normalises then CHECK-constrains and HALTs on dirty data. The effective-manifest reader compares the enum directly — it no longer usesequalsIgnoreCaseon a free-text string. -
module_keyis unique; activation is gated (dependency validation viaModuleActivationDependencyValidator). -
Manifest validation is advisory and read-only (M9-006): it checks key/semver format, self-dependency, duplicate dependencies/permissions/menu paths, and extension type/target, and resolves declared dependencies against the registry — but never registers a module, so it does not bypass Module Engine ownership.
API
See the API Reference. Endpoint groups under /api/v1/module: registered modules,
versions, dependencies, permissions, menu items, tenant/facility activation, and
POST /module-manifests/validate.
Configuration & feature flags
None.
Related features
- Tenant / Facility (activation scope),
Access control (module-contributed permissions). Domain plug-ins call
this over Feign via
exchange.client.module(RegisteredModuleClient,ModuleSdkClient).