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.
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 |
|---|---|---|
|
16 |
Items per index call at start-up. |
|
64 |
Ceiling the batch size grows back to. |
|
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. |
|
5 |
A partial batch is sent once its oldest item has waited this long. |
|
10 |
Connection timeout, fixed. |
|
8 / 60 |
The read timeout of a call is |
|
5 / 300 |
Exponential backoff between attempts when the gateway is unreachable, answers 5xx or refuses the token. |
|
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.