110 lines
4.2 KiB
C#
110 lines
4.2 KiB
C#
// <copyright file="IFacetSealStore.cs" company="StellaOps">
|
|
// Copyright (c) StellaOps. Licensed under BUSL-1.1.
|
|
// </copyright>
|
|
|
|
using System.Collections.Immutable;
|
|
|
|
namespace StellaOps.Facet;
|
|
|
|
/// <summary>
|
|
/// Persistent store for <see cref="FacetSeal"/> instances.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <para>
|
|
/// Implementations provide storage and retrieval of facet seals for drift detection
|
|
/// and quota enforcement. Seals are indexed by image digest and creation time.
|
|
/// </para>
|
|
/// <para>
|
|
/// Sprint: SPRINT_20260105_002_003_FACET (QTA-012)
|
|
/// </para>
|
|
/// </remarks>
|
|
public interface IFacetSealStore
|
|
{
|
|
/// <summary>
|
|
/// Get the most recent seal for an image digest.
|
|
/// </summary>
|
|
/// <param name="imageDigest">The image digest (e.g., "sha256:{hex}").</param>
|
|
/// <param name="ct">Cancellation token.</param>
|
|
/// <returns>The latest seal, or null if no seal exists for this image.</returns>
|
|
Task<FacetSeal?> GetLatestSealAsync(string imageDigest, CancellationToken ct = default);
|
|
|
|
/// <summary>
|
|
/// Get a seal by its combined Merkle root (unique identifier).
|
|
/// </summary>
|
|
/// <param name="combinedMerkleRoot">The seal's combined Merkle root.</param>
|
|
/// <param name="ct">Cancellation token.</param>
|
|
/// <returns>The seal, or null if not found.</returns>
|
|
Task<FacetSeal?> GetByCombinedRootAsync(string combinedMerkleRoot, CancellationToken ct = default);
|
|
|
|
/// <summary>
|
|
/// Get seal history for an image digest.
|
|
/// </summary>
|
|
/// <param name="imageDigest">The image digest.</param>
|
|
/// <param name="limit">Maximum number of seals to return.</param>
|
|
/// <param name="ct">Cancellation token.</param>
|
|
/// <returns>Seals in descending order by creation time (most recent first).</returns>
|
|
Task<ImmutableArray<FacetSeal>> GetHistoryAsync(
|
|
string imageDigest,
|
|
int limit = 10,
|
|
CancellationToken ct = default);
|
|
|
|
/// <summary>
|
|
/// Save a seal to the store.
|
|
/// </summary>
|
|
/// <param name="seal">The seal to save.</param>
|
|
/// <param name="ct">Cancellation token.</param>
|
|
/// <returns>A task representing the async operation.</returns>
|
|
/// <exception cref="ArgumentNullException">If seal is null.</exception>
|
|
/// <exception cref="SealAlreadyExistsException">If a seal with the same combined root exists.</exception>
|
|
Task SaveAsync(FacetSeal seal, CancellationToken ct = default);
|
|
|
|
/// <summary>
|
|
/// Check if a seal exists for an image digest.
|
|
/// </summary>
|
|
/// <param name="imageDigest">The image digest.</param>
|
|
/// <param name="ct">Cancellation token.</param>
|
|
/// <returns>True if at least one seal exists.</returns>
|
|
Task<bool> ExistsAsync(string imageDigest, CancellationToken ct = default);
|
|
|
|
/// <summary>
|
|
/// Delete all seals for an image digest.
|
|
/// </summary>
|
|
/// <param name="imageDigest">The image digest.</param>
|
|
/// <param name="ct">Cancellation token.</param>
|
|
/// <returns>Number of seals deleted.</returns>
|
|
Task<int> DeleteByImageAsync(string imageDigest, CancellationToken ct = default);
|
|
|
|
/// <summary>
|
|
/// Purge seals older than the specified retention period.
|
|
/// </summary>
|
|
/// <param name="retentionPeriod">Retention period from creation time.</param>
|
|
/// <param name="keepAtLeast">Minimum seals to keep per image digest.</param>
|
|
/// <param name="ct">Cancellation token.</param>
|
|
/// <returns>Number of seals purged.</returns>
|
|
Task<int> PurgeOldSealsAsync(
|
|
TimeSpan retentionPeriod,
|
|
int keepAtLeast = 1,
|
|
CancellationToken ct = default);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Exception thrown when attempting to save a duplicate seal.
|
|
/// </summary>
|
|
public sealed class SealAlreadyExistsException : Exception
|
|
{
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="SealAlreadyExistsException"/> class.
|
|
/// </summary>
|
|
/// <param name="combinedMerkleRoot">The duplicate seal's combined root.</param>
|
|
public SealAlreadyExistsException(string combinedMerkleRoot)
|
|
: base($"A seal with combined Merkle root '{combinedMerkleRoot}' already exists.")
|
|
{
|
|
CombinedMerkleRoot = combinedMerkleRoot;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the duplicate seal's combined Merkle root.
|
|
/// </summary>
|
|
public string CombinedMerkleRoot { get; }
|
|
}
|