# Configuring macOS TCC Permissions for Accessibility and Screen Recording in pi-computer-use

> Learn to configure macOS TCC permissions for Accessibility and Screen Recording in pi-computer-use. Verify and trigger grants programmatically with checkPermissions and registerPermissions.

- Repository: [injaneity/pi-computer-use](https://github.com/injaneity/pi-computer-use)
- Tags: how-to-guide
- Published: 2026-07-16

---

**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`](https://github.com/injaneity/pi-computer-use/blob/main/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`](https://github.com/injaneity/pi-computer-use/blob/main/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`](https://github.com/injaneity/pi-computer-use/blob/main/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`](https://github.com/injaneity/pi-computer-use/blob/main/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`](https://github.com/injaneity/pi-computer-use/blob/main/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`](https://github.com/injaneity/pi-computer-use/blob/main/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`](https://github.com/injaneity/pi-computer-use/blob/main/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`](https://github.com/injaneity/pi-computer-use/blob/main/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`](https://github.com/injaneity/pi-computer-use/blob/main/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`](https://github.com/injaneity/pi-computer-use/blob/main/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`](https://github.com/injaneity/pi-computer-use/blob/main/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:

```typescript
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`](https://github.com/injaneity/pi-computer-use/blob/main/src/platform/macos/permissions.ts), lines 55-63.

### Triggering Permission Prompts

To force the macOS authorization dialogs and ScreenCaptureKit pre-registration:

```typescript
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`](https://github.com/injaneity/pi-computer-use/blob/main/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:

```typescript
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`](https://github.com/injaneity/pi-computer-use/blob/main/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:

```bash
tccutil reset Accessibility com.injaneity.pi-computer-use
tccutil reset ScreenCapture com.injaneity.pi-computer-use

```

*Source:* [`docs/troubleshooting.md`](https://github.com/injaneity/pi-computer-use/blob/main/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 **`MacosHelperClient`** in [`src/platform/macos/helper.ts`](https://github.com/injaneity/pi-computer-use/blob/main/src/platform/macos/helper.ts) manages daemon lifecycle, while **[`src/platform/macos/permissions.ts`](https://github.com/injaneity/pi-computer-use/blob/main/src/platform/macos/permissions.ts)** implements the authorization workflow.
- **`checkPermissions`** queries current rights without prompting, while **`registerPermissions`** triggers the native macOS consent dialogs and ScreenCaptureKit pre-authorization.
- Re-signing the helper invalidates existing grants; use **`tccutil reset`** commands 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.restart`** to 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`](https://github.com/injaneity/pi-computer-use/blob/main/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`](https://github.com/injaneity/pi-computer-use/blob/main/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.