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:
| Field | Type | Description |
|---|---|---|
service_id | string | Must match the service_id used in ingested telemetry |
tenant_id | string | Key for the tenant pillar. Empty string = not mapped |
product_id | string | Key for the product pillar. Empty string = not mapped |
custom_keys | map[string]string | Arbitrary 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
- Open Pillar Embeddings in the sidebar
- Click the Service Map tab
- 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.
- 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_expr | Required 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.