Unknowns Ranking Algorithm Reference

Audience: Signals Guild, Policy authors tuning triage, operators configuring rescan cadence.

Scope: The multi-factor scoring algorithm that ranks and triages unknowns in the Stella Ops Signals module — factors, weights, band assignment, determinism, and configuration.

Purpose

When reachability analysis encounters unresolved symbols, edges, or package identities, these are recorded as unknowns. The ranking algorithm prioritizes unknowns by computing a composite score from five factors, then assigns each to a triage band (HOT/WARM/COLD) that determines rescan scheduling and escalation policies.

Scoring Formula

The composite score is computed as:

Score = wP × P + wE × E + wU × U + wC × C + wS × S

Where:

All factors are normalized to [0.0, 1.0] before weighting. The final score is clamped to [0.0, 1.0].

Default Weights

FactorWeightDescription
wP0.25Popularity weight
wE0.25Exploit potential weight
wU0.25Uncertainty density weight
wC0.15Centrality weight
wS0.10Staleness weight

Weights must sum to 1.0 and are configurable via Signals:UnknownsScoring settings.

Factor Details

Factor P: Popularity (Deployment Impact)

Measures how widely the unknown’s package is deployed across monitored environments.

Formula:

P = min(1, log10(1 + deploymentCount) / log10(1 + maxDeployments))

Parameters:

Rationale: Logarithmic scaling prevents a single highly-deployed package from dominating scores while still prioritizing widely-used dependencies.

Factor E: Exploit Potential (CVE Severity)

Estimates the consequence severity if the unknown resolves to a vulnerable component.

Current Implementation:

Planned Enhancements:

Factor U: Uncertainty Density (Flag Accumulation)

Aggregates uncertainty signals from multiple sources. Each flag contributes a weighted penalty.

Flag Weights:

FlagWeightDescription
NoProvenanceAnchor0.30Cannot verify package source
VersionRange0.25Version specified as range, not exact
DynamicCallTarget0.25Reflection, eval, or dynamic dispatch
ConflictingFeeds0.20Contradictory info from different feeds
ExternalAssembly0.20Assembly outside analysis scope
MissingVector0.15No CVSS vector for severity assessment
UnreachableSourceAdvisory0.10Source advisory URL unreachable

Formula:

U = min(1.0, sum(activeFlags × flagWeight))

Example:

Factor C: Centrality (Graph Position Importance)

Measures the unknown’s position importance in the call graph using betweenness centrality.

Formula:

C = min(1.0, betweenness / maxBetweenness)

Parameters:

Rationale: High-betweenness nodes appear on many shortest paths, meaning they’re likely to be reached regardless of entry point.

Related Metrics:

Factor S: Staleness (Evidence Age)

Measures how old the evidence is since the last successful analysis attempt.

Formula:

S = min(1.0, daysSinceLastAnalysis / maxDays)

With exponential decay enhancement (optional):

S = 1 - exp(-daysSinceLastAnalysis / tau)

Parameters:

Special Cases:

Band Assignment

Based on the composite score, unknowns are assigned to triage bands:

BandThresholdRescan PolicyDescription
HOTScore >= 0.7015 minutesImmediate rescan + VEX escalation
WARM0.40 <= Score < 0.7024 hoursScheduled rescan within 12-72h
COLDScore < 0.407 daysWeekly batch processing

Thresholds are configurable:

Signals:
  UnknownsScoring:
    HotThreshold: 0.70
    WarmThreshold: 0.40

Scheduler Integration

The UnknownsRescanWorker processes unknowns based on their band:

HOT Band Processing

WARM Band Processing

COLD Band Processing

Normalization Trace

Each scored unknown includes a NormalizationTrace for debugging and replay:

{
  "rawPopularity": 42,
  "normalizedPopularity": 0.65,
  "popularityFormula": "min(1, log10(1 + 42) / log10(1 + 100))",

  "rawExploitPotential": 0.5,
  "normalizedExploitPotential": 0.5,

  "rawUncertainty": 0.55,
  "normalizedUncertainty": 0.55,
  "activeFlags": ["NoProvenanceAnchor", "VersionRange"],

  "rawCentrality": 250.0,
  "normalizedCentrality": 0.25,

  "rawStaleness": 7,
  "normalizedStaleness": 0.5,

  "weights": {
    "wP": 0.25,
    "wE": 0.25,
    "wU": 0.25,
    "wC": 0.15,
    "wS": 0.10
  },
  "finalScore": 0.52,
  "assignedBand": "Warm",
  "computedAt": "2025-12-15T10:00:00Z"
}

Replay Capability: Given the trace, the exact score can be recomputed:

Score = 0.25×0.65 + 0.25×0.5 + 0.25×0.55 + 0.15×0.25 + 0.10×0.5
      = 0.1625 + 0.125 + 0.1375 + 0.0375 + 0.05
      = 0.5125 ≈ 0.52

API Endpoints

Query Unknowns by Band

GET /api/signals/unknowns?band=hot&limit=50&offset=0

Response:

{
  "items": [
    {
      "id": "unk-123",
      "subjectKey": "myapp|1.0.0",
      "purl": "pkg:npm/lodash@4.17.21",
      "score": 0.82,
      "band": "Hot",
      "flags": { "noProvenanceAnchor": true, "versionRange": true },
      "nextScheduledRescan": "2025-12-15T10:15:00Z"
    }
  ],
  "total": 15,
  "hasMore": false
}

Get Score Explanation

GET /api/signals/unknowns/{id}/explain

Response:

{
  "unknown": { /* full UnknownSymbolDocument */ },
  "normalizationTrace": { /* trace object */ },
  "factorBreakdown": {
    "popularity": { "raw": 42, "normalized": 0.65, "weighted": 0.1625 },
    "exploitPotential": { "raw": 0.5, "normalized": 0.5, "weighted": 0.125 },
    "uncertainty": { "raw": 0.55, "normalized": 0.55, "weighted": 0.1375 },
    "centrality": { "raw": 250, "normalized": 0.25, "weighted": 0.0375 },
    "staleness": { "raw": 7, "normalized": 0.5, "weighted": 0.05 }
  },
  "bandThresholds": { "hot": 0.70, "warm": 0.40 }
}

Configuration Reference

Signals:
  UnknownsScoring:
    # Factor weights (must sum to 1.0)
    WeightPopularity: 0.25
    WeightExploitPotential: 0.25
    WeightUncertainty: 0.25
    WeightCentrality: 0.15
    WeightStaleness: 0.10

    # Popularity normalization
    PopularityMaxDeployments: 100

    # Uncertainty flag weights
    FlagWeightNoProvenance: 0.30
    FlagWeightVersionRange: 0.25
    FlagWeightConflictingFeeds: 0.20
    FlagWeightMissingVector: 0.15
    FlagWeightUnreachableSource: 0.10
    FlagWeightDynamicTarget: 0.25
    FlagWeightExternalAssembly: 0.20

    # Centrality normalization
    CentralityMaxBetweenness: 1000.0

    # Staleness normalization
    StalenessMaxDays: 14
    StalenessTau: 14  # For exponential decay

    # Band thresholds
    HotThreshold: 0.70
    WarmThreshold: 0.40

    # Rescan scheduling
    HotRescanMinutes: 15
    WarmRescanHours: 24
    ColdRescanDays: 7

  UnknownsDecay:
    # Nightly batch decay
    BatchEnabled: true
    MaxSubjectsPerBatch: 1000
    ColdBatchDay: Sunday

Determinism Requirements

The scoring algorithm is fully deterministic:

  1. Same inputs produce identical scores - Given identical UnknownSymbolDocument, deployment counts, and graph metrics, the score will always be the same
  2. Normalization trace enables replay - The trace contains all raw values and weights needed to reproduce the score
  3. Timestamps use UTC ISO 8601 - All ComputedAt, LastAnalyzedAt, and NextScheduledRescan timestamps are UTC
  4. Weights logged per computation - The trace includes the exact weights used, allowing audit of configuration changes

Database Schema

-- Unknowns table (enhanced)
CREATE TABLE signals.unknowns (
    id UUID PRIMARY KEY,
    subject_key TEXT NOT NULL,
    purl TEXT,
    symbol_id TEXT,
    callgraph_id TEXT,

    -- Scoring factors
    popularity_score FLOAT DEFAULT 0,
    deployment_count INT DEFAULT 0,
    exploit_potential_score FLOAT DEFAULT 0,
    uncertainty_score FLOAT DEFAULT 0,
    centrality_score FLOAT DEFAULT 0,
    degree_centrality INT DEFAULT 0,
    betweenness_centrality FLOAT DEFAULT 0,
    staleness_score FLOAT DEFAULT 0,
    days_since_last_analysis INT DEFAULT 0,

    -- Composite score and band
    score FLOAT DEFAULT 0,
    band TEXT DEFAULT 'cold' CHECK (band IN ('hot', 'warm', 'cold')),

    -- Metadata
    flags JSONB DEFAULT '{}',
    normalization_trace JSONB,
    rescan_attempts INT DEFAULT 0,
    last_rescan_result TEXT,

    -- Timestamps
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    last_analyzed_at TIMESTAMPTZ,
    next_scheduled_rescan TIMESTAMPTZ
);

-- Indexes for band-based queries
CREATE INDEX idx_unknowns_band ON signals.unknowns(band);
CREATE INDEX idx_unknowns_score ON signals.unknowns(score DESC);
CREATE INDEX idx_unknowns_next_rescan ON signals.unknowns(next_scheduled_rescan)
    WHERE next_scheduled_rescan IS NOT NULL;
CREATE INDEX idx_unknowns_subject ON signals.unknowns(subject_key);

Metrics and Observability

The following metrics are exposed for monitoring:

MetricTypeDescription
signals_unknowns_totalGaugeTotal unknowns by band
signals_unknowns_rescans_totalCounterRescans triggered by band
signals_unknowns_scoring_duration_secondsHistogramScoring computation time
signals_unknowns_band_transitions_totalCounterBand changes (e.g., WARM->HOT)

Runtime Updated Events

Sprint: SPRINT_20260112_008_SIGNALS_runtime_telemetry_events

When runtime observations change for a CVE and product pair, the Signals module emits runtime.updated events to drive policy reanalysis of unknowns.

Event Types

Event TypeConstantDescription
runtime.updatedRuntimeEventTypes.UpdatedRuntime observations changed for a subject
runtime.ingestedRuntimeEventTypes.IngestedNew runtime observation batch ingested
runtime.confirmedRuntimeEventTypes.ConfirmedRuntime fact confirmed by additional evidence
runtime.exploit_detectedRuntimeEventTypes.ExploitDetectedExploit behavior detected at runtime

Update Types

TypeDescription
NewObservationFirst runtime observation for a subject
StateChangeReachability state changed from previous observation
ConfidenceIncreaseAdditional hits increased confidence score
NewCallPathPreviously unseen call path observed
ExploitTelemetryExploit behavior detected (always triggers reanalysis)

Event Schema

{
  "eventId": "sha256:abc123...",         // Deterministic based on content
  "eventType": "runtime.updated",
  "version": "1.0.0",
  "tenant": "default",
  "cveId": "CVE-2026-1234",              // Optional
  "purl": "pkg:npm/lodash@4.17.21",      // Optional
  "subjectKey": "cve:CVE-2026-1234|purl:pkg:npm/lodash@4.17.21",
  "callgraphId": "cg-scan-001",
  "evidenceDigest": "sha256:def456...",  // Digest of runtime evidence
  "updateType": "NewCallPath",
  "previousState": "observed",           // Null for new observations
  "newState": "observed",
  "confidence": 0.85,                    // 0.0-1.0
  "fromRuntime": true,
  "runtimeMethod": "ebpf",               // "ebpf", "agent", "probe"
  "observedNodeHashes": ["sha256:...", "sha256:..."],
  "pathHash": "sha256:...",              // Optional
  "triggerReanalysis": true,
  "reanalysisReason": "New call path observed at runtime",
  "occurredAtUtc": "2026-01-15T10:30:00Z",
  "traceId": "abc123"                    // Optional correlation ID
}

Reanalysis Triggers

The triggerReanalysis flag is set to true when:

  1. Exploit telemetry detected (always triggers)
  2. State change from previous observation
  3. High-confidence runtime observation (confidence >= 0.8 and fromRuntime=true)
  4. New observation (no previous runtime data)

Event Emission Points

Runtime updated events are emitted from:

  1. RuntimeFactsIngestionService.IngestAsync - After runtime facts are persisted
  2. ReachabilityScoringService - When scores are recomputed with new runtime data

Deterministic Event IDs

Event IDs are computed deterministically using SHA-256 of:

This ensures idempotent event handling and deduplication.