Files
git.stella-ops.org/src/Excititor/StellaOps.Excititor.Worker/Program.cs
2026-01-13 18:53:39 +02:00

134 lines
5.4 KiB
C#

using System.IO;
using System.Linq;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using StellaOps.Plugin;
using StellaOps.Excititor.Connectors.Abstractions;
using StellaOps.Excititor.Core;
using StellaOps.Excititor.Core.Aoc;
using StellaOps.Excititor.Core.Storage;
using StellaOps.Excititor.Core.Orchestration;
using StellaOps.Excititor.Formats.CSAF;
using StellaOps.Excititor.Formats.CycloneDX;
using StellaOps.Excititor.Formats.OpenVEX;
using StellaOps.Excititor.Persistence.Extensions;
using StellaOps.Excititor.Worker.Auth;
using StellaOps.Excititor.Worker.Options;
using StellaOps.Excititor.Worker.Orchestration;
using StellaOps.Excititor.Worker.Plugins;
using StellaOps.Excititor.Worker.Scheduling;
using StellaOps.Excititor.Worker.Signature;
using StellaOps.Excititor.Attestation.Extensions;
using StellaOps.Excititor.Attestation.Verification;
using StellaOps.IssuerDirectory.Client;
var builder = Host.CreateApplicationBuilder(args);
var services = builder.Services;
var configuration = builder.Configuration;
var workerConfig = configuration.GetSection("Excititor:Worker");
var workerConfigSnapshot = workerConfig.Get<VexWorkerOptions>() ?? new VexWorkerOptions();
services.AddOptions<VexWorkerOptions>()
.Bind(workerConfig)
.ValidateOnStart();
services.Configure<VexWorkerPluginOptions>(configuration.GetSection("Excititor:Worker:Plugins"));
services.Configure<TenantAuthorityOptions>(configuration.GetSection("Excititor:Authority"));
services.AddSingleton<IValidateOptions<TenantAuthorityOptions>, TenantAuthorityOptionsValidator>();
services.PostConfigure<VexWorkerOptions>(options =>
{
if (options.DisableConsensus)
{
options.Refresh.Enabled = false;
}
});
// VEX connectors are loaded via plugin catalog below
// Direct connector registration removed in favor of plugin-based loading
services.AddOptions<VexStorageOptions>()
.Bind(configuration.GetSection("Excititor:Storage"))
.ValidateOnStart();
services.AddExcititorPersistence(configuration);
services.TryAddSingleton<IVexProviderStore, InMemoryVexProviderStore>();
services.TryAddScoped<IVexConnectorStateRepository, InMemoryVexConnectorStateRepository>();
services.TryAddSingleton<IVexClaimStore, InMemoryVexClaimStore>();
services.AddCsafNormalizer();
services.AddCycloneDxNormalizer();
services.AddOpenVexNormalizer();
services.AddSingleton<IVexSignatureVerifier, WorkerSignatureVerifier>();
services.AddVexAttestation();
services.Configure<VexAttestationVerificationOptions>(configuration.GetSection("Excititor:Attestation:Verification"));
var issuerDirectorySection = configuration.GetSection("Excititor:IssuerDirectory");
if (issuerDirectorySection.Exists())
{
services.AddIssuerDirectoryClient(issuerDirectorySection);
}
else
{
services.AddIssuerDirectoryClient(configuration);
}
services.PostConfigure<VexAttestationVerificationOptions>(options =>
{
// Workers operate in offline-first environments; allow verification to succeed without Rekor.
options.AllowOfflineTransparency = true;
if (!configuration.GetSection("Excititor:Attestation:Verification").Exists())
{
options.RequireTransparencyLog = false;
}
});
services.AddExcititorAocGuards();
services.AddSingleton<IValidateOptions<VexWorkerOptions>, VexWorkerOptionsValidator>();
services.AddSingleton(TimeProvider.System);
services.TryAddSingleton<IGuidGenerator, DefaultGuidGenerator>();
services.PostConfigure<VexWorkerOptions>(options =>
{
if (!options.Providers.Any(provider => string.Equals(provider.ProviderId, "excititor:redhat", StringComparison.OrdinalIgnoreCase)))
{
options.Providers.Add(new VexWorkerProviderOptions
{
ProviderId = "excititor:redhat",
});
}
});
// Load VEX connector plugins
services.AddSingleton<VexWorkerPluginCatalogLoader>();
services.AddSingleton<PluginCatalog>(provider =>
provider.GetRequiredService<VexWorkerPluginCatalogLoader>().Load().Catalog);
// Orchestrator worker SDK integration
services.AddOptions<VexWorkerOrchestratorOptions>()
.Bind(configuration.GetSection("Excititor:Worker:Orchestrator"))
.ValidateOnStart();
services.AddHttpClient(TenantAuthorityClientFactory.AuthorityClientName);
services.AddHttpClient<IVexWorkerOrchestratorClient, VexWorkerOrchestratorClient>((provider, client) =>
{
var opts = provider.GetRequiredService<IOptions<VexWorkerOrchestratorOptions>>().Value;
if (opts.BaseAddress is not null)
{
client.BaseAddress = opts.BaseAddress;
}
client.Timeout = opts.RequestTimeout;
});
services.AddSingleton<VexWorkerHeartbeatService>();
services.AddSingleton<IVexProviderRunner, DefaultVexProviderRunner>();
services.AddHostedService<VexWorkerHostedService>();
if (!workerConfigSnapshot.DisableConsensus)
{
services.AddSingleton<VexConsensusRefreshService>();
services.AddSingleton<IVexConsensusRefreshScheduler>(static provider => provider.GetRequiredService<VexConsensusRefreshService>());
services.AddHostedService(static provider => provider.GetRequiredService<VexConsensusRefreshService>());
}
services.AddSingleton<ITenantAuthorityClientFactory, TenantAuthorityClientFactory>();
var host = builder.Build();
await host.RunAsync();
// Make Program class file-scoped to prevent it from being exposed to referencing assemblies
file sealed partial class Program;