17 KiB
Here are three tightly scoped UI micro-interactions you can drop into the next front-end sprint. Each maps to a single Angular task and reinforces StellaOps’ auditability, low-noise VEX gating, and evidence provenance for both air-gapped and online users.
1) Audit Trail “Why am I seeing this?” (per-row expandable reason)
Goal: Make every verdict/action explainable at a glance.
- User flow: In Tables (Findings, Components, Policies), each row shows a tiny “ⓘ Why?” chip. Clicking expands an inline panel with the explanation capsule (policy that fired, rule hash, data inputs, and the deterministic graph revision ID).
- Angular task:
- Add
ReasonCapsuleComponent(standalone) with inputs:policyName,ruleId,graphRevisionId,inputsDigest,timestamp,actor. - Drop it into tables via
*ngTemplateOutletso no table refactor. - Keyboard accessible (Enter/Space toggles).
- Add
- Backend contract (read-only):
GET /api/audit/reasons/:verdictId→{ "policy":"Default VEX Merge", "ruleId":"vex.merge.R12", "graphRevisionId":"grv_2025-11-18T12:03Z_b3e1", "inputsDigest":"sha256:…", "actor":"Vexer", "ts":"2025-11-18T12:03:22Z" } - Acceptance criteria:
- Toggle persists open/closed per row in URL state (
?open=rid1,rid3). - Copy-to-clipboard for
graphRevisionId. - Works in offline bundle (served from local API).
- Toggle persists open/closed per row in URL state (
2) Low-noise VEX Gate (inline, gated action with 3 evidence tiers)
Goal: Reduce false prompts and only block when evidence is meaningful.
- User flow: On risky actions (promote image, approve import), the primary button morphs into a VEX Gate: a small dropdown right-aligned to the button label showing Green/Amber/Red status with the top blocking reason. Clicking opens a concise sheet (not a modal) with the minimal evidence set, and a single “Proceed anyway” path if policy allows.
- Angular task:
- Create
VexGateButtonDirectivethat decorates existing<button>to append status chip + sheet host. - Create
VexEvidenceSheetComponentthat renders: top finding, reachability hint, last vendor VEX statement, and policy clause that blocks/allows.
- Create
- Backend contract:
POST /api/vex/gatewith{artifactId, action}→{ "tier":"amber", "reason":"Reachable CVE with pending vendor statement", "policyClause":"gates.required.vendor_vex==true", "allowBypass":true, "expires":"2025-12-31T00:00:00Z" } - Acceptance criteria:
- Button aria-label reflects state (“Promote — VEX Amber”).
- Gate sheet loads <300ms cached; falls back to last known cache if offline.
- “Proceed anyway” requires typed confirmation only when
allowBypass=trueand logs an audit event.
3) Evidence Provenance Chip (DSSE/receipt capture w/ one-tap export)
Goal: Make provenance obvious and exportable for audits.
- User flow: Wherever we render an SBOM, VEX, or Attestation, show a Provenance chip (
Signed • Verified • Logged) with an overflow menu: View DSSE, View Rekor/Receipt, Export bundle (.tar.zst). In air-gap mode, the “Logged” badge swaps to “Queued” with a tooltip. - Angular task:
ProvenanceChipComponentwith inputs:sigStatus,dsseDigest,rekorInclusion,queued.ProvenanceDialogComponentto show DSSE envelope (pretty-printed), inclusion proof (Merkle path), and copy buttons.
- Backend contract:
GET /api/provenance/:artifactId→ DSSE envelope + receipt (or offline queue record).POST /api/provenance/export→ streamsapplication/zstdcontaining: envelope, payload, receipt, local trust anchors.
- Acceptance criteria:
- Chip states:
Unsigned,Signed,Verified,Verified+Logged; offline showsSigned+Queued. - Export under 5s for 10MB payloads; filename includes
graphRevisionId. - All views work with no internet; if Rekor unreachable, show last cached receipt with “not recently synced” hint.
- Chip states:
Dev notes (quick drop-in)
- Module placement:
apps/console/src/app/shared/micro/…(standalone components, tree-shakable). - Theme: Consume existing Tailwind tokens; no new colors (use semantic classes).
- Testing:
- Unit: input contracts + a11y snapshot.
- E2E: Cypress happy path per interaction (open/close, bypass, export).
- Telemetry/Audit: Emit a single
ui_eventper interaction withgraphRevisionId,policyClause,artifactId. - Offline kits: All three rely only on local API; Rekor calls are optional/cached.
If you want, I can turn this into three Jira tickets (with checklists) and a PR scaffold (ng g component stubs + sample mocks) so your team can start today.
Here’s a v0.1 “StellaOps UX Guidelines” you can hand to your UX devs and iterate on. I’ll keep it concrete and opinionated, tuned to your world: SBOMs, evidence, attestations, VEX, online + air‑gap.
0. Who these guidelines are for
These guidelines are for people who:
- Build UI in Angular for the StellaOps console
- Design workflows around evidence, SBOMs, VEX, and policy decisions
- Need to keep high‑assurance users (security, compliance, infra) happy without drowning them in noise
Whenever in doubt: opt for clarity, traceability, and low noise.
1. Core StellaOps UX Principles
These are the “north star” rules. Every screen and interaction should support them:
-
Everything is explainable
- Every verdict, badge, and gating decision must have an explicit “why” nearby.
- Implement via the Reason Capsule (“Why am I seeing this?” expanders) on rows, cards, and actions.
-
Evidence in one hop
- The user should never click more than once from a verdict to the underlying evidence (SBOM, attestation, DSSE, log receipt).
- Use: side panel / drawer or inline expansion instead of forcing navigation to a separate page.
-
Noise is a bug
- Prompts, warnings, and banners are exceptions, not the default.
- High-severity blocking states must be rare, justified, and explicitly tied to a policy clause.
-
Deterministic, not magical
- If something is inferred or uncertain, label it clearly (“Inferred reachability”, “Heuristic match”).
- Never present a guess as a fact.
-
Safe by default, reversible when possible
-
Default to the safer path (e.g., “Block promotion” if in doubt).
-
If a potentially risky override is allowed, make it:
- Explicit
- Audited
- Slightly “hard” (typed confirmation, not just a single click)
-
-
Online and air‑gapped parity
- The same mental model must work whether online or air‑gapped.
- When external services are unavailable, show degraded but honest states (“Verified + Queued” vs “Verified + Logged”).
-
Fast for experts
- Keyboard accessible, dense data tables, predictable shortcuts.
- Avoid wizards for frequent tasks; use in-place controls and drawers instead.
2. Domain model in the UI (what users should always see)
Make these concepts feel consistent across all screens:
- Artifact: image, package, or component.
- Evidence: SBOMs, attestations, VEX docs, logs, receipts, scans.
- Policy: the rules that drive decisions.
- Verdict / Status: “Allowed / Blocked / Needs Review / Unknown”.
- Graph Revision ID: the immutable “snapshot ID” of the decision state.
- Audit Event: who did what, when, and based on which graph revision.
Guideline: Any place the user sees a verdict or action, they should also see:
- The policy that drove it
- The graph revision ID
- A one-click path to evidence and audit trail
Our 3 micro‑interactions (Reason Capsule, VEX Gate, Provenance Chip) exist to make this easy.
3. Core Interaction Patterns
3.1 Reason Capsule: “Why am I seeing this?”
Used in tables, lists, and detail views.
Where to place
- In tables: right side of each row as a small chip or icon:
ⓘ Why? - In cards/detail headers: as a text link: “Why this verdict?”
Behavior
-
Click/Enter/Space toggles an inline expansion under the row or section.
-
Show:
- Policy name (with pill: e.g.,
Policy: Default VEX Merge) - Rule ID (e.g.,
vex.merge.R12) - Graph revision ID (copyable)
- Key inputs (SBOM digest, VEX source, relevant CVE IDs)
- Actor (“Vexer”, “User override: alice@example.com”)
- Timestamp
- Policy name (with pill: e.g.,
UX rules
-
Keep it 3–5 lines max by default; use “Show more” for full detail.
-
Always lead with a plain-language sentence:
- “This artifact is blocked because policy ‘Prod Promotion’ requires a vendor VEX statement for reachable critical CVEs.”
-
Include a copy-to-clipboard affordance for graphRevisionId.
-
Remember accessibility:
aria-expandedreflects state- The expander is focusable and operable via keyboard
3.2 VEX Gate: Gated primary actions
Used on high-impact actions: promote image, approve import, mark as compliant, etc.
Pattern
-
Primary button is enhanced by a VEX status chip:
- Label pattern:
Promote+VEX: Green/Amber/Redchip.
- Label pattern:
-
Clicking the chip or a small caret opens a compact sheet anchored to the button.
-
The sheet shows:
- Tier:
Green(no blocking issues),Amber(warning),Red(blocking) - Top reason in plain language
- Policy clause that applies
- Minimal evidence summary: 1–2 key findings, last vendor VEX statement, reachability
- Whether bypass is allowed and what it implies
- Tier:
Noise rules
-
Don’t show a blocking modal unless absolutely necessary:
- Prefer anchored sheets or drawers that don’t break context.
-
Only require typed confirmation if:
- Tier is
Redand - Policy explicitly allows bypass
- Tier is
Copy guidelines
-
Green:
- “No blocking VEX findings for this artifact under ‘Prod Promotion’ policy.”
-
Amber:
- “Found reachable CVE with pending vendor VEX. Policy allows promotion with review.”
-
Red:
- “Blocking: reachable critical CVE without compensating VEX or mitigation documented.”
Offline/air‑gapped
-
If gate info is coming from cache:
- Label: “Based on last local evaluation (not recently synced).”
-
The action should still be possible if the policy says so, even when external VEX feeds are offline.
3.3 Provenance Chip: Attestations, DSSE, receipts
Appears next to any evidence object (SBOM, attestation, VEX doc) and on artifact details.
States
Use a small chip with an icon + label:
UnsignedSignedSigned • VerifiedSigned • Verified • Logged- Offline variant:
Signed • Verified • Queued
Behavior
-
Clicking opens a Provenance dialog:
- DSSE envelope (structured view with collapsing sections)
- Subject digest(s)
- Inclusion proof / log receipt (if online)
- Trust anchor summary (what keys were trusted)
- Export CTA for full bundle
Rules
-
The chip state must never lie. If Rekor (or similar) is unreachable:
- Show last known receipt + “not recently synced” note.
-
Export action:
- Single button: “Export provenance bundle (.tar.zst)”
- Filename should include artifactId and graphRevisionId.
4. Layout & Information Density
4.1 Tables and lists
-
Default to tables for any list of artifacts, findings, or evidence.
-
Per row:
- Left side: identity (name, version, image tag)
- Middle: key status chips (severity, policy verdicts, provenance state)
- Right: row actions + Reason Capsule
Column order guideline
- Artifact / Evidence identifier
- Primary status (Allowed/Blocked/Needs Review)
- Severity / Risk
- Provenance status
- Last updated / source
- Actions + “Why?”
Avoid more than 7 columns at once; beyond that, use:
- Column visibility toggles
- A compact “More details” drawer
4.2 Detail views (artifact / policy / evidence)
Use a two-pane layout:
- Left: core summary; main actions; VEX Gate button
- Right: tabs or sections:
Evidence,Policies,Audit trail
In the summary header:
-
Show:
- Artifact identity
- Primary verdict
- Graph revision ID
- Provenance chip
-
Provide the one-tap path to:
- Policy explanation (Reason Capsule)
- Provenance dialog
5. Copy & Microcopy Guidelines
Your users are experts; don’t over-simplify. But also don’t write walls of text.
5.1 Tone
- Neutral, precise, never blamey.
- Prefer “This artifact is blocked because…” over “You can’t do this because…”.
- Avoid overloaded acronyms unless they are core to the domain (SBOM, VEX, CVE are fine).
5.2 Phrase patterns
Verdicts
- “Allowed by policy ‘X’.”
- “Blocked by policy ‘X’ (rule R12: requires vendor VEX for critical CVEs).”
- “Needs review: incomplete evidence for ‘X’.”
Uncertainty
- “Inferred reachable based on call graph analysis.”
- “Vendor VEX statement not found in local cache.”
Offline
- “Using cached results from 2025‑11‑29. External logs not reachable.”
Overrides
- “Override applied by alice@example.com on 2025‑11‑18: promotion allowed despite Red VEX gate.”
5.3 Don’ts
- Don’t use “Unknown error”. Always say what failed (e.g., “Failed to fetch Rekor receipt: TLS handshake error”).
- Don’t just show raw IDs without labels (e.g., “grv_...” → always “Graph revision: grv_...”).
- Don’t hide policy names; they are how users think about business intent.
6. States: Loading, Errors, Offline, Edge Cases
6.1 Loading
-
For tables: use skeleton rows, not giant spinners.
-
For buttons with gates:
- Show inline “Checking VEX…” with a subtle spinner in the chip area only.
-
Maximum loading time before showing a message: ~2–3 seconds; after that, show:
- “Still evaluating VEX; you can proceed with last known result from 2025‑11‑18, or wait.”
6.2 Error states
For any failed fetch related to:
- Provenance
- Log receipts
- Policy evaluation
…show:
-
A neutral banner in the relevant area:
- “Could not refresh provenance from log. Using last known verification from 2025‑11‑10.”
-
A clear fallback behavior:
- Block or proceed according to policy, not “best guess”.
6.3 Offline & Air‑gapped
Golden rule: Never pretend to be online when you’re not.
-
Make offline clearly visible but not screaming:
- Small badge in the header:
Air-gapped modeorOffline mode
- Small badge in the header:
-
Provenance chip:
- Use the
Queuedvariant when receipts haven’t been synced.
- Use the
-
Export path:
- Always work without internet (local export).
7. Accessibility & Keyboard Support
Your users will live in this tool; keyboard and a11y aren’t nice-to-haves.
Minimums
-
All interactive chips (Reason Capsule, Provenance Chip, VEX Gate) must:
- Be reachable via Tab
- Be clickable via Enter / Space
- Have descriptive
aria-labels (e.g., “Explain verdict for artifact nginx:1.24.0”)
-
Use visible focus outlines that fit your design tokens.
-
Do not rely solely on color:
- VEX tiers need both color and text (
Green,Amber,Red).
- VEX tiers need both color and text (
8. Implementation Handshake (Design ⇄ Dev)
A simple workflow you can standardize on:
-
Design defines the pattern
-
For each reusable micro‑interaction (Reason Capsule, VEX Gate, Provenance Chip), the designer:
- Specifies inputs/props
- Lists states (default, loading, error, offline)
- Provides content examples
-
-
Dev builds a standalone Angular component
-
One component per pattern:
ReasonCapsuleComponentVexGateButtonDirective + VexEvidenceSheetComponentProvenanceChipComponent + ProvenanceDialogComponent
-
All styling via shared tokens (no hard‑coded colors).
-
-
Shared acceptance criteria
-
Each pattern must have:
- a11y behavior defined
- Loading and error behavior defined
- At least one offline scenario defined
-
E2E check:
-
Can a user go from a verdict to:
- Why it happened,
- Evidence that supports it,
- Audit record of who acted …in ≤ 2 clicks?
-
-
9. Quick “Do This, Not That” Cheat Sheet
-
Do: Show
Blocked • Why?→ inline explanation → evidence link Don’t: Show justBlockedwith no context. -
Do: Attach VEX Gate logic to existing primary buttons as a small extension. Don’t: Introduce a separate, second “Check VEX” action the user has to remember.
-
Do: Use small chips for provenance state (
Signed • Verified • Logged). Don’t: Hide signature/log info in a separate “Advanced” tab. -
Do: Be honest about offline/cached states. Don’t: Fake a “Verified + Logged” status when the log sync actually failed.
If you’d like, I can also turn this into:
- A one-page “UX checklist per screen” your devs can run through before shipping, and
- Concrete Angular interface definitions for each of the shared components so design/dev are literally talking about the same props.