Files
openclaw/docs/platforms/macos.md

205 lines
6.7 KiB
Markdown
Raw Normal View History

---
2026-01-30 03:15:10 +01:00
summary: "OpenClaw macOS companion app (menu bar + gateway broker)"
read_when:
- Implementing macOS app features
- Changing gateway lifecycle or node bridging on macOS
title: "macOS App"
---
2026-01-31 21:13:13 +09:00
2026-01-30 03:15:10 +01:00
# OpenClaw macOS Companion (menu bar + gateway broker)
2025-12-05 23:13:53 +01:00
2026-01-30 03:15:10 +01:00
The macOS app is the **menubar companion** for OpenClaw. It owns permissions,
manages/attaches to the Gateway locally (launchd or manual), and exposes macOS
capabilities to the agent as a node.
2025-12-05 23:13:53 +01:00
2026-01-08 23:06:56 +01:00
## What it does
2026-01-07 21:37:05 +01:00
2026-01-08 23:06:56 +01:00
- Shows native notifications and status in the menu bar.
- Owns TCC prompts (Notifications, Accessibility, Screen Recording, Microphone,
Speech Recognition, Automation/AppleScript).
- Runs or connects to the Gateway (local or remote).
- Exposes macOSonly tools (Canvas, Camera, Screen Recording, `system.run`).
- Starts the local node host service in **remote** mode (launchd), and stops it in **local** mode.
2026-01-08 23:06:56 +01:00
- Optionally hosts **PeekabooBridge** for UI automation.
2026-01-30 03:15:10 +01:00
- Installs the global CLI (`openclaw`) via npm/pnpm on request (bun not recommended for the Gateway runtime).
2026-01-08 23:06:56 +01:00
## Local vs remote mode
- **Local** (default): the app attaches to a running local Gateway if present;
2026-01-30 03:15:10 +01:00
otherwise it enables the launchd service via `openclaw gateway install`.
2026-01-08 23:06:56 +01:00
- **Remote**: the app connects to a Gateway over SSH/Tailscale and never starts
a local process.
The app starts the local **node host service** so the remote Gateway can reach this Mac.
2026-01-31 21:13:13 +09:00
The app does not spawn the Gateway as a child process.
2026-01-08 23:06:56 +01:00
## Launchd control
The app manages a peruser LaunchAgent labeled `bot.molt.gateway`
2026-01-30 03:15:10 +01:00
(or `bot.molt.<profile>` when using `--profile`/`OPENCLAW_PROFILE`; legacy `com.openclaw.*` still unloads).
2026-01-07 21:37:05 +01:00
```bash
launchctl kickstart -k gui/$UID/bot.molt.gateway
launchctl bootout gui/$UID/bot.molt.gateway
2026-01-07 21:37:05 +01:00
```
Replace the label with `bot.molt.<profile>` when running a named profile.
2026-01-08 23:06:56 +01:00
If the LaunchAgent isnt installed, enable it from the app or run
2026-01-30 03:15:10 +01:00
`openclaw gateway install`.
2026-01-08 23:06:56 +01:00
## Node capabilities (mac)
2026-01-08 23:06:56 +01:00
The macOS app presents itself as a node. Common commands:
2026-01-08 23:06:56 +01:00
- Canvas: `canvas.present`, `canvas.navigate`, `canvas.eval`, `canvas.snapshot`, `canvas.a2ui.*`
- Camera: `camera.snap`, `camera.clip`
- Screen: `screen.record`
- System: `system.run`, `system.notify`
2025-12-05 23:13:53 +01:00
2026-01-08 23:06:56 +01:00
The node reports a `permissions` map so agents can decide whats allowed.
Node service + app IPC:
2026-01-31 21:13:13 +09:00
2026-01-21 16:48:31 +00:00
- When the headless node host service is running (remote mode), it connects to the Gateway WS as a node.
- `system.run` executes in the macOS app (UI/TCC context) over a local Unix socket; prompts + output stay in-app.
Diagram (SCI):
2026-01-31 21:13:13 +09:00
```
Gateway -> Node Service (WS)
| IPC (UDS + token + HMAC + TTL)
v
Mac App (UI + TCC + system.run)
```
## Exec approvals (system.run)
`system.run` is controlled by **Exec approvals** in the macOS app (Settings → Exec approvals).
Security + ask + allowlist are stored locally on the Mac in:
```
2026-01-30 03:15:10 +01:00
~/.openclaw/exec-approvals.json
```
Example:
```json
{
"version": 1,
"defaults": {
"security": "deny",
"ask": "on-miss"
},
"agents": {
"main": {
"security": "allowlist",
"ask": "on-miss",
2026-01-31 21:13:13 +09:00
"allowlist": [{ "pattern": "/opt/homebrew/bin/rg" }]
}
}
}
```
Notes:
2026-01-31 21:13:13 +09:00
- `allowlist` entries are glob patterns for resolved binary paths.
- Choosing “Always Allow” in the prompt adds that command to the allowlist.
2026-01-16 05:28:33 +00:00
- `system.run` environment overrides are filtered (drops `PATH`, `DYLD_*`, `LD_*`, `NODE_OPTIONS`, `PYTHON*`, `PERL*`, `RUBYOPT`) and then merged with the apps environment.
2026-01-08 23:06:56 +01:00
## Deep links
2026-01-30 03:15:10 +01:00
The app registers the `openclaw://` URL scheme for local actions.
2026-01-30 03:15:10 +01:00
### `openclaw://agent`
2026-01-08 23:06:56 +01:00
Triggers a Gateway `agent` request.
```bash
2026-01-30 03:15:10 +01:00
open 'openclaw://agent?message=Hello%20from%20deep%20link'
```
Query parameters:
2026-01-31 21:13:13 +09:00
2026-01-08 23:06:56 +01:00
- `message` (required)
- `sessionKey` (optional)
- `thinking` (optional)
2026-01-13 07:15:57 +00:00
- `deliver` / `to` / `channel` (optional)
2026-01-08 23:06:56 +01:00
- `timeoutSeconds` (optional)
- `key` (optional unattended mode key)
Safety:
2026-01-31 21:13:13 +09:00
2026-01-08 23:06:56 +01:00
- Without `key`, the app prompts for confirmation.
2026-02-14 14:53:20 +01:00
- Without `key`, the app enforces a short message limit for the confirmation prompt and ignores `deliver` / `to` / `channel`.
2026-01-08 23:06:56 +01:00
- With a valid `key`, the run is unattended (intended for personal automations).
## Onboarding flow (typical)
2026-01-31 21:13:13 +09:00
1. Install and launch **OpenClaw.app**.
2. Complete the permissions checklist (TCC prompts).
3. Ensure **Local** mode is active and the Gateway is running.
4. Install the CLI if you want terminal access.
2025-12-05 23:13:53 +01:00
## Build & dev workflow (native)
2026-01-08 23:06:56 +01:00
- `cd apps/macos && swift build`
2026-01-30 03:15:10 +01:00
- `swift run OpenClaw` (or Xcode)
2026-01-11 10:15:37 +00:00
- Package app: `scripts/package-mac-app.sh`
2026-01-08 23:06:56 +01:00
2026-01-20 19:17:18 +00:00
## Debug gateway connectivity (macOS CLI)
2026-01-20 19:17:18 +00:00
Use the debug CLI to exercise the same Gateway WebSocket handshake and discovery
logic that the macOS app uses, without launching the app.
```bash
cd apps/macos
2026-01-30 03:15:10 +01:00
swift run openclaw-mac connect --json
swift run openclaw-mac discover --timeout 3000 --json
```
2026-01-20 19:17:18 +00:00
Connect options:
2026-01-31 21:13:13 +09:00
2026-01-20 19:17:18 +00:00
- `--url <ws://host:port>`: override config
- `--mode <local|remote>`: resolve from config (default: config or local)
- `--probe`: force a fresh health probe
- `--timeout <ms>`: request timeout (default: `15000`)
- `--json`: structured output for diffing
Discovery options:
2026-01-31 21:13:13 +09:00
- `--include-local`: include gateways that would be filtered as “local”
2026-01-20 19:17:18 +00:00
- `--timeout <ms>`: overall discovery window (default: `2000`)
- `--json`: structured output for diffing
2026-01-30 03:15:10 +01:00
Tip: compare against `openclaw gateway discover --json` to see whether the
macOS apps discovery pipeline (NWBrowser + tailnet DNSSD fallback) differs from
the Node CLIs `dns-sd` based discovery.
## Remote connection plumbing (SSH tunnels)
When the macOS app runs in **Remote** mode, it opens an SSH tunnel so local UI
components can talk to a remote Gateway as if it were on localhost.
### Control tunnel (Gateway WebSocket port)
2026-01-31 21:13:13 +09:00
- **Purpose:** health checks, status, Web Chat, config, and other control-plane calls.
- **Local port:** the Gateway port (default `18789`), always stable.
- **Remote port:** the same Gateway port on the remote host.
- **Behavior:** no random local port; the app reuses an existing healthy tunnel
or restarts it if needed.
- **SSH shape:** `ssh -N -L <local>:127.0.0.1:<remote>` with BatchMode +
ExitOnForwardFailure + keepalive options.
- **IP reporting:** the SSH tunnel uses loopback, so the gateway will see the node
IP as `127.0.0.1`. Use **Direct (ws/wss)** transport if you want the real client
IP to appear (see [macOS remote access](/platforms/mac/remote)).
For setup steps, see [macOS remote access](/platforms/mac/remote). For protocol
details, see [Gateway protocol](/gateway/protocol).
2026-01-08 23:06:56 +01:00
## Related docs
- [Gateway runbook](/gateway)
2026-01-11 10:15:37 +00:00
- [Gateway (macOS)](/platforms/mac/bundled-gateway)
2026-01-08 23:06:56 +01:00
- [macOS permissions](/platforms/mac/permissions)
- [Canvas](/platforms/mac/canvas)