feat(graph-api): Add schema review notes for upcoming Graph API changes
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled

feat(sbomservice): Add placeholder for SHA256SUMS in LNM v1 fixtures

docs(devportal): Create README for SDK archives in public directory

build(devportal): Implement offline bundle build script

test(devportal): Add link checker script for validating links in documentation

test(devportal): Create performance check script for dist folder size

test(devportal): Implement accessibility check script using Playwright and Axe

docs(devportal): Add SDK quickstart guide with examples for Node.js, Python, and cURL

feat(excititor): Implement MongoDB storage for airgap import records

test(findings): Add unit tests for export filters hash determinism

feat(findings): Define attestation contracts for ledger web service

feat(graph): Add MongoDB options and service collection extensions for graph indexing

test(graph): Implement integration tests for MongoDB provider and service collection extensions

feat(zastava): Define configuration options for Zastava surface secrets

build(tests): Create script to run Concelier linkset tests with TRX output
This commit is contained in:
StellaOps Bot
2025-11-22 19:22:30 +02:00
parent ca09400069
commit 48702191be
76 changed files with 3878 additions and 1081 deletions

View File

@@ -140,11 +140,13 @@ app.MapHealthChecks("/excititor/health");
app.MapPost("/airgap/v1/vex/import", async (
[FromServices] AirgapImportValidator validator,
[FromServices] IAirgapImportStore store,
[FromServices] TimeProvider timeProvider,
[FromBody] AirgapImportRequest request,
CancellationToken cancellationToken) =>
{
var errors = validator.Validate(request, timeProvider.GetUtcNow());
var nowUtc = timeProvider.GetUtcNow();
var errors = validator.Validate(request, nowUtc);
if (errors.Count > 0)
{
var first = errors[0];
@@ -158,6 +160,22 @@ app.MapPost("/airgap/v1/vex/import", async (
});
}
var record = new AirgapImportRecord
{
Id = $"{request.BundleId}:{request.MirrorGeneration}",
BundleId = request.BundleId!,
MirrorGeneration = request.MirrorGeneration!,
SignedAt = request.SignedAt!.Value,
Publisher = request.Publisher!,
PayloadHash = request.PayloadHash!,
PayloadUrl = request.PayloadUrl,
Signature = request.Signature!,
TransparencyLog = request.TransparencyLog,
ImportedAt = nowUtc
};
await store.SaveAsync(record, cancellationToken).ConfigureAwait(false);
return Results.Accepted($"/airgap/v1/vex/import/{request.BundleId}", new
{
bundleId = request.BundleId,

View File

@@ -0,0 +1,29 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using MongoDB.Driver;
namespace StellaOps.Excititor.Storage.Mongo;
public interface IAirgapImportStore
{
Task SaveAsync(AirgapImportRecord record, CancellationToken cancellationToken);
}
internal sealed class MongoAirgapImportStore : IAirgapImportStore
{
private readonly IMongoCollection<AirgapImportRecord> _collection;
public MongoAirgapImportStore(IMongoDatabase database)
{
ArgumentNullException.ThrowIfNull(database);
VexMongoMappingRegistry.Register();
_collection = database.GetCollection<AirgapImportRecord>(VexMongoCollectionNames.AirgapImports);
}
public Task SaveAsync(AirgapImportRecord record, CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(record);
return _collection.InsertOneAsync(record, cancellationToken: cancellationToken);
}
}

View File

@@ -57,6 +57,7 @@ public static class VexMongoServiceCollectionExtensions
services.AddScoped<IVexCacheIndex, MongoVexCacheIndex>();
services.AddScoped<IVexCacheMaintenance, MongoVexCacheMaintenance>();
services.AddScoped<IVexConnectorStateRepository, MongoVexConnectorStateRepository>();
services.AddScoped<IAirgapImportStore, MongoAirgapImportStore>();
services.AddScoped<VexStatementBackfillService>();
services.AddScoped<IVexObservationLookup, MongoVexObservationLookup>();
services.AddSingleton<IVexMongoMigration, VexInitialIndexMigration>();

View File

@@ -1,69 +1,70 @@
using System.Threading;
using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.Serializers;
namespace StellaOps.Excititor.Storage.Mongo;
public static class VexMongoMappingRegistry
{
private static int _initialized;
public static void Register()
{
if (Interlocked.Exchange(ref _initialized, 1) == 1)
{
return;
}
try
{
BsonSerializer.RegisterSerializer(typeof(byte[]), new ByteArraySerializer());
}
catch
{
// serializer already registered safe to ignore
}
RegisterClassMaps();
}
private static void RegisterClassMaps()
{
RegisterClassMap<VexProviderRecord>();
RegisterClassMap<VexProviderDiscoveryDocument>();
RegisterClassMap<VexProviderTrustDocument>();
RegisterClassMap<VexCosignTrustDocument>();
RegisterClassMap<VexConsensusRecord>();
RegisterClassMap<VexProductDocument>();
RegisterClassMap<VexConsensusSourceDocument>();
RegisterClassMap<VexConsensusConflictDocument>();
RegisterClassMap<VexConfidenceDocument>();
RegisterClassMap<VexSignalDocument>();
RegisterClassMap<VexSeveritySignalDocument>();
RegisterClassMap<VexClaimDocumentRecord>();
RegisterClassMap<VexSignatureMetadataDocument>();
RegisterClassMap<VexStatementRecord>();
using System.Threading;
using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.Serializers;
namespace StellaOps.Excititor.Storage.Mongo;
public static class VexMongoMappingRegistry
{
private static int _initialized;
public static void Register()
{
if (Interlocked.Exchange(ref _initialized, 1) == 1)
{
return;
}
try
{
BsonSerializer.RegisterSerializer(typeof(byte[]), new ByteArraySerializer());
}
catch
{
// serializer already registered safe to ignore
}
RegisterClassMaps();
}
private static void RegisterClassMaps()
{
RegisterClassMap<VexProviderRecord>();
RegisterClassMap<VexProviderDiscoveryDocument>();
RegisterClassMap<VexProviderTrustDocument>();
RegisterClassMap<VexCosignTrustDocument>();
RegisterClassMap<VexConsensusRecord>();
RegisterClassMap<VexProductDocument>();
RegisterClassMap<VexConsensusSourceDocument>();
RegisterClassMap<VexConsensusConflictDocument>();
RegisterClassMap<VexConfidenceDocument>();
RegisterClassMap<VexSignalDocument>();
RegisterClassMap<VexSeveritySignalDocument>();
RegisterClassMap<VexClaimDocumentRecord>();
RegisterClassMap<VexSignatureMetadataDocument>();
RegisterClassMap<VexStatementRecord>();
RegisterClassMap<VexCacheEntryRecord>();
RegisterClassMap<VexConnectorStateDocument>();
RegisterClassMap<VexConsensusHoldRecord>();
}
private static void RegisterClassMap<TDocument>()
where TDocument : class
{
if (BsonClassMap.IsClassMapRegistered(typeof(TDocument)))
{
return;
}
BsonClassMap.RegisterClassMap<TDocument>(classMap =>
{
classMap.AutoMap();
classMap.SetIgnoreExtraElements(true);
});
}
}
RegisterClassMap<AirgapImportRecord>();
}
private static void RegisterClassMap<TDocument>()
where TDocument : class
{
if (BsonClassMap.IsClassMapRegistered(typeof(TDocument)))
{
return;
}
BsonClassMap.RegisterClassMap<TDocument>(classMap =>
{
classMap.AutoMap();
classMap.SetIgnoreExtraElements(true);
});
}
}
public static class VexMongoCollectionNames
{
public const string Migrations = "vex.migrations";
@@ -79,4 +80,5 @@ public static class VexMongoCollectionNames
public const string Attestations = "vex.attestations";
public const string Observations = "vex.observations";
public const string Linksets = "vex.linksets";
public const string AirgapImports = "vex.airgap_imports";
}

View File

@@ -33,6 +33,7 @@ internal static class TestServiceOverrides
services.RemoveAll<IVexCacheMaintenance>();
services.RemoveAll<IVexAttestationClient>();
services.RemoveAll<IVexSigner>();
services.RemoveAll<IAirgapImportStore>();
services.AddSingleton<IVexIngestOrchestrator, StubIngestOrchestrator>();
services.AddSingleton<IVexConnectorStateRepository, StubConnectorStateRepository>();
@@ -45,6 +46,7 @@ internal static class TestServiceOverrides
services.AddSingleton<IVexCacheMaintenance, StubCacheMaintenance>();
services.AddSingleton<IVexAttestationClient, StubAttestationClient>();
services.AddSingleton<IVexSigner, StubSigner>();
services.AddSingleton<IAirgapImportStore, StubAirgapImportStore>();
services.RemoveAll<IHostedService>();
services.AddSingleton<IHostedService, NoopHostedService>();
@@ -200,6 +202,17 @@ internal static class TestServiceOverrides
public ValueTask<VexSignedPayload> SignAsync(ReadOnlyMemory<byte> payload, CancellationToken cancellationToken)
=> ValueTask.FromResult(new VexSignedPayload("stub-signature", "stub-key"));
}
private sealed class StubAirgapImportStore : IAirgapImportStore
{
private readonly List<AirgapImportRecord> _records = new();
public Task SaveAsync(AirgapImportRecord record, CancellationToken cancellationToken)
{
_records.Add(record);
return Task.CompletedTask;
}
}
private sealed class StubIngestOrchestrator : IVexIngestOrchestrator
{