From e917bb37a23c0be8300b8331acec1753466723be Mon Sep 17 00:00:00 2001 From: chaodu-agent Date: Sat, 26 Sep 2026 20:14:24 -0400 Subject: [PATCH] =?UTF-8?q?fix(deploy):=20refuse=20to=20install=20an=20ad-?= =?UTF-8?q?hoc=20bundle=20=E2=80=94=20it=20silently=20drops=20TCC=20grants?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TCC (Full Disk Access / Screen Recording / Accessibility) is keyed on the signing identity + bundle id, so an ad-hoc re-sign (codesign -s -) reads to macOS as a new app and drops every grant the human made — the Screens pane freezes and screenshot returns screen_recording=false. This happened repeatedly when a fast SSH deploy fell back to ad-hoc (openab-pty#37, #10 comment). deploy.sh now checks TeamIdentifier after signing and, before touching the LaunchAgent, refuses anything not signed by 6LPQNY95AQ (EXPECT_TEAM to change; ALLOW_ADHOC=1 for a throwaway local build). Net effect: the human grants each permission once and later deploys keep it. README says so. Verified: ad-hoc bundle → refused; the installed team-signed bundle → allowed. --- README.md | 13 +++++++++++++ scripts/deploy.sh | 27 +++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/README.md b/README.md index ca135f5..f499c25 100644 --- a/README.md +++ b/README.md @@ -164,6 +164,19 @@ Verified 2026-09-26 end to end on macmini against the openab-pty runtime (PR #38 `sys_info screenshot mouse key osascript instance_status`, `exec` refused, `sys_info` answered, `DELETE /attach/{id}` detached. +## TCC grants survive re-deploys only if the signature does + +Screen Recording, Accessibility and Full Disk Access are keyed on the **code-signing identity + +bundle id**, not the path. So a grant you make once in System Settings stays across upgrades +**only if every build is signed by the same identity**. An ad-hoc signature (`codesign -s -`) +has no stable identity — macOS treats each one as a new app and silently drops every grant, and +the symptom is the Screens pane freezing / `screen_recording=false` after a deploy. + +Therefore `deploy.sh` **refuses to install anything but a Team-signed bundle** (team `6LPQNY95AQ`). +Run it from a console session (the login keychain is locked over SSH, which is why ad-hoc kept +sneaking in). You grant each permission **once**; later versions keep it. Override for a throwaway +local build with `ALLOW_ADHOC=1`, accepting that you will have to re-grant. + ## Build & test (on macmini; the laptop never compiles Swift) ```sh diff --git a/scripts/deploy.sh b/scripts/deploy.sh index 6eeceaf..6051c13 100755 --- a/scripts/deploy.sh +++ b/scripts/deploy.sh @@ -1,6 +1,9 @@ #!/bin/bash # Deploy oab-instance-mcp on this Mac (run ON the target, e.g. macmini). # scripts/deploy.sh +# Team-signed only: installing an ad-hoc bundle drops the human's TCC grants, so it is +# refused unless ALLOW_ADHOC=1. Expected team defaults to 6LPQNY95AQ (EXPECT_TEAM=... to change). +# # env: KEYCHAIN= keychain holding the identity (default: login keychain) # KEYCHAIN_PASSWORD_FILE= if set, unlock $KEYCHAIN first (needed over non-interactive # SSH: the login keychain answers errSecInternalComponent) @@ -88,6 +91,30 @@ fi codesign --force --options runtime --timestamp=none ${KC_ARGS[@]+"${KC_ARGS[@]}"} --sign "$IDENTITY" --identifier "$BUNDLE_ID" "$APP" codesign --verify --deep --strict "$APP" && echo "signed: $(codesign -dv "$APP" 2>&1 | grep -E '^(Authority=Apple Dev|TeamIdentifier)' | tr '\n' ' ')" +# TCC (Full Disk Access, Screen Recording, Accessibility) is keyed on the code-signing +# identity + bundle id. An ad-hoc signature has no stable identity, so macOS treats each +# ad-hoc build as a NEW app and silently drops every grant the user made — the Screens pane +# then freezes and screenshot returns "screen_recording=false". That happened repeatedly +# (see openab-pty#37 / instance-mcp#10) whenever a fast SSH deploy fell back to `--sign -`. +# Refuse to install anything but a Team-signed bundle, so a grant the human made ONCE is +# never quietly invalidated by a later deploy. Override only when you knowingly want an +# unsigned local build (and accept re-granting): ALLOW_ADHOC=1. +TEAM=$(codesign -dvv "$APP" 2>&1 | sed -n 's/^TeamIdentifier=//p') +EXPECT_TEAM="${EXPECT_TEAM:-6LPQNY95AQ}" +if [ "${ALLOW_ADHOC:-0}" != "1" ]; then + if [ -z "$TEAM" ] || [ "$TEAM" = "not set" ]; then + echo "refusing to install an ad-hoc-signed bundle: it would drop your TCC grants" >&2 + echo " (Full Disk Access / Screen Recording / Accessibility are keyed on the signing identity)." >&2 + echo " Sign with the Apple Development identity from a console session, or set ALLOW_ADHOC=1 to override." >&2 + exit 1 + fi + if [ "$TEAM" != "$EXPECT_TEAM" ]; then + echo "refusing: signed by team $TEAM, expected $EXPECT_TEAM — a different team is a different app to TCC." >&2 + echo " Set EXPECT_TEAM=$TEAM if this is intentional." >&2 + exit 1 + fi +fi + # Re-serve the Playwright MCP (poc/pw-mcp) as browser_* tools when it is installed, # so a lent sandbox session can read pages as text instead of screenshots (#10). UPSTREAM_ARGS=""