10 KiB
Evidence API Reference
This document provides the complete API reference for the StellaOps unified evidence model.
Interfaces
IEvidence
Base interface for all evidence records.
namespace StellaOps.Evidence.Core;
public interface IEvidence
{
/// <summary>
/// Content-addressed evidence identifier (e.g., "sha256:abc123...").
/// </summary>
string EvidenceId { get; }
/// <summary>
/// The type of evidence this record represents.
/// </summary>
EvidenceType Type { get; }
/// <summary>
/// The subject (node ID) this evidence is about.
/// </summary>
string SubjectNodeId { get; }
/// <summary>
/// When the evidence was created (UTC).
/// </summary>
DateTimeOffset CreatedAt { get; }
/// <summary>
/// Cryptographic signatures attesting to this evidence.
/// </summary>
IReadOnlyList<EvidenceSignature> Signatures { get; }
/// <summary>
/// Origin and provenance information.
/// </summary>
EvidenceProvenance? Provenance { get; }
/// <summary>
/// Type-specific properties as key-value pairs.
/// </summary>
IReadOnlyDictionary<string, string> Properties { get; }
}
IEvidenceStore
Storage interface for evidence persistence.
namespace StellaOps.Evidence.Core;
public interface IEvidenceStore
{
/// <summary>
/// Retrieves evidence by its content-addressed ID.
/// </summary>
Task<IEvidence?> GetAsync(string evidenceId, CancellationToken ct = default);
/// <summary>
/// Retrieves all evidence records for a given subject.
/// </summary>
Task<IReadOnlyList<IEvidence>> GetBySubjectAsync(
string subjectNodeId,
CancellationToken ct = default);
/// <summary>
/// Retrieves all evidence records of a specific type.
/// </summary>
Task<IReadOnlyList<IEvidence>> GetByTypeAsync(
EvidenceType type,
CancellationToken ct = default);
/// <summary>
/// Stores an evidence record.
/// </summary>
Task StoreAsync(IEvidence evidence, CancellationToken ct = default);
/// <summary>
/// Checks if evidence with the given ID exists.
/// </summary>
Task<bool> ExistsAsync(string evidenceId, CancellationToken ct = default);
}
IEvidenceAdapter
Adapter interface for converting module-specific types to evidence.
namespace StellaOps.Evidence.Core.Adapters;
public interface IEvidenceAdapter<TInput>
{
/// <summary>
/// Converts a module-specific input to one or more evidence records.
/// </summary>
IReadOnlyList<IEvidence> ToEvidence(TInput input);
}
Records
EvidenceRecord
Standard implementation of IEvidence.
namespace StellaOps.Evidence.Core;
public sealed record EvidenceRecord : IEvidence
{
public required string EvidenceId { get; init; }
public required EvidenceType Type { get; init; }
public required string SubjectNodeId { get; init; }
public required DateTimeOffset CreatedAt { get; init; }
public IReadOnlyList<EvidenceSignature> Signatures { get; init; } = [];
public EvidenceProvenance? Provenance { get; init; }
public IReadOnlyDictionary<string, string> Properties { get; init; } =
new Dictionary<string, string>();
}
EvidenceSignature
Cryptographic signature attached to evidence.
namespace StellaOps.Evidence.Core;
public sealed record EvidenceSignature
{
/// <summary>
/// Identifier of the signer (key ID, tool name, etc.).
/// </summary>
public required string SignerId { get; init; }
/// <summary>
/// Signing algorithm (e.g., "Ed25519", "ES256").
/// </summary>
public required string Algorithm { get; init; }
/// <summary>
/// Base64-encoded signature bytes.
/// </summary>
public required string SignatureBase64 { get; init; }
/// <summary>
/// When the signature was created (UTC).
/// </summary>
public required DateTimeOffset SignedAt { get; init; }
/// <summary>
/// Type of entity that produced the signature.
/// </summary>
public required SignerType SignerType { get; init; }
}
EvidenceProvenance
Origin and provenance information.
namespace StellaOps.Evidence.Core;
public sealed record EvidenceProvenance
{
/// <summary>
/// Source that produced this evidence (e.g., "grype", "trivy").
/// </summary>
public required string Source { get; init; }
/// <summary>
/// Version of the source tool.
/// </summary>
public string? SourceVersion { get; init; }
/// <summary>
/// URI where original data was obtained.
/// </summary>
public string? SourceUri { get; init; }
/// <summary>
/// Digest of the original content.
/// </summary>
public string? ContentDigest { get; init; }
}
Enumerations
EvidenceType
namespace StellaOps.Evidence.Core;
public enum EvidenceType
{
Unknown = 0,
Sbom = 1,
Vulnerability = 2,
Vex = 3,
Attestation = 4,
PolicyDecision = 5,
ScanResult = 6,
Provenance = 7,
Signature = 8,
ProofSegment = 9,
Exception = 10,
Advisory = 11,
CveMatch = 12,
ReachabilityResult = 13
}
SignerType
namespace StellaOps.Evidence.Core;
public enum SignerType
{
Unknown = 0,
Tool = 1,
Human = 2,
Authority = 3,
Vendor = 4,
Service = 5
}
Adapters
EvidenceStatementAdapter
Converts EvidenceStatement from Attestor module.
Input: EvidenceStatementInput
public sealed record EvidenceStatementInput
{
public required string StatementId { get; init; }
public required string SubjectDigest { get; init; }
public required string StatementType { get; init; }
public required string PredicateType { get; init; }
public required DateTimeOffset IssuedAt { get; init; }
public IReadOnlyList<EvidenceSignatureInput>? Signatures { get; init; }
public IReadOnlyDictionary<string, string>? Metadata { get; init; }
}
Output: Single IEvidence record with Type = Attestation.
ProofSegmentAdapter
Converts ProofSegment from Scanner module.
Input: ProofSegmentInput
public sealed record ProofSegmentInput
{
public required string SegmentId { get; init; }
public required string SubjectNodeId { get; init; }
public required string SegmentType { get; init; }
public required string Status { get; init; }
public required DateTimeOffset CreatedAt { get; init; }
public string? PreviousSegmentId { get; init; }
public string? PayloadDigest { get; init; }
public IReadOnlyList<EvidenceSignatureInput>? Signatures { get; init; }
public IReadOnlyDictionary<string, string>? Properties { get; init; }
}
Output: Single IEvidence record with Type = ProofSegment.
VexObservationAdapter
Converts VexObservation from Excititor module.
Input: VexObservationInput
public sealed record VexObservationInput
{
public required string SubjectDigest { get; init; }
public VexObservationUpstreamInput? Upstream { get; init; }
public IReadOnlyList<VexObservationStatementInput>? Statements { get; init; }
public IReadOnlyDictionary<string, string>? Properties { get; init; }
}
public sealed record VexObservationUpstreamInput
{
public required string VexDocumentId { get; init; }
public required string VendorName { get; init; }
public required DateTimeOffset PublishedAt { get; init; }
public string? DocumentDigest { get; init; }
}
public sealed record VexObservationStatementInput
{
public required string VulnerabilityId { get; init; }
public required string ProductId { get; init; }
public required string Status { get; init; }
public required DateTimeOffset Timestamp { get; init; }
public string? Justification { get; init; }
}
Output: Multiple IEvidence records:
- 1 record with
Type = Provenance(from upstream) - N records with
Type = Vex(one per statement)
ExceptionApplicationAdapter
Converts ExceptionApplication from Policy module.
Input: ExceptionApplicationInput
public sealed record ExceptionApplicationInput
{
public required string ApplicationId { get; init; }
public required string TenantId { get; init; }
public required string ExceptionId { get; init; }
public required string FindingId { get; init; }
public required DateTimeOffset AppliedAt { get; init; }
public DateTimeOffset? ExpiresAt { get; init; }
public string? Reason { get; init; }
public string? AppliedBy { get; init; }
public IReadOnlyDictionary<string, string>? Properties { get; init; }
}
Output: Single IEvidence record with Type = Exception.
Implementations
InMemoryEvidenceStore
Thread-safe in-memory evidence store for testing and caching.
var store = new InMemoryEvidenceStore();
// Store evidence
await store.StoreAsync(record);
// Retrieve by ID
var evidence = await store.GetAsync("sha256:abc123...");
// Query by subject
var subjectEvidence = await store.GetBySubjectAsync("pkg:npm/lodash@4.17.21");
// Query by type
var vexRecords = await store.GetByTypeAsync(EvidenceType.Vex);
// Check existence
var exists = await store.ExistsAsync("sha256:abc123...");
Thread Safety: Uses ConcurrentDictionary for all operations.
Common Property Keys
Standard property keys used across evidence types:
| Key | Used By | Description |
|---|---|---|
cve |
Vulnerability, CveMatch | CVE identifier |
severity |
Vulnerability | Severity level (CRITICAL, HIGH, etc.) |
cvss |
Vulnerability | CVSS score |
status |
Vex, ProofSegment | Current status |
justification |
Vex, Exception | Reason for status |
productId |
Vex | Affected product identifier |
exceptionId |
Exception | Parent exception ID |
findingId |
Exception | Finding being excepted |
tenantId |
Exception | Tenant context |
segmentType |
ProofSegment | Type of proof segment |
previousSegmentId |
ProofSegment | Chain link to previous segment |
payloadDigest |
ProofSegment | Content digest |
predicateType |
Attestation | In-toto predicate type URI |
statementType |
Attestation | Statement type identifier |