From 2979d682acd44fb4f0dc798d65ef503e93a44a87 Mon Sep 17 00:00:00 2001 From: Alexis Zubiolo Date: Wed, 27 May 2026 14:16:27 +0200 Subject: [PATCH 1/3] feat: bundle XTM One in the default stack Adds XTM One alongside OpenAEV in the default compose: - New pgsql-copilot service (pgvector/pgvector:pg17) for XTM One's vector store, with dedicated credentials (kept separate from the OpenAEV pg cluster). - New redis service (required by XTM One for queues, cache, websockets) \u2014 OpenAEV itself does not need it. - New xtm-one + xtm-one-worker services on port 4000, sharing the existing minio. - PLATFORM_REGISTRATION_TOKEN shared secret plumbed into the openaev service (OPENAEV_XTM_ONE_URL / OPENAEV_XTM_ONE_TOKEN) and into XTM One (OPENAEV_* federation env vars). - .env.sample documents the new XTM ONE block. Refs XTM-One-Platform/xtm-one#1011 --- .env.sample | 27 ++++++++++++ docker-compose.yml | 102 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 129 insertions(+) diff --git a/.env.sample b/.env.sample index 00e2e52..9419412 100644 --- a/.env.sample +++ b/.env.sample @@ -65,3 +65,30 @@ 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 # +########################### + +# Shared secret used by OpenAEV and XTM One to authenticate registration. +# Both platforms MUST use the same value. +PLATFORM_REGISTRATION_TOKEN=xtm-default-registration-token + +XTM_ONE_HOST=localhost +XTM_ONE_PORT=4000 +XTM_ONE_EXTERNAL_SCHEME=http +# Image tag for filigran/xtm-one and filigran/xtm-one-worker (e.g. rolling, 1.x.y) +XTM_ONE_VERSION=rolling +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-copilot Postgres instance. +XTM_ONE_POSTGRES_USER=copilot +XTM_ONE_POSTGRES_PASSWORD=ChangeMe +# Optional: bucket name in MinIO (auto-created on first boot) +XTM_ONE_S3_BUCKET=copilot-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 4f827b3..438e531 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -31,6 +31,33 @@ services: interval: 10s timeout: 5s retries: 5 + pgsql-copilot: + # 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: copilot + volumes: + - pgsqlcopilotdata:/var/lib/postgresql/data + restart: always + healthcheck: + test: [ "CMD", "pg_isready", "-U", "${XTM_ONE_POSTGRES_USER}", "-d", "copilot" ] + interval: 10s + timeout: 5s + retries: 5 + redis: + # Required by XTM One (queues, cache, websockets). + image: redis:8.6.3 + restart: always + volumes: + - redisdata:/data + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 3 minio: image: minio/minio:latest volumes: @@ -166,6 +193,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 +296,81 @@ services: openaev: condition: service_healthy restart: always + + ########################### + # XTM ONE # + ########################### + + 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-copilot:5432/copilot + - 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:-copilot-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:-} + # OpenAEV federation + - OPENAEV_ENABLE=true + - OPENAEV_URL=${OPENAEV_EXTERNAL_SCHEME}://${OPENAEV_HOST}:${OPENAEV_PORT} + - OPENAEV_API_URL=http://openaev:8080 + - OPENAEV_TOKEN=${OPENAEV_ADMIN_TOKEN} + ports: + - "${XTM_ONE_PORT}:4000" + depends_on: + pgsql-copilot: + 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-copilot:5432/copilot + - 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:-copilot-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: + pgsqlcopilotdata: s3data: amqpdata: esdata: rsakeys: + redisdata: From 703dc03a7ac776097f8e127eb60df5bde63b295d Mon Sep 17 00:00:00 2001 From: Alexis Zubiolo Date: Thu, 28 May 2026 14:40:17 +0200 Subject: [PATCH 2/3] fix: align BASE_URL, add build context, fix admin email - Set OPENAEV_BASE-URL to internal hostname (http://openaev:8080) - Set BASE_URL to internal hostname (http://xtm-one:4000) - Add build directives for xtm-one and xtm-one-worker (context: ../xtm-one) - Align XTM_ONE_ADMIN_EMAIL with OPENAEV_ADMIN_EMAIL in .env.sample --- .env.sample | 9 +++++---- docker-compose.yml | 15 +++++++++++++-- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/.env.sample b/.env.sample index 9419412..9493f19 100644 --- a/.env.sample +++ b/.env.sample @@ -40,8 +40,8 @@ COMPOSE_PROJECT_NAME=xtm OPENAEV_HOST=localhost OPENAEV_PORT=8080 OPENAEV_EXTERNAL_SCHEME=http -OPENAEV_ADMIN_EMAIL= # ChangeMe@domain.com -OPENAEV_ADMIN_PASSWORD= # ChangeMe +OPENAEV_ADMIN_EMAIL=admin@openaev.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,8 +79,9 @@ XTM_ONE_PORT=4000 XTM_ONE_EXTERNAL_SCHEME=http # Image tag for filigran/xtm-one and filigran/xtm-one-worker (e.g. rolling, 1.x.y) XTM_ONE_VERSION=rolling -XTM_ONE_ADMIN_EMAIL=admin@filigran.io -XTM_ONE_ADMIN_PASSWORD=ChangeMe +# Must match OPENAEV_ADMIN_EMAIL so XTM One's JWT is accepted by OpenAEV. +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-copilot Postgres instance. diff --git a/docker-compose.yml b/docker-compose.yml index 438e531..a01e328 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -156,7 +156,9 @@ services: openaev: image: openaev/platform:latest environment: - - OPENAEV_BASE-URL=${OPENAEV_EXTERNAL_SCHEME}://${OPENAEV_HOST}:${OPENAEV_PORT} + # OPENAEV_BASE-URL is used as the JWT ``aud`` validation target. It MUST + # match what XTM One puts in the JWT audience claim (the internal URL). + - OPENAEV_BASE-URL=http://openaev:8080 - OPENAEV_AUTH-LOCAL-ENABLE=true - OPENAEV_ADMIN_EMAIL=${OPENAEV_ADMIN_EMAIL} - OPENAEV_ADMIN_PASSWORD=${OPENAEV_ADMIN_PASSWORD} @@ -302,11 +304,17 @@ services: ########################### xtm-one: + build: + context: ../xtm-one + dockerfile: Dockerfile 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} + # BASE_URL is used as the JWT ``iss`` claim AND as the JWKS host that + # peer platforms (OpenAEV) fetch keys from. It MUST be the internal + # Docker hostname so peers can verify tokens. + - BASE_URL=http://xtm-one:4000 - FRONTEND_URL=${XTM_ONE_EXTERNAL_SCHEME}://${XTM_ONE_HOST}:${XTM_ONE_PORT} - ADMIN_EMAIL=${XTM_ONE_ADMIN_EMAIL} - ADMIN_PASSWORD=${XTM_ONE_ADMIN_PASSWORD} @@ -344,6 +352,9 @@ services: start_period: 60s xtm-one-worker: + build: + context: ../xtm-one + dockerfile: Dockerfile.worker image: filigran/xtm-one-worker:${XTM_ONE_VERSION:-rolling} environment: - PLATFORM_MODE=xtm_one From cf9d789a25f1109b6fa02b2c236c5c87232fb40f Mon Sep 17 00:00:00 2001 From: Alexis Zubiolo Date: Thu, 28 May 2026 15:40:09 +0200 Subject: [PATCH 3/3] fix: use actuator health endpoint and add ES URI for health check - Switch healthcheck from /api/health to /actuator/health/ping (the custom endpoint returns 503 even when the platform is operational) - Add SPRING_ELASTICSEARCH_URIS so Spring's health indicator connects to the correct ES instance - Disable mail health indicator (no real SMTP in dev stack) - Add start_period=120s to give OpenAEV time to boot --- docker-compose.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index a01e328..e054c77 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -177,6 +177,7 @@ services: - OPENAEV_RABBITMQ_USER=${RABBITMQ_DEFAULT_USER} - OPENAEV_RABBITMQ_PASS=${RABBITMQ_DEFAULT_PASS} - ENGINE_URL=http://elasticsearch:9200 + - SPRING_ELASTICSEARCH_URIS=http://elasticsearch:9200 - SPRING_MAIL_HOST=${SMTP_HOST} - SPRING_MAIL_PORT=${SMTP_PORT} - SPRING_MAIL_USERNAME=${SMTP_USERNAME} @@ -195,6 +196,7 @@ services: - OPENAEV_MAIL_IMAP_SSL_TRUST=* - OPENAEV_MAIL_IMAP_STARTTLS_ENABLE=${IMAP_STARTTLS_ENABLE} - OPENAEV_WITH-PROXY=${OPENAEV_WITH_PROXY} + - MANAGEMENT_HEALTH_MAIL_ENABLED=false # XTM One - OPENAEV_XTM_ONE_URL=http://xtm-one:4000 - OPENAEV_XTM_ONE_TOKEN=${PLATFORM_REGISTRATION_TOKEN} @@ -211,10 +213,11 @@ services: condition: service_healthy restart: always healthcheck: - test: [ "CMD", "wget", "-qO-", "http://openaev:8080/api/health?health_access_key=${OPENAEV_HEALTHCHECK_KEY}" ] + test: [ "CMD", "wget", "-qO-", "http://openaev:8080/actuator/health/ping" ] interval: 10s timeout: 5s - retries: 20 + retries: 30 + start_period: 120s ########################### # OPENAEV COLLECTORS #