Skip to content

Configuration Reference

All configuration is done via environment variables in the .env file.

Required Settings

These must be set before first start:

Variable Description Example
DOMAIN Your domain name spot.example.com
ACME_EMAIL Email for Let's Encrypt admin@example.com
SECRET_KEY JWT signing key (32+ chars) Generate with command below
ENCRYPTION_KEY Fernet key for email encryption Generate with command below
POSTGRES_PASSWORD Database password Use strong password
RABBITMQ_PASSWORD Message queue password Use strong password

Generate keys:

# SECRET_KEY
python3 -c "import secrets; print(secrets.token_urlsafe(32))"

# ENCRYPTION_KEY
python3 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"

URLs

After deployment, your services will be available at:

  • Dashboard: https://DOMAIN (e.g., https://spot.example.com)
  • API: https://api.DOMAIN (e.g., https://api.spot.example.com)
  • Traefik Dashboard: https://traefik.DOMAIN (optional, requires TRAEFIK_DASHBOARD_AUTH)

Component Versions

Variable Description Default
CORE_VERSION API Gateway and orchestrators version See docker-compose.yml
DASHBOARD_VERSION Web dashboard version See docker-compose.yml

Database

Variable Description Default
POSTGRES_DB Database name spot
POSTGRES_USER Database user spot
POSTGRES_PASSWORD Database password None - Required

Redis

Variable Description Default
REDIS_PASSWORD Redis password (empty = no auth) Empty
REDIS_MAXMEMORY Max memory for Redis 256mb
REDIS_MAXMEMORY_POLICY Eviction policy allkeys-lru

RabbitMQ

Variable Description Default
RABBITMQ_USER RabbitMQ username spot
RABBITMQ_PASSWORD RabbitMQ password None - Required

Traefik / SSL

Variable Description Default
DOMAIN Base domain for services None - Required
ACME_EMAIL Let's Encrypt email None - Required
TRAEFIK_LOG_LEVEL Traefik log level INFO
TRAEFIK_DASHBOARD_AUTH Basic auth for Traefik dashboard Empty (disabled)

Generate dashboard auth:

htpasswd -nb admin yourpassword

Security

Variable Description Default
SECRET_KEY JWT signing key None - Required
ALLOWED_HOSTS Additional allowed hostnames Auto-includes api.DOMAIN

Email Storage

Variable Description Default
ENCRYPTION_KEY Fernet key for email encryption None - Required
EMAIL_RETENTION_DAYS Days to keep emails (0 = forever) 365

Without ENCRYPTION_KEY, the api-gateway and analyzer-orchestrator refuse to start.

Analyzers

Critical: Analyzers Must Be Installed

SPOT is an orchestration platform ; it coordinates analyzer plugins but does not include them. Without analyzers:

  • All analysis jobs will fail immediately
  • Dashboard will show failed jobs with errors like "No URL configured for analyzer"
  • No phishing detection can occur

You must install at least two analyzer plugins before SPOT can function.

Analyzers (and other plugins) are installed and configured through the dashboard, not via environment variables. See Installing plugins for the full flow.

Official analyzers

Plugin Description Repository
analyzer-nlp NLP analysis (DistilBERT, NER) analyzer-nlp
analyzer-llm LLM analysis (Ollama) analyzer-llm
analyzer-rules Rule-based contextual analysis analyzer-rules

Where the configuration lives

Installed analyzers are tracked in core/config/spot.yaml under plugins.analyzers.<id>. The dashboard writes this file when you install, enable, disable, or change settings. The orchestrator re-reads it per job, so no restart is needed.

plugins:
  analyzers:
    analyzer-nlp:
      enabled: true
      url: "http://analyzer-nlp:8000"
      settings: {}

Verify

The dashboard's Health page reports each plugin's status. From the command line:

docker compose logs analyzer-orchestrator | grep -i analyzer

Plugin source bootstrap

A fresh deployment seeds the public Docker Hub spotproject plugin source on first start so the dashboard catalog is populated out of the box, with no auth and no Git forge required.

Variable Description Default
SPOT_BOOTSTRAP_DEFAULT_PLUGIN_SOURCE Seed the Docker Hub source when the registry is empty true

The seed runs only when the source registry contains zero entries; once any source exists (added by the seed itself, or manually via the dashboard) the bootstrap is a permanent no-op. Deleting the default source from the UI does not trigger a re-seed on the next restart. Set to false for air-gapped deployments or operators using a custom source.

Plugin source types

A plugin source tells SPOT where to discover and download plugin images. Six types are supported, split into two families:

Type Family Lists images via Fetches OCI labels via
gitlab Git forge GitLab API configured registry
gitea Git forge Gitea / Forgejo API configured registry
dockerhub Registry hub.docker.com/v2/repositories/{ns}/ registry-1.docker.io
ghcr Registry api.github.com/{users\|orgs}/{ns}/packages ghcr.io
gitlab_registry Registry GitLab /registry/repositories configured registry
gitea_registry Registry Gitea /api/v1/packages/{owner} same host as URL

Forge sources also surface a Git "view source" link on each catalog card. Registry sources skip the forge entirely and recover the source URL from the org.opencontainers.image.source OCI label when the plugin's Dockerfile sets it (the bundled SPOT plugins all do).

GHCR requires a Personal Access Token with read:packages scope even for public packages — that's a GitHub quirk, not a SPOT requirement. The other public registries (Docker Hub, Codeberg) work anonymously for public namespaces.

The dashboard's Plugins → Sources → New form pre-fills the URL and registry when you pick a type; the same fields are accepted by the REST API at /api/v1/plugin-sources.

Mail Retrievers

Mail retrievers are plugins (mail_retriever kind) that ingest emails into the platform ; SMTP content_filter, future IMAP / Microsoft Graph, etc. They install and configure the same way as analyzers and context providers: from the dashboard's plugin catalog, not via environment variables.

The bundled official retriever is retriever-smtp, which integrates with an existing SMTP MTA (Postfix, exim, ...). See Mail integration for the end-to-end setup, mode selection (blocking vs tag-only), and the operational headers SPOT adds to delivered messages.

Where the configuration lives

Installed mail retrievers are tracked in core/config/spot.yaml under plugins.mail_retrievers.<id>. The dashboard writes this file when you install, enable, disable, or change settings. The orchestrator re-reads it per job, so no restart is needed.

plugins:
  mail_retrievers:
    retriever-smtp:
      enabled: true
      url: "http://retriever-smtp:8000"
      settings:
        MODE: "blocking"
        REINJECT_HOST: "postfix"

Compose profile

retriever-smtp ships under the optional mail-smtp compose profile so a default docker compose up -d doesn't try to start it without configuration. To enable it:

# /opt/spot/.env
COMPOSE_PROFILES=mail-smtp
RETRIEVER_SMTP_REINJECT_HOST=postfix.internal.example.com

The full list of RETRIEVER_SMTP_* variables (mode, listen bind, timeout policy, header prefix) is documented in Mail integration.

Knowledge Store (RAG layer)

The knowledge service embeds documents with Ollama. Operators have two choices ; point at an external Ollama, or enable the bundled side-car under a compose profile.

Variable Description Default
SPOT_INTERNAL_API_KEY Shared key used by context providers and analyzers to reach the knowledge service None - Required
OLLAMA_URL Ollama endpoint for embeddings http://ollama:11434
EMBEDDING_MODEL Model pulled and used for embeddings bge-m3
COMPOSE_PROFILES Comma-separated compose profiles. Set to ollama to enable the bundled side-car Empty
OLLAMA_EXTRA_MODELS Space-separated extra models to pre-pull on the bundled side-car (e.g. qwen2.5:1.5b) Empty

Option 1 ; External Ollama

# /opt/spot/.env
OLLAMA_URL=http://my-ollama-host:11434
EMBEDDING_MODEL=bge-m3

Make sure the model is pulled on that host: ollama pull bge-m3.

Option 2 ; Bundled side-car

# /opt/spot/.env
COMPOSE_PROFILES=ollama
# optional: pre-pull the AI-assistant model too
OLLAMA_EXTRA_MODELS=qwen2.5:1.5b

Then docker compose up -d starts:

  • spot-ollama ; the server (pinned image, persistent ollama_data volume).
  • spot-ollama-init ; one-shot that pulls $EMBEDDING_MODEL and any $OLLAMA_EXTRA_MODELS, then exits. ollama pull is idempotent, so re-running it is cheap.

Watch the first pull (≈ 1.5 GB for bge-m3):

docker compose logs -f ollama-init

Verifying

The knowledge page at https://$DOMAIN/knowledge renders a readiness banner listing each required component ; knowledge service, embedding backend, enabled context providers. Any red row explains how to fix the specific gap. The dashboard calls GET /api/v1/knowledge/readiness on the api-gateway for this; operators can curl it directly for scripting:

curl -sS -H "Authorization: Bearer $TOKEN" \
    https://api.$DOMAIN/api/v1/knowledge/readiness | jq

Email generator (Ollama-powered)

Separate from the Knowledge Store above ; this powers the dashboard's email generator at /generator, which produces synthetic phishing or benign emails on demand for workflow testing and training. It is not a chat assistant. Disabled by default; if OLLAMA_HOST is empty the generator page falls back to its built-in static templates and the AI generation form is hidden.

Re-uses the bundled side-car if enabled (set OLLAMA_HOST=http://ollama:11434), or points at any external Ollama. The model must be pulled on whichever instance you target ; docker exec spot-ollama ollama pull qwen2.5:1.5b, or add qwen2.5:1.5b to OLLAMA_EXTRA_MODELS so the bundled init job pre-pulls it.

Variable Description Default
OLLAMA_HOST Ollama server URL ; empty disables the AI generation form Empty (disabled)
OLLAMA_MODEL Model used for generation (small instruct models work well) qwen2.5:1.5b
OLLAMA_TIMEOUT Per-request timeout in seconds 180

Admin Bootstrap

A fresh database has zero users, so nobody can log in. The bootstrap runs once at api-gateway startup, creates the configured admin account, and lets you sign in to the dashboard. After that, all user management happens in the UI (or the /api/v1/users endpoints).

Variable Description Default
SPOT_BOOTSTRAP_ADMIN Enable admin creation on startup false
SPOT_ADMIN_USERNAME Admin username admin
SPOT_ADMIN_PASSWORD Admin password (min 16 chars, must contain upper, lower, digit) Empty - Required when bootstrap enabled
SPOT_ADMIN_EMAIL Admin email admin@example.com

When it runs

The bootstrap fires only when SPOT_BOOTSTRAP_ADMIN=true and the configured SPOT_ADMIN_USERNAME does not yet exist. If a user with the same username already exists ; whether created by a previous bootstrap or manually in the UI ; the bootstrap is a no-op and the existing password / role / email are left untouched. There is no "force reset" mode; to recover a lost admin password, use the operator-only password reset flow described in the admin runbook.

Password requirements

The api-gateway rejects passwords that don't meet the policy and the service will fail to start with password does not meet complexity requirements. Generate a compliant random password with:

python3 -c "import secrets, string; alphabet = string.ascii_letters + string.digits; print(''.join(secrets.choice(alphabet) for _ in range(24)))"

After first start

Once you have signed in and confirmed the admin account works, set SPOT_BOOTSTRAP_ADMIN=false and remove SPOT_ADMIN_PASSWORD from .env. Leaving the bootstrap enabled is harmless functionally (the existing user is preserved on subsequent starts) but it leaves a plain-text password in .env indefinitely. Apply the change with:

docker compose up -d api-gateway

System Update Notifications

The dashboard checks Codeberg daily for new deploy releases and surfaces an admin-only banner. Detection only ; see UPGRADING.md for the actual upgrade flow.

Variable Description Default
SPOT_UPDATE_CHECK_ENABLED Poll Codeberg and show the banner true
SPOT_DEPLOY_VERSION Set internally by docker-compose.yml to the running deploy version. Do not override. unknown

Local customisations: docker-compose.override.yml

docker-compose.yml is shipped by the deploy repo and rewritten on every git pull. Editing it means losing your changes on the next upgrade ; and a stale local copy will block git pull with a merge conflict.

For all stack customisations (extra services, volumes, ports, resource limits), use docker-compose.override.yml next to docker-compose.yml. Compose merges it automatically, and the deploy repo does not track it.

# docker-compose.override.yml ; operator-owned, not in git
services:
  api-gateway:
    environment:
      MY_EXTRA_SETTING: "value"
    volumes:
      - /opt/spot/custom:/opt/custom

Apply: docker compose up -d. Inspect the merged config: docker compose config.

The System Updates page will warn (red banner) if the local docker-compose.yml differs from the upstream copy at the running version ; that's the helper telling you to move customisations into docker-compose.override.yml before upgrading.

Logging

Variable Description Default
LOG_LEVEL Log level (DEBUG, INFO, WARNING, ERROR) INFO

DNS Requirements

Ensure these DNS records point to your server:

  • DOMAIN (e.g., spot.example.com) -> Your server IP
  • api.DOMAIN (e.g., api.spot.example.com) -> Your server IP
  • traefik.DOMAIN (optional, e.g., traefik.spot.example.com) -> Your server IP

Let's Encrypt will automatically obtain certificates for these domains.