Assessor_2.0/docs/PROJECT_OVERVIEW.md

21 KiB

ASSESSOR — Project Overview

ASSESSOR imports security-scanner reports (PingCastle, Purple Knight, CIS-CAT, Microsoft Defender for IoT, Maester, Group Policy backups, and generic CSV/JSON/XML/HTML/Excel exports), normalizes them into one canonical model, scores the assessed environment, lets an analyst review and compare findings, and presents an executive security posture. It also offers optional AI overview generation and regulatory (ISO 27001/27002) mapping through a retrieval index.

This is the entry point for the whole project. It explains what the software is, how it is put together, how many containers it runs, how to develop it locally, and how it is deployed. Detailed topic docs live alongside this file in docs/; this document links to the right one for each topic.

Status: the application runs today as a local browser app served by a Node.js backend, and is being deployed to an internal server behind an nginx reverse proxy. The full phased plan and its history are in docs/IMPLEMENTATION_PLAN.md; binding rules for anyone (human or AI) changing the code are in AGENTS.md.


1. What runs where — the short answer

There are three long-running services, defined in docker-compose.deploy.yml:

Service Image Role Published on host?
app built from Dockerfile Node.js server: serves the React app and the /api/v1 API from one origin Yes — the only exposed port
postgres postgres:16-alpine System of record (canonical findings, reviews, comparisons, jobs) No
qdrant qdrant/qdrant:v1.19.1 Rebuildable vector index for regulatory RAG retrieval No

So one deployed environment = 3 containers. Only app is reachable from the host; PostgreSQL and Qdrant are private on the internal Docker network.

The server runs two isolated environments in parallel (see docs/DEPLOYMENT.md), each its own set of 3 containers with its own database, artifact volume, and Qdrant index:

Branch Compose project VPN address Public host name (via nginx)
main assessor_prod http://10.0.0.154:4173 https://assessor.soc-ops.de
experimental assessor_experimental http://10.0.0.154:4174 https://staging.assessor.soc-ops.de

That is 6 containers total for the managed stacks. A separate, older HTTPS installation exists on the same host and is intentionally not managed by this workflow until an approved cutover.

For local development you do not containerize the app. You run only the two backing services in Docker (postgres + qdrant, project assessor-2_0 — the single canonical local datastore) and run the Node backend + Vite client as host processes. See §4.


2. Architecture

2.1 Layered design

ASSESSOR keeps a strict separation between format parsing, structural analysis, semantic mapping, normalization, comparison, persistence, and presentation. The core engine is platform-neutral: it never touches the DOM, localStorage, browser File objects, SQL, or the network directly — those live behind adapters. This is what lets the same domain logic run in the browser today and in the server backend now / a future shell later.

┌─────────────────────────────────────────────────────────────┐
│  React UI (src/features, src/components)                      │  presentation only
│  no source-specific parsing, no direct DB access             │
└───────────────┬───────────────────────────────────────────────┘
                │ HTTP (same origin)
┌───────────────▼───────────────────────────────────────────────┐
│  Node backend (server/) — /api/v1 + static React app          │
│  import workers · GPO analysis · CVE enrichment · AI · RAG     │
└───────────────┬───────────────────────────────────────────────┘
                │ AssessorRepository contract (no SQL leaks upward)
┌───────────────▼───────────────────────────────────────────────┐
│  Persistence adapter                                          │
│   • PostgresAssessorRepository  (durable, production)         │
│   • LocalAssessorRepository     (in-memory, dev only)         │
└───────────────┬───────────────────────────────────────────────┘
                │
┌───────────────▼──────────┐        ┌──────────────────────────┐
│  PostgreSQL 16            │        │  Qdrant (regulatory RAG) │
│  system of record        │        │  rebuildable index       │
└──────────────────────────┘        └──────────────────────────┘

The platform-neutral import engine (src/importers, src/domain) turns raw bytes into an ImportSource, parses it through injected runtime adapters, picks a scanner profile via the shared recognition engine, and produces the canonical model. Known tool profiles extend this generic pipeline rather than bypass it.

Key boundary docs:

2.2 The app service in detail

The single app container (or local node dist-server/server.mjs) does three jobs from one origin (port 4173):

  1. Serves the built React SPA from dist/.
  2. Serves the versioned JSON API under /api/v1.
  3. Runs background work: isolated import parser workers, GPO inventory/analysis workers, CVE enrichment, AI overview jobs, and regulatory retrieval.

Same-origin is a deliberate security property: the server sets no CORS, and every write requires the X-Assessor-Request: 1 header with a matching Origin. See docs/LOCAL_BACKEND.md and SECURITY.md.

2.3 API surface

All endpoints live under /api/v1. Top-level resource groups:

health · audit-events · db-structure · reference-data · projects (with nested policy-analyses, environment-profiles, policy-snapshot-aggregations, policy-evaluations, policy-finding-runs, gpo-backups, scan-runs) · assessments · findings · results · reviews · profiles · comparisons · cves · import-sessions.

Imports are atomic sessions (stage → commit → rollback). Errors always carry code, message, and requestId. The contract is in src/api/assessor-api-contract.ts; the server in server/assessor-http-server.ts.


3. Repository layout

server/                 Node backend
  assessor-http-server.ts   HTTP router (/api/v1 + static app)
  index.ts                  process entry, wiring, env parsing
  postgres/                 repository adapter + migrations/
  gpo/                      Group Policy backup ingestion & analysis
  cve/                      NVD CVE enrichment (opt-in, backend-only)
  ai/                       LiteLLM client + AI overview service
  regulatory/               ISO corpus, chunking, Qdrant index, RAG runtime, mapping
  artifacts/                filesystem artifact store
src/                    Browser app + platform-neutral core
  importers/ domain/        platform-neutral engine & canonical model
  profiles/                 scanner profiles (see §5)
  recognition/ analysis/ comparison/ scoring/ evaluation/
  persistence/              repository contract + LocalAssessorRepository
  platform/browser/         browser adapters (File, DOM, storage, download)
  api/                      typed API client & contract
  features/ components/ styles/   React UI
docs/                   all detailed documentation (see §9)
tests/                  vitest suites + local-only & design-language checks
deploy/nginx/           reverse-proxy vhosts for prod + staging
.gitea/workflows/       CI/CD (deploy.yml)
Dockerfile              multi-stage build for the app image
docker-compose.deploy.yml    3-service deployed stack (app+postgres+qdrant)
docker-compose.postgres.yml  local backing services (postgres+qdrant)

4. Local development

Prerequisites

  • Node.js 22 (matches the runtime image) and npm
  • Docker + Docker Compose (for PostgreSQL + Qdrant)

First-time setup

cp .env.postgres.example .env.postgres
# edit .env.postgres: set long, local-only ASSESSOR_POSTGRES_PASSWORD and ASSESSOR_QDRANT_API_KEY
npm ci
npm run db:up          # starts the canonical postgres + qdrant (project assessor-2_0)
npm run dev            # builds/starts backend on 127.0.0.1:4173, then Vite client on 127.0.0.1:5173

npm run dev checks /api/v1/health first, brings up a missing backend, then opens the Vite client (which proxies /api to 4173). The backend binds to 127.0.0.1 only — it is not exposed on the network in local mode.

The canonical local datastore is fixed: Compose project assessor-2_0, database assessor, volume assessor-2_0_assessor-postgres-data, on 127.0.0.1:5432. Never create an alternative DB/volume/port for this workspace (see AGENTS.md → "Canonical local PostgreSQL"). If port 5432 is taken by a different container, stop and report the conflict.

Useful scripts

Command What it does
npm run dev Full local stack (backend + Vite client)
npm run dev:client Vite client only (backend managed separately)
npm run build Build client (dist/) and server (dist-server/)
npm run start:local Run the built server on 4173 using .env.postgres
npm run serve:local build then start:local
npm run db:up / db:down / db:logs Manage local postgres+qdrant
npm run rag:prepare-iso / rag:ingest / rag:evaluate Build/populate/evaluate the regulatory index
npm test vitest + local-only check + design-language check

Without ASSESSOR_DATABASE_URL the backend runs in an explicitly ephemeral in-memory mode. If the URL is set but the DB is unreachable, startup fails (no silent fallback), so a misconfiguration can never produce imports that look saved but are not. Details: docs/POSTGRESQL.md.


5. Supported scanner profiles

Profiles live in src/profiles/: pingcastle, purple-knight, cis-cat, defender-for-iot, maester, group-policy, generic, and public-search (regulatory). Each profile must declare its scoring semantics (securityScoring) and extend the generic import pipeline — see docs/TOOL_PROFILES.md and the profile rules in AGENTS.md.

Any change to recognition, parsing, mapping, identity, scoring inputs, or persisted evidence must follow docs/REIMPORT_POLICY.md (add a processing-manifest version and mark affected historical imports for reimport).


6. Deployment

Deployment is fully driven by the Gitea workflow .gitea/workflows/deploy.yml. You do not run docker compose by hand on the server for a normal release — a push does it.

How a release happens

  1. Push to main (→ stable) or experimental (→ experimental).
  2. The workflow loads that environment's complete env-file secret (PROD_ENV_FILE / STAGING_ENV_FILE) and validates required keys are present.
  3. Runs npm ci, the full npm test, and npm run build.
  4. Acquires a per-environment Docker lock (Gitea ignores the concurrency key), validates the resolved Compose config, and checks port/project isolation (only app/postgres/qdrant may exist; the target port must not be published by another project).
  5. Backs up the existing environment (custom pg_dump, artifact tarball, SHA256SUMS) into the external volume assessor_deployment_backups.
  6. docker compose ... up -d --build --wait for that environment only.
  7. Verifies health via /api/v1/health inside the container, then releases the lock.

The two environments never share PostgreSQL, artifacts, or the Qdrant index — Compose scopes all named volumes by project. restart: unless-stopped keeps both alive across daemon/host restarts. Feature promotion is a experimental → main merge; databases and artifacts are never copied between environments.

Reverse proxy / TLS

nginx terminates TLS and proxies to the VPN ports; vhosts are in deploy/nginx/assessor.conf. proxy_set_header Host $host is mandatory — ASSESSOR rejects writes (403 cross_origin_rejected) if the forwarded host does not match the browser origin. client_max_body_size is 200m to allow ~100 MiB uploads (base64 overhead).

Backups & restore

The workflow writes a backup before every update and never deletes backups. Restore tests must use an isolated temporary Compose project and synthetic data, verify SHA256SUMS, and pg_restore --exit-on-error into a fresh DB. Never run docker compose down --volumes on a permanent environment. Full procedure: docs/DEPLOYMENT.md and docs/POSTGRESQL.md.

Manual deploy (reference only)

docker compose --env-file .env -p assessor_prod -f docker-compose.deploy.yml \
  up -d --build --wait

7. Configuration

Real secret values live only in the git-ignored .env.postgres (workstation) or the deployment env-file secrets — never in the repo or in client-side config. .env.postgres.example is the annotated template.

Variable Purpose
ASSESSOR_HOST / ASSESSOR_PORT Bind address / port (default 127.0.0.1:4173)
ASSESSOR_DATABASE_URL PostgreSQL DSN; absent = ephemeral in-memory dev mode
ASSESSOR_DATABASE_SSL disable only for local loopback; TLS required for remote DBs
ASSESSOR_POSTGRES_* DB name/user/password (compose builds the DSN in deploy)
ASSESSOR_ARTIFACT_ROOT Artifact store path (must be outside the public web root)
ASSESSOR_QDRANT_URL / _API_KEY Regulatory index endpoint & key
ASSESSOR_RAG_EMBEDDING_* Approved embedding model for regulatory ingestion/retrieval
ASSESSOR_AI_MODE (+ ASSESSOR_AI_*) Enables the LiteLLM AI overview; unset = outbound AI off
NVD_API_KEY / ASSESSOR_NVD_TIMEOUT_MS Optional CVE enrichment against the fixed NVD endpoint
ASSESSOR_PARSER_* / ASSESSOR_GPO_* Import/GPO worker limits (timeout, memory, concurrency)

AI and CVE calls are the only outbound network paths, they are backend-only, opt-in, and off by default. Trust boundaries: docs/AI_OVERVIEW_SECURITY.md, docs/REGULATORY_RAG.md, docs/IMPORT_SECURITY_LIMITS.md.


8. Data & persistence

  • PostgreSQL 16 is the system of record: relational keys/foreign keys for identity plus the full versioned canonical object as JSONB (so new scanner fields are captured losslessly). Full-text tsvector + GIN indexes back search.
  • Migrations are versioned SQL files in server/postgres/migrations/ (currently through 027_*). On start the server takes an advisory lock, verifies each migration's version + SHA-256, applies new ones in their own transaction, and refuses to start if an already-applied migration was edited. Never edit an applied migration — add a new numbered file.
  • Qdrant holds the regulatory retrieval index. It is authenticated and rebuildable from the versioned corpus — its volume speeds restarts but is not a system-of-record backup.
  • Artifacts (e.g. GPO raw backups) go to a filesystem store on a dedicated volume, outside the public web root.

Details: docs/POSTGRESQL.md, docs/REGULATORY_RAG.md, MIGRATION.md.


9. Testing & verification

npm test              # vitest + local-only network check + design-language check
npm run build         # type-check + build client and server (CI gate)
npm run test:postgres # integration tests against a real PostgreSQL 16 (ASSESSOR_TEST_DATABASE_URL)

Baseline verification for any relevant change is npm test + npm run build (both run in CI before deploy). Testing is privacy-safe: use synthetic fixtures; never read, log, screenshot, or quote real report content, file names, finding titles, or tenant data. Against a running app, verify only content-free signals (HTTP status, health, anonymized counts, boolean DOM state). See the "Privacy-safe testing" section of AGENTS.md and docs/TEST_BASELINE.md.


10. Security model (summary)

  • Local/same-origin by default: no CORS, write requests need X-Assessor-Request with a matching Origin; the backend binds loopback locally.
  • Imported HTML/XML/Excel/JSON/CSV are untrusted input — active content is never executed; HTML is inertized before table extraction; Excel formulas/VBA stay disabled. Parsers run in isolated workers with time/memory limits.
  • No external uploads, telemetry, or CDN assets. The only outbound paths are the opt-in backend AI (LiteLLM) and CVE enrichment (NVD).
  • Embedded report data is parsed with a bounded static grammar, never evaluated.

Full model: SECURITY.md, docs/IMPORT_SECURITY_LIMITS.md, docs/architecture/gpo-threat-model.md.


11. Documentation map

Start here → this overview. New to ASSESSOR? Read the illustrated docs/ONBOARDING.md first. Then, by topic:

Area Documents
Rules for contributors AGENTS.md · CLAUDE.md
Plan & history docs/IMPLEMENTATION_PLAN.md · MIGRATION.md
Architecture docs/PLATFORM_NEUTRAL_ENGINE.md · docs/PERSISTENCE_BOUNDARY.md · docs/architecture/
Data model docs/CANONICAL_MODEL.md · docs/UNIFIED_RESULT_MODEL.md · docs/RESULT_CONTRACT.md · docs/SCORING_MODEL.md
Import docs/IMPORT_WORKFLOW.md · docs/IMPORT_INTERMEDIATE_MODEL.md · docs/RECOGNITION_ENGINE.md · docs/TOOL_PROFILES.md · docs/REIMPORT_POLICY.md
Compare & filter docs/COMPARISON_ENGINE.md · docs/DYNAMIC_FILTERS.md
Backend & persistence docs/LOCAL_BACKEND.md · docs/POSTGRESQL.md
Deployment docs/DEPLOYMENT.md · deploy/nginx/assessor.conf
AI & regulatory docs/AI_OVERVIEW_SECURITY.md · docs/LLM_DASHBOARD_PROMPT.md · docs/REGULATORY_RAG.md
Security & testing SECURITY.md · docs/IMPORT_SECURITY_LIMITS.md · docs/TEST_BASELINE.md · docs/USER_FLOW_CHECKS.md