# Requirements — Local Dev Master/Slave Setup ## Intent Analysis - **User Request**: Enable running two local instances of the CMS backend simultaneously — one with `SlpModularCms.Modules.Master` loaded (the existing full CMS, acting as master) and one without it (acting purely as a slave/target) — on two different ports, so the master↔slave connection can be tested end-to-end locally. - **Request Type**: New Feature (developer tooling / local run configuration) - **Scope Estimate**: Multiple components — new host project (`SlpModularCms.Api.Slave`), solution/build changes, `appsettings`/`launchSettings` for both instances, frontend dev-server configuration for pointing at either instance. - **Complexity Estimate**: Moderate — no new business logic; the master/slave protocol itself (`Modules.Master`'s `CmsInstanceService`/`SlaveApiClient` and `Modules.Availability`'s `MasterController`/`MasterAvailabilityService`) already exists and is unchanged. Complexity comes from correctly separating build/run configuration for two instances without duplicating orchestration logic. ## Background (from codebase investigation) - `SlpModularCms.Api`'s `Program.cs` does not directly reference `Modules.Master`. Instead, `Infrastructure/ModuleOrchestrator.cs` discovers modules at startup by scanning its own build output directory for `SlpModularCms.Modules.*.dll` and instantiating any `IModule` implementation found — currently every module the API project references (`Core`, `Availability`, `Identity`, `Master`) always ends up in that output folder via `ProjectReference`s in `SlpModularCms.Api.csproj`. - The master/slave connection mechanism already exists and needs no changes: - **Slave side** (present in every instance, since `Modules.Availability` is always loaded): `MasterController` + `MasterAvailabilityService` + `MasterApiKeyProtector` accept and validate a per-instance API key pushed by a master. - **Master side** (only in `Modules.Master`): `CmsInstanceService` + `SlaveApiClient` + `ApiKeyProtector` manage registered CMS instances and talk to them. - The existing frontend "Add CMS Instance" dialog (from `master-cms-module`) is the existing UI flow for registering a slave under a master. - Frontend reads its backend URL from `VITE_API_BASE_URL` (`frontend/src/lib/config.ts`), sourced from a Vite env file (`.env.local`, `.env.example`), evaluated once per dev server process. Vite dev server currently runs on a fixed port (5173, `vite.config.ts`). - Backend CORS allowed origins are configured per-instance under `Cors:AllowedOrigins` in `appsettings.Development.json` / `appsettings.local.json`. ## Decisions from Clarification 1. **Instance separation mechanism**: A new host project, `SlpModularCms.Api.Slave`, with its own `.csproj` referencing `SlpModularCms.Core`, `SlpModularCms.Modules.Identity`, and `SlpModularCms.Modules.Availability` — but **not** `SlpModularCms.Modules.Master`. Because `ModuleOrchestrator` discovers modules purely by scanning its own build output, omitting the `Master` project reference is sufficient to produce a slave-only instance; no config-driven module toggle is needed for this feature. 2. **Production plugin-loading model is explicitly out of scope**: the user separately wants a future production model where the API is built once without module project references and modules are dropped in as standalone DLLs post-build. That is a distinct, larger architectural change (build/packaging/CI, not local dev tooling) and is **not** part of this feature. Capture it as a follow-up idea (see "Out of Scope" below). 3. **Database isolation**: Master and slave instances use separate local databases (separate connection strings/database names), same local SQL Server. 4. **Ports**: Master keeps existing ports (`http://localhost:5284`, `https://localhost:7221`). Slave gets a new pair (`http://localhost:5285`, `https://localhost:7222`). 5. **Launch method**: Two `dotnet run --launch-profile ` invocations (one per terminal/process) — no background helper script required. 6. **Frontend**: The frontend must also be runnable against the slave instance (its own dev server / env configuration), not just the master — so the slave's admin UI is separately reachable for manual testing. 7. **Establishing the connection**: No scripted/seeded auto-registration. The user will manually register the slave under the master using the existing "Add CMS Instance" UI flow, specifically to exercise/validate that flow end-to-end. ## Functional Requirements ### FR-1: Slave-only backend host project - Add `SlpModularCms.Api.Slave`, a new ASP.NET Core Web API project in `src/`, added to the solution. - Bootstraps the same way as `SlpModularCms.Api` (core infrastructure, CORS, rate limiting, controllers, module discovery/orchestration, exception handling, Scalar in Development) — but its `.csproj` references `Core`, `Modules.Identity`, and `Modules.Availability` only (no `Modules.Master` reference), so `ModuleOrchestrator` never discovers/loads the Master module for this instance. - Avoid duplicating the bootstrap/orchestration code (`ModuleOrchestrator`, `ServiceCollectionExtensions`, `ApiPrefixConvention`) between `SlpModularCms.Api` and `SlpModularCms.Api.Slave` — extract shared pieces into a location both projects can reference without pulling in `Modules.Master` (see NFR-1). ### FR-2: Distinct local run configuration per instance - `SlpModularCms.Api` (master) keeps its existing `launchSettings.json` profiles and ports (5284 / 7221). - `SlpModularCms.Api.Slave` gets its own `launchSettings.json` with profiles on ports 5285 (HTTP) and 7222 (HTTPS). - Each project has its own `appsettings.json` / `appsettings.Development.json` / `appsettings.local.json` (gitignored) with: - A distinct `ConnectionStrings:DefaultConnection` (separate database name/catalog) so master and slave don't share data. - `Cors:AllowedOrigins` covering whichever frontend origin(s) will point at that instance. - The slave instance does not need `MasterModule` configuration (that section is only meaningful where `Modules.Master` is loaded). - Both instances can be started independently via `dotnet run --launch-profile ` from their respective project directories. ### FR-3: Frontend configurable against either instance - Add a way to run the existing frontend against the slave instance in addition to the master, without needing two separate frontend codebases: - A second Vite env file (e.g. `.env.slave.local`, gitignored like `.env.local`) setting `VITE_API_BASE_URL` to the slave's HTTPS URL (`https://localhost:7222`). - A second npm script (e.g. `dev:slave`) that runs Vite with that env mode on a different dev-server port (e.g. 5174), so both frontend instances can run side by side if needed. - Update `.env.example` and/or `frontend/README.md` to document both modes. ### FR-4: Documentation for the local master/slave workflow - Add a short runbook (e.g. `frontend/README.md` and/or a root-level `docs`/`README` section, or a dedicated markdown file referenced from the main README) describing: 1. How to start the master backend + master frontend. 2. How to start the slave backend (+ optionally slave frontend) on the alternate ports. 3. How to use the master frontend's existing "Add CMS Instance" dialog to register the local slave (what URL/API key to use) and confirm the connection works (e.g. status shows as connected/healthy). ## Non-Functional Requirements ### NFR-1: No logic duplication between host projects - The master and slave host projects must share bootstrap/orchestration code (module discovery, DI wiring for core infrastructure, controller conventions) rather than maintaining two copies that can drift. Exact extraction mechanism (e.g. a small shared class library, or a shared partial/extension file linked into both projects) is a Functional/Application Design decision, not fixed here. ### NFR-2: Isolation - Master and slave instances must not share a database, so testing against one cannot corrupt or interfere with the other's data. ### NFR-3: Local-only scope - No changes to the actual master/slave communication protocol, production deployment, or CI/CD pipeline. This is strictly a local development convenience. ### NFR-4: Secrets hygiene - Slave's `appsettings.local.json` (and any new gitignored env files) must follow the existing pattern — never committed, following `dotnet-appsettings` conventions already used by `SlpModularCms.Api`. ## Out of Scope - **Production drop-in module DLL loading**: Building the API once without module project references and loading modules from an external "Modules" folder dropped in post-build/at-deploy. This is a distinct future initiative (build/packaging/CI concern) explicitly deferred by the user during clarification — to be captured as a new backlog/feature item when picked up, not designed as part of this feature. - **Automated/scripted slave registration**: The user explicitly wants to exercise the existing "Add CMS Instance" UI flow manually; no seed data or dev-only registration endpoint will be added. - Any change to `Modules.Master`, `Modules.Availability`, or the master/slave communication protocol itself. ## Summary This feature adds a second, Master-less ASP.NET Core host project (`SlpModularCms.Api.Slave`) plus per-instance run configuration (ports, databases, CORS) and frontend tooling, so the developer can run a master CMS instance (full stack, port 5284/7221) and a slave-only instance (no Master module, port 5285/7222) side by side locally, and manually connect them via the existing "Add CMS Instance" UI flow to validate the master/slave integration end-to-end. The production "plugin-style" module loading idea raised during clarification is explicitly out of scope and noted for future consideration.