Managing App Privacy Permissions with tccutil and the TCC Database
Resetting stuck permission prompts, why an app sometimes doesn't re-ask for a permission it should, and what the TCC database actually is.
Every time macOS asks whether an app can access your camera, microphone, contacts, or full disk access, that decision is being recorded by TCC (Transparency, Consent, and Control) — the subsystem tracking privacy permission grants across the system — and tccutil is the command-line tool for directly managing those recorded decisions when the GUI’s Privacy & Security settings pane isn’t sufficient.
Where these permissions actually live
TCC decisions are stored in a SQLite database, separately for system-wide grants and per-user grants:
/Library/Application Support/com.apple.TCC/TCC.db
/Users/<username>/Library/Application Support/com.apple.TCC/TCC.db
Direct modification of these database files is blocked by System Integrity Protection even with root access, which is precisely why tccutil exists as the sanctioned interface for resetting permissions — it’s not just a convenience wrapper, it’s genuinely the only supported way to reset TCC state without disabling SIP entirely, which is not something to do casually.
Resetting a specific permission category for an app
tccutil reset Camera com.example.someapp
This clears the recorded Camera permission decision for the specified app’s bundle identifier specifically, causing macOS to re-prompt for that permission the next time the app actually requests camera access, rather than continuing to enforce whatever decision (allow or deny) was previously recorded.
Resetting an entire permission category system-wide
tccutil reset Microphone
Omitting the bundle identifier resets the specified permission category for every application on the system, which is a considerably broader action — every app that previously had microphone access granted will need to re-request and be re-granted it. This is worth doing deliberately, generally as a troubleshooting step for a specific category-wide problem, rather than as a routine action, given how broadly it resets existing grants.
Finding an app’s exact bundle identifier
tccutil requires the precise bundle identifier, not the application’s display name:
osascript -e 'id of app "Some App Name"'
or, for an already-installed app bundle:
defaults read /Applications/SomeApp.app/Contents/Info.plist CFBundleIdentifier
Getting this wrong (a typo, or using the display name directly) means the reset silently does nothing, since it won’t match any actual recorded TCC entry — worth double-checking directly rather than assuming the display name will work.
Why an app sometimes doesn’t ask for a permission you expect it to
A common point of confusion: an app that should logically need to ask for a permission (say, Full Disk Access) sometimes simply doesn’t prompt, silently failing or behaving as though access were denied without ever showing a request dialog. This frequently traces back to the app never actually triggering the specific system API call that causes TCC to generate a prompt in the first place — TCC prompts are triggered by the specific API call requesting protected data, not proactively by the operating system anticipating what an app might need, so an app using a code path that doesn’t hit that specific trigger simply never prompts, regardless of whether it actually needs the access to function correctly.
Manually granting Full Disk Access when an app doesn’t prompt correctly
For the specific, common case of an app that needs Full Disk Access but doesn’t reliably trigger the automatic prompt, manually adding it through the GUI is the practical workaround:
System Settings > Privacy & Security > Full Disk Access > add the app manually via the "+" button
This is a case where the GUI is actually the more reliable path than tccutil, since Full Disk Access specifically isn’t something tccutil reset can grant — it only resets existing decisions back to an unprompted state, it can’t force a new grant on an app’s behalf.
Listing all current permission grants for review
There’s no single built-in command that cleanly lists every current TCC grant in a readable format, but querying the database directly (read-only, without modifying it) gives visibility SIP’s write-protection doesn’t block:
sqlite3 "$HOME/Library/Application Support/com.apple.TCC/TCC.db" \
"SELECT service, client, auth_value FROM access;"
This read-only query is useful specifically for auditing exactly what’s currently been granted across every app, without needing to click through each category individually in System Settings’ Privacy pane to build the same picture manually.
Why this system exists as a separate layer from file permissions
TCC’s protections are deliberately independent from standard Unix file permissions — a process running as your own user account, with full standard filesystem read/write permission to your Documents folder, still can’t access Contacts, Camera, or other TCC-protected resources without a separate, explicit TCC grant. This extra layer exists specifically because standard Unix permissions were never designed with this specific privacy threat model in mind — protecting categories of genuinely sensitive personal data from being silently accessed by any process the user happens to be running, regardless of what that process’s baseline file-level permissions already allow.
Resetting every TCC permission for an app at once
tccutil reset All com.example.someapp
Rather than resetting one specific permission category at a time, reset All clears every recorded TCC decision for the given bundle identifier in a single command — the practical choice when an app has been reinstalled, updated across a major version boundary, or is generally behaving confusingly enough about permissions that starting from a completely clean slate for that one specific app is more efficient than resetting each affected category individually and re-testing after every single reset.
Why command-line tools specifically hit TCC issues often
A command-line tool invoked from Terminal inherits Terminal.app’s own TCC grants rather than having independent grants of its own — which is exactly why a script or CLI tool that needs Full Disk Access, for instance, requires Terminal itself (or whichever terminal emulator you’re actually using) to hold that grant, not the individual script. This becomes a genuine source of confusion the moment you run the same script through a different terminal emulator, an IDE’s integrated terminal, or a cron/launchd-scheduled invocation — each of these is a different requesting-process identity from TCC’s perspective, and each needs its own explicit grant even though the underlying script being executed is byte-for-byte identical in every case.
A practical troubleshooting sequence when permissions seem stuck
When an app’s behavior suggests a missing permission but System Settings shows it as already granted, the practical sequence is: confirm the exact bundle identifier first (a mismatched identifier means you’re looking at the wrong app’s entry entirely), reset that specific category with tccutil reset, quit and relaunch the app fully rather than assuming a live process picks up the reset immediately, and only then re-test the actual behavior — skipping the full relaunch step specifically is a common reason a reset appears not to have worked, when the real issue is simply that the already-running process cached its prior denied state in memory.
Related:
- Fixing ‘Operation Not Permitted’ in Terminal After a macOS Update
- macOS App Sandboxing and Entitlements Explained
Sources: