Skip to content

Federated

This example runs sutura for one user over two data systems. The metric recurring_revenue is in Postgres, and its dimension segment is in ClickHouse. DataHub is the catalog. One question reads both.

The example is in examples/federated:

Path Contents
conf/base.yaml The sutura settings
compose.yaml Postgres with TLS, ClickHouse, a live DataHub, and the catalog loader
postgres/init.sql The fact table fct_subscription_monthly
clickhouse/init.sql The dimension table dim_customer
loader/load.py The catalog that the loader writes into DataHub
setup.sh Writes the secrets of the local services to .env and .local

The rows are the CSV files of the single player example. All data is synthetic.

The settings

# sutura's settings for this example. One metric from Postgres, grouped by one dimension from
# ClickHouse. Both sources run as the deployment's own user, and DataHub says which platform is
# which source.
server:
  host: "127.0.0.1"
  port: 8080
security:
  identity: "single-user"
  single_user_because: "one operator reads both sources as the deployment's own user"
catalogs:
  # DataHub, read with one token for every caller.
  - name: "datahub"
    kind: "datahub"
    # Required for every catalog kind, and not read for DataHub.
    dir: "/unused-for-datahub"
    data_dir: "/unused-for-datahub"
    version: "example"
    endpoint: "http://127.0.0.1:8081"
    token_file: "/run/sutura/datahub-token"
    metric_property: "sutura"
    # A dataset on the `postgres` platform is read from the source `billing`, and one on the
    # `clickhouse` platform from the source `crm`.
    platforms:
      postgres: "billing"
      clickhouse: "crm"
sources:
  billing:
    kind: "postgres"
    host: "127.0.0.1"
    port: 5432
    database: "federated"
    user: "sutura"
    password_file: "/run/sutura/postgres-password"
    transport_mode: "verified"
    transport_anchors: "/run/sutura/postgres-ca.pem"
    posture: "shared-service-user"
  crm:
    kind: "clickhouse"
    host: "127.0.0.1"
    port: 8123
    user: "default"
    password_file: "/run/sutura/clickhouse-password"
    transport_mode: "plaintext"
    posture: "shared-service-user"

platforms maps the platform of each DataHub dataset to the source that serves it. The dataset fct_subscription_monthly is on the platform postgres, so sutura reads it from billing. The dataset dim_customer is on clickhouse, so sutura reads it from crm. Both sources run as the deployment's own user, and single_user_because records why.

The question

{
  "metrics": ["recurring_revenue"],
  "grain": "month",
  "range": { "start": "2026-06-01", "end": "2026-07-01" },
  "dimensions": ["segment"]
}

The metric is on the model postgres__fct_subscription_monthly. The dimension segment is on the model clickhouse__dim_customer, reached through the relationship subscription_customer. sutura reads the fact rows from Postgres and the dimension rows from ClickHouse, and joins them.

Run it

From examples/federated:

./setup.sh
docker compose up -d --wait postgres clickhouse datahub
docker compose run --rm federated-loader

The files in .local are the files that conf/base.yaml reads from /run/sutura: datahub-token, postgres-password, postgres-ca.pem and clickhouse-password. Every port of these services binds to 127.0.0.1 only.

Start sutura in a second terminal. It stays in the foreground. SUTURA_CONFIG_DIR names the directory that holds base.yaml, and the container reads the files in .local as its own user. It uses the host network, as in the single player example. The image tag ends in -musl: that image carries the Postgres driver, and the plain tag does not:

export SUTURA_VERSION=0.10.0
docker run --rm --network host --user "$(id -u):$(id -g)" \
  -v "$PWD/conf:/conf:ro" -v "$PWD/.local:/run/sutura:ro" -e SUTURA_CONFIG_DIR=/conf \
  ghcr.io/telekom/sutura:$SUTURA_VERSION-musl serve

Post the question:

curl -s -X POST http://127.0.0.1:8080/v1/query \
  -H 'content-type: application/json' \
  -d '{"metrics":["recurring_revenue"],"grain":"month","range":{"start":"2026-06-01","end":"2026-07-01"},"dimensions":["segment"]}'