Files
clawdbot/docs/mac/xpc.md
2025-12-13 16:56:22 +00:00

3.6 KiB

summary, read_when
summary read_when
macOS IPC architecture for Clawdis app, CLI helper, and gateway bridge (control socket + XPC + PeekabooBridge)
Editing IPC contracts or menu bar app IPC

Clawdis macOS IPC architecture (Dec 2025)

Note: the current implementation primarily uses a local UNIX-domain control socket (controlSocketPath) between clawdis-mac and the app. This doc captures the intended long-term Mach/XPC direction and the security constraints, and also documents the separate PeekabooBridge socket used for UI automation.

Goals

  • Single GUI app instance that owns all TCC-facing work (notifications, screen recording, mic, speech, AppleScript).
  • A small surface for automation: the clawdis-mac CLI and the Node gateway talk to the app via local IPC.
  • Predictable permissions: always the same signed bundle ID, launched by launchd, so TCC grants stick.
  • Limit who can connect: only signed clients from our team (with an explicit DEBUG-only escape hatch for development).

How it works

Control socket (current)

  • clawdis-mac talks to the app via a local UNIX socket (controlSocketPath) for app-specific requests (notify, status, ensure-permissions, run, etc.).

PeekabooBridge (UI automation)

  • UI automation uses a separate UNIX socket named bridge.sock and the PeekabooBridge JSON protocol.
  • Host preference order (client-side): Peekaboo.app → Clawdis.app → local execution.
  • Security: bridge hosts require TeamID Y5PE65HELJ; DEBUG-only same-UID escape hatch is guarded by PEEKABOO_ALLOW_UNSIGNED_SOCKET_CLIENTS=1 (Peekaboo convention).
  • See: docs/mac/peekaboo.md for the Clawdis plan and naming.

Mach/XPC (future direction)

  • The app registers a Mach service named com.steipete.clawdis.xpc via a user LaunchAgent at ~/Library/LaunchAgents/com.steipete.clawdis.plist.
  • The launch agent runs dist/Clawdis.app/Contents/MacOS/Clawdis with RunAtLoad=true, KeepAlive=false, and a MachServices entry for the XPC name.
  • The app hosts the XPC listener (NSXPCListener(machServiceName:)) and exports ClawdisXPCService.
  • The CLI (clawdis-mac) connects with NSXPCConnection(machServiceName:); the Node gateway shells out to the CLI.
  • Security: on incoming connections we read the audit token (or PID) and allow only:
    • Code-signed clients with team ID Y5PE65HELJ.
    • In DEBUG builds only, you can opt into allowing same-UID clients by setting CLAWDIS_ALLOW_UNSIGNED_SOCKET_CLIENTS=1.

Operational flows

  • Restart/rebuild: SIGN_IDENTITY="Apple Development: Peter Steinberger (2ZAC4GM7GD)" scripts/restart-mac.sh
    • Kills existing instances
    • Swift build + package
    • Writes/bootstraps/kickstarts the LaunchAgent
  • CLI version: clawdis-mac --version (pulled from package.json during packaging)
  • Single instance: app exits early if another instance with the same bundle ID is running.

Why launchd (not anonymous endpoints)

  • A Mach service avoids brittle endpoint handoffs and lets the CLI/Node connect even if the app was started by launchd.
  • RunAtLoad without KeepAlive means the app starts once; if it crashes it stays down (no unwanted respawn), but CLI calls will re-spawn via launchd.

Hardening notes

  • Prefer requiring a TeamID match for all privileged surfaces.
    • Clawdis control socket: CLAWDIS_ALLOW_UNSIGNED_SOCKET_CLIENTS=1 (DEBUG-only) may allow same-UID callers for local development.
    • PeekabooBridge: PEEKABOO_ALLOW_UNSIGNED_SOCKET_CLIENTS=1 (DEBUG-only) may allow same-UID callers for local development.
  • All communication remains local-only; no network sockets are exposed.
  • TCC prompts originate only from the GUI app bundle; run scripts/package-mac-app.sh so the signed bundle ID stays stable.