docs: add design for security headers, coverage gates, and web vitals

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-18 02:06:10 +00:00
parent 9dcb6f6de3
commit fc7ba0846f

View File

@@ -0,0 +1,116 @@
# Security Headers, Coverage Gates & Web Vitals Design
> **Date:** 2026-03-18
> **Product:** ResolutionFlow
> **Branch:** `feat/security-headers-coverage-performance`
> **Purpose:** Add HTTP security headers, enforce test coverage gates, and instrument Core Web Vitals reporting
---
## Decisions
| Decision | Choice | Rationale |
|----------|--------|-----------|
| Security headers priority | First | Highest trust signal for MSP buyers, real protection |
| CSP rollout strategy | Report-only first, then enforce | Avoids breaking third-party integrations (PostHog, Sentry, Google Fonts, React Flow) |
| Backend coverage gate | 80% fail threshold | Already near this level, prevents drift |
| Frontend coverage gate | Report-only (no gate yet) | Starting from zero — establish baseline first |
| Web Vitals destination | PostHog | 100% of sessions captured (vs Sentry's 20% sample), correlate with product analytics |
---
## 1. Security Headers Middleware
### New file: `backend/app/core/security_headers.py`
Starlette middleware that adds security headers to every response.
### Headers
| Header | Value | Purpose |
|--------|-------|---------|
| `Strict-Transport-Security` | `max-age=31536000; includeSubDomains` | Force HTTPS for 1 year |
| `X-Content-Type-Options` | `nosniff` | Prevent MIME sniffing |
| `X-Frame-Options` | `DENY` | Block iframe embedding |
| `Referrer-Policy` | `strict-origin-when-cross-origin` | Limit referrer leakage |
| `Permissions-Policy` | `camera=(), microphone=(), geolocation=()` | Disable unused browser APIs |
| `Content-Security-Policy-Report-Only` | *(see below)* | CSP in report-only mode |
### CSP Directive (report-only)
```
default-src 'self';
script-src 'self' https://us.i.posthog.com https://*.sentry.io;
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
font-src 'self' https://fonts.gstatic.com;
img-src 'self' data: blob:;
connect-src 'self' https://us.posthog.com https://us.i.posthog.com https://*.sentry.io https://api.resolutionflow.com;
frame-ancestors 'none';
base-uri 'self';
form-action 'self'
```
### Wiring
- Add middleware in `main.py` after CORS middleware (so CORS preflight responses aren't affected)
- CSP directives configurable via `config.py` so promotion to enforcing mode is a config change, not a code change
- HSTS only sent when `DEBUG=false` (avoid locking localhost into HTTPS)
### Tests
Integration test that hits an endpoint and asserts all expected headers are present with correct values.
---
## 2. Coverage Gates
### Backend: Enforce at 80%
- Add `--cov-fail-under=80` to the pytest command in CI
- One-line change — reporting already wired up with `pytest-cov`
### Frontend: Report-only (establish baseline)
- Install `@vitest/coverage-v8` as dev dependency
- Add coverage config to `vite.config.ts`:
- Reporters: `text` + `json-summary` + `html`
- Include: `src/**/*.{ts,tsx}`
- Exclude: `src/test/`, `src/types/`, `**/*.d.ts`
- Add `test:coverage` script to `package.json`
- Update CI to run `npm run test:coverage` instead of `npm test`
- Display summary in CI output — no failure threshold yet
- Add `coverage/` to `.gitignore`
---
## 3. Web Vitals → PostHog
### Install
`web-vitals` npm package.
### New file: `frontend/src/lib/webVitals.ts`
- Import `onLCP`, `onINP`, `onCLS`, `onFCP`, `onTTFB` from `web-vitals`
- Each callback sends a PostHog event (`web_vitals`) with properties:
- `metric_name` — LCP, INP, CLS, FCP, TTFB
- `metric_value` — numeric value
- `metric_rating` — good / needs-improvement / poor
- `page_path` — current route
- Single `initWebVitals()` function that registers all observers
### Wiring
Call `initWebVitals()` in `main.tsx` after PostHog initialization.
---
## Scope Summary
| Area | Scope | Files |
|------|-------|-------|
| Security headers | New middleware + config + test | 3-4 backend files |
| Coverage gates | CI config + vitest coverage setup | CI workflow + 3 frontend config files |
| Web Vitals | New lib + dependency + main.tsx wiring | 2-3 frontend files |
Small, contained changes across all three. No architectural changes or new database models.