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 at the same time, and it can appear in several 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 upserts: sending the same service_id again, in one request or across several, 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 groupings read the custom_keys map. The key name in the map must be the key the pillar groups by: a region pillar reads "region", an az pillar "az", a team pillar "team".
A service without that key in custom_keys is left out of that custom pillar (it still counts toward the tenant and product pillars).
Viewing the map
GET /api/v1/pillars/service-map?limit=1000
The Service Map tab of the Pillar Embeddings page shows the same list.
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.