12 KiB
Requirements — Master CMS Module
Intent Analysis
- User Request: Add a new "Master" module that fills the
/cmspage, allows the owner to manage the availability of registered slave CMS instances, and integrates with the existing availability check mechanism — while exempting the Master CMS itself from external checks. - Request Type: New Feature
- Scope Estimate: Multiple Components — new backend module, new frontend UI, modifications to the existing availability flow on slave CMS instances
- Complexity Estimate: Complex — involves a Master/Slave network topology, bi-directional registration, background integrity checks, caching, fallback logic, and API key authentication
System Context
The existing system (SlpModularCms) is a modular ASP.NET Core CMS. The Availability module manages system status via IAvailabilityService and AvailabilityMiddleware. Slave CMSes are other deployed instances of the same SlpModularCms application. The Master Module adds a control plane layer on top of the existing availability system.
Functional Requirements
FR-MASTER-01 — New Module: SlpModularCms.Modules.Master
A new separate module SlpModularCms.Modules.Master is created within the existing solution. This module is installed only on the Master CMS. Slave CMS instances do not install this module. The module registers its own services and HTTP endpoints following the existing IModule pattern.
FR-MASTER-02 — CmsInstance Entity
A new CmsInstance database entity is added to SlpModularCms.Core with the following fields:
Id(Guid, PK)Name(string, required) — friendly name for the slave CMSUrl(string, required) — base URL of the slave CMS APIApiKey(string, required) — secret key used by the Master to authenticate against the slaveStatus(enum:Available/NotAvailable/Inactive) — the master-controlled availability stateDisableMessage(string, nullable) — required whenStatus = NotAvailable; shown to end-users of the slaveLastContactedAt(DateTimeOffset, nullable) — timestamp of the last successful master → slave contactLastStatusPushedAt(DateTimeOffset, nullable) — timestamp of the last status push to the slave
FR-MASTER-03 — Auto-Registration: Master Registers Itself with Slave
When a slave is added to the Master's CmsInstance registry, the Master automatically contacts the slave and registers itself (pushes its own URL). The slave stores the master's URL in its local database. No manual configuration is required on the slave side to know the Master URL.
The slave exposes a dedicated internal registration endpoint (POST /api/internal/master/register) that accepts the Master's URL and API key. After successful registration, the slave uses the stored Master URL for all future availability pulls.
FR-MASTER-04 — Integrity Check Service (Background Service)
A background service (periodic, configurable interval) runs on the Master CMS and periodically re-verifies that each registered slave still has the correct Master URL stored. If a slave's registered master does not match, the Master re-pushes its registration to that slave. This prevents clients from circumventing the master by removing or altering the stored master URL.
FR-MASTER-05 — Status Push: Master Sets Slave Availability
When the Master owner changes a slave's status (to Available or NotAvailable), the Master immediately calls the slave's existing PUT /api/availability/status endpoint (or a dedicated internal endpoint) to push the new status. Authentication uses the ApiKey from the CmsInstance record, sent as an X-Master-Api-Key header.
FR-MASTER-06 — Slave Pull Model: Periodic Master Check
The slave CMS periodically pulls its availability status from the Master. The pull interval is configurable in the slave's appsettings.json (MasterModule:CacheMinutes). If this value is not configured, a default of 60 minutes is used. The pulled status is cached locally in memory.
FR-MASTER-07 — Slave Fallback Behavior
If the Master CMS is unreachable when the slave attempts a pull:
- The slave falls back to the value currently stored in its local database (
GlobalAvailabilityState). - The local database value defaults to
Availablebefore any Master contact has occurred. - This ensures the feature is a fail-open safety measure — clients are not blocked if the Master is down.
FR-MASTER-08 — Slave Availability Middleware Integration
On a slave CMS, the availability check becomes a two-phase gate. The existing IAvailabilityService behavior is preserved — the Master adds an additional outer gate, not a replacement.
Check order:
- Master gate (outer): If a Master URL is registered in the slave's DB, check the Master-sourced cached status (refreshed per FR-MASTER-06).
- If Master status =
NotAvailable→ block all requests. Only the frontend dashboard route is accessible so that users can see the availability widget with theDisableMessage. - If Master is unreachable → fall back to locally stored Master status (last known value; default
Available). - If no Master registered → skip Master gate entirely.
- If Master status =
- Local gate (inner): If the Master gate passes (status =
Availableor no Master registered), apply the existingIAvailabilityServicecheck as it works today (localAvailable/Maintenance/NotAvailablelogic, Owner/Admin bypass, etc.).
This means: when the Master sets a slave to Available, all existing local availability behavior continues unchanged. When the Master sets NotAvailable, the local gate is never reached.
The DisableMessage from the Master is included in the 503 Service Unavailable response body and delivered to the slave's frontend availability widget.
FR-MASTER-09 — Master CMS Exemption
When the Master Module is installed and active on a CMS instance, that CMS is automatically exempt from the external Master availability check. It does not pull status from any Master. The Master CMS retains and uses its own local IAvailabilityService check (existing behavior unchanged).
FR-MASTER-10 — Role Access Control
All Master Module management features (viewing and modifying slave CMS registrations) are restricted to the Owner role only. This applies to both the backend API endpoints and the frontend /cms page.
FR-MASTER-11 — /cms Page: Slave CMS List
The frontend /cms page displays a list of all registered slave CMSes. For each slave, the following is shown:
- Name
- URL
- Current status badge (
Available/NotAvailable/Inactive) - Last contacted timestamp
- Disable message (if status is
NotAvailable)
Inactive entries are visually greyed out to indicate they are no longer in active use.
FR-MASTER-12 — /cms Page: Add Slave CMS
The Owner can add a new slave CMS registration by providing:
- Name (required)
- URL (required)
- API key (required)
On save, the Master immediately attempts to register itself with the slave (FR-MASTER-03) and stores the result.
FR-MASTER-13 — /cms Page: Set Slave Status
The Owner can change the status of a registered slave CMS to:
Available— slave is enabled (normal operation)NotAvailable— slave is disabled; a mandatory disable message must be providedInactive— slave is greyed out in the UI; no availability enforcement is applied (the Master does not contact the slave)
Slave registrations cannot be deleted; setting to Inactive is the "soft removal" mechanism.
FR-MASTER-14 — Mandatory Disable Message
When the Owner sets a slave's status to NotAvailable, a non-empty DisableMessage is required. This message is pushed to the slave together with the status change and included in the slave's 503 error response to end-users.
FR-MASTER-15 — Project Documentation
The root README.md already exists and covers project structure, dev setup, authentication, frontend development, database migrations, adding a module, and production setup. The following sections need to be updated or added to reflect this feature.
Updated: README.md — "Database Migraties" section
The current section only covers migrations in SlpModularCms.Core. It must be updated to document the per-module migration pattern (NFR-MASTER-06):
- How to add a migration for a specific module (using
--project src\SlpModularCms.Modules.<Name>) - How to apply module-specific migrations
- Note that each module owns its own tables and migrations
Updated: README.md — "Nieuwe Module Toevoegen" section
Step 3 (IModule implementeren) must be extended to document the optional per-module DbContext pattern introduced by the Master Module:
- How to add a module-specific
DbContext - How to register it at startup via
RegisterServices - How to apply its migrations in
UseModule
Updated: README.md — "Productie Setup" section
The environment variables list must be extended with the new MasterModule: keys:
MasterModule__CacheMinutes(slave instances only)MasterModule__IntegrityCheckIntervalMinutes(master instance only)
Updated: frontend/README.md
Currently contains Vite template boilerplate. Replace with project-specific frontend developer documentation (prerequisites, scripts, environment variables). The root README already covers most of this — the frontend README can be a brief pointer to the root README plus frontend-specific notes.
Non-Functional Requirements
NFR-MASTER-01 — Fail-Open Safety
The entire Master Module is designed as a safety measure, not a blocker. If any part of the Master → Slave communication fails (network error, timeout, misconfiguration), the slave must continue serving requests using its local fallback. End-users must never be blocked solely because the Master is unreachable.
NFR-MASTER-02 — Configurable Cache Interval
The slave pull interval is configurable via appsettings.json (MasterModule:CacheMinutes). Default: 60 minutes. This follows the existing dotnet-appsettings pattern used in this project.
NFR-MASTER-03 — API Key Security
The ApiKey stored in CmsInstance is a secret token used for Master → Slave authentication. It must not be exposed in API list responses. The slave validates the X-Master-Api-Key header on all Master-initiated requests.
NFR-MASTER-04 — Background Service Interval
The Master's integrity check background service interval is configurable in Master CMS appsettings.json (MasterModule:IntegrityCheckIntervalMinutes). Default: 60 minutes.
NFR-MASTER-05 — Test Coverage
New backend code must follow the existing test coverage standard (≥ 80%). New services and controllers in SlpModularCms.Modules.Master and any slave-side extensions require unit tests.
NFR-MASTER-06 — Per-Module Database Migrations
Each module manages its own database schema through a module-specific EF Core DbContext and a dedicated migrations assembly within that module's project. This ensures that tables belonging to a module are only created on CMS instances where that module is installed:
SlpModularCms.Modules.Masterowns theCmsInstancestable → migrations live inModules.Master- Slave-side tables (e.g., stored Master registration URL) belong to the module or service that introduces them → migrations live in the corresponding project
SlpModularCms.Coreretains only the shared/core entities (users, roles, tokens,GlobalAvailabilityState)- Each module's
Configure(IApplicationBuilder)method applies its own pending EF Core migrations at startup - A migration is created for every discrete schema change (one migration per logical change, not batched)
Scope Boundaries
| In Scope | Out of Scope |
|---|---|
New SlpModularCms.Modules.Master project |
Slave module as a separate installable package |
Frontend /cms page with slave management UI |
Multi-level master hierarchy (master of masters) |
| Slave-side auto-registration endpoint | Real-time push notifications to slave (WebSockets/SignalR) |
| Slave-side availability pull + cache + fallback | Authentication delegation (SSO between master and slave) |
| Master integrity check background service | Slave removal / permanent delete |
CmsInstance entity in SlpModularCms.Modules.Master (own DbContext + migrations) |
|
| Frontend changes included in this feature | |
Updated README.md (migrations + module guide + prod env vars) |
Full README rewrite |
Updated frontend/README.md (project-specific content) |