From 7bbd09047bd7ce1ce573e0d434abcf72a76de1f6 Mon Sep 17 00:00:00 2001 From: Dallin Romney Date: Sat, 27 Jun 2026 16:04:30 -0700 Subject: [PATCH] docs: simplify macOS app overview (#97120) * docs: simplify macOS app docs * docs: preserve macOS app detail links * docs: address macOS review feedback --- docs/platforms/mac/bundled-gateway.md | 28 +++ docs/platforms/mac/canvas.md | 13 +- docs/platforms/mac/remote.md | 3 + docs/platforms/mac/xpc.md | 4 + docs/platforms/macos.md | 267 ++++++-------------------- 5 files changed, 110 insertions(+), 205 deletions(-) diff --git a/docs/platforms/mac/bundled-gateway.md b/docs/platforms/mac/bundled-gateway.md index c133d9dffca..d5e09484c27 100644 --- a/docs/platforms/mac/bundled-gateway.md +++ b/docs/platforms/mac/bundled-gateway.md @@ -57,6 +57,34 @@ Logging: The macOS app checks the gateway version against its own version. If they're incompatible, update the global CLI to match the app version. +## State directory on macOS + +Keep OpenClaw state on a local, non-synced disk. Avoid iCloud Drive and other +cloud-synced folders because sync latency and file locks can affect sessions, +credentials, and Gateway state. + +Set `OPENCLAW_STATE_DIR` to a local path only when you need an override. +`openclaw doctor` warns about common cloud-synced state paths and recommends +moving back to local storage. See +[environment variables](/help/environment#path-related-env-vars) and +[Doctor](/gateway/doctor). + +## Debug app connectivity + +Use the macOS debug CLI from a source checkout to exercise the same Gateway +WebSocket handshake and discovery logic the app uses: + +```bash +cd apps/macos +swift run openclaw-mac connect --json +swift run openclaw-mac discover --timeout 3000 --json +``` + +`connect` accepts `--url`, `--token`, `--timeout`, and `--json`. `discover` +accepts `--timeout`, `--json`, and `--include-local`. Compare discovery output +with `openclaw gateway discover --json` when you need to separate CLI discovery +from app-side connection issues. + ## Smoke check ```bash diff --git a/docs/platforms/mac/canvas.md b/docs/platforms/mac/canvas.md index d8404bd9262..bc1aad3dc8d 100644 --- a/docs/platforms/mac/canvas.md +++ b/docs/platforms/mac/canvas.md @@ -114,7 +114,18 @@ Example (in JS): window.location.href = "openclaw://agent?message=Review%20this%20design"; ``` -The app prompts for confirmation unless a valid key is provided. +Supported query parameters: + +- `message`: prefilled agent prompt. +- `sessionKey`: stable session identifier. +- `thinking`: optional thinking profile. +- `deliver`, `to`, or `channel`: delivery target. +- `timeoutSeconds`: optional run timeout. +- `key`: app-generated safety token for trusted local callers. + +The app prompts for confirmation unless a valid key is provided. Unkeyed links +show the message and URL before approval, and ignore delivery routing fields; +keyed links use the normal Gateway run path. ## Security notes diff --git a/docs/platforms/mac/remote.md b/docs/platforms/mac/remote.md index a9a448d6711..012e1d49f27 100644 --- a/docs/platforms/mac/remote.md +++ b/docs/platforms/mac/remote.md @@ -24,6 +24,9 @@ In SSH tunnel mode, discovered LAN/tailnet hostnames are saved as `gateway.remote.sshTarget`. The app keeps `gateway.remote.url` on the local tunnel endpoint, for example `ws://127.0.0.1:18789`, so CLI, Web Chat, and the local node-host service all use the same safe loopback transport. +When discovery returns both raw Tailnet IPs and stable hostnames, the app +prefers Tailscale MagicDNS or LAN names so remote connections survive address +changes better. If the local tunnel port differs from the remote gateway port, set `gateway.remote.remotePort` to the port on the remote host. diff --git a/docs/platforms/mac/xpc.md b/docs/platforms/mac/xpc.md index 8bed518d4dc..a27caf1c422 100644 --- a/docs/platforms/mac/xpc.md +++ b/docs/platforms/mac/xpc.md @@ -21,6 +21,10 @@ title: "macOS IPC" - 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.*`). +- Common Mac node commands include `canvas.*`, `camera.snap`, `camera.clip`, + `screen.snapshot`, `screen.record`, `system.run`, and `system.notify`. +- The node reports a `permissions` map so agents can see whether screen, + camera, microphone, speech, automation, or accessibility access is available. ### Node service + app IPC diff --git a/docs/platforms/macos.md b/docs/platforms/macos.md index 6ad13fc1977..fe965af080c 100644 --- a/docs/platforms/macos.md +++ b/docs/platforms/macos.md @@ -1,228 +1,87 @@ --- -summary: "OpenClaw macOS companion app (menu bar + gateway broker)" +summary: "Install and use the OpenClaw macOS menu bar app" read_when: - - Implementing macOS app features - - Changing gateway lifecycle or node bridging on macOS + - Installing the macOS app + - Deciding between local and remote Gateway mode on macOS + - Looking for macOS app release downloads title: "macOS app" --- -The macOS app is the **menu-bar 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. +The macOS app is the OpenClaw **menu bar companion**. Use it when you want a +native tray UI, macOS permission prompts, notifications, WebChat, voice input, +Canvas, or Mac-hosted node tools such as `system.run`. -## What it does +If you only need the CLI and Gateway, start with [Getting started](/start/getting-started). -- 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 macOS-only tools (Canvas, Camera, Screen Recording, `system.run`). -- Starts the local node host service in **remote** mode (launchd), and stops it in **local** mode. -- Optionally hosts **PeekabooBridge** for UI automation. -- Installs the global CLI (`openclaw`) on request via npm, pnpm, or bun (the app prefers npm, then pnpm, then bun; Node remains the recommended Gateway runtime). +## Download -## Local vs remote mode +Download macOS app builds from the +[OpenClaw GitHub releases](https://github.com/openclaw/openclaw/releases). +When a release includes macOS app assets, look for: -- **Local** (default): the app attaches to a running local Gateway if present; - otherwise it enables the launchd service via `openclaw gateway install`. -- **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. - The app does not spawn the Gateway as a child process. - Gateway discovery now prefers Tailscale MagicDNS names over raw tailnet IPs, - so the Mac app recovers more reliably when tailnet IPs change. +- `OpenClaw-.dmg` (preferred) +- `OpenClaw-.zip` -## Launchd control +Some releases only include CLI, evidence, or Windows assets. If the newest +release has no macOS app asset, use the newest release that does, or build the +app from source with [macOS dev setup](/platforms/mac/dev-setup). -The app manages a per-user LaunchAgent labeled `ai.openclaw.gateway` -(or `ai.openclaw.` when using `--profile`/`OPENCLAW_PROFILE`; legacy `com.openclaw.*` still unloads). - -```bash -launchctl kickstart -k gui/$UID/ai.openclaw.gateway -launchctl bootout gui/$UID/ai.openclaw.gateway -``` - -Replace the label with `ai.openclaw.` when running a named profile. - -If the LaunchAgent isn't installed, enable it from the app or run -`openclaw gateway install`. - -If the gateway repeatedly disappears for minutes to hours and only resumes when you touch the Control UI or SSH into the host, see the troubleshooting note for macOS Maintenance Sleep / `ENETDOWN` crashes and launchd's respawn-protection gate in [Gateway troubleshooting](/gateway/troubleshooting#macos-gateway-silently-stops-responding-then-resumes-when-you-touch-the-dashboard). - -## Node capabilities (mac) - -The macOS app presents itself as a node. Common commands: - -- Canvas: `canvas.present`, `canvas.navigate`, `canvas.eval`, `canvas.snapshot`, `canvas.a2ui.*` -- Camera: `camera.snap`, `camera.clip` -- Screen: `screen.snapshot`, `screen.record` -- System: `system.run`, `system.notify` - -The node reports a `permissions` map so agents can decide what's allowed. - -Node service + app IPC: - -- 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): - -``` -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: - -``` -~/.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 patterns for resolved binary paths, or bare command names for PATH-invoked commands. -- Raw shell command text that contains shell control or expansion syntax (`&&`, `||`, `;`, `|`, `` ` ``, `$`, `<`, `>`, `(`, `)`) is treated as an allowlist miss and requires explicit approval (or allowlisting the shell binary). -- Choosing "Always Allow" in the prompt adds that command to the allowlist. -- `system.run` environment overrides are filtered (drops `PATH`, `DYLD_*`, `LD_*`, `BASHOPTS`, `FPATH`, `KSH_ENV`, `NODE_OPTIONS`, `NODE_REDIRECT_WARNINGS`, `NODE_REPL_EXTERNAL_MODULE`, `NODE_REPL_HISTORY`, `NODE_V8_COVERAGE`, `PYTHON*`, `PERL*`, `RUBYOPT`, `SHELLOPTS`, `PS4`, `TCLLIBPATH`) and then merged with the app's environment. -- For shell wrappers (`bash|sh|zsh ... -c/-lc`), request-scoped environment overrides are reduced to a small explicit allowlist (`TERM`, `LANG`, `LC_*`, `COLORTERM`, `NO_COLOR`, `FORCE_COLOR`). -- For allow-always decisions in allowlist mode, known dispatch wrappers (`env`, `flock`, `nice`, `nohup`, `stdbuf`, `timeout`) persist inner executable paths instead of wrapper paths. If unwrapping is not safe, no allowlist entry is persisted automatically. - -## Deep links - -The app registers the `openclaw://` URL scheme for local actions. - -### `openclaw://agent` - -Triggers 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) - -Safety: - -- Without `key`, the app prompts for confirmation. -- Without `key`, the app enforces a short message limit for the confirmation prompt and ignores `deliver` / `to` / `channel`. -- With a valid `key`, the run is unattended (intended for personal automations). - -## Onboarding flow (typical) +## First run 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. +2. Complete the macOS permission checklist. +3. Pick **Local** or **Remote** mode. +4. Install the `openclaw` CLI if the app asks for it. +5. Open WebChat from the menu bar and send a test message. -## State dir placement (macOS) +For the CLI/Gateway setup path, use [Getting started](/start/getting-started). +For permission recovery, use [macOS permissions](/platforms/mac/permissions). -Avoid putting your OpenClaw state dir in iCloud or other cloud-synced folders. -Sync-backed paths can add latency and occasionally cause file-lock/sync races for -sessions and credentials. +## Choose a Gateway mode -Prefer a local non-synced state path such as: +| Mode | Use it when | Detail page | +| ------ | --------------------------------------------------------------------------------------- | -------------------------------------------------- | +| Local | This Mac should run the Gateway and keep it alive with launchd. | [Gateway on macOS](/platforms/mac/bundled-gateway) | +| Remote | Another host runs the Gateway and this Mac should control it over SSH, LAN, or Tailnet. | [Remote control](/platforms/mac/remote) | -```bash -OPENCLAW_STATE_DIR=~/.openclaw -``` +Local mode requires an installed `openclaw` CLI. The app can install it, or you +can follow [Gateway on macOS](/platforms/mac/bundled-gateway). -If `openclaw doctor` detects state under: +## What the app owns -- `~/Library/Mobile Documents/com~apple~CloudDocs/...` -- `~/Library/CloudStorage/...` +- Menu bar status, notifications, health, and WebChat. +- macOS permission prompts for screen, microphone, speech, automation, and accessibility. +- Local node tools such as Canvas, camera/screen capture, notifications, and `system.run`. +- Exec approval prompts for Mac-hosted commands. +- Remote-mode SSH tunnels or direct Gateway connections. -it will warn and recommend moving back to a local path. +The app does **not** replace the OpenClaw Gateway or general CLI docs. Core +Gateway configuration, providers, plugins, channels, tools, and security live in +their own docs. -## Build and dev workflow (native) +## macOS detail pages -- `cd apps/macos && swift build` -- `swift run OpenClaw` (or Xcode) -- Package app: `scripts/package-mac-app.sh` +| Task | Read | +| ---------------------------------------- | ------------------------------------------------------------------------------------------- | +| Install or debug the CLI/Gateway service | [Gateway on macOS](/platforms/mac/bundled-gateway) | +| Keep state out of cloud-synced folders | [Gateway on macOS](/platforms/mac/bundled-gateway#state-directory-on-macos) | +| Debug app discovery and connectivity | [Gateway on macOS](/platforms/mac/bundled-gateway#debug-app-connectivity) | +| Understand launchd behavior | [Gateway lifecycle](/platforms/mac/child-process) | +| Fix permissions or signing/TCC issues | [macOS permissions](/platforms/mac/permissions) | +| Connect to a remote Gateway | [Remote control](/platforms/mac/remote) | +| Read menu bar status and health checks | [Menu bar](/platforms/mac/menu-bar), [Health checks](/platforms/mac/health) | +| Use the embedded chat UI | [WebChat](/platforms/mac/webchat) | +| Use voice wake or push-to-talk | [Voice wake](/platforms/mac/voicewake) | +| Use Canvas and Canvas deep links | [Canvas](/platforms/mac/canvas) | +| Host PeekabooBridge for UI automation | [Peekaboo bridge](/platforms/mac/peekaboo) | +| Configure command approvals | [Exec approvals](/tools/exec-approvals), [advanced details](/tools/exec-approvals-advanced) | +| Inspect Mac node commands and app IPC | [macOS IPC](/platforms/mac/xpc) | +| Capture logs | [macOS logging](/platforms/mac/logging) | +| Build from source | [macOS dev setup](/platforms/mac/dev-setup) | -## Debug gateway connectivity (macOS CLI) +## Related -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 -swift run openclaw-mac connect --json -swift run openclaw-mac discover --timeout 3000 --json -``` - -Connect options: - -- `--url `: override config -- `--mode `: resolve from config (default: config or local) -- `--probe`: force a fresh health probe -- `--timeout `: request timeout (default: `15000`) -- `--json`: structured output for diffing - -Discovery options: - -- `--include-local`: include gateways that would be filtered as "local" -- `--timeout `: overall discovery window (default: `2000`) -- `--json`: structured output for diffing - - -Compare against `openclaw gateway discover --json` to see whether the macOS app's discovery pipeline (`local.` plus the configured wide-area domain, with wide-area and Tailscale Serve fallbacks) differs from the Node CLI's `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) - -- **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 :127.0.0.1:` 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). - -## Related docs - -- [Gateway runbook](/gateway) -- [Gateway (macOS)](/platforms/mac/bundled-gateway) -- [macOS permissions](/platforms/mac/permissions) -- [Canvas](/platforms/mac/canvas) +- [Platforms](/platforms) +- [Getting started](/start/getting-started) +- [Gateway](/gateway) +- [Exec approvals](/tools/exec-approvals)