# Code Generation Summary — U1 Hosting & Serving **Date**: 2026-07-27 **Requirements**: FR-07, FR-10, FR-24 --- ## Files Created | Path | Purpose | |---|---| | `src/SlpModularCms.Core/Hosting/JwtTokenValidation.cs` | Single source of the JWT validation parameters | | `src/SlpModularCms.Core/Hosting/Health/HealthReport.cs` | Liveness response model | | `src/SlpModularCms.Core/Hosting/Health/HealthCheckExtensions.cs` | `AddCmsHealthChecks()` / `MapCmsHealthChecks()` | | `src/SlpModularCms.Core/Hosting/Security/IAdminTokenValidator.cs` | Contract for the validated admin bypass | | `src/SlpModularCms.Core/Hosting/Security/AdminTokenValidator.cs` | Implementation | | `src/SlpModularCms.Api/Extensions/StaticContentExtensions.cs` | Two-mount composition and SPA fallbacks | | `src/SlpModularCms.Api/Extensions/WebsitePlaceholder.html` | Embedded placeholder page | | `src/SlpModularCms.Core.Tests/Hosting/AdminTokenValidatorTests.cs` | 13 tests | | `src/SlpModularCms.Core.Tests/Hosting/HealthReportTests.cs` | 3 tests | ## Files Modified | Path | Change | |---|---| | `src/SlpModularCms.Core/Hosting/ServiceCollectionExtensions.cs` | Validation parameters built once via the factory, registered as a singleton and shared with the bearer scheme; `IAdminTokenValidator` registered | | `src/SlpModularCms.Api/Program.cs` | `AddCmsHealthChecks()`, `UseCmsStaticContent()`, `MapCmsHealthChecks()`, `MapCmsSpaFallbacks()` | | `src/SlpModularCms.Api/SlpModularCms.Api.csproj` | Placeholder embedded as a resource | | `src/SlpModularCms.Api.Slave/Program.cs` | Health checks; no static mounts | | `src/SlpModularCms.Modules.Availability/Middleware/AvailabilityMiddleware.cs` | `/health` bypass; `IsAdminBypass` delegates to the validator; unvalidated `ReadJwtToken` removed | | `src/SlpModularCms.Modules.Availability.Tests/AvailabilityMiddlewareTests.cs` | Constructor change; new bypass and forged-token cases | | `src/SlpModularCms.Modules.Availability.Tests/AvailabilityMiddlewareMasterGateTests.cs` | Constructor change; `/health` bypass case | No duplicate or parallel files were created — every existing file was modified in place. --- ## Implementation Decisions ### The forged-token fix is proven against the real validator, not only a substitute The middleware's own tests substitute `IAdminTokenValidator`, which is correct unit-testing practice — the middleware's job is to *ask*, not to validate. But a substitute keeps passing even if the middleware were later rewired back to unvalidated token parsing. A nested `WithRealValidator` class therefore wires the middleware to the actual `AdminTokenValidator` and asserts both halves of the fix: a **forged unsigned Owner token is rejected**, and a **genuine Owner token still bypasses**. The second matters as much as the first — an administrator must always be able to reach a disabled instance to switch it back on. ### `AddCmsHealthChecks()` deliberately takes no options Adding a database probe therefore requires editing this method, which is visible in review, rather than flipping a setting. Liveness-only is enforced by the shape of the API instead of by discipline. ### The placeholder is an embedded resource, and that was verified `wwwroot/web/` is owned and overwritten by a separate website workspace, so a placeholder file there would be deleted by the first real deployment or mistaken for part of the customer's site. Embedding keeps it outside that boundary. Because a wrong resource name would fail *silently* — falling back to a minimal inline HTML string — the compiled assembly's manifest was inspected to confirm the name resolves: `SlpModularCms.Api.Extensions.WebsitePlaceholder.html`. ### Static mounts are resolved at startup `RegisterMount` only registers a mount when its directory exists, so a directory created *after* the process started is not served until the next restart. This is correct for the intended deployment model — the atomic release switch links `wwwroot/web/` into place before the process starts — but it is behaviour worth knowing: dropping a website into a running instance requires a restart. --- ## Deviation From the Plan **Step 11 (`StaticContentTests`) was not implemented as written.** The plan placed it in `SlpModularCms.Core.Tests`, but `StaticContentExtensions` lives in the `SlpModularCms.Api` project, which `Core.Tests` does not reference and must not. `SlpModularCms.Api` has no test project, by the same deliberate convention that gives `SlpModularCms.Api.Slave` none — the Clients solution folder holds deployables, not tested libraries. Creating one would have been a structural change outside this unit's scope. What the step was meant to cover is mostly ASP.NET Core's own static-file behaviour rather than this project's logic. The genuinely project-specific behaviours — mount ordering, fallback precedence, the `nonfile` constraint, the placeholder path and the `/admin` redirect — require a composed host and are therefore **carried to the phase-level Build and Test stage**, where both hosts are started. Carried to Build and Test: - `/admin` redirects to `/admin/` - A missing asset under either mount returns `404`, never HTML - A client-side route under `/admin` serves the admin `index.html` - A client-side route at the root serves the website `index.html`, or the placeholder when absent - `/health` answers while the instance is availability-disabled --- ## Verification | Check | Result | |---|---| | `dotnet build SlpModularCms.sln -c Release` | ✅ 0 errors | | `SlpModularCms.Core.Tests` | ✅ 83 passed (was 54) | | `SlpModularCms.Modules.Availability.Tests` | ✅ 82 passed (was 78) | | `SlpModularCms.Modules.Identity.Tests` | ✅ 37 passed (unchanged) | | `SlpModularCms.Modules.Master.Tests` | ✅ 51 passed (was 50) | | Embedded resource name resolves | ✅ Verified against the compiled assembly manifest | No failures occurred during generation; nothing needed fixing and retrying.