# TECH - Kickoff Blueprint from Pick'n'Play This document extracts the implementation patterns, conventions, and guardrails from the current codebase so a new webapp can start with proven structure and avoid known pitfalls. Scope covered: - Backend (`Program.cs`, `Endpoints/*`, `Infrastructure/*`, `Data/*`, `Domain/*`, `Contracts/*`) - Frontend (`wwwroot/*`) - Tooling and CI (`scripts/*`, `.github/workflows/ci.yml`, npm tooling) - Tests (`GameList.Tests/*`) - Review carryover (`REVIEW.md` unresolved topics) ## 1) Stack and baseline choices - ASP.NET Core Minimal API on .NET 10. - EF Core + SQLite in current project (single-node deployment). - Cookie authentication (`HttpOnly`, `SameSite=Strict`, secure in production). - Static frontend (HTML/CSS/JS modules), no frontend framework. - OpenAPI generated from backend and consumed by generated JS client. - xUnit integration-heavy test suite with in-memory SQLite and coverage gates. ## 2) Architecture patterns to keep ### 2.1 API shape and layering - Route mapping in thin endpoint modules (`MapXEndpoints` per feature area). - Domain logic in workflow services (`*WorkflowService`) instead of endpoint lambdas. - Service responses normalized via `ServiceResult` + `ServiceError`, then mapped to HTTP at the edge. - Consistent `ProblemDetails` payloads with `error` extension for machine-usable errors. - Endpoint-level concerns handled by endpoint filters (`AdminOnlyFilter`, `PhaseRequirementFilter`, `PhaseOrJokerFilter`). Keep this split: - Endpoint adapters: auth, deserialization, HTTP mapping only. - Workflow services: validation, query/update rules, transactions. - Helpers: shared utility and security-sensitive routines. ### 2.2 Middleware pipeline discipline - Security and behavior depend on middleware ordering; keep explicit ordering. - Important current order: 1. Forwarded headers 2. Rate limiter 3. HSTS + HTTPS redirect (prod) 4. Security headers writer 5. Base path 6. Global exception handling 7. Authentication 8. Ensure player still exists 9. CSRF origin/referer checks 10. Authorization 11. State change notifier middleware 12. Static files 13. Endpoint mapping ### 2.3 State synchronization - Event-driven invalidation with SSE (`/api/events/state`) plus heartbeats. - Conditional reads for state (`ETag` + `If-None-Match`) to return `304`. - In-process notifier (`StateChangeNotifier`) with monotonic version and etag stamp. - Mutation middleware (`StateChangeNotificationMiddleware`) emits invalidation only for successful mutating API calls. This pattern is a strong baseline for low to medium scale and should be the default in the new app. ### 2.4 Security baseline - Cookie auth with short/medium session sliding expiration plus absolute lifetime cap. - Explicit same-origin CSRF checks for authenticated mutating API calls. - Rate limiting on auth-sensitive and admin-sensitive surfaces with custom `429` payload. - Security headers on all responses (`CSP`, `X-Content-Type-Options`, `X-Frame-Options`, `Referrer-Policy`, `Permissions-Policy`). - Forwarded headers restricted to configured trusted proxies/networks only. - Owner/admin protection rules enforced in business logic and DB constraints. - Destructive admin operations require password re-confirmation. - Password hashing is versioned and supports transparent upgrade on successful auth. - Current hash defaults to Argon2id, with legacy compatibility retained. ### 2.5 Data and invariants - Strong DB model with explicit constraints: - Unique normalized username - Unique owner partial index - Unique vote key `(PlayerId, SuggestionId)` - Seeded singleton app state row - DB-level guardrails (trigger) complement app-level checks: - Suggestion limit enforced in DB (`suggestion_limit_exceeded`) to survive concurrent writes. - EF patterns: - `AsNoTracking()` for read-only queries - `ExecuteUpdateAsync` / `ExecuteDeleteAsync` for efficient bulk operations - Explicit transactions for multi-step destructive/admin operations - Conflict handling around unique constraints ### 2.6 Workflow and permission model - Phases (`Suggest`, `Vote`, `Results`) drive endpoint access and UX behavior. - Effective phase can be derived from persisted phase + global `resultsOpen`. - Reconciliation helper functions centralize phase alignment rules. - Admin abilities are intentionally constrained per operation (e.g., only specific transitions allowed). Keep centralization of workflow rules. Avoid spreading phase/permission checks inline. ### 2.7 Frontend architecture - ES module split by concern: - API wrapper - Data loaders - UI composition - Feature-specific renderers/handlers - Shared utils and runtime dependency injection - Single runtime state object with deliberate clear/reset logic. - Refresh scheduler: - Serialized refreshes (no overlap) - Adaptive polling backoff - SSE-triggered immediate refresh for state mutations - Visibility-aware refresh suppression - API client is generated from OpenAPI operation ids, not handwritten endpoints. - Internationalization: - translation file validation at startup - language-specific FAQ markdown loading with fallback to default language ### 2.8 Frontend safety and rendering hygiene - Safe rendering helpers: - `escapeHtml` for template interpolation - `safeUrl` for links/media - Sensitive modal content set via `textContent`, not interpolated HTML. - Trusted output patterns covered by regression tests. Maintain this as a non-negotiable standard for any user-supplied content path. ### 2.9 Testing strategy patterns - Full-stack integration tests via `WebApplicationFactory`. - Real migrations applied to in-memory SQLite during test host startup. - HTTP side effects mocked deterministically (`StubHttpMessageHandler` and `IHttpClientFactory` replacement). - Coverage-focused tests for: - auth/security rules - middleware behavior - filter behavior - link/vote/result edge cases - OpenAPI operation id stability - CI-local parity script (`scripts/ci-local.ps1`) mirrors pipeline flow. ### 2.10 Tooling and contract discipline - OpenAPI generated during build (`openapi/GameList.json`). - JS client generated from OpenAPI with required operation-id checks. - Separate lint + format + tests + coverage threshold checks. - Build configured with warnings as errors in CI/local script. ## 3) Concrete feature set currently implemented Use this as a reusable "starter scope menu" for the new app: - Auth: - register/login/logout - owner bootstrap via admin key - immutable display name post-registration - auth options endpoint for registration UX - Identity/session: - cookie claim identity with admin claim - stale/deleted-account cookie invalidation - absolute session lifetime enforcement - State: - `/api/state`, `/api/me` - phase next/prev - SSE state invalidation - etag conditional state reads - Suggestions: - create/update/delete/mine/all - phase gating + admin override behavior - suggestion cap + joker path - link metadata exposure - screenshot URL and reachability validation - Votes: - vote upsert - finalize/unfinalize - linked suggestion vote fan-out - conflict handling on concurrent insert/update - Results: - gated by phase + admin-open flag - ordered leaderboard with aggregates and voter metadata - per-user own vote context - Admin: - results open/close toggle with phase realignment - vote status panel - joker grant - player phase correction - admin role grant/revoke (owner protected) - player delete with cascades and password confirmation - link/unlink suggestions with vote reset and unfinalize behavior - reset/factory reset with password confirmation ## 4) REVIEW.md unresolved topics -> new-project design defaults This section translates outstanding review risks into early decisions for the new app. ### 4.1 Data store scalability Review concern: - SQLite bottlenecks under higher write concurrency and multi-node scaling. New-project default: - Start with PostgreSQL (or SQL Server) for production profile. - Keep provider abstraction and provider-specific migration strategy from day one. - Keep SQLite only as local dev/test convenience if needed. ### 4.2 Workflow extensibility Review concern: - Workflow transitions are hard-coded in many places. New-project default: - Define transitions in a single state-machine table/model. - Drive backend authorization and frontend navigation from same transition metadata. - Add tests that validate the transition table itself. ### 4.3 Authorization model growth Review concern: - Role booleans (`IsAdmin`, `IsOwner`) limit future permission expansion. New-project default: - Use role/permission tables or claims-based capabilities. - Keep owner as a protected capability, not a special-case boolean spread across code. - Use policy-based authorization with explicit capability names. ### 4.4 Frontend maintainability Review concern: - String-template-heavy UI + global mutable state can become fragile. New-project default: - Move to TypeScript (or strict JSDoc typing) early. - Keep module boundaries by feature. - Keep explicit escaping/safe-url guards and DOM `textContent` standards. - If not using framework, introduce a small typed view-model layer. ### 4.5 In-memory cache bounds Review concern: - Unbounded dictionaries may grow under high-cardinality traffic. New-project default: - Replace unbounded maps with bounded `MemoryCache` (size + TTL + eviction). - For distributed deployments, use Redis with cardinality and TTL controls. ### 4.6 Linking/results query scaling Review concern: - Link and result workflows currently pull full sets into memory. New-project default: - Persist link-group ids and compute aggregates in SQL. - Add pagination/windowing for large result sets. - Benchmark query plans on realistic volumes. ### 4.7 External URL validation latency Review concern: - Reachability validation happens synchronously on write path. New-project default: - Accept user URL quickly, validate asynchronously (job queue/background worker), store validation status. - Optionally proxy or prefetch media through controlled media service. ## 5) Topics from REVIEW that are already fixed and should be carried forward - Polling amplification has been reduced through SSE + etag conditional reads. - CSRF protection is explicit (same-origin validation for authenticated mutating requests). - Password hashing is versioned and modernized (Argon2id current, transparent upgrades). - CSP is tightened compared to prior permissive baseline (no inline style allowance, no insecure image origins). - API drift risk reduced with OpenAPI generation + generated frontend client. These are not optional add-ons; they should be baseline in the new app. ## 6) New-project starter checklist - Bootstrap: - choose production DB provider first - define transition/state-machine model before endpoint coding - define permission/capability model before admin features - Security: - cookie or token strategy finalized with CSRF model - rate limiting partitions and thresholds defined - strict CSP and security headers in first commit - versioned password hashing with migration strategy - trusted proxy/host settings explicit - Contract: - OpenAPI generation enabled in build - generated client wired into frontend - operation-id stability tested - Data integrity: - enforce critical invariants both app-side and DB-side - transaction boundaries for multi-entity admin actions - Frontend: - module boundaries and state refresh model defined - escaping/url-safe helpers mandatory - i18n structure and fallback behavior in place - Testing: - integration test host with real migrations - deterministic stubs for network dependencies - coverage gate enforced in local + CI scripts ## 7) Keep/avoid quick reference Keep: - Thin endpoints + workflow services. - Shared service result abstraction. - Explicit middleware order. - SSE + ETag state sync. - Generated API client from OpenAPI. - DB-enforced invariants. - Regression tests for security-sensitive UI rendering. Avoid: - Hard-coded workflow transitions scattered in backend/frontend. - Boolean-only role model for long-term products. - Unbounded in-memory caches. - Synchronous external network checks on hot write paths. - Manual API contract duplication between docs/frontend/backend.