From 641080a0b6f60eeae9719586a9823138be222a04 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 3 Jan 2026 20:04:07 +0100 Subject: [PATCH] fix: document macOS permission requirements --- CHANGELOG.md | 1 + docs/mac/permissions.md | 40 +++++++++++++++++++++++++++++++++++++ docs/mac/signing.md | 10 ++++++---- scripts/codesign-mac-app.sh | 23 +++++++++++++++++++++ 4 files changed, 70 insertions(+), 4 deletions(-) create mode 100644 docs/mac/permissions.md diff --git a/CHANGELOG.md b/CHANGELOG.md index e3015e875..b2e083c1b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -108,6 +108,7 @@ - Skills: add tmux-first coding-agent skill + `requires.anyBins` gate for multi-CLI setup (thanks @sreekaransrinath). ### Fixes +- macOS codesign: make ad-hoc signing opt-in with loud warnings and document TCC permission fragility — thanks @mcinteerj. - Gog calendar: format date ranges as RFC 3339 with timezone to satisfy Google Calendar API (thanks @jayhickey). - macOS onboarding: add scrollable page gutter for overflowing content (#105) — thanks @thewilloftheshadow. - Chat UI: keep the chat scrolled to the latest message after switching sessions. diff --git a/docs/mac/permissions.md b/docs/mac/permissions.md new file mode 100644 index 000000000..87d024b67 --- /dev/null +++ b/docs/mac/permissions.md @@ -0,0 +1,40 @@ +--- +summary: "macOS permission persistence (TCC) and signing requirements" +read_when: + - Debugging missing or stuck macOS permission prompts + - Packaging or signing the macOS app + - Changing bundle IDs or app install paths +--- +# macOS permissions (TCC) + +macOS permission grants are fragile. TCC associates a permission grant with the +app's code signature, bundle identifier, and on-disk path. If any of those change, +macOS treats the app as new and may drop or hide prompts. + +## Requirements for stable permissions +- Same path: run the app from a fixed location (for Clawdis, `dist/Clawdis.app`). +- Same bundle identifier: changing the bundle ID creates a new permission identity. +- Signed app: unsigned or ad-hoc signed builds do not persist permissions. +- Consistent signature: use a real Apple Development or Developer ID certificate + so the signature stays stable across rebuilds. + +Ad-hoc signatures generate a new identity every build. macOS will forget previous +grants, and prompts can disappear entirely until the stale entries are cleared. + +## Recovery checklist when prompts disappear +1. Quit the app. +2. Remove the app entry in System Settings -> Privacy & Security. +3. Relaunch the app from the same path and re-grant permissions. +4. If the prompt still does not appear, reset TCC entries with `tccutil` and try again. +5. Some permissions only reappear after a full macOS restart. + +Example resets (replace bundle ID as needed): + +```bash +sudo tccutil reset Accessibility com.clawdis.mac +sudo tccutil reset ScreenCapture com.clawdis.mac +sudo tccutil reset AppleEvents +``` + +If you are testing permissions, always sign with a real certificate. Ad-hoc +builds are only acceptable for quick local runs where permissions do not matter. diff --git a/docs/mac/signing.md b/docs/mac/signing.md index 489c22151..e1e20abce 100644 --- a/docs/mac/signing.md +++ b/docs/mac/signing.md @@ -9,22 +9,24 @@ This app is usually built from `scripts/package-mac-app.sh`, which now: - sets a stable debug bundle identifier: `com.clawdis.mac.debug` - writes the Info.plist with that bundle id (override via `BUNDLE_ID=...`) -- calls `scripts/codesign-mac-app.sh` to sign the main binary, bundled CLI, and app bundle so macOS treats each rebuild as the same signed bundle and keeps TCC permissions (notifications, accessibility, screen recording, mic, speech). Requires a valid signing identity. +- calls `scripts/codesign-mac-app.sh` to sign the main binary, bundled CLI, and app bundle so macOS treats each rebuild as the same signed bundle and keeps TCC permissions (notifications, accessibility, screen recording, mic, speech). For stable permissions, use a real signing identity; ad-hoc is opt-in and fragile (see `docs/mac/permissions.md`). - uses `CODESIGN_TIMESTAMP=auto` by default; it enables trusted timestamps for Developer ID signatures. Set `CODESIGN_TIMESTAMP=off` to skip timestamping (offline debug builds). - inject build metadata into Info.plist: `ClawdisBuildTimestamp` (UTC) and `ClawdisGitCommit` (short hash) so the About pane can show build, git, and debug/release channel. - **Packaging requires Bun**: The embedded gateway relay is compiled using `bun`. Ensure it is installed (`curl -fsSL https://bun.sh/install | bash`). -- reads `SIGN_IDENTITY` from the environment. Add `export SIGN_IDENTITY="Apple Development: Your Name (TEAMID)"` (or your Developer ID Application cert) to your shell rc to always sign with your cert; otherwise signing falls back to ad‑hoc. +- reads `SIGN_IDENTITY` from the environment. Add `export SIGN_IDENTITY="Apple Development: Your Name (TEAMID)"` (or your Developer ID Application cert) to your shell rc to always sign with your cert. Ad-hoc signing requires explicit opt-in via `ALLOW_ADHOC_SIGNING=1` or `SIGN_IDENTITY="-"` (not recommended for permission testing). ## Usage ```bash # from repo root -scripts/package-mac-app.sh # ad-hoc signing +scripts/package-mac-app.sh # auto-selects identity; errors if none found SIGN_IDENTITY="Developer ID Application: Your Name" scripts/package-mac-app.sh # real cert +ALLOW_ADHOC_SIGNING=1 scripts/package-mac-app.sh # ad-hoc (permissions will not stick) +SIGN_IDENTITY="-" scripts/package-mac-app.sh # explicit ad-hoc (same caveat) ``` ### Ad-hoc Signing Note -When signing with `SIGN_IDENTITY="-"` (ad-hoc), the script automatically disables the **Hardened Runtime** (`--options runtime`). This is necessary to prevent crashes when the app attempts to load embedded frameworks (like Sparkle) that do not share the same Team ID. +When signing with `SIGN_IDENTITY="-"` (ad-hoc), the script automatically disables the **Hardened Runtime** (`--options runtime`). This is necessary to prevent crashes when the app attempts to load embedded frameworks (like Sparkle) that do not share the same Team ID. Ad-hoc signatures also break TCC permission persistence; see `docs/mac/permissions.md` for recovery steps. ## Build metadata for About diff --git a/scripts/codesign-mac-app.sh b/scripts/codesign-mac-app.sh index c6310b795..1ce748e77 100755 --- a/scripts/codesign-mac-app.sh +++ b/scripts/codesign-mac-app.sh @@ -71,6 +71,26 @@ if [ -z "$IDENTITY" ]; then fi echo "Using signing identity: $IDENTITY" +if [[ "$IDENTITY" == "-" ]]; then + cat <<'WARN' >&2 + +================================================================================ +!!! AD-HOC SIGNING IN USE - PERMISSIONS WILL NOT STICK (macOS RESTRICTION) !!! + +macOS ties permissions to the code signature, bundle ID, and app path. +Ad-hoc signing generates a new signature every build, so macOS treats the app +as a different binary and will forget permissions (prompts may vanish). + +For correct permission behavior you MUST sign with a real Apple Development or +Developer ID certificate. + +If prompts disappear: remove the app entry in System Settings -> Privacy & Security, +relaunch the app, and re-grant. Some permissions only reappear after a full +macOS restart. +================================================================================ + +WARN +fi timestamp_arg="--timestamp=none" case "$TIMESTAMP_MODE" in @@ -90,6 +110,9 @@ case "$TIMESTAMP_MODE" in exit 1 ;; esac +if [[ "$IDENTITY" == "-" ]]; then + timestamp_arg="--timestamp=none" +fi options_args=() if [[ "$IDENTITY" != "-" ]]; then