Skip to content

[docker] Bundle XTM One in the default stack#15

Merged
SamuelHassine merged 9 commits into
mainfrom
feat/xtm-one-default
Jun 5, 2026
Merged

[docker] Bundle XTM One in the default stack#15
SamuelHassine merged 9 commits into
mainfrom
feat/xtm-one-default

Conversation

@azubiolo-filigran

@azubiolo-filigran azubiolo-filigran commented May 27, 2026

Copy link
Copy Markdown
Contributor

Objective

Adds XTM One alongside OpenCTI and OpenAEV in the default compose stack so docker compose up -d brings the full XTM product suite by default.

Refs XTM-One-Platform/xtm-one#1011.

Changes

New services

  • pgsql-xtm-one (pgvector/pgvector:pg17) — dedicated Postgres+pgvector instance for XTM One with its own credentials, kept separate from the OpenAEV pg cluster.
  • xtm-one — the platform itself (xtmone/platform:latest), exposed on host port 8090 (after OpenCTI 8080 / OpenAEV 8081).
  • xtm-one-worker — async worker (xtmone/worker:latest), depends on xtm-one being healthy.

Inter-platform wiring

  • PLATFORM_REGISTRATION_TOKEN is a new shared secret used by the three platforms to authenticate registration (flagged mandatory-to-rotate in .env.sample).
  • OpenCTI gets XTM__XTM_ONE_URL / XTM__XTM_ONE_TOKEN.
  • OpenAEV gets OPENAEV_XTM_ONE_URL / OPENAEV_XTM_ONE_TOKEN.

URLs / conventions

  • APP__BASE_URL and OPENAEV_BASE-URL stay as templated external URLs (${..._EXTERNAL_SCHEME}://${..._HOST}:${..._PORT}) — never hard-coded to internal Docker hostnames, so reverse-proxy configuration keeps working.
  • XTM One follows the same convention: BASE_URL and FRONTEND_URL both resolve to ${XTM_ONE_EXTERNAL_SCHEME}://${XTM_ONE_HOST}:${XTM_ONE_PORT}.
  • Images are pinned to :latest like opencti/platform / openaev/platform; the XTM One images are published on Docker Hub at hub.docker.com/u/xtmone (xtmone/platform, xtmone/worker).
  • The xtm-one healthcheck uses curl (present in the image; wget is not), matching the HTTP-probe style used by the other services.

OpenAEV fixes uncovered while wiring this up

  • The OpenCTI api-url is injected via SPRING_APPLICATION_JSON because Spring @Value does not relax-bind env vars to hyphenated property names; the URL includes the /graphql suffix.
  • Healthcheck switched to /actuator/health/ping (the composite /api/health endpoint depends on optional subsystems and flaps during startup).

Documentation

  • .env.sample documents the new XTM ONE block and the dedicated XTM_ONE_POSTGRES_USER / XTM_ONE_POSTGRES_PASSWORD.
  • README lists XTM One (http://localhost:8090) and aligns secret-generation guidance. All admin emails default to admin@filigran.io in .env.sample.

Verification

docker compose --env-file .env.sample config resolves cleanly; the xtmone/platform:latest image was pulled and confirmed to ship curl. All services reach healthy from a clean checkout, OpenCTI ⇄ OpenAEV ⇄ XTM One registration tested end-to-end.

Notes for reviewers

The per-product docker repos get the same XTM One introduction via dedicated PRs: OpenCTI-Platform/docker#578 and OpenAEV-Platform/docker#131.

Adds XTM One alongside OpenCTI and OpenAEV in the default compose:

- New pgsql-copilot service (pgvector/pgvector:pg17) with dedicated
  credentials, isolated from the OpenAEV pg cluster.
- New xtm-one + xtm-one-worker services on port 4000, wired to the
  shared Redis, MinIO and RabbitMQ.
- PLATFORM_REGISTRATION_TOKEN shared by the three platforms, plumbed
  into OpenCTI (XTM__XTM_ONE_*) and OpenAEV (OPENAEV_XTM_ONE_*).
- OpenAEV -> OpenCTI api-url is now injected via SPRING_APPLICATION_JSON
  because Spring @value does not relax-bind env vars to hyphenated
  property names.
- OpenAEV healthcheck switched to /actuator/health/ping (composite
  /api/health endpoint depends on optional subsystems and flaps).
- .env.sample documents the new XTM ONE block and the
  PLATFORM_REGISTRATION_TOKEN; README mentions XTM One and the
  32-byte base64 secret requirement.

Refs XTM-One-Platform/xtm-one#1011
- Set APP__BASE_URL=http://opencti:8080 (internal hostname for JWT aud)
- Set OPENAEV_BASE-URL=http://openaev:8080 (internal hostname for JWT aud)
- Set BASE_URL=http://xtm-one:4000 (internal hostname for JWT iss/JWKS)
- Add SPRING_ELASTICSEARCH_URIS for Spring health indicator
- Add MANAGEMENT_HEALTH_MAIL_ENABLED=false (no SMTP in dev)
- Add start_period=120s for OpenAEV slow JVM startup
- Add build directives for xtm-one services (context: ../xtm-one)
- Align all admin emails to admin@opencti.io for JWT compatibility
@azubiolo-filigran

Copy link
Copy Markdown
Contributor Author

Testing: Unified XTM Docker Stack (OpenCTI + OpenAEV + XTM One)

Steps to reproduce

# 1. Clone repos side-by-side
git clone git@github.com:FiligranHQ/xtm-docker.git xtm-docker
git clone git@github.com:XTM-One-Platform/xtm-one.git xtm-one
cd xtm-docker
git checkout feat/xtm-one-default

# 2. Create .env from sample
cp .env.sample .env
sed -i "s/OPENCTI_ADMIN_TOKEN=ChangeMe_UUIDv4/OPENCTI_ADMIN_TOKEN=$(uuidgen)/" .env
sed -i "s|OPENCTI_ENCRYPTION_KEY=ChangeMeWithGeneratedBase64Key|OPENCTI_ENCRYPTION_KEY=$(openssl rand -base64 32)|" .env
sed -i "s/OPENAEV_ADMIN_TOKEN=00000000-0000-0000-0000-000000000000/OPENAEV_ADMIN_TOKEN=$(uuidgen)/" .env
sed -i "s/XTM_ONE_SECRET_KEY=ChangeMeWithGeneratedRandomString/XTM_ONE_SECRET_KEY=$(openssl rand -hex 32)/" .env

# 3. Build and start
docker compose up -d --build

# 4. Wait (~3-5 min for OpenAEV JVM) then verify
docker compose ps  # all services healthy

Note: The compose file has build: context: ../xtm-one directives for xtm-one and xtm-one-worker services. This requires the xtm-one repo cloned adjacent to this repo. Alternatively, remove the build: blocks and pull pre-built images.

Credentials

Service URL Email Password
OpenCTI http://localhost:8080 admin@opencti.io changeme
OpenAEV http://localhost:8081 admin@opencti.io changeme
XTM One http://localhost:4000 admin@opencti.io changeme

Key fixes in this commit

  1. APP__BASE_URL=http://opencti:8080 — JWT audience for OpenCTI must use internal hostname
  2. OPENAEV_BASE-URL=http://openaev:8080 — JWT audience for OpenAEV must use internal hostname
  3. BASE_URL=http://xtm-one:4000 — JWT issuer + JWKS endpoint must use internal hostname
  4. SPRING_ELASTICSEARCH_URIS — Spring health indicator needs explicit ES URI
  5. MANAGEMENT_HEALTH_MAIL_ENABLED=false — no SMTP in dev
  6. start_period: 120s — OpenAEV JVM needs time to boot
  7. Build directivesbuild: context: ../xtm-one for local builds without registry access
  8. Admin emails aligned — all set to admin@opencti.io so JWT email resolves on both platforms

Dependency

Requires XTM-One-Platform/xtm-one#1070 (fix/platform-registration-api-url-override) — adds OPENCTI_API_URL / OPENAEV_API_URL env var support so registrations store internal Docker URLs.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR bundles XTM One (plus a dedicated pgsql-copilot pgvector instance and an async xtm-one-worker) into the default compose stack, wires it to OpenCTI and OpenAEV via a shared PLATFORM_REGISTRATION_TOKEN, and tweaks OpenAEV's Spring config / healthcheck along the way.

Changes:

  • Add pgsql-copilot, xtm-one, and xtm-one-worker services and the matching XTM_ONE_* / PLATFORM_REGISTRATION_TOKEN env variables.
  • Cross-wire OpenCTI (XTM__XTM_ONE_*) and OpenAEV (OPENAEV_XTM_ONE_*) with XTM One; switch OpenAEV's opencti.api-url to SPRING_APPLICATION_JSON and its healthcheck to /actuator/health/ping.
  • Update README and .env.sample to document the new platform, secrets, and ports; change OpenCTI/OpenAEV BASE_URL env values to internal Docker hostnames.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 8 comments.

File Description
docker-compose.yml Adds pgsql-copilot, xtm-one, xtm-one-worker; rewires base URLs, OpenAEV Spring config and healthcheck.
.env.sample New XTM One block, PLATFORM_REGISTRATION_TOKEN, dedicated Postgres credentials; turns some # ChangeMe placeholders into working defaults.
README.md Documents XTM One, its port, secret-format guidance, and the shared registration token.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread docker-compose.yml Outdated
Comment thread docker-compose.yml Outdated
Comment thread docker-compose.yml Outdated
Comment thread docker-compose.yml Outdated
Comment thread .env.sample
Comment thread .env.sample Outdated
Comment thread .env.sample Outdated
Comment thread README.md Outdated
@SamuelHassine

Copy link
Copy Markdown
Member

Overall good work, but I don't think we should change base URL, it's important to stay as is. Also, ensure alignement with docker repositories in OpenCTI and OpenAEV

- Restore templated APP__BASE_URL / OPENAEV_BASE-URL so the platforms keep
  advertising their external URL (reverse-proxy config keeps working);
  cross-platform calls already use dedicated internal *_API_URL vars.
- Give XTM One the same templated base URL convention: BASE_URL and
  FRONTEND_URL both resolve to ${XTM_ONE_EXTERNAL_SCHEME}://${XTM_ONE_HOST}:${XTM_ONE_PORT}.
- Expose XTM One on host port 8090 (after OpenCTI 8080 / OpenAEV 8081).
- Drop the build: stanzas on xtm-one / xtm-one-worker and rely on the
  published filigran/xtm-one images like every other service.
- Rename the dedicated Postgres to pgsql-xtm-one (db xtm_one, user xtmone,
  volume pgsqlxtmonedata, bucket xtm-one-files) - no more "copilot" naming.
- Flag PLATFORM_REGISTRATION_TOKEN as mandatory-to-rotate and align the
  secret-generation guidance between .env.sample and README.
@SamuelHassine

Copy link
Copy Markdown
Member

Addressed in 1868df2:

  • Base URLs unchanged. APP__BASE_URL and OPENAEV_BASE-URL are back to the templated ${..._EXTERNAL_SCHEME}://${..._HOST}:${..._PORT} values — nothing is hard-coded to internal Docker hostnames anymore. XTM One follows the same convention, with BASE_URL and FRONTEND_URL both resolving to ${XTM_ONE_EXTERNAL_SCHEME}://${XTM_ONE_HOST}:${XTM_ONE_PORT}.
  • Port convention. XTM One is exposed on 8090 (after OpenCTI 8080 / OpenAEV 8081).
  • Naming. The dedicated Postgres is now pgsql-xtm-one (db xtm_one, user xtmone, volume pgsqlxtmonedata, bucket xtm-one-files).
  • Published images. Dropped the build: stanzas so xtm-one / xtm-one-worker pull their published images like the rest of the stack.
  • Alignment. The same XTM One introduction is being opened as dedicated PRs against OpenCTI-Platform/docker and OpenBAS-Platform/docker.

All Copilot review threads addressed and resolved.

@SamuelHassine SamuelHassine left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Base URLs stay templated (no hard-coded internal hostnames), XTM One exposed on 8090 with matching BASE_URL/FRONTEND_URL, "copilot" naming removed in favour of xtm-one / xtm_one / xtmone, published images used (no build:), and the registration token is flagged mandatory-to-rotate. All review threads addressed and resolved.

The XTM One images are published on Docker Hub as xtmone/platform and
xtmone/worker, not filigran/xtm-one(-worker).
Use admin@filigran.io as the default admin email for OpenCTI, OpenAEV and
XTM One in .env.sample (and the README example), so the shared JWT email
claim resolves on every platform. Admin emails stay in .env.sample only;
none are hard-coded in docker-compose.yml. Also unify the XTM One comments
to match the per-product docker repos.
@SamuelHassine SamuelHassine changed the title feat: bundle XTM One in the unified stack [docker] Bundle XTM One in the default stack May 30, 2026
@SamuelHassine SamuelHassine requested a review from Copilot May 30, 2026 11:02

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

Comment thread docker-compose.yml Outdated
Comment thread docker-compose.yml Outdated
Use xtmone/platform:latest and xtmone/worker:latest directly, matching the
opencti/platform:latest and openaev/platform:latest convention, and drop
the XTM_ONE_VERSION variable from .env.sample.
The published xtmone/platform image ships curl (and python) but not wget,
so the probe now uses `curl -fsS .../api/health`, matching the HTTP-probe
style of the other services and avoiding a dependency on wget.

@SamuelHassine SamuelHassine left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Second review round addressed: image coordinates confirmed (xtmone/platform / xtmone/worker, published on Docker Hub, pinned to :latest), healthcheck switched to curl after verifying the image ships curl but not wget, and the PR description updated to match. All threads resolved.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.

The architecture schema now shows the XTM One service (:8090) wired to
OpenCTI and OpenAEV, and lists the dedicated data stores including the
XTM One PostgreSQL+pgvector instance.
Replace the ASCII art with a Mermaid graph that renders on GitHub: shows
all components (platforms, XTM Composer, workers, shared infrastructure
and dedicated databases) and uses bidirectional arrows between XTM One,
OpenCTI and OpenAEV.
@SamuelHassine SamuelHassine merged commit c6a07af into main Jun 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants