Sprint 4 — Sidebar restructure (S4-T01+T02):
5 groups: Release Control, Security, Operations, Audit & Evidence, Setup & Admin
Groups 4+5 collapsed by default for new users
Operations extracted from Release Control into own group
Audit extracted from Security into own group
groupOrder and resolveMenuGroupLabel updated
Approvals badge moved to section-level
Sprint 2 — Demo data badges (S2-T04+T05):
Backend: isDemo=true on all compatibility/seed responses in
PackAdapterEndpoints, QuotaCompatibilityEndpoints, VulnerabilitiesController
Frontend: "(Demo)" badges on Usage & Limits page quotas
Frontend: "(Demo)" badges on triage artifact list when seed data
New PlatformItemResponse/PlatformListResponse with IsDemo field
Sprint 6 — Audit emission infrastructure (S6-T01+T02):
New shared library: src/__Libraries/StellaOps.Audit.Emission/
- AuditActionAttribute: [AuditAction("module", "action")] endpoint tag
- AuditActionFilter: IEndpointFilter that auto-emits UnifiedAuditEvent
- HttpAuditEventEmitter: POSTs to Timeline /api/v1/audit/ingest
- Single-line DI: services.AddAuditEmission(configuration)
Timeline service: POST /api/v1/audit/ingest ingestion endpoint
- IngestAuditEventStore: 10k-event ring buffer
- CompositeUnifiedAuditEventProvider: merges HTTP-polled + ingested
Documentation: docs/modules/audit/AUDIT_EMISSION_GUIDE.md
Angular build: 0 errors. .NET builds: 0 errors.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6.1 KiB
Audit Event Emission Guide
This guide explains how to add automatic audit event emission to any StellaOps service using the shared StellaOps.Audit.Emission library.
Overview
The audit emission infrastructure provides:
AuditActionAttribute-- marks an endpoint for automatic audit event emissionAuditActionFilter-- ASP.NET Core endpoint filter that creates and sendsUnifiedAuditEventpayloadsHttpAuditEventEmitter-- posts events to the Timeline service'sPOST /api/v1/audit/ingestendpointAddAuditEmission()-- single-line DI registration
Events flow: Service endpoint -> AuditActionFilter -> HttpAuditEventEmitter -> Timeline /api/v1/audit/ingest -> IngestAuditEventStore -> merged into unified audit query results.
Step 1: Add project reference
In your service's .csproj, add a reference to the shared library:
<ProjectReference Include="..\..\__Libraries\StellaOps.Audit.Emission\StellaOps.Audit.Emission.csproj" />
Adjust the relative path as needed for your service's location in the repo.
Step 2: Register in DI (Program.cs)
Add a single line to your service's Program.cs:
using StellaOps.Audit.Emission;
// After other service registrations:
builder.Services.AddAuditEmission(builder.Configuration);
This registers:
AuditActionFilter(scoped endpoint filter)HttpAuditEventEmitterasIAuditEventEmitter(singleton)AuditEmissionOptionsbound from configuration
Step 3: Tag endpoints
Add the AuditActionFilter and AuditActionAttribute metadata to any endpoint you want audited:
using StellaOps.Audit.Emission;
app.MapPost("/api/v1/environments", CreateEnvironment)
.AddEndpointFilter<AuditActionFilter>()
.WithMetadata(new AuditActionAttribute("concelier", "create"));
app.MapPut("/api/v1/environments/{id}", UpdateEnvironment)
.AddEndpointFilter<AuditActionFilter>()
.WithMetadata(new AuditActionAttribute("concelier", "update"));
app.MapDelete("/api/v1/environments/{id}", DeleteEnvironment)
.AddEndpointFilter<AuditActionFilter>()
.WithMetadata(new AuditActionAttribute("concelier", "delete"));
Attribute parameters
| Parameter | Required | Description |
|---|---|---|
module |
Yes | Module name from UnifiedAuditCatalog.Modules (e.g., "authority", "policy", "jobengine", "vex", "scanner", "integrations") |
action |
Yes | Action name from UnifiedAuditCatalog.Actions (e.g., "create", "update", "delete", "promote", "approve") |
ResourceType |
No | Optional resource type override. If omitted, inferred from the URL path segment after the version prefix. |
Step 4: Configuration (optional)
The emitter reads configuration from the AuditEmission section or environment variables:
{
"AuditEmission": {
"TimelineBaseUrl": "http://timeline.stella-ops.local",
"Enabled": true,
"TimeoutSeconds": 3
}
}
Environment variable overrides:
STELLAOPS_TIMELINE_URL-- overridesTimelineBaseUrlAuditEmission__Enabled-- set tofalseto disable emissionAuditEmission__TimeoutSeconds-- HTTP timeout for ingest calls
How the filter works
- The endpoint executes normally and returns its result to the caller.
- After execution, the filter checks for
AuditActionAttributemetadata. - If present, it builds an
AuditEventPayloadcontaining:- Module and Action from the attribute
- Actor from
HttpContext.Userclaims (sub,name,email,stellaops:tenant) - Resource from route parameters (first matching
id,resourceId, etc., or first GUID value) - Severity inferred from HTTP response status code (2xx=info, 4xx=warning, 5xx=error)
- Description auto-generated:
"{Action} {module} resource {resourceId}" - CorrelationId from
X-Correlation-Idheader orHttpContext.TraceIdentifier
- The event is posted asynchronously (fire-and-forget) to
POST /api/v1/audit/ingest. - Failures are logged but never propagated -- audit emission must not affect the endpoint response.
Timeline ingest endpoint
The Timeline service exposes:
POST /api/v1/audit/ingest
- Auth: Requires
timeline:writescope - Body: JSON matching the
AuditEventPayloadschema (camelCase) - Response:
202 Acceptedwith{ "eventId": "...", "status": "accepted" } - Gateway route: Already covered by the existing
/api/v1/audit(.*)route inrouter-gateway-local.json
Ingested events are stored in an in-memory ring buffer (max 10,000 events) and merged with the HTTP-polled events from other modules (JobEngine, Policy, EvidenceLocker, Notify) in the unified audit query results.
Architecture decisions
- Fire-and-forget emission: Audit events are sent asynchronously after the endpoint responds. This ensures zero latency impact on the audited endpoint.
- No compile-time dependency on Timeline: The
AuditEventPayloadDTOs in the emission library are wire-compatible withUnifiedAuditEventbut live in a separate namespace, avoiding circular dependencies. - In-memory ingest store: For the alpha phase, ingested events are stored in memory. A future sprint will add Postgres persistence for the ingest store.
- Composite event provider: The Timeline service merges HTTP-polled events with ingested events, so all audit data appears in a single unified stream.
File locations
| File | Path |
|---|---|
| Shared library | src/__Libraries/StellaOps.Audit.Emission/ |
| Attribute | src/__Libraries/StellaOps.Audit.Emission/AuditActionAttribute.cs |
| Filter | src/__Libraries/StellaOps.Audit.Emission/AuditActionFilter.cs |
| Emitter | src/__Libraries/StellaOps.Audit.Emission/HttpAuditEventEmitter.cs |
| DI extension | src/__Libraries/StellaOps.Audit.Emission/AuditEmissionServiceExtensions.cs |
| Ingest endpoint | src/Timeline/StellaOps.Timeline.WebService/Endpoints/UnifiedAuditEndpoints.cs |
| Ingest store | src/Timeline/StellaOps.Timeline.WebService/Audit/IngestAuditEventStore.cs |
| Composite provider | src/Timeline/StellaOps.Timeline.WebService/Audit/CompositeUnifiedAuditEventProvider.cs |