Signals API
Elke worker, ingest-handler en hub-side scanner schrijft Trust-Score-relevante
findings naar één tabel: signal_streams. Trust Score, Anomaly
Correlation, Compliance Pareto en alle dashboards lezen daar uit. Geen
per-feature silo-tabellen meer.
Wire-format conventies
| Veld | Vorm |
|---|---|
source | lowercase_snake_case — cert_scan, dns_check, … |
subject_type | lowercase_snake_case — agent, endpoint, domain, dependency, … |
signal_key | dotted lowercase — cert.expiry_days, dns.dmarc_present |
signal_value | JSONB; convention: {"value": <native>, …metadata} |
severity | NULL of één van `info |
observed_at | TIMESTAMPTZ; partition-kolom in de hypertable |
ttl_seconds | NULL = sticky |
Postgres CHECK-constraints valideren elke regex zodat een typo direct als
23514 boven komt; signals.Emitter.validate() doet dezelfde check in Go
zodat de error op de call site landt.
Go interface
import "github.com/gotrust/monsys/hub/api/signals"
emitter := signals.NewSQLEmitter(pool)
err := emitter.Emit(ctx, tenantID, signals.Signal{ Source: "my_check", SubjectType: "agent", SubjectID: agentID.String(), Key: "my.finding", Value: map[string]any{"value": 42, "extra": "context"}, Severity: signals.SeverityHigh, ObservedAt: time.Now().UTC(),})EmitBatch gebruikt pgx.CopyFrom; all-or-nothing: één invalid signal
verwerpt de hele batch (zie signals/emitter.go rationale).
Agent-emitted signals
De agent kan signals emit via een speciaal agent_signals payload-type
(Fase 1.4–1.6). Hub valideert:
source∈ allowlist (backup_check,clock_check,endpoint_posture)subject_type∈ allowlist (agent)subject_idoverschreven naar caller-agent_id wanneersubject_type=agent
Een gecompromitteerde agent kan nooit findings claimen voor een andere agent in zijn tenant.
Source → Trust Score categorie
Mapping in hub/api/trust_score.SourceToCategory. Een nieuwe source
toevoegen = één entry erbij; zonder die entry telt de source nul punten
in Trust Score.
RLS
signal_streams heeft ENABLE ROW LEVEL SECURITY met
USING tenant_id = current_setting('app.current_tenant', true)::UUID.
Defense in depth. Elke hub-side query MOET nog steeds expliciet
WHERE tenant_id = $1 bevatten — set_config(..., true) overleeft pgxpool’s
connection-recycling niet. Zie docs/internal/decisions/2026-Q2-connected-dashboards.md D-0003.
Compressie staat niet aan op signal_streams (Timescale 2.18+ weigert
RLS+columnstore combinatie). Retention via drop_chunks, default 180d.