Overview
The analytics layer exposes six reports. Each report is computed from the in-memory index on every request. No SDK calls occur after the initial fetch.
Reports
| Report | Endpoint | Purpose |
|---|---|---|
| Blast radius | GET /api/v1/analytics/blast-radius?id=&type= | Impact set of a connector group or server group. |
| Policy shadows | GET /api/v1/analytics/policy-shadows | Policy pairs covering the same (SCIM group, segment) pair. |
| Orphan clusters | GET /api/v1/analytics/orphan-clusters | Segments with no policy coverage, grouped by segment group. |
| Domain overlaps | GET /api/v1/analytics/domain-overlaps | Hostnames that appear in more than one segment. |
| Connector load | GET /api/v1/analytics/connector-load | Per-connector-group counts of policies, segments, and SCIM groups served. |
| SCIM reach | GET /api/v1/analytics/scim-reach | Per-SCIM-group counts of policies, segment groups, and segments granted. |
Common shape
Most reports use NamedRef:
type NamedRef struct { ID string `json:"id"` Name string `json:"name"`}When a referenced name cannot be resolved (deleted or stale reference),
Name falls back to ID. Entries are never dropped silently.
Refresh model
Reports run against the memoized *index.Index on *Server. A
background warmer (StartIndexWarmer) rebuilds the index every 4 minutes
on a 5-minute TTL, so handlers almost always hit a warm index.
singleflight coalesces concurrent rebuilds.
Each BuildIndex reads through the per-resource caches in
internal/fetcher. Each resource has its own TTL (5 minutes for volatile
resources, 1 hour for stable ones). Fetcher cache misses trigger an SDK
refetch on the request that hits the miss; concurrent callers after
expiry collapse to one fetch via double-checked locking.
POST /api/v1/refresh forces both layers to reload on demand (30-second
global throttle). See Architecture for layer
detail and Roadmap for remaining proactive-refresh
work at the fetcher layer.