up
	
		
			
	
		
	
	
		
	
		
			Some checks failed
		
		
	
	
		
			
				
	
				Build Test Deploy / authority-container (push) Has been cancelled
				
			
		
			
				
	
				Build Test Deploy / docs (push) Has been cancelled
				
			
		
			
				
	
				Build Test Deploy / deploy (push) Has been cancelled
				
			
		
			
				
	
				Build Test Deploy / build-test (push) Has been cancelled
				
			
		
			
				
	
				Docs CI / lint-and-preview (push) Has been cancelled
				
			
		
		
	
	
				
					
				
			
		
			Some checks failed
		
		
	
	Build Test Deploy / authority-container (push) Has been cancelled
				
			Build Test Deploy / docs (push) Has been cancelled
				
			Build Test Deploy / deploy (push) Has been cancelled
				
			Build Test Deploy / build-test (push) Has been cancelled
				
			Docs CI / lint-and-preview (push) Has been cancelled
				
			This commit is contained in:
		
							
								
								
									
										23
									
								
								src/StellaOps.Vexer.Export/AGENTS.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/StellaOps.Vexer.Export/AGENTS.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| # AGENTS | ||||
| ## Role | ||||
| Produces deterministic VEX export artifacts, coordinates cache lookups, and bridges artifact storage with attestation generation. | ||||
| ## Scope | ||||
| - Export orchestration pipeline: query signature resolution, cache lookup, snapshot building, attestation handoff. | ||||
| - Format-neutral builder interfaces consumed by format-specific plug-ins. | ||||
| - Artifact store abstraction wiring (S3/MinIO/filesystem) with offline-friendly packaging. | ||||
| - Export metrics/logging and deterministic manifest emission. | ||||
| ## Participants | ||||
| - WebService invokes the export engine to service `/vexer/export` requests. | ||||
| - Attestation module receives built artifacts through this layer for signing. | ||||
| - Worker reuses caching and artifact utilities for scheduled exports and GC routines. | ||||
| ## Interfaces & contracts | ||||
| - `IExportEngine`, `IExportSnapshotBuilder`, cache provider interfaces, and artifact store adapters. | ||||
| - Hook points for format plug-ins (JSON, JSONL, OpenVEX, CSAF, ZIP bundle). | ||||
| ## In/Out of scope | ||||
| In: orchestration, caching, artifact store interactions, manifest metadata. | ||||
| Out: format-specific serialization (lives in Formats.*), policy evaluation (Policy), HTTP presentation (WebService). | ||||
| ## Observability & security expectations | ||||
| - Emit cache hit/miss counters, export durations, artifact sizes, and attestation timing logs. | ||||
| - Ensure no sensitive tokens/URIs are logged. | ||||
| ## Tests | ||||
| - Engine orchestration tests, cache behavior, and artifact lifecycle coverage will live in `../StellaOps.Vexer.Export.Tests`. | ||||
							
								
								
									
										128
									
								
								src/StellaOps.Vexer.Export/ExportEngine.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								src/StellaOps.Vexer.Export/ExportEngine.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,128 @@ | ||||
| using System.Collections.Immutable; | ||||
| using System.IO; | ||||
| using System.Linq; | ||||
| using Microsoft.Extensions.DependencyInjection; | ||||
| using Microsoft.Extensions.Logging; | ||||
| using StellaOps.Vexer.Core; | ||||
| using StellaOps.Vexer.Policy; | ||||
| using StellaOps.Vexer.Storage.Mongo; | ||||
|  | ||||
| namespace StellaOps.Vexer.Export; | ||||
|  | ||||
| public interface IExportEngine | ||||
| { | ||||
|     ValueTask<VexExportManifest> ExportAsync(VexExportRequestContext context, CancellationToken cancellationToken); | ||||
| } | ||||
|  | ||||
| public sealed record VexExportRequestContext( | ||||
|     VexQuery Query, | ||||
|     VexExportFormat Format, | ||||
|     DateTimeOffset RequestedAt, | ||||
|     bool ForceRefresh = false); | ||||
|  | ||||
| public interface IVexExportDataSource | ||||
| { | ||||
|     ValueTask<VexExportDataSet> FetchAsync(VexQuery query, CancellationToken cancellationToken); | ||||
| } | ||||
|  | ||||
| public sealed record VexExportDataSet( | ||||
|     ImmutableArray<VexConsensus> Consensus, | ||||
|     ImmutableArray<VexClaim> Claims, | ||||
|     ImmutableArray<string> SourceProviders); | ||||
|  | ||||
| public sealed class VexExportEngine : IExportEngine | ||||
| { | ||||
|     private readonly IVexExportStore _exportStore; | ||||
|     private readonly IVexPolicyEvaluator _policyEvaluator; | ||||
|     private readonly IVexExportDataSource _dataSource; | ||||
|     private readonly IReadOnlyDictionary<VexExportFormat, IVexExporter> _exporters; | ||||
|     private readonly ILogger<VexExportEngine> _logger; | ||||
|  | ||||
|     public VexExportEngine( | ||||
|         IVexExportStore exportStore, | ||||
|         IVexPolicyEvaluator policyEvaluator, | ||||
|         IVexExportDataSource dataSource, | ||||
|         IEnumerable<IVexExporter> exporters, | ||||
|         ILogger<VexExportEngine> logger) | ||||
|     { | ||||
|         _exportStore = exportStore ?? throw new ArgumentNullException(nameof(exportStore)); | ||||
|         _policyEvaluator = policyEvaluator ?? throw new ArgumentNullException(nameof(policyEvaluator)); | ||||
|         _dataSource = dataSource ?? throw new ArgumentNullException(nameof(dataSource)); | ||||
|         _logger = logger ?? throw new ArgumentNullException(nameof(logger)); | ||||
|  | ||||
|         if (exporters is null) | ||||
|         { | ||||
|             throw new ArgumentNullException(nameof(exporters)); | ||||
|         } | ||||
|  | ||||
|         _exporters = exporters.ToDictionary(x => x.Format); | ||||
|     } | ||||
|  | ||||
|     public async ValueTask<VexExportManifest> ExportAsync(VexExportRequestContext context, CancellationToken cancellationToken) | ||||
|     { | ||||
|         ArgumentNullException.ThrowIfNull(context); | ||||
|         var signature = VexQuerySignature.FromQuery(context.Query); | ||||
|  | ||||
|         if (!context.ForceRefresh) | ||||
|         { | ||||
|             var cached = await _exportStore.FindAsync(signature, context.Format, cancellationToken).ConfigureAwait(false); | ||||
|             if (cached is not null) | ||||
|             { | ||||
|                 _logger.LogInformation("Reusing cached export for {Signature} ({Format})", signature.Value, context.Format); | ||||
|                 return cached with { FromCache = true }; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         var dataset = await _dataSource.FetchAsync(context.Query, cancellationToken).ConfigureAwait(false); | ||||
|         var exporter = ResolveExporter(context.Format); | ||||
|  | ||||
|         var exportRequest = new VexExportRequest( | ||||
|             context.Query, | ||||
|             dataset.Consensus, | ||||
|             dataset.Claims, | ||||
|             context.RequestedAt); | ||||
|  | ||||
|         var digest = exporter.Digest(exportRequest); | ||||
|  | ||||
|         await using var buffer = new MemoryStream(); | ||||
|         var result = await exporter.SerializeAsync(exportRequest, buffer, cancellationToken).ConfigureAwait(false); | ||||
|  | ||||
|         var exportId = FormattableString.Invariant($"exports/{context.RequestedAt:yyyyMMddTHHmmssfffZ}/{digest.Digest}"); | ||||
|         var manifest = new VexExportManifest( | ||||
|             exportId, | ||||
|             signature, | ||||
|             context.Format, | ||||
|             context.RequestedAt, | ||||
|             digest, | ||||
|             dataset.Claims.Length, | ||||
|             dataset.SourceProviders, | ||||
|             fromCache: false, | ||||
|             consensusRevision: _policyEvaluator.Version, | ||||
|             attestation: null, | ||||
|             sizeBytes: result.BytesWritten); | ||||
|  | ||||
|         await _exportStore.SaveAsync(manifest, cancellationToken).ConfigureAwait(false); | ||||
|  | ||||
|         _logger.LogInformation( | ||||
|             "Export generated for {Signature} ({Format}) size={SizeBytes} bytes", | ||||
|             signature.Value, | ||||
|             context.Format, | ||||
|             result.BytesWritten); | ||||
|  | ||||
|         return manifest; | ||||
|     } | ||||
|  | ||||
|     private IVexExporter ResolveExporter(VexExportFormat format) | ||||
|         => _exporters.TryGetValue(format, out var exporter) | ||||
|             ? exporter | ||||
|             : throw new InvalidOperationException($"No exporter registered for format '{format}'."); | ||||
| } | ||||
|  | ||||
| public static class VexExportServiceCollectionExtensions | ||||
| { | ||||
|     public static IServiceCollection AddVexExportEngine(this IServiceCollection services) | ||||
|     { | ||||
|         services.AddSingleton<IExportEngine, VexExportEngine>(); | ||||
|         return services; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										18
									
								
								src/StellaOps.Vexer.Export/StellaOps.Vexer.Export.csproj
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/StellaOps.Vexer.Export/StellaOps.Vexer.Export.csproj
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| <Project Sdk="Microsoft.NET.Sdk"> | ||||
|   <PropertyGroup> | ||||
|     <TargetFramework>net10.0</TargetFramework> | ||||
|     <LangVersion>preview</LangVersion> | ||||
|     <Nullable>enable</Nullable> | ||||
|     <ImplicitUsings>enable</ImplicitUsings> | ||||
|     <TreatWarningsAsErrors>true</TreatWarningsAsErrors> | ||||
|   </PropertyGroup> | ||||
|   <ItemGroup> | ||||
|     <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" /> | ||||
|     <PackageReference Include="Microsoft.Extensions.Options" Version="8.0.0" /> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <ProjectReference Include="..\StellaOps.Vexer.Core\StellaOps.Vexer.Core.csproj" /> | ||||
|     <ProjectReference Include="..\StellaOps.Vexer.Policy\StellaOps.Vexer.Policy.csproj" /> | ||||
|     <ProjectReference Include="..\StellaOps.Vexer.Storage.Mongo\StellaOps.Vexer.Storage.Mongo.csproj" /> | ||||
|   </ItemGroup> | ||||
| </Project> | ||||
							
								
								
									
										8
									
								
								src/StellaOps.Vexer.Export/TASKS.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/StellaOps.Vexer.Export/TASKS.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| If you are working on this file you need to read docs/ARCHITECTURE_VEXER.md and ./AGENTS.md). | ||||
| # TASKS | ||||
| | Task | Owner(s) | Depends on | Notes | | ||||
| |---|---|---|---| | ||||
| |VEXER-EXPORT-01-001 – Export engine orchestration|Team Vexer Export|VEXER-CORE-01-003|DONE (2025-10-15) – Export engine scaffolding with cache lookup, data source hooks, and deterministic manifest emission.| | ||||
| |VEXER-EXPORT-01-002 – Cache index & eviction hooks|Team Vexer Export|VEXER-EXPORT-01-001, VEXER-STORAGE-01-003|TODO – Wire cache lookup/write path against `vex.cache` collection and add GC utilities for Worker to prune stale entries deterministically.| | ||||
| |VEXER-EXPORT-01-003 – Artifact store adapters|Team Vexer Export|VEXER-EXPORT-01-001|TODO – Provide pluggable storage adapters (filesystem, S3/MinIO) with offline bundle packaging and hash verification.| | ||||
| |VEXER-EXPORT-01-004 – Attestation handoff integration|Team Vexer Export|VEXER-EXPORT-01-001, VEXER-ATTEST-01-001|TODO – Connect export engine to attestation client, persist Rekor metadata, and reuse cached attestations.| | ||||
		Reference in New Issue
	
	Block a user