Provcache UI Components

This document describes the Angular components for visualizing provenance cache data, trust scores, and proof trees in the StellaOps Console.

Overview

The Provcache UI components provide visual feedback for:

Components

ProvenanceBadgeComponent

Selector: stellaops-provenance-badge

Displays a compact badge indicating the provenance state of a decision.

Inputs

InputTypeDefaultDescription
state'cached' | 'computed' | 'stale' | 'unknown''unknown'Current provenance state
cacheDetailsCacheDetails | nullnullOptional cache metadata
trustScorenumber | nullnullOptional trust score (0-100)
size'small' | 'medium' | 'large''medium'Badge size variant

Outputs

OutputTypeDescription
viewProofTreeEventEmitter<void>Emitted when user clicks to view proof

States & Icons

StateIconTooltipCSS Class
cached“Provenance-cached”badge--cached
computed🔄“Freshly computed”badge--computed
stale“Stale - recomputing”badge--stale
unknown“Unknown provenance”badge--unknown

Usage

<!-- Basic usage -->
<stellaops-provenance-badge [state]="'cached'"></stellaops-provenance-badge>

<!-- With cache details -->
<stellaops-provenance-badge
  [state]="'cached'"
  [cacheDetails]="{ source: 'valkey', ageSeconds: 45, veriKey: 'sha256:abc...' }"
  [trustScore]="85"
  (viewProofTree)="openProofTree()">
</stellaops-provenance-badge>

Accessibility


TrustScoreDisplayComponent

Selector: stellaops-trust-score-display

Visualizes a trust score (0-100) with optional breakdown by evidence component.

Inputs

InputTypeDefaultDescription
scorenumber0Overall trust score (0-100)
breakdownTrustScoreBreakdown | nullnullComponent-level scores
mode'donut' | 'badge' | 'inline''donut'Display mode
showBreakdownbooleanfalseShow breakdown tooltip
compactbooleanfalseUse compact sizing
thresholds{ high: number; medium: number }{ high: 80, medium: 50 }Color thresholds

TrustScoreBreakdown Interface

interface TrustScoreBreakdown {
  reachability: number;     // 0.0-1.0 (25% weight)
  sbomCompleteness: number; // 0.0-1.0 (20% weight)
  vexCoverage: number;      // 0.0-1.0 (20% weight)
  policyFreshness: number;  // 0.0-1.0 (15% weight)
  signerTrust: number;      // 0.0-1.0 (20% weight)
}

Display Modes

ModeDescription
donutCircular SVG chart with score in center
badgeCompact pill-shaped badge
inlineText-only display for tight spaces

Color Coding

Score RangeClassColor
≥ high (80)score--highGreen (#4caf50)
≥ medium (50)score--mediumYellow (#ff9800)
< mediumscore--lowRed (#f44336)

Usage

<!-- Donut chart with breakdown -->
<stellaops-trust-score-display
  [score]="85"
  [breakdown]="breakdown"
  [mode]="'donut'"
  [showBreakdown]="true">
</stellaops-trust-score-display>

<!-- Compact badge -->
<stellaops-trust-score-display
  [score]="72"
  [mode]="'badge'"
  [compact]="true">
</stellaops-trust-score-display>

ProofTreeComponent

Selector: stellaops-proof-tree

Renders a decision’s proof chain as a collapsible tree with verification capabilities.

Inputs

InputTypeDefaultDescription
digestDecisionDigest | nullnullThe decision digest to display
merkleTreeMerkleTree | nullnullOptional Merkle tree structure
verdictsVerdictEntry[][]Vulnerability verdicts
evidenceChunksEvidenceChunk[][]Evidence artifacts
isVerifyingbooleanfalseShow verification spinner

Outputs

OutputTypeDescription
copyVeriKeyEventEmitter<string>VeriKey copied to clipboard
copyHashEventEmitter<string>Any hash copied
verifyEventEmitter<void>Verify button clicked
downloadEvidenceEventEmitter<EvidenceChunk>Evidence download requested

Tree Structure

DecisionDigest
├── VeriKey: sha256:abc123...
├── Trust Score: 85
│   ├── Reachability: 95%
│   ├── SBOM Completeness: 85%
│   ├── VEX Coverage: 90%
│   ├── Policy Freshness: 88%
│   └── Signer Trust: 92%
├── Verdicts (12)
│   ├── CVE-2025-1234 → not_affected
│   ├── CVE-2025-5678 → fixed
│   └── ...
├── Merkle Tree
│   ├── Root: sha256:root...
│   ├── Left
│   │   ├── sha256:sbom... (sbom-hash)
│   │   └── sha256:vex... (vex-set-hash)
│   └── Right
│       ├── sha256:policy... (policy-hash)
│       └── sha256:signers... (signers-hash)
└── Evidence Chunks (5)
    ├── [sbom] SPDX 2.3 SBOM document
    ├── [vex] VEX statement bundle
    └── ...

Verdict Statuses

StatusColorMeaning
not_affectedGreenVulnerability not applicable
fixedBluePatched/remediated
affectedRedVulnerable
under_investigationYellowPending analysis
mitigatedCyanMitigating controls in place

Usage

<stellaops-proof-tree
  [digest]="decisionDigest"
  [merkleTree]="merkleTree"
  [verdicts]="verdicts"
  [evidenceChunks]="chunks"
  [isVerifying]="verifying"
  (verify)="verifyProof()"
  (copyVeriKey)="copyToClipboard($event)"
  (downloadEvidence)="downloadChunk($event)">
</stellaops-proof-tree>

InputManifestComponent

Selector: stellaops-input-manifest

Displays the exact inputs that formed a VeriKey and cached decision.

Inputs

InputTypeDefaultDescription
manifestInputManifest | nullnullThe manifest to display
mode'full' | 'compact' | 'summary''full'Display mode
displayConfigInputManifestDisplayConfig(all true)Section visibility

Outputs

OutputTypeDescription
copyVeriKeyEventEmitter<string>VeriKey copied
refreshEventEmitter<void>Refresh requested

InputManifest Interface

interface InputManifest {
  veriKey: string;
  sourceArtifact: SourceArtifactInfo;
  sbom: SbomInfo;
  vex: VexInfo;
  policy: PolicyInfoManifest;
  signers: SignerInfo;
  timeWindow: TimeWindowInfo;
  generatedAt: string;
}

interface SourceArtifactInfo {
  digest: string;
  artifactType: string;
  ociReference?: string;
  sizeBytes?: number;
}

interface SbomInfo {
  hash: string;
  format: string;         // 'spdx-2.3', 'cyclonedx-1.6'
  packageCount: number;
  completenessScore: number;
  createdAt: string;
}

interface VexInfo {
  hashSetHash: string;
  statementCount: number;
  sources: string[];
  latestStatementAt: string;
}

interface PolicyInfoManifest {
  hash: string;
  packId: string;
  version: number;
  lastUpdatedAt: string;
  name?: string;
}

interface SignerInfo {
  setHash: string;
  signerCount: number;
  certificates?: CertificateInfo[];
}

interface CertificateInfo {
  subject: string;
  issuer: string;
  fingerprint: string;
  expiresAt: string;
  trustLevel: 'fulcio' | 'enterprise-ca' | 'self-signed';
}

interface TimeWindowInfo {
  bucket: string;
  startsAt: string;
  endsAt: string;
}

Display Modes

ModeDescription
fullAll sections with full details
compactReduced spacing, abbreviated values
summaryGrid layout for quick overview

Section Visibility

interface InputManifestDisplayConfig {
  showSource: boolean;
  showSbom: boolean;
  showVex: boolean;
  showPolicy: boolean;
  showSigners: boolean;
  showTimeWindow: boolean;
}

Usage

<!-- Full manifest display -->
<stellaops-input-manifest
  [manifest]="inputManifest"
  [mode]="'full'"
  (copyVeriKey)="copyToClipboard($event)">
</stellaops-input-manifest>

<!-- Security-focused view -->
<stellaops-input-manifest
  [manifest]="inputManifest"
  [mode]="'compact'"
  [displayConfig]="{ showSource: false, showSbom: false, showVex: true, showPolicy: true, showSigners: true, showTimeWindow: false }">
</stellaops-input-manifest>

Storybook Documentation

All components have Storybook stories in src/Web/StellaOps.Web/src/stories/provcache/:

FileDescription
provenance-badge.stories.tsAll badge states, trust scores, sizes
trust-score-display.stories.tsScore ranges, modes, breakdowns
input-manifest.stories.tsModes, SBOM formats, certificates
proof-tree.stories.tsTree depths, verdicts, verification

Run Storybook to explore:

cd src/Web/StellaOps.Web
npm run storybook

Integration Examples

Finding Row Integration

// finding-row.component.ts
@Component({
  selector: 'stellaops-finding-row',
  template: `
    <div class="finding-row">
      <stellaops-provenance-badge
        [state]="finding.provenanceState"
        [cacheDetails]="finding.cacheDetails"
        [trustScore]="finding.trustScore"
        (viewProofTree)="openProofPanel.emit(finding)">
      </stellaops-provenance-badge>
      <!-- ... rest of finding row -->
    </div>
  `
})
export class FindingRowComponent {
  @Input() finding!: FindingRow;
  @Output() openProofPanel = new EventEmitter<FindingRow>();
}

Decision Detail Panel

// decision-detail-panel.component.ts
@Component({
  template: `
    <div class="decision-detail-panel">
      <stellaops-trust-score-display
        [score]="digest.trustScore"
        [breakdown]="digest.trustScoreBreakdown"
        [mode]="'donut'"
        [showBreakdown]="true">
      </stellaops-trust-score-display>

      <stellaops-proof-tree
        [digest]="digest"
        [merkleTree]="merkleTree"
        [verdicts]="verdicts"
        [evidenceChunks]="chunks"
        [isVerifying]="verifying$ | async"
        (verify)="verifyProof()"
        (downloadEvidence)="downloadChunk($event)">
      </stellaops-proof-tree>

      <stellaops-input-manifest
        [manifest]="manifest"
        [mode]="'full'">
      </stellaops-input-manifest>
    </div>
  `
})
export class DecisionDetailPanelComponent {
  // ...
}

Theming

Components use CSS custom properties for theming:

/* Dark mode overrides */
@media (prefers-color-scheme: dark) {
  --trust-score-high: #81c784;
  --trust-score-medium: #ffb74d;
  --trust-score-low: #e57373;
  --badge-cached-bg: #1b5e20;
  --badge-computed-bg: #0d47a1;
}

Accessibility

All components follow WCAG 2.1 AA guidelines: