Configuring macOS TCC Permissions for Accessibility and Screen Recording in pi-computer-use
To configure macOS TCC permissions for pi-computer-use, the native helper app requires both Accessibility and Screen Recording grants, which can be verified programmatically via checkPermissions and triggered through registerPermissions in src/platform/macos/permissions.ts.
The pi-computer-use repository implements a native macOS automation layer that relies on a signed helper app (/Applications/pi-computer-use.app) to perform UI observation and input. Because macOS protects these capabilities through TCC (Transparency, Consent, and Control) frameworks, the codebase includes a dedicated permission management system that guides users through granting Accessibility and Screen Recording rights while handling edge cases like code signing changes and terminal attribution.
Understanding the Permission Architecture
The permission system centers on the MacosHelperClient class defined in src/platform/macos/helper.ts. This client manages a daemonized native process that executes all macOS-specific UI operations on behalf of the JavaScript backend.
When the application launches, ensureInstalled (line 39 in helper.ts) verifies that the helper app exists in /Applications/ with proper executable permissions. Once installed, ensureDaemon launches the background process and validates protocol compatibility before any UI automation can occur.
TCC restrictions require explicit user consent for two distinct capabilities:
- Accessibility: Allows the helper to inject input events and query UI element hierarchies
- Screen Recording: Permits capture of display contents via ScreenCaptureKit
The status of these permissions is encapsulated in a PermissionStatus object returned by the helper, containing boolean flags for accessibility, screenRecordingCapturable (live ScreenCaptureKit probe), and screenRecordingPreflight (TCC database entry).
The Permission Flow Implementation
The coordination logic resides in src/platform/macos/permissions.ts, which implements a seven-stage workflow:
1. Pre-flight Installation Check
Before requesting permissions, the system ensures the helper binary is properly installed and codesigned. The ensureInstalled function in helper.ts handles this validation, copying the bundled helper to /Applications/ if necessary and ensuring it has the +x executable bit.
2. Permission Status Verification
The checkPermissions function (line 55 in permissions.ts) sends a "checkPermissions" command to the daemon via macosHelper.command. This returns the current authorization state without triggering user prompts, allowing the application to detect missing grants before attempting UI operations.
3. User Prompting and System Settings Integration
When permissions are missing, ensureMacosReady invokes ensurePermissions, which displays a native prompt built by permissionPrompt (line 36 in permissions.ts). The system then calls openPermissionPane to launch the appropriate System Settings section (Privacy & Security > Accessibility or Screen Recording).
4. Permission Registration
The registerPermissions function (line 82 in permissions.ts) performs the actual authorization request. This triggers two distinct macOS behaviors:
- Displays the Accessibility consent dialog for input injection rights
- Executes a live ScreenCaptureKit capture to pre-list the helper in the Screen Recording permissions pane
This dual-registration approach ensures the helper appears in both TCC databases immediately, rather than waiting for first-use authorization.
5. Signing Migration Handling
When the helper is re-signed (for example, during development or after certificate updates), macOS invalidates existing TCC grants. The codebase detects this scenario and emits SIGNING_MIGRATION_WARNING (line 13 in permissions.ts), advising users to toggle permissions off and on again in System Settings to re-establish the code signature relationship.
6. Attribution Edge Cases
If the helper is launched as a plain binary from a terminal rather than through the canonical app bundle, the source attribution becomes "caller" (line 17 in permissions.ts). In this mode, TCC grants attach to the launching terminal application rather than the helper itself. The permission prompt adds a specific hint warning users that grants will apply to the terminal, and recommends restarting Pi to use the proper /Applications/pi-computer-use.app bundle for correct attribution.
7. Recheck Workflow
After the user toggles permissions in System Settings, the UI presents a Recheck button. Clicking this invokes macosHelper.restart (line 30 in helper.ts), which terminates and relaunches the daemon so it picks up the fresh TCC grants without requiring a full application restart.
Code Examples for Permission Management
Checking Current Permission Status
Use this pattern to programmatically verify if the helper has sufficient rights before attempting UI automation:
import { macosHelper } from "./platform/macos/helper.ts";
async function isFullyGranted(signal?: AbortSignal): Promise<boolean> {
const status = await macosHelper.command<{
accessibility: boolean;
screenRecordingCapturable: boolean;
screenRecordingPreflight: boolean;
}>("checkPermissions", {}, { signal });
return status.accessibility && status.screenRecordingCapturable;
}
Source: src/platform/macos/permissions.ts, lines 55-63.
Triggering Permission Prompts
To force the macOS authorization dialogs and ScreenCaptureKit pre-registration:
import { macosHelper } from "./platform/macos/helper.ts";
async function registerAndPrompt(signal?: AbortSignal) {
// This will show the Accessibility consent dialog and run a ScreenCaptureKit probe.
await macosHelper.command("registerPermissions", {}, { signal, timeoutMs: 15_000 });
}
Source: src/platform/macos/permissions.ts, lines 82-86.
Rechecking After Manual Toggle
After the user updates permissions in System Settings, restart the helper to pick up changes:
import { macosHelper } from "./platform/macos/helper.ts";
async function recheckPermissions(signal?: AbortSignal) {
await macosHelper.restart(signal); // Restarts the helper so it picks up new grants
const status = await macosHelper.command("checkPermissions", {}, { signal });
console.log("Current status:", status);
}
Source: src/platform/macos/helper.ts, lines 30-37.
Resetting TCC Entries via CLI
When troubleshooting signing migration issues or corrupted permission states, reset the TCC database entries for the helper:
tccutil reset Accessibility com.injaneity.pi-computer-use
tccutil reset ScreenCapture com.injaneity.pi-computer-use
Source: docs/troubleshooting.md, lines 60-63.
Handling Edge Cases and Troubleshooting
Code Signing Changes
The helper application is codesigned with the identifier com.injaneity.pi-computer-use. When this signature changes—whether through updates, development builds, or certificate rotation—macOS considers this a new application and revokes existing TCC grants. The system detects this condition and displays the SIGNING_MIGRATION_WARNING, instructing users to manually toggle both Accessibility and Screen Recording permissions off and then on again in System Settings.
Black Screen Capture Issues
If the permission appears granted in System Settings but capture returns black frames, this typically indicates a ScreenCaptureKit entitlement mismatch. The screenRecordingPreflight boolean may return true while screenRecordingCapturable returns false. In this scenario, use the tccutil reset commands above and re-grant permissions while ensuring the helper is launched from /Applications/pi-computer-use.app rather than a terminal binary.
Terminal Attribution Conflicts
Running the helper directly from a shell (e.g., ./pi-computer-use.app/Contents/MacOS/pi-computer-use) causes TCC to attribute grants to the terminal emulator. The permissionPrompt function detects this "caller" attribution state and warns that "grants will attach to the launching app." Always use the installed app bundle in /Applications/ to ensure permissions bind correctly to the helper.
Summary
- pi-computer-use requires a native macOS helper app (
/Applications/pi-computer-use.app) that operates under TCC restrictions for Accessibility and Screen Recording. - The
MacosHelperClientinsrc/platform/macos/helper.tsmanages daemon lifecycle, whilesrc/platform/macos/permissions.tsimplements the authorization workflow. checkPermissionsqueries current rights without prompting, whileregisterPermissionstriggers the native macOS consent dialogs and ScreenCaptureKit pre-authorization.- Re-signing the helper invalidates existing grants; use
tccutil resetcommands to clear stale entries and re-grant permissions. - Always launch the helper from its installed app bundle location to avoid terminal attribution issues, and use
macosHelper.restartto pick up permission changes without restarting the main application.
Frequently Asked Questions
Why does pi-computer-use need both Accessibility and Screen Recording permissions?
Accessibility permits the helper to query UI element hierarchies and inject keyboard/mouse events via the macOS accessibility APIs, while Screen Recording (specifically ScreenCaptureKit) allows the helper to capture the display contents for computer vision tasks. The PermissionStatus object tracks these separately because Screen Recording has two states: the TCC database entry (screenRecordingPreflight) and the actual capture capability (screenRecordingCapturable), which verifies that the helper can successfully create a capture session.
How do I fix "grant shows as granted but capture is black"?
This occurs when the TCC database records a grant for a different code signature or when the helper is running with terminal attribution. Run the CLI reset commands: tccutil reset Accessibility com.injaneity.pi-computer-use and tccutil reset ScreenCapture com.injaneity.pi-computer-use. Then ensure you are running the helper from /Applications/pi-computer-use.app rather than a terminal path, and click the Recheck button in the UI to invoke macosHelper.restart.
What is the significance of the "caller" attribution warning?
When the helper binary is launched directly from a terminal (e.g., ./pi-computer-use.app/Contents/MacOS/pi-computer-use), macOS attributes TCC grants to the launching terminal application rather than the helper itself. This causes permissions to fail when the helper runs normally. The code detects this "caller" state (line 17 in permissions.ts) and warns that grants will attach to the terminal. Resolve this by installing the helper to /Applications/ and restarting Pi to use the canonical app bundle path.
How does the Recheck button work after I toggle permissions?
Clicking Recheck triggers the macosHelper.restart method (line 30 in helper.ts), which terminates the current daemon process and spawns a new instance. This is necessary because macOS only evaluates TCC grants at process launch; the running helper cannot detect permission changes made in System Settings while it is active. The restart ensures the new process loads with the updated TCC rights, and checkPermissions is then re-executed to verify the grants.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →