FTUX fixes (Sprint 316-001): - Remove all hardcoded fake data from dashboard — fresh installs show honest setup guide instead of fake crisis data (5 fake criticals gone) - Curate advisory source defaults: 32 sources disabled by default (ecosystem, geo-restricted, exploit, hardware, mirror). ~43 core sources remain enabled. StellaOps Mirror no longer enabled at priority 1. - Filter Mirror-category sources from Create Domain wizard to prevent circular mirror-from-mirror chains - Add 404 catch-all route — unknown URLs show "Page Not Found" instead of silently rendering the dashboard - Fix arrow characters in release target path dropdown (? → →) - Add login credentials to quickstart documentation - Update Feature Matrix: 14 release orchestration features marked as shipped (was marked planned) Platform contract repairs (from prior session): - Add /api/v1/jobengine/quotas/summary endpoint on Platform - Fix gateway route prefix matching for /policy/shadow/* and /policy/simulations/* (regex routes instead of exact match) - Fix VexHub PostgresVexSourceRepository missing interface method - Fix advisory-vex-sources sweep text expectation - Fix mirror operator journey auth (session storage token extraction) Verified: 110/111 canonical routes passing (1 unrelated stale approval ref) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
13 KiB
First-Time User Audit Series — Stella Ops
Date: 2026-03-16
Method: Interactive Playwright walkthrough + source code analysis + product deep-dive
Stack: 63 containers live at https://stella-ops.local, demo-prod tenant
Part 1: The Product Mental Model
Stella Ops is a release control plane for non-Kubernetes container estates. Every operator interaction answers one of three questions:
- Is it safe? — Vulnerability findings, SBOM health, reachability evidence, VEX dispositions, advisory feed freshness
- Is it approved? — Policy gates passed, human approvals given, evidence sealed
- Is it working? — Services healthy, feeds syncing, jobs running, no dead letters
The UI, documentation, and workflows must be organized around these three pillars.
Part 2: Findings (Prioritized)
CRITICAL
C1. Dashboard is 100% Hardcoded — Zero Real API Data
Source file: dashboard-v3.component.ts:966-1200
Every number on the dashboard is fake:
summarysignal:{ activePromotions: 3, blockedPromotions: 1 }— literalresolveStatusSeed(): generates metrics by env type (dev=healthy, staging=degraded, prod+us-east=blocked)reachabilityStats:{ bCoverage: 72, iCoverage: 88, rCoverage: 61 }— literalnightlyOpsSignals: 4 hardcoded items- Alerts: 3 hardcoded
<li>HTML elements - Activity: 3 hardcoded cards
- Not a single API call to any backend service
The dashboard tells you "5 critical findings, blocked" but security posture shows 0 findings. This destroys trust immediately.
Solution: [See Part 3 — Dashboard Redesign]
C2. StellaOps Mirror Source Enabled at Priority 1 on Every Fresh Install
Source file: SourceDefinitions.cs:1351-1366
The StellaMirror source definition:
BaseEndpoint = "https://mirror.stella-ops.org/api/v1" // nonexistent external URL
DefaultPriority = 1 // HIGHEST priority — overrides all other sources
EnabledByDefault = true // inherited, always on
On every fresh self-hosted install, the highest-priority advisory source points to a URL that doesn't exist. Health check returns "SSL/TLS error." The system is in "Direct" mode but its top-priority source is a mirror.
Solution: Set EnabledByDefault = false on StellaMirror. Only enable when mirror consumer wizard is completed.
C3. Mirror Source, Mirror Mode, and Mirror Consumer Wizard Are Disconnected
Three independent systems that should be one:
- Catalog toggle: Enable "StellaOps Mirror" → hardcoded to
mirror.stella-ops.org - Mode badge: Shows "Direct" regardless of which sources are enabled
- Consumer wizard: "Connect to Mirror" → 4-step wizard with custom URL
Enabling the catalog source doesn't invoke the wizard. Completing the wizard doesn't update the catalog source. The mode badge doesn't reflect reality.
Solution: Unify — catalog mirror toggle should redirect to consumer wizard. Wizard completion should update source endpoint + enable source + set mode.
C4. 74 of 75 Advisory Sources Enabled by Default — 18 Fail Immediately
EnabledByDefault = true in the base SourceDefinition class. Every source — including geo-restricted (FSTEC Russia, NKCKI Russia), ecosystem-specific (npm, PyPI, Maven, etc.), and the broken mirror — is enabled on fresh install.
After "Check All": 55 healthy, 18 failed. Stats bar shows confusing "55 enabled" (doesn't match 74 actually enabled).
Solution: Curate defaults. Enable ~25 core sources (Primary + major vendors + major distributions). Disable ecosystem, geo-restricted, hardware, and mirror by default.
HIGH
H1. Login Credentials Not Documented Anywhere
Quickstart says "Open https://stella-ops.local" — never mentions username/password. User must find S001_demo_seed.sql to discover admin / Admin@Stella2026!.
Solution: Add credentials to quickstart.md step 5. Add hint to the Welcome page.
H2. Registry Search Returns Mock Results Despite API Failures
In release creation wizard, searching "nginx" returns "nginx-service" at registry.internal/nginx-service with fake digest sha256:nginx1234567.... Console shows 4 errors: Failed to load resource: /api/registry/images/search. The mock fallback silently produces results from a nonexistent registry.
Solution: When registry API fails, show error — not mock results. Don't allow sealing releases with mock artifacts.
H3. Create Mirror Domain Wizard Allows Selecting Mirror Source
The source picker in Create Domain includes "StellaOps Mirror" under Mirror (1) category. This creates a mirror domain that sources from a nonexistent upstream mirror — circular/broken dependency.
Solution: Filter out category === 'Mirror' from the Create Domain source picker.
H4. AuthRef URI Required — No Plain Credentials for Evaluation
Harbor registry wizard requires authref://vault/path#secret. No vault exists on a fresh install. No way to enter username:password for evaluation.
Solution: Allow authref://env/VARIABLE_NAME or authref://inline/base64 for dev/eval. Document authref format with examples.
MEDIUM
M1. Bundle Version Shows UUID Instead of Release Name
After sealing "API Gateway v2.1", the page shows a4451892-b6b2-41f6-8539-7b9c6d33d140 v1 as the heading.
Solution: Show human-readable name as primary heading, UUID as metadata.
M2. "Created by" Shows User ID Hash, Not Username
Bundle version shows b08639745d6549348d843aa311c98958 instead of "admin".
Solution: Resolve user IDs to display names in read-model projections.
M3. Context Controls Race Condition on First Load
Topbar shows "No regions defined" for 2-3 seconds, then self-heals to "4 regions". Dashboard below shows environment cards immediately (from seed data).
Solution: Show loading skeleton for context controls until inventory loads.
M4. Doctor Quick Check Skips Database Checks
check.db.connection and check.db.latency show "Skipped — not applicable" despite Postgres being the primary database.
Solution: Always include DB connection check in Quick Check.
M5. Doctor Stream Endpoint Returns Console Error
After Quick Check, console shows Failed to load resource: /api/v1/doctor/run/.../stream. Results still render via fallback.
Solution: Fix streaming endpoint or suppress console error when fallback succeeds.
M6. Events Status Flickers "DEGRADED" → "CONNECTED" on Login
First 2-3 seconds show "Events: DEGRADED" (yellow), then settles to "CONNECTED" (green).
Solution: Show "Connecting..." during initial establishment, not "DEGRADED".
M7. Advisory Sources Show "stale" on Fresh Install (Never Synced)
Security Posture: "GitHub Advisory Database — stale", "NVD — stale". These have never been synced.
Solution: Show "Not synced" for sources that have never fetched. Reserve "stale" for sources whose last sync exceeds freshness threshold.
M8. Feature Matrix Shows Release Orchestration as "Planned" (⏳) — But It's Built
FEATURE_MATRIX.md lists Environment CRUD, Release Bundles, Promotion Workflows as ⏳. The live UI has working releases, approvals, promotions. A buyer would think releases don't work.
Solution: Update FEATURE_MATRIX.md to reflect current implementation status.
M9. Stats Bar Count Mismatch
After Check All, stats show "55 enabled" but 74 sources are actually enabled (toggles ON). The "enabled" count in the stats bar doesn't match the source state.
Solution: Fix stats computation to count actual enabled sources, not only those with successful health checks.
LOW
L1. Arrow Character Not Rendering in Target Path
Release wizard dropdown shows "Dev ? Stage ? Prod" instead of "Dev → Stage → Prod".
Solution: Use \u2192 or HTML entity →.
L2. No 404 Page — Unknown Routes Show Dashboard
/nonexistent-route-12345 renders the Dashboard instead of "Page Not Found".
Solution: Add wildcard catch-all route with a proper 404 component.
Part 3: Dashboard Redesign
Layout: 3-Column Mission Board
┌──────────────────────────────────────────────────────────────┐
│ Mission Board — [Demo Production] [↻ Refresh] │
│ Last updated: 15 Mar 2026, 23:15 UTC │
├──────────────────┬───────────────────────────────────────────┤
│ │ │
│ SECURITY POSTURE │ ENVIRONMENTS & ACTIONS (2/3) │
│ (1/3) │ │
│ │ ┌───────────────────────────────────────┐ │
│ ┌──────────────┐ │ │ PROMOTION PIPELINE │ │
│ │ Vuln Summary │ │ │ [env cards: Dev→Stage→Prod by region] │ │
│ │ C:12 H:34 │ │ │ blocked envs highlighted at top │ │
│ │ M:89 L:156 │ │ └───────────────────────────────────────┘ │
│ │ [donut chart] │ │ │
│ │ Reachable: 9 │ │ ┌───────────────────────────────────────┐ │
│ │ Unknown: 23 │ │ │ NEEDS ATTENTION │ │
│ └──────────────┘ │ │ ⚠ 3 approvals blocked │ │
│ │ │ ⚠ 2 waivers expiring │ │
│ ┌──────────────┐ │ │ 🔴 Feed freshness degraded │ │
│ │ SBOM Health │ │ └───────────────────────────────────────┘ │
│ │ 247 comps │ │ │
│ │ 231 fresh │ │ ┌───────────────────────────────────────┐ │
│ │ B:72% I:88% │ │ │ LIVE ACTIVITY │ │
│ │ R:61% │ │ │ • admin sealed "API Gateway v2.1" │ │
│ └──────────────┘ │ │ • NVD synced (142 new advisories) │ │
│ │ │ • Doctor: 7 pass, 1 warn, 1 fail │ │
│ ┌──────────────┐ │ └───────────────────────────────────────┘ │
│ │ Feed Status │ │ │
│ │ 55/75 active │ │ │
│ │ 18 failed │ │ │
│ └──────────────┘ │ │
├──────────────────┴───────────────────────────────────────────┤
│ PLATFORM: 63 svcs ✓ │ DB:OK │ Events:ON │ Doctor:7/1/1 │
│ Feed:Live │ Evidence:ON │ Offline:OK │ DLQ:3 pending │
└──────────────────────────────────────────────────────────────┘
Empty State (Fresh Install)
When no real data exists, show setup guidance — not fake numbers:
Welcome to Stella Ops — Release Control Plane
① Connect a registry [Setup Integrations →]
② Define your topology [Topology Wizard →]
③ Scan your first image [Start Scan →]
④ Create a release [Create Release →]
Platform Health: 63/63 services ✓
Advisory Sources: 55/75 healthy [Configure →]
Part 4: Solutions Implementation Map
| # | Fix | Effort | Files |
|---|---|---|---|
| C2 | Mirror source EnabledByDefault = false |
S | SourceDefinitions.cs |
| C4 | Curate default sources (~25 instead of 74) | S | SourceDefinitions.cs |
| H1 | Add credentials to quickstart | S | docs/quickstart.md |
| H3 | Filter mirror sources from domain builder | S | mirror-domain-builder.component.ts |
| M8 | Update Feature Matrix status markers | S | docs/FEATURE_MATRIX.md |
| L1 | Fix arrow character encoding | S | Release version form component |
| L2 | Add 404 catch-all route | S | app.routes.ts |
| M3 | Loading skeleton for context controls | S | app-topbar.component.ts |
| M6 | "Connecting..." instead of "DEGRADED" | S | topbar events status |
| M7 | "Not synced" vs "stale" for fresh sources | S | advisory-source-catalog.component.ts |
| M9 | Fix stats bar enabled count | S | advisory-source-catalog.component.ts |
| C1/Phase1 | Dashboard honest empty state | M | dashboard-v3.component.ts |
| C3 | Unify mirror toggle → consumer wizard | M | catalog + wizard components |
| H2 | Registry search error instead of mock | M | release version wizard |
| C1/Phase2 | Dashboard 3-column layout with real APIs | L | dashboard-v3.component.ts + API clients |