From cb213b55f6e3819a577d137d96303981a97a7a2b Mon Sep 17 00:00:00 2001 From: Jefferson Warrior Date: Fri, 9 Jan 2026 09:05:46 -0600 Subject: [PATCH 1/2] feat: add auto-signing detection to restart-mac.sh --- scripts/restart-mac.sh | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/scripts/restart-mac.sh b/scripts/restart-mac.sh index 84a4f71b7..a22bc0608 100755 --- a/scripts/restart-mac.sh +++ b/scripts/restart-mac.sh @@ -15,6 +15,9 @@ LOCK_DIR="${TMPDIR:-/tmp}/clawdbot-restart-${LOCK_KEY}" LOCK_PID_FILE="${LOCK_DIR}/pid" WAIT_FOR_LOCK=0 LOG_PATH="${CLAWDBOT_RESTART_LOG:-/tmp/clawdbot-restart.log}" +NO_SIGN=0 +SIGN=0 +AUTO_DETECT_SIGNING=1 log() { printf '%s\n' "$*"; } fail() { printf 'ERROR: %s\n' "$*" >&2; exit 1; } @@ -64,13 +67,31 @@ acquire_lock() { done } +check_signing_keys() { + local available_identities + available_identities="$(security find-identity -p codesigning -v 2>/dev/null | grep -E '(Developer ID Application|Apple Distribution|Apple Development)' || true)" + + if [ -n "$available_identities" ]; then + return 0 + else + return 1 + fi +} + trap cleanup EXIT INT TERM for arg in "$@"; do case "${arg}" in --wait|-w) WAIT_FOR_LOCK=1 ;; + --no-sign) NO_SIGN=1; AUTO_DETECT_SIGNING=0 ;; + --sign) SIGN=1; AUTO_DETECT_SIGNING=0 ;; --help|-h) - log "Usage: $(basename "$0") [--wait]" + log "Usage: $(basename "$0") [--wait] [--no-sign] [--sign]" + log " --wait Wait for other restart to complete instead of exiting" + log " --no-sign Force no code signing (fastest for development)" + log " --sign Force code signing (will fail if no signing key available)" + log "" + log "Default behavior: Auto-detect signing keys, fallback to --no-sign if none found" exit 0 ;; *) ;; @@ -118,6 +139,24 @@ run_step "bundle canvas a2ui" bash -lc "cd '${ROOT_DIR}' && pnpm canvas:a2ui:bun run_step "clean build cache" bash -lc "cd '${ROOT_DIR}/apps/macos' && rm -rf .build .build-swift .swiftpm 2>/dev/null || true" run_step "swift build" bash -lc "cd '${ROOT_DIR}/apps/macos' && swift build -q --product Clawdbot" +if [ "$AUTO_DETECT_SIGNING" -eq 1 ]; then + if check_signing_keys; then + log "==> Signing keys detected, will code sign" + SIGN=1 + else + log "==> No signing keys found, will skip code signing (--no-sign)" + NO_SIGN=1 + fi +fi + +if [ "$NO_SIGN" -eq 1 ]; then + export ALLOW_ADHOC_SIGNING=1 + export SIGN_IDENTITY="-" +elif [ "$SIGN" -eq 1 ]; then + unset ALLOW_ADHOC_SIGNING + unset SIGN_IDENTITY +fi + # 3) Package app (default to bundling the embedded gateway + CLI). run_step "package app" bash -lc "cd '${ROOT_DIR}' && SKIP_TSC=${SKIP_TSC:-1} SKIP_GATEWAY_PACKAGE=${SKIP_GATEWAY_PACKAGE:-0} '${ROOT_DIR}/scripts/package-mac-app.sh'" From 317e15c746c996354ff707d4987afa8032722b89 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 10 Jan 2026 23:48:33 +0100 Subject: [PATCH 2/2] fix: harden restart-mac signing (#580) (thanks @jeffersonwarrior) --- CHANGELOG.md | 1 + scripts/package-mac-app.sh | 2 +- scripts/restart-mac.sh | 17 +++++++++-------- src/cli/update-cli.test.ts | 16 +++++++--------- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d285cdc6..8b45a3066 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,7 @@ - Gateway/Control UI: make `chat.send` non-blocking, wire Stop to `chat.abort`, and treat `/stop` as an out-of-band abort. (#653) - Gateway/Control UI: allow `chat.abort` without `runId` (abort active runs), suppress post-abort chat streaming, and prune stuck chat runs. (#653) - Gateway/Control UI: sniff image attachments for chat.send, drop non-images, and log mismatches. (#670) — thanks @cristip73. +- macOS: force `restart-mac.sh --sign` to require identities and keep bundled Node signed for relay verification. (#580) — thanks @jeffersonwarrior. - Gateway/Agent: accept image attachments on `agent` (multimodal message) and add live gateway image probe (`CLAWDBOT_LIVE_GATEWAY_IMAGE_PROBE=1`). - CLI: `clawdbot sessions` now includes `elev:*` + `usage:*` flags in the table output. - CLI/Pairing: accept positional provider for `pairing list|approve` (npm-run compatible); update docs/bot hints. diff --git a/scripts/package-mac-app.sh b/scripts/package-mac-app.sh index 6671bffd5..d07859d1e 100755 --- a/scripts/package-mac-app.sh +++ b/scripts/package-mac-app.sh @@ -294,7 +294,7 @@ package_relay_node() { cp "$relay_node_build_dir/node-${BUILD_ARCHS[0]}" "$relay_node" fi chmod +x "$relay_node" - if [[ "${STRIP_NODE:-1}" == "1" ]]; then + if [[ "${STRIP_NODE:-0}" == "1" ]]; then /usr/bin/strip -x "$relay_node" 2>/dev/null || true fi rm -rf "$relay_node_build_dir" diff --git a/scripts/restart-mac.sh b/scripts/restart-mac.sh index a22bc0608..dc21d1699 100755 --- a/scripts/restart-mac.sh +++ b/scripts/restart-mac.sh @@ -68,14 +68,8 @@ acquire_lock() { } check_signing_keys() { - local available_identities - available_identities="$(security find-identity -p codesigning -v 2>/dev/null | grep -E '(Developer ID Application|Apple Distribution|Apple Development)' || true)" - - if [ -n "$available_identities" ]; then - return 0 - else - return 1 - fi + security find-identity -p codesigning -v 2>/dev/null \ + | grep -Eq '(Developer ID Application|Apple Distribution|Apple Development)' } trap cleanup EXIT INT TERM @@ -98,6 +92,10 @@ for arg in "$@"; do esac done +if [[ "$NO_SIGN" -eq 1 && "$SIGN" -eq 1 ]]; then + fail "Cannot use --sign and --no-sign together" +fi + mkdir -p "$(dirname "$LOG_PATH")" rm -f "$LOG_PATH" exec > >(tee "$LOG_PATH") 2>&1 @@ -153,6 +151,9 @@ if [ "$NO_SIGN" -eq 1 ]; then export ALLOW_ADHOC_SIGNING=1 export SIGN_IDENTITY="-" elif [ "$SIGN" -eq 1 ]; then + if ! check_signing_keys; then + fail "No signing identity found. Use --no-sign or install a signing key." + fi unset ALLOW_ADHOC_SIGNING unset SIGN_IDENTITY fi diff --git a/src/cli/update-cli.test.ts b/src/cli/update-cli.test.ts index 8be638001..46cae63ad 100644 --- a/src/cli/update-cli.test.ts +++ b/src/cli/update-cli.test.ts @@ -26,15 +26,13 @@ vi.mock("../runtime.js", () => ({ })); describe("update-cli", () => { - it( - "exports updateCommand and registerUpdateCli", - async () => { - const { updateCommand, registerUpdateCli } = await import("./update-cli.js"); - expect(typeof updateCommand).toBe("function"); - expect(typeof registerUpdateCli).toBe("function"); - }, - 20_000, - ); + it("exports updateCommand and registerUpdateCli", async () => { + const { updateCommand, registerUpdateCli } = await import( + "./update-cli.js" + ); + expect(typeof updateCommand).toBe("function"); + expect(typeof registerUpdateCli).toBe("function"); + }, 20_000); it("updateCommand runs update and outputs result", async () => { const { runGatewayUpdate } = await import("../infra/update-runner.js");