using System.IO; using System.Net.Http; using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; using NetEscapades.Configuration.Yaml; using StellaOps.Auth.Abstractions; using StellaOps.Auth.ServerIntegration; using StellaOps.Configuration; using StellaOps.Signals.Authentication; using StellaOps.Signals.Hosting; using StellaOps.Signals.Models; using StellaOps.Signals.Options; using StellaOps.Signals.Parsing; using StellaOps.Signals.Persistence; using StellaOps.Signals.Routing; using StellaOps.Signals.Services; using StellaOps.Signals.Storage; var builder = WebApplication.CreateBuilder(args); builder.Configuration.AddStellaOpsDefaults(options => { options.BasePath = builder.Environment.ContentRootPath; options.EnvironmentPrefix = "SIGNALS_"; options.ConfigureBuilder = configurationBuilder => { var contentRoot = builder.Environment.ContentRootPath; foreach (var relative in new[] { "../etc/signals.yaml", "../etc/signals.local.yaml", "signals.yaml", "signals.local.yaml" }) { var path = Path.Combine(contentRoot, relative); configurationBuilder.AddYamlFile(path, optional: true); } }; }); var bootstrap = builder.Configuration.BindOptions( SignalsOptions.SectionName, static (options, _) => { SignalsAuthorityOptionsConfigurator.ApplyDefaults(options.Authority); options.Validate(); }); builder.Services.AddOptions() .Bind(builder.Configuration.GetSection(SignalsOptions.SectionName)) .PostConfigure(static options => { SignalsAuthorityOptionsConfigurator.ApplyDefaults(options.Authority); options.Validate(); }) .Validate(static options => { try { options.Validate(); return true; } catch (Exception ex) { throw new OptionsValidationException( SignalsOptions.SectionName, typeof(SignalsOptions), new[] { ex.Message }); } }) .ValidateOnStart(); builder.Services.AddSingleton(sp => sp.GetRequiredService>().Value); builder.Services.AddSingleton(); builder.Services.AddSingleton(TimeProvider.System); builder.Services.AddSingleton(); builder.Services.AddProblemDetails(); builder.Services.AddHealthChecks(); builder.Services.AddRouting(options => options.LowercaseUrls = true); builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); // Configure callgraph artifact storage based on driver if (bootstrap.Storage.IsRustFsDriver()) { // Configure HttpClient for RustFS builder.Services.AddHttpClient(RustFsCallgraphArtifactStore.HttpClientName, (sp, client) => { var opts = sp.GetRequiredService>().Value; client.Timeout = opts.Storage.RustFs.Timeout; }) .ConfigurePrimaryHttpMessageHandler(sp => { var opts = sp.GetRequiredService>().Value; var handler = new HttpClientHandler(); if (opts.Storage.RustFs.AllowInsecureTls) { handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator; } return handler; }); builder.Services.AddSingleton(); } else { builder.Services.AddSingleton(); } builder.Services.AddSingleton(new SimpleJsonCallgraphParser("java")); builder.Services.AddSingleton(new SimpleJsonCallgraphParser("nodejs")); builder.Services.AddSingleton(new SimpleJsonCallgraphParser("python")); builder.Services.AddSingleton(new SimpleJsonCallgraphParser("go")); builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(sp => { var options = sp.GetRequiredService>().Value; return new RedisReachabilityCache(options.Cache); }); builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(sp => { var inner = sp.GetRequiredService(); var cache = sp.GetRequiredService(); return new ReachabilityFactCacheDecorator(inner, cache); }); builder.Services.AddSingleton(); builder.Services.AddOptions() .Bind(builder.Configuration.GetSection(UnknownsScoringOptions.SectionName)); builder.Services.AddOptions() .Bind(builder.Configuration.GetSection(UnknownsDecayOptions.SectionName)); builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddHostedService(); builder.Services.AddSingleton(); builder.Services.AddHttpClient((sp, client) => { var opts = sp.GetRequiredService().Events.Router; if (Uri.TryCreate(opts.BaseUrl, UriKind.Absolute, out var baseUri)) { client.BaseAddress = baseUri; } if (opts.TimeoutSeconds > 0) { client.Timeout = TimeSpan.FromSeconds(opts.TimeoutSeconds); } client.DefaultRequestHeaders.ConnectionClose = false; }).ConfigurePrimaryHttpMessageHandler(sp => { var opts = sp.GetRequiredService().Events.Router; var handler = new HttpClientHandler(); if (opts.AllowInsecureTls) { handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator; } return handler; }); builder.Services.AddSingleton(sp => { var options = sp.GetRequiredService(); var eventBuilder = sp.GetRequiredService(); if (!options.Events.Enabled) { return new NullEventsPublisher(); } if (string.Equals(options.Events.Driver, "redis", StringComparison.OrdinalIgnoreCase)) { return new RedisEventsPublisher( options, sp.GetRequiredService(), eventBuilder, sp.GetRequiredService>()); } if (string.Equals(options.Events.Driver, "router", StringComparison.OrdinalIgnoreCase)) { return sp.GetRequiredService(); } return new InMemoryEventsPublisher( sp.GetRequiredService>(), eventBuilder); }); builder.Services.AddSingleton(); builder.Services.AddSingleton(); // Sprint: SPRINT_3800_0001_0002 builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); if (bootstrap.Authority.Enabled) { builder.Services.AddHttpContextAccessor(); builder.Services.AddStellaOpsScopeHandler(); builder.Services.AddAuthorization(options => { options.AddStellaOpsScopePolicy(SignalsPolicies.Read, SignalsPolicies.Read); options.AddStellaOpsScopePolicy(SignalsPolicies.Write, SignalsPolicies.Write); options.AddStellaOpsScopePolicy(SignalsPolicies.Admin, SignalsPolicies.Admin); }); builder.Services.AddStellaOpsResourceServerAuthentication( builder.Configuration, configurationSection: $"{SignalsOptions.SectionName}:Authority", configure: resourceOptions => { resourceOptions.Authority = bootstrap.Authority.Issuer; resourceOptions.RequireHttpsMetadata = bootstrap.Authority.RequireHttpsMetadata; resourceOptions.MetadataAddress = bootstrap.Authority.MetadataAddress; resourceOptions.BackchannelTimeout = TimeSpan.FromSeconds(bootstrap.Authority.BackchannelTimeoutSeconds); resourceOptions.TokenClockSkew = TimeSpan.FromSeconds(bootstrap.Authority.TokenClockSkewSeconds); resourceOptions.Audiences.Clear(); foreach (var audience in bootstrap.Authority.Audiences) { resourceOptions.Audiences.Add(audience); } resourceOptions.RequiredScopes.Clear(); foreach (var scope in bootstrap.Authority.RequiredScopes) { resourceOptions.RequiredScopes.Add(scope); } foreach (var tenant in bootstrap.Authority.RequiredTenants) { resourceOptions.RequiredTenants.Add(tenant); } foreach (var network in bootstrap.Authority.BypassNetworks) { resourceOptions.BypassNetworks.Add(network); } }); } else { builder.Services.AddAuthorization(); builder.Services.AddAuthentication(options => { options.DefaultAuthenticateScheme = "Anonymous"; options.DefaultChallengeScheme = "Anonymous"; }).AddScheme("Anonymous", static _ => { }); } var app = builder.Build(); if (!bootstrap.Authority.Enabled) { app.Logger.LogWarning("Signals Authority authentication is disabled; relying on header-based development fallback."); } app.UseAuthentication(); app.UseAuthorization(); app.MapHealthChecks("/healthz").AllowAnonymous(); app.MapGet("/readyz", (SignalsStartupState state, SignalsSealedModeMonitor sealedModeMonitor) => { if (!sealedModeMonitor.IsCompliant(out var reason)) { return Results.Json( new { status = "sealed-mode-blocked", reason }, statusCode: StatusCodes.Status503ServiceUnavailable); } return state.IsReady ? Results.Ok(new { status = "ready" }) : Results.StatusCode(StatusCodes.Status503ServiceUnavailable); }).AllowAnonymous(); var signalsGroup = app.MapGroup("/signals"); signalsGroup.MapGet("/ping", (HttpContext context, SignalsOptions options, SignalsSealedModeMonitor sealedModeMonitor) => { if (!Program.TryAuthorize(context, requiredScope: SignalsPolicies.Read, fallbackAllowed: options.Authority.AllowAnonymousFallback, out var authFailure)) { return authFailure ?? Results.Unauthorized(); } if (!Program.TryEnsureSealedMode(sealedModeMonitor, out var sealedFailure)) { return sealedFailure ?? Results.StatusCode(StatusCodes.Status503ServiceUnavailable); } return Results.NoContent(); }).WithName("SignalsPing"); signalsGroup.MapGet("/status", (HttpContext context, SignalsOptions options, SignalsSealedModeMonitor sealedModeMonitor) => { if (!Program.TryAuthorize(context, SignalsPolicies.Read, options.Authority.AllowAnonymousFallback, out var failure)) { return failure ?? Results.Unauthorized(); } var sealedCompliant = sealedModeMonitor.IsCompliant(out var sealedReason); return Results.Ok(new { service = "signals", version = typeof(Program).Assembly.GetName().Version?.ToString() ?? "unknown", sealedMode = new { enforced = sealedModeMonitor.EnforcementEnabled, compliant = sealedCompliant, reason = sealedCompliant ? null : sealedReason } }); }).WithName("SignalsStatus"); signalsGroup.MapPost("/callgraphs", async Task ( HttpContext context, SignalsOptions options, CallgraphIngestRequest request, ICallgraphIngestionService ingestionService, SignalsSealedModeMonitor sealedModeMonitor, CancellationToken cancellationToken) => { if (!Program.TryAuthorize(context, SignalsPolicies.Write, options.Authority.AllowAnonymousFallback, out var authFailure)) { return authFailure ?? Results.Unauthorized(); } if (!Program.TryEnsureSealedMode(sealedModeMonitor, out var sealedFailure)) { return sealedFailure ?? Results.StatusCode(StatusCodes.Status503ServiceUnavailable); } try { var result = await ingestionService.IngestAsync(request, cancellationToken).ConfigureAwait(false); return Results.Accepted($"/signals/callgraphs/{result.CallgraphId}", result); } catch (CallgraphIngestionValidationException ex) { return Results.BadRequest(new { error = ex.Message }); } catch (CallgraphParserNotFoundException ex) { return Results.BadRequest(new { error = ex.Message }); } catch (CallgraphParserValidationException ex) { return Results.UnprocessableEntity(new { error = ex.Message }); } catch (FormatException ex) { return Results.BadRequest(new { error = ex.Message }); } }).WithName("SignalsCallgraphIngest"); signalsGroup.MapGet("/callgraphs/{callgraphId}", async Task ( HttpContext context, SignalsOptions options, string callgraphId, ICallgraphRepository callgraphRepository, SignalsSealedModeMonitor sealedModeMonitor, CancellationToken cancellationToken) => { if (!Program.TryAuthorize(context, SignalsPolicies.Read, options.Authority.AllowAnonymousFallback, out var authFailure)) { return authFailure ?? Results.Unauthorized(); } if (!Program.TryEnsureSealedMode(sealedModeMonitor, out var sealedFailure)) { return sealedFailure ?? Results.StatusCode(StatusCodes.Status503ServiceUnavailable); } if (string.IsNullOrWhiteSpace(callgraphId)) { return Results.BadRequest(new { error = "callgraphId is required." }); } var document = await callgraphRepository.GetByIdAsync(callgraphId.Trim(), cancellationToken).ConfigureAwait(false); return document is null ? Results.NotFound() : Results.Ok(document); }).WithName("SignalsCallgraphGet"); signalsGroup.MapGet("/callgraphs/{callgraphId}/manifest", async Task ( HttpContext context, SignalsOptions options, string callgraphId, ICallgraphRepository callgraphRepository, SignalsSealedModeMonitor sealedModeMonitor, CancellationToken cancellationToken) => { if (!Program.TryAuthorize(context, SignalsPolicies.Read, options.Authority.AllowAnonymousFallback, out var authFailure)) { return authFailure ?? Results.Unauthorized(); } if (!Program.TryEnsureSealedMode(sealedModeMonitor, out var sealedFailure)) { return sealedFailure ?? Results.StatusCode(StatusCodes.Status503ServiceUnavailable); } if (string.IsNullOrWhiteSpace(callgraphId)) { return Results.BadRequest(new { error = "callgraphId is required." }); } var document = await callgraphRepository.GetByIdAsync(callgraphId.Trim(), cancellationToken).ConfigureAwait(false); if (document is null || string.IsNullOrWhiteSpace(document.Artifact.ManifestPath)) { return Results.NotFound(); } var manifestPath = Path.Combine(options.Storage.RootPath, document.Artifact.ManifestPath); if (!File.Exists(manifestPath)) { return Results.NotFound(new { error = "manifest not found" }); } var bytes = await File.ReadAllBytesAsync(manifestPath, cancellationToken).ConfigureAwait(false); return Results.File(bytes, "application/json"); }).WithName("SignalsCallgraphManifestGet"); signalsGroup.MapPost("/runtime-facts", async Task ( HttpContext context, SignalsOptions options, RuntimeFactsIngestRequest request, IRuntimeFactsIngestionService ingestionService, SignalsSealedModeMonitor sealedModeMonitor, CancellationToken cancellationToken) => { if (!Program.TryAuthorize(context, SignalsPolicies.Write, options.Authority.AllowAnonymousFallback, out var authFailure)) { return authFailure ?? Results.Unauthorized(); } if (!Program.TryEnsureSealedMode(sealedModeMonitor, out var sealedFailure)) { return sealedFailure ?? Results.StatusCode(StatusCodes.Status503ServiceUnavailable); } try { var response = await ingestionService.IngestAsync(request, cancellationToken).ConfigureAwait(false); return Results.Accepted($"/signals/runtime-facts/{response.SubjectKey}", response); } catch (RuntimeFactsValidationException ex) { return Results.BadRequest(new { error = ex.Message }); } }).WithName("SignalsRuntimeIngest"); signalsGroup.MapPost("/runtime-facts/synthetic", async Task ( HttpContext context, SignalsOptions options, SyntheticRuntimeProbeRequest request, ICallgraphRepository callgraphRepository, IRuntimeFactsIngestionService ingestionService, SyntheticRuntimeProbeBuilder probeBuilder, SignalsSealedModeMonitor sealedModeMonitor, CancellationToken cancellationToken) => { if (!Program.TryAuthorize(context, SignalsPolicies.Write, options.Authority.AllowAnonymousFallback, out var authFailure)) { return authFailure ?? Results.Unauthorized(); } if (!Program.TryEnsureSealedMode(sealedModeMonitor, out var sealedFailure)) { return sealedFailure ?? Results.StatusCode(StatusCodes.Status503ServiceUnavailable); } if (string.IsNullOrWhiteSpace(request.CallgraphId)) { return Results.BadRequest(new { error = "callgraphId is required." }); } var callgraph = await callgraphRepository.GetByIdAsync(request.CallgraphId.Trim(), cancellationToken).ConfigureAwait(false); if (callgraph is null) { return Results.NotFound(new { error = "callgraph not found." }); } var subject = request.Subject ?? new ReachabilitySubject { ScanId = $"synthetic-{callgraph.Id}" }; var events = probeBuilder.BuildEvents(callgraph, request.EventCount); var metadata = request.Metadata is null ? new Dictionary(StringComparer.Ordinal) : new Dictionary(request.Metadata, StringComparer.Ordinal); metadata.TryAdd("source", "synthetic-probe"); var ingestRequest = new RuntimeFactsIngestRequest { CallgraphId = callgraph.Id, Subject = subject, Events = events, Metadata = metadata }; var response = await ingestionService.IngestAsync(ingestRequest, cancellationToken).ConfigureAwait(false); return Results.Accepted($"/signals/runtime-facts/{response.SubjectKey}", response); }).WithName("SignalsRuntimeIngestSynthetic"); signalsGroup.MapPost("/reachability/union", async Task ( HttpContext context, SignalsOptions options, [FromHeader(Name = "X-Analysis-Id")] string? analysisId, IReachabilityUnionIngestionService ingestionService, SignalsSealedModeMonitor sealedModeMonitor, CancellationToken cancellationToken) => { if (!Program.TryAuthorize(context, SignalsPolicies.Write, options.Authority.AllowAnonymousFallback, out var authFailure)) { return authFailure ?? Results.Unauthorized(); } if (!Program.TryEnsureSealedMode(sealedModeMonitor, out var sealedFailure)) { return sealedFailure ?? Results.StatusCode(StatusCodes.Status503ServiceUnavailable); } var id = string.IsNullOrWhiteSpace(analysisId) ? Guid.NewGuid().ToString("N") : analysisId.Trim(); if (!string.Equals(context.Request.ContentType, "application/zip", StringComparison.OrdinalIgnoreCase)) { return Results.BadRequest(new { error = "Content-Type must be application/zip" }); } try { var response = await ingestionService.IngestAsync(id, context.Request.Body, cancellationToken).ConfigureAwait(false); return Results.Accepted($"/signals/reachability/union/{response.AnalysisId}/meta", response); } catch (Exception ex) { return Results.BadRequest(new { error = ex.Message }); } }).WithName("SignalsReachabilityUnionIngest"); signalsGroup.MapGet("/reachability/union/{analysisId}/meta", async Task ( HttpContext context, SignalsOptions options, string analysisId, SignalsSealedModeMonitor sealedModeMonitor, CancellationToken cancellationToken) => { if (!Program.TryAuthorize(context, SignalsPolicies.Read, options.Authority.AllowAnonymousFallback, out var authFailure)) { return authFailure ?? Results.Unauthorized(); } if (!Program.TryEnsureSealedMode(sealedModeMonitor, out var sealedFailure)) { return sealedFailure ?? Results.StatusCode(StatusCodes.Status503ServiceUnavailable); } if (string.IsNullOrWhiteSpace(analysisId)) { return Results.BadRequest(new { error = "analysisId is required." }); } var path = Path.Combine(options.Storage.RootPath, "reachability_graphs", analysisId.Trim(), "meta.json"); if (!File.Exists(path)) { return Results.NotFound(); } var bytes = await File.ReadAllBytesAsync(path, cancellationToken).ConfigureAwait(false); return Results.File(bytes, "application/json"); }).WithName("SignalsReachabilityUnionMeta"); signalsGroup.MapGet("/reachability/union/{analysisId}/files/{fileName}", async Task ( HttpContext context, SignalsOptions options, string analysisId, string fileName, SignalsSealedModeMonitor sealedModeMonitor, CancellationToken cancellationToken) => { if (!Program.TryAuthorize(context, SignalsPolicies.Read, options.Authority.AllowAnonymousFallback, out var authFailure)) { return authFailure ?? Results.Unauthorized(); } if (!Program.TryEnsureSealedMode(sealedModeMonitor, out var sealedFailure)) { return sealedFailure ?? Results.StatusCode(StatusCodes.Status503ServiceUnavailable); } if (string.IsNullOrWhiteSpace(analysisId) || string.IsNullOrWhiteSpace(fileName)) { return Results.BadRequest(new { error = "analysisId and fileName are required." }); } var root = Path.Combine(options.Storage.RootPath, "reachability_graphs", analysisId.Trim()); var path = Path.Combine(root, fileName.Replace('/', Path.DirectorySeparatorChar)); if (!File.Exists(path)) { return Results.NotFound(); } var contentType = fileName.EndsWith(".json", StringComparison.OrdinalIgnoreCase) ? "application/json" : "application/x-ndjson"; var bytes = await File.ReadAllBytesAsync(path, cancellationToken).ConfigureAwait(false); return Results.File(bytes, contentType); }).WithName("SignalsReachabilityUnionFile"); signalsGroup.MapPost("/runtime-facts/ndjson", async Task ( HttpContext context, SignalsOptions options, [AsParameters] RuntimeFactsStreamMetadata metadata, IRuntimeFactsIngestionService ingestionService, SignalsSealedModeMonitor sealedModeMonitor, CancellationToken cancellationToken) => { if (!Program.TryAuthorize(context, SignalsPolicies.Write, options.Authority.AllowAnonymousFallback, out var authFailure)) { return authFailure ?? Results.Unauthorized(); } if (!Program.TryEnsureSealedMode(sealedModeMonitor, out var sealedFailure)) { return sealedFailure ?? Results.StatusCode(StatusCodes.Status503ServiceUnavailable); } if (metadata is null || string.IsNullOrWhiteSpace(metadata.CallgraphId)) { return Results.BadRequest(new { error = "callgraphId is required." }); } var subject = metadata.ToSubject(); var isGzip = string.Equals(context.Request.Headers.ContentEncoding, "gzip", StringComparison.OrdinalIgnoreCase); var events = await RuntimeFactsNdjsonReader.ReadAsync(context.Request.Body, isGzip, cancellationToken).ConfigureAwait(false); if (events.Count == 0) { return Results.BadRequest(new { error = "runtime fact stream was empty." }); } var request = new RuntimeFactsIngestRequest { Subject = subject, CallgraphId = metadata.CallgraphId, Events = events }; try { var response = await ingestionService.IngestAsync(request, cancellationToken).ConfigureAwait(false); return Results.Accepted($"/signals/runtime-facts/{response.SubjectKey}", response); } catch (RuntimeFactsValidationException ex) { return Results.BadRequest(new { error = ex.Message }); } }).WithName("SignalsRuntimeIngestNdjson"); signalsGroup.MapGet("/facts/{subjectKey}", async Task ( HttpContext context, SignalsOptions options, string subjectKey, IReachabilityFactRepository factRepository, SignalsSealedModeMonitor sealedModeMonitor, CancellationToken cancellationToken) => { if (!Program.TryAuthorize(context, SignalsPolicies.Read, options.Authority.AllowAnonymousFallback, out var authFailure)) { return authFailure ?? Results.Unauthorized(); } if (!Program.TryEnsureSealedMode(sealedModeMonitor, out var sealedFailure)) { return sealedFailure ?? Results.StatusCode(StatusCodes.Status503ServiceUnavailable); } if (string.IsNullOrWhiteSpace(subjectKey)) { return Results.BadRequest(new { error = "subjectKey is required." }); } var fact = await factRepository.GetBySubjectAsync(subjectKey.Trim(), cancellationToken).ConfigureAwait(false); return fact is null ? Results.NotFound() : Results.Ok(fact); }).WithName("SignalsFactsGet"); signalsGroup.MapPost("/unknowns", async Task ( HttpContext context, SignalsOptions options, UnknownsIngestRequest request, IUnknownsIngestionService ingestionService, SignalsSealedModeMonitor sealedModeMonitor, CancellationToken cancellationToken) => { if (!Program.TryAuthorize(context, SignalsPolicies.Write, options.Authority.AllowAnonymousFallback, out var authFailure)) { return authFailure ?? Results.Unauthorized(); } if (!Program.TryEnsureSealedMode(sealedModeMonitor, out var sealedFailure)) { return sealedFailure ?? Results.StatusCode(StatusCodes.Status503ServiceUnavailable); } try { var response = await ingestionService.IngestAsync(request, cancellationToken).ConfigureAwait(false); return Results.Accepted($"/signals/unknowns/{response.SubjectKey}", response); } catch (UnknownsValidationException ex) { return Results.BadRequest(new { error = ex.Message }); } }).WithName("SignalsUnknownsIngest"); signalsGroup.MapGet("/unknowns/{subjectKey}", async Task ( HttpContext context, SignalsOptions options, string subjectKey, IUnknownsRepository repository, SignalsSealedModeMonitor sealedModeMonitor, CancellationToken cancellationToken) => { if (!Program.TryAuthorize(context, SignalsPolicies.Read, options.Authority.AllowAnonymousFallback, out var authFailure)) { return authFailure ?? Results.Unauthorized(); } if (!Program.TryEnsureSealedMode(sealedModeMonitor, out var sealedFailure)) { return sealedFailure ?? Results.StatusCode(StatusCodes.Status503ServiceUnavailable); } if (string.IsNullOrWhiteSpace(subjectKey)) { return Results.BadRequest(new { error = "subjectKey is required." }); } var items = await repository.GetBySubjectAsync(subjectKey.Trim(), cancellationToken).ConfigureAwait(false); return items.Count == 0 ? Results.NotFound() : Results.Ok(items); }).WithName("SignalsUnknownsGet"); signalsGroup.MapGet("/unknowns", async Task ( HttpContext context, SignalsOptions options, IUnknownsRepository repository, SignalsSealedModeMonitor sealedModeMonitor, [FromQuery] string? band, [FromQuery] int limit = 100, [FromQuery] int offset = 0, CancellationToken cancellationToken = default) => { if (!Program.TryAuthorize(context, SignalsPolicies.Read, options.Authority.AllowAnonymousFallback, out var authFailure)) { return authFailure ?? Results.Unauthorized(); } if (!Program.TryEnsureSealedMode(sealedModeMonitor, out var sealedFailure)) { return sealedFailure ?? Results.StatusCode(StatusCodes.Status503ServiceUnavailable); } limit = Math.Clamp(limit, 1, 1000); offset = Math.Max(0, offset); UnknownsBand? bandFilter = null; if (!string.IsNullOrWhiteSpace(band) && Enum.TryParse(band, ignoreCase: true, out var parsedBand)) { bandFilter = parsedBand; } var items = await repository.QueryAsync(bandFilter, limit, offset, cancellationToken).ConfigureAwait(false); return Results.Ok(new { items, count = items.Count, limit, offset, band = bandFilter?.ToString().ToLowerInvariant() }); }).WithName("SignalsUnknownsQuery"); signalsGroup.MapGet("/unknowns/{id}/explain", async Task ( HttpContext context, SignalsOptions options, string id, IUnknownsRepository repository, IUnknownsScoringService scoringService, SignalsSealedModeMonitor sealedModeMonitor, CancellationToken cancellationToken) => { if (!Program.TryAuthorize(context, SignalsPolicies.Read, options.Authority.AllowAnonymousFallback, out var authFailure)) { return authFailure ?? Results.Unauthorized(); } if (!Program.TryEnsureSealedMode(sealedModeMonitor, out var sealedFailure)) { return sealedFailure ?? Results.StatusCode(StatusCodes.Status503ServiceUnavailable); } if (string.IsNullOrWhiteSpace(id)) { return Results.BadRequest(new { error = "id is required." }); } var unknown = await repository.GetByIdAsync(id.Trim(), cancellationToken).ConfigureAwait(false); if (unknown is null) { return Results.NotFound(new { error = $"Unknown with id '{id}' not found." }); } return Results.Ok(new { id = unknown.Id, subjectKey = unknown.SubjectKey, band = unknown.Band.ToString().ToLowerInvariant(), score = unknown.Score, normalizationTrace = unknown.NormalizationTrace, flags = unknown.Flags, nextScheduledRescan = unknown.NextScheduledRescan, rescanAttempts = unknown.RescanAttempts, createdAt = unknown.CreatedAt, updatedAt = unknown.UpdatedAt }); }).WithName("SignalsUnknownsExplain"); signalsGroup.MapPost("/reachability/recompute", async Task ( HttpContext context, SignalsOptions options, ReachabilityRecomputeRequest request, IReachabilityScoringService scoringService, SignalsSealedModeMonitor sealedModeMonitor, CancellationToken cancellationToken) => { if (!Program.TryAuthorize(context, SignalsPolicies.Admin, options.Authority.AllowAnonymousFallback, out var authFailure)) { return authFailure ?? Results.Unauthorized(); } if (!Program.TryEnsureSealedMode(sealedModeMonitor, out var sealedFailure)) { return sealedFailure ?? Results.StatusCode(StatusCodes.Status503ServiceUnavailable); } try { var fact = await scoringService.RecomputeAsync(request, cancellationToken).ConfigureAwait(false); return Results.Ok(new { fact.Id, fact.CallgraphId, subject = fact.Subject, fact.EntryPoints, fact.States, fact.ComputedAt }); } catch (ReachabilityScoringValidationException ex) { return Results.BadRequest(new { error = ex.Message }); } catch (ReachabilityCallgraphNotFoundException ex) { return Results.NotFound(new { error = ex.Message }); } }).WithName("SignalsReachabilityRecompute"); app.Run(); public partial class Program { internal static bool TryAuthorize(HttpContext httpContext, string requiredScope, bool fallbackAllowed, out IResult? failure) { if (httpContext.User?.Identity?.IsAuthenticated == true) { if (TokenScopeAuthorizer.HasScope(httpContext.User, requiredScope)) { failure = null; return true; } failure = Results.StatusCode(StatusCodes.Status403Forbidden); return false; } if (!fallbackAllowed) { failure = Results.Unauthorized(); return false; } if (!httpContext.Request.Headers.TryGetValue("X-Scopes", out var scopesHeader) || string.IsNullOrWhiteSpace(scopesHeader.ToString())) { failure = Results.Unauthorized(); return false; } var principal = HeaderScopeAuthorizer.CreatePrincipal(scopesHeader.ToString()); if (HeaderScopeAuthorizer.HasScope(principal, requiredScope)) { failure = null; return true; } failure = Results.StatusCode(StatusCodes.Status403Forbidden); return false; } internal static bool TryEnsureSealedMode(SignalsSealedModeMonitor monitor, out IResult? failure) { if (!monitor.EnforcementEnabled) { failure = null; return true; } if (monitor.IsCompliant(out var reason)) { failure = null; return true; } failure = Results.Json( new { error = "sealed-mode evidence invalid", reason }, statusCode: StatusCodes.Status503ServiceUnavailable); return false; } }