- Introduced AGENTS.md, README.md, TASKS.md, and implementation_plan.md for Vexer, detailing mission, responsibilities, key components, and operational notes. - Established similar documentation structure for Vulnerability Explorer and Zastava modules, including their respective workflows, integrations, and observability notes. - Created risk scoring profiles documentation outlining the core workflow, factor model, governance, and deliverables. - Ensured all modules adhere to the Aggregation-Only Contract and maintain determinism and provenance in outputs.
		
			
				
	
	
	
		
			8.2 KiB
		
	
	
	
	
	
	
	
			
		
		
	
	Scanner Orchestrator Events (ORCH-SVC-38-101)
Last updated: 2025-10-26
The Notifications Studio initiative (NOTIFY-SVC-38-001) and orchestrator backlog (ORCH-SVC-38-101) standardise how platform services emit lifecycle events. This document describes the Scanner WebService contract for the new orchestrator envelopes (scanner.event.*) and how they supersede the legacy Redis-backed scanner.report.ready / scanner.scan.completed events.
1. Envelope overview
Orchestrator events share a deterministic JSON envelope:
| Field | Type | Notes | 
|---|---|---|
eventId | 
uuid | 
Globally unique identifier generated per occurrence. | 
kind | 
string | 
Event identifier; Scanner emits scanner.event.report.ready and scanner.event.scan.completed. | 
version | 
integer | 
Schema version. Initial release uses 1. | 
tenant | 
string | 
Tenant that owns the scan/report. Mirrors Authority claims. | 
occurredAt | 
date-time | 
UTC instant when the underlying state transition happened (e.g., report persisted). | 
recordedAt | 
date-time | 
UTC instant when the event was durably written. Optional but recommended. | 
source | 
string | 
Producer identifier (scanner.webservice). | 
idempotencyKey | 
string | 
Deterministic key for duplicate suppression (see §4). | 
correlationId | 
string | 
Maps back to the API request or scan identifier. | 
traceId / spanId | 
string | 
W3C trace context propagated into downstream telemetry. | 
scope | 
object | 
Describes the affected artefact. Requires repo and digest; optional namespace, component, image. | 
attributes | 
object | 
Flat string map for frequently queried metadata (e.g., policy revision). | 
payload | 
object | 
Event-specific body (see §2). | 
Canonical schemas live under docs/events/scanner.event.*@1.json. Samples that round-trip through NotifyCanonicalJsonSerializer are stored in docs/events/samples/.
2. Event kinds and payloads
2.1 scanner.event.report.ready
Emitted once a signed report is persisted and attested. Payload highlights:
reportId/scanId— identifiers for the persisted report and originating scan. Until Scan IDs are surfaced by the API,scanIdmirrorsreportIdso downstream correlators can stabilise on a single key.- Attributes: 
reportId,policyRevisionId,policyDigest,verdict— pre-sorted for deterministic routing. - Links:
ui→/ui/reports/{reportId}on the current host.report→{apiBasePath}/{reportsSegment}/{reportId}(defaults to/api/v1/reports/{reportId}).policy→{apiBasePath}/{policySegment}/revisions/{revisionId}when a revision is present.attestation→/ui/attestations/{reportId}when a DSSE envelope is included.
 imageDigest— OCI image digest associated with the analysis.generatedAt— report generation timestamp (ISO-8601 UTC).verdict—pass,warn, orfailafter policy evaluation.summary— blocked/warned/ignored/quieted counters (all non-negative integers).delta— newly critical/high counts and optionalkevarray.quietedFindingCount— mirrorssummary.quieted.policy— revision metadata (digest,revisionId) surfaced for routing.links— UI/report/policy URLs suitable for operators.dsse— embedded DSSE envelope (payload, type, signature list).report— canonical report document; identical to the DSSE payload.
Schema: docs/events/scanner.event.report.ready@1.json
Sample: docs/events/samples/scanner.event.report.ready@1.sample.json
2.2 scanner.event.scan.completed
Emitted after scan execution finishes (success or policy failure). Payload highlights:
reportId/scanId/imageDigest— identifiers mirroring the report-ready event. As with the report-ready payload,scanIdcurrently mirrorsreportIdas a temporary shim.- Attributes: 
reportId,policyRevisionId,policyDigest,verdict. - Links: same as above (
ui,report,policy) withattestationpopulated when DSSE metadata exists. verdict,summary,delta,policy— same semantics as above.findings— array of surfaced findings withid,severity, optionalcve,purl, andreachability.links,dsse,report— same structure as §2.1 (allows Notifier to reuse signatures).
Schema: docs/events/scanner.event.scan.completed@1.json
Sample: docs/events/samples/scanner.event.scan.completed@1.sample.json
2.3 Relationship to legacy events
| Legacy Redis event | Replacement orchestrator event | Notes | 
|---|---|---|
scanner.report.ready | 
scanner.event.report.ready | 
Adds versioning, idempotency, trace context. Payload is a superset of the legacy fields. | 
scanner.scan.completed | 
scanner.event.scan.completed | 
Same data plus explicit scan identifiers and orchestrator metadata. | 
Legacy schemas remain for backwards-compatibility during migration, but new integrations must target the orchestrator variants.
3. Deterministic serialization
- Producers must serialise events using 
NotifyCanonicalJsonSerializerto guarantee consistent key ordering and whitespace. - Timestamps (
occurredAt,recordedAt,payload.generatedAt) useDateTimeOffset.UtcDateTime.ToString("O"). - Payload arrays (
delta.kev,findings) should be pre-sorted (e.g., alphabetical CVE order) so hash-based consumers remain stable. - Optional fields are omitted rather than emitted as 
null. 
4. Idempotency and correlation
Idempotency keys dedupe repeated publishes and align with the orchestrator’s outbox pattern:
| Event kind | Idempotency key template | 
|---|---|
scanner.event.report.ready | 
scanner.event.report.ready:<tenant>:<reportId> | 
scanner.event.scan.completed | 
scanner.event.scan.completed:<tenant>:<scanId> | 
Keys are ASCII lowercase; components should be trimmed and validated before concatenation. Retries must reuse the same key.
correlationId should match the scan identifier that appears in REST responses (scanId). Re-using the same value across the pair of events allows Notifier and orchestrator analytics to stitch lifecycle data together.
5. Versioning and evolution
- Increment the 
versionfield and the@<version>suffix for breaking changes (field removals, type changes, semantic shifts). - Additive optional fields may remain within version 1; update the JSON schema and samples accordingly.
 - When introducing 
@2, keep the@1schema/docs in place until orchestrator subscribers confirm migration. 
6. Consumer checklist
- Validate incoming payloads against the schema for the targeted version.
 - Use 
idempotencyKeyfor dedupe, noteventId. - Map 
traceId/spanIdinto telemetry spans to preserve causality. - Prefer 
payload.report→policy.revisionIdwhen populating templates; the top-levelattributesare convenience duplicates for quick routing. - Reserve the legacy Redis events for transitional compatibility only; downstream systems should subscribe to the orchestrator bus exposed by ORCH-SVC-38-101.
 
7. Implementation status and next actions
- Scanner WebService — 
SCANNER-EVENTS-16-301(blocked) andSCANNER-EVENTS-16-302(doing) track the production of these envelopes. The remaining blocker is the .NET 10 preview OpenAPI/Auth dependency drift that currently breaksdotnet test. Once Gateway and Notifier owners land the replacement packages, rerun the full test suite and capture fresh fixtures underdocs/events/samples/. - Gateway/Notifier consumers — subscribe to the orchestrator stream documented in ORCH-SVC-38-101. When the Scanner tasks unblock, regenerate notifier contract tests against the sample events included here.
 - Docs cadence — update this file and the matching JSON schemas whenever payload fields change. Use the rehearsal checklist in 
docs/modules/devops/runbooks/launch-cutover.mdto confirm downstream validation before the production cutover. Record gaps or newly required fields indocs/modules/devops/runbooks/launch-readiness.mdso they land in the launch checklist. 
Imposed rule reminder: work of this type or tasks of this type on this component must also be applied everywhere else it should be applied.