Skip to main content

Service Pillar Map

The service pillar map is the lookup table that tells InfraSage which tenant, product, and custom dimensions each service belongs to. Without mappings, services are silently excluded from Pillar Embedding scoring.


Data Model

Each entry maps one service_id to:

FieldTypeDescription
service_idstringMust match the service_id used in ingested telemetry
tenant_idstringKey for the tenant pillar. Empty string = not mapped
product_idstringKey for the product pillar. Empty string = not mapped
custom_keysmap[string]stringArbitrary key/value pairs for custom pillars

A service can belong to a tenant and a product simultaneously. A service can appear in multiple custom dimensions at once.


Adding Mappings

Via UI

  1. Open Pillar Embeddings in the sidebar
  2. Click the Service Map tab
  3. Paste CSV lines into the bulk import box (one service per line):
payment-service, acme-corp, checkout
auth-service, acme-corp, platform
worker-svc, beta-inc,

Format: service_id, tenant_id, product_id

Leave product_id blank if the service should only be mapped to a tenant.

  1. Click Apply. Changes take effect on the next vectorizer tick (≤ 60 s).

Via API

Single or batch upsert

curl -X PUT https://api.infrasage.dev/api/v1/pillars/service-map \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"entries": [
{
"service_id": "payment-service",
"tenant_id": "acme-corp",
"product_id": "checkout",
"custom_keys": { "region": "ap-south-1", "team": "payments" }
},
{
"service_id": "auth-service",
"tenant_id": "acme-corp",
"product_id": "platform",
"custom_keys": { "region": "us-east-1", "team": "platform" }
},
{
"service_id": "worker-svc",
"tenant_id": "beta-inc",
"product_id": "",
"custom_keys": {}
}
]
}'

The API performs a batch upsert (insert or replace) using ClickHouse ReplacingMergeTree FINAL semantics. Sending the same service_id twice in one request or across multiple requests always keeps the latest values.

List current mappings

GET /api/v1/pillars/service-map?limit=500

Automation

At service deployment

Add a mapping call to your CI/CD pipeline or Helm chart post-install hook:

# Example: inject mapping from Kubernetes labels
SERVICE_ID=$(kubectl get deployment $DEPLOYMENT -o jsonpath='{.metadata.labels.app}')
TENANT_ID=$(kubectl get deployment $DEPLOYMENT -o jsonpath='{.metadata.labels.infrasage.dev/tenant}')
PRODUCT_ID=$(kubectl get deployment $DEPLOYMENT -o jsonpath='{.metadata.labels.infrasage.dev/product}')

curl -X PUT https://api.infrasage.dev/api/v1/pillars/service-map \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{\"entries\": [{\"service_id\": \"$SERVICE_ID\", \"tenant_id\": \"$TENANT_ID\", \"product_id\": \"$PRODUCT_ID\", \"custom_keys\": {}}]}"

Bulk import from a CMDB or service catalog

Export a CSV from your CMDB and use the API to load it:

# cmdb_export.csv: service_id,tenant_id,product_id,region
while IFS=',' read -r svc tenant product region; do
entries+="{\"service_id\":\"$svc\",\"tenant_id\":\"$tenant\",\"product_id\":\"$product\",\"custom_keys\":{\"region\":\"$region\"}},"
done < cmdb_export.csv

# Remove trailing comma and wrap
payload="{\"entries\":[${entries%,}]}"
curl -X PUT https://api.infrasage.dev/api/v1/pillars/service-map \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "$payload"

Custom Keys

Custom keys (custom_keys map) are used by custom pillars. The key name in the map must match the string argument in the pillar's key_expr:

Pillar key_exprRequired map key
mapElement(m.custom_keys, 'region')"region"
mapElement(m.custom_keys, 'az')"az"
mapElement(m.custom_keys, 'team')"team"

A service without the required key in custom_keys is excluded from that custom pillar's embedding (it contributes to tenant and product pillars normally).


ClickHouse Table

The service map is stored in infrasage_service_pillar_map:

CREATE TABLE infrasage_service_pillar_map (
service_id String,
tenant_id String,
product_id String,
custom_keys Map(String, String),
updated_at DateTime DEFAULT now()
) ENGINE = ReplacingMergeTree(updated_at)
ORDER BY service_id;

Reads use FINAL to deduplicate in-flight merges. For large tables (> 100 k rows) consider running a periodic OPTIMIZE TABLE ... FINAL to keep read latency low.


Viewing the Map

# Via API (paginated)
GET /api/v1/pillars/service-map?limit=1000

# Via ClickHouse directly
SELECT service_id, tenant_id, product_id, custom_keys
FROM infrasage_service_pillar_map FINAL
ORDER BY service_id;

Required RBAC Permission

PUT /api/v1/pillars/service-map requires the modify:thresholds permission (available to operator role and above). GET is available to all authenticated roles.