Skip to content

Commit 8249a59

Browse files
committed
no-mistakes(review): Captain: require observed macOS permission attribution
1 parent d22af05 commit 8249a59

3 files changed

Lines changed: 61 additions & 25 deletions

File tree

bin/fm-macos-permissions.sh

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -353,34 +353,34 @@ printf '\n'
353353

354354
printf '%s\n' 'Ghostty (terminal launcher)'
355355
print_permission 'Full Disk Access' 'CONDITIONAL' "$ghostty_fda" \
356-
'Needed only for protected Mail, Messages, Safari, Home, backup, or administrative data.'
356+
'Protected-path work needs the exact responsible entry macOS observes; add Ghostty only when macOS names it.'
357357
print_permission 'Automation' 'CONDITIONAL' 'PER TARGET' \
358-
'Needed only for Apple Events from Ghostty to System Events or another named app; tmux does not use it.'
358+
'The first-use Allow dialog establishes the exact controller-to-target pair for terminal-driven app automation; tmux does not use Apple Events.'
359359
print_permission 'Screen Recording' 'CONDITIONAL' "$ghostty_screen" \
360-
'Needed for native desktop capture in this launch context, not for Chrome DevTools Protocol screenshots.'
360+
'Native desktop capture needs the exact responsible entry macOS observes; add Ghostty only when macOS names it.'
361361
print_permission 'Accessibility' 'CONDITIONAL' "$ghostty_accessibility" \
362-
'Needed for native UI inspection or input in this launch context, not for tmux or browser-protocol control.'
362+
'Native UI control needs the exact responsible entry macOS observes; add Ghostty only when macOS names it.'
363363
printf '\n'
364364

365365
printf 'Claude Code PATH command target (%s)\n' "${claude_binary:-UNKNOWN: not found on PATH}"
366366
print_permission 'Full Disk Access' 'LAUNCHER OR CONDITIONAL' "$claude_fda" \
367367
'Use the exact responsible entry macOS observes for protected-path access; command ancestry alone does not establish it.'
368368
print_permission 'Automation' 'CONDITIONAL' 'PER TARGET' \
369-
'Needed only when Claude sends Apple Events to a named app; it is not needed for tmux.'
369+
'The first-use Allow dialog establishes the exact controller-to-target pair for app automation; approve Claude only when macOS names it as controller.'
370370
print_permission 'Screen Recording' 'CONDITIONAL' "$claude_screen" \
371-
'Needed only if a Claude-launched native visual tool captures the desktop.'
371+
'Native desktop capture needs the exact responsible entry macOS observes; command ancestry does not establish it.'
372372
print_permission 'Accessibility' 'CONDITIONAL' "$claude_accessibility" \
373-
'Needed only if a Claude-launched native UI tool inspects or controls other applications.'
373+
'Native UI control needs the exact responsible entry macOS observes; command ancestry does not establish it.'
374374
printf '\n'
375375

376376
printf 'Codex PATH command target (%s)\n' "${codex_binary:-UNKNOWN: not found on PATH}"
377377
print_permission 'Full Disk Access' 'LAUNCHER OR CONDITIONAL' "$codex_fda" \
378378
'Use the exact responsible entry macOS observes for protected-path access; the current PATH command does not establish it.'
379379
print_permission 'Automation' 'CONDITIONAL' 'UNKNOWN' "$codex_automation_note"
380380
print_permission 'Screen Recording' 'REQUIRED FOR COMPUTER USE' "$codex_screen" \
381-
'Native Computer Use needs screen pixels; chrome-devtools-axi page screenshots do not.'
381+
'Native Computer Use needs screen pixels for the exact responsible entry macOS observes; DevTools screenshots do not.'
382382
print_permission 'Accessibility' 'REQUIRED FOR COMPUTER USE' "$codex_accessibility" \
383-
'Native Computer Use needs the macOS accessibility tree and input control.'
383+
'Native Computer Use needs Accessibility for the exact responsible entry macOS observes.'
384384
printf '\n'
385385

386386
printf 'no-mistakes CLI PATH entry (%s)\n' "${no_mistakes_command:-UNKNOWN: not found on PATH}"
@@ -405,7 +405,7 @@ print_permission 'Accessibility' 'REQUIRED FOR COMPUTER USE' "$no_mistakes_acces
405405
'Daemon-launched Computer Use needs UI control, but the responsible identity is unknown; use the exact Accessibility entry macOS observes.'
406406
printf '\n'
407407

408-
printf '%s\n' 'Stored Automation relationships'
408+
printf '%s\n' 'Stored Automation candidate relationships (not proof of responsibility)'
409409
print_automation_pairs 'Ghostty' com.mitchellh.ghostty "$ghostty_binary"
410410
print_automation_pairs 'Claude Code' com.anthropic.claude-code "$claude_command" "$claude_binary"
411411
print_automation_pairs 'Codex' codex "$codex_command" "$codex_binary" \

docs/macos-permissions.md

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -34,52 +34,58 @@ macOS may attribute `claude`, `codex`, or descendant requests to Ghostty, but co
3434

3535
The no-mistakes daemon is launched independently by launchd, so a Ghostty grant does not by itself establish access for gate agents spawned by that daemon.
3636

37-
When macOS lists a different responsible entry than this guide predicts, grant only that observed entry and preserve the controller-to-target relationship shown for Automation.
37+
For every service, grant only the responsible entry macOS observes and preserve the controller-to-target relationship shown for Automation.
3838

3939
Apple recommends the TCC attribution log for finding that responsible app or binary on managed machines, and the same read-only log is useful during local diagnosis.
4040

4141
## Ghostty
4242

4343
- **Full Disk Access - captain must click when protected data is in scope.**
44-
Add `/Applications/Ghostty.app` when firstmate or a terminal-launched agent must read Mail, Messages, Safari, Home, Time Machine backups, or protected administrative data.
44+
Terminal-launched work needs this capability only when it must read Mail, Messages, Safari, Home, Time Machine backups, or protected administrative data.
45+
Grant only the exact responsible entry macOS observes for that protected-path service.
46+
Add `/Applications/Ghostty.app` only when macOS attributes that protected-path service to Ghostty.
4547
Firstmate state and repositories stored under ordinary unprotected home-directory paths do not require this grant.
46-
- **Automation - captain must approve each requested target when Apple Events are in scope.**
47-
Click Allow in the first target-specific dialog for Ghostty controlling System Events or the other named application.
48+
- **Automation - captain must approve each requested target when terminal-driven app automation is in scope.**
49+
Trigger the required operation and use the first-use Allow dialog to identify the exact controller-to-target pair macOS observes.
50+
Approve Ghostty controlling a target only when macOS shows Ghostty as the controller for that exact pair.
4851
If that request was denied or later disabled, review or enable the relationship under Automation in System Settings.
4952
Do not grant Automation for tmux control because tmux never uses Apple Events.
5053
- **Screen & System Audio Recording - captain must approve when native desktop capture is in scope.**
51-
This unlocks `screencapture`, ScreenCaptureKit, or native Computer Use when TCC attributes the request to Ghostty.
52-
Click Allow or Allow While Using the App if macOS presents the first-use dialog, or add or enable Ghostty in System Settings if access was denied, disabled, or must be added manually.
54+
This unlocks `screencapture`, ScreenCaptureKit, or native Computer Use for the exact responsible entry macOS observes.
55+
Approve Ghostty only when macOS attributes that Screen Recording request to Ghostty.
56+
Click Allow or Allow While Using the App if macOS presents the first-use dialog, or add or enable the observed entry in System Settings if access was denied, disabled, or must be added manually.
5357
It is not needed for `chrome-devtools-axi` page screenshots.
5458
- **Accessibility - captain must click when native application control is in scope.**
55-
This unlocks accessibility-tree inspection, focus changes, clicks, typing, and other UI control when TCC attributes the request to Ghostty.
59+
This unlocks accessibility-tree inspection, focus changes, clicks, typing, and other UI control for the exact responsible entry macOS observes.
60+
Approve Ghostty only when macOS attributes that Accessibility request to Ghostty.
5661
It is not needed for tmux or Chrome DevTools Protocol control.
5762

5863
## Claude Code
5964

6065
- **Full Disk Access - captain must click only when macOS attributes protected access to a separate entry.**
6166
Grant only the exact responsible entry macOS observes when protected data is in scope because command ancestry alone cannot identify it.
62-
- **Automation - captain must approve each requested target when Claude sends Apple Events.**
63-
The relationship is Claude Code controlling System Events or another named application, and it appears only after that target-specific request.
67+
- **Automation - captain must approve each requested target when this workflow performs app automation.**
68+
Trigger the required operation and use the first-use Allow dialog to identify the exact controller-to-target pair macOS observes.
69+
Approve Claude Code controlling a target only when macOS shows Claude Code as the controller for that exact pair.
6470
Click Allow in the first dialog, or use Automation in System Settings to review or change a recorded relationship.
6571
Claude Code does not need Automation for tmux.
6672
- **Screen & System Audio Recording - no baseline grant is needed.**
67-
Grant the responsible entry only if a Claude-launched native visual tool captures the desktop rather than a browser page through DevTools.
73+
Grant the exact responsible entry macOS observes only if a native visual tool invoked by this workflow captures the desktop rather than a browser page through DevTools.
6874
Approve a first-use dialog if macOS presents one, or use System Settings after denial or when adding the entry manually.
6975
- **Accessibility - no baseline grant is needed.**
70-
Grant the responsible entry only if a Claude-launched native UI tool inspects or controls another application.
76+
Grant the exact responsible entry macOS observes only if a native UI tool invoked by this workflow inspects or controls another application.
7177

7278
## Codex
7379

7480
- **Full Disk Access - captain must click only when macOS attributes protected access to a separate entry.**
7581
Grant only the exact responsible entry macOS observes when protected data is in scope because the current Codex command path cannot identify it.
76-
- **Automation - captain must approve each requested target when Codex sends Apple Events.**
82+
- **Automation - captain must approve each requested target when this workflow performs app automation.**
7783
The helper reports Codex Automation capability as `UNKNOWN` because a command found on `PATH` does not establish the active TCC controller identity.
7884
When possible, it separately reports the exact `codesign` result for that current command filesystem target without applying the result to another installation or running process.
79-
Click Allow in the first dialog, or use Automation in System Settings to review or change a recorded relationship.
85+
Trigger the required operation and approve only the controller-to-target pair shown by the first-use Allow dialog, or use Automation in System Settings to review or change a recorded relationship.
8086
Apple documents that entitlement as permission to prompt rather than permission to bypass the prompt in [Apple Events Entitlement](https://developer.apple.com/documentation/bundleresources/entitlements/com.apple.security.automation.apple-events).
8187
- **Screen & System Audio Recording - captain must approve for native Codex Computer Use.**
82-
This unlocks desktop pixels for Computer Use when the responsible entry is Codex, Ghostty, or the Codex Computer Use helper shown by macOS.
88+
This unlocks desktop pixels for Computer Use only for the exact responsible entry macOS observes for that Screen Recording request.
8389
Click Allow or Allow While Using the App if macOS presents the first-use dialog, or add or enable the responsible entry in System Settings if access was denied, disabled, or must be added manually.
8490
It is not needed for `chrome-devtools-axi` page screenshots.
8591
- **Accessibility - captain must click for native Codex Computer Use.**
@@ -94,7 +100,7 @@ Apple recommends the TCC attribution log for finding that responsible app or bin
94100
Use the exact responsible entry shown by macOS or TCC attribution, and treat the configured path printed by the helper as diagnostic evidence only.
95101
A Ghostty Full Disk Access grant does not by itself establish protected-path access for the independently launched daemon tree.
96102
- **Automation - captain approval is necessary but not sufficient for daemon-launched Apple Events.**
97-
Automation is still a pair such as no-mistakes controlling System Events or another named application.
103+
Trigger the required operation and use the first-use Allow dialog to identify the exact controller-to-target pair macOS observes.
98104
The helper reports the running daemon capability as `UNKNOWN` because inspecting the configured path cannot establish the entitlement of an already loaded process image.
99105
Apple documents the entitlement as target-signing-dependent, so a separate current-image inspection is required before making any capability claim.
100106
- **Screen & System Audio Recording - captain must approve for daemon-launched Codex Computer Use.**

tests/fm-macos-permissions.test.sh

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,35 @@ test_missing_probe_is_honest() {
145145
pass 'missing and undetectable grants are reported honestly'
146146
}
147147

148+
test_permission_guidance_requires_observed_attribution() {
149+
local world out docs
150+
world=$(make_world attribution-guidance)
151+
out=$(run_report "$world")
152+
docs=$(<"$ROOT/docs/macos-permissions.md")
153+
154+
assert_contains "$out" \
155+
'Protected-path work needs the exact responsible entry macOS observes; add Ghostty only when macOS names it.' \
156+
'Ghostty Full Disk Access guidance assumed responsibility'
157+
assert_contains "$out" \
158+
'The first-use Allow dialog establishes the exact controller-to-target pair for terminal-driven app automation' \
159+
'terminal Automation guidance assumed a controller-to-target pair'
160+
assert_contains "$out" \
161+
'approve Claude only when macOS names it as controller' \
162+
'Claude Automation guidance assumed the controller identity'
163+
assert_contains "$docs" \
164+
'only when macOS attributes that protected-path service to Ghostty.' \
165+
'documentation assumed Ghostty Full Disk Access responsibility'
166+
assert_contains "$docs" \
167+
'use the first-use Allow dialog to identify the exact controller-to-target pair macOS observes.' \
168+
'documentation did not require observed Automation attribution'
169+
case "$out$docs" in
170+
*'Apple Events from Ghostty to System Events'*|*'The relationship is Claude Code controlling'*)
171+
fail 'fixed controller or target attribution remained in guidance'
172+
;;
173+
esac
174+
pass 'permission guidance requires macOS-observed attribution'
175+
}
176+
148177
test_readable_database_reports_stored_rows() {
149178
local world out ghostty_section daemon_section
150179
world=$(make_world readable)
@@ -406,6 +435,7 @@ test_non_macos_refuses() {
406435
}
407436

408437
test_missing_probe_is_honest
438+
test_permission_guidance_requires_observed_attribution
409439
test_readable_database_reports_stored_rows
410440
test_mixed_identity_evidence_is_unknown
411441
test_partial_database_evidence_is_unknown

0 commit comments

Comments
 (0)