Aggregation-Only Guard Library Reference
WITHDRAWN (2026-09-27, SPRINT_20260927_003 PCD-62). The AOC libraries (
StellaOps.Aoc,StellaOps.Aoc.AspNetCore), theStellaOps.Aoc.AnalyzersRoslyn analyzer and the CLI Aoc plug-in are no longer built or shipped. Their sources are frozen undersrc/__Obsoleted/Aoc/andsrc/__Obsoleted/Cli/(CODE_OF_CONDUCT 15.4). No service referenced them, and the plug-in’s direct-PostgreSQL verify form (--postgresoption) read anaoc_documentstable that no migration creates. This page describes the withdrawn code and is kept for reference. Still live and separate: theaoc:verifyscope. The core CLIaoccommand was withdrawn on 2026-10-01 (SPRINT_20260928_008 PCD-153); the Platform/api/v1/aoc/*compatibility endpoints (Development and testing only) and the Console AOC pages were withdrawn on 2026-10-08 (SPRINT_20261007_002 CCP-005 and CCP-004).
Libraries:
StellaOps.Aoc,StellaOps.Aoc.AspNetCore(project references; not published as NuGet packages) Source:src/Aoc/__Libraries/Audience: owners of code that accepts raw upstream payloads, Platform maintainers, QA
The Aggregation-Only Contract (AOC) guard library enforces the ingestion rules summarised in the AOC guardrails checklist and specified in ../architecture.md. Use the guard wherever raw advisory, VEX, or similar upstream payloads are accepted, so that forbidden fields are rejected before they reach storage.
Consumer status (2026-09-26). The services that wired this guard (Concelier, Excititor) are retired and frozen under
src/__Obsoleted/Concelier/. At commit942a18727cno live deployable callsAddAocGuardorRequireAocGuard; see../architecture.md§7.
Packages
StellaOps.Aoc
IAocGuard/AocWriteGuard— validate aJsonElementand returnAocGuardResult.AocGuardOptions— immutable record: signature enforcement, tenant requirement, required and allowed top-level fields, allowed signature formats.AocViolation/AocViolationCode— structured violations surfaced to callers.AocError— canonical error DTO (code,message,violations[]) used by the HTTP helper.ServiceCollectionExtensions.AddAocGuard()— registers the singleton guard.AocGuardExtensions.ValidateOrThrow()— throwsAocGuardExceptionwhen validation fails.
StellaOps.Aoc.AspNetCore
AocGuardEndpointFilter<TRequest>— minimal-API endpoint filter that runs request payloads through the guard before the handler.AocGuardEndpointFilterExtensions.RequireAocGuard<TRequest>()— adds the filter; overloads takeFunc<TRequest, object?>orFunc<TRequest, IEnumerable<object?>>, plus optionalJsonSerializerOptionsandAocGuardOptions.AocHttpResults.Problem(HttpContext, AocGuardException, ...)— RFC 7807 response carrying violation codes.
Minimal API integration
AocGuardOptions properties are init-only, so Configure<AocGuardOptions>(o => o.X = ...) does not compile. Register a configured instance instead, or pass guardOptions per endpoint:
using Microsoft.Extensions.Options;
using StellaOps.Aoc;
using StellaOps.Aoc.AspNetCore.Routing;
using StellaOps.Aoc.AspNetCore.Results;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAocGuard();
builder.Services.AddSingleton(Options.Create(AocGuardOptions.Default with
{
RequireSignatureMetadata = true,
RequireTenant = true,
}));
var app = builder.Build();
app.MapPost("/ingest", (IngestionRequest request) => Results.Accepted())
.RequireAocGuard<IngestionRequest>(request => request.Payload)
.ProducesProblem(StatusCodes.Status400BadRequest);
app.UseExceptionHandler(errorApp =>
{
errorApp.Run(async context =>
{
var feature = context.Features.Get<IExceptionHandlerFeature>();
if (feature?.Error is AocGuardException guardException)
{
await AocHttpResults.Problem(context, guardException).ExecuteAsync(context);
return;
}
context.Response.StatusCode = StatusCodes.Status500InternalServerError;
});
});
Key points:
- Register the guard before wiring repositories or workers that call it.
- The filter resolves options from the per-call
guardOptions, elseIOptions<AocGuardOptions>, elseAocGuardOptions.Default. - The payload selector may return several payloads (batch ingestion); each is validated and the first failure short-circuits with an
AocHttpResults.Problemresponse. AocHttpResults.Problememits the serializedAocErrorunder theerrorextension, pluscodeandviolations.
Allowed top-level fields
AocGuardOptions.Default allows the required fields (tenant, source, upstream, content, linkset) plus _id, identifiers, attributes, supersedes, createdAt/created_at, ingestedAt/ingested_at, links, advisory_key, and statements. Unknown fields produce ERR_AOC_007. Field names compare case-insensitively. To extend the allowlist:
var options = AocGuardOptions.Default with
{
AllowedTopLevelFields = AocGuardOptions.Default.AllowedTopLevelFields.Add("experimental_field"),
};
Worker / repository usage
Inject IAocGuard wherever documents are persisted and call ValidateOrThrow before the write:
public sealed class RawDocumentRepository
{
private readonly IAocGuard _guard;
public RawDocumentRepository(IAocGuard guard) => _guard = guard;
public Task WriteAsync(JsonDocument document, CancellationToken cancellationToken)
{
_guard.ValidateOrThrow(document.RootElement);
// proceed with storage logic
return Task.CompletedTask;
}
}
Configuration tips
- Adjust
RequiredTopLevelFieldsorAllowedTopLevelFields(viawith) when staging schema changes. - Setting
RequireSignatureMetadata = falsedisables theupstream.signaturechecks; record any such waiver in the owning module’s sprint. - Restrict
AllowedSignatureFormatswhen the ingesting service only accepts known envelope formats.
Testing guidance
- Unit-test guard behaviour with fixture payloads; the module’s own tests are under
src/Aoc/__Tests/. - Service-level tests should assert that guarded endpoints return
ERR_AOC_*codes throughAocHttpResults. - The
StellaOps.Aoc.AnalyzersdiagnosticAOC0003warns on database writes in ingestion code that are not preceded by anIAocGuardvalidation; reference the analyzer as described in../architecture.md§4.1.
