OpenClaw macOS IPC architecture
A local Unix socket connects the node host service to the macOS app for exec approvals andsystem.run. An openclaw-mac debug CLI (apps/macos/Sources/OpenClawMacCLI) exists for discovery/connect checks; agent actions still flow through the Gateway WebSocket and node.invoke. The node-backed computer.act path runs embedded Peekaboo automation in-process; standalone Peekaboo clients use PeekabooBridge.
Goals
- Single GUI app instance that owns all TCC-facing work (notifications, screen recording, mic, speech, AppleScript).
- A small surface for automation: Gateway + node commands, in-process
computer.act, plus PeekabooBridge for standalone UI automation clients. - Predictable permissions: always the same signed bundle ID, launched by launchd, so TCC grants stick.
How it works
Gateway + node transport
- The app runs the Gateway (local mode) and connects to it as a node.
- Agent actions are performed via
node.invoke(e.g.system.run,system.notify,canvas.present). - Node commands include
canvas.present,canvas.hide,canvas.navigate,camera.list,camera.snap,camera.clip,camera.ptz.status,camera.ptz.control,screen.snapshot,screen.record,computer.act,system.run, andsystem.notify. - The node reports a
permissionsmap so agents can see whether screen, camera, microphone, speech, automation, or accessibility access is available.
Node service + app IPC
- A headless node host service connects to the Gateway WebSocket.
system.runrequests are forwarded to the macOS app over a local Unix socket (ExecApprovalsSocket.swift).- The app performs the exec in UI context, prompts if needed, and returns output.
- The socket owns the request lifetime. Node cancellation or a socket deadline closes the response reader, cancelling the native prompt or command and its process group. Stopping the app’s exec server also cancels and drains active requests before releasing its socket lease.
- Clients still half-close their write side after sending one JSONL request. That normal request EOF does not cancel execution; the response reader remains open until the result arrives.
PeekabooBridge (UI automation)
- The built-in agent
computertool does not use this socket. A paired macOS node fulfillscomputer.actin the app process with embedded Peekaboo services. - UI automation uses a separate UNIX socket (
~/Library/Application Support/OpenClaw/<socket>) and the PeekabooBridge JSON protocol. - Host preference order (client-side): Peekaboo.app -> Claude.app -> OpenClaw.app -> local execution.
- Security: bridge hosts require the exact signed Peekaboo client bundle identifier and Peekaboo’s canonical
current/legacy release signer set; a DEBUG-only same-UID escape hatch is guarded by
PEEKABOO_ALLOW_UNSIGNED_SOCKET_CLIENTS=1(Peekaboo convention). - See: PeekabooBridge usage for details.
Operational flows
- Restart/rebuild:
scripts/restart-mac.shkills existing instances, rebuilds via Swift, repackages, and relaunches. It auto-detects an available signing identity and falls back to--no-signif none is found; pass--signto require signing (fails if no key is available) or--no-signto force the unsigned path. An explicitSIGN_IDENTITYis preserved through packaging; otherwisescripts/codesign-mac-app.shauto-detects the certificate. - Single instance: the app checks
NSWorkspace.runningApplicationsfor a duplicate bundle ID and exits if more than one instance is found (isDuplicateInstance()inMenuBar.swift).
Hardening notes
- Prefer requiring a TeamID match for all privileged surfaces.
- 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; keep the signed bundle ID stable across rebuilds.
- Exec approvals socket hardening: file mode
0600, shared token stored in theexec_approvals_configrow ofstate/openclaw.sqlite, peer-UID check (getpeereid), HMAC-SHA256 challenge/response, and a short TTL on requests.