save progress

This commit is contained in:
StellaOps Bot
2026-01-06 09:42:02 +02:00
parent 94d68bee8b
commit 37e11918e0
443 changed files with 85863 additions and 897 deletions

View File

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