Skip to content
VerifAIer
Home / Docs / Engineering / Signal lifecycle
Engineering · Lifecycles

Signal lifecycle

Signals are the export boundary, a deterministic, self-contained snapshot of one operation's trust posture, ready to leave the system through a connector.

Signal lifecycle

SignalEngine.generate(...) projects any combination of existing runtime outputs into deterministic SignalEvents wrapped in a SignalExport. It is strictly a generation layer: no networking, no HTTP, no queues, no telemetry, and no communication with WIW. It reads only already-safe serialized fields and duplicates no upstream logic.

src/vailidator/signals/engine.py · models.py
flowchart TD
  IN["Any of: evidence · trust · identity
trust_profile · passport · reputation"] --> G["SignalEngine.generate(...)"] G --> H{"any usable data?"} H -->|no| E0["SignalExport (signal_count 0, signals empty)"] H -->|yes| PJ["project fields with precedence
trust > identity > profile > passport"] PJ --> SID["signal_id = 'sig_' + sha256(type|agent|evidence|receipt|prov|passport|reputation)[:16]"] SID --> EVT["one consolidated SignalEvent"] EVT --> XID["export_id = 'sigx_' + sha256(signal_ids)[:16]"] XID --> EX["SignalExport (signal_count 1)"]
Signal lifecycle, deterministic projection into a portable signal. Source: signals/engine.py.

When any input carries data, the engine emits a single consolidated SignalEvent that flattens the operation's ids and posture (trust/reputation scores, compliance status, risk/quality levels, provider/runtime/platform). Field selection uses a fixed precedence, for scores and levels, trust > identity > profile > passport. When no input has data, the export is empty (signal_count 0) rather than an error.

Content-addressed determinism

  • signal_id = sig_ + SHA-256 over signal_type | agent_id | evidence_id | receipt_id | provenance_id | passport_id | reputation_id (first 16 hex). No timestamp is in the hash, so the same operation always yields the same signal id.
  • export_id = sigx_ + SHA-256 over the contained signal ids.
  • created_at is injectable via clock: the only non-deterministic field, and it is outside the id hash by design.

Endpoint & composition

POST /api/v1/signals/export is a thin transport. If evidence is supplied (and truthy, an empty {} is treated as absent), it composes the chain Evidence → Trust → Identity → Trust Profile → Passport → Reputation → SignalExport, but any explicitly supplied layer takes precedence and is never recomputed. The response is the full SignalExport dict.

Where signals go. Delivery is a separate concern. The connector layer (connectors/) wraps a SignalExport into a ConnectorPayload addressed to a target (WIW, SIEM, data lake, OEM/white-label, analytics, archive). Six of its seven modules are generation-only: no networking, no queues, and the signal lifecycle ends at a portable, deterministic artifact. wiw_contribution.py is the exception and the only one that opens a socket. It POSTs one sealed, minimized signal to a destination the caller names, it is reachable from no route on this API, and it is authorized through the same preventive boundary as every other governed action. Nothing is sent unless you call it.