# NFR Requirements — Unit: Offerings ## NFR-OFF-01: Rate Limiting on the Public Read Endpoint (SECURITY-11) **Requirement**: `GET /api/v1/offerings` gets its own named rate-limiting policy (`offerings-public`), following the existing `login`/`refresh`/`sentry-tunnel` pattern in `AddCmsRateLimiting`. **Rationale**: NFR Requirements Q1 = A — the public, anonymous, high-traffic endpoint is the one worth defending against scraping/abuse; admin endpoints are already behind authentication (`AdminOnly`), judged lower priority for a dedicated limiter in this unit. **Scope for Code Generation**: A new `AddFixedWindowLimiter("offerings-public", ...)` entry, configuration-driven via a new `RateLimiting:OfferingsPublic` appsettings section (mirroring `RateLimiting:Login` etc.'s `PermitLimit`/`WindowSeconds` shape). Exact default values are a Code Generation Planning detail — generous enough not to affect legitimate site traffic, consistent with the existing `sentry-tunnel` policy's "generous but bounded" framing. ## NFR-OFF-02: No HTTP Caching Headers (Deliberate, Not an Oversight) **Requirement**: `GET /api/v1/offerings` does not set `Cache-Control`/ETag headers in this unit, despite the external hand-off doc inviting it. **Rationale**: NFR Requirements Q2 = B — the frontend already uses TanStack Query with default settings (refetch on mount/focus, no custom `staleTime`), so there's no functional caching gap to close; adding HTTP-level caching now would be optimizing a path with no observed or anticipated problem. ## NFR-OFF-03: Audit Trail Completion — "Who" Alongside "When" (SECURITY-13) **Requirement**: `Offering.LastModifiedByUserId` (added to the entity, see domain-entities.md) is set from the authenticated admin's user id on every create, update, and delete. **Rationale**: NFR Requirements Q3 = A — closes the remaining half of the SECURITY-13 open item flagged in requirements.md (`CreatedAt`/`UpdatedAt` from Functional Design already covered "when"). Still a minimal audit trail, not a full audit-log table with before/after value history — that remains a documented, accepted gap (consistent with the original SECURITY-13 assessment in requirements.md). **Explicitly rejected approach**: extending `SlpModularCms.Core.Observability.SecurityEvents` (the `RateLimitTriggered`-style structured Sentry-alerting mechanism) to also log content mutations. Investigated and rejected: that class is purpose-built for alertable anomalies at Warning level feeding Sentry alert rules (SECURITY-14) — a routine, expected "admin edited an offering" event is not an anomaly, and logging it through the same channel would pollute the exact alerting mechanism SECURITY-14 depends on. The audit fields on the entity itself are the right mechanism for this unit's scope. ## NFR-OFF-04: Test Coverage Standard (Consistency with Prior Modules) **Requirement**: `SlpModularCms.Modules.Offerings` targets the same ≥80% test coverage standard already established for new modules (`master-cms-module`'s NFR-MASTER-05). **Rationale**: NFR Requirements Q4 = A — consistency across modules rather than a new, unit-specific bar. ## Out of Scope for This Unit - Property-based testing: explicitly not enforced for this feature (D-12/Q12 = C from requirements.md) — standard example-based xUnit + FluentAssertions + NSubstitute tests, matching every existing module's test project. - New infrastructure/technology: none — reuses the existing MariaDB/EF Core/rate-limiting/logging stack.