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, requiresTRAEFIK_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:
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.
Verify
The dashboard's Health page reports each plugin's status. From the command line:
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¶
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, persistentollama_datavolume).spot-ollama-init; one-shot that pulls$EMBEDDING_MODELand any$OLLAMA_EXTRA_MODELS, then exits.ollama pullis idempotent, so re-running it is cheap.
Watch the first pull (≈ 1.5 GB for bge-m3):
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:
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:
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 IPapi.DOMAIN(e.g.,api.spot.example.com) -> Your server IPtraefik.DOMAIN(optional, e.g.,traefik.spot.example.com) -> Your server IP
Let's Encrypt will automatically obtain certificates for these domains.