namespace StellaOps.Policy.Engine.ReachabilityFacts; /// /// HTTP client interface for fetching reachability facts from Signals service. /// public interface IReachabilityFactsSignalsClient { /// /// Gets a reachability fact by subject key. /// /// Subject key (scan ID or component key). /// Cancellation token. /// The reachability fact document, or null if not found. Task GetBySubjectAsync( string subjectKey, CancellationToken cancellationToken = default); /// /// Gets multiple reachability facts by subject keys. /// /// Subject keys to lookup. /// Cancellation token. /// Dictionary of subject key to fact. Task> GetBatchBySubjectsAsync( IReadOnlyList subjectKeys, CancellationToken cancellationToken = default); /// /// Triggers recomputation of reachability for a subject. /// /// Recompute request. /// Cancellation token. /// True if recompute was triggered. Task TriggerRecomputeAsync( SignalsRecomputeRequest request, CancellationToken cancellationToken = default); /// /// Gets a reachability fact with its associated subgraph slice. /// Fetches from Signals for the fact and ReachGraph Store for the subgraph. /// /// Subject key (scan ID or component key). /// Optional CVE ID to slice by. /// Cancellation token. /// The reachability fact with subgraph, or null if not found. Task GetWithSubgraphAsync( string subjectKey, string? cveId = null, CancellationToken cancellationToken = default); } /// /// Response containing both the reachability fact and its subgraph slice. /// public sealed record ReachabilityFactWithSubgraph( SignalsReachabilityFactResponse Fact, ReachGraphSlice? Subgraph); /// /// Represents a slice of the reachability graph for a specific query. /// public sealed record ReachGraphSlice { /// /// Schema version. /// public string? SchemaVersion { get; init; } /// /// Slice query information. /// public ReachGraphSliceQuery? SliceQuery { get; init; } /// /// Parent graph digest. /// public string? ParentDigest { get; init; } /// /// BLAKE3 digest of this slice. /// public string? Digest { get; init; } /// /// Nodes in the slice. /// public List? Nodes { get; init; } /// /// Edges in the slice. /// public List? Edges { get; init; } /// /// Number of nodes. /// public int NodeCount { get; init; } /// /// Number of edges. /// public int EdgeCount { get; init; } /// /// Sink node IDs. /// public List? Sinks { get; init; } /// /// Paths from entrypoints to sinks. /// public List? Paths { get; init; } } /// /// Slice query information. /// public sealed record ReachGraphSliceQuery { public string? Type { get; init; } public string? Query { get; init; } public string? Cve { get; init; } } /// /// Node in a reachability graph slice. /// public sealed record ReachGraphSliceNode { public string? Id { get; init; } public string? Kind { get; init; } public string? Ref { get; init; } public string? File { get; init; } public int? Line { get; init; } public bool IsEntrypoint { get; init; } public bool IsSink { get; init; } } /// /// Edge in a reachability graph slice. /// public sealed record ReachGraphSliceEdge { public string? From { get; init; } public string? To { get; init; } public ReachGraphEdgeExplanation? Why { get; init; } } /// /// Edge explanation in a reachability graph. /// public sealed record ReachGraphEdgeExplanation { public string? Type { get; init; } public string? Loc { get; init; } public string? Guard { get; init; } public double Confidence { get; init; } } /// /// Path from entrypoint to sink. /// public sealed record ReachGraphPath { public string? Entrypoint { get; init; } public string? Sink { get; init; } public List? Hops { get; init; } public List? Edges { get; init; } } /// /// Response from Signals /facts/{subjectKey} endpoint. /// Maps to ReachabilityFactDocument in Signals module. /// public sealed record SignalsReachabilityFactResponse { /// /// Document ID. /// public string Id { get; init; } = string.Empty; /// /// Callgraph ID. /// public string CallgraphId { get; init; } = string.Empty; /// /// Subject information. /// public SignalsSubject? Subject { get; init; } /// /// Entry points. /// public List? EntryPoints { get; init; } /// /// Reachability states. /// public List? States { get; init; } /// /// Runtime facts. /// public List? RuntimeFacts { get; init; } /// /// CAS URI for runtime-facts batch artifact. /// public string? RuntimeFactsBatchUri { get; init; } /// /// BLAKE3 hash of runtime-facts batch. /// public string? RuntimeFactsBatchHash { get; init; } /// /// Additional metadata. /// public Dictionary? Metadata { get; init; } /// /// Context facts for provenance. /// public SignalsContextFacts? ContextFacts { get; init; } /// /// Uncertainty information. /// public SignalsUncertainty? Uncertainty { get; init; } /// /// Edge bundle references. /// public List? EdgeBundles { get; init; } /// /// Whether quarantined edges exist. /// public bool HasQuarantinedEdges { get; init; } /// /// Reachability score. /// public double Score { get; init; } /// /// Risk score. /// public double RiskScore { get; init; } /// /// Count of unknowns. /// public int UnknownsCount { get; init; } /// /// Unknowns pressure. /// public double UnknownsPressure { get; init; } /// /// Computation timestamp. /// public DateTimeOffset ComputedAt { get; init; } /// /// Subject key. /// public string SubjectKey { get; init; } = string.Empty; } /// /// Subject information from Signals. /// public sealed record SignalsSubject { public string? ImageDigest { get; init; } public string? Component { get; init; } public string? Version { get; init; } public string? ScanId { get; init; } } /// /// Reachability state from Signals. /// public sealed record SignalsReachabilityState { public string Target { get; init; } = string.Empty; public bool Reachable { get; init; } public double Confidence { get; init; } public string Bucket { get; init; } = "unknown"; public string? LatticeState { get; init; } public string? PreviousLatticeState { get; init; } public double Weight { get; init; } public double Score { get; init; } public List? Path { get; init; } public SignalsEvidence? Evidence { get; init; } public DateTimeOffset? LatticeTransitionAt { get; init; } } /// /// Evidence from Signals. /// public sealed record SignalsEvidence { public List? RuntimeHits { get; init; } public List? BlockedEdges { get; init; } } /// /// Runtime fact from Signals. /// public sealed record SignalsRuntimeFact { public string SymbolId { get; init; } = string.Empty; public string? CodeId { get; init; } public string? SymbolDigest { get; init; } public string? Purl { get; init; } public string? BuildId { get; init; } public int HitCount { get; init; } public DateTimeOffset? ObservedAt { get; init; } } /// /// Context facts from Signals. /// public sealed record SignalsContextFacts; /// /// Uncertainty information from Signals. /// public sealed record SignalsUncertainty { public string? AggregateTier { get; init; } public double? RiskScore { get; init; } } /// /// Edge bundle reference from Signals. /// public sealed record SignalsEdgeBundleReference { public string BundleId { get; init; } = string.Empty; public string Reason { get; init; } = string.Empty; public int EdgeCount { get; init; } public string? CasUri { get; init; } public string? DsseDigest { get; init; } public bool HasRevokedEdges { get; init; } } /// /// Request to trigger reachability recomputation. /// public sealed record SignalsRecomputeRequest { /// /// Subject key to recompute. /// public required string SubjectKey { get; init; } /// /// Tenant ID. /// public required string TenantId { get; init; } }