Bolt on managed Macs: MDM deployment and privacy permissions

Audience: the Mac admin or security reviewer deploying Bolt to a fleet where end users do not have administrator rights.

The problem this solves

Several of Bolt’s features depend on macOS privacy permissions (TCC): Accessibility, Automation, Calendars/Reminders/Contacts, access to the user’s Desktop/Documents/Downloads, and Screen Recording.

Normally the user grants these by clicking through a prompt and then unlocking System Settings > Privacy & Security. Unlocking that pane requires an administrator password. On a corp-managed Mac the user does not have one, so the user cannot grant the permission at all - not “it is inconvenient”, it is not possible.

The supported mechanism is a PPPC (Privacy Preferences Policy Control) configuration profile, pushed by your MDM, that pre-approves the permissions on the user’s behalf. The template is a direct download:

  • bolt-pppc.mobileconfig - the profile template
  • this page - what each entry is for, how to deploy it, and its limits

Two hard requirements before anything works

  1. The profile must be delivered by an MDM. macOS accepts a hand-installed PPPC profile but silently ignores its privacy grants. Only a profile pushed over a user-approved MDM enrollment (which is every modern DEP/ADE or manual UAMDM enrollment) actually writes the TCC grants. Do not test by double-clicking the file and conclude it does not work.
  2. The profile must match the code signature of the build you deploy. The CodeRequirement strings in the template are the designated requirement of the shipped release build, signed with Developer ID Application: Sparcle Inc. (4VSW7NBZ2S). Internal or development builds are signed with a different certificate and will not match - by design, so a scratch build cannot inherit the fleet’s grants.

What Bolt is signed with

Property Value
Bundle identifier app.sparcle.bolt.enterprise
Team ID 4VSW7NBZ2S
Signing identity Developer ID Application: Sparcle Inc. (4VSW7NBZ2S)
Hardened runtime Yes
App sandbox No
Notarized Yes. The ticket is stapled to the app.

You can re-derive the designated requirement from any build you have on disk:

codesign -d -r- "/Applications/Bolt.app"

The template’s CodeRequirement is:

identifier "app.sparcle.bolt.enterprise" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = "4VSW7NBZ2S"

This pins the app to Sparcle’s Developer ID team. Another vendor’s app, or a re-signed copy, does not satisfy it and gets nothing from this profile.

What the profile requests, and why

Each permission below is requested because a specific Bolt feature needs it.

Requested

Permission (TCC service) What it is for
Accessibility (kTCCServiceAccessibility) The App Menus feature: Bolt reads the frontmost application’s menu bar so the user can search and trigger any menu command from the Bolt launcher, and then activates the chosen item. Also backs the double-tap modifier shortcut that opens Bolt.
Input Monitoring (kTCCServiceListenEvent) The global modifier-key watcher behind the double-tap activation shortcut. Bolt watches for modifier-key state changes only; it does not tap or record keystrokes.
Files and Folders - Desktop, Documents, Downloads (kTCCServiceSystemPolicyDesktopFolder, ...DocumentsFolder, ...DownloadsFolder) Bolt’s local file index, so the user’s own documents are searchable from the launcher. The index is local to the machine.
Automation (kTCCServiceAppleEvents), scoped per target app Driving specific Apple apps on the user’s behalf. This surface got NARROWER in the EventKit/Contacts.framework migration (below): Calendar/Reminders/Contacts reads and most writes moved off it. What is left: System Events, Calendar (only for inviting attendees and RSVPing: the two things EventKit’s public API cannot do), Mail (no sanctioned API exists for a user’s own Mail.app data), Messages, Finder. See the per-app table below.
Calendars (kTCCServiceCalendar), Reminders (kTCCServiceReminders), Contacts (kTCCServiceAddressBook): new, not per-target-app Calendar/Reminders/Contacts reads and writes, via Apple’s own frameworks instead of driving those apps with Apple Events. Read the “EventKit/Contacts.framework migration” section below before deploying an updated profile to an existing fleet: a fleet running an older profile will see brand-new prompts.
Screen Recording (kTCCServiceScreenCapture) Screenshot capture and reading text off the screen (OCR). Cannot be silently pre-granted - see the limits section. The profile instead lets a standard user approve it without an admin.
Full Disk Access (kTCCServiceSystemPolicyAllFiles) - optional, separate payload Broader Spotlight coverage, and the ~/Pictures / ~/Music / ~/Movies folders that have no PPPC key of their own. Bolt works without it. Ship without this payload first.

Automation targets, individually:

Target app Bundle ID What Bolt does with it
System Events com.apple.systemevents Read which app is frontmost and its window bounds; hide/minimise/quit an app from the launcher
Calendar com.apple.iCal Invite attendees on an event Bolt just created, and RSVP to an event: the only two Calendar operations still on Apple Events. Everything else (reading meetings/calendars, creating an event) goes through EventKit; see the Calendars/Reminders/Contacts row above.
Mail com.apple.mail List recent message headers; open one body on request
Messages com.apple.MobileSMS Send a message the user composed in Bolt
Finder com.apple.finder Reveal a file, eject a volume, empty the trash - the file actions on launcher results

Deleting any one of these <dict> entries disables exactly that integration and nothing else. Bolt handles a denied Automation target by hiding the feature, not by failing.

EventKit/Contacts.framework migration: read this before deploying to an existing fleet

Calendar/Reminders/Contacts reads and writes used to go through Apple Events (kTCCServiceAppleEvents, driving Calendar.app/Reminders.app/Contacts.app directly) like everything else in the Automation payload. They now go through Apple’s own EventKit and Contacts.framework APIs instead, which is the sanctioned, narrower way to do it.

This changes what TCC service gates each feature, and that matters for a fleet that already deployed the OLD profile (without payload 3, kTCCServiceCalendar / kTCCServiceReminders / kTCCServiceAddressBook):

  • A user who already granted Automation for Calendar/Reminders/Contacts will be prompted again: once per service, the first time they use the matching feature after upgrading Bolt. Automation and Calendars/Reminders/Contacts are independent TCC grants; an old Automation “yes” does not carry over.
  • On a managed Mac with no updated profile, that prompt is a dead end: a standard user cannot grant Calendars/Reminders/Contacts themselves if your MDM restricts it, and they have no path to answer a prompt your policy did not anticipate.
  • The fix is to push the updated profile (with payload 3) before rolling out the Bolt version that includes this migration, the same way you would for any new permission this app started needing. There is no way to avoid the one-time re-prompt for users who already granted Automation: only to make sure they CAN answer it.
  • A denied Calendars/Reminders/Contacts grant degrades the same way a denied Automation grant always has: the feature returns an honest error rather than a silent empty result, and the rest of Bolt is unaffected.

Deliberately not requested

An over-broad profile is a security smell, so these were checked and left out:

Not requested Why
Microphone (kTCCServiceMicrophone) A profile cannot pre-grant Microphone; it can only deny it. Bolt uses the microphone only while the user holds the mic button to talk, and macOS asks the user the first time. Speech is transcribed on the device.
Camera (kTCCServiceCamera) Bolt does not use the camera.
Speech Recognition Bolt does not use Apple’s speech recognition service. Transcription happens on the device.
Photos / Media Library Bolt explicitly skips .photoslibrary bundles while indexing precisely to avoid this prompt.
kTCCServicePostEvent Bolt does not synthesise input events. There is no CGEventPost / CGEventCreateKeyboardEvent call site; menu items are activated through the Accessibility API’s press action instead.
Bluetooth, Location, HomeKit No CoreBluetooth, CoreLocation or HomeKit use. (Bolt can toggle Bluetooth power via the blueutil helper, which is not a TCC-protected operation.)
kTCCServiceSystemPolicySysAdminFiles, removable/network volumes Not needed. Bolt indexes the user’s own home folders only.

(Contacts/Calendars/Reminders as direct TCC services used to be on this list: Bolt used to reach that data entirely through Apple Events. They are now REQUESTED, see the table above and the migration note.)

What PPPC cannot pre-grant, and what to do instead

This is the part most templates get wrong. Be aware of it before you promise users a zero-touch experience.

Permission Can MDM pre-grant it silently? Reality
Accessibility Yes Fully pre-granted. The App Menus feature works on first launch with no prompt.
Automation (Apple Events) Yes Fully pre-granted, per target app.
Desktop / Documents / Downloads Yes Fully pre-granted.
Input Monitoring Yes Fully pre-granted.
Full Disk Access Yes Fully pre-granted (optional payload).
Screen Recording No Apple does not honour Authorization=Allow for kTCCServiceScreenCapture. A profile can only deny it, or - what the template does - set AllowStandardUserToSetSystemService, which lets a standard, non-admin user approve it themselves without an administrator unlocking System Settings. That still requires one click from the user, but it removes the admin blocker. This value requires macOS 11 or later.
Microphone / Camera No Deny-only via PPPC, so the profile does not carry them. macOS asks the user for the microphone the first time the mic button is used. Bolt does not use the camera.

If your MDM rejects AllowStandardUserToSetSystemService, delete that block from the profile. The consequence is that Screen Recording goes back to being admin-gated; everything else in the profile is unaffected.

Because Apple has changed the behaviour of this service across releases, verify the Screen Recording behaviour on one machine running your fleet’s macOS version before a wide rollout. Do not assume it from this document.

The network filter is not in the current release

This page is about privacy (TCC) grants. A macOS network egress filter would be a system extension, governed by a different kind of profile. The current release does not ship a system extension, so there is no system-extension profile to deploy and nothing to pre-approve. You can confirm that on any Mac running Bolt:

systemextensionsctl list | grep 4VSW7NBZ2S     # expect no output

If a later release adds one, this page will change with it.

Deploying it

1. Customise the template

Everything you must change is marked in the file’s header comment.

cp bolt-pppc.mobileconfig bolt-pppc-acme.mobileconfig
uuidgen   # run five times, one per PayloadUUID

Replace, in bolt-pppc-acme.mobileconfig:

  • ORGANIZATION-PLACEHOLDER - everywhere it appears, in PayloadOrganization and in the five PayloadIdentifier values (use your own reverse-DNS prefix, e.g. com.acme.bolt.pppc).
  • All five PayloadUUID values (00000000-0000-0000-0000-0000000000NN): four payloads plus the profile itself. Every UUID must be unique to your organization. Reusing the placeholders across two organizations, or between two profiles in one MDM, causes profiles to overwrite each other.
  • Optionally PayloadDisplayName / PayloadDescription for what users see in System Settings > Profiles.

Then decide what to delete:

  • Payload 4 (Full Disk Access) - delete the whole <dict> unless you have decided you want it. Recommended: delete it initially.
  • Any Automation target app you do not want Bolt to reach - delete that one <dict> from the kTCCServiceAppleEvents array (payload 2).
  • Payload 3 (Calendars/Reminders/Contacts via EventKit/Contacts.framework) has no per-target-app entries to prune: it is one <dict> per SERVICE. Delete a whole service block only if you want that feature off entirely (e.g. no Contacts lookups at all).

Validate the result before uploading:

plutil -lint bolt-pppc-acme.mobileconfig

2. Upload to your MDM

The profile is a standard signed-or-unsigned .mobileconfig with com.apple.TCC.configuration-profile-policy payloads, scoped to the system (device) level, not the user level. Every major MDM can deliver it, though each calls it something different:

  • Jamf Pro - either upload the file under Computers > Configuration Profiles > Upload, or rebuild the same entries in the built-in Privacy Preferences Policy Control payload UI. Scope to a computer group, level “Computer Level”.
  • Microsoft Intune - Devices > macOS > Configuration profiles > Templates > Custom, upload the .mobileconfig, deployment channel “Device channel”.
  • Kandji - add a Custom Profile library item and upload the file, scoped to a Blueprint.
  • Mosyle / Addigy / SimpleMDM / others - all have an equivalent “custom profile” or “PPPC” upload.

We have not verified the exact menu path in each vendor’s current UI, so treat the above as a pointer, not a click-by-click script. What matters is: custom/uploaded profile, device (system) scope, delivered by MDM.

Order does not matter - the profile can land before or after the app is installed. TCC consults the profile at the moment permission is checked.

3. Deploy the app

Install Bolt.app into /Applications with your normal software delivery mechanism (an MDM-pushed package, Munki, Jamf policy). Apps delivered that way do not carry the quarantine flag, so Gatekeeper does not challenge them on first launch.

The browser extension needs a native-messaging host, and managed browsers can block it

The Bolt browser extension never receives a credential from a web page. It asks the installed app for the per-install key over Chrome Native Messaging (chrome.runtime.connectNative), because native messaging is the one channel a web page cannot sit in: the browser launches a binary named in a host manifest that points into the installed bundle, and reaching it requires local write access to the browser’s NativeMessagingHosts/ directory, which a page categorically does not have.

Bolt writes that manifest itself at startup, for every Chromium-family browser already present on the machine:

~/Library/Application Support/<browser>/NativeMessagingHosts/app.sparcle.bolt.pages.json

On managed Macs that is not always enough, and it fails silently. Two Chromium enterprise policies make the user-level manifest useless without producing any error: the browser simply never launches the host, and the extension reports no per-install key from the Bolt native-messaging host, which looks identical to “Bolt is not installed”:

Policy Effect What the admin must do
NativeMessagingUserLevelHosts = false User-level manifests are ignored entirely. Only the system-level path is read. Deploy the manifest to /Library/Google/Chrome/NativeMessagingHosts/ (and the equivalent per browser) from your installer or an MDM file payload. It needs admin rights, so the app cannot do it.
NativeMessagingBlocklist containing * Every host is blocked unless explicitly allowed. Add app.sparcle.bolt.pages to NativeMessagingAllowlist.
ExtensionInstallBlocklist The extension itself never installs. Allow the extension ID, or force-install it.

Ship this rather than typing it. bolt-nativemessaging.mobileconfig carries the NativeMessagingAllowlist entry for Chrome, Chrome Beta, Chrome Canary, Brave, Edge, Chromium and Vivaldi in one profile. Push it with the PPPC profile.

It matters more than it looks. Both policies above fail silently: the extension simply never obtains its per-install key, sends nothing, and tells the user Bolt is not installed. There is no error an admin would see and nothing in Bolt’s own UI that says the browser refused. On a fleet where either policy is set, every user hits that and it looks like Bolt is broken.

The profile grants no permission to Bolt and no access to browsing data. It names one host, app.sparcle.bolt.pages, as allowed.

Bolt reports which of these it can detect. If a managed-preferences file exists for a browser it has just written a manifest for, startup logs:

[Bolt] native-messaging host manifest: N written, M already current, K failed
  (managed policy present for com.google.Chrome - a user-level manifest may be ignored)

It deliberately does not parse the policy to decide whether it is blocked. Saying “a policy exists here and this is the key that would block us” is something we can prove; asserting the effective value would be a guess presented as a fact.

Verifying it took effect

# 1. Did Bolt write a manifest for this browser?
cat ~/Library/Application\ Support/Google/Chrome/NativeMessagingHosts/app.sparcle.bolt.pages.json

# 2. Does `path` point at a binary that exists and is the installed bundle?
/usr/libexec/PlistBuddy -c 'Print :CFBundleIdentifier' /Applications/Bolt.app/Contents/Info.plist

# 3. Is a policy about to override it?
defaults read com.google.Chrome NativeMessagingUserLevelHosts   # false = user-level ignored
defaults read com.google.Chrome NativeMessagingBlocklist        # "*" = allowlist required

# 4. What the extension itself says: DevTools console on the extension's service worker.
#    "no per-install key from the Bolt native-messaging host" means step 1, 2 or 3 failed.

Notarization

The shipped build is signed with a Developer ID certificate and a hardened runtime, and it is notarized, with the ticket stapled to the app.

  • PPPC is unaffected either way. Notarization plays no part in whether a PPPC grant applies; only the code signature matters.
  • Gatekeeper on first launch: a copy a user downloads is quarantined, and Gatekeeper checks it and lets it open because it is notarized. An app deployed by MDM or a managed installer is not quarantined at all.

Check it yourself on any copy:

spctl --assess --type execute -v /Applications/Bolt.app
# expect: accepted, source=Notarized Developer ID

What the user experiences

With the profile:

  • App Menus works on first launch, no prompt, no admin password.
  • Calendar, Reminders and Contacts work with no Calendars/Reminders/Contacts prompts (EventKit/Contacts.framework, payload 3); Mail, and inviting attendees / RSVPing on an event, work with no Automation prompts (payload 2); Messages and Finder integrations likewise need no Automation prompt.
  • Local file search covers Desktop, Documents and Downloads with no folder prompts.
  • Screen Recording still needs one user click the first time a screenshot or OCR action is used - but the user can complete it themselves, without an admin.

Without the profile, on a machine where the user has no admin rights:

  • App Menus does not work at all and cannot be enabled by the user. This is the single biggest degradation.
  • Calendars/Reminders/Contacts prompts appear per service (not per target app); Automation prompts appear per target app for Mail/attendee-invite/RSVP/Messages/Finder. The user can approve either kind themselves (neither requires admin), so everything still works after clicking through: just with an extra prompt per feature on first use.
  • Folder prompts appear for Desktop/Documents/Downloads; again user-approvable.
  • Screen Recording cannot be enabled without an admin.
  • Everything else works: the launcher, search over already-indexed content, clipboard history, the AI features, and all local utilities are unaffected. Bolt does not fail to start or lose data without these permissions.

Verifying the grant took effect

On a test machine, after the profile has been delivered:

# 1. Is the profile actually installed at system scope?
sudo profiles show -type configuration | grep -A2 -i "bolt"

# 2. Did the grants land in the system TCC database?
#    (requires Full Disk Access for Terminal, or run from your MDM's script runner)
sudo sqlite3 "/Library/Application Support/com.apple.TCC/TCC.db" \
  "SELECT service, client, auth_value FROM access WHERE client='app.sparcle.bolt.enterprise';"

auth_value of 2 means allowed. An MDM-delivered grant appears in the system TCC.db (/Library/...), not the user one (~/Library/...).

Then confirm behaviourally, which is the check that actually matters:

  • Launch Bolt, put another app in front, open the Bolt launcher and search for one of that app’s menu commands. If App Menus lists and triggers it, Accessibility is live.
  • Ask Bolt for your next meeting. If it answers without a Calendars permission prompt, the Calendars entry (payload 3) is live.
  • System Settings > Privacy & Security > Accessibility should show Bolt enabled with the toggle greyed out and a “managed by your organization” indication - a grey, non-editable toggle is the visual signature of a working PPPC grant.

If the toggle is present but editable and off, the profile did not match: the usual cause is a CodeRequirement mismatch (you deployed a differently-signed build), or the profile was installed by hand rather than by MDM.

Implementation note: the bolt-ax helper process

Worth knowing, because it is the kind of detail that makes a profile appear not to work.

Bolt’s Accessibility work is not done in the main binary. It runs in a small helper, bolt-ax, launched as a child process. Bolt uses the signed copy inside the app bundle (Contents/MacOS/bolt-ax) when it is there, and otherwise unpacks its own copy. The helper is where the Accessibility calls are made; the parent only asks whether it is trusted.

The profile does not need a separate entry for the helper. macOS attributes a TCC check for a child process to its responsible process, which is Bolt.app. The grant made to app.sparcle.bolt.enterprise is therefore the grant the helper runs under.

Two consequences for you:

  • If Accessibility appears granted but App Menus still does not work, the failure is in helper launch or responsibility attribution, not in your profile. Collect Bolt’s logs before re-editing the profile.
  • The same pattern applies to the OCR helper, bolt-ocr, which runs under the parent’s Screen Recording grant.

Support

Questions about this profile, or a permission you would rather not grant: [email protected]. Bolt is built to degrade feature-by-feature rather than fail, so tell us your constraint and we will tell you exactly what you lose.