Normal view

There are new articles available, click to refresh the page.
Before yesterdayMain stream

What does RunningBoard do? 4 Assertions

By: hoakley
31 July 2025 at 14:30

This week’s dive into RunningBoard tackles one of the central questions: this subsystem repeatedly uses the term assertion, but what are these assertions, and how does RunningBoard handle them?

In computing, assertions may have their origin in hardware verification languages like Verilog, where they’re used to state required conditions in declarative form. They appear to have entered macOS through the background service assertiond, which made a name for itself as a killer of processes and apps. Power assertions have been noted in the log and elsewhere for at least the last decade, and are mentioned in IOPMLib, part of IOKit. Since macOS Catalina, assertions have been at the centre of RunningBoard, which remains essentially undocumented.

Developer app

To get a better idea of how assertions are used, I launched Apple’s Developer app on a Mac mini M4 Pro running macOS 15.5, and followed log entries for a period of over 14 seconds from the start of the launch process. Although RunningBoard’s job description records this app’s platform as 6, typical of a Catalyst app, in other log entries it’s claimed not to be a Catalyst app.

At an arbitrary start time of 01.126 seconds, LaunchServices decided to launch this app initially through CoreServicesUIAgent, which 0.04 seconds later requested RunningBoard to execute the launch request. This eventually led to a connection being initialised to FrontBoard Scene Manager, where the app was registered as a new scene, and activated.

Example assertion

FrontBoard set the process visibility to Foreground:
01.593239 com.apple.FrontBoard [app<application.developer.apple.wwdc-Release.9312198.9312203~>:2946] Setting process visibility to: Foreground

RunningBoard then announced it was acquiring a new assertion, giving its descriptor and the PID of the target process
01.593248 com.apple.runningboard Acquiring assertion: <RBSAssertionDescriptor| "com.apple.frontboard.after-life.subordinate" ID:(null) target:2946>
01.593288 com.apple.runningboard PERF: Received request from [osservice<com.apple.uikitsystemapp(501)>:748] (euid 501, auid 501) (persona (null)): acquireAssertionWithDescriptor:error:
01.593289 runningboardd acquireAssertionWithDescriptor

A fuller description followed, including the RunningBoard ID, and the attributes of the assertion
01.593324 com.apple.runningboard Acquiring assertion targeting [app<application.developer.apple.wwdc-Release.9312198.9312203(501)>:2946] from originator [osservice<com.apple.uikitsystemapp(501)>:748] with description <RBSAssertionDescriptor| "com.apple.frontboard.after-life.subordinate" ID:424-748-2228 target:2946 attributes:[
<RBSDomainAttribute| domain:"com.apple.frontboard" name:"AfterLife-Subordinate" sourceEnvironment:"(null)">
]>

This assertion was made active, and RunningBoard stated how many assertions were currently targeting that process
01.593343 com.apple.runningboard Assertion 424-748-2228 (target:[app<application.developer.apple.wwdc-Release.9312198.9312203(501)>:2946]) will be created as active
01.593389 com.apple.runningboard app<application.developer.apple.wwdc-Release.9312198.9312203(501)> is now targeted by 11 assertions

That triggered a state update for the process
01.593400 runningboardd state update

In this case, RunningBoard couldn’t do anything, so left the process’s assertions as they were
01.593461 com.apple.runningboard _checkForSuspendableAssertionCycle for app<application.developer.apple.wwdc-Release.9312198.9312203(501)> bailing out since it's not holding a suspendable assertion
01.593466 com.apple.runningboard Removing 0 assertions

This did, though, alter the inheritance of existing assertions
01.593556 com.apple.runningboard Process: [app<application.developer.apple.wwdc-Release.9312198.9312203(501)>:2946] has changes in inheritances: {(
<RBSInheritance| environment:(none) name:com.apple.launchservices.userfacing origID:424-391-2215 0>,
<RBSInheritance| environment:(none) name:com.apple.launchservices.userfacing origID:424-391-2215 0>,
<RBSInheritance| environment:(none) name:com.apple.launchservices.userfacing origID:424-391-2214 0>,
<RBSInheritance| environment:(none) name:com.apple.frontboard.visibility origID:424-420-2216 0>
)}

This completed the acquisition of this assertion, and the process’s new state was calculated
01.593564 com.apple.runningboard Finished acquiring assertion 424-748-2228 (target:[app<application.developer.apple.wwdc-Release.9312198.9312203(501)>:2946])
01.593741 com.apple.runningboard Calculated state for app<application.developer.apple.wwdc-Release.9312198.9312203(501)>: running-active (role: UserInteractiveFocal) (endowments: <private>)

This was all accomplished in around 0.0005 seconds. FrontBoard then continued processing the app’s scene
01.593743 com.apple.FrontBoard Ingesting properties from UIApplicationSceneSettings...

RunningBoard assertion numbers are of the form 424-748-2228, where the second group 748 is the PID of the source of the assertion, and the third group 2228 is a sequential number.

Assertions recorded

  • Assertion 424-620-2211 for LaunchServices to launch the app. For this, RunningBoard created its job description with its process ID of 2946, assigned it memory limits, stated the process will be managed, and stated it was running-active with a role of UserInteractive.
  • Assertion 424-424-2212 RunningBoard Underlying Assertion. RunningBoard stated the process will be created as active, calculated its processStartTime, created a new RunningBoard Coalition with an ID of 4460, set its Jetsam priority to 0 (the lowest priority over memory use), and set its Darwin GPU to “deny”. Its state was given as running-interactive-NotVisible with a taskState of 4.
  • Assertion 424-391-2213 foregroundApp 2946. This changed its role to UserInteractiveNonFocal, and was repeated in assertion 424-391-2214.
  • Assertion 424-391-2215 frontmost 2946. RunningBoard changed it to running-active-NotVisible, with a Jetsam priority of 100 (extremely unlikely to be killed to free memory), and Darwin GPU to “allow”.
  • Assertion 424-391-2217 LaunchServices notification. It was changed to running-active with a role of UserInteractiveFocal, handshaking took place with the process, and the launch request was recorded. Slightly later, RunningBoard announced it had started “death monitoring” of the process, in case its launch request was denied and the process was killed.
  • Assertion 424-748-2218 FrontBoard Bootstrap Background. Following this, the process acquired its first power assertion ID 34241, outside of RunningBoard.
  • Assertion 424-2946-2219 Shared Background Assertion
  • Assertion 424-655-2220 File Coordination Claim, the first of a series of five similar assertions. For each, RunningBoard recorded Prevent Task Suspend, Prevent Task Throttle Down. After the last it checked for a Suspendable Assertion Cycle, then removed several assertions held for the process.
  • Assertion 424-2946-2227 App Nap adapter assertion. RunningBoard then enabled and configured AppNap for the process, and set the AppNap state.
  • Assertion 424-748-2228 FrontBoard AfterLife-Subordinate. This is the assertion detailed above.
  • Assertion 424-748-2229 FrontBoard Visibility Workspace Foreground Focal. With this, RunningBoard set the process to running-active-Visible with taskState 4.
  • Assertion 424-420-2230 App Drawing. Following that, storekitagent was running as PID 2947, accompanying the Developer app.
  • Assertion 424-2946-2232 CFNetwork StorageDB.
  • Assertion 424-2946-2233 System Animation Fence.
  • Assertion 424-420-2234 App Visible. Following that, uikitsystemapp was drawing, presumably for the Developer app.
  • Assertion 424-420-2236 FuseBoard Process Window State Visible.
  • Assertion 424-2946-2238 system animation fence, which was repeated a total of five times.
  • Assertion 424-2946-2244 Shared Background Assertion 1, following which were two more assertions for system animation fence.
  • Assertion 424-2946-2247 com.apple.CFNetwork.StorageDB again.
  • Assertion 424-655-2248 File coordination claim, repeated in Assertion 424-655-2250.

All these assertions were completed in 14 seconds.

Reading RunningBoard

As shown in the series of assertions recorded in the log for the Developer app, RunningBoard provides a detailed account of milestones through the launch and early running of this app, covering much other than its security and TCC activity.

This starts with a job description containing a great deal of useful information about the app, when it’s neither visible nor focal. At that stage it’s given a minimal Jetsam priority, putting it in the front line to be forcibly quit if memory was short, and it’s denied access to the GPU. As launch proceeds, its Jetsam priority is raised to 100 and it’s allowed GPU access. Its role is then changed to UserInteractiveFocal, its window management is handled by FrontBoard and it becomes visible, and able to undergo AppNap. Two supporting services are engaged, storekitagent to handle its data, and uikitsystemapp to draw its interface.

Once the novelty of RunningBoard had worn off, I had come to consider its incessant chatter in the log as a distraction. However, a log extract obtained with the subsystem set to com.apple.runningboard provides a detailed account of events during an app’s life cycle, without the nuisance of privacy censorship, or the app having to make its own log entries.

Key points for close reading of the log

  • Set a predicate for obtaining log entries for the com.apple.runningboard subsystem, and initially filter entries on Acquiring assertion in the Messages field.
  • Identify the Process ID of interest.
  • Track assertion descriptions, giving the reason for each assertion.
  • Note assertion IDs, and interpret them with the aid of the PID given in their second number field.
  • Follow assertions for linked processes through their PIDs.

What does RunningBoard do? 2 Managed apps

By: hoakley
15 July 2025 at 14:30

In the previous article I looked at how RunningBoard monitored an ordinary notarized macOS app, but didn’t manage its life cycle by limiting its access to resources such as memory, CPU or GPU. Here I extend that to the two types of app that are most likely to be managed by RunningBoard, Catalyst and iPadOS apps.

Catalyst

Apple introduced this as a streamlined way for developers of iPadOS apps to be able to create a version that runs in macOS from Catalina onwards. When running on macOS, Catalyst apps are dependent on RunningBoard’s life cycle management, UIKitSystem (which first appeared in macOS Mojave) to bridge between UIKit’s class framework in iPadOS and those in macOS, and additional -board services including FrontBoard, FuseBoard and BaseBoard. Catalyst apps also have access to some features in AppKit, the central class framework for apps developed for macOS, at least until SwiftUI has started to replace it.

Although Apple might have had high hopes of Catalyst bringing many new apps from iPadOS, it hasn’t proved popular with third-party developers, and the ability of Apple silicon Macs to run iPadOS and iOS apps has reduced its relevance to users.

Job descriptions

RunningBoard’s lengthy job descriptions recorded in the log early during the app launch process allow regular macOS, Catalyst and iPadOS apps to be distinguished easily. At the start of each job description, the Platform is recorded, 1 for macOS, 2 for iPadOS, and 6 for Catalyst.

Other differences in job descriptions include:

  • In EnvironmentVariables, iPadOS adds Container values for CFFIXED_USER_HOME and _DYLD_CLOSURE_HOME.
  • In AdditionalProperties, both Catalyst and iPadOS apps have “Managed” set to true, and SpawnConstraints containing their CDHashes.
  • In AdditionalProperties, iPadOS apps have a BeforeTranslocationBundlePath specified.

Catalyst launch

Catalyst apps are launched using the same basic sequence of events as regular macOS apps, with some additional overhead resulting from their UIKitSystem and -board service support. However, when they reach RunningBoard they become managed, in the test case denying it access to the GPU:
00.984295 com.apple.runningboard [app…] Memory Limits: active 0 inactive 0
00.984303 com.apple.runningboard [app…] This process will be managed.
00.984307 com.apple.runningboard <private> is not freezer eligible, disabling freezing.
00.985634 com.apple.runningboard [app…] Set jetsam priority to 0 [0] flag[1]
00.985649 com.apple.runningboard 3638 Set Darwin GPU to "deny"
00.985708 com.apple.runningboard 3638 setGPURole role to 2 (no effect for this process)
00.985715 com.apple.runningboard [app…] Disabled CPU monitoring
00.985716 com.apple.runningboard [app…] Reset CPU monitoring limits to defaults
00.985717 com.apple.runningboard [app…] Resumed CPU monitoring

where [app…] is the app identifier.

Later management includes regular state updates, such as
01.209325 com.apple.runningboard Update delivered for [app…] with taskState 4
01.209327 com.apple.runningboard Received state update for 3638 (app…, running-active-Visible

iPadOS launch

iPadOS and iOS apps are launched completely differently on macOS. Initially MIS, presumably MobileInstallationService, validates the app and its Wrapper, which are translocated to a hidden location in /private/var/folders, from where the wrapped app will be launched. Translocation isn’t intended as a security measure, as it is with macOS apps run there when in quarantine, but is required to work around two limitations:

  • iOS/iPadOS apps may expect to be run from a path that doesn’t contain whitespaces. The path to the translocation folder guarantees that.
  • In macOS, the user can run apps from (almost) any path, such as the Desktop, and can rename apps. The translocation path ensures the app’s name and path remain fixed.

Those are reflected in the following log entries, which have changed little since Big Sur:
00.946662 com.apple.mis Bundle: <private>
00.946662 com.apple.mis Is main executable: 0
00.953668 com.apple.syspolicy MIS validation result: 0
00.953673 com.apple.syspolicy Valid app wrapper: <private>
00.953719 com.apple.syspolicy appWrapperPolicyResult: <private>, AWPolicyResult: 1,1,0
00.956399 com.apple.securityd SecTranslocateCreateGeneric: created /private/var/folders/x4/[…]/d/Wrapper
00.965043 com.apple.launchservices LAUNCH: translocate to <private> from <private>
01.047816 com.apple.launchservices Marking <private> as .requiresSecureLaunch because it is PLATFORM_IOS or PLATFORM_MACCATALYST.
01.049878 com.apple.launchservices LAUNCH: _LSLaunchThruRunningboard: com.parrot.freeflight6 / <private>

FairPlay DRM is then accessed through the Secure Enclave Processor.

When it reaches the attention of RunningBoard, the app’s memory limits are set, it’s denied the GPU, and it’s then managed through its life cycle.
02.365127 com.apple.runningboard [app…:3466] Memory Limits: active 16384 inactive 16384
02.365137 com.apple.runningboard [app…:3466] This process will be managed.
02.365142 com.apple.runningboard <private> is not freezer eligible, disabling freezing.
02.365150 com.apple.runningboard Now tracking process: [app…:3466]
02.367347 com.apple.runningboard 3466 Set Darwin GPU to "deny"
02.367450 com.apple.runningboard 3466 setGPURole role to 2 (no effect for this process)
02.367460 com.apple.runningboard [app…:3466] Disabled CPU monitoring
02.367462 com.apple.runningboard [app…:3466] Reset CPU monitoring limits to defaults
02.367464 com.apple.runningboard [app…:3466] Resumed CPU monitoring
02.367496 com.apple.runningboard [app…:3466] set Memory Limits to Hard Inactive (16384)

where [app…:3466] is the app identifier.

That’s followed by frequent assertions and state updates.

PerfPowerServices

Some users have reported RunningBoard using high CPU %, sometimes associated with high levels from PerfPowerServices. By chance, during these tests I encountered a similar situation. For several seconds, the log was filled with entries recording com.apple.PerfPowerServices requesting management information from RunningBoard about most if not all services running at that time.

Many entry sequences followed the pattern:
00.004740 com.apple.runningboard PERF: Received request from [osservice<com.apple.PerfPowerServices>:976] (euid 0, auid 0) (persona (null)): lookupHandleForPredicate:error:
00.004743 com.apple.runningboard PERF: Received lookupHandleForPredicate request from [osservice<com.apple.PerfPowerServices>:976] (euid 0, auid 0) (persona (null))
00.004971 com.apple.runningboard _multiInstance = 0
00.004973 com.apple.runningboard _executablePath = /usr/sbin/cfprefsd
00.004974 com.apple.runningboard no additional launch properties found for <private>
00.005020 com.apple.runningboard _resolveProcessWithIdentifier pid 2712 euid 277 auid 277
00.005040 com.apple.runningboard Resolved pid 2712 to [osservice<com.apple.cfprefsd.xpc.agent(277)>:2712]
00.005089 com.apple.runningboard memorystatus_control error: MEMORYSTATUS_CMD_CONVERT_MEMLIMIT_MB(-1) returned -1 22 (Invalid argument)
00.005091 com.apple.runningboard memorystatus_control error: MEMORYSTATUS_CMD_CONVERT_MEMLIMIT_MB(0) returned -1 22 (Invalid argument)
00.007828 com.apple.runningboard Full encoding handle <private>, with data 44b0344500000a98, and pid 2712
00.008019 com.apple.runningboard [osservice<com.apple.cfprefsd.xpc.agent(277)>:2712] is not RunningBoard jetsam managed.
00.008040 com.apple.runningboard [osservice<com.apple.cfprefsd.xpc.agent(277)>:2712] This process will not be managed.

for multiple copies of cfprefsd, and many other processes.

Presumably PerfPowerServices is concerned with performance power services, but the purpose of these many requests is unknown. After a few seconds of frenetic activity, and more than 10,000 log entries, this subsided and normal running was resumed. If anyone can provide an explanation, I’d be most grateful.

Key points

  • RunningBoard job descriptions record the app Platform: 1 for macOS, 2 for iPadOS, and 6 for Catalyst.
  • Catalyst apps are managed by RunningBoard through a relatively normal launch sequence.
  • iPadOS/iOS apps are launched differently, in a Wrapper and translocated to avoid problems with their name and path in macOS.
  • iPadOS/iOS apps rely on FairPlay DRM accessed through the Secure Enclave.
  • iPadOS/iOS apps have memory limits imposed by RunningBoard.
  • PerfPowerServices can busy RunningBoard with many requests and high CPU %. The cause is unknown, but that high activity should settle of its own accord.

❌
❌