Aggregation-Only Guard Library Reference

Packages: StellaOps.Aoc, StellaOps.Aoc.AspNetCore
Related tasks: WEB-AOC-19-001, WEB-AOC-19-003, DEVOPS-AOC-19-001
Audience: Concelier/Excititor service owners, Platform guild, QA

The Aggregation-Only Contract (AOC) guard library enforces the canonical ingestion rules described in the Aggregation-Only Contract reference (see also the condensed AOC guardrails checklist). Service owners should use the guard whenever raw advisory or VEX payloads are accepted, so that forbidden fields are rejected long before they reach PostgreSQL.

Packages

StellaOps.Aoc

StellaOps.Aoc.AspNetCore

Minimal API integration

using StellaOps.Aoc;
using StellaOps.Aoc.AspNetCore.Routing;
using StellaOps.Aoc.AspNetCore.Results;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddAocGuard();
builder.Services.Configure<AocGuardOptions>(options =>
{
    options.RequireSignatureMetadata = true;
    options.RequireTenant = true;
});

var app = builder.Build();

app.MapPost("/ingest", async (IngestionRequest request, IAocGuard guard, ILogger<Program> logger) =>
    {
        // additional application logic
        return Results.Accepted();
    })
    .RequireAocGuard<IngestionRequest>(
        request => new object?[] { request.Payload },
        serializerOptions: null,
        guardOptions: null)
    .ProducesProblem(StatusCodes.Status400BadRequest)
    .WithTags("AOC");

app.UseExceptionHandler(errorApp =>
{
    errorApp.Run(async context =>
    {
        var exceptionHandler = context.Features.Get<IExceptionHandlerFeature>();
        if (exceptionHandler?.Error is AocGuardException guardException)
        {
            var result = AocHttpResults.Problem(context, guardException);
            await result.ExecuteAsync(context);
            return;
        }

        context.Response.StatusCode = StatusCodes.Status500InternalServerError;
    });
});

Key points:

Allowed top-level fields

AocWriteGuard enforces the contract’s top-level allowlist: _id, tenant, source, upstream, content, identifiers, linkset, supersedes, createdAt/created_at, ingestedAt/ingested_at, and attributes. Unknown fields produce ERR_AOC_007 violations. When staging schema changes, extend the allowlist through AocGuardOptions.AllowedTopLevelFields:

builder.Services.Configure<AocGuardOptions>(options =>
{
    options.AllowedTopLevelFields =
        options.AllowedTopLevelFields.Add("experimental_field");
});

Worker / repository usage

Inject IAocGuard (or a module-specific wrapper such as IVexRawWriteGuard) anywhere documents are persisted. Call ValidateOrThrow before writes to guarantee fail-fast behaviour, for example:

public sealed class AdvisoryRawRepository
{
    private readonly IAocGuard _guard;

    public AdvisoryRawRepository(IAocGuard guard) => _guard = guard;

    public Task WriteAsync(JsonDocument document, CancellationToken cancellationToken)
    {
        _guard.ValidateOrThrow(document.RootElement);
        // proceed with storage logic
    }
}

Configuration tips

Testing guidance

For questions or updates, coordinate with the BE‑Base Platform guild and reference WEB-AOC-19-001.