pub struct Verdict {Show 36 fields
pub verdict_id: String,
pub verdict: String,
pub source: String,
pub confidence: f64,
pub composite_score: u8,
pub reasoning: String,
pub similar_to: Vec<String>,
pub evidence_gaps: Vec<String>,
pub suggested_actions: Vec<String>,
pub data_freshness_at: Option<String>,
pub data_oldest_signal_at: Option<String>,
pub stale_since_at: Option<String>,
pub staleness_reason: Option<String>,
pub computed_at: Option<String>,
pub severity: Option<String>,
pub decision: Option<String>,
pub previous_verdict: Option<PreviousVerdict>,
pub availability: AvailabilityBlock,
pub envelope_version: Option<u32>,
pub customer_state: Option<String>,
pub state: Option<String>,
pub source_state: Option<String>,
pub policy_version: Option<String>,
pub audit_record_id: Option<String>,
pub audit_record_hash: Option<String>,
pub attestation: Option<Value>,
pub evidence: Option<Value>,
pub composition: Option<Value>,
pub rich_data: Option<RichData>,
pub top_findings: Option<TopFindings>,
pub reason_class: Option<String>,
pub attestation_status: Option<String>,
pub freshness: Option<Freshness>,
pub remediation: Option<Remediation>,
pub axes: Option<Axes>,
pub matched_rule_id: Option<String>,
}Fields§
§verdict_id: String§verdict: StringALLOWED_NO_FINDINGS | VECTOR_VERDICT | DM_THRESHOLD_BLOCK |
INSUFFICIENT_DATA per locked Verdict-label enum.
CLEANLIB-468: tolerant deserialize. The envelope-v2 customer-verdict wire
(Path A) sends verdict as a nested OBJECT {type,status,customer_state},
while POST /v1/scan (ScanResult) and the bincode verdict cache
send/store it as a flat STRING. A bare String field errored on the
object — invalid type: map, expected a string — the CLEANLIB-462 live
CLI break. [de_verdict_label] accepts EITHER form (object → its type;
string → as-is) and is format-aware via is_human_readable: JSON uses the
string-or-map visitor, bincode (non-self-describing, positional) uses
deserialize_string so the cache round-trip is unaffected. Serialize is
unchanged (emits the flat string). No dependency on the App verdict_label
field or on deploy ordering.
source: String§confidence: f64§composite_score: u8§reasoning: String§similar_to: Vec<String>§evidence_gaps: Vec<String>§suggested_actions: Vec<String>§data_freshness_at: Option<String>§data_oldest_signal_at: Option<String>§stale_since_at: Option<String>§staleness_reason: Option<String>§computed_at: Option<String>§severity: Option<String>App-canonical severity tier (NONE | LOW | MEDIUM | HIGH |
CRITICAL per cleanlib-core::Severity). Cycle-9 Lane-2 M1 close.
Option<String> for serde-default tolerance against pre-M1 payloads.
decision: Option<String>Coarse gating decision (ALLOW | WARN | DENY |
RISK_ACCEPTANCE_REQUIRED). Sister of js/py/go SDK carrying.
Cycle-9 Lane-2 M1 close. Optional for serde-default tolerance.
previous_verdict: Option<PreviousVerdict>Prior-verdict comparison shape; envelope emits null when no prior
verdict exists. v0.1.3 parity-ripple with sdk-go::PreviousVerdict
(cycle-13 M1’ ship). NOTE: no skip_serializing_if — Verdict is
bincode-serialized by cleanlib-cli PersistentCache, which is
positional and breaks if fields are conditionally omitted. JSON
consumers see previous_verdict: null which matches the App’s
canonical envelope shape.
availability: AvailabilityBlockCycle-15 observability honesty signal. Non-Optional per CLEANLIB-104
App-3.1 Gate M3 flip (2026-07-01). Sister of
cleanlib_core::AvailabilityBlock.
Serde #[serde(default)] at the struct level (line 21) provides
fail-open: pre-M3 payloads omitting the availability key
deserialize to AvailabilityBlock::default() (degraded_stale = false).
This preserves compatibility with pre-cycle-15 payloads AND happy-path
verdicts that previously omitted the block via None.
envelope_version: Option<u32>Envelope schema version — 2 on envelope-v2 responses, None on v1.
customer_state: Option<String>Shipped CLEANLIB-178 OUTPUT display taxonomy (clean … blocked_by_policy),
hoisted server-side so the client skips from_wire on the happy path.
state: Option<String>Coarse client UX status (BLOCKED | WARN | ALLOWED | UNKNOWN |
RISK_ACCEPTANCE_REQUIRED).
source_state: Option<String>Producer wire 8-enum (DM_THRESHOLD_BLOCK …) — canonical forward name for
source (retained above for v1 back-compat).
policy_version: Option<String>Active policy bundle version, promoted to top-level in v2.
audit_record_id: Option<String>FK (ULID) to the frozen WORM audit record.
audit_record_hash: Option<String>hex SHA-256 — tamper-evident binding to the audit record’s content_hash.
attestation: Option<Value>CLEANLIB-496 (C1): the signed attestation the App emits — the full
SignedAttestation object {attestation:{…10 fields…}, signature_b64, key_id}. Carried as a passthrough Value (not a mirrored typed struct)
so the CLI --output json/--output sarif can surface it verbatim for
verify_attestation.py without duplicating the cosign-signer schema.
None on v1 / unsigned responses.
evidence: Option<Value>CLEANLIB-518 §4: enhanced-verdict evidence[] the App emits under the
CLEANLIB_ENHANCED_VERDICT flag (typed Evidence items in stream1).
Carried as a passthrough Value (shape-agnostic) — same tolerant
discipline as attestation above — so verdict_to_envelope can surface
it verbatim into rich_data.evidence ahead of the A-anchor shape
finalizing (CLEANLIB-525). A rigid typed Vec<Evidence> can replace this
later without a wire break (additive: unknown fields already tolerated,
no deny_unknown_fields). None when the flag is off / on v1.
composition: Option<Value>CLEANLIB-518 §4: enhanced-verdict composition{} object (dependency /
provenance composition breakdown) the App emits under the same flag.
Same passthrough discipline as evidence above; surfaced verbatim into
rich_data.composition. None when absent.
rich_data: Option<RichData>CLEANLIB-601 (Option B): the App’s typed rich_data block, preserved so
the CLI fix command can read rich_data.recommended_version as a typed
field instead of parsing the suggested_actions marker string (Option A).
Both A+B are defense-in-depth: the typed field lands cleanly, and the
marker survives even if the App refactors the struct. None on v1 /
responses without the block. Appended at the tail to keep bincode
(cleanlib-cli PersistentCache) field order stable for existing entries;
the cache get() self-heals on struct-shape change (deser-fail → miss).
top_findings: Option<TopFindings>CLEANLIB-613: the App’s top_findings block — the per-CVE findings that
back a VECTOR_VERDICT/DENY, each carrying a structured fixed_version.
This is the App’s ALWAYS-populated remediation source, unlike the
flag-gated rich_data.recommended_version (Option B) which was dark in
prod. Previously UNMODELED — so the CLI fix command discarded the App’s
own answer at deserialize and produced no_recommendation for known-
vulnerable packages while exiting 0 (a fail-open). fix now reads the
cumulative (max) top_findings.findings[].fixed_version as the upgrade
target. Tolerant-passthrough: Option + struct-level #[serde(default)],
no deny_unknown_fields, so a v1 payload or a future field never fails the
reader. Appended at the tail to keep the bincode (PersistentCache) field
order stable; the cache get() self-heals on struct-shape change.
reason_class: Option<String>CLEANLIB-652 (CX-3) part 1: machine-stable reason a verdict is non-clean,
hoisted top-level on envelope-v2 (App reason_class). Exactly one of
CVE_AFFECTING | CVE_ON_KEV | CVE_ON_RANSOMWARE | MALICIOUS_TRIAGE |
POLICY_DENY | INSUFFICIENT_DATA | RANGE_NOT_RESOLVED, or None on a
clean verdict (no reason owed) and on v1. Values are already codename-clean
(e.g. POLICY_DENY, not the engine type name) so the MACHINE surface passes
them through verbatim; the HUMAN surface maps to friendly text via
crate::customer_state-style rendering in cleanlib-cli, with a safe
generic for any unknown future value (forward-compat — no
deny_unknown_fields). Distinct from errors::Problem.reason_class, which
is an RFC-7807 error-branch key on the error path.
attestation_status: Option<String>CLEANLIB-652 (CX-3) part 1: renderable attestation status (App
attestation_status) — signed | signature_absent as emitted by the App.
signature_invalid is reserved for the CLIENT to set when local verification
of Verdict::attestation fails (producer/consumer split). None on v1.
Machine surface passthrough; human surface maps to Signed / Unsigned /
Invalid signature. Appended at the tail to keep the bincode (PersistentCache)
field order stable for existing entries.
freshness: Option<Freshness>CLEANLIB-652 (CX-3) part 2: per-axis data freshness — the App’s nested
freshness block (App verbs::Freshness) with SEPARATE ages for the CVE,
behavioral, and policy axes, plus stalest_axis and overall_as_of (= MIN
of the non-null axis ages, Client-confirmed 652 c767287). Additive: it
ENRICHES the existing flat data_freshness_at / data_oldest_signal_at /
stale_since_at fields, it does not replace them. None on v1 / pre-part-2.
Tail-appended to keep the bincode (PersistentCache) field order stable.
remediation: Option<Remediation>CLEANLIB-652 (CX-3) part 3a: structured remediation — a single upgrade
target (App verbs::Remediation) so the client renders an actionable
upgrade without parsing prose. None when no safe upgrade exists (honest —
matches the App’s “no fix to recommend” semantics, distinct from
top_findings[].fixed_version / rich_data.recommended_version which fix
still uses). Tail-appended for bincode field-order stability.
axes: Option<Axes>CLEANLIB-780: the per-axis result envelope (App axes), reporting the
advisory / threat / availability planes SEPARATELY so a consulted-and-empty
advisory reads as an honest GREEN (“checked, none found”) rather than an
alarmist data-gap, and a behavioral-malicious signal is not collapsed into
the advisory verdict. v2-gated + skip-None on the wire → None on v1.
Tail-appended for bincode (PersistentCache) field-order stability; the cache
get() self-heals on struct-shape change (deser-fail → miss). See Axes.
matched_rule_id: Option<String>CLEANLIB-855: the policy rule ID that produced a DM_THRESHOLD_BLOCK/
DM_THRESHOLD_WARN verdict, when one did. None on every other
source (Vector/CVE/absence paths never match a customer policy rule)
and on pre-855 App builds. A curated supply-chain-compromise-bridge-*
prefix distinguishes a Compromised render from the generic
BlockedByPolicy — see [crate::customer_state::CustomerState:: from_block_origin] and crate::customer_state::is_curated_supply_chain_compromise.
Tail-appended for bincode (PersistentCache) field-order stability.