Announcing Santa and Workshop 2026.8
Tags front and center, time based rules, file access process overrides, and much more
Where 2026.7 was about making new capabilities usable at fleet scale, 2026.8 is about tags, the thing you express scale with in the first place. Tags move into the main navigation, the tagged settings editor has been rebuilt so it is obvious which settings a tag actually owns, and a host will now tell you which tag each of its settings came from. Alongside that, Santa gains a new axis of enforcement: time based rules decide not just what may run but when, and can quit what they allowed once the window closes. File access rules gain per-process overrides, Signals learn to match ordered sequences of events, and the dashboard has been redesigned around what needs your attention.
Tags, Front and Center
In Workshop, a tag is how you say who a policy is for: every rule and every setting is scoped by tag, and the tag order decides which wins when a host carries several. Tags used to sit two levels down in Settings, split across two pages. In 2026.8 they get a top-level section in the main navigation, and a tag’s own page is where its settings live.
The editor was rebuilt around one idea: a tag either manages a setting or it does not. Each setting carries a Managed switch. Turn it on and the fields become editable and belong to that tag; leave it off and the section gets out of the way, so the page reads as the list of things this tag actually sets. A sticky rail shows a status dot per section: owned, untouched, or blocking a save.
Host details got the same treatment from the other direction. A host’s overview now carries an Effective settings table: Setting, Value, and Source. The Source cell names the tag that supplied each value, or Host Settings when the host sets it itself, or a dash for a default. The host’s tag order sits below it, so the reason a value won is on the same screen as the value, and the host’s settings tab is that same editor pointed at the host.
The tags list is now a real table, counting the rules, file access rules, managed settings, exceptions, groups and hosts each tag carries, and flagging any of your own tags that is not in the tag order and so applies to nothing. Tag names can contain spaces, and rename and delete are on the tag’s Actions menu.
Use Cases
- See what a tag owns. Read down the Managed switches instead of cross-referencing another page.
- Explain a host’s configuration. Answer “why is this host in Monitor mode” from its Effective settings table, then click through to the tag that set it.
- Tune one host without a new tag. Manage a setting on the host and see it attributed as
Host Settingsalongside what it inherits.
Time Based Rules
Santa has always answered the question of what may run. In 2026.8 it can answer when. A time based rule is an execution rule whose CEL expression calls policy_for_range(). The call returns one policy while a window is open and a different one while it is closed, so a single rule can allow an application during working hours and block it for the rest of the week. There is no separate schedule object to manage: the window lives in the rule, next to the identifier it applies to.
How It Works
policy_for_range() comes in four forms, one per window shape, with the policy arguments always last. The time based rules documentation covers all four, along with validation and caching.
// Working hours on each host's own clock, blocked outside them.
policy_for_range(weekdays(), "09:00", "17:00", ALLOWLIST, BLOCKLIST)
// One maintenance window for the whole fleet, read in a named time zone.
policy_for_range([0, 1, 2, 3, 4, 5, 6], "01:00", "05:00", "UTC", ALLOWLIST, BLOCKLIST)
// A dated exception: a migration week, an audit, a vendor's support window.
policy_for_range(timestamp("2026-09-14T00:00:00Z"), timestamp("2026-09-21T00:00:00Z"), ALLOWLIST, BLOCKLIST)
Three properties matter. The window is evaluated on the Mac at the moment of the execution, so a host that is offline or asleep still opens and closes its windows on time. The out-of-range slot does not have to block: both slots accept any CEL return value, so it can be AUDIT while you measure the rule, or require_touchid_with_cooldown_minutes(60) to keep an out-of-hours path open with a person at the keyboard. And the result is never cached. Any expression calling policy_for_range() is re-evaluated on every execution, which is what lets a window turn over, so prefer a narrow identifier over a broad one on a hot path.
Quitting What the Time Window Allowed
Left alone, a window governs new executions only: a process that started inside the window keeps running after it closes. Wrapping the in-range policy in kill_on_expiry() closes that gap. Santa warns the user shortly before the deadline, so there is time to save any work in progress.
policy_for_range(weekdays(), "09:00", "17:00", kill_on_expiry(ALLOWLIST), BLOCKLIST)
Use Cases
- Measure before you enforce. Put
AUDITin the out-of-range slot and read a week of events to see exactly who a blocking version would have stopped. - Put working hours on a sensitive tool. Allow an administrative or finance application during the working day and block it outside, without standing up a separate scheduling system.
- Grant timed access.
policy_for_range(duration("30m"), kill_on_expiry(require_touchid_only_with_cooldown_minutes(30)))asks for a fingerprint, and quits what it allowed when the half hour runs out. - Open a dated exception. Allow a migration tool for exactly one week with two timestamps, and let it close itself.
Per-Process Overrides for File Access Rules
A path-centric file access rule pairs a set of paths with a list of processes, and until now every option applied to that whole list. In 2026.8, each process can carry its own overrides instead: its own action, and its own version of the rule’s settings for read access, silent mode, block message and event detail. Anything left on inherit keeps the rule’s value. Workshop also keeps a per-tag list of the processes that belong in nearly every rule, seeded with the Apple Spotlight and XProtect components that read almost every file on disk, and composes it into your path-centric rules as they are sent out. The file access rules documentation covers how the list resolves when several tags set it, and how a rule opts out.
Use Cases
- Block one noisy process quietly. Deny a background agent that constantly touches a protected path, without a notification the user cannot act on.
- Stage a rollout inside one rule. Set the processes you are unsure about to Audit while the rest of the rule enforces, then promote them as the events come back clean.
- Give one process its own explanation. Point a single process at its own block message and help URL without splitting the rule.
- Stop re-adding the same processes. Keep the tooling that reads every file on disk in the per-tag list.
Improved Signals
Signals shipped in 2026.6 and evaluated one event at a time. 2026.7 added windows, so a signal could require a condition to happen a number of times within a period. 2026.8 adds the missing piece: order.
Cross-Event Sequences
A sequence is one CEL branch per stage, each guarded by its event type. sequence() takes the stage number, the number of stages, the maximum span and the join key, and returns true only on the final stage, so the signal fires once, when the chain completes.
(has(event.execution) && event.execution.Target.Executable.Path.startsWith('/tmp/') &&
sequence(1, 2, duration('60s'), event.execution.Target.ID)) ||
(has(event.network_activity) &&
sequence(2, 2, duration('60s'), event.network_activity.Process.ID))
That is a binary executing out of /tmp/, and within a minute that same process opening a network connection. The stages join on process identity, so the connection only counts if it came from the process that just ran. The key is an ordinary argument, so it can also be a path, a hash, or several fields combined, and the report carries every event that contributed to the match.
Full Process Trees on a Match
By default a match carries the matched process and its ancestry. Turn on Full Process Tree and it reports the whole tree around it, siblings and descendants included. It stays bounded: the walk is breadth first so the nearest relatives survive truncation, and anything past the budget falls back to the ancestry.
A Real Process Tree Viewer
The process tree is now a proper tree widget rather than a flat list, with expand and collapse, filtering by path, argument or PID, keyboard navigation, and a details panel carrying identity, parent, responsible process, signing details and arguments for the selected node. The matched process is badged, the responsible process gets its own color, and it opens on the matched chain plus its immediate context rather than the entire tree.
Use Cases
- Detect a chain, not a coincidence. Match a binary running out of
/tmp/and then connecting out within a minute. - Keep the context you need. Turn on Full Process Tree to see what a match did next, not only where it came from.
- Triage in one place. Filter a large tree to a path or a PID without leaving the signal.
More in Workshop 2026.8
A Redesigned Dashboard
The dashboard has been rebuilt, its sections reordered by how much of each is work you can act on. The queues waiting on you come first, as counts that link straight to the page that clears them: binary approvals, multi-party approvals, untriaged signal reports, and hosts on an agent older than the latest release. Below them sit coverage gaps and risk engine conflicts, the fleet’s enforcement standing, and recent activity, with one range toggle switching those between 7, 30 and 90 days. Fleet CPU and memory moved to a new Fleet Metrics report, which now takes a tag selector, so you can read those numbers for one tag rather than the whole fleet.
Package Rules
Package rules let you name software in a catalog and have Workshop keep its execution rules in sync as new versions ship. Four things improved in 2026.8:
- Bulk import. When creating a rule, the name field now takes a pasted list:
brew listoutput, a column of names, or a CSV. Workshop validates each name against the catalog and creates a rule per name per tag, keeping any that fail so you can retry. - Nix. Nix is now a package rule source, resolved to the exact binaries the build produced and the binary cache signed. Santa’s package inventory scan covers Nix too.
- Faster updates. Workshop subscribes to a change feed from the catalog, so a package syncs shortly after its identifiers change rather than on the next sweep. Creating a rule no longer blocks on the catalog: the first sync runs in the background.
- Canonical names. Homebrew keeps old formula names as aliases, so
pkg-configandpkgconfare the same formula. Workshop now records what a subscription resolves to, so an update published under the canonical name still reaches a rule subscribed under an alias. The name you entered stays the rule’s name.
You may also like
Announcing Santa and Workshop 2026.7
Santa and Workshop 2026.7 add rule packs for every rule type, richer Signals, package inventory scanning, redesigned Commands, and more
Announcing Santa & Workshop 2026.6
Santa and Workshop 2026.6 add network rules, privilege management, Signals, webhooks, a redesigned Ready for Lockdown report, and more
Announcing Santa 2026.5
Santa 2026.5 adds sandbox profile rules, on-demand binary upload, CEL audit policies, and CEL fallback coverage for platform binaries.