//
// SPDX-License-Identifier: BUSL-1.1
//
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;
using StellaOps.Signals.Ebpf.Cgroup;
using StellaOps.Signals.Ebpf.Output;
using StellaOps.Signals.Ebpf.Parsers;
using StellaOps.Signals.Ebpf.Probes;
using StellaOps.Signals.Ebpf.Services;
using StellaOps.Signals.Ebpf.Symbols;
namespace StellaOps.Signals.Ebpf;
///
/// DI registration extensions for eBPF runtime evidence collection.
///
public static class ServiceCollectionExtensions
{
///
/// Adds eBPF runtime evidence collection services.
///
/// The service collection.
/// Optional configuration callback.
/// The service collection for chaining.
public static IServiceCollection AddEbpfRuntimeEvidence(
this IServiceCollection services,
Action? configureOptions = null)
{
var options = new EbpfEvidenceOptions();
configureOptions?.Invoke(options);
// Register options
services.AddSingleton(options);
services.AddSingleton(options.WriterOptions);
services.AddSingleton(options.CollectorOptions);
// Register memory cache if not already registered
services.TryAddSingleton(sp =>
new MemoryCache(new MemoryCacheOptions
{
SizeLimit = options.SymbolCacheSizeLimit,
}));
// Register symbol resolver
services.AddSingleton(sp =>
{
var logger = sp.GetRequiredService>();
var cache = sp.GetRequiredService();
return new EnhancedSymbolResolver(logger, cache, options.ProcRoot);
});
// Register cgroup resolver
services.AddSingleton(sp =>
{
var logger = sp.GetRequiredService>();
return new CgroupContainerResolver(logger, options.ProcRoot, options.CgroupRoot);
});
// Register event parser
services.AddSingleton(sp =>
{
var logger = sp.GetRequiredService>();
var symbolResolver = sp.GetRequiredService();
return new EventParser(logger, symbolResolver);
});
// Register probe loader
services.AddSingleton(sp =>
{
var logger = sp.GetRequiredService>();
var symbolResolver = sp.GetRequiredService();
return new CoreProbeLoader(logger, symbolResolver, options.ProbeDirectory);
});
// Register air-gap probe loader separately (different interface)
if (options.UseAirGapMode)
{
services.AddSingleton(sp =>
{
var airGapLogger = sp.GetRequiredService>();
return new AirGapProbeLoader(airGapLogger);
});
}
// Register NDJSON writer
services.AddSingleton(sp =>
{
var logger = sp.GetRequiredService>();
return new RuntimeEvidenceNdjsonWriter(
logger,
options.OutputDirectory,
options.WriterOptions);
});
// Register the unified evidence collector
services.AddSingleton(sp =>
{
var logger = sp.GetRequiredService>();
var probeLoader = sp.GetRequiredService();
var eventParser = sp.GetRequiredService();
var cgroupResolver = sp.GetRequiredService();
var writer = sp.GetRequiredService();
return new RuntimeEvidenceCollector(
logger,
probeLoader,
eventParser,
cgroupResolver,
writer,
options.CollectorOptions);
});
// Register the legacy IRuntimeSignalCollector adapter
services.AddSingleton(sp =>
{
var logger = sp.GetRequiredService>();
var probeLoader = sp.GetRequiredService();
return new RuntimeSignalCollector(logger, probeLoader);
});
return services;
}
///
/// Adds eBPF runtime evidence collection with air-gap mode enabled.
///
///
/// Air-gap mode uses offline probe loading without network dependencies.
///
public static IServiceCollection AddEbpfRuntimeEvidenceAirGap(
this IServiceCollection services,
Action? configureOptions = null)
{
return services.AddEbpfRuntimeEvidence(options =>
{
options.UseAirGapMode = true;
configureOptions?.Invoke(options);
});
}
}
///
/// Options for eBPF evidence collection services.
///
public sealed class EbpfEvidenceOptions
{
///
/// Path to the /proc filesystem (default: /proc).
///
public string ProcRoot { get; set; } = "/proc";
///
/// Path to the cgroup filesystem (default: /sys/fs/cgroup).
///
public string CgroupRoot { get; set; } = "/sys/fs/cgroup";
///
/// Directory containing compiled BPF probe objects.
///
public string? ProbeDirectory { get; set; }
///
/// Directory for NDJSON evidence output.
///
public string OutputDirectory { get; set; } = "/var/lib/stellaops/evidence";
///
/// Whether to use air-gap mode (offline probe loading).
///
public bool UseAirGapMode { get; set; }
///
/// Maximum size of the symbol resolution cache.
///
public long SymbolCacheSizeLimit { get; set; } = 100000;
///
/// NDJSON writer options.
///
public NdjsonWriterOptions WriterOptions { get; set; } = new();
///
/// Collector options.
///
public RuntimeEvidenceCollectorOptions CollectorOptions { get; set; } = new();
}