Security Boundaries
The API Is the Security Boundary
Section titled “The API Is the Security Boundary”All security enforcement in Spectral happens at the API layer. The backend is the single trust boundary —
every request to /api/* is authenticated and authorized by middleware before reaching any route handler.
This means:
- Authentication is enforced by
AuthMiddleware(JWT or API key validation) - Authorization is enforced by
require_permission()dependencies on each route - Tenant isolation is enforced by RLS context set per-request with the authenticated
workspace_id
No data leaves the database without passing through these controls.
Frontend Auth Guard Is a UX Gate
Section titled “Frontend Auth Guard Is a UX Gate”The dashboard’s AuthGuard component (apps/dashboard/src/components/auth/auth-guard.tsx) checks whether the
user has a valid session and redirects unauthenticated users to /login. This is a user experience gate,
not a security control.
Why This Is Acceptable
Section titled “Why This Is Acceptable”- No sensitive data in static HTML — page shells contain layout and UI components, not customer data
- All data fetches require authentication — every API call includes a bearer token validated server-side
- Guard prevents confusion, not attack — without it, unauthenticated users would see empty dashboards and broken UI rather than a clean login redirect
What This Means for Engineers
Section titled “What This Means for Engineers”- Do not rely on the frontend guard for access control. If a route should be restricted, enforce it
with
require_permission()on the API endpoint. - Do not embed sensitive data in page components. Data should always be fetched from authenticated API endpoints, never baked into the static build.
- Treat the frontend as untrusted. Any value coming from the client (headers, query params, request bodies) must be validated server-side.
Proxy-Aware IP Resolution
Section titled “Proxy-Aware IP Resolution”Behind reverse proxies (Cloudflare, Render), request.client.host returns the proxy’s IP, not the
real client. The TrustedProxyMiddleware resolves the real client IP from X-Forwarded-For and
X-Real-IP headers when the direct connection comes from a configured trusted proxy.
Configure trusted proxies via the TRUSTED_PROXIES environment variable (comma-separated IPs).
When empty (default), proxy headers are ignored and the direct connection IP is used.
The resolved IP is available at request.state.client_ip for audit logging and rate limiting.
Related
Section titled “Related”- Access Control — roles, scopes, and RLS isolation
- Authentication & Authorization — implementation details for middleware and dependencies