Restructure solution layout by module
	
		
			
	
		
	
	
		
	
		
			Some checks failed
		
		
	
	
		
			
				
	
				Docs CI / lint-and-preview (push) Has been cancelled
				
			
		
		
	
	
				
					
				
			
		
			Some checks failed
		
		
	
	Docs CI / lint-and-preview (push) Has been cancelled
				
			This commit is contained in:
		| @@ -1,121 +1,121 @@ | ||||
| # 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. | ||||
| # 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. | ||||
|   | ||||
		Reference in New Issue
	
	Block a user