// // Copyright (c) StellaOps. Licensed under AGPL-3.0-or-later. // using System.Collections.Immutable; namespace StellaOps.Facet; /// /// Sealed manifest of facets for an image at a point in time. /// /// /// /// A FacetSeal captures the cryptographic state of all facets in an image, /// enabling drift detection and quota enforcement on subsequent scans. /// /// /// The seal can be optionally signed with DSSE for authenticity verification. /// /// public sealed record FacetSeal { /// /// Current schema version. /// public const string CurrentSchemaVersion = "1.0.0"; /// /// Gets the schema version for forward compatibility. /// public string SchemaVersion { get; init; } = CurrentSchemaVersion; /// /// Gets the image digest this seal applies to. /// /// /// Format: "sha256:{hex}" or "sha512:{hex}". /// public required string ImageDigest { get; init; } /// /// Gets when the seal was created. /// public required DateTimeOffset CreatedAt { get; init; } /// /// Gets the optional build attestation reference (in-toto provenance). /// public string? BuildAttestationRef { get; init; } /// /// Gets the individual facet seals. /// public required ImmutableArray Facets { get; init; } /// /// Gets the quota configuration per facet. /// /// /// Keys are facet IDs. Facets without explicit quotas use default values. /// public ImmutableDictionary? Quotas { get; init; } /// /// Gets the combined Merkle root of all facet roots. /// /// /// Computed from facet Merkle roots in sorted order by FacetId. /// Enables single-value integrity verification. /// public required string CombinedMerkleRoot { get; init; } /// /// Gets the optional DSSE signature over canonical form. /// /// /// Base64-encoded DSSE envelope when the seal is signed. /// public string? Signature { get; init; } /// /// Gets the signing key identifier, if signed. /// public string? SigningKeyId { get; init; } /// /// Gets whether this seal is signed. /// public bool IsSigned => !string.IsNullOrEmpty(Signature); /// /// Gets the quota for a specific facet, or default if not configured. /// /// The facet identifier. /// The configured quota or . public FacetQuota GetQuota(string facetId) { if (Quotas is not null && Quotas.TryGetValue(facetId, out var quota)) { return quota; } return FacetQuota.Default; } /// /// Gets a facet entry by ID. /// /// The facet identifier. /// The facet entry or null if not found. public FacetEntry? GetFacet(string facetId) => Facets.FirstOrDefault(f => f.FacetId == facetId); }