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. GET /api/v1/dashboard, summary, total_images, images_scanned_24h, critical_vulns, policy_violations, trends, vuln_trend_7d, scan_volume_7d, top_vulns and policy_status do not exist 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