The Signal Engine is not a cron job. Here is why.
Most 'compliance engines' are a cron that checks a checklist once a day. Ours is not a cron. Here is why.
So. The competition has a “compliance engine.” You look under the hood; a 24h cron, a list of SQL conditions, an email blast. That is not an engine. That is rust.
TL;DR
- Cron = batch run at 3 AM; signal arrives 23 hours too late
- Event-driven on every mutation is too expensive (1,000 signals/sec)
- Our solution: signal scheduler with reactive deps at the model level plus cron fallback
- Median latency: 14 seconds; max: 4 minutes
The problem; cron is not enough
Context: a client logs an incident at 10:00. They should get a NIS2-001 signal (incident >24h without notification) at 10:00 the next day, as an early warning. Cron runs every 24h at 03:00; signal arrives 5 hours late. How it ended; a missed deadline, an administrative fine, client looking for another vendor.
The decision; event-based signal scheduler plus per-type intervals
Each signal type has its own interval (tickRate). Some are reactive (on every mutation), some are cron (once an hour). DPIA-MISSING fires reactively because it changes on every RcpEntry update. ASV-EXPIRED fires once a day because the last scan date rarely changes.
The solution; two paths
Reactive path
Every mutation on RcpEntry / Supplier / Contract / Incident passes through middleware that publishes an event to BullMQ. A worker picks it up, asks the scheduler “which signal types react to this event,” fires the checks. The result lands in the Signal table.
Cron path
For types that have no natural trigger (e.g. “contract expires in 90 days”) it runs once a day at 04:00. For “ASV scan overdue”; once a week on Sunday.
Pitfalls we hit
Numbers
Median latency in production: 14 seconds from mutation to Signal write. P99: 4 minutes (when the worker is busy). Cron-only fallback: 1h. That is enough for the 22 signal types we currently handle.
Written by someone on the RealSec team. Want to talk specifics ; get in touch.