macOS App
OpenClaw macOS companion app (menu bar + Gateway broker).
The macOS app is OpenClaw's menu bar companion. It handles permissions, manages/connects to a local Gateway (launchd or manual), and exposes macOS capabilities to the Agent as a node.
What it can do
- Show native notifications and status in the menu bar.
- Handle TCC authorization prompts (notifications, accessibility, screen recording, microphone, speech recognition, automation/AppleScript).
- Run or connect to Gateway (local or remote).
- Expose macOS-specific tools (Canvas, Camera, Screen Recording, ''system.run'').
- Start local node host service (launchd) in remote mode, stop it in local mode.
- Optionally host PeekabooBridge for UI automation.
- Install global CLI (''openclaw'') via npm/pnpm if needed (not recommended to use bun as Gateway runtime).
Local mode vs Remote mode
- ''Local (default)'': If a running local Gateway is detected, connect directly; otherwise enable launchd service via ''openclaw gateway install''.
- Remote: App connects to a remote Gateway via SSH/Tailscale and does not start gateway process locally.
In this mode, the app starts the local node host service so the remote Gateway can connect to this Mac.
The app does not spawn Gateway as a subprocess.
Launchd control
The app manages a per-user LaunchAgent with label ''bot.molt.gateway''
(''bot.molt.<profile>'' when using ''--profile''/''OPENCLAW_PROFILE''; legacy ''com.openclaw.*'' will be uninstalled).
If using a named profile, replace the label with ''bot.molt.<profile>''.
If LaunchAgent is not installed, you can enable it in the app or run ''openclaw gateway install''.
launchctl kickstart -k gui/$UID/bot.molt.gateway launchctl bootout gui/$UID/bot.molt.gateway
PlatformsMacosPage step 03: P5
PlatformsMacosPage step 03: P6
Node capabilities (mac)
The macOS app connects as a node. Common commands:
- Canvas: ''canvas.present'', ''canvas.navigate'', ''canvas.eval'', ''canvas.snapshot'', ''canvas.a2ui.*''
- Camera: ''camera.snap'', ''camera.clip''
- Screen: ''screen.record''
- System: ''system.run'', ''system.notify''
The node reports a ''permissions'' map so the Agent can determine which capabilities are allowed.
Node service vs app IPC:
- When the headless node host service runs (remote mode), it connects to Gateway as a node via WS.
- ''system.run'' executes in the macOS app (UI/TCC context), communicating with the app over a local Unix socket; prompts and output stay in the app.
Diagram (SCI):
Exec approvals (system.run)
''system.run'' is controlled by ''Exec approvals'' in the macOS app (Settings → Exec approvals). Security policy, ask policy, and allowlist are stored locally:
~/.openclaw/exec-approvals.json
Example:
{
"version": 1,
"defaults": {
"security": "deny",
"ask": "on-miss"
},
"agents": {
"main": {
"security": "allowlist",
"ask": "on-miss",
"allowlist": [{ "pattern": "/opt/homebrew/bin/rg" }]
}
}
}Notes:
- ''allowlist'' entries are glob matches against resolved executable paths.
- Choosing "Always Allow" in the prompt adds that command to the allowlist.
''system.run'' environment variable overrides are filtered (dropping ''PATH'', ''DYLD_*'', ''LD_*'', ''NODE_OPTIONS'', ''PYTHON*'', ''PERL*'', ''RUBYOPT''), then merged with app environment.
Deep links
The app registers the ''openclaw://'' URL scheme for local actions.
#
`openclaw://agent`
Trigger a Gateway ''agent'' request:
open 'openclaw://agent?message=Hello%20from%20deep%20link'
Query parameters:
- ''message'' (required)
- ''sessionKey'' (optional)
- ''thinking'' (optional)
- ''deliver'' / ''to'' / ''channel'' (optional)
- ''timeoutSeconds'' (optional)
- ''key'' (optional: unattended mode key)
Security:
- Without ''key'', the app shows a confirmation dialog.
- With valid ''key'', runs unattended (for personal automation).
Typical onboarding flow
1. Install and launch OpenClaw.app.
2. Complete the permissions checklist (TCC authorization prompts).
3. Ensure local mode is enabled and Gateway is running.
4. If terminal access is needed, install the CLI.
Building & development (native)
- ''cd apps/macos && swift build''
- ''swift run OpenClaw'' (or use Xcode)
- Package: ''scripts/package-mac-app.sh''
Troubleshooting gateway connection (macOS CLI)
Use the debug CLI to reproduce the Gateway WebSocket handshake and discovery logic used by the macOS app without launching the app:
cd apps/macos swift run openclaw-mac connect --json swift run openclaw-mac discover --timeout 3000 --json
Connect parameters:
- ''--url <ws://host:port>'': override config
- ''--mode <local|remote>'': resolve from config (default: per-config or local)
- ''--probe'': force a fresh health probe
- ''--timeout <ms>'': request timeout (default ''15000'')
- ''--json'': structured output for diff
Discovery parameters:
- ''--include-local'': include gateways that would otherwise be filtered as "local"
- ''--timeout <ms>'': overall discovery window (default ''2000'')
- ''--json'': structured output
Tip: Compare with ''openclaw gateway discover --json'' to see if the macOS app's discovery pipeline (NWBrowser + tailnet DNS‑SD fallback) differs from the Node CLI's ''dns-sd'' discovery.
Remote connection details (SSH tunnel)
When the macOS app runs in remote mode, it opens SSH tunnels so local UI components can access the remote Gateway as if it were localhost.
#
Control tunnel (Gateway WebSocket port)
- Purpose: health checks, status, Web Chat, config, and other control plane calls
- ''Local port:'' Gateway port (default ''18789''), fixed
- Remote port: same Gateway port on remote host
- Behavior: does not use random local ports; app reuses a healthy tunnel or restarts as needed
- ''SSH form:'' ''ssh -N -L <local>:127.0.0.1:<remote>'', with BatchMode, ExitOnForwardFailure, keepalive enabled
- ''IP observation:'' SSH tunnel goes over loopback, so gateway sees node IP as ''127.0.0.1''. If you want the real client IP shown, use ''Direct (ws/wss)'' transport (see ''macOS remote access'').
See ''macOS remote access'' for setup steps. See ''Gateway protocol'' for protocol details.