Dashboard Data Flow

Overview

The Dashboard Data Flow describes how the Stella Ops Console (Web UI) assembles a security-posture overview and presents it to users. The dashboard provides visibility into vulnerability counts, advisory-feed health, environment topology, and personal activity across the tenant’s managed assets.

Reconciliation note (verified against src/Web on 2026-05-30). The dashboard is composed client-side, not by a single server-side aggregation endpoint. The Angular DashboardV3Component (src/Web/StellaOps.Web/src/app/features/dashboard-v3/dashboard-v3.component.ts) fans out to several independent backend APIs from the browser and merges the results into a persona-aware “Mission Board”. There is no GET /api/v1/dashboard endpoint and no Platform-side fan-out (/internal/stats, /internal/verdicts/summary, etc.) backing the live dashboard — those were never implemented. A separate GET /api/v1/dashboard/summary Pack-adapter endpoint does exist on the Platform service, but it returns hardcoded demo data (IsDemo: true) and the Console does not consume it (see Pack-Adapter Dashboard Summary).

Business Value: Operators gain immediate visibility into their security posture without querying multiple systems, with the layout and lenses tailored to the signed-in user’s role (persona).

Actors

ActorTypeRole
UserHumanViews the persona-aware Mission Board, follows links to deep-dive surfaces
Console (Web UI)SystemComposes the dashboard client-side; calls multiple APIs in parallel and merges results
GatewayServiceRoutes, authenticates (JWT), and stamps identity headers/envelopes
Findings SecurityServiceProvides the tenant/scoped production risk aggregate (GET /api/risk/aggregated-status)
Concelier / Excititor (Advisory Sources)ServiceProvides advisory & VEX source status (GET /api/v1/advisory-sources/status)
Platform ContextServiceProvides regions, environments, and scope preferences (/api/v2/context/*)
Notify / NotifierServiceProvides the personal activity feed (developer persona)

Prerequisites

Flow Diagram

┌─────────────────────────────────────────────────────────────────────────────────┐
│                  Dashboard Data Flow (client-side composition)                   │
└─────────────────────────────────────────────────────────────────────────────────┘

┌────┐      ┌──────────────────────┐      ┌─────────┐      ┌──────────────────────────┐
│User│      │ Console (DashboardV3) │      │ Gateway │      │ Backend APIs             │
└─┬──┘      └──────────┬───────────┘      └────┬────┘      └────────────┬─────────────┘
  │  Open Dashboard    │                       │                        │
  │───────────────────>│                       │                        │
  │                    │ Resolve active persona │                        │
  │                    │ from auth.scopes()     │                        │
  │                    │───────┐                │                        │
  │                    │<──────┘                │                        │
  │                    │                        │                        │
  │                    │ (parallel client calls; Bearer JWT + X-StellaOps-TenantId)
  │                    │                        │                        │
  │                    │ GET /api/v2/context/regions ─────────────────>  │ Platform Context
  │                    │ GET /api/v2/context/preferences ─────────────>  │ Platform Context
  │                    │ GET /api/v2/context/environments ────────────>  │ Platform Context
  │                    │ GET /api/risk/aggregated-status ─────────────>  │ Findings Security
  │                    │ GET /api/v1/advisory-sources/status ─────────>  │ Advisory Sources
  │                    │ GET (notifications refresh) ─────────────────>  │ Notify
  │                    │                        │                        │
  │                    │<──── responses (each lens loads independently) ─│
  │                    │                        │                        │
  │ Render lens grid   │ Merge into signals;    │                        │
  │ (persona-filtered) │ compute derived counts │                        │
  │<───────────────────│                        │                        │

Step-by-Step

1. User Opens Dashboard

2. Authentication & Persona Resolution

3. Client-Side Data Fan-Out

On init (ngOnInit), the component issues parallel calls directly from the browser. Each lens loads and degrades independently — there is no single aggregate request.

DataSource serviceEndpoint (browser → gateway)Client
RegionsPlatform ContextGET /api/v2/context/regionsPlatformContextStore
Scope preferencesPlatform ContextGET /api/v2/context/preferences (PUT to persist)PlatformContextStore
EnvironmentsPlatform ContextGET /api/v2/context/environments?regions=…PlatformContextStore
Vulnerability postureFindings SecurityGET /api/risk/aggregated-statusRiskHttpClient.getAggregatedStatus()
Advisory/VEX feed statusAdvisory SourcesGET /api/v1/advisory-sources/statusSourceManagementApi.getStatus()
Personal activity feedNotifynotifications store refresh (developer persona)NotificationsStore

Notes grounded in source:

4. Lens Composition & Derived Values

The component holds the responses in signals and computes derived values for the persona-filtered lens grid (visibleLensCards()selectLensCards(registry, persona, scopes)):

When the tenant has no environments, the dashboard shows an empty-install “Bootstrap Release Control” setup guide instead of populated risk lenses.

5. Response Shapes (real contracts)

Vulnerability posture — GET /api/risk/aggregated-status (TypeScript models AggregatedRiskStatus / FindingsPostureStats in src/Web/StellaOps.Web/src/app/core/api/risk.models.ts):

interface FindingsPostureStats {
  total: number;
  bySeverity: Record<'critical' | 'high' | 'medium' | 'low' | 'unknown', number>;
  criticalOpen: number;
  computedAt: string; // aggregate generation time; nullable `asOf` carries source currency
  traceId: string;
}

Advisory/VEX feed status — GET /api/v1/advisory-sources/status (model SourceStatusResponse in source-management.api.ts):

interface SourceStatusResponse {
  sources: Array<{
    sourceId: string;
    enabled: boolean;
    lastCheck?: { isHealthy: boolean; status: string; checkedAt: string; /* … */ } | null;
    syncSupported?: boolean;
    syncState?: string;
    readyForSync?: boolean;
  }>;
}

Platform context — /api/v2/context/{regions,environments,preferences} (models in platform-context.store.ts): PlatformContextRegion[], PlatformContextEnvironment[], and PlatformContextPreferences.

Data Contracts

Request Headers (every lens call)

HeaderRequiredDescription
AuthorizationYesBearer JWT (attached by the auth interceptor)
X-StellaOps-TenantIdYesTenant identifier (constant StellaOpsHeaders.Tenant)
X-StellaOps-TraceIdNoObservability trace id (stamped by VulnerabilityHttpClient)
X-StellaOps-RequestIdNoPer-request id (stamped by VulnerabilityHttpClient)
Content-TypeOn POST/PUTapplication/json

Pack-Adapter Dashboard Summary (NOT the live source)

The Platform service exposes GET /api/v1/dashboard/summary (src/Platform/StellaOps.Platform.WebService/Endpoints/PackAdapterEndpoints.cs) guarded by the HealthRead policy. This is a Pack v2 demo projection: it returns a fixed snapshot wrapped with IsDemo: true and hardcoded values (CVE-2026-1234, us-prod, etc.). The live Console dashboard does not call it. Its DTO (for reference only) is:

record DashboardSummaryDto(
    DataConfidenceBadgeDto DataConfidence,
    int EnvironmentsWithCriticalReachable,
    int TotalCriticalReachable,
    decimal SbomCoveragePercent,
    decimal VexCoveragePercent,
    int BlockedApprovals,
    int ExceptionsExpiringSoon,
    IReadOnlyList<EnvironmentRiskSnapshotDto> EnvironmentRisk,
    IReadOnlyList<DashboardDriverDto> TopDrivers);

NOT IMPLEMENTED (was orphaned doc content). Earlier revisions of this flow described a single GET /api/v1/dashboard aggregation endpoint returning a snake_case DTO with summary (total_images, images_scanned_24h, critical_vulns, policy_violations), trends (vuln_trend_7d, scan_volume_7d), top_vulns, and policy_status. No such endpoint or DTO exists in src/. The live dashboard has no trend series, no top_vulns aggregation, and no policy_status block; those fields were never built. Treat the old contract as roadmap/aspirational, not current behavior.

Error Handling

Because the dashboard is composed of independent client calls, failures are isolated per lens rather than failing the whole page.

ConditionObserved behavior (per source)
Invalid/expired JWTAuth interceptor / guard routes to the Authority login flow
getStats() errors (any status)Loader catches → vulnStats = null; risk/vuln lenses show an empty/loading state
Advisory source status errorsLoader catches → empty source list; feed lens + health dot reflect “needs attention”
Context regions/preferences errorStore continues with empty regions/defaults so the app stays usable
Context environments errorStore records error; environment lenses show empty state
No environments for tenantDashboard renders the “Bootstrap Release Control” setup guide

The previous HTTP-status recovery table (401 → login, 404 → tenant selection, 504 → partial dashboard with stale-data indicator, 429 → backoff) assumed a single aggregation endpoint and a stale-data indicator that do not exist. It has been replaced with the per-lens behavior above.

Observability

NOT VERIFIED IN SOURCE. The metrics, trace spans, and structured log events below (dashboard_request_total, dashboard_latency_seconds, dashboard_module_latency_seconds, a dashboard-request server span with per-module child spans, and dashboard.* log events) describe a server-side aggregation service that was never implemented. No such telemetry exists for the client-composed dashboard. The closest real signal is the client-side debug log emitted by VulnerabilityHttpClient.logRequest() (console.debug('[VulnHttpClient]', method, path, statusCode, durationMs)), which records each vuln API call. Keep this section as a forward-looking target only.

(Roadmap) Metrics

MetricTypeLabels
dashboard_request_totalCountertenant, status
dashboard_latency_secondsHistogramtenant
dashboard_module_latency_secondsHistogrammodule