up
Some checks failed
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Mirror Thin Bundle Sign & Verify / mirror-sign (push) Has been cancelled
api-governance / spectral-lint (push) Has been cancelled

This commit is contained in:
StellaOps Bot
2025-11-24 07:52:25 +02:00
parent 5970f0d9bd
commit 150b3730ef
215 changed files with 8119 additions and 740 deletions

View File

@@ -0,0 +1,37 @@
using Microsoft.AspNetCore.Mvc;
using StellaOps.Policy.Engine.AdvisoryAI;
namespace StellaOps.Policy.Engine.Endpoints;
public static class AdvisoryAiKnobsEndpoint
{
public static IEndpointRouteBuilder MapAdvisoryAiKnobs(this IEndpointRouteBuilder routes)
{
routes.MapGet("/policy/advisory-ai/knobs", GetAsync)
.WithName("PolicyEngine.AdvisoryAI.Knobs.Get");
routes.MapPut("/policy/advisory-ai/knobs", PutAsync)
.WithName("PolicyEngine.AdvisoryAI.Knobs.Put");
return routes;
}
private static IResult GetAsync(AdvisoryAiKnobsService service)
{
var profile = service.Get();
return Results.Json(profile);
}
private static IResult PutAsync(
[FromBody] IReadOnlyList<AdvisoryAiKnob> knobs,
AdvisoryAiKnobsService service)
{
if (knobs is null || knobs.Count == 0)
{
return Results.BadRequest(new { message = "knobs are required" });
}
var profile = service.Set(knobs);
return Results.Json(profile);
}
}

View File

@@ -0,0 +1,30 @@
using Microsoft.AspNetCore.Mvc;
using StellaOps.Policy.Engine.BatchContext;
namespace StellaOps.Policy.Engine.Endpoints;
public static class BatchContextEndpoint
{
public static IEndpointRouteBuilder MapBatchContext(this IEndpointRouteBuilder routes)
{
routes.MapPost("/policy/batch/context", HandleAsync)
.WithName("PolicyEngine.BatchContext.Create");
return routes;
}
private static IResult HandleAsync(
[FromBody] BatchContextRequest request,
BatchContextService service)
{
try
{
var response = service.Create(request);
return Results.Json(response);
}
catch (ArgumentException ex)
{
return Results.BadRequest(new { message = ex.Message });
}
}
}

View File

@@ -0,0 +1,43 @@
using Microsoft.AspNetCore.Mvc;
using StellaOps.Policy.Engine.Ledger;
namespace StellaOps.Policy.Engine.Endpoints;
public static class LedgerExportEndpoint
{
public static IEndpointRouteBuilder MapLedgerExport(this IEndpointRouteBuilder routes)
{
routes.MapPost("/policy/ledger/export", BuildAsync)
.WithName("PolicyEngine.Ledger.Export");
routes.MapGet("/policy/ledger/export/{exportId}", GetAsync)
.WithName("PolicyEngine.Ledger.GetExport");
return routes;
}
private static async Task<IResult> BuildAsync(
[FromBody] LedgerExportRequest request,
LedgerExportService service,
CancellationToken cancellationToken)
{
try
{
var export = await service.BuildAsync(request, cancellationToken).ConfigureAwait(false);
return Results.Json(export);
}
catch (Exception ex) when (ex is ArgumentException or KeyNotFoundException)
{
return Results.BadRequest(new { message = ex.Message });
}
}
private static async Task<IResult> GetAsync(
[FromRoute] string exportId,
LedgerExportService service,
CancellationToken cancellationToken)
{
var export = await service.GetAsync(exportId, cancellationToken).ConfigureAwait(false);
return export is null ? Results.NotFound() : Results.Json(export);
}
}

View File

@@ -0,0 +1,62 @@
using Microsoft.AspNetCore.Mvc;
using StellaOps.Policy.Engine.Orchestration;
namespace StellaOps.Policy.Engine.Endpoints;
public static class OrchestratorJobEndpoint
{
public static IEndpointRouteBuilder MapOrchestratorJobs(this IEndpointRouteBuilder routes)
{
routes.MapPost("/policy/orchestrator/jobs", SubmitAsync)
.WithName("PolicyEngine.Orchestrator.Jobs.Submit");
routes.MapPost("/policy/orchestrator/jobs/preview", PreviewAsync)
.WithName("PolicyEngine.Orchestrator.Jobs.Preview");
routes.MapGet("/policy/orchestrator/jobs/{jobId}", GetAsync)
.WithName("PolicyEngine.Orchestrator.Jobs.Get");
return routes;
}
private static async Task<IResult> SubmitAsync(
[FromBody] OrchestratorJobRequest request,
OrchestratorJobService service,
CancellationToken cancellationToken)
{
try
{
var job = await service.SubmitAsync(request, cancellationToken).ConfigureAwait(false);
return Results.Json(job);
}
catch (ArgumentException ex)
{
return Results.BadRequest(new { message = ex.Message });
}
}
private static async Task<IResult> PreviewAsync(
[FromBody] OrchestratorJobRequest request,
OrchestratorJobService service,
CancellationToken cancellationToken)
{
try
{
var job = await service.PreviewAsync(request, cancellationToken).ConfigureAwait(false);
return Results.Json(job);
}
catch (ArgumentException ex)
{
return Results.BadRequest(new { message = ex.Message });
}
}
private static async Task<IResult> GetAsync(
[FromRoute] string jobId,
OrchestratorJobService service,
CancellationToken cancellationToken)
{
var job = await service.GetAsync(jobId, cancellationToken).ConfigureAwait(false);
return job is null ? Results.NotFound() : Results.Json(job);
}
}

View File

@@ -3,6 +3,7 @@ using System.Text.Json;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Mvc;
using StellaOps.Policy.Engine.Streaming;
using StellaOps.Policy.Engine.Overlay;
namespace StellaOps.Policy.Engine.Endpoints;
@@ -19,6 +20,7 @@ public static class PathScopeSimulationEndpoint
private static async Task<IResult> HandleAsync(
[FromBody] PathScopeSimulationRequest request,
PathScopeSimulationService service,
PathScopeSimulationBridgeService bridge,
CancellationToken cancellationToken)
{
try
@@ -31,6 +33,19 @@ public static class PathScopeSimulationEndpoint
responseBuilder.AppendLine(line);
}
// Emit change event stub when run in what-if mode.
if (request.Options.Deterministic && request.Options.IncludeTrace)
{
var bridgeRequest = new PathScopeSimulationBridgeRequest(
Tenant: request.Tenant,
Rules: Array.Empty<string>(),
Overlays: null,
Paths: new[] { request },
Mode: "preview",
Seed: null);
await bridge.SimulateAsync(bridgeRequest, cancellationToken).ConfigureAwait(false);
}
return Results.Text(responseBuilder.ToString(), "application/x-ndjson", Encoding.UTF8);
}
catch (PathScopeSimulationException ex)

View File

@@ -0,0 +1,43 @@
using Microsoft.AspNetCore.Mvc;
using StellaOps.Policy.Engine.Orchestration;
namespace StellaOps.Policy.Engine.Endpoints;
public static class PolicyWorkerEndpoint
{
public static IEndpointRouteBuilder MapPolicyWorker(this IEndpointRouteBuilder routes)
{
routes.MapPost("/policy/worker/run", RunAsync)
.WithName("PolicyEngine.Worker.Run");
routes.MapGet("/policy/worker/jobs/{jobId}", GetResultAsync)
.WithName("PolicyEngine.Worker.GetResult");
return routes;
}
private static async Task<IResult> RunAsync(
[FromBody] WorkerRunRequest request,
PolicyWorkerService service,
CancellationToken cancellationToken)
{
try
{
var result = await service.ExecuteAsync(request, cancellationToken).ConfigureAwait(false);
return Results.Json(result);
}
catch (KeyNotFoundException ex)
{
return Results.NotFound(new { message = ex.Message });
}
}
private static async Task<IResult> GetResultAsync(
[FromRoute] string jobId,
IWorkerResultStore store,
CancellationToken cancellationToken)
{
var result = await store.GetByJobIdAsync(jobId, cancellationToken).ConfigureAwait(false);
return result is null ? Results.NotFound() : Results.Json(result);
}
}

View File

@@ -0,0 +1,55 @@
using Microsoft.AspNetCore.Mvc;
using StellaOps.Policy.Engine.Snapshots;
namespace StellaOps.Policy.Engine.Endpoints;
public static class SnapshotEndpoint
{
public static IEndpointRouteBuilder MapSnapshots(this IEndpointRouteBuilder routes)
{
routes.MapPost("/policy/snapshots", CreateAsync)
.WithName("PolicyEngine.Snapshots.Create");
routes.MapGet("/policy/snapshots", ListAsync)
.WithName("PolicyEngine.Snapshots.List");
routes.MapGet("/policy/snapshots/{snapshotId}", GetAsync)
.WithName("PolicyEngine.Snapshots.Get");
return routes;
}
private static async Task<IResult> CreateAsync(
[FromBody] SnapshotRequest request,
SnapshotService service,
CancellationToken cancellationToken)
{
try
{
var snapshot = await service.CreateAsync(request, cancellationToken).ConfigureAwait(false);
return Results.Json(snapshot);
}
catch (ArgumentException ex)
{
return Results.BadRequest(new { message = ex.Message });
}
}
private static async Task<IResult> ListAsync(
[FromQuery(Name = "tenant_id")] string? tenantId,
SnapshotService service,
CancellationToken cancellationToken)
{
var (items, cursor) = await service.ListAsync(tenantId, cancellationToken).ConfigureAwait(false);
return Results.Json(new { items, next_cursor = cursor });
}
private static async Task<IResult> GetAsync(
[FromRoute] string snapshotId,
SnapshotService service,
CancellationToken cancellationToken)
{
var snapshot = await service.GetAsync(snapshotId, cancellationToken).ConfigureAwait(false);
return snapshot is null ? Results.NotFound() : Results.Json(snapshot);
}
}

View File

@@ -0,0 +1,56 @@
using Microsoft.AspNetCore.Mvc;
using StellaOps.Policy.Engine.TrustWeighting;
namespace StellaOps.Policy.Engine.Endpoints;
public static class TrustWeightingEndpoint
{
public static IEndpointRouteBuilder MapTrustWeighting(this IEndpointRouteBuilder routes)
{
routes.MapGet("/policy/trust-weighting", GetAsync)
.WithName("PolicyEngine.TrustWeighting.Get");
routes.MapPut("/policy/trust-weighting", PutAsync)
.WithName("PolicyEngine.TrustWeighting.Put");
routes.MapGet("/policy/trust-weighting/preview", PreviewAsync)
.WithName("PolicyEngine.TrustWeighting.Preview");
return routes;
}
private static IResult GetAsync(TrustWeightingService service)
{
var profile = service.Get();
return Results.Json(profile);
}
private static IResult PutAsync(
[FromBody] IReadOnlyList<TrustWeightingEntry> weights,
TrustWeightingService service)
{
if (weights is null || weights.Count == 0)
{
return Results.BadRequest(new { message = "weights are required" });
}
var profile = service.Set(weights);
return Results.Json(profile);
}
private static IResult PreviewAsync(
[FromQuery(Name = "overlay_hash")] string? overlayHash,
TrustWeightingService service)
{
var profile = service.Get();
var preview = new
{
weights = profile.Weights,
profile_hash = profile.ProfileHash,
overlay_hash = overlayHash,
mode = "preview"
};
return Results.Json(preview);
}
}

View File

@@ -0,0 +1,75 @@
using Microsoft.AspNetCore.Mvc;
using StellaOps.Policy.Engine.Violations;
namespace StellaOps.Policy.Engine.Endpoints;
public static class ViolationEndpoint
{
public static IEndpointRouteBuilder MapViolations(this IEndpointRouteBuilder routes)
{
routes.MapPost("/policy/violations/events", EmitEventsAsync)
.WithName("PolicyEngine.Violations.Events");
routes.MapPost("/policy/violations/severity", FuseAsync)
.WithName("PolicyEngine.Violations.Severity");
routes.MapPost("/policy/violations/conflicts", ConflictsAsync)
.WithName("PolicyEngine.Violations.Conflicts");
return routes;
}
private static async Task<IResult> EmitEventsAsync(
[FromBody] ViolationEventRequest request,
ViolationEventService service,
CancellationToken cancellationToken)
{
try
{
var events = await service.EmitAsync(request, cancellationToken).ConfigureAwait(false);
return Results.Json(new { events });
}
catch (Exception ex) when (ex is ArgumentException or KeyNotFoundException)
{
return Results.BadRequest(new { message = ex.Message });
}
}
private static async Task<IResult> FuseAsync(
[FromBody] ViolationEventRequest request,
ViolationEventService eventService,
SeverityFusionService fusionService,
CancellationToken cancellationToken)
{
try
{
await eventService.EmitAsync(request, cancellationToken).ConfigureAwait(false);
var fused = await fusionService.FuseAsync(request.SnapshotId, cancellationToken).ConfigureAwait(false);
return Results.Json(new { fused });
}
catch (Exception ex) when (ex is ArgumentException or KeyNotFoundException)
{
return Results.BadRequest(new { message = ex.Message });
}
}
private static async Task<IResult> ConflictsAsync(
[FromBody] ConflictRequest request,
ViolationEventService eventService,
SeverityFusionService fusionService,
ConflictHandlingService conflictService,
CancellationToken cancellationToken)
{
try
{
await eventService.EmitAsync(new ViolationEventRequest(request.SnapshotId), cancellationToken).ConfigureAwait(false);
var fused = await fusionService.FuseAsync(request.SnapshotId, cancellationToken).ConfigureAwait(false);
var conflicts = await conflictService.ComputeAsync(request.SnapshotId, fused, cancellationToken).ConfigureAwait(false);
return Results.Json(new { conflicts });
}
catch (Exception ex) when (ex is ArgumentException or KeyNotFoundException)
{
return Results.BadRequest(new { message = ex.Message });
}
}
}