---
title: "Signals - Workshop Docs"
description: "Signals - Enterprise control plane for Santa. Manage rules, approvals, telemetry, and policies across your macOS fleet."
doc_version: "1"
last_updated: "2026-10-16"
canonical: "https://northpole.security/docs/workshop/rules/signals"
---
# Signals

A signal is a CEL detection rule that Santa evaluates on the host against [telemetry](https://northpole.security/docs/workshop/telemetry). A match does not block anything. Santa reports it back, and the report appears under Signals in [Events](https://northpole.security/docs/workshop/events). Signals need telemetry enabled on the hosts they target.

You manage signals under **Rules > Signals**. Each signal applies to one operating system and to the hosts in its tags.

## The `event` Variable

A signal’s expression reads the current telemetry record through `event`, and must return a boolean. Guard each branch with `has()` for the event type it reads:

```
has(event.execution) && event.execution.Target.Executable.Path.startsWith("/tmp/")
```

Workshop checks every field against the telemetry schema of the signal’s operating system when you save it. A misspelt field, or a field from the other platform, is rejected rather than silently never matching on the host. See the [macOS schema](https://northpole.security/docs/workshop/telemetry/schema/macos) for the fields each event carries.

## Functions

Signals can use the standard CEL macros, the CEL string extensions, and these functions:

Function

Returns

Description

`correlate(window, threshold)`

`bool`

True once the preceding condition has matched `threshold` times within the trailing window

`correlate(window, threshold, groupBy)`

`bool`

The same, with a separate window per `groupBy` value (a string or a list of strings)

`days(n)`

`duration`

A duration of `n` days, for windows longer than `duration()` expresses cleanly

`sequence(stage, of, maxspan, key)`

`bool`

True on the final stage of an ordered sequence of events. See Sequences

Place `correlate()` and `sequence()` after the condition they apply to (`cond && correlate(...)`), so only matching events count. Their windows and progress persist across scans.

## Sequences

`sequence()` matches an ordered series of events that share a join key, all within a time span. Write one branch per stage, each guarded by its event type and predicate, with `sequence()` last:

```
(has(event.execution) && event.execution.Target.Executable.Path == "/usr/bin/curl" &&
 sequence(1, 2, duration("30s"), event.execution.Target.ID)) ||
(has(event.network_activity) &&
 sequence(2, 2, duration("30s"), event.network_activity.Process.ID))
```

-   `stage` is the branch’s position in the sequence, starting at 1.
-   `of` is the number of stages in the sequence.
-   `maxspan` is how long the whole sequence has to complete, measured from its first stage.
-   `key` joins the stages: only events with the same key advance the same sequence.

The signal reports when the final stage matches.

### Join Keys

The key must be one of these types:

Key

Example

A process identity

`event.execution.Target.ID`

A string

`event.execution.Target.Executable.Path`

A list of strings

`[event.execution.Hostname, event.execution.Target.Executable.Path]`

Workshop rejects any other key when you save the signal, because Santa cannot join on it and the signal would never report. That includes a bare PID such as `event.execution.Target.ID.PID`: PIDs are reused, so a process identity pairs the PID with a value that tells two processes with the same PID apart. It also includes a whole process record such as `event.execution.Target`: pass its `ID` instead.

### Stage Predicates

When two stages match the same event type, make their predicates mutually exclusive. If one event can satisfy both, it restarts the sequence on one stage and is refused by the other as the same event, so the signal may never report.

:::info Requirements

`sequence()` requires Santa **2026.8**. Workshop sets a minimum Santa version of 2026.8 on every signal that uses it, so hosts on anything older are not sent the signal.

:::

## See Also

-   [Events](https://northpole.security/docs/workshop/events): where signal reports appear
-   [Telemetry](https://northpole.security/docs/workshop/telemetry): how to enable telemetry collection
-   [macOS schema](https://northpole.security/docs/workshop/telemetry/schema/macos): the fields `event` exposes
-   [CEL Guide](https://northpole.security/docs/workshop/rules/cel-guide): CEL in execution rules

## Sitemap

- [Home](https://northpole.security/index.md)
- [Workshop](https://northpole.security/workshop.md)
- [Santa](https://northpole.security/santa.md)
- [Features](https://northpole.security/features.md)
- [Cookbook](https://northpole.security/cookbook.md)
- [Docs](https://northpole.security/docs.md)
- [Blog](https://northpole.security/blog.md)
- [Glossary](https://northpole.security/glossary.md)
- [About](https://northpole.security/about.md)
- [Contact](https://northpole.security/contact.md)
