This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
QuantWarden is a post-quantum cryptography (PQC) attack-surface scanner. Users register organizations, add assets (domains/hosts), and run scans that discover subdomains, probe open ports, and analyze TLS/SSL posture for quantum-readiness (ML-KEM key exchange, etc.). Results roll up into a CBOM (Cryptographic Bill of Materials), PQC scores, and reports.
This is a Git repository containing two application directories plus a master Compose file:
quantwarden-ui-main/— Next.js 15 control plane + a Node scan worker (execution plane). These share the samesrc/libcodebase and Prisma client.quantwarden-backend-main/— A polyglot monorepo of stateless scan microservices (Python FastAPI + one Go service).docker-compose.yml(root) — Master orchestrator that builds and wires everything together; seeREADME.md.
The UI and backend retain separate build systems even though they share the root repository.
cp .env.example .env # defaults already work for internal Docker networking
docker compose up -d --build # UI :3000; worker and scanner APIs stay on the private Compose networknpm install # runs `prisma generate` via postinstall
npm run dev # Next dev server (uses .next-dev distDir)
npm run build # prisma generate && next build
npm run lint # eslint (eslint-config-next)
npm run worker:check # typecheck the worker (tsc --noEmit)
npm run worker:start # build + run the worker locally (worker/bootstrap.cjs)There is no UI test runner configured. The worker has no separate test suite.
python3 start_monorepo_servers.py # launches the three core scanner services
python3 start_monorepo_servers.py --setup # interactive port selectionRun a single service manually, e.g. nmap-api:
cd quantwarden-backend-main/nmap-api
python3 -m uvicorn main:app --host 0.0.0.0 --port 8010 --reloadThe Go subfinder service: cd subfinder-api && SUBFINDER_API_ADDR=:8085 go run .
nmap-api is the only service with tests: cd quantwarden-backend-main/nmap-api && pytest.
The single most important thing to understand is the control-plane / execution-plane split, coordinated entirely through shared Postgres (Neon in prod) state — there is no direct RPC from app to worker except a one-shot "wake" ping.
- App (Next.js) is the control plane. API routes under
src/app/api/orgs/scans/**create rows inasset_scan_batch/asset_scan(queued), or createscan_schedulerows. The app does not execute scans (it's designed to run on Vercel where long jobs aren't allowed). - After creating a manual batch, the app calls
src/lib/scan-worker-wake.ts→POST {SCAN_WORKER_WAKE_URL}/internal/wakewith a bearerSCAN_WORKER_WAKE_SECRET. This only flips the worker into "active" (fast-poll) mode; the batch still succeeds if the wake fails. - Worker (
worker/src/index.ts) is the execution plane. It runs two loops —scheduler(materializes due schedules into queued batches) andexecutor(claims and runs queued scan items). It has active mode (fast polling, ~1.5s) and idle mode (~30min) to avoid keeping Neon hot. A wake request or detected work refreshes the active window. - The executor claims items via
claimNextPendingScanand dispatches byengine:portDiscovery→port-discovery-runner.ts(calls nmap-api),subdomainDiscovery→subdomain-discovery-runner.ts(calls subfinder-api), elseopenssl-scan-runner.ts(calls openssl-api). Runners write progress/results back to Postgres;refreshScanBatchrecomputes batch status. - Stale
runningitems (>5min, e.g. from a worker crash) are auto-recovered tofailedat the top of each executor tick. - The UI subscribes to live progress via SSE:
src/app/api/orgs/scans/stream/route.ts+src/components/scan-activity-provider.tsx.
After each batch step completes, the worker advances per-org workflows stored in org_scan_workflow:
onboarding: subdomain_discovery → port_discovery → openssl → doneasset_added: port_discovery → openssl → done
The worker is not a separate package. worker/tsconfig.json sets baseUrl: ".." with @/* → src/* and includes ../src/lib/**/*.ts, so the worker imports the same src/lib/* modules and the same src/lib/prisma.ts client the app uses. worker/bootstrap.cjs rewrites the @/ alias at runtime and loads .env plus .env.local. Editing anything in src/lib/ affects both the app and the worker.
The core backend contains the Go Subfinder API plus Python port-discovery and OpenSSL APIs. They are stateless and called by the worker. PySSL and the MCP bridge are retained only as optional developer tools in the backend Compose profile.
- Prisma schema:
quantwarden-ui-main/prisma/schema.prisma. Core models:Organization/Member/Role/Invitation/JoinRequest(multi-tenant orgs with RBAC),Asset,AssetScanBatch/AssetScan,ScanSchedule/ScanScheduleRun,NmapAsset/NmapAssetScan,OrganizationPortDiscoveryConfig. Better Auth models:User/Session/Account/Verification/LoginCode. - Some tables (
org_scan_workflow, scheduling tables) are created/ensured at runtime by the worker viaensure*helpers (scan-workflow-schema.ts,scan-schedule-server.ts) rather than Prisma migrations. Several DB ops useprisma.$queryRawUnsafe/$executeRawUnsafeagainst these snake_case tables. - Prisma 7 + pg adapter:
src/lib/prisma.tsusesPrismaPgover apg.Pool, cached onglobalThisoutside production. - Auth: Better Auth with
organization,magicLink, andusernameplugins. Username/password is the default (USERNAME_AUTH_ENABLED=true). Email OTP/magic-link and email invitations require bothEMAIL_AUTH_ENABLED=trueand SMTP. Username accounts use an internal synthetic email underUSERNAME_EMAIL_DOMAINso existing invite/membership code works unchanged./api/auth/methodsexposes enabled modes; password recovery for username accounts is org-admin-only. No Google or Resend. - PQC scoring (
src/lib/pqc-scoring.ts): turns raw OpenSSL scan data into a 0–100 score / A–F tier across key-exchange (40), symmetric (30), protocol (20), auth (10) plus penalties.src/lib/cbom.ts,pqc.ts,reporting.tsbuild CBOM/report outputs consumed by thecbom/,posture/,reporting/pages.
- Authenticated app lives under
src/app/app/[org_slug]/...; org-scoped UI pieces are in_components/. The public CBOM explorer is undersrc/app/cbom/explorer/.... - API routes under
src/app/api/. Auth catch-all isapi/auth/[...all]. Scan-related endpoints are underapi/orgs/scans/.
- The app/worker contract requires
SCAN_WORKER_WAKE_SECRETto be identical on both sides, andSCAN_WORKER_WAKE_URLon the app to point at the worker's control port (8088). Worker health is on8089(/healthz). next.config.tsseparates dev (.next-dev) and prod (.next) build dirs and emits standalone production output for the runtime image.- Templates: root
.env.example,quantwarden-ui-main/.env.worker.example,quantwarden-backend-main/.env.docker.example. Worker polling cadence and OpenSSL probe batching are tuned via theSCAN_WORKER_*andOPENSSL_API_*vars documented inquantwarden-ui-main/worker/README.md.