From b2e3013898db03c4bc3afe40d32f96a2dabf6256 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 7 Dec 2025 00:30:40 +0100 Subject: [PATCH] mac: add signing helper and document debug bundle --- docs/mac/signing.md | 16 +++++++++-- scripts/codesign-mac-app.sh | 55 +++++++++++++++++++++++++++++++++++++ scripts/package-mac-app.sh | 16 ++++++----- 3 files changed, 77 insertions(+), 10 deletions(-) create mode 100755 scripts/codesign-mac-app.sh diff --git a/docs/mac/signing.md b/docs/mac/signing.md index 91797d514..e717a9aa2 100644 --- a/docs/mac/signing.md +++ b/docs/mac/signing.md @@ -4,13 +4,15 @@ This app is usually built from `scripts/package-mac-app.sh`, which now: - sets a stable debug bundle identifier: `com.steipete.clawdis.debug` - writes the Info.plist with that bundle id (override via `BUNDLE_ID=...`) -- ad‑hoc signs the main binary, the bundled CLI, and the app bundle so macOS treats each rebuild as the same signed bundle and keeps TCC permissions (notifications, accessibility, screen recording, mic, speech) +- 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). Defaults to ad‑hoc; set `SIGN_IDENTITY="Developer ID Application: …"` to use a real cert. +- injects build metadata into Info.plist: `ClawdisBuildTimestamp` (UTC) and `ClawdisGitCommit` (short hash) so the About pane can show build, git, and debug/release channel. ## Usage ```bash # from repo root -scripts/package-mac-app.sh +scripts/package-mac-app.sh # ad-hoc signing +SIGN_IDENTITY="Developer ID Application: Your Name" scripts/package-mac-app.sh # real cert ``` If you need a different bundle id (e.g. release build): @@ -19,6 +21,14 @@ If you need a different bundle id (e.g. release build): BUNDLE_ID=com.steipete.clawdis scripts/package-mac-app.sh ``` +## Build metadata for About + +`package-mac-app.sh` stamps the bundle with: +- `ClawdisBuildTimestamp`: ISO8601 UTC at package time +- `ClawdisGitCommit`: short git hash (or `unknown` if unavailable) + +The About tab reads these keys to show version, build date, git commit, and whether it’s a debug build (via `#if DEBUG`). Run the packager to refresh these values after code changes. + ## Why -TCC permissions are tied to the bundle identifier *and* code signature. Unsigned debug builds with changing UUIDs were causing macOS to forget grants after each rebuild. Ad‑hoc signing the binaries and keeping a fixed bundle id/path (`dist/Clawdis.app`) preserves the grants between builds, matching the VibeTunnel approach. +TCC permissions are tied to the bundle identifier *and* code signature. Unsigned debug builds with changing UUIDs were causing macOS to forget grants after each rebuild. Signing the binaries (ad‑hoc by default) and keeping a fixed bundle id/path (`dist/Clawdis.app`) preserves the grants between builds, matching the VibeTunnel approach. diff --git a/scripts/codesign-mac-app.sh b/scripts/codesign-mac-app.sh new file mode 100755 index 000000000..2580c0395 --- /dev/null +++ b/scripts/codesign-mac-app.sh @@ -0,0 +1,55 @@ +#!/usr/bin/env bash +set -euo pipefail + +APP_BUNDLE="${1:-dist/Clawdis.app}" +IDENTITY="${SIGN_IDENTITY:--}" +ENT_TMP=$(mktemp /tmp/clawdis-entitlements.XXXXXX.plist) + +if [ ! -d "$APP_BUNDLE" ]; then + echo "App bundle not found: $APP_BUNDLE" >&2 + exit 1 +fi + +echo "Using signing identity: $IDENTITY" + +cat > "$ENT_TMP" <<'PLIST' + + + + + com.apple.security.hardened-runtime + + com.apple.security.automation.apple-events + + + +PLIST + +# clear extended attributes to avoid stale signatures +xattr -cr "$APP_BUNDLE" 2>/dev/null || true + +sign_item() { + local target="$1" + codesign --force --options runtime --timestamp=none --entitlements "$ENT_TMP" --sign "$IDENTITY" "$target" +} + +# Sign main binary and CLI helper if present +if [ -f "$APP_BUNDLE/Contents/MacOS/Clawdis" ]; then + echo "Signing main binary"; sign_item "$APP_BUNDLE/Contents/MacOS/Clawdis" +fi +if [ -f "$APP_BUNDLE/Contents/MacOS/ClawdisCLI" ]; then + echo "Signing CLI helper"; sign_item "$APP_BUNDLE/Contents/MacOS/ClawdisCLI" +fi + +# Sign any embedded frameworks/dylibs if they ever appear +if [ -d "$APP_BUNDLE/Contents/Frameworks" ]; then + find "$APP_BUNDLE/Contents/Frameworks" \( -name "*.framework" -o -name "*.dylib" \) -print0 | while IFS= read -r -d '' f; do + echo "Signing framework: $f"; sign_item "$f" + done +fi + +# Finally sign the bundle +sign_item "$APP_BUNDLE" + +rm -f "$ENT_TMP" +echo "Codesign complete for $APP_BUNDLE" diff --git a/scripts/package-mac-app.sh b/scripts/package-mac-app.sh index 20ed25489..f3e8adf3d 100755 --- a/scripts/package-mac-app.sh +++ b/scripts/package-mac-app.sh @@ -8,7 +8,9 @@ ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)" APP_ROOT="$ROOT_DIR/dist/Clawdis.app" BUILD_PATH="$ROOT_DIR/apps/macos/.build" PRODUCT="Clawdis" -BUNDLE_ID="com.steipete.clawdis.debug" +BUNDLE_ID="${BUNDLE_ID:-com.steipete.clawdis.debug}" +BUILD_TS=$(date -u +"%Y-%m-%dT%H:%M:%SZ") +GIT_COMMIT=$(cd "$ROOT_DIR" && git rev-parse --short HEAD 2>/dev/null || echo "unknown") cd "$ROOT_DIR/apps/macos" @@ -42,6 +44,10 @@ cat > "$APP_ROOT/Contents/Info.plist" <15.0 LSUIElement + ClawdisBuildTimestamp + ${BUILD_TS} + ClawdisGitCommit + ${GIT_COMMIT} NSUserNotificationUsageDescription Clawdis needs notification permission to show alerts for agent actions. NSScreenCaptureDescription @@ -70,12 +76,8 @@ fi echo "⏹ Stopping any running Clawdis" killall -q Clawdis 2>/dev/null || true -echo "🔏 Ad-hoc signing binaries for stable TCC permissions" -codesign --force --options runtime --timestamp=none --sign - "$APP_ROOT/Contents/MacOS/Clawdis" -if [ -f "$APP_ROOT/Contents/MacOS/ClawdisCLI" ]; then - codesign --force --options runtime --timestamp=none --sign - "$APP_ROOT/Contents/MacOS/ClawdisCLI" -fi -codesign --force --options runtime --timestamp=none --sign - "$APP_ROOT" +echo "🔏 Signing bundle (ad-hoc by default; set SIGN_IDENTITY for real cert)" +SIGN_IDENTITY="${SIGN_IDENTITY:--}" "$ROOT_DIR/scripts/codesign-mac-app.sh" "$APP_ROOT" echo "✅ Bundle ready at $APP_ROOT"