Operational flow of background task coordination

Detecting and registering background items

File monitoring

backgroundtaskmanagementd relies on the ServiceManagement daemon to catch new persistence items. The smd daemon’s launchd configuration includes an FSEvents watcher on relevant paths (Apple uses an XPC event named "com.apple.xpc.smd.WatchPaths" to tie FSEvents to smd). When an installer or app drops a new plist into a LaunchAgents/Daemons directory, or when an app calls an API to register a helper, smd is notified. For example, adding a file us.zoom.ZoomDaemon.plist under /Library/LaunchDaemons triggers smd to log “Got a fsevent. Doing re-register” and “Bootstrapping legacy plists”. It then invokes an XPC call on backgroundtaskmanagementd (the BTMService interface) like -launchdWillScanPath: for that directory. This prompts backgroundtaskmanagementd to scan the directory or item and register the launch item in its database. The daemon logs an entry such as registerLaunchItem: … type=legacy daemon, … url=/Library/LaunchDaemons/…ZoomDaemon.plist, config=…ZoomDaemon. At this point, the item is recorded in the BTM system with attributes indicating it’s present and enabled.

App API calls

In addition to file-system detection, apps on macOS 13+ can register background helpers via the ServiceManagement API (specifically the new SMAppService). For instance, a sandboxed app might call SMAppService.loginItem(identifier: ...) to enable a login item bundled within its .app. This API will communicate (likely through XPC) with backgroundtaskmanagementd and smd to create the necessary launchd entry without a physical .plist file in /Library/LaunchAgents. Instead, the configuration is stored in BTM’s internal database, and launchd is instructed to load the helper from the app bundle. The Apple Deployment guide notes that Ventura “simplifies the installation of login items… by updating helper executables from earlier versions” and that the framework uses SMAppService to control those in-app helpers. Thus, whether via file or API, all roads lead to backgroundtaskmanagementd: it hears about the new background task either from smd’s FSEvent or an XPC call, and it registers the item in a central repository.

Database and persistence storage

backgroundtaskmanagementd maintains a database of known background tasks under /var/db/com.apple.backgroundtaskmanagement. Specifically, it uses a file named BackgroundItems-v*.btm to store the list of items and their state. This is a versioned binary property list archive (using NSKeyedArchiver serialization) containing BTM’s data structures. Each major OS update can bump the version number (v8.btm was used in early Ventura; macOS updates may use v9 or higher, migrating the data). The daemon’s code (class BTMStore) formats the filename with a version number and handles upgrades. When changes occur (item added/removed or toggled), BTM updates this file atomically: logs show it writing to a temp path in ...TemporaryItems/BackgroundItems-vx.btm then moving it to the /var/db/com.apple.backgroundtaskmanagement/BackgroundItems-vx.btm location. This ensures crash-safe updates.

Inside this database, each background task entry includes details such as: a UUID, the item’s launchd label (identifier), the program path (or bundle ID for in-app helpers), and a set of disposition flags indicating its status. Common flags observed are: enabled, allowed, visible, notified. These have the following semantics:

  • Enabled – the item is enabled in launchd (i.e. not in a disabled state). If false, launchd should not auto-start it. Toggling an item off via UI would disable it here.
  • Allowed – the user (or MDM) has given permission for this item to run. By default, new items are “allowed” until the user actively disallows them. If an item is toggled off, it becomes not allowed (and effectively disabled). In practice Apple uses “allowed” especially in context of MDM-managed items: managed items are auto-allowed by policy.
  • Visible – whether the item should be shown in the UI list. Apple can mark certain persistent system helpers as hidden. Generally, third-party items are visible unless intentionally hidden.
  • Notified – tracks if a user notification about this item’s addition has been shown (to avoid spamming the user repeatedly). Once an item has triggered the “Background Items Added” alert (or the daily consolidated alert for managed installs), it is marked notified=true, so subsequent boots or re-additions don’t alert again within a time window.

For example, a log after upgrading to Ventura showed backgroundtaskmanagementd registering a legacy daemon with “disposition=[enabled, allowed, visible, notified]”, meaning the item was enabled to run, allowed by policy, visible to the user, and (already) notified to the user. If the user were to disable that item in settings, we’d expect “allowed” (and likely “enabled”) to flip to false in the database. The database approach is a central source of truth; System Settings actually reads from this BTM store to display the list of login/background items, instead of combing through files in multiple directories. Apple even provides a command-line utility sfltool dumpbtm to dump the contents of the BTM database by deserializing the .btm file. This unified store streamlines persistence management but is proprietary (serialized objects with some obfuscation, as Wardle noted by humorously pointing out spelling mistakes inside the archive data).

Launch coordination and enforcement

After an item is registered in BTM, the next step is deciding whether to launch it. The default behavior on add is to allow execution (Apple did not want to break legitimate apps, so new items are not blocked by default – they’re just made visible and user-controllable). Thus, once backgroundtaskmanagementd records the item, smd proceeds to load it via launchd (unless policy says otherwise). In logs, this appears as: “Bootstrap by smd for /Library/LaunchDaemons/com.tailscale.tailscaled.plist succeeded”, immediately after BTM registration. That indicates launchd started the daemon (here Tailscale’s) as requested by smd. In the case of items added during an OS upgrade or already present, BTM registers them at boot and typically allows them to run (marking them “allowed”). They are considered “grandfathered”; Ventura didn’t require re-approval for items that existed before the upgrade. The user would still see them listed in Login Items, but no initial block.

Preventing launch

If an item has been disabled by the user, backgroundtaskmanagementd will intervene to stop it from running. Technically, this is achieved by interacting with launchd’s disable mechanism. For user agents, it might write to the user’s disabled.plist (as mentioned) or call launchctl disable via smd on that service label. For system daemons, it can similarly mark them disabled (though on a personal Mac, users typically can’t disable system LaunchDaemons via UI, only their own login items). If an item is disabled, its enabled/allowed flags in the DB are false, and smd will not bootstrap it on login/boot. Should a process somehow attempt to launch a disabled item (e.g. via launchctl kickstart), BTM’s policy would likely cause it to immediately exit or not be started at all. This enforcement ensures that a user’s choice to turn off a background app is respected system-wide. (Notably, if MDM marked an item as managed and non-disableable, the UI toggle is greyed out and BTM will refuse any user attempt to disable it, essentially forcing allowed=true until the profile is removed.)

Runtime coordination

backgroundtaskmanagementd itself doesn’t actively throttle or supervise the process after launch (that’s left to macOS’s usual process management). However, it does keep track of the running status via launchd events indirectly. If a background task process exits or is removed, the daemon updates the state in its database on next reboot or when it notices the file gone. There was an initial bug where the removal event (ES_EVENT_TYPE_NOTIFY_BTM_LAUNCH_ITEM_REMOVE) wasn’t properly delivered to security clients, but BTM still would update its UI (e.g., if you manually delete a launchd plist, it should eventually disappear from the Login Items list after a refresh or reboot). Apple has since fixed the ES removal notification issues in updates.

Integration with app life cycles

For apps that use Login Items (SMAppService), backgroundtaskmanagementd ties the helper’s lifecycle to the main app. The helper is typically embedded in the app bundle and runs in the user context at login. If the user deletes the main app, macOS can detect orphaned login items and remove them. In fact, backgroundtaskmanagementd consults an Apple-maintained attributions.plist of known helper executables and their parent apps. This attribution helps label entries (e.g., showing “Google Chrome” as the source of a keystone updater) and likely aids in cleanup; if an app is uninstalled, BTM can identify associated helpers via code signature (Team ID or bundle ID prefix) and purge them. This is speculative but aligns with Apple’s design that the new system should reduce “ghost” login items. Indeed, admin guides note that the list of login items is now managed in the BTM database and issues of “stale” entries can be resolved by resetting that data (sfltool resetbtm).

Interaction with Launch Services and XPC

All coordination between these components happens through XPC interprocess communication and LaunchServices:

  • The backgroundtaskmanagementd <-> smd communication is via XPC. Apple’s logs show BTMManager.registerLaunchItemWithBundleURL being invoked in smd (likely an XPC call into BTM). smd essentially says “launchd is about to scan/load this item, please register it” to BTM, and BTM replies perhaps with a “go/no-go”. The methods launchdWillScanPath: and registerLaunchItem: are part of BTM’s XPC service interface. This tight coupling ensures nothing sneaks past launchd without BTM noticing – even if a malicious process tried to manually load a LaunchAgent, smd hooks would alert BTM.
  • The System Settings app uses a higher-level interface (likely via the private BackgroundTaskManagement.framework or sfltool) to query and toggle items. Changes the user makes in the GUI (toggling off an item) result in calls to an API which triggers backgroundtaskmanagementd to update the DB and call smd/launchd to unload or disable that item immediately.
  • The BackgroundTaskManagementAgent ⇔ backgroundtaskmanagementd likely communicate via an XPC or distributed notification. When BTMd decides a notification should be shown, it sends the relevant info (item name, type, etc.) to the agent, which then posts a user notification (the agent log “Posting managed item notification request…” confirms this flow). The agent might use the User Notifications framework to display the banner, and mark the item’s “notified” flag once done.
  • There is also interaction with Launch Services for in-app login items. When an app calls SMAppService, the framework might involve launchd to register a new service label on the fly (with smd brokering the request). Indeed, Apple’s smd (ServiceManagement) has long provided SMLoginItemSetEnabled() which in Ventura is superseded by SMAppService – underneath, these likely connect to smd which calls into backgroundtaskmanagementd to create or remove the login item entry. So, e.g., enabling a login item via code results in BTM adding an entry (visible in Login Items UI) and using launchd to start it. Disabling via code or app removal triggers BTM to remove the entry (and maybe stop the process).

In summary, backgroundtaskmanagementd orchestrates multiple moving parts through XPC: it listens for file events and API calls via smd, it communicates with a user-space agent for notifications, and it updates system state via launchd/ServiceManagement. All these ensure that the user-space behavior (what the user sees and controls) is kept consistent with the kernel-level registration (what launchd will actually do).