Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
27 changes: 27 additions & 0 deletions scripts/deploy.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
#!/bin/bash
# Deploy oab-instance-mcp on this Mac (run ON the target, e.g. macmini).
# scripts/deploy.sh <allow-login-email> <codesign-identity>
# 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=<path to keychain-db> keychain holding the identity (default: login keychain)
# KEYCHAIN_PASSWORD_FILE=<path> if set, unlock $KEYCHAIN first (needed over non-interactive
# SSH: the login keychain answers errSecInternalComponent)
Expand Down Expand Up @@ -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=""
Expand Down
Loading