From 62b2b51b252c2ed02d6be5e6d1148e14bb94c57b Mon Sep 17 00:00:00 2001 From: Samuel Hassine Date: Sat, 30 May 2026 12:39:05 +0200 Subject: [PATCH 1/5] [docker] Bundle XTM One in the default stack Adds XTM One alongside OpenAEV in the default compose stack: - New redis service (required by XTM One) and a dedicated pgsql-xtm-one service (pgvector/pgvector:pg17) with its own credentials and volume, isolated from the OpenAEV pg cluster. - New xtm-one + xtm-one-worker services (published filigran images), reusing the existing minio. - xtm-one exposed on host port 8090; BASE_URL and FRONTEND_URL both resolve to the templated ${XTM_ONE_EXTERNAL_SCHEME}://${XTM_ONE_HOST}:${XTM_ONE_PORT}. - OpenAEV wired to XTM One via OPENAEV_XTM_ONE_URL / OPENAEV_XTM_ONE_TOKEN and the shared PLATFORM_REGISTRATION_TOKEN. - .env.sample documents the XTM ONE block and the mandatory-to-rotate PLATFORM_REGISTRATION_TOKEN. --- .env.sample | 30 ++++++++++++++ docker-compose.yml | 100 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 130 insertions(+) diff --git a/.env.sample b/.env.sample index 00e2e52..01d0870 100644 --- a/.env.sample +++ b/.env.sample @@ -33,6 +33,10 @@ IMAP_STARTTLS_ENABLE=false XTM_COMPOSER_ID=8215614c-7139-422e-b825-b20fd2a13a23 COMPOSE_PROJECT_NAME=xtm +# Shared secret used by OpenAEV to register itself with XTM One. +# The platforms sharing an XTM One instance MUST use the same value. +PLATFORM_REGISTRATION_TOKEN=ChangeMeWithGeneratedRandomString # [MANDATORY] Replace with a long random string (e.g. `openssl rand -hex 32`) + ########################### # OPENAEV # ########################### @@ -65,3 +69,29 @@ COLLECTOR_NVD_NIST_CVE_API_KEY= #Optionnal but recommended INJECTOR_NMAP_ID=76f8f4d6-9f6f-4e61-befc-48f735876a4a INJECTOR_NUCLEI_ID=e1bad898-9804-427d-99e4-dc32c5f2898d + +########################### +# XTM ONE # +########################### + +XTM_ONE_HOST=localhost +XTM_ONE_PORT=8090 +XTM_ONE_EXTERNAL_SCHEME=http +# Image tag for filigran/xtm-one and filigran/xtm-one-worker (e.g. rolling, testing-xtm-one, 1.260513.0) +XTM_ONE_VERSION=rolling +# Must match OPENAEV_ADMIN_EMAIL so XTM One's JWT email claim resolves to an +# existing user on the platform. +XTM_ONE_ADMIN_EMAIL=admin@openaev.io +XTM_ONE_ADMIN_PASSWORD=changeme +# Long random string (e.g. `openssl rand -hex 32`). Used to sign sessions/tokens. +XTM_ONE_SECRET_KEY=ChangeMeWithGeneratedRandomString +# Credentials for the dedicated pgsql-xtm-one Postgres instance (kept +# separate from the OpenAEV pg cluster). +XTM_ONE_POSTGRES_USER=xtmone +XTM_ONE_POSTGRES_PASSWORD=ChangeMe +# Optional: bucket name in MinIO (auto-created on first boot) +XTM_ONE_S3_BUCKET=xtm-one-files +# Optional: enterprise license PEM (leave empty in xtm_one mode) +XTM_ONE_ENTERPRISE_LICENSE= +XTM_ONE_LOG_LEVEL=info +XTM_ONE_LOG_FORMAT=json diff --git a/docker-compose.yml b/docker-compose.yml index a8a5464..784c75e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -17,6 +17,17 @@ services: timeout: 5s retries: 3 restart: always + redis: + # Used by XTM One. + image: redis:8.6.3 + restart: always + volumes: + - redisdata:/data + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 3 pgsql: image: postgres:17-alpine environment: @@ -166,6 +177,9 @@ services: - OPENAEV_MAIL_IMAP_SSL_TRUST=* - OPENAEV_MAIL_IMAP_STARTTLS_ENABLE=${IMAP_STARTTLS_ENABLE} - OPENAEV_WITH-PROXY=${OPENAEV_WITH_PROXY} + # XTM One + - OPENAEV_XTM_ONE_URL=http://xtm-one:4000 + - OPENAEV_XTM_ONE_TOKEN=${PLATFORM_REGISTRATION_TOKEN} ports: - "${OPENAEV_PORT}:8080" depends_on: @@ -266,9 +280,95 @@ services: openaev: condition: service_healthy restart: always + + ########################### + # XTM ONE # + ########################### + + pgsql-xtm-one: + # Dedicated pgvector-enabled instance for XTM One (kept separate from + # the OpenAEV pg cluster to isolate data and migrations). + image: pgvector/pgvector:pg17 + environment: + POSTGRES_USER: ${XTM_ONE_POSTGRES_USER} + POSTGRES_PASSWORD: ${XTM_ONE_POSTGRES_PASSWORD} + POSTGRES_DB: xtm_one + volumes: + - pgsqlxtmonedata:/var/lib/postgresql/data + restart: always + healthcheck: + test: [ "CMD", "pg_isready", "-U", "${XTM_ONE_POSTGRES_USER}", "-d", "xtm_one" ] + interval: 10s + timeout: 5s + retries: 5 + + xtm-one: + image: filigran/xtm-one:${XTM_ONE_VERSION:-rolling} + environment: + - PLATFORM_MODE=xtm_one + - PLATFORM_REGISTRATION_TOKEN=${PLATFORM_REGISTRATION_TOKEN} + - BASE_URL=${XTM_ONE_EXTERNAL_SCHEME}://${XTM_ONE_HOST}:${XTM_ONE_PORT} + - FRONTEND_URL=${XTM_ONE_EXTERNAL_SCHEME}://${XTM_ONE_HOST}:${XTM_ONE_PORT} + - ADMIN_EMAIL=${XTM_ONE_ADMIN_EMAIL} + - ADMIN_PASSWORD=${XTM_ONE_ADMIN_PASSWORD} + - SECRET_KEY=${XTM_ONE_SECRET_KEY} + - DATABASE_URL=postgresql+asyncpg://${XTM_ONE_POSTGRES_USER}:${XTM_ONE_POSTGRES_PASSWORD}@pgsql-xtm-one:5432/xtm_one + - REDIS_URL=redis://redis:6379 + - S3_ENDPOINT=minio:9000 + - S3_ACCESS_KEY=${MINIO_ROOT_USER} + - S3_SECRET_KEY=${MINIO_ROOT_PASSWORD} + - S3_BUCKET=${XTM_ONE_S3_BUCKET:-xtm-one-files} + - S3_USE_SSL=false + - LOG_LEVEL=${XTM_ONE_LOG_LEVEL:-info} + - LOG_FORMAT=${XTM_ONE_LOG_FORMAT:-json} + - ENTERPRISE_LICENSE=${XTM_ONE_ENTERPRISE_LICENSE:-} + # Internal API endpoint for the OpenAEV integration (Docker hostname) + - OPENAEV_API_URL=http://openaev:8080 + ports: + - "${XTM_ONE_PORT}:4000" + depends_on: + pgsql-xtm-one: + condition: service_healthy + redis: + condition: service_healthy + minio: + condition: service_healthy + restart: always + healthcheck: + test: ["CMD-SHELL", "python -c \"import urllib.request; urllib.request.urlopen('http://localhost:4000/api/health')\""] + interval: 15s + timeout: 10s + retries: 5 + start_period: 60s + + xtm-one-worker: + image: filigran/xtm-one-worker:${XTM_ONE_VERSION:-rolling} + environment: + - PLATFORM_MODE=xtm_one + - PLATFORM_REGISTRATION_TOKEN=${PLATFORM_REGISTRATION_TOKEN} + - ADMIN_EMAIL=${XTM_ONE_ADMIN_EMAIL} + - ADMIN_PASSWORD=${XTM_ONE_ADMIN_PASSWORD} + - SECRET_KEY=${XTM_ONE_SECRET_KEY} + - DATABASE_URL=postgresql+asyncpg://${XTM_ONE_POSTGRES_USER}:${XTM_ONE_POSTGRES_PASSWORD}@pgsql-xtm-one:5432/xtm_one + - REDIS_URL=redis://redis:6379 + - S3_ENDPOINT=minio:9000 + - S3_ACCESS_KEY=${MINIO_ROOT_USER} + - S3_SECRET_KEY=${MINIO_ROOT_PASSWORD} + - S3_BUCKET=${XTM_ONE_S3_BUCKET:-xtm-one-files} + - S3_USE_SSL=false + - LOG_LEVEL=${XTM_ONE_LOG_LEVEL:-info} + - LOG_FORMAT=${XTM_ONE_LOG_FORMAT:-json} + - ENTERPRISE_LICENSE=${XTM_ONE_ENTERPRISE_LICENSE:-} + depends_on: + xtm-one: + condition: service_healthy + restart: always + volumes: pgsqldata: s3data: amqpdata: esdata: rsakeys: + redisdata: + pgsqlxtmonedata: From cabd91dae36965e6cb4f09b2602970cdd918a441 Mon Sep 17 00:00:00 2001 From: Samuel Hassine Date: Sat, 30 May 2026 12:41:30 +0200 Subject: [PATCH 2/5] [docker] Use published xtmone/platform and xtmone/worker images The XTM One images are published on Docker Hub as xtmone/platform and xtmone/worker, not filigran/xtm-one(-worker). --- .env.sample | 2 +- docker-compose.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.env.sample b/.env.sample index 01d0870..0d58b92 100644 --- a/.env.sample +++ b/.env.sample @@ -77,7 +77,7 @@ INJECTOR_NUCLEI_ID=e1bad898-9804-427d-99e4-dc32c5f2898d XTM_ONE_HOST=localhost XTM_ONE_PORT=8090 XTM_ONE_EXTERNAL_SCHEME=http -# Image tag for filigran/xtm-one and filigran/xtm-one-worker (e.g. rolling, testing-xtm-one, 1.260513.0) +# Image tag for xtmone/platform and xtmone/worker (e.g. rolling, testing-xtm-one, 1.260513.0) XTM_ONE_VERSION=rolling # Must match OPENAEV_ADMIN_EMAIL so XTM One's JWT email claim resolves to an # existing user on the platform. diff --git a/docker-compose.yml b/docker-compose.yml index 784c75e..5809242 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -303,7 +303,7 @@ services: retries: 5 xtm-one: - image: filigran/xtm-one:${XTM_ONE_VERSION:-rolling} + image: xtmone/platform:${XTM_ONE_VERSION:-rolling} environment: - PLATFORM_MODE=xtm_one - PLATFORM_REGISTRATION_TOKEN=${PLATFORM_REGISTRATION_TOKEN} @@ -342,7 +342,7 @@ services: start_period: 60s xtm-one-worker: - image: filigran/xtm-one-worker:${XTM_ONE_VERSION:-rolling} + image: xtmone/worker:${XTM_ONE_VERSION:-rolling} environment: - PLATFORM_MODE=xtm_one - PLATFORM_REGISTRATION_TOKEN=${PLATFORM_REGISTRATION_TOKEN} From 1d09bc3ae7a8c6690ab9b6b83d5eb547f8a31c79 Mon Sep 17 00:00:00 2001 From: Samuel Hassine Date: Sat, 30 May 2026 13:00:48 +0200 Subject: [PATCH 3/5] [docker] Align admin emails to admin@filigran.io Use admin@filigran.io as the default admin email for OpenAEV and XTM One in .env.sample (with a default password) so the shared JWT email claim resolves on the platform. Admin emails live in .env.sample only; none are hard-coded in the compose file. Unify the XTM One comments to match the other docker repos. --- .env.sample | 17 ++++++++--------- docker-compose.yml | 3 +-- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/.env.sample b/.env.sample index 0d58b92..888eff2 100644 --- a/.env.sample +++ b/.env.sample @@ -33,8 +33,8 @@ IMAP_STARTTLS_ENABLE=false XTM_COMPOSER_ID=8215614c-7139-422e-b825-b20fd2a13a23 COMPOSE_PROJECT_NAME=xtm -# Shared secret used by OpenAEV to register itself with XTM One. -# The platforms sharing an XTM One instance MUST use the same value. +# Shared secret used to register the platforms with XTM One. +# All platforms sharing an XTM One instance MUST use the same value. PLATFORM_REGISTRATION_TOKEN=ChangeMeWithGeneratedRandomString # [MANDATORY] Replace with a long random string (e.g. `openssl rand -hex 32`) ########################### @@ -44,8 +44,8 @@ PLATFORM_REGISTRATION_TOKEN=ChangeMeWithGeneratedRandomString # [MANDATORY] Repl OPENAEV_HOST=localhost OPENAEV_PORT=8080 OPENAEV_EXTERNAL_SCHEME=http -OPENAEV_ADMIN_EMAIL= # ChangeMe@domain.com -OPENAEV_ADMIN_PASSWORD= # ChangeMe +OPENAEV_ADMIN_EMAIL=admin@filigran.io +OPENAEV_ADMIN_PASSWORD=changeme OPENAEV_ADMIN_TOKEN=00000000-0000-0000-0000-000000000000 # [MANDATORY] Replace with a valid UUIDv4 OPENAEV_HEALTHCHECK_KEY=ChangeMe OPENAEV_ADMIN_ENCRYPTION_KEY= # ChangeMe @@ -79,14 +79,13 @@ XTM_ONE_PORT=8090 XTM_ONE_EXTERNAL_SCHEME=http # Image tag for xtmone/platform and xtmone/worker (e.g. rolling, testing-xtm-one, 1.260513.0) XTM_ONE_VERSION=rolling -# Must match OPENAEV_ADMIN_EMAIL so XTM One's JWT email claim resolves to an -# existing user on the platform. -XTM_ONE_ADMIN_EMAIL=admin@openaev.io +# Must match the admin email of the connected platform(s) so XTM One's JWT +# email claim resolves to an existing user. +XTM_ONE_ADMIN_EMAIL=admin@filigran.io XTM_ONE_ADMIN_PASSWORD=changeme # Long random string (e.g. `openssl rand -hex 32`). Used to sign sessions/tokens. XTM_ONE_SECRET_KEY=ChangeMeWithGeneratedRandomString -# Credentials for the dedicated pgsql-xtm-one Postgres instance (kept -# separate from the OpenAEV pg cluster). +# Credentials for the dedicated pgsql-xtm-one Postgres instance. XTM_ONE_POSTGRES_USER=xtmone XTM_ONE_POSTGRES_PASSWORD=ChangeMe # Optional: bucket name in MinIO (auto-created on first boot) diff --git a/docker-compose.yml b/docker-compose.yml index 5809242..5e9ba97 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -286,8 +286,7 @@ services: ########################### pgsql-xtm-one: - # Dedicated pgvector-enabled instance for XTM One (kept separate from - # the OpenAEV pg cluster to isolate data and migrations). + # Dedicated pgvector-enabled instance for XTM One. image: pgvector/pgvector:pg17 environment: POSTGRES_USER: ${XTM_ONE_POSTGRES_USER} From 0551f7775d81e183ec91237ebfa1b933c7fe0856 Mon Sep 17 00:00:00 2001 From: Samuel Hassine Date: Sat, 30 May 2026 13:05:48 +0200 Subject: [PATCH 4/5] [docker] Pin XTM One images to latest Use xtmone/platform:latest and xtmone/worker:latest directly, matching the opencti/platform:latest convention, and drop the XTM_ONE_VERSION variable from .env.sample. --- .env.sample | 2 -- docker-compose.yml | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.env.sample b/.env.sample index 888eff2..e209166 100644 --- a/.env.sample +++ b/.env.sample @@ -77,8 +77,6 @@ INJECTOR_NUCLEI_ID=e1bad898-9804-427d-99e4-dc32c5f2898d XTM_ONE_HOST=localhost XTM_ONE_PORT=8090 XTM_ONE_EXTERNAL_SCHEME=http -# Image tag for xtmone/platform and xtmone/worker (e.g. rolling, testing-xtm-one, 1.260513.0) -XTM_ONE_VERSION=rolling # Must match the admin email of the connected platform(s) so XTM One's JWT # email claim resolves to an existing user. XTM_ONE_ADMIN_EMAIL=admin@filigran.io diff --git a/docker-compose.yml b/docker-compose.yml index 5e9ba97..7030f27 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -302,7 +302,7 @@ services: retries: 5 xtm-one: - image: xtmone/platform:${XTM_ONE_VERSION:-rolling} + image: xtmone/platform:latest environment: - PLATFORM_MODE=xtm_one - PLATFORM_REGISTRATION_TOKEN=${PLATFORM_REGISTRATION_TOKEN} @@ -341,7 +341,7 @@ services: start_period: 60s xtm-one-worker: - image: xtmone/worker:${XTM_ONE_VERSION:-rolling} + image: xtmone/worker:latest environment: - PLATFORM_MODE=xtm_one - PLATFORM_REGISTRATION_TOKEN=${PLATFORM_REGISTRATION_TOKEN} From 327fb371fa2ce9ded931ac9aca5f35b1ed99d5e1 Mon Sep 17 00:00:00 2001 From: Samuel Hassine Date: Sat, 30 May 2026 13:12:22 +0200 Subject: [PATCH 5/5] [docker] Address review: curl healthcheck for XTM One Use curl for the XTM One healthcheck (the xtmone/platform image ships curl, not wget), matching the HTTP-probe style of the other services. --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 7030f27..b49f7a1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -334,7 +334,7 @@ services: condition: service_healthy restart: always healthcheck: - test: ["CMD-SHELL", "python -c \"import urllib.request; urllib.request.urlopen('http://localhost:4000/api/health')\""] + test: ["CMD-SHELL", "curl -fsS http://localhost:4000/api/health || exit 1"] interval: 15s timeout: 10s retries: 5