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.
flowchart TD IN["Any of: evidence · trust · identitySignal lifecycle, deterministic projection into a portable signal. Source: signals/engine.py.
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)"]
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 oversignal_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_atis injectable viaclock: 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.
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.