Prerequisites

Prerequisites#

Software#

Generally speaking, requirements are the following:

  • A GNU/Linux distribution. Tested on Debian Trixie.

  • Python version >= 3.11. Tested with Python 3.13 and 3.14

  • Kvrocks database.

  • Poetry.

  • PostgreSQL server for storage.

  • An email server — outgoing email for the accounts confirmation (password recovery, etc.).

Postfix, or an equivalent software, is required for the email notifications.

For the Web server you can use Gunicorn, uWSGI, Apache or Nginx.

Hardware#

For the core of the Vulnerability-Lookup service, we recommend the following baseline specifications for a public-facing deployment (running Kvrocks and PostgreSQL) serving a large number of concurrent clients:

  • 16 CPU cores

  • 20 GB RAM

  • 2 TB NVMe storage

The new VLAI capabilities are straightforward to set up, as we intentionally avoided adding complexity to the core Vulnerability-Lookup software or its dependencies. Instead, we developed a separate component called ML-Gateway, which is queried by the core service.

The requirements for ML-Gateway are currently minimal. For VLAI Severity, no GPU or specialized hardware is needed, as it uses a lightweight NLP model that is loaded at runtime. ML-Gateway requires:

  • 4 GB of memory

  • 200 GB of disk space

  • At least 4 vCPU cores

These requirements may increase depending on the VLAI features added in the future.

The address of the gateway is set with ML_GATEWAY in config/website.py. It powers the severity classification and the ATT&CK technique suggestions on the vulnerability page, and the two similarity searches over the ATT&CK bi-encoder space: the “Related by attack behaviour” block of the vulnerability page (GET /api/vlai/related/<vulnerability_id>) and the technique pages at /attack/technique/<technique_id> (GET /api/vlai/attack-techniques/<technique_id>/vulnerabilities), reached from the ATT&CK navbar entry, a technique index backed by GET /api/vlai/attack-techniques/catalog. The gateway owns the vectors and the search; Vulnerability-Lookup only sends descriptions and renders the answers, so no ML dependency is installed here.

For the searches to know about new records, the gateway index has to follow the feeders. Set ATTACK_EMBEDDING_INDEXER = True next to ML_GATEWAY in config/website.py and poetry run start also launches the indexer, a consumer of the vulnerability channel that sends the description of every published record to the gateway in small batches (it retries with backoff, keeps a bounded backlog and never holds a feeder back). The gateway embeds a batch synchronously on the CPU, so the indexer adapts the batch size to its pace instead of sending a fixed one: a batch that times out is not resent as is but halved, and the size grows back slowly once full batches go through again. The gateway’s index endpoint is its only writing endpoint and requires a shared secret: set ML_GATEWAY_TOKEN next to ML_GATEWAY to the value the gateway was started with as ML_GATEWAY_INDEX_TOKEN. The indexer sends it as a bearer token on every index call; a gateway that refuses it (HTTP 401) is logged once at error level and treated like an outage, the batches waiting with backoff until the token is fixed. The read endpoints need no token. It can also be run on its own:

poetry run index_attack_embeddings --start
poetry run index_attack_embeddings --stop

The tunables are constants at the top of bin/index_attack_embeddings.py:

Constant

Default

Meaning

BATCH_SIZE_INITIAL

16

Items per index call at start-up.

BATCH_SIZE_MAX

64

Ceiling the batch size grows back to.

BATCH_GROW_AFTER / BATCH_GROW_STEP

4 / 4

After this many consecutive full batches went through, the size grows by this many items. Partial batches (a quiet channel flushed on age) do not count.

BATCH_MAX_AGE_S

5

A partial batch is sent once its oldest item has waited this long.

GATEWAY_CONNECT_TIMEOUT_S

10

Connection timeout, fixed.

GATEWAY_TIMEOUT_PER_ITEM_S / GATEWAY_TIMEOUT_MIN_S

8 / 60

The read timeout of a call is max(GATEWAY_TIMEOUT_MIN_S, GATEWAY_TIMEOUT_PER_ITEM_S × items), so 128 s for the initial batch and 512 s for the largest.

RETRY_BACKOFF_MIN_S / RETRY_BACKOFF_MAX_S

5 / 300

Exponential backoff between attempts when the gateway is unreachable, answers 5xx or refuses the token.

PENDING_MAX_ITEMS

20000

Descriptions kept in memory (buffered plus awaiting retry) while the gateway cannot take them; the oldest are dropped past it.

A read timeout (the gateway took the call but did not answer in time) halves the batch size, down to one item, and retries the same items after RETRY_BACKOFF_MIN_S without escalating the backoff: shrinking is the remedy. Only when a single item still times out is the gateway treated as down, with the ordinary backoff. Every change of the batch size is logged once (a warning when it shrinks, with the cause; an info line when it is back at BATCH_SIZE_MAX), not on every attempt.

The indexer only covers what is published after it starts. The existing corpus is loaded once on the gateway host from the NDJSON dumps Vulnerability-Lookup publishes (bin/dump.py), with ml-gw-cli backfill-index --dumps <dumps dir>, and both paths reduce a record to the same identifier and description. Rebuild the index (and rerun the backfill) when the served model revision changes: the gateway reports the mismatch on every call until then.

Network#

Deployment on the different servers requires an Internet connection, as updates are retrieved from the GitHub repository. The feeders also need Internet access to various locations, including Git repositories, HTTP endpoints, and specific APIs.