using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Text.Json.Serialization; namespace StellaOps.Scanner.Surface.FS; /// /// Canonical manifest describing surface artefacts produced for a scan. /// public sealed record SurfaceManifestDocument { public const string DefaultSchema = "stellaops.surface.manifest@1"; [JsonPropertyName("schema")] public string Schema { get; init; } = DefaultSchema; [JsonPropertyName("tenant")] public string Tenant { get; init; } = string.Empty; [JsonPropertyName("imageDigest")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? ImageDigest { get; init; } = null; [JsonPropertyName("scanId")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? ScanId { get; init; } = null; [JsonPropertyName("generatedAt")] public DateTimeOffset GeneratedAt { get; init; } = DateTimeOffset.MinValue; [JsonPropertyName("source")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SurfaceManifestSource? Source { get; init; } = null; [JsonPropertyName("artifacts")] public IReadOnlyList Artifacts { get; init; } = ImmutableArray.Empty; [JsonPropertyName("determinismRoot")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? DeterminismMerkleRoot { get; init; } = null; [JsonPropertyName("replayBundle")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public ReplayBundleReference? ReplayBundle { get; init; } = null; } public sealed record ReplayBundleReference { [JsonPropertyName("uri")] public string Uri { get; init; } = string.Empty; [JsonPropertyName("sha256")] public string Sha256 { get; init; } = string.Empty; [JsonPropertyName("policyPin")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? PolicySnapshotId { get; init; } = null; [JsonPropertyName("feedPin")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? FeedSnapshotId { get; init; } = null; } /// /// Identifies the producer of the manifest. /// public sealed record SurfaceManifestSource { [JsonPropertyName("component")] public string Component { get; init; } = "scanner.worker"; [JsonPropertyName("version")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Version { get; init; } = null; [JsonPropertyName("workerInstance")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? WorkerInstance { get; init; } = null; [JsonPropertyName("attempt")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public int? Attempt { get; init; } = null; } /// /// Describes a surface artefact referenced by the manifest. /// public sealed record SurfaceManifestArtifact { [JsonPropertyName("kind")] public string Kind { get; init; } = string.Empty; [JsonPropertyName("uri")] public string Uri { get; init; } = string.Empty; [JsonPropertyName("digest")] public string Digest { get; init; } = string.Empty; [JsonPropertyName("mediaType")] public string MediaType { get; init; } = string.Empty; [JsonPropertyName("format")] public string Format { get; init; } = string.Empty; [JsonPropertyName("sizeBytes")] public long SizeBytes { get; init; } = 0; [JsonPropertyName("view")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? View { get; init; } = null; [JsonPropertyName("storage")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SurfaceManifestStorage? Storage { get; init; } = null; [JsonPropertyName("metadata")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public IReadOnlyDictionary? Metadata { get; init; } = null; } /// /// Storage descriptor for an artefact. /// public sealed record SurfaceManifestStorage { [JsonPropertyName("bucket")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Bucket { get; init; } = null; [JsonPropertyName("objectKey")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? ObjectKey { get; init; } = null; [JsonPropertyName("sizeBytes")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public long? SizeBytes { get; init; } = null; [JsonPropertyName("contentType")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? ContentType { get; init; } = null; } /// /// Result from publishing a surface manifest. /// public sealed record SurfaceManifestPublishResult( string ManifestDigest, string ManifestUri, string ArtifactId, SurfaceManifestDocument Document, string? DeterminismMerkleRoot = null);