signals.fact.updated Event Contract (SIGNALS-24-005)
Audience: Signals Guild, Policy Engine and Notifications consumers, Console integrators.
Purpose: Define the durable event-bus contract that the Signals module emits when a fact changes. This replaces the in-memory logger used during early development so reachability caches can be invalidated and downstream consumers (Policy Engine, Notifications, Console) can subscribe deterministically.
When a callgraph, runtime, reachability, or signal fact changes, Signals publishes a small, content-addressed envelope. Consumers fetch the full fact body from CAS by digest rather than receiving large payloads inline, keeping the bus lightweight and the contract stable.
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
{
"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:
tenantto keep per-tenant ordering. - Retry policy: exponential backoff up to 5 minutes; move to DLQ thereafter with
dlq_reasonheader.
Consumer expectations
- De-duplicate on
event_idanddigest. - 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/Valkey 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@v1payload shape already covered by unit tests. - Implementation:
Signals.Eventsdefaults to Valkey Streams (signals.fact.updated.v1withsignals.fact.updated.dlq), emitting envelopes that includeevent_id,fact_version, and deterministicfact.digest(sha256) generated by the reachability fact hasher. - Router transport: set
Signals.Events.Driver=routerto POST envelopes to the Stella Ops Router gateway (BaseUrl+Path, default/router/events/signals.fact.updated) with optional API key/headers. This path forwards to downstream consumers registered in Router; Valkey remains mandatory for the reachability cache but is not required for event fan-out when the router transport is enabled.
Related
- CAS Promotion Checklist (SIGNALS-24-002) — the CAS tier this contract references via
digest. - Provenance Appendix Checklist (SIGNALS-24-003) — provenance fields carried alongside the fact body.
- Runtime Facts — one of the
fact_kindsources that triggers this event.
