Some checks failed
		
		
	
	Docs CI / lint-and-preview (push) Has been cancelled
				
			- Implemented PolicyDslValidator with command-line options for strict mode and JSON output. - Created PolicySchemaExporter to generate JSON schemas for policy-related models. - Developed PolicySimulationSmoke tool to validate policy simulations against expected outcomes. - Added project files and necessary dependencies for each tool. - Ensured proper error handling and usage instructions across tools.
		
			
				
	
	
		
			122 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			122 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| # 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, `scanId` mirrors `reportId` so 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`, or `fail` after policy evaluation.
 | ||
| - `summary` — blocked/warned/ignored/quieted counters (all non-negative integers).
 | ||
| - `delta` — newly critical/high counts and optional `kev` array.
 | ||
| - `quietedFindingCount` — mirrors `summary.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, `scanId` currently mirrors `reportId` as a temporary shim.
 | ||
| - **Attributes:** `reportId`, `policyRevisionId`, `policyDigest`, `verdict`.
 | ||
| - **Links:** same as above (`ui`, `report`, `policy`) with `attestation` populated when DSSE metadata exists.
 | ||
| - `verdict`, `summary`, `delta`, `policy` — same semantics as above.
 | ||
| - `findings` — array of surfaced findings with `id`, `severity`, optional `cve`, `purl`, and `reachability`.
 | ||
| - `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 `NotifyCanonicalJsonSerializer` to guarantee consistent key ordering and whitespace.
 | ||
| - Timestamps (`occurredAt`, `recordedAt`, `payload.generatedAt`) use `DateTimeOffset.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 `version` field 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 `@1` schema/docs in place until orchestrator subscribers confirm migration.
 | ||
| 
 | ||
| ## 6. Consumer checklist
 | ||
| 
 | ||
| 1. Validate incoming payloads against the schema for the targeted version.
 | ||
| 2. Use `idempotencyKey` for dedupe, not `eventId`.
 | ||
| 3. Map `traceId`/`spanId` into telemetry spans to preserve causality.
 | ||
| 4. Prefer `payload.report` → `policy.revisionId` when populating templates; the top-level `attributes` are convenience duplicates for quick routing.
 | ||
| 5. 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) and `SCANNER-EVENTS-16-302` (doing) track the production of these envelopes. The remaining blocker is the .NET 10 preview OpenAPI/Auth dependency drift that currently breaks `dotnet test`. Once Gateway and Notifier owners land the replacement packages, rerun the full test suite and capture fresh fixtures under `docs/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/ops/launch-cutover.md` to confirm downstream validation before the production cutover. Record gaps or newly required fields in `docs/ops/launch-readiness.md` so 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.
 |