Files
slp-modular-cms/aidlc-docs/features/slpsoftware-api/inception/requirements/requirements.md
T
SluijsensandClaude Sonnet 5 fa389e42ee
Continuous Integration / config (pull_request) Successful in 11s
Continuous Integration / changes (pull_request) Successful in 21s
Continuous Integration / backend-build (pull_request) Successful in 6m10s
Continuous Integration / vulnerability-scan (pull_request) Successful in 4m59s
Continuous Integration / frontend-prepare (pull_request) Successful in 1m27s
Continuous Integration / backend-test (pull_request) Failing after 7m48s
Continuous Integration / frontend-build (pull_request) Successful in 2m5s
Continuous Integration / frontend-test (pull_request) Successful in 4m24s
Continuous Integration / frontend-lint (pull_request) Successful in 2m0s
Continuous Integration / publish-test (pull_request) Skipped
Continuous Integration / publish-production (pull_request) Skipped
Continuous Integration / deploy-test (pull_request) Skipped
Continuous Integration / deploy-production (pull_request) Skipped
Adds SlpModularCms.Api.SlpSoftware and extracts shared CmsHost composition
Unit 1 of the slpsoftware-api feature (FR-1/FR-2/FR-3): a new Client project
in the Clients solution folder, intended to eventually become the deployed
API for test.slpsoftware.nl/slpsoftware.nl, hosting the same four modules as
SlpModularCms.Api plus a future Offerings module.

- Extracts SlpModularCms.Api/Program.cs's hosting-pipeline composition into
  SlpModularCms.Core.Hosting.CmsHost (ConfigureServices/ConfigurePipeline),
  shared by both Client projects so they cannot drift apart
- Moves StaticContentExtensions.cs + WebsitePlaceholder.html from Api into
  Core, since CmsHost cannot live in Api but Core cannot depend on Api
- Adds SlpModularCms.Api.SlpSoftware with its own isolated local dev database
  and dev ports (5286/7223, distinct from Api's and Api.Slave's)
- Adds SlpModularCms.Api.Tests with WebApplicationFactory-based pipeline
  regression tests (security headers, health check, SPA fallback, rate
  limiting), scoped to Api per NFR Design
- Adds a frontend dev:slpsoftware pnpm script mirroring dev:slave
- Fixes GlobalExceptionHandler logging routine 401s (e.g. an expired/missing
  refresh token) as unhandled errors -- pre-existing, unrelated to this
  feature's own scope, found while testing the new instance

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FWyStNL2ZsjrS7FLd7xvvN
2026-08-02 01:28:39 +02:00

18 KiB

Requirements — SlpSoftware Production API

Intent Analysis

  • User Request: Add a new deployable API in the Clients solution folder, SlpModularCms.Api.SlpSoftware, that will eventually replace SlpModularCms.Api as the API deployed to test.slpsoftware.nl / slpsoftware.nl. It hosts the existing Master module plus a new module covering what the SlpSoftware website needs, per the external hand-off doc packages-api-handoff.md.
  • Request Type: New Feature (new Client project + new module), with a CI/CD retarget as a downstream consequence.
  • Scope Estimate: Multiple Components — new Client project, shared hosting-pipeline extraction in SlpModularCms.Core, a new module + its test project, and (in the Operations phase) an extension of the existing deployment pipeline.
  • Complexity Estimate: Complex — touches solution structure, an in-flight Operations-phase feature's deploy pipeline (gitea-deployment-workflow), and a cross-workspace content contract owned by another repo's frontend.

System Context

  • The Clients solution folder exists but is currently empty; SlpModularCms.Api and SlpModularCms.Api.Slave sit under Application today (per CLAUDE.md / AGENTS.md). SlpModularCms.Api.SlpSoftware will be the first project ever placed in Clients.
  • SlpModularCms.Api hosts four modules today: Core, Identity, Availability, Master (see Program.cs / module orchestrator), plus a composed hosting pipeline built during gitea-deployment-workflow: static content + SPA fallback for /admin and wwwroot/web/, health checks, security headers/CSP, rate limiting, Sentry, Data Protection, and startup migrations.
  • The external hand-off doc (K:\Development\SlpSoftware\Projects\SlpSoftware\...\packages-api-handoff.md, read-only reference) asks for a single public, unauthenticated GET /api/v1/packages backing the "Drie manieren om te starten" section of the website. The frontend already calls fetch('/api/v1/packages') with a relative, same-origin path.
  • That same reference workspace's WEBSITE_WORKSPACE.md, together with the (merged) gitea-deployment-workflow feature, establishes that in this repo the public site (wwwroot/web/) and the API are already served same-origin by one Client API process — so the hand-off doc's "add an nginx proxy_pass" open item does not apply here once Api.SlpSoftware is the deployed host (Q6 = A).
  • Roles available today (HierarchicalRoleHandler, ServiceCollectionExtensions): Owner (100) > Administrator (50) > User (10), with existing OwnerOnly / AdminOnly / UserOnly authorization policies.

Decisions (traced to clarification questions)

# Decision Source
D-1 SlpModularCms.Api.SlpSoftware is a new project in Clients; SlpModularCms.Api is unchanged and stays in Application as the dev host. Q1 = A
D-2 Api.SlpSoftware hosts the same four modules as Api (Core, Identity, Availability, Master) plus the new module. Q2 = A
D-3 The shared hosting/pipeline composition in Program.cs is extracted into a reusable method in SlpModularCms.Core (e.g. CmsHost.Configure(...)) so both Client Program.cs files stay thin and cannot drift apart. Q3 = A
D-4 This feature includes admin CRUD (create/edit/delete/reorder) for the new content, not just the public read endpoint from the hand-off doc — that's the reason this becomes a CMS-managed module rather than staying hardcoded. Q4 = B
D-5 No automatic seed migration for the three current packages. The user will enter content manually, but the current live values must be preserved as documented reference data so they aren't lost. Retrieved directly from the reference workspace's content.ts — see Reference Content below. Q5 = X (custom)
D-6 No nginx changes are needed for /api/v1/ once Api.SlpSoftware is the deployed, same-origin host. Q6 = A
D-7 Retargeting the CI/CD pipeline (deploy-scp.yaml, continuous_integration.yaml, deployment-instructions.md — currently owned by gitea-deployment-workflow) to Api.SlpSoftware is in scope for this feature's Operations phase, extending the existing pipeline rather than duplicating it. Q7 = A
D-8 SlpModularCms.Api remains permanently as the local dev host, same role as Api.Slave today — no removal planned. Q8 = A
D-9 The new module is named SlpModularCms.Modules.Offerings — deliberately domain-generic (not Packages, which reads as a software/library package, and not SlpSoftware-specific), since it must be reusable later for unrelated tenants/domains (the user's stated example: a photography business selling photoshoot packages). Q9 = A (custom, follow-up round)
D-10 The module gets its own DbContext (e.g. OfferingsDbContext), consistent with the existing Availability/Master module-isolation pattern. Q10 = A
D-11 The Security Baseline extension is enabled and blocking for this feature. Q11 = A
D-12 The Property-Based Testing extension is not enforced for this feature. Q12 = C
D-13 This feature runs the Operations phase after Construction. Q13 = A
D-14 The public route is /api/v1/offerings, matching the module name, not /api/v1/packages. The corresponding frontend fetch-path change (usePackagesQuery.ts) in the external reference workspace is out of scope for this feature — the user will update it separately, on their own. User feedback, post-Requirements-approval; confirmed via chat clarification (frontend update: "dat regel ik zelf")
D-15 The CI/CD pipeline retarget (FR-9) is a cutover: SlpModularCms.Api is replaced by SlpModularCms.Api.SlpSoftware as the pipeline's build/deploy target, not run side by side. User feedback, post-Requirements-approval

Functional Requirements

FR-1 — New Client Project: SlpModularCms.Api.SlpSoftware

A new project SlpModularCms.Api.SlpSoftware is created under the Clients solution folder (first project ever placed there), per D-1. SlpModularCms.Api is not moved, renamed, or otherwise modified in role.

FR-2 — Module Composition

SlpModularCms.Api.SlpSoftware references and hosts: SlpModularCms.Core, SlpModularCms.Modules.Identity, SlpModularCms.Modules.Availability, SlpModularCms.Modules.Master, and the new SlpModularCms.Modules.Offerings (D-2, D-9).

FR-3 — Shared Hosting Pipeline Extraction

The composed hosting pipeline currently duplicated in SlpModularCms.Api/Program.cs (static content + SPA fallback for /admin and wwwroot/web/, health checks, security headers/CSP, rate limiting, Sentry, Data Protection, startup migrations, module orchestrator) is extracted into a reusable method in SlpModularCms.Core (e.g. CmsHost.Configure(...)). Both SlpModularCms.Api/Program.cs and SlpModularCms.Api.SlpSoftware/Program.cs call this shared method; project-specific differences (e.g. which modules are registered) remain explicit at each call site (D-3).

FR-4 — New Module: SlpModularCms.Modules.Offerings

A new module SlpModularCms.Modules.Offerings is created following the existing IModule pattern, with its test project SlpModularCms.Modules.Offerings.Tests under Tests/Modules (per CLAUDE.md structure rules). The module owns an Offering entity and its own OfferingsDbContext + migrations, isolated from other modules' schemas (D-9, D-10).

FR-5 — Offering Entity

The Offering entity carries the fields required by the public API contract (see FR-6) and by admin management (FR-7): a stable slug-like Id, Title, Description, Price (pre-formatted display string, not a number — e.g. "€ 300" or "Op maat"), PriceNote, an ordered list of Features (plain text), CtaLabel, a Featured flag, and an explicit DisplayOrder (or equivalent ordering field) so admin reordering (FR-7) has something durable to persist, independent of the public array's rendered order.

FR-6 — Public Endpoint: GET /api/v1/offerings

A public, unauthenticated GET /api/v1/offerings endpoint returns a JSON array of offerings in display order, matching the field contract from the hand-off doc:

[
  {
    "id": "pakket_01",
    "title": "Landingspagina",
    "description": "Eén overtuigende pagina die je product of dienst helder neerzet.",
    "price": "€ 300",
    "priceNote": "eenmalig, excl. btw",
    "features": ["Eén pagina in HTML & CSS", "..."],
    "ctaLabel": "Kies landingspagina",
    "featured": false
  }
]

Note on the frontend dependency: the frontend in the external, read-only reference workspace currently fetches the old path (fetch('/api/v1/packages')). Changing the route to /api/v1/offerings (D-14) means that fetch call needs to change too — the user has confirmed this is out of scope for this feature; they will update it separately in that workspace. Until that frontend change happens, the website's package section will show its error state against a deployed Api.SlpSoftware, same as today against no backend at all.

FR-7 — Admin CRUD for Offerings

The admin SPA (served under /admin by the same Client API, per the existing hosting pipeline) gets management screens to create, edit, delete, and reorder offerings, backed by authenticated API endpoints (POST / PUT / DELETE / a reorder operation on /api/admin/offerings or equivalent). These endpoints require the existing AdminOnly authorization policy (Administrator role or higher), consistent with other content-management operations in this system (D-4).

FR-8 — Reference Content (Not Auto-Seeded)

No database seed migration is created (D-5). Instead, the three packages currently live on the website are captured here as the reference values the user will enter manually through the new admin CRUD (FR-7), retrieved from the external reference workspace's content.ts:

id title description price priceNote features ctaLabel featured
pakket_01 Landingspagina Eén overtuigende pagina die je product of dienst helder neerzet. € 300 eenmalig, excl. btw Eén pagina in HTML & CSS; Ontwerp op maat, geen template; Responsive op elk scherm; Snelle laadtijd & SEO-basis Kies landingspagina false
pakket_02 Website Een complete website met meerdere pagina's, klaar om te groeien. € 750 eenmalig, excl. btw Drie pagina's in HTML & CSS; Extra pagina's als optie bij te bestellen; Consistente huisstijl over alle pagina's; Responsive, snel & SEO-basis Kies website true
pakket_03 Maatwerk Grotere websites, een eigen back-end of andere .NET-projecten. Op maat offerte na intake Grotere websites & webapplicaties; Losse back-end in .NET / C#; Koppelingen & API's; Advies over de beste aanpak Vraag offerte aan false

Per the hand-off doc's business rule BR-4 (content fidelity), these values must be reproduced exactly, not paraphrased, if/when entered.

FR-9 — CI/CD Retarget (Operations Phase)

The existing Gitea Actions pipeline (continuous_integration.yaml, deploy-scp.yaml) and deployment-instructions.md — currently targeting SlpModularCms.Api, owned by the gitea-deployment-workflow feature — is extended (not duplicated) to build, test, and deploy SlpModularCms.Api.SlpSoftware as the artifact shipped to test.slpsoftware.nl / slpsoftware.nl. This is executed in this feature's Operations phase, coordinated with gitea-deployment-workflow rather than forking its pipeline (D-7). Per D-15, this is a cutover: the pipeline's build/deploy target switches from SlpModularCms.Api to SlpModularCms.Api.SlpSoftware — it does not build and deploy both APIs side by side.


Non-Functional Requirements

NFR-1 — No Nginx Changes Required

No nginx location /api/v1/ proxy is added. Api.SlpSoftware serves the site, /admin, and /api/v1 same-origin from one process, matching the existing Api hosting model (D-6).

NFR-2 — Module Test Coverage

SlpModularCms.Modules.Offerings follows this project's existing test coverage standard for new modules, with unit tests for the Offering entity, OfferingsDbContext, the public read endpoint, and the admin CRUD endpoints (including authorization checks).

NFR-3 — Long-Term Reusability of the Offerings Module

The module's naming, entity design, and any tenant/site-scoping must not bake in SlpSoftware-specific assumptions (e.g. hardcoded copy, hardcoded routes beyond the one fixed contract in FR-6), since the user's stated intent is to reuse this module for unrelated future sites/domains (D-9).

NFR-4 — Property-Based Testing Not Required

No PBT tooling or rules are introduced for this feature's tests (D-12).


Security Compliance (Security Baseline extension — enabled, blocking)

Rule Status Notes
SECURITY-01 Encryption at rest/transit Pre-existing, unchanged OfferingsDbContext uses the same MariaDB connection (TLS-enforced) as Availability/Master; no new data store technology introduced.
SECURITY-02 Access logging on intermediaries N/A No load balancer, API gateway, or CDN in this architecture — the application itself is the only network-facing component.
SECURITY-03 Application-level logging Pre-existing, unchanged Reuses the shared logging/Sentry setup extracted in FR-3.
SECURITY-04 HTTP security headers Pre-existing, unchanged Reuses the shared security-headers middleware extracted in FR-3; applies identically on Api.SlpSoftware.
SECURITY-05 Input validation Addressed Admin CRUD endpoints (FR-7) validate all Offering fields (type, length bounds on Title/Description/Price/PriceNote/CtaLabel, array bounds on Features) and use EF Core parameterized queries.
SECURITY-06 Least-privilege access policies Addressed Admin endpoints use the existing AdminOnly policy, not OwnerOnly or an ad-hoc broader policy (FR-7).
SECURITY-07 Restrictive network configuration N/A No cloud networking/firewall resources introduced by this feature.
SECURITY-08 Application-level access control Addressed The public GET /api/v1/offerings (FR-6) is deliberately and explicitly anonymous, matching the hand-off doc's requirement; all mutation endpoints (FR-7) require authentication and the AdminOnly policy, following deny-by-default.
SECURITY-09 Hardening and misconfiguration Pre-existing, unchanged Reuses the shared pipeline's existing error handling (GlobalExceptionHandler) and Development-only OpenAPI/Scalar exposure.
SECURITY-10 Supply chain Pre-existing, unchanged New module adds no new third-party dependencies beyond what the solution already uses (EF Core, ASP.NET Core); covered by the existing blocking vulnerability gate from gitea-deployment-workflow.
SECURITY-11 Secure design Addressed Admin (security-critical) operations are isolated behind AdminOnly, distinct from the public read path; rate limiting is inherited from the shared pipeline (FR-3) and therefore also applies to the new public endpoint.
SECURITY-12 Authentication and credentials Pre-existing, unchanged Reuses the existing Identity module; no new credential handling introduced.
SECURITY-13 Software and data integrity Addressed Admin mutations to Offering records should be auditable at the same level as other content mutations in this system (who/when) — tracked as an open item (see below) if no existing audit mechanism covers module-level content changes.
SECURITY-14 Alerting and monitoring Pre-existing, unchanged Reuses the shared Sentry-based alerting from gitea-deployment-workflow; no new alert categories introduced by this feature.
SECURITY-15 Exception handling and fail-safe defaults Pre-existing, unchanged Reuses the shared GlobalExceptionHandler; new endpoints follow the same fail-closed pattern (auth failure → 401/403, not silent fallback).

Open item: SECURITY-13 audit-trail coverage for Offering create/edit/delete/reorder is not yet confirmed against an existing system-wide audit mechanism (or its absence) — to be resolved at Functional Design / NFR Design for the Offerings unit, not blocking Requirements sign-off.


Scope Boundaries

In Scope Out of Scope
New SlpModularCms.Api.SlpSoftware project in Clients Moving/renaming SlpModularCms.Api
Shared CmsHost.Configure(...)-style extraction in SlpModularCms.Core Removing or deprecating SlpModularCms.Api
New SlpModularCms.Modules.Offerings module + SlpModularCms.Modules.Offerings.Tests Multi-tenant/site-scoping infrastructure for reuse by a future, unrelated site (only the naming/design must not preclude it — D-9, NFR-3)
Offering entity + OfferingsDbContext + migrations Automatic data seeding of the three current packages (D-5)
Public GET /api/v1/offerings (unauthenticated) nginx configuration changes (D-6)
Admin CRUD (create/edit/delete/reorder) for offerings, AdminOnly-protected Frontend changes in the external SlpSoftware reference workspace, including updating usePackagesQuery.ts's fetch path to /api/v1/offerings (D-14 — user's own follow-up)
CI/CD retarget of the existing pipeline to Api.SlpSoftware as a cutover (Operations phase, D-15) Running Api and Api.SlpSoftware pipelines side by side
Documenting the current live package copy as reference content (FR-8) Entering that content into the running system (the user will do this manually)

Assumptions

# Assumption Risk if wrong
A-1 The public route stays /api/v1/packages even though the module is Offerings. Resolved 2026-08-01: route is /api/v1/offerings (D-14); frontend update is the user's own, separate follow-up. Closed
A-2 Admin CRUD for offerings requires AdminOnly (Administrator role), not OwnerOnly. Low-medium — if the user wants offering management restricted to Owner only, this is a one-line policy change, best confirmed at Application Design.

9 functional requirements, 4 non-functional requirements, 15 traced decisions, 1 open assumption (1 resolved), 1 open item (security audit-trail coverage).