Skip to content

Configuration

All configuration is loaded from a YAML file and can be overridden by environment variables. The application looks for config.yaml in the following locations, in order:

  1. Current working directory (./config.yaml)
  2. ./configs/config.yaml

Environment variables take precedence over the file. The prefix is EASM_, dots in the key path are replaced with underscores (e.g. database.dsn → EASM_DATABASE_DSN).

The full example file lives at backend/configs/config.yaml.


Contents


app

Application version metadata shared by the API and frontend.

app:
  version: "0.1.6 Sirius"
Key Default Description
app.version release value Current application version shown by GET /api/v1/version and used by Update Center unless overridden

Environment variable: EASM_APP_VERSION

For normal releases, update app.version only. updates.current_version should remain empty unless you need a temporary override.


server

Controls the HTTP API listen address. Used by the api service only; the worker ignores this section.

server:
  host: 0.0.0.0   # interface to bind; use 127.0.0.1 to restrict to localhost
  port: "8080"    # TCP port (string)
Key Default Description
server.host 0.0.0.0 Bind address
server.port 8080 Listen port

Environment variables: EASM_SERVER_HOST, EASM_SERVER_PORT


database

PostgreSQL connection string. Both the API and the worker require this.

database:
  dsn: postgres://easm:easm@postgres:5432/easm?sslmode=disable
Key Default Description
database.dsn postgres://easm:easm@postgres:5432/easm?sslmode=disable Full libpq connection URI

Environment variable: EASM_DATABASE_DSN

The DSN format is:

postgres://<user>:<password>@<host>:<port>/<database>?<options>

Common options:

Option Purpose
sslmode=disable No TLS (development / internal networks)
sslmode=require Require TLS
sslmode=verify-full Require TLS + verify server certificate
connect_timeout=10 Connection timeout in seconds

The API runs all pending Goose migrations automatically on startup. No manual goose up is required.


redis

Redis is used as the scan job queue and for future caching. Both the API and the worker require this.

redis:
  addr: redis:6379
  password: ""
  db: 0
Key Default Description
redis.addr redis:6379 host:port of the Redis instance
redis.password (empty) AUTH password; leave empty if Redis has no password
redis.db 0 Redis database index (0–15)

Environment variables: EASM_REDIS_ADDR, EASM_REDIS_PASSWORD, EASM_REDIS_DB


jwt

JSON Web Token settings used by the API for authentication. The worker does not use JWTs.

jwt:
  secret: change-me-in-production
  access_token_ttl: 15   # minutes
  refresh_token_ttl: 1    # days
Key Default Description
jwt.secret change-me-in-production HMAC-SHA256 signing key — must be changed in production
jwt.access_token_ttl 15 Access token lifetime in minutes
jwt.refresh_token_ttl 1 Maximum authenticated session lifetime in days

Environment variables: EASM_JWT_SECRET, EASM_JWT_ACCESS_TOKEN_TTL, EASM_JWT_REFRESH_TOKEN_TTL

Recommendations: - Generate a secret with openssl rand -hex 32 - Keep access_token_ttl short (≤ 30 minutes) - refresh_token_ttl is non-sliding: refreshes do not extend the original session deadline. - Access and refresh JWTs carry distinct token-type claims; access tokens cannot be used as refresh tokens, and refresh tokens cannot be used as API bearer access tokens.


smtp

SMTP is used by email-based two-factor authentication.

smtp:
  host: smtp.example.com
  port: 587
  username: hxeasm@example.com
  password: secret
  from: hxeasm@example.com
  use_tls: true

Email OTP cannot be enabled when SMTP is not configured.

Environment variables: EASM_SMTP_HOST, EASM_SMTP_PORT, EASM_SMTP_USERNAME, EASM_SMTP_PASSWORD, EASM_SMTP_FROM, EASM_SMTP_USE_TLS


two_factor

Controls issuer and encryption key material for two-factor authentication.

two_factor:
  issuer: hxEASM
  encryption_key: ""

If encryption_key is empty, hxEASM derives the TOTP encryption key from jwt.secret. Production deployments should set a strong jwt.secret and may set a dedicated two_factor.encryption_key.

Environment variables: EASM_TWO_FACTOR_ISSUER, EASM_TWO_FACTOR_ENCRYPTION_KEY

See two-factor-authentication.md.


log

Controls log output format and verbosity. Logs are written to stdout as JSON (zerolog).

log:
  level: info
Key Default Description
log.level info One of: debug, info, warn, error

Environment variable: EASM_LOG_LEVEL


storage

S3-compatible object storage for screenshots, raw outputs, evidence files, report exports, and other file artifacts. Both the API and worker use this section.

storage:
  provider: s3
  endpoint: http://minio:9000
  region: us-east-1
  bucket: easm-files
  access_key: easm
  secret_key: easm-password
  use_ssl: false
  public_base_url: http://s3.localhost:3000
Key Default Description
storage.provider s3 Storage backend. Current implementation expects S3-compatible storage
storage.endpoint http://minio:9000 Internal S3/MinIO endpoint used by API and worker
storage.region us-east-1 S3 region value
storage.bucket easm-files Bucket for uploaded artifacts
storage.access_key easm S3 access key
storage.secret_key easm-password S3 secret key; change in production
storage.use_ssl false Use HTTPS/TLS for the S3 endpoint
storage.public_base_url (empty) Browser-visible endpoint used when generating presigned download URLs

Environment variables:

  • EASM_STORAGE_PROVIDER
  • EASM_STORAGE_ENDPOINT
  • EASM_STORAGE_REGION
  • EASM_STORAGE_BUCKET
  • EASM_STORAGE_ACCESS_KEY
  • EASM_STORAGE_SECRET_KEY
  • EASM_STORAGE_USE_SSL
  • EASM_STORAGE_PUBLIC_BASE_URL

storage.public_base_url is important for MinIO/S3 deployments behind Docker or a reverse proxy. It must match the host the browser opens, otherwise presigned URLs can fail with SignatureDoesNotMatch. For local Docker Compose, use http://s3.localhost:3000 so the signed URL host matches the Nginx S3 route.

See files.md for file artifact behavior and storage flow.


updates

Admin-only Central Update API checks for the Update Center. This subsystem is informational only: it does not run shell commands, pull images, update files, or restart containers.

updates:
  enabled: true
  center_url: ""
  api_key: ""
  product: hxeasm
  channel: stable
  timeout_seconds: 10
  check_interval_minutes: 360
  current_version: ""
Key Default Description
updates.enabled true Enables Central Update API checks
updates.center_url (empty) Central Update API base URL, for example https://updates.example.com
updates.api_key (empty) Customer Update Center API key; backend-only secret
updates.product hxeasm Product identifier sent as product
updates.channel stable Release channel sent as channel
updates.timeout_seconds 10 HTTP request timeout
updates.check_interval_minutes 360 Cache TTL for automatic/status checks
updates.current_version (empty) Optional override; leave empty to use app.version

Environment variables:

  • EASM_UPDATES_ENABLED
  • EASM_UPDATE_CENTER_URL
  • EASM_UPDATE_CENTER_APIKEY
  • EASM_UPDATE_CENTER_PRODUCT
  • EASM_UPDATE_CENTER_CHANNEL
  • EASM_UPDATE_CENTER_TIMEOUT_SECONDS
  • EASM_UPDATES_CHECK_INTERVAL_MINUTES
  • EASM_UPDATES_CURRENT_VERSION

Leave EASM_UPDATE_CENTER_URL or EASM_UPDATE_CENTER_APIKEY empty to disable update checks gracefully. The GitHub token is stored only in the Central Update API, not in customer hxEASM deployments.

EASM_UPDATE_CENTER_URL=https://updates.example.com
EASM_UPDATE_CENTER_APIKEY=

Use HTTPS in production. http:// is accepted for local development. Do not commit real Update Center API keys.

See updates.md for API behavior, UI behavior, troubleshooting, and manual update commands.


research

Research repository paths used by worker-side plugins. Docker Compose mounts ./hxresearch read-only at /opt/hxeasm/hxresearch.

research:
  path: /opt/hxeasm/hxresearch
  nuclei_templates_path: /opt/hxeasm/hxresearch/nuclei
Key Default Description
research.path /opt/hxeasm/hxresearch Root research directory inside worker containers
research.nuclei_templates_path /opt/hxeasm/hxresearch/nuclei Custom Nuclei template directory used by nuclei_custom_templates

Environment variables: EASM_RESEARCH_PATH, EASM_RESEARCH_NUCLEI_TEMPLATES_PATH

nuclei_custom_templates is not included in built-in scan profiles. Add it to a custom profile only after reviewing templates under hxresearch/nuclei.


scan_profile_order and scan_profiles

Defines the scan profiles returned by GET /api/v1/scan-profiles and used by the worker to resolve plugin execution order.

scan_profile_order controls the display/API order. If it is present, it must include every configured profile exactly once.

scan_profile_order:
  - quick
  - default
  - custom_tls

scan_profiles:
  custom_tls:
    name: Custom TLS Audit
    description: Custom certificate inventory
    plugins:
      - subfinder
      - dnsx
      - httpx
      - tlsx
Field Required Description
profile ID yes Stable identifier stored on scans. Use lowercase letters, numbers, _, or -
name yes Human-readable label returned to the frontend
description no Human-readable description returned to the frontend
plugins yes Ordered list of registered plugin IDs

Validation runs at API and worker startup:

  • scan_profiles must not be empty.
  • Every profile must have a non-empty name and at least one plugin.
  • Empty or duplicate plugin names inside one profile are rejected.
  • Every plugin must be registered in the plugin registry.
  • scan_profile_order, when set, must match the profile set exactly.

After editing scan profiles, restart both API and worker. Future scans can only use configured profile IDs. Historical scans keep their stored profile ID even if a profile is later removed.


tools

Defines which CLI security tools the worker installs at startup. Each key is the tool name as it should appear in $PATH (i.e. the binary filename).

Tools already present in $PATH are always skipped. Installation runs once per worker startup.

tools:
  <tool-name>:
    enabled: <bool>
    install_method: <"apk" | "binary" | "skip">
    # method-specific fields below

install_method: apk

Runs apk add --no-cache <apk_package>. Only works on Alpine Linux (the default container base image).

tools:
  nmap:
    enabled: true
    install_method: apk
    apk_package: nmap
Field Required Description
enabled yes false skips this tool entirely
install_method yes "apk"
apk_package yes Package name as known to apk

install_method: binary

Downloads a file from binary_url, extracts the binary named binary_name, and places it in install_dir.

Supported archive formats: .zip, .tar.gz / .tgz, or raw binary (no extension).

tools:
  subfinder:
    enabled: true
    install_method: binary
    binary_url: https://github.com/projectdiscovery/subfinder/releases/download/v2.6.6/subfinder_2.6.6_linux_amd64.zip
    binary_name: subfinder
    install_dir: /usr/local/bin
Field Required Description
enabled yes false skips this tool entirely
install_method yes "binary"
binary_url yes Direct download URL (zip, tar.gz, or raw binary)
binary_name no Filename to extract. Defaults to the tool key name
install_dir no Destination directory. Defaults to /usr/local/bin

install_method: skip

Does nothing. The worker logs a warning if the binary is not found in $PATH but continues running.

tools:
  amass:
    enabled: true
    install_method: skip

Built-in tool defaults

The following tools are pre-configured in configs/config.yaml:

Tool Enabled Method Notes
nmap yes apk Port scanner; Alpine packages nmap nmap-scripts
subfinder yes binary Subdomain discovery; ProjectDiscovery
httpx yes binary HTTP probe and screenshot source; ProjectDiscovery
nuclei yes binary Vulnerability scanner; ProjectDiscovery
amass yes binary Attack surface mapping; OWASP
dnsx yes binary DNS resolver/prober; ProjectDiscovery
naabu yes binary Port discovery; ProjectDiscovery
tlsx yes binary TLS/certificate discovery; ProjectDiscovery
katana yes binary Web crawling/path discovery; ProjectDiscovery
alterx no binary Experimental permutation tool; disabled by default
shuffledns no skip Experimental DNS brute force; resolver/wordlist setup required
asnmap no binary ASN mapping; disabled by default
uncover no binary Provider-backed discovery; disabled by default

Update binary_url values when upgrading tool versions. Always use the linux_amd64 build for the default worker image.


Full example

app:
  version: "0.1.6 Sirius"

server:
  host: 0.0.0.0
  port: "8080"

database:
  dsn: postgres://easm:easm@postgres:5432/easm?sslmode=disable

redis:
  addr: redis:6379
  password: ""
  db: 0

jwt:
  secret: change-me-in-production
  access_token_ttl: 15
  refresh_token_ttl: 1

log:
  level: info

storage:
  provider: s3
  endpoint: http://minio:9000
  region: us-east-1
  bucket: easm-files
  access_key: easm
  secret_key: easm-password
  use_ssl: false
  public_base_url: http://s3.localhost:3000

updates:
  enabled: true
  center_url: ""
  api_key: ""
  product: hxeasm
  channel: stable
  timeout_seconds: 10
  check_interval_minutes: 360
  current_version: ""

research:
  path: /opt/hxeasm/hxresearch
  nuclei_templates_path: /opt/hxeasm/hxresearch/nuclei

tools:
  nmap:
    enabled: true
    install_method: apk
    apk_package: nmap

  subfinder:
    enabled: true
    install_method: binary
    binary_url: https://github.com/projectdiscovery/subfinder/releases/download/v2.6.6/subfinder_2.6.6_linux_amd64.zip
    binary_name: subfinder
    install_dir: /usr/local/bin

  httpx:
    enabled: true
    install_method: binary
    binary_url: https://github.com/projectdiscovery/httpx/releases/download/v1.6.8/httpx_1.6.8_linux_amd64.zip
    binary_name: httpx
    install_dir: /usr/local/bin

  nuclei:
    enabled: true
    install_method: binary
    binary_url: https://github.com/projectdiscovery/nuclei/releases/download/v3.2.9/nuclei_3.2.9_linux_amd64.zip
    binary_name: nuclei
    install_dir: /usr/local/bin

  amass:
    enabled: true
    install_method: binary
    binary_url: https://github.com/owasp-amass/amass/releases/download/v4.2.0/amass_Linux_amd64.zip
    binary_name: amass
    install_dir: /usr/local/bin

  dnsx:
    enabled: true
    install_method: binary
    binary_url: https://github.com/projectdiscovery/dnsx/releases/download/v1.2.2/dnsx_1.2.2_linux_amd64.zip
    binary_name: dnsx
    install_dir: /usr/local/bin

  naabu:
    enabled: true
    install_method: binary
    binary_url: https://github.com/projectdiscovery/naabu/releases/download/v2.3.3/naabu_2.3.3_linux_amd64.zip
    binary_name: naabu
    install_dir: /usr/local/bin

  tlsx:
    enabled: true
    install_method: binary
    binary_url: https://github.com/projectdiscovery/tlsx/releases/download/v1.1.9/tlsx_1.1.9_linux_amd64.zip
    binary_name: tlsx
    install_dir: /usr/local/bin

  katana:
    enabled: true
    install_method: binary
    binary_url: https://github.com/projectdiscovery/katana/releases/download/v1.1.2/katana_1.1.2_linux_amd64.zip
    binary_name: katana
    install_dir: /usr/local/bin

  alterx:
    enabled: false
    install_method: binary
    binary_url: https://github.com/projectdiscovery/alterx/releases/download/v0.0.4/alterx_0.0.4_linux_amd64.zip
    binary_name: alterx
    install_dir: /usr/local/bin

  shuffledns:
    enabled: false
    install_method: skip
    binary_url: ""
    binary_name: shuffledns
    install_dir: /usr/local/bin

  asnmap:
    enabled: false
    install_method: binary
    binary_url: https://github.com/projectdiscovery/asnmap/releases/download/v1.1.1/asnmap_1.1.1_linux_amd64.zip
    binary_name: asnmap
    install_dir: /usr/local/bin

  uncover:
    enabled: false
    install_method: binary
    binary_url: https://github.com/projectdiscovery/uncover/releases/download/v1.0.9/uncover_1.0.9_linux_amd64.zip
    binary_name: uncover
    install_dir: /usr/local/bin

Nginx Basic Auth

The frontend container uses Nginx to serve the React application and proxy /api/ requests to the API service. Docker Compose can enable an optional Nginx Basic Auth layer before the normal hxEASM login and RBAC flow. It is disabled by default.

HXEASM_NGINX_BASIC_AUTH_ENABLED=false
HXEASM_NGINX_BASIC_AUTH_HTPASSWD=
HXEASM_EXPOSE_MINIO_CONSOLE=false

To enable it, generate one htpasswd-compatible bcrypt line and store that line in the deployment environment:

htpasswd -nbB hxeasm 'strong-password'
HXEASM_NGINX_BASIC_AUTH_ENABLED=true
HXEASM_NGINX_BASIC_AUTH_HTPASSWD='hxeasm:$2y$05$...'

Only hashes should be stored here; do not use or commit plaintext passwords. When enabled, the frontend entrypoint writes the htpasswd line to /etc/nginx/.htpasswd, renders auth_basic directives into the Nginx config, and starts Nginx. If Basic Auth is enabled without an htpasswd line, the container exits instead of starting unprotected. The hash value is never printed by the entrypoint.

This is an additional reverse-proxy gate only. Existing hxEASM authentication, JWTs, API keys, and backend RBAC remain required. In the default Compose file only frontend Nginx publishes a host port. Basic Auth covers the app.localhost web app shell. /api/ remains protected by hxEASM Bearer tokens and backend RBAC, because Basic Auth and Bearer auth both use the HTTP Authorization header. s3.localhost is not Basic Auth protected by default so presigned file URLs do not require a second Basic Auth prompt.

Restart the frontend after changing these variables:

docker compose up -d --build frontend

Single-entrypoint hostnames

The default Compose stack exposes only frontend Nginx on 3000 and routes by hostname. Use http://app.localhost:3000 for the UI/API, http://s3.localhost:3000 for presigned S3 URLs, and optionally http://minio.localhost:3000 for the MinIO console when HXEASM_EXPOSE_MINIO_CONSOLE=true.


Docker Compose Persistent Data

The default Docker Compose deployment uses host bind mounts for runtime data:

Host path Container path Purpose
~/.hxeasm/data/postgres /var/lib/postgresql/data PostgreSQL database files
~/.hxeasm/data/redis /data Redis persistence
~/.hxeasm/data/minio /data MinIO objects, screenshots, evidence, reports
~/.hxeasm/data/nuclei /root Nuclei templates, cache, config
~/.hxeasm/data/backups (host only) Future backup exports

scripts/init-data-dirs.sh creates these directories and scripts/up.sh runs it automatically. Because these are bind mounts, docker compose down -v does not remove the runtime data. Remove data only by explicitly deleting files from ~/.hxeasm/data.

If an older deployment used Docker named volumes, copy data out of those volumes before deleting them; hxEASM does not automatically migrate named volume contents.


Docker Compose environment variables

In docker-compose.yml, settings are passed as environment variables. The YAML config file is mounted read-only for the worker.

services:
  api:
    environment:
      - EASM_SERVER_PORT=8080
      - EASM_DATABASE_DSN=postgres://easm:easm@postgres:5432/easm?sslmode=disable
      - EASM_REDIS_ADDR=redis:6379
      - EASM_JWT_SECRET=${JWT_SECRET:-change-me-in-production}
      - EASM_JWT_ACCESS_TOKEN_TTL=15
      - EASM_JWT_REFRESH_TOKEN_TTL=1
      - EASM_LOG_LEVEL=info
      - EASM_STORAGE_ENDPOINT=http://minio:9000
      - EASM_STORAGE_BUCKET=easm-files
      - EASM_STORAGE_ACCESS_KEY=easm
      - EASM_STORAGE_SECRET_KEY=${MINIO_ROOT_PASSWORD:-easm-password}
      - EASM_STORAGE_PUBLIC_BASE_URL=http://s3.localhost:3000
      - EASM_UPDATE_CENTER_URL=${EASM_UPDATE_CENTER_URL:-}
      - EASM_UPDATE_CENTER_APIKEY=${EASM_UPDATE_CENTER_APIKEY:-}
      - EASM_UPDATE_CENTER_PRODUCT=${EASM_UPDATE_CENTER_PRODUCT:-hxeasm}
      - EASM_UPDATE_CENTER_CHANNEL=${EASM_UPDATE_CENTER_CHANNEL:-stable}
      - EASM_UPDATE_CENTER_TIMEOUT_SECONDS=${EASM_UPDATE_CENTER_TIMEOUT_SECONDS:-10}

  frontend:
    environment:
      - HXEASM_NGINX_BASIC_AUTH_ENABLED=${HXEASM_NGINX_BASIC_AUTH_ENABLED:-false}
      - HXEASM_NGINX_BASIC_AUTH_HTPASSWD=${HXEASM_NGINX_BASIC_AUTH_HTPASSWD:-}
      - HXEASM_EXPOSE_MINIO_CONSOLE=${HXEASM_EXPOSE_MINIO_CONSOLE:-false}

  worker:
    environment:
      - EASM_DATABASE_DSN=postgres://easm:easm@postgres:5432/easm?sslmode=disable
      - EASM_REDIS_ADDR=redis:6379
      - EASM_LOG_LEVEL=info
      - EASM_STORAGE_ENDPOINT=http://minio:9000
      - EASM_STORAGE_BUCKET=easm-files
      - EASM_STORAGE_ACCESS_KEY=easm
      - EASM_STORAGE_SECRET_KEY=${MINIO_ROOT_PASSWORD:-easm-password}
      - EASM_STORAGE_PUBLIC_BASE_URL=http://s3.localhost:3000
      - EASM_TEST_MODE=false
    volumes:
      - ./backend/configs:/app/configs:ro   # tool config
      - ./hxresearch:/opt/hxeasm/hxresearch:ro

To set secrets from a .env file:

# .env
JWT_SECRET=your-very-long-random-secret-here
MINIO_ROOT_PASSWORD=change-this-minio-password
EASM_UPDATE_CENTER_URL=https://updates.example.com
EASM_UPDATE_CENTER_APIKEY=
HXEASM_NGINX_BASIC_AUTH_ENABLED=false
HXEASM_NGINX_BASIC_AUTH_HTPASSWD=
HXEASM_EXPOSE_MINIO_CONSOLE=false
docker compose --env-file .env up -d

Variable reference

Variable Config key Component
EASM_APP_VERSION app.version API
EASM_SERVER_HOST server.host API
EASM_SERVER_PORT server.port API
EASM_DATABASE_DSN database.dsn API, Worker
EASM_REDIS_ADDR redis.addr API, Worker
EASM_REDIS_PASSWORD redis.password API, Worker
EASM_REDIS_DB redis.db API, Worker
EASM_JWT_SECRET jwt.secret API
EASM_JWT_ACCESS_TOKEN_TTL jwt.access_token_ttl API
EASM_JWT_REFRESH_TOKEN_TTL jwt.refresh_token_ttl API
EASM_LOG_LEVEL log.level API, Worker
EASM_STORAGE_PROVIDER storage.provider API, Worker
EASM_STORAGE_ENDPOINT storage.endpoint API, Worker
EASM_STORAGE_REGION storage.region API, Worker
EASM_STORAGE_BUCKET storage.bucket API, Worker
EASM_STORAGE_ACCESS_KEY storage.access_key API, Worker
EASM_STORAGE_SECRET_KEY storage.secret_key API, Worker
EASM_STORAGE_USE_SSL storage.use_ssl API, Worker
EASM_STORAGE_PUBLIC_BASE_URL storage.public_base_url API, Worker
EASM_UPDATES_ENABLED updates.enabled API
EASM_UPDATE_CENTER_URL updates.center_url API
EASM_UPDATE_CENTER_APIKEY updates.api_key API
EASM_UPDATE_CENTER_PRODUCT updates.product API
EASM_UPDATE_CENTER_CHANNEL updates.channel API
EASM_UPDATE_CENTER_TIMEOUT_SECONDS updates.timeout_seconds API
EASM_UPDATES_CHECK_INTERVAL_MINUTES updates.check_interval_minutes API
EASM_UPDATES_CURRENT_VERSION updates.current_version API
EASM_RESEARCH_PATH research.path API, Worker
EASM_RESEARCH_NUCLEI_TEMPLATES_PATH research.nuclei_templates_path API, Worker
EASM_TEST_MODE (env only) Worker
HXEASM_NGINX_BASIC_AUTH_ENABLED (env only) Frontend Nginx
HXEASM_NGINX_BASIC_AUTH_HTPASSWD (env only) Frontend Nginx
HXEASM_EXPOSE_MINIO_CONSOLE (env only) Frontend Nginx

Scans Settings and Custom Profiles

Settings -> Scans exposes an admin-only profile and plugin management foundation. Config-backed profiles from backend/configs/config.yaml remain the default source of truth and are read-only in the UI. Admin-created custom profiles are stored in PostgreSQL and merged into the same runtime profile registry as config profiles.

The worker still executes one ordered plugin chain per scan. Before resolving a profile, it refreshes enabled custom profiles from the database, then runs plugins through the existing worker execution path.

Plugin settings are stored separately in plugin_settings and injected into PluginInput.Settings after validation against each plugin's declared option schema. Existing plugins ignore this field unless they explicitly support options.