OpenClawSkills
GitHub
Platforms • TutorialHeader.readTime

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.

Tutorial.step

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).

Tutorial.step

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.

Tutorial.step

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''.

Bash
launchctl kickstart -k gui/$UID/bot.molt.gateway
launchctl bootout gui/$UID/bot.molt.gateway

PlatformsMacosPage step 03: P5

PlatformsMacosPage step 03: P6

Tutorial.step

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):

Tutorial.step

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:

Terminal
~/.openclaw/exec-approvals.json

Example:

Json
{
  "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.

Tutorial.step

Deep links

The app registers the ''openclaw://'' URL scheme for local actions.

#

Tutorial.step

`openclaw://agent`

Trigger a Gateway ''agent'' request:

Bash
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).

Tutorial.step

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.

Tutorial.step

Building & development (native)

- ''cd apps/macos && swift build''

- ''swift run OpenClaw'' (or use Xcode)

- Package: ''scripts/package-mac-app.sh''

Tutorial.step

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:

Bash
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.

Tutorial.step

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.

#

Tutorial.step

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.

Tutorial.step

Related docs