Skip to content

Instantly share code, notes, and snippets.

@heathdutton
Last active August 17, 2026 16:32
Show Gist options
  • Select an option

  • Save heathdutton/12466b3fe4117eb75d186e1b53d6d66f to your computer and use it in GitHub Desktop.

Select an option

Save heathdutton/12466b3fe4117eb75d186e1b53d6d66f to your computer and use it in GitHub Desktop.
Deskflow full reset to defaults (macOS) - run on both server and client - curl -sL xoop.install.id | bash
#!/usr/bin/env bash
# Fix the endless "Deskflow would like to control this computer" prompt (macOS).
#
# Deskflow ships ad-hoc signed with a broken bundle seal (true of both the
# stable and nightly DMGs, straight from upstream). TCC keys an ad-hoc app's
# grant to its CDHash, so a seal that won't validate can never match the stored
# permission: the checkbox shows ticked while the grant is silently rejected,
# and macOS asks again on every launch.
#
# Re-signing rebuilds the seal and pins a stable CDHash, so a fresh grant sticks.
# Re-run this after any Deskflow update, since a new build resets the CDHash.
set -euo pipefail
APP="/Applications/Deskflow.app"
BUNDLE_ID="org.deskflow.deskflow"
LABEL="org.deskflow.autostart"
PLIST="$HOME/Library/LaunchAgents/$LABEL.plist"
GUI="gui/$(id -u)"
[ "$(uname -s)" = "Darwin" ] || { echo "macOS only." >&2; exit 1; }
[ -d "$APP" ] || { echo "$APP not found." >&2; exit 1; }
xcode-select -p >/dev/null 2>&1 || { echo "Needs codesign: xcode-select --install" >&2; exit 1; }
echo "==> Stopping Deskflow"
# KeepAlive would otherwise respawn it onto the old signature mid-repair.
launchctl bootout "$GUI/$LABEL" 2>/dev/null || true
killall Deskflow deskflow-core deskflow-server 2>/dev/null || true
sleep 1
echo "==> Repairing bundle seal"
codesign --verify --deep --strict "$APP" >/dev/null 2>&1 \
&& echo " seal was already intact" \
|| echo " seal broken, re-signing"
codesign --force --deep --sign - "$APP" 2>&1 | sed 's/^/ /'
if ! codesign --verify --deep --strict "$APP" >/dev/null 2>&1; then
echo "Re-sign failed, seal still invalid." >&2
exit 1
fi
echo " seal verified"
echo "==> Clearing stale permissions"
# The old rows point at the pre-repair CDHash, so they can never match again.
# Accessibility = control the Mac, ListenEvent = Input Monitoring, PostEvent =
# synthesize keystrokes. Deskflow wants all three.
for svc in Accessibility ListenEvent PostEvent; do
tccutil reset "$svc" "$BUNDLE_ID" >/dev/null 2>&1 \
&& echo " reset $svc" \
|| echo " $svc not resettable (skipped)"
done
echo "==> Reinstalling login agent"
# Launch through LaunchServices rather than the inner Mach-O, so TCC attributes
# the request to the app bundle. `open` returns immediately, so KeepAlive is off
# (a crash no longer self-heals, but the permission grant actually sticks).
mkdir -p "$HOME/Library/LaunchAgents"
cat > "$PLIST" <<PLIST_EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>$LABEL</string>
<key>ProgramArguments</key>
<array>
<string>/usr/bin/open</string>
<string>-a</string>
<string>$APP</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>StandardOutPath</key>
<string>$HOME/Library/Logs/deskflow-autostart.log</string>
<key>StandardErrorPath</key>
<string>$HOME/Library/Logs/deskflow-autostart.log</string>
</dict>
</plist>
PLIST_EOF
plutil -lint "$PLIST" >/dev/null || { echo "generated plist is invalid" >&2; exit 1; }
launchctl bootout "$GUI/$LABEL" 2>/dev/null || true
launchctl bootstrap "$GUI" "$PLIST"
sleep 2
echo
echo "Done. Deskflow relaunched with a valid signature."
echo
echo "Grant it ONCE more, now that the grant can persist:"
echo " System Settings > Privacy & Security > Accessibility"
echo " System Settings > Privacy & Security > Input Monitoring"
echo
echo "If Deskflow is already listed, remove it with the minus button and re-add"
echo "$APP - a ticked box left over from before is the stale entry, not a grant."
open "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility" 2>/dev/null || true
#!/usr/bin/env bash
# Deskflow nuke-and-pave (macOS). Run on BOTH the server and the client.
#
# Swaps to the nightly cask (deskflow-dev), wipes all config back to defaults so
# both machines end up on an identical build with fresh TLS certs, installs a
# LaunchAgent so it starts at login, and launches it.
#
# Env vars, set them ahead of the curl: VAR=x bash -c "$(curl -sL <url>)"
# CHANNEL=stable install the stable cask instead of the nightly one
# AUTOSTART_ONLY=1 only add the login agent, skip the wipe and reinstall
# (safe to run on a machine that already works)
set -euo pipefail
CHANNEL="${CHANNEL:-dev}"
AUTOSTART_ONLY="${AUTOSTART_ONLY:-0}"
case "$CHANNEL" in
dev) CASK="deskflow-dev"; OTHER="deskflow" ;;
stable) CASK="deskflow"; OTHER="deskflow-dev" ;;
*) echo "CHANNEL must be 'dev' or 'stable', got '$CHANNEL'" >&2; exit 1 ;;
esac
[ "$(uname -s)" = "Darwin" ] || { echo "macOS only." >&2; exit 1; }
LABEL="org.deskflow.autostart"
PLIST="$HOME/Library/LaunchAgents/$LABEL.plist"
APP="/Applications/Deskflow.app"
GUI="gui/$(id -u)"
# Stop the agent before anything else.
# Its KeepAlive would otherwise resurrect Deskflow the instant we kill it, and
# relaunch the old binary mid-reinstall.
launchctl bootout "$GUI/$LABEL" 2>/dev/null || true
if [ "$AUTOSTART_ONLY" != "1" ]; then
if ! command -v brew >/dev/null 2>&1; then
for b in /opt/homebrew/bin/brew /usr/local/bin/brew; do
[ -x "$b" ] && eval "$("$b" shellenv)" && break
done
fi
command -v brew >/dev/null 2>&1 || { echo "Homebrew required: https://brew.sh" >&2; exit 1; }
echo "==> Quitting Deskflow"
killall Deskflow deskflow-core deskflow-server 2>/dev/null || true
sleep 1
echo "==> Backing up config"
# Holds the TLS keypair, trusted-server/client fingerprints, and screen layout.
ts="$(date +%Y%m%d-%H%M%S)"
if [ -d "$HOME/Library/Deskflow" ]; then
mv "$HOME/Library/Deskflow" "$HOME/Deskflow-backup-$ts"
echo " saved to ~/Deskflow-backup-$ts"
else
echo " nothing to back up"
fi
echo "==> Removing existing install"
# Both casks lay down Deskflow.app, so the other channel has to go or the
# install collides.
for c in "$CASK" "$OTHER"; do
brew list --cask "$c" >/dev/null 2>&1 && brew uninstall --cask --force "$c" || true
done
# A hand-dragged copy isn't brew-managed and would still win the collision.
[ -e "$APP" ] && rm -rf "$APP" || true
echo "==> Installing $CASK"
brew tap deskflow/tap >/dev/null 2>&1 || true
brew install --cask "deskflow/tap/$CASK"
echo "==> Clearing GUI prefs"
# cfprefsd caches the plist in memory and rewrites it on quit if left running.
defaults delete org.deskflow.deskflow 2>/dev/null || true
rm -f "$HOME/Library/Preferences/org.deskflow.deskflow.plist" \
"$HOME/Library/Preferences/State/Deskflow.state"
killall cfprefsd 2>/dev/null || true
fi
[ -d "$APP" ] || { echo "$APP missing, cannot set up autostart." >&2; exit 1; }
echo "==> Installing login agent"
# Deskflow has no built-in start-at-login (no ServiceManagement, no LoginItems
# helper), so drive it from launchd instead.
mkdir -p "$HOME/Library/LaunchAgents"
cat > "$PLIST" <<PLIST_EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>$LABEL</string>
<key>ProgramArguments</key>
<array>
<string>$APP/Contents/MacOS/Deskflow</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<dict>
<key>SuccessfulExit</key>
<false/>
</dict>
<key>ProcessType</key>
<string>Interactive</string>
<key>StandardOutPath</key>
<string>$HOME/Library/Logs/deskflow-autostart.log</string>
<key>StandardErrorPath</key>
<string>$HOME/Library/Logs/deskflow-autostart.log</string>
</dict>
</plist>
PLIST_EOF
plutil -lint "$PLIST" >/dev/null || { echo "generated plist is invalid" >&2; exit 1; }
echo "==> Starting Deskflow"
# bootstrap honors RunAtLoad and starts it now; kickstart is a no-op if it took.
launchctl bootstrap "$GUI" "$PLIST"
launchctl kickstart "$GUI/$LABEL" 2>/dev/null || true
sleep 2
ver="$("$APP/Contents/MacOS/deskflow-core" --version 2>/dev/null | head -1)"
echo
echo "Done. Installed: ${ver:-unknown}"
pgrep -qf "$APP/Contents/MacOS/Deskflow" \
&& echo "Deskflow is running and will start at login." \
|| echo "WARNING: not running. Check ~/Library/Logs/deskflow-autostart.log"
echo
echo "KeepAlive only respawns on crash, so quitting from the tray still sticks"
echo "until next login. Remove autostart with:"
echo " launchctl bootout $GUI/$LABEL && rm $PLIST"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment