10 KiB
VEX Consensus and Issuer Trust
This document consolidates the VEX concepts StellaOps relies on: ingesting upstream VEX without rewriting it, correlating evidence across sources, and producing a deterministic, explainable "effective" status for a component-vulnerability pair.
Scope
- VEX ingestion and provenance (what is stored and why)
- Correlation (linksets) versus consensus (effective status)
- Issuer trust and offline operation
This is not an API reference; module dossiers define concrete schemas and endpoints.
Vocabulary (Minimal)
- VEX statement: a claim about vulnerability status for a product/component (for example:
affected,fixed,not_affected,under_investigation). - Observation: an immutable record of a single upstream VEX document as received (including provenance and raw payload).
- Linkset: a deterministic correlation group that ties together statements that refer to the same
(vulnerabilityId, productKey)across providers. - Consensus decision (effective VEX): the platform's deterministic result after policy rules evaluate available VEX/advisory/reachability evidence.
Observation Model (Link, Not Merge)
StellaOps treats upstream VEX as append-only evidence.
An observation records:
- Provenance: tenant, provider/issuer identity, receive timestamps (UTC), signature status, and content hash.
- Raw payload: stored losslessly so auditors and operators can retrieve exactly what was ingested.
- Derived tuples: extracted
(vulnerabilityId, productKey, status, justification?, version hints, references)used for correlation and UI presentation.
An observation is never mutated. If upstream publishes a revision, StellaOps stores a new observation and records a supersedes relationship.
Linksets (Correlation Without Consensus)
Linksets exist to make multi-source evidence explainable without collapsing it:
- Group statements that likely refer to the same product-vulnerability pair.
- Preserve conflicts (status disagreements, justification divergence, version range clashes) as first-class facts.
- Provide stable IDs generated from canonical, sorted inputs (deterministic hashing).
Linksets do not invent consensus; they only align evidence so downstream layers (Policy/Console/Exports) can explain what is known and what disagrees.
Consensus (Effective Status)
The effective VEX status is computed by policy evaluation using:
- Correlated VEX evidence (observations + linksets)
- Advisory evidence (observations/linksets from Concelier)
- Optional reachability and other signals
Key properties:
- Deterministic: the same inputs yield the same output.
- Explainable: the decision includes an explanation trace and evidence references.
- Uncertainty-aware: when critical evidence is missing or conflicts are unresolved, the result can remain
under_investigationinstead of implying safety.
Aggregation-Only Guardrails (AOC)
To avoid hidden rewriting of upstream data, the platform enforces:
- Raw-first storage: upstream payloads are stored as received; normalized projections are derived but do not replace raw data.
- No merge of sources: each provider's statements remain independently addressable.
- Provenance is mandatory: missing provenance or unverifiable signatures are surfaced as ingestion failures or warnings (policy-driven).
- Idempotent writes: identical content hashes do not create duplicate observations.
- Deterministic outputs: stable ordering and canonical hashing for linksets and exports.
Issuer Directory and Trust
Issuer trust is a first-class input:
- Issuers are identified by stable provider IDs and, where applicable, cryptographic identity (certificate chain, key id, transparency proof).
- The issuer directory defines which issuers are trusted per tenant/environment and how they are weighted/accepted by policy.
- Offline sites carry required trust material (roots and allowlists) inside the Offline Kit so verification does not require network access.
Console Integration
The Console uses these concepts to keep VEX explainable:
- VEX views show provider provenance, signature/issuer status, and snapshot timestamps.
- Conflicts are displayed as conflicts (what disagrees and why), not silently resolved in the UI.
- The effective VEX status shown in triage views links back to underlying observations/linksets and the policy explanation.
See docs/UI_GUIDE.md for the operator workflow perspective.
Anchor-Aware Mode (v1.1)
Sprint: SPRINT_20260112_004_BE_policy_determinization_attested_rules
Anchor-aware mode enforces cryptographic attestation requirements on VEX proofs used for allow decisions.
VexProofGate Options
| Option | Type | Default | Strict Mode |
|---|---|---|---|
AnchorAwareMode |
bool | false |
true |
RequireVexAnchoring |
bool | false |
true |
RequireRekorVerification |
bool | false |
true |
RequireSignedStatements |
bool | false |
true |
RequireProofForFixed |
bool | false |
true |
MaxAllowedConflicts |
int | 5 |
0 |
MaxProofAgeHours |
int | 168 |
72 |
Strict Anchor-Aware Preset
For production environments requiring maximum security:
var options = VexProofGateOptions.StrictAnchorAware;
// Enables: RequireVexAnchoring, RequireRekorVerification,
// RequireSignedStatements, RequireProofForFixed
// Sets: MinimumConfidenceTier=high, MaxAllowedConflicts=0, MaxProofAgeHours=72
Metadata Keys
When passing VEX proof context through policy evaluation:
| Key | Type | Description |
|---|---|---|
vex_proof_anchored |
bool | Whether proof has DSSE anchoring |
vex_proof_envelope_digest |
string | DSSE envelope sha256 digest |
vex_proof_rekor_verified |
bool | Whether Rekor transparency verified |
vex_proof_rekor_log_index |
long | Rekor log index if verified |
Failure Reasons
| Reason | Description |
|---|---|
vex_not_anchored |
VEX proof requires DSSE anchoring but is not anchored |
rekor_verification_missing |
VEX proof requires Rekor verification but not verified |
VEX Change Events
Sprint: SPRINT_20260112_006_EXCITITOR_vex_change_events
Excititor emits deterministic events when VEX statements change, enabling policy reanalysis.
Event Types
| Event | Description | Policy Trigger |
|---|---|---|
vex.statement.added |
New statement ingested | Immediate reanalysis |
vex.statement.superseded |
Statement replaced | Immediate reanalysis |
vex.statement.conflict |
Status disagreement detected | Queue for review |
vex.status.changed |
Effective status changed | Immediate reanalysis |
Conflict Detection
Conflicts are detected when multiple providers report different statuses for the same vulnerability-product pair:
| Conflict Type | Description |
|---|---|
status_mismatch |
Different status values (e.g., affected vs not_affected) |
trust_tie |
Equal trust scores with different recommendations |
supersession_conflict |
Disagreement on which statement supersedes |
Event Ordering
Events follow deterministic ordering:
- Ordered by timestamp (ascending)
- Conflict events after related statement events
- Same-timestamp events sorted by provider ID
Integration with Policy
Subscribe to VEX events for automatic reanalysis:
subscriptions:
- event: vex.statement.*
action: reanalyze
filter:
trustScore: { $gte: 0.7 }
See Excititor Architecture for full event schemas.
Offline / Air-Gap Operation
- VEX observations/linksets are included in Offline Kit snapshots with content hashes and timestamps.
- Verification workflows (signatures, issuer trust) must work offline using bundled trust roots and manifests.
- The Console should surface snapshot identity and staleness budgets when operating offline.
Related Docs
docs/modules/excititor/architecture.mddocs/modules/vex-lens/architecture.mddocs/ARCHITECTURE_OVERVIEW.mddocs/OFFLINE_KIT.mddocs/modules/policy/determinization-api.md
Grey Queue and Unknown Mapping
Sprint: SPRINT_20260112_004_POLICY_unknowns_determinization_greyqueue
When VEX correlation produces inconclusive results, observations are routed to the Grey Queue for monitoring or manual adjudication.
Mapping to OpenVEX Status
Uncertain observations preserve OpenVEX spec alignment:
| Internal State | OpenVEX Status | Description |
|---|---|---|
PendingDeterminization |
under_investigation |
Evidence incomplete; monitoring active |
Disputed |
under_investigation |
Conflicting evidence from multiple sources |
GuardedPass |
under_investigation |
Allowed with runtime guardrails |
VEX Conflict Types
The Grey Queue surfaces VEX-specific conflicts:
| Conflict | Example | Resolution Path |
|---|---|---|
| Status mismatch | Vendor says not_affected, distro says affected |
Trust-weighted consensus or manual |
| Justification gap | Status not_affected but no justification provided |
Request clarification or manual |
| Version range conflict | Overlapping but different affected ranges | Manual analysis |
| Supersession dispute | Multiple statements claim to supersede | Timestamp and trust resolution |
Deterministic Conflict Detection
Conflicts are detected via structured comparison:
- Same vulnerability, same product, different status →
VexStatusConflict - VEX not_affected + confirmed reachability →
VexReachabilityContradiction - Multiple issuers, equal trust, opposite conclusions →
TrustTie
Console Behavior for Grey Queue
When displaying Grey Queue items:
- Show the observation state badge (e.g., "Pending" or "Disputed")
- Display all conflicting sources with provider identity
- Surface the reanalysis fingerprint for reproducibility
- List pending triggers awaiting data
- Provide action buttons for manual resolution
Offline Grey Queue
In offline/air-gap mode:
- Grey Queue state is included in Offline Kit snapshots
- Manual adjudications are recorded locally and synced on reconnection
- Staleness budgets apply to pending determinations
- Conflict detection works with cached issuer trust data