Files
RpgRoller/TECH.md

191 lines
8.0 KiB
Markdown

# TECH - Kickoff Blueprint
## 0) Current scaffold status
- Root solution: `RpgRoller.sln`
- Backend/full-stack project: `RpgRoller` (Minimal API + static `wwwroot` frontend)
- Frontend source: `RpgRoller/frontend` (TypeScript)
- Frontend module split: `RpgRoller/frontend/app/*` (dom/state/loaders/render/events/actions)
- Test project: `RpgRoller.Tests` (xUnit + `WebApplicationFactory` integration tests)
- Persistence: EF Core + SQLite (`RpgRoller/Data/RpgRollerDbContext.cs`) with in-memory runtime cache in `GameService`
- OpenAPI source: `openapi/RpgRoller.json`
- Generated client source: `RpgRoller/frontend/generated/api-client.ts`
- Generated client output: `RpgRoller/wwwroot/generated/api-client.js`
- Local CI parity entrypoint: `scripts/ci-local.ps1`
- API endpoint modules: `RpgRoller/Api/*Endpoints.cs` + shared session/auth helpers
- Service boundary model: API request DTOs are mapped to `RpgRoller.Services/*Command` records before workflow execution
- Current backend features: auth/session, campaign/character/skill management, ruleset-aware rolls, filtered campaign logs, and SSE state updates.
- Current frontend features: authenticated campaign workspace with live log updates and full roll workflow controls.
## 1) Stack and baseline choices
- ASP.NET Core Minimal API on .NET 10.
- EF Core with SQLite file persistence in current project (single-node deployment).
- Game state is hydrated once on startup and then served from in-memory state; writes are persisted back to SQLite after successful mutations.
- Cookie authentication (`HttpOnly`, `SameSite=Strict`, secure in production).
- A minimal frontend framework supporting mixing 3D graphics with 2D elements, or a modern framework-less alternative (HTMl/CSS/TypeScript).
- OpenAPI generated from backend and consumed by generated client.
- xUnit integration-heavy test suite with isolated SQLite test databases 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<T>` + `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 forward compatibility retained via versioning.
### 2.5 Data and invariants
- Strong DB models backed by SQLite
- DB-level guardrails (trigger) to complement app-level checks
- 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 Frontend architecture
- modules 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.7 Testing strategy patterns
- Full-stack integration tests via `WebApplicationFactory`.
- Real migrations applied to in-memory Database 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.8 Tooling and contract discipline
- OpenAPI generated during build (`openapi/RpgRoller.json`).
- 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
Use this as a reusable "starter scope menu" for the new app:
- Auth:
- register/login/logout
- owner bootstrap via admin key
- 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`
- SSE state invalidation
- etag conditional state reads
## 4) New-project starter checklist
- 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.