Skip to content

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)

  1. server/routes/scan.ts receives POST /scan.
  2. server/handlers/scanHandlers.ts parses the body via stackValidation.ts.
  3. scanStack resolves enabled built-in sources and custom RSS feeds.
  4. Parallel fetches per tool complete; results merge and sort.
  5. Optional enrichVulnsWithTranslations runs when translate: true.
  6. JSON ScanResponse returns to the client; persistLastScan in src/lib/scanCache.ts writes cve-radar:last-scan with 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