Architecture¶
The repository is a monorepo: a React 19 + Vite + TypeScript single-page application and an Express API that aggregates public vulnerability feeds. The SPA never talks to NVD or OSV directly; all outbound traffic goes through the server so keys, caching, and rate limits stay centralized.
This chapter maps folders to responsibilities, describes how scan and watch flow through handlers, and points integrators to the OpenAPI document served at runtime.
High-level topology¶
flowchart LR
classDef ui fill:#e9edf5,stroke:#00baba,color:#253343
classDef api fill:#f3fcfc,stroke:#008c8c,color:#253343
classDef ext fill:#fff7ed,stroke:#eda232,color:#253343
Browser[Browser SPA]:::ui --> API[Express /api]:::api
API --> NVD[NVD]:::ext
API --> OSV[OSV]:::ext
API --> GH[GitHub]:::ext
API --> CISA[CISA KEV]:::ext
API --> RSS[RSS]:::ext
Diagram walkthrough¶
The browser persists stack, locale, and source toggles in localStorage, then calls POST /api/scan or POST /api/watch. The API layer validates JSON bodies, invokes scanStack in server/services/scan.ts, and returns merged vulnerabilities. Provider-specific logic lives under server/services/; shared deduplication is in server/lib/merge.ts; translation enrichment is in server/services/enrich.ts.
Step-by-step request path (full scan)¶
server/routes/scan.tsreceivesPOST /scan.server/handlers/scanHandlers.tsparses the body viastackValidation.ts.scanStackresolves enabled built-in sources and custom RSS feeds.- Parallel fetches per tool complete; results merge and sort.
- Optional
enrichVulnsWithTranslationsruns whentranslate: true. - JSON
ScanResponsereturns to the client;persistLastScaninsrc/lib/scanCache.tswritescve-radar:last-scanwith a stack key.
Database layer (optional PostgreSQL)¶
When DATABASE_URL is set, scan metadata persists in PostgreSQL via server/db/scanHistory.ts and tenant stacks via server/db/tenants.ts. SQL migrations live in server/db/migrations/*.sql and run automatically on pool startup; applied files are recorded in _schema_migrations so restarts skip already-applied DDL.
Type-safe schema definitions for incremental ORM migration live in server/db/schema.ts (Drizzle ORM). server/db/drizzle.ts exposes getDb() backed by the shared pg pool. Tenant and scan-history persistence (server/db/tenants.ts, server/db/scanHistory.ts) uses Drizzle query builders; SQL migrations in server/db/migrations/*.sql still run on pool startup.
Notification services¶
Watch-mode server alerts use server/services/notifications/NotificationService.ts. Channel adapters (Slack, Discord, Telegram, SMTP, generic webhook) read env vars documented in Configuration. server/services/alerts.ts delegates to the service for backward compatibility with ALERT_WEBHOOK_URL.
Shared contract layer¶
Types and severity ordering live in shared/ (shared/api/vulnerability.ts, shared/constants/severityOrder.ts). Both src/ and server/ import via @shared/*; server/types.ts re-exports for backward compatibility. npm run build:server compiles shared/ into dist-server/.
API layer layout¶
| Module | Role |
|---|---|
server/index.ts |
Express app, mounts routers |
server/routes/health.ts |
GET /health |
server/routes/meta.ts |
capabilities, sources, openapi.json |
server/routes/scan.ts |
scan, watch, scan/validate |
server/routes/v1.ts |
Versioned mount of the same handlers |
server/routes/translateLocale.ts |
POST /translate |
server/handlers/scanHandlers.ts |
Shared scan/watch/validate logic |
server/openapi/spec.json |
OpenAPI 3.1 contract |
Legacy paths remain under /api/*; integrations should prefer /api/v1/* (see API reference).
Frontend map¶
| Area | Path |
|---|---|
| Shell | src/components/layout/AppShell.tsx, SubNav.tsx, SetupWizard.tsx |
| Tabs | src/components/tabs/* |
| Scan cache | src/lib/scanCache.ts |
| API client | src/api/scan.ts, watch.ts, scanPayload.ts |
| i18n | src/i18n/messages/*.ts |
| Hooks | useVulnScan, useWatch, useVulnTranslations, useFontScale |
| Middleware | server/middleware/rateLimit.ts, scanTimeout.ts |
Backend services (feeds)¶
| Provider | Module |
|---|---|
| NVD | server/services/nvd.ts |
| OSV | server/services/osv.ts |
| GitHub | server/services/github.ts |
| CISA KEV | server/services/cisa.ts |
| RSS | server/services/rss.ts |
| Translation | server/services/translate.ts |
Caching and limits¶
In-memory cache (server/lib/cache.ts) backs RSS XML and translation strings. NVD delay comes from server/lib/rateLimit.ts (upstream pacing). Request rate limits for scan/watch are enforced in server/middleware/rateLimit.ts (separate buckets; validate exempt). Client-side scan results persist in localStorage via scanCache.ts. Capabilities advertised via GET /api/capabilities mirror hard limits in validators (50 tools, 20 custom feeds).
Next: Alerts