@@ -0,0 +1,299 @@
# Unified Trust Score Architecture
> **Ownership:** Policy Guild • Signals Guild
> **Services:** `StellaOps.Signals.UnifiedScore` (facade), `StellaOps.Signals.EvidenceWeightedScore` (core), `StellaOps.Policy.Determinization` (entropy)
> **Related docs:** [Policy architecture](../modules/policy/architecture.md), [EWS migration](../modules/policy/design/confidence-to-ews-migration.md), [Score Proofs API](../api/scanner-score-proofs-api.md)
This document describes the **unified trust score facade** that provides a single API for accessing risk scores, uncertainty metrics, and evidence from the underlying EWS and Determinization systems.
---
## 1 · Design Principle: Facade Over Rewrite
Stella Ops has mature, battle-tested scoring systems:
| System | Purpose | Maturity |
|--------|---------|----------|
| **EWS** | 6-dimension risk scoring with guardrails | Production (1000+ determinism tests) |
| **Determinization** | Entropy, confidence decay, conflict detection | Production |
| **RiskEngine** | Signal-specific providers (CVSS/KEV/EPSS) | Production |
The unified score facade **does not replace these systems** . Instead, it:
1. **Combines** EWS scores with Determinization entropy in a single result
2. **Externalizes** EWS weights to versioned manifest files for auditing
3. **Exposes** the unknowns fraction (U) as a first-class metric
---
## 2 · Architecture
```
┌─────────────────────────────────────────────────────────────┐
│ IUnifiedScoreService │
│ (Facade) │
├─────────────────────────────────────────────────────────────┤
│ • ComputeAsync(request) → UnifiedScoreResult │
│ • Combines EWS + Determinization + ConflictDetector │
│ • Loads weights from versioned manifests │
└─────────────┬───────────────────────┬───────────────────────┘
│ │
▼ ▼
┌─────────────────────────┐ ┌─────────────────────────┐
│ EvidenceWeightedScore │ │ Determinization │
│ Calculator │ │ │
├─────────────────────────┤ ├─────────────────────────┤
│ • 6-dimension scoring │ │ • Entropy calculation │
│ • Guardrails (caps/ │ │ • Confidence decay │
│ floors) │ │ • Signal gap tracking │
│ • Anchor metadata │ │ • Fingerprinting │
│ • Policy digest │ │ • Conflict detection │
└─────────────────────────┘ └─────────────────────────┘
│ │
▼ ▼
┌─────────────────────────────────────────────────────────────┐
│ etc/weights/*.json │
│ (Versioned Weight Manifests) │
└─────────────────────────────────────────────────────────────┘
```
---
## 3 · What the Facade Provides
### 3.1 · Unified Score Result
```csharp
public sealed record UnifiedScoreResult
{
// From EWS
public int Score { get ; } // 0-100
public string Bucket { get ; } // ActNow, ScheduleNext, Investigate, Watchlist
public IReadOnlyList < DimensionContribution > Breakdown { get ; }
public AppliedGuardrails Guardrails { get ; }
public string EwsDigest { get ; } // SHA-256 of EWS result
// From Determinization
public double UnknownsFraction { get ; } // U metric (0.0 = complete, 1.0 = no data)
public UnknownsBand UnknownsBand { get ; } // Complete, Adequate, Sparse, Insufficient
public IReadOnlyList < SignalGap > Gaps { get ; }
public IReadOnlyList < SignalConflict > Conflicts { get ; }
public string DeterminizationFingerprint { get ; }
// Combined
public IReadOnlyList < SignalDelta > DeltaIfPresent { get ; } // Impact if missing signals arrive
public string WeightManifestRef { get ; } // version + hash
public DateTimeOffset ComputedAt { get ; }
}
```
### 3.2 · Unknowns Fraction (U)
The `UnknownsFraction` directly exposes Determinization's entropy calculation:
```
U = 1 - (weighted_present_signals / total_weight)
```
| U Range | Band | Meaning | Action |
|---------|------|---------|--------|
| 0.0 – 0.2 | Complete | All signals present | Automated decisions |
| 0.2 – 0.4 | Adequate | Sufficient for evaluation | Automated decisions |
| 0.4 – 0.6 | Sparse | Signal gaps exist | Manual review recommended |
| 0.6 – 1.0 | Insufficient | Critical data missing | Block pending more signals |
Thresholds align with existing Determinization config:
- `RefreshEntropyThreshold: 0.40` → triggers signal refresh
- `ManualReviewEntropyThreshold: 0.60` → requires human review
### 3.3 · Delta-If-Present
When signals are missing, the facade calculates potential score impact:
```json
{
"delta_if_present" : [
{
"signal" : "reachability" ,
"min_impact" : -15 ,
"max_impact" : + 8 ,
"description" : "If reachability confirmed as not-reachable, score decreases by up to 15"
},
{
"signal" : "runtime" ,
"min_impact" : 0 ,
"max_impact" : + 25 ,
"description" : "If runtime execution observed, score increases by up to 25"
}
]
}
```
---
## 4 · Weight Manifests
### 4.1 · Location
Weight manifests are stored in `etc/weights/` with versioned filenames:
```
etc/weights/
├── v2026-01-22.weights.json
├── v2026-02-01.weights.json
└── ...
```
### 4.2 · Schema
```json
{
"version" : "v2026-01-22" ,
"effective_from" : "2026-01-22T00:00:00Z" ,
"description" : "EWS default weights" ,
"weights" : {
"rch" : 0.30 ,
"rts" : 0.25 ,
"bkp" : 0.15 ,
"xpl" : 0.15 ,
"src" : 0.10 ,
"mit" : 0.10
},
"hash" : "sha256:..."
}
```
### 4.3 · Versioning Rules
1. **Immutable once published** – Manifest content never changes after creation
2. **Hash verification** – SHA-256 of canonical JSON ensures integrity
3. **Policy pinning** – Policies can specify `weights_ref` to lock a version
4. **Fallback** – If manifest missing, EWS uses compiled defaults
---
## 5 · Existing Systems (Unchanged)
### 5.1 · EWS Formula (Preserved)
The EWS formula remains unchanged:
```
rawScore = (RCH × w_rch) + (RTS × w_rts) + (BKP × w_bkp) +
(XPL × w_xpl) + (SRC × w_src) - (MIT × w_mit)
finalScore = clamp(rawScore, 0, 1) × 100
```
With guardrails:
- **Speculative cap** (45): RCH=0 and RTS=0
- **Not-affected cap** (15): BKP≥1.0, VEX=not_affected, RTS< 0.6
- ** Runtime floor ** ( 60 ) : RTS ≥ 0 . 8
### 5.2 · Determinization (Preserved)
Entropy and decay calculations remain unchanged:
```
entropy = 1 - (present_weight / total_weight)
decay = max(floor, exp(-ln(2) × age_days / half_life_days))
```
With conflict detection:
- VEX vs Reachability contradiction
- Static vs Runtime contradiction
- Multiple VEX status conflict
- Backport vs Status conflict
---
## 6 · Integration Points
### 6.1 · CLI Commands
```bash
# Existing (enhanced)
stella gate score evaluate --finding-id CVE-2024-1234@pkg:npm/lodash \
--cvss 7.5 --epss 0.15 --reachability function \
--show-unknowns --show-deltas
# New
stella score compute --finding-id CVE-2024-1234@pkg:npm/lodash \
--cvss 7.5 --epss 0.15
stella score explain CVE-2024-1234@pkg:npm/lodash
stella gate score weights list
stella gate score weights show v2026-01-22
stella gate score weights diff v2026-01-22 v2026-02-01
```
### 6.2 · API Endpoints
```
POST /api/v1/score/evaluate # Compute unified score
GET /api/v1/score/{id}/replay # Fetch signed replay proof
GET /api/v1/score/weights # List weight manifests
GET /api/v1/score/weights/{v} # Get specific manifest
```
#### Replay Endpoint Response
The `/score/{id}/replay` endpoint returns a DSSE-signed attestation with payload type `application/vnd.stella.score+json` :
```json
{
"signed_replay_log_dsse" : "BASE64" ,
"rekor_inclusion" : { "logIndex" : 12345 , "rootHash" : "…" },
"canonical_inputs" : [
{ "name" : "sbom.json" , "sha256" : "…" },
{ "name" : "vex.json" , "sha256" : "…" }
],
"transforms" : [
{ "name" : "canonicalize_spdx" , "version" : "1.1" },
{ "name" : "age_decay" , "params" : { "lambda" : 0.02 }}
],
"algebra_steps" : [
{ "signal" : "rch" , "w" : 0.30 , "value" : 0.78 , "term" : 0.234 }
],
"final_score" : 85
}
```
Replay proofs are stored as OCI referrers (" StellaBundle " pattern ) attached to the scored artifact .
### 6.3 · Console UI
Finding detail views show:
- Score with bucket ( existing )
- Unknowns fraction ( U ) with color-coded band
- Delta-if-present for missing signals
- Weight manifest version
---
## 7 · Determinism Guarantees
The facade inherits determinism from underlying systems:
| Aspect | Guarantee |
| -------- | ----------- |
| EWS score | Identical inputs → identical score ( 1000 + iteration tests ) |
| Entropy | Identical signal presence → identical U |
| Fingerprint | Content-addressed SHA-256 |
| Weight manifest | Immutable after creation |
The facade adds no additional sources of non-determinism .
---
## 8 · What We're NOT Doing
- ❌ Replacing EWS formula
- ❌ Replacing Determinization entropy calculation
- ❌ Changing guardrail logic
- ❌ Changing conflict detection
- ❌ Breaking existing CLI commands
- ❌ Breaking existing API contracts
The facade is **additive** – existing functionality continues to work unchanged .