48 lines
2.3 KiB
Markdown
48 lines
2.3 KiB
Markdown
# signals.fact.updated event contract (SIGNALS-24-005 prep)
|
|
|
|
**Purpose**: replace the in-memory logger used during Signals development with a real event bus contract so reachability caches can be invalidated and downstream consumers (Policy Engine, Notifications, Console) can subscribe deterministically.
|
|
|
|
## Topic / channel
|
|
- Primary topic: `signals.fact.updated.v1`
|
|
- Dead-letter topic: `signals.fact.updated.dlq`
|
|
- Delivery: at-least-once; consumers must de-duplicate using `event_id`.
|
|
|
|
## Message envelope
|
|
```jsonc
|
|
{
|
|
"event_id": "uuid-v4", // stable across retries; used for idempotency
|
|
"emitted_at": "2025-11-20T00:00:00Z", // UTC, RFC3339
|
|
"tenant": "acme", // required; lower-case
|
|
"subject_key": "sbom:sha256:…" , // subject of facts (asset, sbom, host). Deterministic model key.
|
|
"fact_kind": "callgraph" | "runtime" | "reachability" | "signal", // enums mapped from Signals domain
|
|
"fact_version": 1, // monotonically increasing per subject_key + fact_kind
|
|
"digest": "sha256:…", // CAS digest of canonical fact document
|
|
"content_type": "application/json", // or application/vnd.stellaops.ndjson when chunked
|
|
"producer": "StellaOps.Signals", // emitting service
|
|
"source": {
|
|
"pipeline": "signals", // consistent with Observability tags
|
|
"release": "0.4.0-alpha" // optional
|
|
},
|
|
"trace": {
|
|
"trace_id": "…", // pass-through if available
|
|
"span_id": "…"
|
|
}
|
|
}
|
|
```
|
|
|
|
## Routing / partitions
|
|
- Partition key: `tenant` to keep per-tenant ordering.
|
|
- Retry policy: exponential backoff up to 5 minutes; move to DLQ thereafter with `dlq_reason` header.
|
|
|
|
## Consumer expectations
|
|
- De-duplicate on `event_id` and `digest`.
|
|
- Fetch fact body from CAS using `digest`; avoid embedding large payloads in the message.
|
|
- If consumer cannot resolve CAS, treat as transient and retry later (do not drop).
|
|
|
|
## Security / air-gap posture
|
|
- No PII; tenant id only.
|
|
- Works offline when bus is intra-cluster (e.g., NATS/Redis Streams); external exporters disabled in sealed mode.
|
|
|
|
## Provenance
|
|
- This contract supersedes the temporary log-based publisher referenced in Signals sprint 0143 Execution Log (2025-11-18). Aligns with `signals.fact.updated@v1` payload shape already covered by unit tests.
|