Architecture
Status: Current-state architecture with proposed hardening items
Last updated: 2026-10-02
Context
The system has a browser frontend and a Spring Boot API. The API validates JSON requests, normalizes submitted email values, calls XposedOrNot, maps provider data into an internal model, and computes a score. The browser receives only the ExposureWatch response.
flowchart TD
User[User browser]
Frontend[SvelteKit frontend<br/>Vercel deployment]
API[Spring Boot API]
Provider[Breach data source<br/>XposedOrNot API]
User --> Frontend
Frontend --> API
API --> Provider
Provider --> API
API --> Frontend
Frontend --> User
Deployment
- The SvelteKit frontend is deployed to Vercel at https://exposurewatch.vercel.app.
- The Spring Boot API is deployed to Render at https://exposurewatch-api.onrender.com.
- Render builds the API from
server/Dockerfileusingrender.yaml. - The API allows the configured Vercel origin and local Vite development origins through CORS.
Flowchart with sequence
sequenceDiagram
participant U as User
participant F as SvelteKit Frontend
participant A as ExposureWatch API
participant P as XposedOrNot Provider
U->>F: Enter email + submit
F->>A: POST /exposure/check { email }
A->>A: Validate + compute score
A->>P: Call provider API
P-->>A: Return breach data
A-->>F: JSON exposure result
F-->>U: Render score, level, breaches
The frontend is deployed separately from the API. The backend remains the browser's only breach-data integration boundary.
Components
The frontend is deployed separately from the API. The backend remains the browser's only breach-data integration boundary.
| Component | Responsibility | Evidence |
|---|---|---|
src/routes/+page.svelte |
Owns page state and composes form, score, and breach list. | Current source |
src/lib/api/exposure.ts |
Builds the configured API URL and sends JSON requests. | Current source |
ExposureController |
Exposes JSON and form variants of /exposure/check. |
Current source |
ExposureService |
Normalizes email, maps provider records, and calculates the response. | Current source |
ExposureScoring |
Applies 20 points per breach, capped at 100, then maps levels. | Current source |
XposedOrNotClient |
Calls the upstream endpoint and supports detailed/name-only response shapes. | Current source |
ApiExceptionHandler |
Maps validation, configuration, and provider exceptions to problem details. | Current source |
Request Flow
- The form requires an email-shaped value and submits it to the frontend API wrapper.
- The API validates JSON requests, trims and lowercases the email, then calls XposedOrNot.
- Provider details are mapped to
BreachRecordvalues; missing data classes become empty lists. - The scoring service returns a value from 0 to 100 and a level.
- The frontend renders the score and breach list or an error state.
Boundaries And Risks
- The provider is an external availability, quota, and terms dependency.
- No cache or application-level rate limit is present in the inspected implementation.
- CORS allows the configured production origin and localhost development ports for
/exposure/**. - Raw email handling, logging, retention, and privacy policy require explicit verification.
- The frontend and backend use the backend's title-case level contract; see ADR-0001.
Proposed Hardening
Add provider protection and environment-specific deployment checks before treating the service as production-ready. Controller and scoring coverage now exists through deterministic backend tests.