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; }
}