using System; using System.Collections.Generic; using System.Text.Json.Serialization; namespace StellaOps.Cli.Services.Models; // CLI-PROMO-70-001: Promotion attestation models /// /// Request for assembling a promotion attestation. /// internal sealed class PromotionAssembleRequest { [JsonPropertyName("tenant")] public string Tenant { get; init; } = string.Empty; [JsonPropertyName("image")] public string Image { get; init; } = string.Empty; [JsonPropertyName("sbomPath")] public string? SbomPath { get; init; } [JsonPropertyName("vexPath")] public string? VexPath { get; init; } [JsonPropertyName("fromEnvironment")] public string FromEnvironment { get; init; } = "staging"; [JsonPropertyName("toEnvironment")] public string ToEnvironment { get; init; } = "prod"; [JsonPropertyName("actor")] public string? Actor { get; init; } [JsonPropertyName("pipeline")] public string? Pipeline { get; init; } [JsonPropertyName("ticket")] public string? Ticket { get; init; } [JsonPropertyName("notes")] public string? Notes { get; init; } [JsonPropertyName("skipRekor")] public bool SkipRekor { get; init; } [JsonPropertyName("outputPath")] public string? OutputPath { get; init; } } /// /// Promotion attestation predicate following stella.ops/promotion@v1 schema. /// internal sealed class PromotionPredicate { [JsonPropertyName("_type")] public string Type { get; init; } = "stella.ops/promotion@v1"; [JsonPropertyName("subject")] public IReadOnlyList Subject { get; init; } = Array.Empty(); [JsonPropertyName("materials")] public IReadOnlyList Materials { get; init; } = Array.Empty(); [JsonPropertyName("promotion")] public PromotionMetadata Promotion { get; init; } = new(); [JsonPropertyName("rekor")] public PromotionRekorEntry? Rekor { get; init; } [JsonPropertyName("attestation")] public PromotionAttestationMetadata? Attestation { get; init; } } /// /// Subject in promotion attestation (image reference). /// internal sealed class PromotionSubject { [JsonPropertyName("name")] public string Name { get; init; } = string.Empty; [JsonPropertyName("digest")] public IReadOnlyDictionary Digest { get; init; } = new Dictionary(); } /// /// Material in promotion attestation (SBOM, VEX, etc.). /// internal sealed class PromotionMaterial { [JsonPropertyName("role")] public string Role { get; init; } = string.Empty; [JsonPropertyName("algo")] public string Algo { get; init; } = "sha256"; [JsonPropertyName("digest")] public string Digest { get; init; } = string.Empty; [JsonPropertyName("format")] public string? Format { get; init; } [JsonPropertyName("uri")] public string? Uri { get; init; } } /// /// Promotion metadata. /// internal sealed class PromotionMetadata { [JsonPropertyName("from")] public string From { get; init; } = "staging"; [JsonPropertyName("to")] public string To { get; init; } = "prod"; [JsonPropertyName("actor")] public string? Actor { get; init; } [JsonPropertyName("timestamp")] public DateTimeOffset Timestamp { get; init; } = DateTimeOffset.UtcNow; [JsonPropertyName("pipeline")] public string? Pipeline { get; init; } [JsonPropertyName("ticket")] public string? Ticket { get; init; } [JsonPropertyName("notes")] public string? Notes { get; init; } } /// /// Rekor entry in promotion attestation. /// internal sealed class PromotionRekorEntry { [JsonPropertyName("uuid")] public string Uuid { get; init; } = string.Empty; [JsonPropertyName("logIndex")] public long LogIndex { get; init; } [JsonPropertyName("inclusionProof")] public PromotionInclusionProof? InclusionProof { get; init; } } /// /// Merkle inclusion proof. /// internal sealed class PromotionInclusionProof { [JsonPropertyName("rootHash")] public string RootHash { get; init; } = string.Empty; [JsonPropertyName("hashes")] public IReadOnlyList Hashes { get; init; } = Array.Empty(); [JsonPropertyName("treeSize")] public long TreeSize { get; init; } [JsonPropertyName("checkpoint")] public PromotionCheckpoint? Checkpoint { get; init; } } /// /// Rekor checkpoint. /// internal sealed class PromotionCheckpoint { [JsonPropertyName("origin")] public string Origin { get; init; } = string.Empty; [JsonPropertyName("size")] public long Size { get; init; } [JsonPropertyName("hash")] public string Hash { get; init; } = string.Empty; [JsonPropertyName("signedNote")] public string? SignedNote { get; init; } } /// /// Attestation metadata. /// internal sealed class PromotionAttestationMetadata { [JsonPropertyName("bundle_sha256")] public string BundleSha256 { get; init; } = string.Empty; [JsonPropertyName("witness")] public string? Witness { get; init; } } /// /// Result of promotion assemble operation. /// internal sealed class PromotionAssembleResult { [JsonPropertyName("success")] public bool Success { get; init; } [JsonPropertyName("predicate")] public PromotionPredicate? Predicate { get; init; } [JsonPropertyName("outputPath")] public string? OutputPath { get; init; } [JsonPropertyName("imageDigest")] public string ImageDigest { get; init; } = string.Empty; [JsonPropertyName("materials")] public IReadOnlyList Materials { get; init; } = Array.Empty(); [JsonPropertyName("rekorEntry")] public PromotionRekorEntry? RekorEntry { get; init; } [JsonPropertyName("errors")] public IReadOnlyList Errors { get; init; } = Array.Empty(); [JsonPropertyName("warnings")] public IReadOnlyList Warnings { get; init; } = Array.Empty(); } // CLI-PROMO-70-002: Promotion attest/verify models /// /// Request for attesting a promotion predicate. /// internal sealed class PromotionAttestRequest { [JsonPropertyName("tenant")] public string Tenant { get; init; } = string.Empty; [JsonPropertyName("predicatePath")] public string? PredicatePath { get; init; } [JsonPropertyName("predicate")] public PromotionPredicate? Predicate { get; init; } [JsonPropertyName("keyId")] public string? KeyId { get; init; } [JsonPropertyName("useKeyless")] public bool UseKeyless { get; init; } [JsonPropertyName("outputPath")] public string? OutputPath { get; init; } [JsonPropertyName("uploadToRekor")] public bool UploadToRekor { get; init; } = true; } /// /// Result of promotion attest operation. /// internal sealed class PromotionAttestResult { [JsonPropertyName("success")] public bool Success { get; init; } [JsonPropertyName("bundlePath")] public string? BundlePath { get; init; } [JsonPropertyName("dsseEnvelope")] public DsseEnvelope? DsseEnvelope { get; init; } [JsonPropertyName("rekorEntry")] public PromotionRekorEntry? RekorEntry { get; init; } [JsonPropertyName("auditId")] public string? AuditId { get; init; } [JsonPropertyName("signerKeyId")] public string? SignerKeyId { get; init; } [JsonPropertyName("signedAt")] public DateTimeOffset? SignedAt { get; init; } [JsonPropertyName("errors")] public IReadOnlyList Errors { get; init; } = Array.Empty(); [JsonPropertyName("warnings")] public IReadOnlyList Warnings { get; init; } = Array.Empty(); } /// /// DSSE envelope for promotion attestation. /// internal sealed class DsseEnvelope { [JsonPropertyName("payloadType")] public string PayloadType { get; init; } = "application/vnd.in-toto+json"; [JsonPropertyName("payload")] public string Payload { get; init; } = string.Empty; [JsonPropertyName("signatures")] public IReadOnlyList Signatures { get; init; } = Array.Empty(); } /// /// DSSE signature. /// internal sealed class DsseSignature { [JsonPropertyName("keyid")] public string KeyId { get; init; } = string.Empty; [JsonPropertyName("sig")] public string Sig { get; init; } = string.Empty; [JsonPropertyName("cert")] public string? Cert { get; init; } } /// /// Request for verifying a promotion attestation. /// internal sealed class PromotionVerifyRequest { [JsonPropertyName("tenant")] public string Tenant { get; init; } = string.Empty; [JsonPropertyName("bundlePath")] public string? BundlePath { get; init; } [JsonPropertyName("predicatePath")] public string? PredicatePath { get; init; } [JsonPropertyName("sbomPath")] public string? SbomPath { get; init; } [JsonPropertyName("vexPath")] public string? VexPath { get; init; } [JsonPropertyName("trustRootPath")] public string? TrustRootPath { get; init; } [JsonPropertyName("checkpointPath")] public string? CheckpointPath { get; init; } [JsonPropertyName("skipRekorVerification")] public bool SkipRekorVerification { get; init; } [JsonPropertyName("skipSignatureVerification")] public bool SkipSignatureVerification { get; init; } } /// /// Result of promotion verify operation. /// internal sealed class PromotionVerifyResult { [JsonPropertyName("success")] public bool Success { get; init; } [JsonPropertyName("verified")] public bool Verified { get; init; } [JsonPropertyName("signatureVerification")] public PromotionSignatureVerification? SignatureVerification { get; init; } [JsonPropertyName("materialVerification")] public PromotionMaterialVerification? MaterialVerification { get; init; } [JsonPropertyName("rekorVerification")] public PromotionRekorVerification? RekorVerification { get; init; } [JsonPropertyName("predicate")] public PromotionPredicate? Predicate { get; init; } [JsonPropertyName("errors")] public IReadOnlyList Errors { get; init; } = Array.Empty(); [JsonPropertyName("warnings")] public IReadOnlyList Warnings { get; init; } = Array.Empty(); } /// /// Signature verification result. /// internal sealed class PromotionSignatureVerification { [JsonPropertyName("verified")] public bool Verified { get; init; } [JsonPropertyName("keyId")] public string? KeyId { get; init; } [JsonPropertyName("algorithm")] public string? Algorithm { get; init; } [JsonPropertyName("certSubject")] public string? CertSubject { get; init; } [JsonPropertyName("certIssuer")] public string? CertIssuer { get; init; } [JsonPropertyName("validFrom")] public DateTimeOffset? ValidFrom { get; init; } [JsonPropertyName("validTo")] public DateTimeOffset? ValidTo { get; init; } [JsonPropertyName("error")] public string? Error { get; init; } } /// /// Material verification result. /// internal sealed class PromotionMaterialVerification { [JsonPropertyName("verified")] public bool Verified { get; init; } [JsonPropertyName("materials")] public IReadOnlyList Materials { get; init; } = Array.Empty(); } /// /// Individual material verification entry. /// internal sealed class PromotionMaterialVerificationEntry { [JsonPropertyName("role")] public string Role { get; init; } = string.Empty; [JsonPropertyName("verified")] public bool Verified { get; init; } [JsonPropertyName("expectedDigest")] public string ExpectedDigest { get; init; } = string.Empty; [JsonPropertyName("actualDigest")] public string? ActualDigest { get; init; } [JsonPropertyName("error")] public string? Error { get; init; } } /// /// Rekor verification result. /// internal sealed class PromotionRekorVerification { [JsonPropertyName("verified")] public bool Verified { get; init; } [JsonPropertyName("uuid")] public string? Uuid { get; init; } [JsonPropertyName("logIndex")] public long? LogIndex { get; init; } [JsonPropertyName("inclusionProofVerified")] public bool InclusionProofVerified { get; init; } [JsonPropertyName("checkpointVerified")] public bool CheckpointVerified { get; init; } [JsonPropertyName("error")] public string? Error { get; init; } }