Most macOS update prompts are honest about what they’re not telling you. “A new update is available” is technically true. It just leaves out whether that update patches two actively exploited zero-days or just fixes a calculator rounding error. Users make different decisions with different information, and IT teams that don’t surface that context end up enforcing updates at arbitrary speeds regardless of actual risk.
Sentinel is my attempt to fix that. It’s a two-component system — one that runs on admin infrastructure and figures out how urgently a given update should be deployed, and one that runs on each managed Mac and tells users why they’re being asked to update. Those two pieces talk to each other through a Jamf Configuration Profile, and together they make the urgency visible at every layer.
Standing on Other People’s Work
Before going further, I want to be clear about what I didn’t build.
The Notifier component is an extension of Dan Snelson’s DDM OS Reminder. If you manage Macs with Jamf and haven’t read Dan’s work, fix that immediately. His script handles the actual DDM integration, deadline enforcement, and dialog lifecycle that underpins everything. I added the CVE context layer on top; the scaffolding is his.
The CVE data comes from two sources. The SOFA feed — maintained by the macOS Admins community — provides a structured, frequently updated catalog of macOS releases, their associated CVEs, and which ones are being actively exploited in the wild. It’s updated within hours of Apple releases and has become the standard reference for the Mac admin community. The National Vulnerability Database (NIST) provides the authoritative CVSS severity scores that turn a list of CVE IDs into something actionable: CRITICAL, HIGH, MEDIUM, or LOW.
Dialog UI is built on swiftDialog by Bart Reardon — the de facto standard for native macOS dialog workflows in Jamf environments.
The Orchestrator’s core idea — checking CVE severity and adjusting the rollout schedule accordingly — came directly from this Tech It Out post on automating Apple OS DDM software updates in Jamf Pro using Azure Runbooks. The post describes using Azure Runbooks to create DDM update plans automatically and varying deployment speed based on the severity of CVEs in the release. The Orchestrator is a different implementation of that same idea — a local bash script on a LaunchDaemon schedule rather than Azure infrastructure — but the CVE-to-urgency logic is the insight I took from there and built on.
The scaffolding for both scripts was generated with Shikomi.
The Problem with Uniform Urgency
Before Sentinel, we used SUPER to manage macOS updates — a LaunchDaemon-based agent that handles dialogs, deferrals, scheduling, and enforcement across the fleet. The deadline was the same whether the update patched one CVE or twenty, whether those CVEs were theoretical or being actively exploited. Users got the same generic dialog regardless of risk level.
This creates two failure modes. In low-severity updates, users feel like IT is crying wolf — they defer repeatedly because they’ve learned that the urgency isn’t real. In critical updates, the same messaging means users don’t understand why this update matters more than the last seventeen. Neither outcome is good.
The fix is obvious in retrospect: let the security data drive both the deployment timeline and the user messaging, and make sure those two things are in sync.
The Two Components
Sentinel Update Orchestrator
The Orchestrator runs on admin infrastructure — typically on a LaunchDaemon schedule, once or twice a day. It does the backend work:
- Queries the SOFA feed for the latest macOS release and its associated CVEs
- Queries the NVD API to get CVSS severity scores for each CVE
- Determines the highest severity across all CVEs in the update
- Calculates optimal per-group rollout timelines based on that severity
- Creates per-device update plans in Jamf via the Managed Software Updates API
- Writes severity metadata to a Jamf Configuration Profile for the Notifier to consume
The severity-to-deferral mapping looks like this:
| Severity | Alpha | Beta | Gamma | Gamma2 | Release |
|---|---|---|---|---|---|
| Actively Exploited | 0d | 0d | 1d | 2d | 3d |
| CRITICAL | 0d | 1d | 3d | 5d | 7d |
| HIGH | 0d | 2d | 4d | 7d | 10d |
| MEDIUM/LOW | 0d | 3d | 7d | 11d | 15d |
When something has actively exploited CVEs, the entire fleet gets the update within three days. For routine patches, the Release group doesn’t become eligible until two weeks after Alpha. The urgency is baked into the timeline automatically.
The NVD API has rate limits — 5 requests per 30 seconds without an API key, 50 with one. When an update has 15+ CVEs, querying each one sequentially would be slow and error-prone. The Orchestrator caches results in-memory: the first run of the day queries the NVD for each unique CVE ID, subsequent plan creation calls hit the cache. In practice this means 99%+ of NVD API calls are eliminated after the first execution.
One problem I ran into: when Apple releases a point update (say 15.4.1 right after 15.4), there’s a window where SOFA hasn’t yet indexed the new version. If the Orchestrator targets LATEST_ANY, Jamf will target whichever version it considers latest, which may not be what SOFA reported. Since v2.10.3, I pin to SPECIFIC_VERSION from the SOFA feed rather than using version-relative targeting, which eliminates deadline drift during that window.
Sentinel Update Notifier
The Notifier runs on each managed Mac — typically at login and on a LaunchDaemon trigger. It inherits the DDM OS Reminder logic from Dan Snelson’s work and adds a CVE context layer.
Severity comes from two tiers:
Managed Preferences (preferred) — reads
com.prizepicks.sentinel.severitypushed by the Orchestrator. This includesUpdateSeverity,TargetOSVersion,CVECount, andActiveCVECount, all derived from NVD data. When the Orchestrator has run, the Notifier uses the same severity judgment that drove the deployment timeline.SOFA feed (fallback) — if the managed preference hasn’t arrived yet (new device, MDM latency), the Notifier queries SOFA directly and calculates severity from CVE counts. Less precise than CVSS scores, but always available.
Urgency escalation over time:
- 60+ days out: no dialog
- 60–7 days: dialog with “Remind Me Later”
- 7–3 days: “Remind Me Later” removed
- 3–0 days: blurscreen — users can’t dismiss without acknowledging
- Deadline: Apple’s DDM enforces the update automatically
What users actually see:
For a CRITICAL update with active exploits:
Title: macOS Update Required — 🚨 CRITICAL SECURITY UPDATE
This update fixes 2 actively exploited vulnerabilities currently
used in real-world attacks. Your device must be updated immediately.
CVEs Fixed: 12 | 🚨 Actively Exploited: 2 | Severity: CRITICAL
Time Remaining: 3 days, 4 hours
For a routine update:
Title: macOS Update Required
This update includes 8 security fixes.
CVEs Fixed: 8 | Severity: MEDIUM
Time Remaining: 12 days
The Notifier also checks if the user is in a Keynote presentation, Zoom call, or Teams meeting before displaying. If they are, it waits up to 75 minutes before trying again.
How They Talk to Each Other
The integration point is a Jamf Configuration Profile. When the Orchestrator determines severity, it PUTs updated preference data to a managed profile via the Jamf Classic API. Jamf deploys it to devices in ~15 minutes. The Notifier reads it with defaults read /Library/Managed Preferences/com.prizepicks.sentinel.severity.
Before this integration existed, the Orchestrator used NVD for severity (accurate CVSS scores) while the Notifier used SOFA CVE counts (less precise). A CRITICAL update could trigger emergency deployment timelines while users saw a generic “security update” dialog with no indication of urgency. Now they’re driven by the same source of truth.
Orchestrator (admin Mac, daily)
├── Queries SOFA feed
├── Queries NVD API (with caching)
├── Calculates severity
├── Creates per-device Jamf update plans
└── PUTs severity metadata → Configuration Profile
│
MDM push (~15 min)
│
▼
Notifier (each managed Mac, at login)
├── Reads managed prefs (Orchestrator's severity)
├── Falls back to SOFA feed if prefs unavailable
├── Calculates urgency escalation based on deadline
└── Displays appropriate dialog
Credential Management
The Orchestrator needs Jamf API credentials, an optional NVD API key, and access to Jamf configuration. Rather than a config file, credentials are loaded from a priority chain:
- 1Password CLI (
op read) — for interactive use and team sharing - macOS System Keychain — for LaunchDaemon deployments where no user session exists
~/.jamf_secretsfile — simple fallback- Environment variables — last resort
The System Keychain support was important for production deployment. LaunchDaemons run without a user session, which means no 1Password prompt and no interactive auth flow. Storing Jamf credentials in the System Keychain (accessible to root) made automated scheduling reliable without compromising security.
What I’d Do Differently
The five-group phased deployment model (Alpha → Beta → Gamma → Gamma2 → Release) is the right structure, but maintaining five smart groups in Jamf requires ongoing hygiene. People change teams, devices get replaced, alpha testers leave the company. The groups need periodic auditing.
I also underestimated how much value the SOFA fallback provides. The managed preference takes ~15 minutes to push after the Orchestrator runs, which means newly enrolled devices often hit the Notifier before the profile arrives. The SOFA fallback means they still get meaningful context — just using count-based rather than CVSS-based severity. In practice the difference is usually one severity tier, which is acceptable.
The actively exploited detection is the feature that makes the whole system worth running. The SOFA feed flags CVEs that are being exploited in the wild, and those trigger 0-day deployment to Alpha and immediate urgency messaging to users. In the months since deploying this, we’ve had two updates where that path was activated — and both times, users upgraded substantially faster than historical baselines.
Links
- SUPER — Software Update Policy Enforcer Reloaded, the enforcement tool used before Sentinel
- Dan Snelson’s DDM OS Reminder — the foundation the Notifier builds on
- SOFA Feed — macOS release and CVE data
- swiftDialog — dialog framework
- Automating Apple OS DDM Software Updates in Jamf Pro using Azure Runbooks — Tech It Out post on backend DDM orchestration with Azure Runbooks