# Excititor Timeline Events (OBS-52-001) Defines the event envelope for evidence timelines emitted by Excititor. All fields are aggregation-only; no consensus/merge logic. ## Envelope ```json { "type": "excititor.timeline.v1", "tenant": "default", "eventId": "urn:uuid:...", "timestamp": "2025-11-23T23:10:00Z", "traceId": "beefcafe...", "spanId": "deadb33f...", "source": "excititor.web", "kind": "observation|linkset", "action": "ingest|update|backfill|replay", "observationId": "vex:obs:sha256:...", "linksetId": "CVE-2024-0001:pkg:maven/org.demo/app@1.2.3", "justifications": ["component_not_present"], "conflicts": [ {"providerId": "suse-csaf", "status": "fixed", "justification": null} ], "evidenceHash": "sha256:...", // content-addressed payload hash "dsseEnvelopeHash": "sha256:...", // if attested (see OBS-54-001) "metadata": {"connector": "ubuntu-csaf", "mirrorGeneration": 12} } ``` ## Semantics - `eventId` is stable per write; retries reuse the same ID. - `timestamp` must be UTC; derive from TimeProvider. - `traceId`/`spanId` propagate ingestion traces; if tracing is disabled, set both to `null`. - `kind` + `action` drive downstream storage and alerting. - `evidenceHash` is the raw document hash; `dsseEnvelopeHash` appears only when OBS-54-001 is enabled. ## Determinism - Sort `justifications` and `conflicts` ascending by providerId/status before emit. - Emit at-most-once per storage write; idempotent consumers rely on `(eventId, tenant)`. ## Transport - Default topic: `excititor.timeline.v1` (NATS/Redis). Subject includes tenant: `excititor.timeline.v1.`. - Payload size should stay <32 KiB; truncate conflict arrays with `truncated=true` flag if needed (keep hash counts deterministic).