Vendor OpenClaw source as Adolf fork baseline
Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled
Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled
Adolf is a fork/vendored clone of github.com/openclaw/openclaw (v2026.6.11), free to diverge. Tree copied sans upstream .git; upstream remote added for future syncs. Node pinned to 24 (.nvmrc); engines already require >=22.19. Preserves docs/ARCHITECTURE.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LeqyaxJF2nbRXJtae2kNB2
This commit is contained in:
227
docs/platforms/android.md
Normal file
227
docs/platforms/android.md
Normal file
@@ -0,0 +1,227 @@
|
||||
---
|
||||
summary: "Android app (node): connection runbook + Connect/Chat/Voice/Canvas command surface"
|
||||
read_when:
|
||||
- Pairing or reconnecting the Android node
|
||||
- Debugging Android gateway discovery or auth
|
||||
- Verifying chat history parity across clients
|
||||
title: "Android app"
|
||||
---
|
||||
|
||||
<Note>
|
||||
The official Android app is available on [Google Play](https://play.google.com/store/apps/details?id=ai.openclaw.app&hl=en_IN). It is a companion node and requires a running OpenClaw Gateway. Source: [apps/android](https://github.com/openclaw/openclaw/tree/main/apps/android) ([build instructions](https://github.com/openclaw/openclaw/blob/main/apps/android/README.md)).
|
||||
</Note>
|
||||
|
||||
## Support snapshot
|
||||
|
||||
- Role: companion node app (Android does not host the Gateway).
|
||||
- Gateway required: yes (run it on macOS, Linux, or Windows via WSL2).
|
||||
- Install: [Google Play](https://play.google.com/store/apps/details?id=ai.openclaw.app&hl=en_IN) for the app, [Getting Started](/start/getting-started) for the Gateway, then [Pairing](/channels/pairing).
|
||||
- Gateway: [Runbook](/gateway) + [Configuration](/gateway/configuration).
|
||||
- Protocols: [Gateway protocol](/gateway/protocol) (nodes + control plane).
|
||||
|
||||
System control (launchd/systemd) lives on the Gateway host — see [Gateway](/gateway).
|
||||
|
||||
## Connection runbook
|
||||
|
||||
Android node app ⇄ (mDNS/NSD + WebSocket) ⇄ **Gateway**
|
||||
|
||||
Android connects directly to the Gateway WebSocket and uses device pairing (`role: node`).
|
||||
|
||||
For Tailscale or public hosts, Android requires a secure endpoint:
|
||||
|
||||
- Preferred: Tailscale Serve / Funnel with `https://<magicdns>` / `wss://<magicdns>`
|
||||
- Also supported: any other `wss://` Gateway URL with a real TLS endpoint
|
||||
- Cleartext `ws://` remains supported on private LAN addresses / `.local` hosts, plus `localhost`, `127.0.0.1`, and the Android emulator bridge (`10.0.2.2`)
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Gateway running on another machine (or reachable via SSH).
|
||||
- Android device/emulator can reach the gateway WebSocket:
|
||||
- Same LAN with mDNS/NSD, **or**
|
||||
- Same Tailscale tailnet using Wide-Area Bonjour / unicast DNS-SD (see below), **or**
|
||||
- Manual gateway host/port (fallback)
|
||||
- Tailnet/public mobile pairing does **not** use raw tailnet IP `ws://` endpoints. Use Tailscale Serve or another `wss://` URL instead.
|
||||
- The `openclaw` CLI available on the gateway machine (or via SSH), to approve pairing requests.
|
||||
|
||||
### 1. Start the Gateway
|
||||
|
||||
```bash
|
||||
openclaw gateway --port 18789 --verbose
|
||||
```
|
||||
|
||||
Confirm in logs you see something like:
|
||||
|
||||
- `listening on ws://0.0.0.0:18789`
|
||||
|
||||
For remote Android access over Tailscale, prefer Serve/Funnel instead of a raw tailnet bind:
|
||||
|
||||
```bash
|
||||
openclaw gateway --tailscale serve
|
||||
```
|
||||
|
||||
This gives Android a secure `wss://` / `https://` endpoint. A plain `gateway.bind: "tailnet"` setup is not enough for first-time remote Android pairing unless you also terminate TLS separately.
|
||||
|
||||
### 2. Verify discovery (optional)
|
||||
|
||||
From the gateway machine:
|
||||
|
||||
```bash
|
||||
dns-sd -B _openclaw-gw._tcp local.
|
||||
```
|
||||
|
||||
More debugging notes: [Bonjour](/gateway/bonjour).
|
||||
|
||||
If you also configured a wide-area discovery domain, compare against:
|
||||
|
||||
```bash
|
||||
openclaw gateway discover --json
|
||||
```
|
||||
|
||||
That shows `local.` plus the configured wide-area domain in one pass, using the resolved service endpoint instead of TXT-only hints.
|
||||
|
||||
#### Cross-network discovery via unicast DNS-SD
|
||||
|
||||
Android NSD/mDNS discovery does not cross networks. If the Android node and the gateway are on different networks but connected via Tailscale, use Wide-Area Bonjour / unicast DNS-SD instead. Discovery alone is not sufficient for tailnet/public Android pairing — the discovered route still needs a secure endpoint (`wss://` or Tailscale Serve):
|
||||
|
||||
1. Set up a DNS-SD zone (example `openclaw.internal.`) on the gateway host and publish `_openclaw-gw._tcp` records.
|
||||
2. Configure Tailscale split DNS for your chosen domain pointing at that DNS server.
|
||||
|
||||
Details and example CoreDNS config: [Bonjour](/gateway/bonjour).
|
||||
|
||||
### 3. Connect from Android
|
||||
|
||||
In the Android app:
|
||||
|
||||
- The app keeps its gateway connection alive via a **foreground service** (persistent notification).
|
||||
- Open the **Connect** tab.
|
||||
- Use **Setup Code** or **Manual** mode.
|
||||
- If discovery is blocked, use manual host/port in **Advanced controls**. For private LAN hosts, `ws://` still works. For Tailscale/public hosts, turn on TLS and use a `wss://` / Tailscale Serve endpoint.
|
||||
|
||||
After the first successful pairing, Android auto-reconnects on launch: the manual endpoint (if enabled), otherwise the last discovered gateway (best-effort).
|
||||
|
||||
### Presence alive beacons
|
||||
|
||||
After the authenticated node session connects, and when the app moves to the background while the foreground service is still connected, Android calls `node.event` with `event: "node.presence.alive"`. The gateway records this as `lastSeenAtMs`/`lastSeenReason` on the paired node/device metadata only after the authenticated node device identity is known.
|
||||
|
||||
The app counts the beacon as successfully recorded only when the gateway response includes `handled: true`. Older gateways may acknowledge `node.event` with `{ "ok": true }`; that response is compatible but does not count as a durable last-seen update.
|
||||
|
||||
### 4. Approve pairing (CLI)
|
||||
|
||||
On the gateway machine:
|
||||
|
||||
```bash
|
||||
openclaw devices list
|
||||
openclaw devices approve <requestId>
|
||||
openclaw devices reject <requestId>
|
||||
```
|
||||
|
||||
Pairing details: [Pairing](/channels/pairing).
|
||||
|
||||
Optional: if the Android node always connects from a tightly controlled subnet, you can opt in to first-time node auto-approval with explicit CIDRs or exact IPs:
|
||||
|
||||
```json5
|
||||
{
|
||||
gateway: {
|
||||
nodes: {
|
||||
pairing: {
|
||||
autoApproveCidrs: ["192.168.1.0/24"],
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
This is disabled by default. It applies only to fresh `role: node` pairing with no requested scopes. Operator/browser pairing and any role, scope, metadata, or public-key change still require manual approval.
|
||||
|
||||
### 5. Verify the node is connected
|
||||
|
||||
```bash
|
||||
openclaw nodes status
|
||||
openclaw gateway call node.list --params "{}"
|
||||
```
|
||||
|
||||
### 6. Chat + history
|
||||
|
||||
The Android Chat tab supports session selection (default `main`, plus other existing sessions):
|
||||
|
||||
- History: `chat.history` (display-normalized — inline directive tags, plain-text tool-call XML payloads (`<tool_call>`, `<function_call>`, `<tool_calls>`, `<function_calls>`, and truncated variants), and leaked ASCII/full-width model control tokens are stripped; silent-token assistant rows such as exact `NO_REPLY` / `no_reply` are omitted; oversized rows can be replaced with placeholders)
|
||||
- Send: `chat.send`
|
||||
- Push updates (best-effort): `chat.subscribe` -> `event:"chat"`
|
||||
|
||||
### 7. Canvas + camera
|
||||
|
||||
#### Gateway Canvas Host (recommended for web content)
|
||||
|
||||
To have the node show real HTML/CSS/JS that the agent can edit on disk, point the node at the Gateway canvas host.
|
||||
|
||||
<Note>
|
||||
Nodes load canvas from the Gateway HTTP server (same port as `gateway.port`, default `18789`).
|
||||
</Note>
|
||||
|
||||
1. Create `~/.openclaw/workspace/canvas/index.html` on the gateway host.
|
||||
2. Navigate the node to it (LAN):
|
||||
|
||||
```bash
|
||||
openclaw nodes invoke --node "<Android Node>" --command canvas.navigate --params '{"url":"http://<gateway-hostname>.local:18789/__openclaw__/canvas/"}'
|
||||
```
|
||||
|
||||
Tailnet (optional): if both devices are on Tailscale, use a MagicDNS name or tailnet IP instead of `.local`, e.g. `http://<gateway-magicdns>:18789/__openclaw__/canvas/`.
|
||||
|
||||
This server injects a live-reload client into HTML and reloads on file changes. The Gateway also serves `/__openclaw__/a2ui/`, but the Android app treats remote A2UI pages as render-only. Action-capable A2UI commands use the bundled app-owned A2UI page.
|
||||
|
||||
Canvas commands (foreground only):
|
||||
|
||||
- `canvas.eval`, `canvas.snapshot`, `canvas.navigate` (use `{"url":""}` or `{"url":"/"}` to return to the default scaffold). `canvas.snapshot` returns `{ format, base64 }` (default `format="jpeg"`).
|
||||
- A2UI: `canvas.a2ui.push`, `canvas.a2ui.reset` (`canvas.a2ui.pushJSONL` legacy alias). These use the bundled app-owned A2UI page for action-capable rendering.
|
||||
|
||||
Camera commands (foreground only; permission-gated): `camera.snap` (jpg), `camera.clip` (mp4). See [Camera node](/nodes/camera) for parameters and CLI helpers.
|
||||
|
||||
### 8. Voice + expanded Android command surface
|
||||
|
||||
- Voice tab: Android has two explicit capture modes. **Mic** is a manual Voice-tab session that sends each pause as a chat turn and stops when the app leaves the foreground or the user leaves the Voice tab. **Talk** is continuous Talk Mode and keeps listening until toggled off or the node disconnects.
|
||||
- Talk Mode promotes the existing foreground service from `connectedDevice` to `connectedDevice|microphone` before capture starts, then demotes it when Talk Mode stops. The node service declares `FOREGROUND_SERVICE_CONNECTED_DEVICE` with `CHANGE_NETWORK_STATE`; Android 14+ also requires the `FOREGROUND_SERVICE_MICROPHONE` declaration, the `RECORD_AUDIO` runtime grant, and the microphone service type at runtime.
|
||||
- By default, Android Talk uses native speech recognition, Gateway chat, and `talk.speak` through the configured gateway Talk provider. Local system TTS is used only when `talk.speak` is unavailable.
|
||||
- Android Talk uses realtime Gateway relay only when `talk.realtime.mode` is `realtime` and `talk.realtime.transport` is `gateway-relay`.
|
||||
- Voice wake is implemented in source (`VoiceWakeMode`) but the shipping app runtime always forces it to `off` on connect — there is no user-facing toggle today.
|
||||
- Additional Android command families (availability depends on device, permissions, and user settings):
|
||||
- `device.status`, `device.info`, `device.permissions`, `device.health`
|
||||
- `device.apps` only when **Settings > Phone Capabilities > Installed Apps** is enabled; it lists launcher-visible apps by default (pass `includeNonLaunchable` for the full list).
|
||||
- `notifications.list`, `notifications.actions` (see [Notification forwarding](#notification-forwarding) below)
|
||||
- `photos.latest`
|
||||
- `contacts.search`, `contacts.add`
|
||||
- `calendar.events`, `calendar.add`
|
||||
- `callLog.search`
|
||||
- `sms.search`
|
||||
- `motion.activity`, `motion.pedometer`
|
||||
|
||||
## Assistant entrypoints
|
||||
|
||||
Android supports launching OpenClaw from the system assistant trigger (Google Assistant). Holding the home button (or another `ACTION_ASSIST` trigger) opens the app; saying "Hey Google, ask OpenClaw `<prompt>`" matches the app's declared App Actions query pattern and hands the prompt into the chat composer without auto-sending it.
|
||||
|
||||
This uses Android **App Actions** (`shortcuts.xml` capability) declared in the app manifest. No gateway-side configuration is needed — the assistant intent is handled entirely by the Android app.
|
||||
|
||||
<Note>
|
||||
App Actions availability depends on the device, Google Play Services version, and whether the user has set OpenClaw as the default assistant app.
|
||||
</Note>
|
||||
|
||||
## Notification forwarding
|
||||
|
||||
Android can forward device notifications to the gateway as `node.event` items. This is configured **on the device**, in the app's Settings sheet — not in gateway/`openclaw.json` config.
|
||||
|
||||
| Setting | Description |
|
||||
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| Forward Notification Events | Master toggle. Off by default; requires Notification Listener Access to be granted first. |
|
||||
| Package Filter | **Allowlist** (only listed package IDs forwarded) or **Blocklist** (default: all packages except listed IDs). OpenClaw's own package is always excluded in Blocklist mode to prevent forwarding loops. |
|
||||
| Quiet Hours | Local HH:mm start/end window that suppresses forwarding. Disabled by default; defaults to `22:00`-`07:00` once enabled. |
|
||||
| Max Events / Minute | Per-device rate limit on forwarded notifications. Default 20. |
|
||||
| Route Session Key | Optional. Pins forwarded notification events into a specific session instead of the device's default notification route. |
|
||||
|
||||
<Note>
|
||||
Notification forwarding requires the Android Notification Listener permission. The app prompts for this during setup.
|
||||
</Note>
|
||||
|
||||
## Related
|
||||
|
||||
- [iOS app](/platforms/ios)
|
||||
- [Nodes](/nodes)
|
||||
- [Android node troubleshooting](/nodes/troubleshooting)
|
||||
12
docs/platforms/digitalocean.md
Normal file
12
docs/platforms/digitalocean.md
Normal file
@@ -0,0 +1,12 @@
|
||||
---
|
||||
summary: "Redirect to /install/digitalocean"
|
||||
title: "DigitalOcean (platform)"
|
||||
redirect: /install/digitalocean
|
||||
---
|
||||
|
||||
This page has moved to [DigitalOcean](/install/digitalocean).
|
||||
|
||||
## Related
|
||||
|
||||
- [Install overview](/install)
|
||||
- [VPS hosting](/vps)
|
||||
117
docs/platforms/easyrunner.md
Normal file
117
docs/platforms/easyrunner.md
Normal file
@@ -0,0 +1,117 @@
|
||||
---
|
||||
summary: "Run the OpenClaw Gateway on EasyRunner with Podman and Caddy"
|
||||
read_when:
|
||||
- Deploying OpenClaw on EasyRunner
|
||||
- Running the Gateway behind EasyRunner's Caddy proxy
|
||||
- Choosing persistent volumes and auth for a hosted Gateway
|
||||
title: "EasyRunner"
|
||||
---
|
||||
|
||||
EasyRunner hosts the OpenClaw Gateway as a small containerized app behind its
|
||||
Caddy proxy. This guide assumes an EasyRunner host that runs Podman-compatible
|
||||
Compose apps and terminates HTTPS through Caddy.
|
||||
|
||||
## Before you begin
|
||||
|
||||
- An EasyRunner server with a domain routed to it.
|
||||
- The official OpenClaw image (`ghcr.io/openclaw/openclaw`) or your own build.
|
||||
- A persistent config volume for `/home/node/.openclaw`.
|
||||
- A persistent workspace volume for `/home/node/.openclaw/workspace`.
|
||||
- A strong Gateway token or password.
|
||||
|
||||
Keep device auth enabled when possible. If your reverse proxy cannot carry
|
||||
device identity correctly, fix trusted-proxy settings first (see
|
||||
[Trusted proxy auth](/gateway/trusted-proxy-auth)); use dangerous auth
|
||||
bypasses only on a fully private, operator-controlled network.
|
||||
|
||||
## Compose app
|
||||
|
||||
Create an EasyRunner app with a Compose file shaped like this:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
openclaw:
|
||||
image: ghcr.io/openclaw/openclaw:latest
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
OPENCLAW_GATEWAY_TOKEN: ${OPENCLAW_GATEWAY_TOKEN}
|
||||
OPENCLAW_HOME: /home/node
|
||||
OPENCLAW_STATE_DIR: /home/node/.openclaw
|
||||
OPENCLAW_CONFIG_PATH: /home/node/.openclaw/openclaw.json
|
||||
OPENCLAW_WORKSPACE_DIR: /home/node/.openclaw/workspace
|
||||
volumes:
|
||||
- openclaw-config:/home/node/.openclaw
|
||||
- openclaw-workspace:/home/node/.openclaw/workspace
|
||||
labels:
|
||||
caddy: openclaw.example.com
|
||||
caddy.reverse_proxy: "{{upstreams 1455}}"
|
||||
command: ["node", "openclaw.mjs", "gateway", "--bind", "lan", "--port", "1455"]
|
||||
|
||||
volumes:
|
||||
openclaw-config:
|
||||
openclaw-workspace:
|
||||
```
|
||||
|
||||
Replace `openclaw.example.com` with your Gateway hostname. Store
|
||||
`OPENCLAW_GATEWAY_TOKEN` in EasyRunner's secret/environment manager instead of
|
||||
committing it to the app definition. The image binds to loopback by default,
|
||||
so the explicit `--bind lan --port 1455` in `command` is required for Caddy to
|
||||
reach the container.
|
||||
|
||||
## Configure OpenClaw
|
||||
|
||||
Inside the persistent config volume, keep the Gateway reachable only through
|
||||
the proxy and require auth:
|
||||
|
||||
```json5
|
||||
{
|
||||
gateway: {
|
||||
bind: "lan",
|
||||
port: 1455,
|
||||
auth: {
|
||||
token: "${OPENCLAW_GATEWAY_TOKEN}",
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
If Caddy terminates TLS for the Gateway, configure trusted-proxy settings for
|
||||
the exact proxy path rather than disabling auth checks globally. See
|
||||
[Trusted proxy auth](/gateway/trusted-proxy-auth).
|
||||
|
||||
## Verify
|
||||
|
||||
From your workstation:
|
||||
|
||||
```bash
|
||||
openclaw gateway probe --url https://openclaw.example.com --token <token>
|
||||
openclaw gateway status --url https://openclaw.example.com --token <token>
|
||||
```
|
||||
|
||||
From the EasyRunner host, `GET /healthz` (liveness) and `GET /readyz`
|
||||
(readiness) need no auth and back the image's built-in container health
|
||||
check. Also check the app logs for a listening Gateway and no startup
|
||||
SecretRef, plugin, or channel auth failures.
|
||||
|
||||
## Updates and backups
|
||||
|
||||
- Pull or build the new OpenClaw image, then redeploy the EasyRunner app.
|
||||
- Back up the `openclaw-config` volume before updates. It holds
|
||||
`openclaw.json`, `agents/<agentId>/agent/auth-profiles.json`, and installed
|
||||
plugin package state.
|
||||
- Back up `openclaw-workspace` if agents write durable project data there.
|
||||
- Run `openclaw doctor` after major updates to catch config migrations and
|
||||
service warnings.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- `gateway probe` cannot connect: confirm the Caddy hostname points at the app
|
||||
and that the container listens on `0.0.0.0:1455`.
|
||||
- Auth fails: rotate the token in EasyRunner secrets and the local client
|
||||
command together.
|
||||
- Files are root-owned after restore: the image runs as `node` (uid 1000);
|
||||
repair the mounted volumes so that user can write
|
||||
`/home/node/.openclaw` and `/home/node/.openclaw/workspace`.
|
||||
- Browser or channel plugins fail: check whether the required external
|
||||
binaries, network egress, and mounted credentials are available inside the
|
||||
container.
|
||||
65
docs/platforms/index.md
Normal file
65
docs/platforms/index.md
Normal file
@@ -0,0 +1,65 @@
|
||||
---
|
||||
summary: "Platform support overview (Gateway + companion apps)"
|
||||
read_when:
|
||||
- Looking for OS support or install paths
|
||||
- Deciding where to run the Gateway
|
||||
title: "Platforms"
|
||||
---
|
||||
|
||||
OpenClaw core is written in TypeScript. **Node is the recommended runtime**.
|
||||
Bun is not recommended for the Gateway — known issues with WhatsApp and
|
||||
Telegram channels; see [Bun (experimental)](/install/bun) for details.
|
||||
|
||||
Companion apps exist for Windows Hub, macOS (menu bar app), and mobile nodes
|
||||
(iOS/Android). Linux companion apps are planned, but the Gateway is fully
|
||||
supported today. On Windows, choose Windows Hub for the desktop app, native
|
||||
PowerShell install for terminal-first use, or WSL2 for the most
|
||||
Linux-compatible Gateway runtime.
|
||||
|
||||
## Choose your OS
|
||||
|
||||
- macOS: [macOS](/platforms/macos)
|
||||
- iOS: [iOS](/platforms/ios)
|
||||
- Android: [Android](/platforms/android)
|
||||
- Windows: [Windows](/platforms/windows)
|
||||
- Linux: [Linux](/platforms/linux)
|
||||
|
||||
## VPS and hosting
|
||||
|
||||
- VPS hub: [VPS hosting](/vps)
|
||||
- Fly.io: [Fly.io](/install/fly)
|
||||
- Hetzner (Docker): [Hetzner](/install/hetzner)
|
||||
- GCP (Compute Engine): [GCP](/install/gcp)
|
||||
- Azure (Linux VM): [Azure](/install/azure)
|
||||
- exe.dev (VM + HTTPS proxy): [exe.dev](/install/exe-dev)
|
||||
- EasyRunner (Podman + Caddy): [EasyRunner](/platforms/easyrunner)
|
||||
|
||||
## Common links
|
||||
|
||||
- Install guide: [Getting Started](/start/getting-started)
|
||||
- Windows Hub: [Windows](/platforms/windows)
|
||||
- Gateway runbook: [Gateway](/gateway)
|
||||
- Gateway configuration: [Configuration](/gateway/configuration)
|
||||
- Service status: `openclaw gateway status`
|
||||
|
||||
## Gateway service install (CLI)
|
||||
|
||||
Use one of these (all supported):
|
||||
|
||||
- Wizard (recommended): `openclaw onboard --install-daemon`
|
||||
- Direct: `openclaw gateway install`
|
||||
- Configure flow: `openclaw configure` → select **Gateway service**
|
||||
- Repair/migrate: `openclaw doctor` (offers to install or fix the service)
|
||||
|
||||
The service target depends on OS:
|
||||
|
||||
- macOS: LaunchAgent (`ai.openclaw.gateway`, or `ai.openclaw.<profile>` for a named profile)
|
||||
- Linux/WSL2: systemd user service (`openclaw-gateway[-<profile>].service`)
|
||||
- Native Windows: Scheduled Task (`OpenClaw Gateway` or `OpenClaw Gateway (<profile>)`), with a per-user Startup-folder login item fallback if task creation is denied
|
||||
|
||||
## Related
|
||||
|
||||
- [Install overview](/install)
|
||||
- [Windows Hub](/platforms/windows)
|
||||
- [macOS app](/platforms/macos)
|
||||
- [iOS app](/platforms/ios)
|
||||
235
docs/platforms/ios.md
Normal file
235
docs/platforms/ios.md
Normal file
@@ -0,0 +1,235 @@
|
||||
---
|
||||
summary: "iOS node app: connect to the Gateway, pairing, canvas, and troubleshooting"
|
||||
read_when:
|
||||
- Pairing or reconnecting the iOS node
|
||||
- Running the iOS app from source
|
||||
- Debugging gateway discovery or canvas commands
|
||||
title: "iOS app"
|
||||
---
|
||||
|
||||
Availability: iPhone app builds are distributed through Apple channels when enabled for a release. Local development builds can also run from source.
|
||||
|
||||
## What it does
|
||||
|
||||
- Connects to a Gateway over WebSocket (LAN or tailnet).
|
||||
- Exposes node capabilities: Canvas, Screen snapshot, Camera capture, Location, Talk mode, Voice wake.
|
||||
- Receives `node.invoke` commands and reports node status events.
|
||||
|
||||
## Requirements
|
||||
|
||||
- Gateway running on another device (macOS, Linux, or Windows via WSL2).
|
||||
- Network path:
|
||||
- Same LAN via Bonjour, **or**
|
||||
- Tailnet via unicast DNS-SD (example domain: `openclaw.internal.`), **or**
|
||||
- Manual host/port (fallback).
|
||||
|
||||
## Quick start (pair + connect)
|
||||
|
||||
1. Start an authenticated Gateway with a route your phone can reach. Tailscale
|
||||
Serve is the recommended remote path:
|
||||
|
||||
```bash
|
||||
openclaw gateway --port 18789 --tailscale serve
|
||||
```
|
||||
|
||||
For a trusted same-LAN setup, use an authenticated `gateway.bind: "lan"`
|
||||
instead. The default loopback bind is not reachable from a phone. If the
|
||||
Gateway has not been configured yet, run `openclaw onboard` first so setup-code
|
||||
creation has a token or password auth path.
|
||||
|
||||
2. Open the [Control UI](/web/control-ui), select **Nodes**, and click
|
||||
**Pair mobile device** in the **Devices** card.
|
||||
|
||||
3. In the iOS app, open **Settings** -> **Gateway**, scan the QR code (or paste
|
||||
the setup code), and connect.
|
||||
|
||||
4. The official app connects automatically. If **Devices** shows a pending
|
||||
request, review its role and scopes before approving it.
|
||||
|
||||
The Control UI button requires an already paired session with `operator.admin`.
|
||||
As a terminal fallback, pick a discovered gateway in the iOS app (or enable
|
||||
Manual Host and enter host/port), then approve the request on the Gateway host:
|
||||
|
||||
```bash
|
||||
openclaw devices list
|
||||
openclaw devices approve <requestId>
|
||||
```
|
||||
|
||||
If the app retries pairing with changed auth details (role/scopes/public key), the previous pending request is superseded and a new `requestId` is created. Run `openclaw devices list` again before approval.
|
||||
|
||||
Optional: if the iOS node always connects from a tightly controlled subnet, you can opt in to first-time node auto-approval with explicit CIDRs or exact IPs:
|
||||
|
||||
```json5
|
||||
{
|
||||
gateway: {
|
||||
nodes: {
|
||||
pairing: {
|
||||
autoApproveCidrs: ["192.168.1.0/24"],
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
This is disabled by default. It applies only to fresh `role: node` pairing with no requested scopes. Operator/browser pairing and any role, scope, metadata, or public-key change still require manual approval.
|
||||
|
||||
5. Verify connection:
|
||||
|
||||
```bash
|
||||
openclaw nodes status
|
||||
openclaw gateway call node.list --params "{}"
|
||||
```
|
||||
|
||||
## Relay-backed push for official builds
|
||||
|
||||
Official distributed iOS builds use an external push relay instead of publishing the raw APNs token to the gateway. Official App Store builds from the public release lane use the hosted relay at `https://ios-push-relay.openclaw.ai`; this base URL is hardcoded for App Store distribution and does not read any override.
|
||||
|
||||
Custom relay deployments require a deliberately separate iOS build/deployment path whose relay URL matches the gateway relay URL. The App Store release lane never accepts a custom relay URL. If you're using a custom relay build, set the matching gateway relay URL:
|
||||
|
||||
```json5
|
||||
{
|
||||
gateway: {
|
||||
push: {
|
||||
apns: {
|
||||
relay: {
|
||||
baseUrl: "https://relay.example.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
How the flow works:
|
||||
|
||||
- The iOS app registers with the relay using App Attest and a StoreKit app transaction JWS.
|
||||
- The relay returns an opaque relay handle plus a registration-scoped send grant.
|
||||
- The iOS app fetches the paired gateway identity (`gateway.identity.get`) and includes it in relay registration, so the relay-backed registration is delegated to that specific gateway.
|
||||
- The app forwards that relay-backed registration to the paired gateway with `push.apns.register`.
|
||||
- The gateway uses that stored relay handle for `push.test`, background wakes, and wake nudges.
|
||||
- If the app later connects to a different gateway or a build with a different relay base URL, it refreshes the relay registration instead of reusing the old binding.
|
||||
|
||||
What the gateway does **not** need for this path: no deployment-wide relay token, no direct APNs key for official App Store relay-backed sends.
|
||||
|
||||
Expected operator flow:
|
||||
|
||||
1. Install the official iOS app.
|
||||
2. Optional: set `gateway.push.apns.relay.baseUrl` on the gateway only when using a deliberately separate custom relay build.
|
||||
3. Pair the app to the gateway and let it finish connecting.
|
||||
4. The app publishes `push.apns.register` once it has an APNs token, the operator session is connected, and relay registration succeeds.
|
||||
5. After that, `push.test`, reconnect wakes, and wake nudges can use the stored relay-backed registration.
|
||||
|
||||
## Background alive beacons
|
||||
|
||||
When iOS wakes the app for a silent push, background refresh, or significant-location event, the app attempts a short node reconnect and then calls `node.event` with `event: "node.presence.alive"`. The gateway records this as `lastSeenAtMs`/`lastSeenReason` on the paired node/device metadata only after the authenticated node device identity is known.
|
||||
|
||||
The app treats a background wake as successfully recorded only when the gateway response includes `handled: true`. Older gateways may acknowledge `node.event` with `{ "ok": true }`; that response is compatible but does not count as a durable last-seen update.
|
||||
|
||||
Compatibility note:
|
||||
|
||||
- `OPENCLAW_APNS_RELAY_BASE_URL` still works as a temporary env override for the gateway (`gateway.push.apns.relay.baseUrl` is the config-first path).
|
||||
- The App Store release build's push mode hardcodes the hosted relay host and never reads a relay-URL override — the `OPENCLAW_PUSH_RELAY_BASE_URL` build-time env var only affects local/sandbox iOS build modes.
|
||||
|
||||
## Authentication and trust flow
|
||||
|
||||
The relay exists to enforce two constraints direct APNs-on-gateway cannot provide for official iOS builds:
|
||||
|
||||
- Only genuine OpenClaw iOS builds distributed through Apple can use the hosted relay.
|
||||
- A gateway can send relay-backed pushes only for iOS devices that paired with that specific gateway.
|
||||
|
||||
Hop by hop:
|
||||
|
||||
1. `iOS app -> gateway`: the app pairs with the gateway through the normal Gateway auth flow, giving it an authenticated node session plus an authenticated operator session. The operator session calls `gateway.identity.get`.
|
||||
2. `iOS app -> relay`: the app calls the relay registration endpoints over HTTPS with App Attest proof plus a StoreKit app transaction JWS. The relay validates the bundle ID, App Attest proof, and Apple distribution proof, and requires the official/production distribution path — this is what blocks local Xcode/dev builds from using the hosted relay, since a local build cannot satisfy the official Apple distribution proof.
|
||||
3. `gateway identity delegation`: before relay registration, the app fetches the paired gateway identity from `gateway.identity.get` and includes it in the relay registration payload. The relay returns a relay handle and a registration-scoped send grant delegated to that gateway identity.
|
||||
4. `gateway -> relay`: the gateway stores the relay handle and send grant from `push.apns.register`. On `push.test`, reconnect wakes, and wake nudges, the gateway signs the send request with its own device identity; the relay verifies both the stored send grant and the gateway signature against the delegated gateway identity from registration. Another gateway cannot reuse that stored registration, even if it somehow obtains the handle.
|
||||
5. `relay -> APNs`: the relay owns the production APNs credentials and the raw APNs token for the official build. The gateway never stores the raw APNs token for relay-backed official builds; the relay sends the final push to APNs on behalf of the paired gateway.
|
||||
|
||||
Why this design was created: to keep production APNs credentials out of user gateways, avoid storing raw official-build APNs tokens on the gateway, allow hosted relay usage only for official OpenClaw iOS builds, and prevent one gateway from sending wake pushes to iOS devices owned by a different gateway.
|
||||
|
||||
Local/manual builds remain on direct APNs. If you are testing those builds without the relay, the gateway still needs direct APNs credentials:
|
||||
|
||||
```bash
|
||||
export OPENCLAW_APNS_TEAM_ID="TEAMID"
|
||||
export OPENCLAW_APNS_KEY_ID="KEYID"
|
||||
export OPENCLAW_APNS_PRIVATE_KEY_P8="$(cat /path/to/AuthKey_KEYID.p8)"
|
||||
```
|
||||
|
||||
These are gateway-host runtime env vars, not Fastlane settings. `apps/ios/fastlane/.env` only stores App Store Connect auth such as `APP_STORE_CONNECT_KEY_ID` and `APP_STORE_CONNECT_ISSUER_ID`; it does not configure direct APNs delivery for local iOS builds.
|
||||
|
||||
Recommended gateway-host storage, consistent with other provider credentials under `~/.openclaw/credentials/`:
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.openclaw/credentials/apns
|
||||
chmod 700 ~/.openclaw/credentials/apns
|
||||
mv /path/to/AuthKey_KEYID.p8 ~/.openclaw/credentials/apns/AuthKey_KEYID.p8
|
||||
chmod 600 ~/.openclaw/credentials/apns/AuthKey_KEYID.p8
|
||||
export OPENCLAW_APNS_PRIVATE_KEY_PATH="$HOME/.openclaw/credentials/apns/AuthKey_KEYID.p8"
|
||||
```
|
||||
|
||||
Do not commit the `.p8` file or place it under the repo checkout.
|
||||
|
||||
## Discovery paths
|
||||
|
||||
### Bonjour (LAN)
|
||||
|
||||
The iOS app browses `_openclaw-gw._tcp` on `local.` and, when configured, the same wide-area DNS-SD discovery domain. Same-LAN gateways appear automatically from `local.`; cross-network discovery can use the configured wide-area domain without changing the beacon type.
|
||||
|
||||
### Tailnet (cross-network)
|
||||
|
||||
If mDNS is blocked, use a unicast DNS-SD zone (choose a domain; example: `openclaw.internal.`) and Tailscale split DNS. See [Bonjour](/gateway/bonjour) for the CoreDNS example.
|
||||
|
||||
### Manual host/port
|
||||
|
||||
In Settings, enable **Manual Host** and enter the gateway host + port (default `18789`).
|
||||
|
||||
## Canvas + A2UI
|
||||
|
||||
The iOS node renders a WKWebView canvas. Use `node.invoke` to drive it:
|
||||
|
||||
```bash
|
||||
openclaw nodes invoke --node "iOS Node" --command canvas.navigate --params '{"url":"http://<gateway-host>:18789/__openclaw__/canvas/"}'
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
- The Gateway canvas host serves `/__openclaw__/canvas/` and `/__openclaw__/a2ui/`, from the Gateway HTTP server (same port as `gateway.port`, default `18789`).
|
||||
- The iOS node keeps the built-in scaffold as the connected default view. `canvas.a2ui.push` and `canvas.a2ui.reset` use the bundled app-owned A2UI page.
|
||||
- Remote Gateway A2UI pages are render-only on iOS; native A2UI button actions are accepted only from bundled app-owned pages.
|
||||
- Return to the built-in scaffold with `canvas.navigate` and `{"url":""}`.
|
||||
|
||||
## Computer Use relationship
|
||||
|
||||
The iOS app is a mobile node surface, not a Codex Computer Use backend. Codex Computer Use and `cua-driver mcp` control a local macOS desktop through MCP tools; the iOS app exposes iPhone capabilities through OpenClaw node commands such as `canvas.*`, `camera.*`, `screen.*`, `location.*`, and `talk.*`.
|
||||
|
||||
Agents can still operate the iOS app through OpenClaw by invoking node commands, but those calls go through the gateway node protocol and follow iOS foreground/background limits. Use [Codex Computer Use](/plugins/codex-computer-use) for local desktop control and this page for iOS node capabilities.
|
||||
|
||||
### Canvas eval / snapshot
|
||||
|
||||
```bash
|
||||
openclaw nodes invoke --node "iOS Node" --command canvas.eval --params '{"javaScript":"(() => { const {ctx} = window.__openclaw; ctx.clearRect(0,0,innerWidth,innerHeight); ctx.lineWidth=6; ctx.strokeStyle=\"#ff2d55\"; ctx.beginPath(); ctx.moveTo(40,40); ctx.lineTo(innerWidth-40, innerHeight-40); ctx.stroke(); return \"ok\"; })()"}'
|
||||
```
|
||||
|
||||
```bash
|
||||
openclaw nodes invoke --node "iOS Node" --command canvas.snapshot --params '{"maxWidth":900,"format":"jpeg"}'
|
||||
```
|
||||
|
||||
## Voice wake + talk mode
|
||||
|
||||
- Voice wake and talk mode are available in Settings.
|
||||
- OpenAI realtime Talk uses client-owned WebRTC when `talk.realtime.transport` is `webrtc`; an explicit `gateway-relay` configuration remains Gateway-owned. See [Talk mode](/nodes/talk).
|
||||
- Talk-capable iOS nodes advertise the `talk` capability and can declare `talk.ptt.start`, `talk.ptt.stop`, `talk.ptt.cancel`, and `talk.ptt.once`; the Gateway allows those push-to-talk commands by default for trusted Talk-capable nodes.
|
||||
- iOS may suspend background audio; treat voice features as best-effort when the app is not active.
|
||||
|
||||
## Common errors
|
||||
|
||||
- `NODE_BACKGROUND_UNAVAILABLE`: bring the iOS app to the foreground (canvas/camera/screen commands require it).
|
||||
- `A2UI_HOST_UNAVAILABLE`: the bundled A2UI page was not reachable in the app WebView; keep the app foregrounded on the Screen tab and retry.
|
||||
- Pairing prompt never appears: run `openclaw devices list` and approve manually.
|
||||
- Reconnect fails after reinstall: the Keychain pairing token was cleared; re-pair the node.
|
||||
|
||||
## Related docs
|
||||
|
||||
- [Pairing](/channels/pairing)
|
||||
- [Discovery](/gateway/discovery)
|
||||
- [Bonjour](/gateway/bonjour)
|
||||
132
docs/platforms/linux.md
Normal file
132
docs/platforms/linux.md
Normal file
@@ -0,0 +1,132 @@
|
||||
---
|
||||
summary: "Linux support + companion app status"
|
||||
read_when:
|
||||
- Looking for Linux companion app status
|
||||
- Planning platform coverage or contributions
|
||||
- Debugging Linux OOM kills or exit 137 on a VPS or container
|
||||
title: "Linux app"
|
||||
---
|
||||
|
||||
The Gateway is fully supported on Linux. Node is the recommended runtime; Bun
|
||||
is not recommended (known WhatsApp/Telegram issues).
|
||||
|
||||
There is no native Linux companion app yet. Contributions are welcome.
|
||||
|
||||
## Quick path (VPS)
|
||||
|
||||
1. Install Node 24 (recommended) or Node 22.19+ (LTS, still supported).
|
||||
2. `npm i -g openclaw@latest`
|
||||
3. `openclaw onboard --install-daemon`
|
||||
4. From your laptop: `ssh -N -L 18789:127.0.0.1:18789 <user>@<host>`
|
||||
5. Open `http://127.0.0.1:18789/` and authenticate with the configured shared
|
||||
secret (token by default; password if `gateway.auth.mode` is `"password"`).
|
||||
|
||||
Full server guide: [Linux Server](/vps). Step-by-step VPS example:
|
||||
[exe.dev](/install/exe-dev).
|
||||
|
||||
## Install
|
||||
|
||||
- [Getting Started](/start/getting-started)
|
||||
- [Install & updates](/install/updating)
|
||||
- Optional: [Bun (experimental)](/install/bun), [Nix](/install/nix), [Docker](/install/docker)
|
||||
|
||||
## Gateway service (systemd)
|
||||
|
||||
Install with one of:
|
||||
|
||||
```bash
|
||||
openclaw onboard --install-daemon
|
||||
openclaw gateway install
|
||||
openclaw configure # select "Gateway service" when prompted
|
||||
```
|
||||
|
||||
Repair or migrate an existing install:
|
||||
|
||||
```bash
|
||||
openclaw doctor
|
||||
```
|
||||
|
||||
`openclaw gateway install` renders a systemd **user** unit by default. Full
|
||||
service guidance, including the **system**-level unit variant for shared or
|
||||
always-on hosts, lives in the [Gateway runbook](/gateway#supervision-and-service-lifecycle).
|
||||
|
||||
Write a unit by hand only for a custom setup. Minimal user-unit example
|
||||
(`~/.config/systemd/user/openclaw-gateway[-<profile>].service`):
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=OpenClaw Gateway (profile: <profile>, v<version>)
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
StartLimitBurst=5
|
||||
StartLimitIntervalSec=60
|
||||
|
||||
[Service]
|
||||
ExecStart=/usr/local/bin/openclaw gateway --port 18789
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
RestartPreventExitStatus=78
|
||||
TimeoutStopSec=30
|
||||
TimeoutStartSec=30
|
||||
SuccessExitStatus=0 143
|
||||
OOMPolicy=continue
|
||||
KillMode=control-group
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
```
|
||||
|
||||
Enable it:
|
||||
|
||||
```bash
|
||||
systemctl --user enable --now openclaw-gateway[-<profile>].service
|
||||
```
|
||||
|
||||
## Memory pressure and OOM kills
|
||||
|
||||
On Linux, the kernel picks an OOM victim when a host, VM, or container cgroup
|
||||
runs out of memory. The Gateway is a poor victim because it owns long-lived
|
||||
sessions and channel connections, so OpenClaw biases transient child
|
||||
processes to be killed first when possible.
|
||||
|
||||
For eligible Linux child spawns, OpenClaw wraps the command in a short
|
||||
`/bin/sh` shim that raises the child's own `oom_score_adj` to `1000`, then
|
||||
`exec`s the real command. This is unprivileged: a process may always raise
|
||||
its own OOM score.
|
||||
|
||||
Covered child process surfaces:
|
||||
|
||||
- Supervisor-managed command children
|
||||
- PTY shell children
|
||||
- MCP stdio server children
|
||||
- OpenClaw-launched browser/Chrome processes (via the plugin SDK process runtime)
|
||||
|
||||
The wrapper is Linux-only and skipped when `/bin/sh` is unavailable, or when
|
||||
the child env sets `OPENCLAW_CHILD_OOM_SCORE_ADJ` to `0`, `false`, `no`, or
|
||||
`off`.
|
||||
|
||||
Verify a child process:
|
||||
|
||||
```bash
|
||||
cat /proc/<child-pid>/oom_score_adj
|
||||
```
|
||||
|
||||
Expected value for covered children is `1000`; the Gateway process itself
|
||||
keeps its normal score (usually `0`).
|
||||
|
||||
The systemd unit's `OOMPolicy=continue` keeps the Gateway service alive when
|
||||
a transient child is selected by the OOM killer instead of marking the whole
|
||||
unit failed and restarting all channels; the failed child/session reports its
|
||||
own error.
|
||||
|
||||
This does not replace normal memory tuning. If a VPS or container repeatedly
|
||||
kills children, raise the memory limit, reduce concurrency, or add stronger
|
||||
resource controls (systemd `MemoryMax=`, container memory limits).
|
||||
|
||||
## Related
|
||||
|
||||
- [Install overview](/install)
|
||||
- [Linux server](/vps)
|
||||
- [Raspberry Pi](/platforms/raspberry-pi)
|
||||
- [Gateway runbook](/gateway)
|
||||
- [Gateway configuration](/gateway/configuration)
|
||||
120
docs/platforms/mac/bundled-gateway.md
Normal file
120
docs/platforms/mac/bundled-gateway.md
Normal file
@@ -0,0 +1,120 @@
|
||||
---
|
||||
summary: "Gateway runtime on macOS (external launchd service)"
|
||||
read_when:
|
||||
- Packaging OpenClaw.app
|
||||
- Debugging the macOS gateway launchd service
|
||||
- Installing the gateway CLI for macOS
|
||||
title: "Gateway on macOS"
|
||||
---
|
||||
|
||||
OpenClaw.app does not bundle Node/Bun or the Gateway runtime. The macOS app
|
||||
expects an **external** `openclaw` CLI install, does not spawn the Gateway as
|
||||
a child process, and manages a per-user launchd service to keep the Gateway
|
||||
running (or attaches to an already-running local Gateway).
|
||||
|
||||
## Automatic setup
|
||||
|
||||
On a fresh Mac, choose **This Mac** during onboarding. The app runs its
|
||||
signed, bundled installer script before the Gateway wizard: it installs a
|
||||
user-space Node runtime and the matching `openclaw` CLI under `~/.openclaw`,
|
||||
then installs and starts the per-user launchd service. This path needs no
|
||||
Terminal, Homebrew, or administrator access.
|
||||
|
||||
The app bundles the installer script only, not the Node or Gateway payload;
|
||||
setup needs an internet connection to download the runtime and matching
|
||||
OpenClaw package.
|
||||
|
||||
## Manual recovery
|
||||
|
||||
Node 24 is recommended for a manual install; Node 22.19+ also works. Install
|
||||
`openclaw` globally:
|
||||
|
||||
```bash
|
||||
npm install -g openclaw@<version>
|
||||
```
|
||||
|
||||
Use **Retry setup** after a failed automatic setup. If that still fails,
|
||||
install the CLI manually with the command above, then choose **Check again**
|
||||
in onboarding.
|
||||
|
||||
## Launchd (Gateway as LaunchAgent)
|
||||
|
||||
Label: `ai.openclaw.gateway` (default profile), or `ai.openclaw.<profile>`
|
||||
for a named profile.
|
||||
|
||||
Plist location (per-user): `~/Library/LaunchAgents/ai.openclaw.gateway.plist`
|
||||
(or `ai.openclaw.<profile>.plist`).
|
||||
|
||||
The macOS app owns LaunchAgent install/update for the default profile in
|
||||
Local mode. The CLI can also install it directly: `openclaw gateway install`
|
||||
(named profiles are selected via the `OPENCLAW_PROFILE` env var).
|
||||
|
||||
Behavior:
|
||||
|
||||
- "OpenClaw Active" enables/disables the LaunchAgent.
|
||||
- Quitting the app does **not** stop the Gateway (launchd keeps it alive).
|
||||
- If a Gateway is already running on the configured port, the app attaches to
|
||||
it instead of starting a new one.
|
||||
|
||||
Logging:
|
||||
|
||||
- launchd stdout: `~/Library/Logs/openclaw/gateway.log` (profiles use
|
||||
`gateway-<profile>.log`)
|
||||
- launchd stderr: suppressed
|
||||
|
||||
## Version compatibility
|
||||
|
||||
The macOS app checks the Gateway version against its own version. Onboarding
|
||||
automatically runs managed setup when an existing CLI is missing or
|
||||
incompatible. Use **Retry setup** to repeat installation, or **Check again**
|
||||
after repairing an external CLI.
|
||||
|
||||
## State directory on macOS
|
||||
|
||||
Keep OpenClaw state on a local, non-synced disk. Avoid iCloud Drive and other
|
||||
cloud-synced folders; 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`, `--probe`, and `--json`
|
||||
(plus client-identity overrides; run with `--help` for the full list).
|
||||
`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
|
||||
openclaw --version
|
||||
|
||||
OPENCLAW_SKIP_CHANNELS=1 \
|
||||
OPENCLAW_SKIP_CANVAS_HOST=1 \
|
||||
openclaw gateway --port 18999 --bind loopback
|
||||
```
|
||||
|
||||
Then:
|
||||
|
||||
```bash
|
||||
openclaw gateway call health --url ws://127.0.0.1:18999 --timeout 3000
|
||||
```
|
||||
|
||||
## Related
|
||||
|
||||
- [macOS app](/platforms/macos)
|
||||
- [Gateway runbook](/gateway)
|
||||
116
docs/platforms/mac/canvas.md
Normal file
116
docs/platforms/mac/canvas.md
Normal file
@@ -0,0 +1,116 @@
|
||||
---
|
||||
summary: "Agent-controlled Canvas panel embedded via WKWebView + custom URL scheme"
|
||||
read_when:
|
||||
- Implementing the macOS Canvas panel
|
||||
- Adding agent controls for visual workspace
|
||||
- Debugging WKWebView canvas loads
|
||||
title: "Canvas"
|
||||
---
|
||||
|
||||
The macOS app embeds an agent-controlled **Canvas panel** using `WKWebView`, a
|
||||
lightweight visual workspace for HTML/CSS/JS, A2UI, and small interactive UI
|
||||
surfaces.
|
||||
|
||||
## Where Canvas lives
|
||||
|
||||
Canvas state is stored under Application Support:
|
||||
|
||||
- `~/Library/Application Support/OpenClaw/canvas/<session>/...`
|
||||
|
||||
The Canvas panel serves those files via a custom URL scheme,
|
||||
`openclaw-canvas://<session>/<path>`:
|
||||
|
||||
- `openclaw-canvas://main/` -> `<canvasRoot>/main/index.html`
|
||||
- `openclaw-canvas://main/assets/app.css` -> `<canvasRoot>/main/assets/app.css`
|
||||
- `openclaw-canvas://main/widgets/todo/` -> `<canvasRoot>/main/widgets/todo/index.html`
|
||||
|
||||
If no `index.html` exists at the root, the app shows a built-in scaffold page.
|
||||
|
||||
## Panel behavior
|
||||
|
||||
- Borderless, resizable panel anchored near the menu bar (or mouse cursor).
|
||||
- Remembers size/position per session.
|
||||
- Auto-reloads when local canvas files change.
|
||||
- Only one Canvas panel is visible at a time (session switches as needed).
|
||||
|
||||
Canvas can be disabled from Settings -> **Allow Canvas**. When disabled,
|
||||
canvas node commands return `CANVAS_DISABLED`.
|
||||
|
||||
## Agent API surface
|
||||
|
||||
Canvas is exposed via the Gateway WebSocket, so the agent can show/hide the
|
||||
panel, navigate to a path or URL, evaluate JavaScript, and capture a
|
||||
snapshot image:
|
||||
|
||||
```bash
|
||||
openclaw nodes canvas present --node <id>
|
||||
openclaw nodes canvas navigate --node <id> --url "/"
|
||||
openclaw nodes canvas eval --node <id> --js "document.title"
|
||||
openclaw nodes canvas snapshot --node <id>
|
||||
```
|
||||
|
||||
`canvas.navigate` accepts local canvas paths, `http(s)` URLs, and `file://`
|
||||
URLs. Passing `"/"` shows the local scaffold or `index.html`.
|
||||
|
||||
## A2UI in Canvas
|
||||
|
||||
A2UI is hosted by the Gateway canvas host and rendered inside the Canvas
|
||||
panel. When the Gateway advertises a Canvas host, the macOS app auto-navigates
|
||||
to the A2UI host page on first open.
|
||||
|
||||
Default A2UI host URL: `http://<gateway-host>:18789/__openclaw__/a2ui/`
|
||||
|
||||
### A2UI commands (v0.8)
|
||||
|
||||
Canvas accepts A2UI v0.8 server-to-client messages: `beginRendering`,
|
||||
`surfaceUpdate`, `dataModelUpdate`, `deleteSurface`. `createSurface` (v0.9) is
|
||||
not supported yet.
|
||||
|
||||
```bash
|
||||
cat > /tmp/a2ui-v0.8.jsonl <<'EOFA2'
|
||||
{"surfaceUpdate":{"surfaceId":"main","components":[{"id":"root","component":{"Column":{"children":{"explicitList":["title","content"]}}}},{"id":"title","component":{"Text":{"text":{"literalString":"Canvas (A2UI v0.8)"},"usageHint":"h1"}}},{"id":"content","component":{"Text":{"text":{"literalString":"If you can read this, A2UI push works."},"usageHint":"body"}}}]}}
|
||||
{"beginRendering":{"surfaceId":"main","root":"root"}}
|
||||
EOFA2
|
||||
|
||||
openclaw nodes canvas a2ui push --jsonl /tmp/a2ui-v0.8.jsonl --node <id>
|
||||
```
|
||||
|
||||
Quick smoke test:
|
||||
|
||||
```bash
|
||||
openclaw nodes canvas a2ui push --node <id> --text "Hello from A2UI"
|
||||
```
|
||||
|
||||
## Triggering agent runs from Canvas
|
||||
|
||||
Canvas can trigger new agent runs via `openclaw://agent?...` deep links:
|
||||
|
||||
```js
|
||||
window.location.href = "openclaw://agent?message=Review%20this%20design";
|
||||
```
|
||||
|
||||
Supported query parameters:
|
||||
|
||||
| Parameter | Meaning |
|
||||
| -------------------------- | ----------------------------------------------------- |
|
||||
| `message` | Prefilled agent prompt. |
|
||||
| `sessionKey` | Stable session identifier. |
|
||||
| `thinking` | Optional thinking profile. |
|
||||
| `deliver`, `to`, `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
|
||||
|
||||
- Canvas scheme blocks directory traversal; files must live under the session root.
|
||||
- Local Canvas content uses a custom scheme (no loopback server required).
|
||||
- External `http(s)` URLs are allowed only when explicitly navigated.
|
||||
|
||||
## Related
|
||||
|
||||
- [macOS app](/platforms/macos)
|
||||
- [WebChat](/web/webchat)
|
||||
72
docs/platforms/mac/child-process.md
Normal file
72
docs/platforms/mac/child-process.md
Normal file
@@ -0,0 +1,72 @@
|
||||
---
|
||||
summary: "Gateway lifecycle on macOS (launchd)"
|
||||
read_when:
|
||||
- Integrating the mac app with the gateway lifecycle
|
||||
title: "Gateway lifecycle on macOS"
|
||||
---
|
||||
|
||||
The macOS app manages the Gateway via **launchd** by default and does not
|
||||
spawn the Gateway as a child process. It first tries to attach to an
|
||||
already-running Gateway on the configured port; if none is reachable, it
|
||||
enables the launchd service via the external `openclaw` CLI (no embedded
|
||||
runtime). This gives reliable auto-start at login and restart on crashes.
|
||||
|
||||
Child-process mode (Gateway spawned directly by the app) is **not in use**
|
||||
today. If you need tighter coupling to the UI, run the Gateway manually in a
|
||||
terminal.
|
||||
|
||||
## Default behavior (launchd)
|
||||
|
||||
- The app installs a per-user LaunchAgent labeled `ai.openclaw.gateway` (or
|
||||
`ai.openclaw.<profile>` when using `--profile`/`OPENCLAW_PROFILE`).
|
||||
- When Local mode is enabled, the app ensures the LaunchAgent is loaded and
|
||||
starts the Gateway if needed.
|
||||
- Logs are written to the launchd gateway log path (visible in Debug Settings).
|
||||
|
||||
Common commands:
|
||||
|
||||
```bash
|
||||
launchctl kickstart -k gui/$UID/ai.openclaw.gateway
|
||||
launchctl bootout gui/$UID/ai.openclaw.gateway
|
||||
```
|
||||
|
||||
Replace the label with `ai.openclaw.<profile>` when running a named profile.
|
||||
|
||||
## Unsigned dev builds
|
||||
|
||||
`scripts/restart-mac.sh --no-sign` is for fast local builds without signing
|
||||
keys. To stop launchd from pointing at an unsigned relay binary, it writes
|
||||
`~/.openclaw/disable-launchagent`.
|
||||
|
||||
Signed runs of `scripts/restart-mac.sh` clear this override if the marker is
|
||||
present. To reset manually:
|
||||
|
||||
```bash
|
||||
rm ~/.openclaw/disable-launchagent
|
||||
```
|
||||
|
||||
## Attach-only mode
|
||||
|
||||
To force the macOS app to never install or manage launchd, launch it with
|
||||
`--attach-only` (or `--no-launchd`). This sets
|
||||
`~/.openclaw/disable-launchagent`, so the app only attaches to an already
|
||||
running Gateway. Toggle the same behavior in Debug Settings.
|
||||
|
||||
## Remote mode
|
||||
|
||||
Remote mode never starts a local Gateway. The app uses an SSH tunnel to the
|
||||
remote host and connects over that tunnel.
|
||||
|
||||
## Why we prefer launchd
|
||||
|
||||
- Auto-start at login.
|
||||
- Built-in restart/KeepAlive semantics.
|
||||
- Predictable logs and supervision.
|
||||
|
||||
If a true child-process mode is ever needed again, it should be documented as
|
||||
a separate, explicit dev-only mode.
|
||||
|
||||
## Related
|
||||
|
||||
- [macOS app](/platforms/macos)
|
||||
- [Gateway runbook](/gateway)
|
||||
106
docs/platforms/mac/dev-setup.md
Normal file
106
docs/platforms/mac/dev-setup.md
Normal file
@@ -0,0 +1,106 @@
|
||||
---
|
||||
summary: "Setup guide for developers working on the OpenClaw macOS app"
|
||||
read_when:
|
||||
- Setting up the macOS development environment
|
||||
title: "macOS dev setup"
|
||||
---
|
||||
|
||||
# macOS developer setup
|
||||
|
||||
Build and run the OpenClaw macOS application from source.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- **Xcode 26.2+** (Swift 6.2 toolchain), on the latest macOS available in
|
||||
Software Update.
|
||||
- **Node.js 24 & pnpm** for the gateway, CLI, and packaging scripts. Node
|
||||
22.19+ also works.
|
||||
|
||||
## 1. Install dependencies
|
||||
|
||||
```bash
|
||||
pnpm install
|
||||
```
|
||||
|
||||
## 2. Build and package the app
|
||||
|
||||
```bash
|
||||
./scripts/package-mac-app.sh
|
||||
```
|
||||
|
||||
Outputs `dist/OpenClaw.app`. Without an Apple Developer ID certificate, the
|
||||
script falls back to ad-hoc signing.
|
||||
|
||||
For dev run modes, signing flags, and Team ID troubleshooting, see
|
||||
[apps/macos/README.md](https://github.com/openclaw/openclaw/blob/main/apps/macos/README.md).
|
||||
Fast dev loop from repo root: `scripts/restart-mac.sh` (add `--no-sign` for
|
||||
ad-hoc signing; TCC permissions do not stick with `--no-sign`).
|
||||
|
||||
<Note>
|
||||
Ad-hoc signed apps may trigger security prompts. If the app crashes
|
||||
immediately with "Abort trap 6", see [Troubleshooting](#troubleshooting).
|
||||
</Note>
|
||||
|
||||
## 3. Install the CLI and Gateway
|
||||
|
||||
The packaged app embeds the canonical `scripts/install-cli.sh` installer. On a
|
||||
fresh profile, choose **This Mac** during onboarding; the app installs the
|
||||
matching user-space CLI and runtime before starting the Gateway wizard.
|
||||
|
||||
For manual development recovery, install the matching CLI yourself:
|
||||
|
||||
```bash
|
||||
npm install -g openclaw@<version>
|
||||
```
|
||||
|
||||
`pnpm add -g openclaw@<version>` and `bun add -g openclaw@<version>` also
|
||||
work. Node remains the recommended runtime for the Gateway itself.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Build fails: toolchain or SDK mismatch
|
||||
|
||||
The macOS app build expects the latest macOS SDK and the Swift 6.2 toolchain
|
||||
(Xcode 26.2+).
|
||||
|
||||
```bash
|
||||
xcodebuild -version
|
||||
xcrun swift --version
|
||||
```
|
||||
|
||||
If versions don't match, update macOS/Xcode and re-run the build.
|
||||
|
||||
### App crashes on permission grant
|
||||
|
||||
If the app crashes when you try to allow **Speech Recognition** or
|
||||
**Microphone** access, it may be a corrupted TCC cache or signature mismatch.
|
||||
|
||||
1. Reset TCC permissions for the debug bundle id:
|
||||
|
||||
```bash
|
||||
tccutil reset All ai.openclaw.mac.debug
|
||||
```
|
||||
|
||||
2. If that fails, temporarily change `BUNDLE_ID` in
|
||||
[`scripts/package-mac-app.sh`](https://github.com/openclaw/openclaw/blob/main/scripts/package-mac-app.sh)
|
||||
to force a clean slate from macOS.
|
||||
|
||||
### Gateway "Starting..." indefinitely
|
||||
|
||||
Check whether a zombie process holds the port:
|
||||
|
||||
```bash
|
||||
openclaw gateway status
|
||||
openclaw gateway stop
|
||||
|
||||
# If you're not using a LaunchAgent (dev mode / manual runs), find the listener:
|
||||
lsof -nP -iTCP:18789 -sTCP:LISTEN
|
||||
```
|
||||
|
||||
If a manual run holds the port, stop it (Ctrl+C), or kill the PID found above
|
||||
as a last resort.
|
||||
|
||||
## Related
|
||||
|
||||
- [macOS app](/platforms/macos)
|
||||
- [Install overview](/install)
|
||||
48
docs/platforms/mac/health.md
Normal file
48
docs/platforms/mac/health.md
Normal file
@@ -0,0 +1,48 @@
|
||||
---
|
||||
summary: "How the macOS app reports gateway/channel health states"
|
||||
read_when:
|
||||
- Debugging mac app health indicators
|
||||
title: "Health checks (macOS)"
|
||||
---
|
||||
|
||||
# Health checks on macOS
|
||||
|
||||
How to read the linked-channel health state from the menu bar app.
|
||||
|
||||
## Menu bar
|
||||
|
||||
Status dot:
|
||||
|
||||
- Green: linked + probe healthy.
|
||||
- Orange: linked but a channel probe reports degraded/not connected.
|
||||
- Red: not linked yet.
|
||||
|
||||
The secondary line reads "linked · auth 12m" or shows the failure reason.
|
||||
"Run Health Check Now" in the menu triggers an on-demand probe.
|
||||
|
||||
## Settings
|
||||
|
||||
- General tab shows a Health card: status dot, summary line (link state +
|
||||
auth age), and an optional failure detail line, with **Retry now** and
|
||||
**Open logs** buttons.
|
||||
- **Channels tab** surfaces per-channel status and controls (login QR,
|
||||
logout, probe, last disconnect/error) for WhatsApp and Telegram.
|
||||
|
||||
## How the probe works
|
||||
|
||||
The app calls the Gateway's `health` RPC over its existing WebSocket
|
||||
connection (not a CLI shell-out) every ~60s and on demand. The RPC loads
|
||||
creds and reports status without sending messages. The app caches the last
|
||||
good snapshot and the last error separately so the UI loads instantly and
|
||||
does not flicker while offline.
|
||||
|
||||
## When in doubt
|
||||
|
||||
Use the CLI flow in [Gateway health](/gateway/health) (`openclaw status`,
|
||||
`openclaw status --deep`, `openclaw health --json`) and tail
|
||||
`/tmp/openclaw/openclaw-*.log`, filtering for `web-heartbeat` / `web-reconnect`.
|
||||
|
||||
## Related
|
||||
|
||||
- [Gateway health](/gateway/health)
|
||||
- [macOS app](/platforms/macos)
|
||||
44
docs/platforms/mac/icon.md
Normal file
44
docs/platforms/mac/icon.md
Normal file
@@ -0,0 +1,44 @@
|
||||
---
|
||||
summary: "Menu bar icon states and animations for OpenClaw on macOS"
|
||||
read_when:
|
||||
- Changing menu bar icon behavior
|
||||
title: "Menu bar icon"
|
||||
---
|
||||
|
||||
# Menu Bar Icon States
|
||||
|
||||
Scope: macOS app (`apps/macos`). Rendering: `CritterIconRenderer.makeIcon(...)`. Animation/state wiring: `CritterStatusLabel` + `CritterStatusLabel+Behavior.swift`.
|
||||
|
||||
## States
|
||||
|
||||
| State | Trigger | Visual |
|
||||
| --------------------- | ----------------------------------------- | --------------------------------------------------------------------------------------------------- |
|
||||
| Idle | Default | Normal blink/wiggle animation |
|
||||
| Paused | `isPaused=true` | Status item uses `appearsDisabled`; no motion |
|
||||
| Voice wake (big ears) | Wake word heard | Ears scale to `1.9x` with `earHoles=true` (circular holes for readability); drops after silence |
|
||||
| Working | `isWorking=true` or an active `IconState` | Faster leg wiggle (`legWiggle` up to `1.0`) plus a small horizontal offset; additive to idle wiggle |
|
||||
|
||||
A tool-activity badge (SF Symbol puck, e.g. `chevron.left.slash.chevron.right` for exec) can render on top of the same critter icon when a session has an active job or tool. That badge comes from `IconState`/`ActivityKind`; see [Menu bar](/platforms/mac/menu-bar) for the full state model.
|
||||
|
||||
## Voice wake ears
|
||||
|
||||
- Trigger: `AppStateStore.shared.triggerVoiceEars(ttl: nil)`, called from the voice-wake capture pipeline (`VoiceWakeRuntime`) and from voice-wake debug/test tooling (`VoiceWakeTester`, `VoiceWakeOverlayController`).
|
||||
- Stop: `stopVoiceEars()`, called when capture finalizes.
|
||||
- Silence window before finalizing: `2.0s` normally, `5.0s` if only the trigger word was heard and no further speech followed (`VoiceWakeRuntime.silenceWindow` / `triggerOnlySilenceWindow`).
|
||||
- While boosted, idle blink/wiggle/leg/ear timers are suspended (`earBoostActive` gates the animation task in `CritterStatusLabel+Behavior`).
|
||||
|
||||
## Shapes and sizes
|
||||
|
||||
- Canvas: 18x18pt template image, rendered into a 36x36px bitmap backing store (2x) so the icon stays crisp on Retina.
|
||||
- Ear scale defaults to `1.0`; voice boost sets `earScale=1.9` and `earHoles=true` without changing the overall frame.
|
||||
- Leg scurry uses `legWiggle` up to `1.0` with a small horizontal jiggle.
|
||||
|
||||
## Behavioral notes
|
||||
|
||||
- No external CLI/broker toggle for ears or working state; both are driven internally by app signals (`AppState.setWorking`, `AppState.triggerVoiceEars`) to avoid accidental flapping.
|
||||
- Keep any new TTL short (well under 10s) so the icon returns to baseline quickly if a job hangs.
|
||||
|
||||
## Related
|
||||
|
||||
- [Menu bar](/platforms/mac/menu-bar)
|
||||
- [macOS app](/platforms/macos)
|
||||
59
docs/platforms/mac/logging.md
Normal file
59
docs/platforms/mac/logging.md
Normal file
@@ -0,0 +1,59 @@
|
||||
---
|
||||
summary: "OpenClaw logging: rolling diagnostics file log + unified log privacy flags"
|
||||
read_when:
|
||||
- Capturing macOS logs or investigating private data logging
|
||||
- Debugging voice wake/session lifecycle issues
|
||||
title: "macOS logging"
|
||||
---
|
||||
|
||||
# Logging (macOS)
|
||||
|
||||
## Rolling diagnostics file log (Debug pane)
|
||||
|
||||
The macOS app logs through swift-log (unified logging by default) and can also write a rotating local file log for durable capture (`DiagnosticsFileLog`).
|
||||
|
||||
- Enable: **Debug pane -> Logs -> App logging -> "Write rolling diagnostics log (JSONL)"** (off by default).
|
||||
- Verbosity: **Debug pane -> Logs -> App logging -> Verbosity** picker.
|
||||
- Location: `~/Library/Logs/OpenClaw/diagnostics.jsonl`.
|
||||
- Rotation: rotates at 5 MB; up to 5 backups suffixed `.1`...`.5` (oldest dropped).
|
||||
- Clear: **Debug pane -> Logs -> App logging -> "Clear"** deletes the active file and all backups.
|
||||
|
||||
Treat the file as sensitive; do not share it without review.
|
||||
|
||||
## Unified logging private data on macOS
|
||||
|
||||
Unified logging redacts most payloads unless a subsystem opts into `privacy -off`. This is controlled by a plist in `/Library/Preferences/Logging/Subsystems/` keyed by subsystem name. Only new log entries pick up the flag, so enable it before reproducing an issue. Background: [macOS logging privacy shenanigans](https://steipete.me/posts/2025/logging-privacy-shenanigans).
|
||||
|
||||
## Enable for OpenClaw (`ai.openclaw`)
|
||||
|
||||
Write the plist to a temp file first, then install it atomically as root:
|
||||
|
||||
```bash
|
||||
cat <<'EOF' >/tmp/ai.openclaw.plist
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>DEFAULT-OPTIONS</key>
|
||||
<dict>
|
||||
<key>Enable-Private-Data</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
EOF
|
||||
sudo install -m 644 -o root -g wheel /tmp/ai.openclaw.plist /Library/Preferences/Logging/Subsystems/ai.openclaw.plist
|
||||
```
|
||||
|
||||
No reboot required; logd picks up the file quickly, but only new log lines include private payloads. View the richer output with `./scripts/clawlog.sh --category WebChat --last 5m` (`--last`/`-l` sets the time range, default `5m`; `--category`/`-c` filters by category).
|
||||
|
||||
## Disable after debugging
|
||||
|
||||
- Remove the override: `sudo rm /Library/Preferences/Logging/Subsystems/ai.openclaw.plist`.
|
||||
- Optionally run `sudo log config --reload` to force logd to drop the override immediately.
|
||||
- This surface can include phone numbers and message bodies; keep the plist in place only while actively needed.
|
||||
|
||||
## Related
|
||||
|
||||
- [macOS app](/platforms/macos)
|
||||
- [Gateway logging](/gateway/logging)
|
||||
92
docs/platforms/mac/menu-bar.md
Normal file
92
docs/platforms/mac/menu-bar.md
Normal file
@@ -0,0 +1,92 @@
|
||||
---
|
||||
summary: "Menu bar status logic and what is surfaced to users"
|
||||
read_when:
|
||||
- Tweaking mac menu UI or status logic
|
||||
title: "Menu bar"
|
||||
---
|
||||
|
||||
## What is shown
|
||||
|
||||
- The current agent work state renders in the menu bar icon and in the first status row of the menu.
|
||||
- Health status is hidden while work is active; it returns once all sessions are idle.
|
||||
- A root "Context" item opens a submenu with recent sessions instead of expanding them in the root menu.
|
||||
- A "Nodes" block in the root menu lists paired **devices** only (from `node.list`), not client/presence entries.
|
||||
- A root "Usage" section appears below Context when provider usage snapshots are available, followed by cost details when available.
|
||||
|
||||
## State model
|
||||
|
||||
- Source: `WorkActivityStore` (`apps/macos/Sources/OpenClaw/WorkActivityStore.swift`).
|
||||
- Events arrive as `ControlAgentEvent` with a `runId`; the handler (`ControlChannel.routeWorkActivity`) reads `sessionKey` from the event payload and defaults to `"main"` if absent.
|
||||
- Priority: the main session (`sessionKey == "main"` by default) always wins. If main is active, its state shows immediately. If main is idle, the most recently active non-main session shows instead. The store does not flip mid-activity; it only switches when the current session goes idle or main becomes active.
|
||||
- Activity kinds:
|
||||
- `job`: high-level command execution (`state: started|streaming|done|error|...`).
|
||||
- `tool`: `phase: start|result` with `name`, optional `meta`/`args`.
|
||||
|
||||
## IconState enum (Swift)
|
||||
|
||||
- `idle`
|
||||
- `workingMain(ActivityKind)`
|
||||
- `workingOther(ActivityKind)`
|
||||
- `overridden(ActivityKind)` (debug override)
|
||||
|
||||
### ActivityKind -> badge symbol
|
||||
|
||||
`ActivityKind` wraps a `ToolKind` (`bash`, `read`, `write`, `edit`, `attach`, `other`) or a bare `job`. Each maps to an SF Symbol badge drawn over the critter icon (`IconState.badgeSymbolName`):
|
||||
|
||||
| Kind | Symbol |
|
||||
| --------------- | ---------------------------------- |
|
||||
| `bash` | `chevron.left.slash.chevron.right` |
|
||||
| `read` | `doc` |
|
||||
| `write` | `pencil` |
|
||||
| `edit` | `pencil.tip` |
|
||||
| `attach` | `paperclip` |
|
||||
| `other` / `job` | `gearshape.fill` |
|
||||
|
||||
### Visual mapping
|
||||
|
||||
- `idle`: normal critter, no badge.
|
||||
- `workingMain`: badge with symbol, full tint (`.primary` prominence), leg "working" animation.
|
||||
- `workingOther`: badge with symbol, muted tint (`.secondary` prominence), no scurry.
|
||||
- `overridden`: uses the chosen symbol/tint regardless of real activity.
|
||||
|
||||
## Context submenu
|
||||
|
||||
- The root menu shows one "Context" row with a session count/status; it opens a submenu (`MenuSessionsInjector`).
|
||||
- The submenu header shows the active session count for the last 24 hours.
|
||||
- Each session row keeps its token bar, age, preview, thinking/verbose toggle, reset, compact, and delete actions.
|
||||
- Loading, disconnected, and session-load error messages render inside the Context submenu.
|
||||
- Usage and cost sections stay root-level below Context so they remain glanceable without opening the submenu.
|
||||
|
||||
## Status row text (menu)
|
||||
|
||||
- While work is active: `<Session role> · <activity label>` (`"\(roleLabel) · \(activity.label)"` in `MenuContentView`), where role label is `Main` or `Other`.
|
||||
- When idle: falls back to the health summary.
|
||||
|
||||
## Event ingestion
|
||||
|
||||
- Source: control-channel `agent` events, routed by `ControlChannel.routeWorkActivity(from:)`.
|
||||
- Parsed fields:
|
||||
- `stream: "job"` with `data.state` for start/stop.
|
||||
- `stream: "tool"` with `data.phase`, `data.name`, optional `data.meta`/`data.args`.
|
||||
- Tool labels come from `ToolDisplayRegistry.resolve(name:args:meta:)`; unresolved names fall back to the raw tool name.
|
||||
|
||||
## Debug override
|
||||
|
||||
- Settings > Debug > "Icon override" picker:
|
||||
- `System (auto)` (default)
|
||||
- `Working: main` / `Working: other` (per tool kind: bash, read, write, edit, other)
|
||||
- `Idle`
|
||||
- Stored under `UserDefaults` key `openclaw.iconOverride`; mapped to `IconState.overridden`.
|
||||
|
||||
## Testing checklist
|
||||
|
||||
- Trigger main session job: icon switches immediately and status row shows the main label.
|
||||
- Trigger non-main session job while main is idle: icon/status shows the non-main session; stays stable until it finishes.
|
||||
- Start main while another session is active: icon flips to main instantly.
|
||||
- Rapid tool bursts: badge does not flicker (2s grace window before clearing a finished tool, `WorkActivityStore.toolResultGrace`).
|
||||
- Health row reappears once all sessions are idle.
|
||||
|
||||
## Related
|
||||
|
||||
- [macOS app](/platforms/macos)
|
||||
- [Menu bar icon](/platforms/mac/icon)
|
||||
68
docs/platforms/mac/peekaboo.md
Normal file
68
docs/platforms/mac/peekaboo.md
Normal file
@@ -0,0 +1,68 @@
|
||||
---
|
||||
summary: "PeekabooBridge integration for macOS UI automation"
|
||||
read_when:
|
||||
- Hosting PeekabooBridge in OpenClaw.app
|
||||
- Integrating Peekaboo via Swift Package Manager
|
||||
- Changing PeekabooBridge protocol/paths
|
||||
- Deciding between PeekabooBridge, Codex Computer Use, and cua-driver MCP
|
||||
title: "Peekaboo bridge"
|
||||
---
|
||||
|
||||
OpenClaw can host **PeekabooBridge** as a local, permission-aware UI automation broker (`PeekabooBridgeHostCoordinator`, backed by the `steipete/Peekaboo` Swift package). This lets the `peekaboo` CLI drive UI automation while reusing the macOS app's TCC permissions.
|
||||
|
||||
## What this is (and is not)
|
||||
|
||||
- **Host**: OpenClaw.app can act as a PeekabooBridge host.
|
||||
- **Client**: the `peekaboo` CLI (there is no separate `openclaw ui ...` surface).
|
||||
- **UI**: visual overlays stay in Peekaboo.app; OpenClaw is a thin broker host.
|
||||
|
||||
## Relationship to other desktop-control paths
|
||||
|
||||
OpenClaw has three desktop-control paths that intentionally stay separate:
|
||||
|
||||
- **PeekabooBridge host**: OpenClaw.app hosts the local PeekabooBridge socket. The `peekaboo` CLI is the client and uses OpenClaw.app's macOS permissions for screenshots, clicks, menus, dialogs, Dock actions, and window management.
|
||||
- **Codex Computer Use**: the bundled `codex` plugin checks and can install Codex's `computer-use` MCP plugin (`extensions/codex/src/app-server/computer-use.ts`), then lets Codex own native desktop-control tool calls during Codex-mode turns. OpenClaw does not proxy those actions through PeekabooBridge.
|
||||
- **Direct `cua-driver` MCP**: OpenClaw can register TryCua's upstream `cua-driver mcp` server as a normal MCP server, giving agents the CUA driver's own schemas and pid/window/element-index workflow without routing through the Codex marketplace or the PeekabooBridge socket.
|
||||
|
||||
Use Peekaboo for the broad macOS automation surface via OpenClaw.app's permission-aware bridge host. Use Codex Computer Use when a Codex-mode agent should rely on Codex's native plugin. Use direct `cua-driver mcp` to expose the CUA driver to any OpenClaw-managed runtime as a normal MCP server.
|
||||
|
||||
## Enable the bridge
|
||||
|
||||
In the macOS app: **Settings -> Enable Peekaboo Bridge**.
|
||||
|
||||
When enabled, OpenClaw starts a local UNIX socket server at `~/Library/Application Support/OpenClaw/<socket-name>`. If disabled, the host stops and `peekaboo` falls back to other available hosts. The coordinator also maintains legacy socket symlinks (`clawdbot`, `clawdis`, `moltbot` under Application Support) pointing at the current socket for older `peekaboo` installs.
|
||||
|
||||
## Client discovery order
|
||||
|
||||
Peekaboo clients typically try hosts in this order:
|
||||
|
||||
1. Peekaboo.app (full UX)
|
||||
2. Claude.app (if installed)
|
||||
3. OpenClaw.app (thin broker)
|
||||
|
||||
Use `peekaboo bridge status --verbose` to see which host is active and which socket path is in use. Override with:
|
||||
|
||||
```bash
|
||||
export PEEKABOO_BRIDGE_SOCKET=/path/to/bridge.sock
|
||||
```
|
||||
|
||||
## Security and permissions
|
||||
|
||||
- The bridge validates **caller code signatures**; an allowlist of TeamIDs is enforced (Peekaboo host TeamID plus the running app's own TeamID).
|
||||
- Prefer the signed bridge/app identity over a generic `node` runtime for Accessibility. Granting Accessibility to `node` lets any package launched by that Node executable inherit GUI automation access; see [macOS permissions](/platforms/mac/permissions#accessibility-grants-for-node-and-cli-runtimes).
|
||||
- Requests time out after 10 seconds (`requestTimeoutSec: 10`).
|
||||
- If required permissions are missing, the bridge returns a clear error message rather than launching System Settings.
|
||||
|
||||
## Snapshot behavior (automation)
|
||||
|
||||
Snapshots are stored in memory with a 10-minute validity window and a cap of 50 snapshots (`InMemorySnapshotManager`); artifacts are not deleted on cleanup. If you need longer retention, re-capture from the client.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- If `peekaboo` reports "bridge client is not authorized", ensure the client is properly signed or run the host with `PEEKABOO_ALLOW_UNSIGNED_SOCKET_CLIENTS=1` in **debug** mode only.
|
||||
- If no hosts are found, open one of the host apps (Peekaboo.app or OpenClaw.app) and confirm permissions are granted.
|
||||
|
||||
## Related
|
||||
|
||||
- [macOS app](/platforms/macos)
|
||||
- [macOS permissions](/platforms/mac/permissions)
|
||||
59
docs/platforms/mac/permissions.md
Normal file
59
docs/platforms/mac/permissions.md
Normal file
@@ -0,0 +1,59 @@
|
||||
---
|
||||
summary: "macOS permission persistence (TCC) and signing requirements"
|
||||
read_when:
|
||||
- Debugging missing or stuck macOS permission prompts
|
||||
- Deciding whether to grant Accessibility to node or a CLI runtime
|
||||
- Packaging or signing the macOS app
|
||||
- Changing bundle IDs or app install paths
|
||||
title: "macOS permissions"
|
||||
---
|
||||
|
||||
macOS permission grants are fragile. TCC associates a permission grant with the app's code signature, bundle identifier, and on-disk path. If any of those change, macOS treats the app as new and may drop or hide prompts.
|
||||
|
||||
## Requirements for stable permissions
|
||||
|
||||
- Same path: run the app from a fixed location (for OpenClaw, `dist/OpenClaw.app`).
|
||||
- Same bundle identifier: OpenClaw's bundle ID is `ai.openclaw.mac`; changing it creates a new permission identity.
|
||||
- Signed app: unsigned or ad-hoc signed builds do not persist permissions.
|
||||
- Consistent signature: use a real Apple Development or Developer ID certificate so the signature stays stable across rebuilds.
|
||||
|
||||
Ad-hoc signatures generate a new identity every build. macOS forgets previous grants, and prompts can disappear entirely until the stale entries are cleared.
|
||||
|
||||
## Accessibility grants for Node and CLI runtimes
|
||||
|
||||
Prefer granting Accessibility to OpenClaw.app, Peekaboo.app, or another signed helper with its own bundle identifier instead of a generic `node` binary.
|
||||
|
||||
macOS TCC grants Accessibility to the code identity of the process it sees. If a Homebrew, nvm, pnpm, or npm workflow causes a shared `node` executable to receive Accessibility, any JavaScript package launched through that same executable may inherit GUI automation privileges.
|
||||
|
||||
Treat a `node` entry in System Settings as broad permission for that Node runtime, not as permission for one npm package. Avoid granting Accessibility to `node` unless you trust every script and package launched through that exact Node install.
|
||||
|
||||
If you accidentally granted Accessibility to `node`, remove that entry from System Settings -> Privacy & Security -> Accessibility. Then grant the signed app or helper that should own UI automation.
|
||||
|
||||
## Recovery checklist when prompts disappear
|
||||
|
||||
1. Quit the app.
|
||||
2. Remove the app entry in System Settings -> Privacy & Security.
|
||||
3. Relaunch the app from the same path and re-grant permissions.
|
||||
4. If the prompt still does not appear, reset TCC entries with `tccutil` and try again.
|
||||
5. Some permissions only reappear after a full macOS restart.
|
||||
|
||||
Example resets (using OpenClaw's bundle ID, `ai.openclaw.mac`):
|
||||
|
||||
```bash
|
||||
sudo tccutil reset Accessibility ai.openclaw.mac
|
||||
sudo tccutil reset ScreenCapture ai.openclaw.mac
|
||||
sudo tccutil reset AppleEvents
|
||||
```
|
||||
|
||||
## Files and folders permissions (Desktop/Documents/Downloads)
|
||||
|
||||
macOS may also gate Desktop, Documents, and Downloads for terminal/background processes. If file reads or directory listings hang, grant access to the same process context that performs file operations (for example Terminal/iTerm, LaunchAgent-launched app, or SSH process).
|
||||
|
||||
Workaround: move files into the OpenClaw workspace (`~/.openclaw/workspace`) if you want to avoid per-folder grants.
|
||||
|
||||
If you are testing permissions, always sign with a real certificate. Ad-hoc builds are only acceptable for quick local runs where permissions do not matter.
|
||||
|
||||
## Related
|
||||
|
||||
- [macOS app](/platforms/macos)
|
||||
- [macOS signing](/platforms/mac/signing)
|
||||
117
docs/platforms/mac/remote.md
Normal file
117
docs/platforms/mac/remote.md
Normal file
@@ -0,0 +1,117 @@
|
||||
---
|
||||
summary: "macOS app flow for controlling a remote OpenClaw gateway"
|
||||
read_when:
|
||||
- Setting up or debugging remote mac control
|
||||
title: "Remote control"
|
||||
---
|
||||
|
||||
This flow lets the macOS app act as a full remote control for an OpenClaw gateway running on another host (desktop/server). The app connects directly to trusted LAN/Tailnet gateway URLs, or manages an SSH tunnel when the remote gateway is loopback-only. Health checks, Voice Wake forwarding, and Web Chat reuse the same remote configuration from _Settings -> General_.
|
||||
|
||||
## Modes
|
||||
|
||||
- **Local (this Mac)**: everything runs on the laptop; no SSH involved.
|
||||
- **Remote over SSH (default)**: OpenClaw commands run on the remote host. The app opens an SSH connection with `-o BatchMode`, your chosen identity/key, and a local port-forward.
|
||||
- **Remote direct (ws/wss)**: no SSH tunnel; the app connects to the gateway URL directly (LAN, Tailscale, Tailscale Serve, or a public HTTPS reverse proxy).
|
||||
|
||||
## Remote transports
|
||||
|
||||
- **SSH tunnel** (default): uses `ssh -N -L ...` to forward the gateway port to localhost. The gateway sees the node's IP as `127.0.0.1` because the tunnel is loopback.
|
||||
- **Direct (ws/wss)**: connects straight to the gateway URL. The gateway sees the real client IP.
|
||||
|
||||
The app disables SSH connection multiplexing and post-authentication backgrounding for its own SSH processes so it can monitor and restart the exact process, even if the selected alias enables `ControlMaster` or `ForkAfterAuthentication`.
|
||||
|
||||
SSH host-key verification is strict by default because gateway credentials travel through this tunnel. To opt into a managed SSH alias's own trust behavior, set `--ssh-host-key-policy openssh` via `openclaw-mac configure-remote`, or set `gateway.remote.sshHostKeyPolicy` to `"openssh"` directly. Review the alias and any matching `Host *` or system configuration before opting in. Changing the SSH target (in the app or via `configure-remote`) resets the policy back to `strict` unless you explicitly opt in again for the new target.
|
||||
|
||||
In SSH tunnel mode, discovered LAN/tailnet hostnames save 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 loopback transport. When discovery returns both raw Tailnet IPs and stable hostnames, the app prefers Tailscale MagicDNS or LAN names so 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.
|
||||
|
||||
Browser automation in remote mode is owned by the CLI node host, not the native macOS app node. The app starts the installed node host service when possible; to enable browser control from that Mac, install/start it with `openclaw node install ...` and `openclaw node start` (or run `openclaw node run ...` in the foreground), then target that browser-capable node.
|
||||
|
||||
## Prereqs on the remote host
|
||||
|
||||
1. Install Node + pnpm and build/install the OpenClaw CLI (`pnpm install && pnpm build && pnpm link --global`).
|
||||
2. Ensure `openclaw` is on PATH for non-interactive shells (symlink into `/usr/local/bin` or `/opt/homebrew/bin` if needed).
|
||||
3. For SSH transport: set up key-based SSH auth. Tailscale IPs are recommended for stable reachability off-LAN.
|
||||
|
||||
## macOS app setup
|
||||
|
||||
To preconfigure the app without the welcome flow, over SSH:
|
||||
|
||||
```bash
|
||||
openclaw-mac configure-remote \
|
||||
--ssh-target user@gateway-host \
|
||||
--local-port 18789 \
|
||||
--remote-port 18789 \
|
||||
--token "$OPENCLAW_GATEWAY_TOKEN"
|
||||
```
|
||||
|
||||
Or for a gateway already reachable on a trusted LAN or Tailnet, skip SSH entirely:
|
||||
|
||||
```bash
|
||||
openclaw-mac configure-remote \
|
||||
--direct-url ws://192.168.0.202:18789 \
|
||||
--token "$OPENCLAW_GATEWAY_TOKEN"
|
||||
```
|
||||
|
||||
Both forms write `~/.openclaw/openclaw.json`, mark onboarding complete, and let the app own the selected transport on next start. `--local-port`/`--remote-port` default to `18789`. Other flags: `--password`, `--identity <path>`, `--ssh-host-key-policy <strict|openssh>`, `--project-root <path>`, `--cli-path <path>`, `--json`. Run `openclaw-mac configure-remote --help` for the full reference.
|
||||
|
||||
To configure from the UI instead:
|
||||
|
||||
1. Open _Settings -> General_.
|
||||
2. Under **OpenClaw runs**, pick **Remote** and set:
|
||||
- **Transport**: **SSH tunnel** or **Direct (ws/wss)**.
|
||||
- **SSH target**: `user@host` (optional `:port`). If the gateway is on the same LAN and advertises Bonjour, pick it from the discovered list to auto-fill this field.
|
||||
- **Gateway URL** (Direct only): `wss://gateway.example.ts.net` (or `ws://...` for local/LAN).
|
||||
- **Identity file** (advanced): path to your key.
|
||||
- **Project root** (advanced): remote checkout path used for commands.
|
||||
- **CLI path** (advanced): optional path to a runnable `openclaw` entrypoint/binary (auto-filled when advertised).
|
||||
3. Hit **Test remote**. Success means the remote `openclaw status --json` ran correctly. Failures usually mean PATH/CLI issues; exit 127 means the CLI was not found remotely.
|
||||
4. Health checks and Web Chat now run through the selected transport automatically.
|
||||
|
||||
## Web Chat
|
||||
|
||||
- **SSH tunnel**: connects to the gateway over the forwarded WebSocket control port (default 18789).
|
||||
- **Direct (ws/wss)**: connects straight to the configured gateway URL.
|
||||
- There is no separate Web Chat HTTP server.
|
||||
|
||||
## Permissions
|
||||
|
||||
- The remote host needs the same TCC approvals as local (Automation, Accessibility, Screen Recording, Microphone, Speech Recognition, Notifications). Run onboarding on that machine once to grant them.
|
||||
- Nodes advertise their permission state via `node.list` / `node.describe` so agents know what is available.
|
||||
|
||||
## Security notes
|
||||
|
||||
- Prefer loopback binds on the remote host and connect via SSH, Tailscale Serve, or a trusted Tailnet/LAN direct URL.
|
||||
- SSH tunneling requires an already-trusted host key by default. Trust the host key first (add it to the configured known-hosts file), or explicitly set `gateway.remote.sshHostKeyPolicy: "openssh"` for a managed alias whose OpenSSH trust policy you accept.
|
||||
- If you bind the Gateway to a non-loopback interface, require valid Gateway auth: token, password, or an identity-aware reverse proxy with `gateway.auth.mode: "trusted-proxy"`.
|
||||
- See [Security](/gateway/security) and [Tailscale](/gateway/tailscale).
|
||||
|
||||
## WhatsApp login flow (remote)
|
||||
|
||||
- Run `openclaw channels login --channel whatsapp --verbose` **on the remote host**. Scan the QR with WhatsApp on your phone.
|
||||
- Re-run login on that host if auth expires. The health check surfaces link problems.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Symptom | Cause / fix |
|
||||
| ------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `exit 127` / not found | `openclaw` is not on PATH for non-login shells. Add it to `/etc/paths`, your shell rc, or symlink into `/usr/local/bin`/`/opt/homebrew/bin`. |
|
||||
| Health probe failed | Check SSH reachability, PATH, and that Baileys (WhatsApp) is logged in (`openclaw status --json`). |
|
||||
| Web Chat stuck | Confirm the gateway is running on the remote host and the forwarded port matches the gateway WS port; the UI requires a healthy WS connection. |
|
||||
| Node IP shows `127.0.0.1` | Expected with the SSH tunnel. Switch **Transport** to **Direct (ws/wss)** if you want the gateway to see the real client IP. |
|
||||
| Dashboard works but Mac capabilities are offline | The operator/control connection is healthy, but the companion node connection is not connected or is missing its command surface. Open the menu bar device section and check whether the Mac is `paired · disconnected`. For `wss://*.ts.net` Tailscale Serve endpoints, the app detects stale legacy TLS leaf pins after certificate rotation, clears the stale pin once macOS trusts the new certificate, and retries automatically. If the certificate is not system-trusted or the host is not a Tailscale Serve name, set `gateway.remote.tlsFingerprint` to the expected certificate fingerprint, review the certificate, or switch to **Remote over SSH**. |
|
||||
| Voice Wake | Trigger phrases forward automatically in remote mode; no separate forwarder is needed. |
|
||||
|
||||
## Notification sounds
|
||||
|
||||
Pick sounds per notification from scripts with `openclaw nodes notify`, for example:
|
||||
|
||||
```bash
|
||||
openclaw nodes notify --node <id> --title "Ping" --body "Remote gateway ready" --sound Glass
|
||||
```
|
||||
|
||||
There is no global default-sound toggle in the app; callers choose a sound (or none) per request.
|
||||
|
||||
## Related
|
||||
|
||||
- [macOS app](/platforms/macos)
|
||||
- [Remote access](/gateway/remote)
|
||||
42
docs/platforms/mac/signing.md
Normal file
42
docs/platforms/mac/signing.md
Normal file
@@ -0,0 +1,42 @@
|
||||
---
|
||||
summary: "Signing steps for macOS debug builds generated by packaging scripts"
|
||||
read_when:
|
||||
- Building or signing mac debug builds
|
||||
title: "macOS signing"
|
||||
---
|
||||
|
||||
# mac signing (debug builds)
|
||||
|
||||
[`scripts/package-mac-app.sh`](https://github.com/openclaw/openclaw/blob/main/scripts/package-mac-app.sh) builds and packages the app to a fixed path (`dist/OpenClaw.app`), then calls [`scripts/codesign-mac-app.sh`](https://github.com/openclaw/openclaw/blob/main/scripts/codesign-mac-app.sh) to sign it. TCC permissions are tied to the bundle ID and code signature; keeping both stable (and the app at a fixed path) across rebuilds keeps macOS from forgetting TCC grants (notifications, accessibility, screen recording, mic, speech).
|
||||
|
||||
- Debug bundle identifier defaults to `ai.openclaw.mac.debug` (override with `BUNDLE_ID=...`).
|
||||
- Node: `>=22.19.0 <23` or `>=23.11.0` (repo `package.json` `engines`). The packager also builds the Control UI (`pnpm ui:build`).
|
||||
- Requires a real signing identity by default; the codesign script exits with an error if none is found and `ALLOW_ADHOC_SIGNING` is not set. Ad-hoc signing (`SIGN_IDENTITY="-"`) is explicit opt-in and does not persist TCC permissions across rebuilds. See [macOS permissions](/platforms/mac/permissions).
|
||||
- Reads `SIGN_IDENTITY` from the environment (e.g. `export SIGN_IDENTITY="Apple Development: Your Name (TEAMID)"`, or a Developer ID Application cert). Without it, `codesign-mac-app.sh` auto-selects an identity in this order: Developer ID Application, Apple Distribution, Apple Development, then the first valid codesigning identity found.
|
||||
- `CODESIGN_TIMESTAMP=auto` (default) enables trusted timestamps only for Developer ID Application signatures. Set `on`/`off` to force either way.
|
||||
- Stamps Info.plist with `OpenClawBuildTimestamp` (ISO8601 UTC) and `OpenClawGitCommit` (short hash, `unknown` if unavailable) so the About tab can show build, git, and debug/release channel.
|
||||
- Runs a Team ID audit after signing and fails if any Mach-O inside the bundle has a different Team ID. Set `SKIP_TEAM_ID_CHECK=1` to bypass.
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
# from repo root
|
||||
scripts/package-mac-app.sh # auto-selects identity; errors if none found
|
||||
SIGN_IDENTITY="Developer ID Application: Your Name" scripts/package-mac-app.sh # real cert
|
||||
ALLOW_ADHOC_SIGNING=1 scripts/package-mac-app.sh # ad-hoc (permissions will not stick)
|
||||
SIGN_IDENTITY="-" scripts/package-mac-app.sh # explicit ad-hoc (same caveat)
|
||||
DISABLE_LIBRARY_VALIDATION=1 scripts/package-mac-app.sh # dev-only Sparkle Team ID mismatch workaround
|
||||
```
|
||||
|
||||
### Ad-hoc signing note
|
||||
|
||||
`SIGN_IDENTITY="-"` disables the Hardened Runtime (`--options runtime`) to prevent crashes when the app loads embedded frameworks (like Sparkle) that do not share the same Team ID. Ad-hoc signatures also break TCC permission persistence; see [macOS permissions](/platforms/mac/permissions) for recovery steps.
|
||||
|
||||
## Build metadata for About
|
||||
|
||||
The About tab reads `OpenClawBuildTimestamp` and `OpenClawGitCommit` from Info.plist to show version, build date, git commit, and whether the build is DEBUG (via `#if DEBUG`). Re-run the packager after code changes to refresh these values.
|
||||
|
||||
## Related
|
||||
|
||||
- [macOS app](/platforms/macos)
|
||||
- [macOS permissions](/platforms/mac/permissions)
|
||||
37
docs/platforms/mac/skills.md
Normal file
37
docs/platforms/mac/skills.md
Normal file
@@ -0,0 +1,37 @@
|
||||
---
|
||||
summary: "macOS Skills settings UI and gateway-backed status"
|
||||
read_when:
|
||||
- Updating the macOS Skills settings UI
|
||||
- Changing skills gating or install behavior
|
||||
title: "Skills (macOS)"
|
||||
---
|
||||
|
||||
The macOS app surfaces OpenClaw skills via the gateway; it does not parse skills locally.
|
||||
|
||||
## Data source
|
||||
|
||||
- `skills.status` (gateway) returns all skills plus eligibility and missing requirements, including allowlist blocks for bundled skills.
|
||||
- Requirements come from `metadata.openclaw.requires` in each `SKILL.md`.
|
||||
|
||||
## Install actions
|
||||
|
||||
- `metadata.openclaw.install` defines install options (brew/node/go/uv/download).
|
||||
- The app calls `skills.install` to run installers on the gateway host.
|
||||
- Operator-owned `security.installPolicy` (`enabled`, `targets`, `exec`) can block gateway-backed skill installs before installer metadata runs. Built-in dangerous-code scanning (used for plugin installs) is not wired into the skill install flow.
|
||||
- If every install option is `download`, the gateway surfaces all download choices.
|
||||
- Otherwise the gateway picks one preferred installer using current install preferences (`skills.install.preferBrew`, `skills.install.nodeManager`) and host binaries: Homebrew first when `preferBrew` is enabled and `brew` is present, then `uv`, then the configured node manager, then Homebrew again if available (even without `preferBrew`), then `go`, then `download`.
|
||||
- Node install labels reflect the configured node manager, including `yarn`.
|
||||
|
||||
## Env/API keys
|
||||
|
||||
- The app stores keys in `~/.openclaw/openclaw.json` under `skills.entries.<skillKey>`.
|
||||
- `skills.update` patches `enabled`, `apiKey`, and `env`.
|
||||
|
||||
## Remote mode
|
||||
|
||||
- Install and config updates happen on the gateway host, not the local Mac.
|
||||
|
||||
## Related
|
||||
|
||||
- [Skills](/tools/skills)
|
||||
- [macOS app](/platforms/macos)
|
||||
55
docs/platforms/mac/voice-overlay.md
Normal file
55
docs/platforms/mac/voice-overlay.md
Normal file
@@ -0,0 +1,55 @@
|
||||
---
|
||||
summary: "Voice overlay lifecycle when wake-word and push-to-talk overlap"
|
||||
read_when:
|
||||
- Adjusting voice overlay behavior
|
||||
title: "Voice overlay"
|
||||
---
|
||||
|
||||
# Voice Overlay Lifecycle (macOS)
|
||||
|
||||
Audience: macOS app contributors. Goal: keep the voice overlay predictable when wake-word and push-to-talk overlap.
|
||||
|
||||
## Behavior
|
||||
|
||||
- If the overlay is already visible from wake-word and the user presses the hotkey, the hotkey session adopts the existing text instead of resetting it. The overlay stays up while the hotkey is held. On release: send if there is trimmed text, otherwise dismiss.
|
||||
- Wake-word alone still auto-sends on silence; push-to-talk sends immediately on release.
|
||||
|
||||
## Implementation
|
||||
|
||||
- `VoiceSessionCoordinator` (`apps/macos/Sources/OpenClaw/VoiceSessionCoordinator.swift`) is the single owner of the active voice session. It is a `@MainActor @Observable` singleton, not an actor. API: `startSession`, `updatePartial`, `finalize`, `sendNow`, `dismiss`, `updateLevel`, `snapshot`. Each session carries a `UUID` token; calls with a stale or mismatched token are dropped.
|
||||
- `VoiceWakeOverlayController` (`VoiceWakeOverlayController+Session.swift`) renders the overlay and forwards user actions (`requestSend`, `dismiss`) back through the coordinator via the session token. It never owns the session state itself.
|
||||
- Push-to-talk (`VoicePushToTalk.begin()`) adopts any visible overlay text as `adoptedPrefix` (via `VoiceSessionCoordinator.shared.snapshot()`) so pressing the hotkey while the wake overlay is up keeps the text and appends new speech. On release, it waits up to 1.5s for a final transcript before falling back to the current text.
|
||||
- On `dismiss`, the overlay calls `VoiceSessionCoordinator.overlayDidDismiss`, which triggers `VoiceWakeRuntime.refresh(state:)` so manual X-dismiss, empty-text dismiss, and post-send dismiss all resume wake-word listening.
|
||||
- Unified send path: if trimmed text is empty, dismiss; otherwise `sendNow` plays the send chime once, forwards via `VoiceWakeForwarder`, then dismisses.
|
||||
|
||||
## Logging
|
||||
|
||||
Voice subsystem is `ai.openclaw`; each component logs under its own category:
|
||||
|
||||
| Category | Component |
|
||||
| ----------------------- | ----------------------------------------------- |
|
||||
| `voicewake.coordinator` | `VoiceSessionCoordinator` |
|
||||
| `voicewake.overlay` | `VoiceWakeOverlayController`/`VoiceWakeOverlay` |
|
||||
| `voicewake.ptt` | Push-to-talk hotkey and capture |
|
||||
| `voicewake.runtime` | Wake-word runtime |
|
||||
| `voicewake.chime` | Chime playback |
|
||||
| `voicewake.sync` | Global settings sync |
|
||||
| `voicewake.forward` | Transcript forwarding |
|
||||
| `voicewake.meter` | Mic level monitor |
|
||||
|
||||
## Debugging checklist
|
||||
|
||||
- Stream logs while reproducing a sticky overlay:
|
||||
|
||||
```bash
|
||||
sudo log stream --predicate 'subsystem == "ai.openclaw" AND category CONTAINS "voicewake"' --level info --style compact
|
||||
```
|
||||
|
||||
- Verify only one active session token; stale callbacks are dropped by the coordinator.
|
||||
- Confirm push-to-talk release always calls `end()` with the active token; if text is empty, expect a dismiss without chime or send.
|
||||
|
||||
## Related
|
||||
|
||||
- [macOS app](/platforms/macos)
|
||||
- [Voice wake (macOS)](/platforms/mac/voicewake)
|
||||
- [Talk mode](/nodes/talk)
|
||||
69
docs/platforms/mac/voicewake.md
Normal file
69
docs/platforms/mac/voicewake.md
Normal file
@@ -0,0 +1,69 @@
|
||||
---
|
||||
summary: "Voice wake and push-to-talk modes plus routing details in the mac app"
|
||||
read_when:
|
||||
- Working on voice wake or PTT pathways
|
||||
title: "Voice wake (macOS)"
|
||||
---
|
||||
|
||||
# Voice Wake & Push-to-Talk
|
||||
|
||||
## Requirements
|
||||
|
||||
Voice Wake and push-to-talk require macOS 26 or newer. On older macOS the controls are hidden from the Voice settings page, which shows the macOS 26 requirement instead.
|
||||
|
||||
## Modes
|
||||
|
||||
- **Wake-word mode** (default): an always-on Speech recognizer waits for trigger tokens (`swabbleTriggerWords`). On match it starts capture, shows the overlay with partial text, and auto-sends after silence.
|
||||
- **Push-to-talk (hold Right Option)**: hold the right Option key to capture immediately, no trigger needed. The overlay appears while held; releasing finalizes and forwards after a short delay so you can edit the text.
|
||||
|
||||
## Runtime behavior (wake-word)
|
||||
|
||||
- The recognizer lives in `VoiceWakeRuntime`.
|
||||
- Trigger fires only when there is a meaningful pause between the wake word and the next word (`triggerPauseWindow` = 0.55s). The overlay/chime can start on the pause even before the command begins.
|
||||
- Silence windows: 2.0s (`silenceWindow`) when speech is flowing, 5.0s (`triggerOnlySilenceWindow`) if only the trigger was heard.
|
||||
- Hard stop: 120s (`captureHardStop`) to prevent runaway sessions.
|
||||
- Debounce between sessions: 350ms (`debounceAfterSend`) after a send.
|
||||
- The overlay is driven via `VoiceWakeOverlayController`, with committed/volatile text coloring.
|
||||
- After send, the recognizer restarts cleanly to listen for the next trigger.
|
||||
|
||||
## Lifecycle invariants
|
||||
|
||||
- If Voice Wake is enabled and permissions are granted, the wake-word recognizer stays listening, except during an active push-to-talk capture.
|
||||
- Overlay dismissal, including manual dismiss via the X button, always resumes the recognizer: `VoiceSessionCoordinator.overlayDidDismiss` calls `VoiceWakeRuntime.refresh(state:)` on every dismiss path. See [Voice overlay](/platforms/mac/voice-overlay) for the session/token model.
|
||||
|
||||
## Push-to-talk specifics
|
||||
|
||||
- Hotkey detection uses a global `.flagsChanged` monitor for right Option (`keyCode 61` + `.option`). It only observes events, never swallows them.
|
||||
- Capture lives in `VoicePushToTalk`: starts Speech immediately, streams partials to the overlay, and calls `VoiceWakeForwarder` on release.
|
||||
- Starting push-to-talk pauses the wake-word runtime to avoid dueling audio taps; it restarts automatically after release.
|
||||
- Permissions: requires Microphone + Speech; receiving key events needs Accessibility/Input Monitoring approval.
|
||||
- External keyboards: some do not expose right Option as expected. Offer a fallback shortcut if users report misses.
|
||||
|
||||
## User-facing settings
|
||||
|
||||
- **Voice Wake** toggle: enables the wake-word runtime.
|
||||
- **Hold Right Option to talk**: enables the push-to-talk monitor.
|
||||
- Language and mic pickers, a live level meter, a trigger-word table, and a tester (local-only, never forwards).
|
||||
- The mic picker preserves the last selection if a device disconnects, shows a disconnected hint, and temporarily falls back to the system default until it returns.
|
||||
- **Sounds**: chimes on trigger detect and on send, defaulting to the macOS "Glass" system sound. Pick any `NSSound`-loadable file (e.g. MP3/WAV/AIFF) per event, or choose **No Sound**.
|
||||
|
||||
## Forwarding behavior
|
||||
|
||||
- On forward, `VoiceWakeForwarder.selectedSessionOptions` picks the active WebChat session key if one is set, otherwise the gateway's main session key.
|
||||
- It looks up that session via `sessions.list` and derives the delivery channel and target from the session's delivery context (falling back to its last channel/target, then to a parsed session key), defaulting to WebChat if nothing resolves.
|
||||
- If delivery fails, the error is logged (`voicewake.forward` category) and the run is still visible via WebChat/session logs.
|
||||
|
||||
## Forwarding payload
|
||||
|
||||
- `VoiceWakeForwarder.prefixedTranscript(_:)` prepends a machine-hint line (resolved host name, falling back to "this Mac") before the transcript, shared between wake-word and push-to-talk paths.
|
||||
|
||||
## Quick verification
|
||||
|
||||
- Toggle push-to-talk on, hold Right Option, speak, release: overlay should show partials then send.
|
||||
- While holding, the menu-bar ears should stay enlarged (`triggerVoiceEars(ttl: nil)`); they drop after release.
|
||||
|
||||
## Related
|
||||
|
||||
- [Voice wake](/nodes/voicewake)
|
||||
- [Voice overlay](/platforms/mac/voice-overlay)
|
||||
- [macOS app](/platforms/macos)
|
||||
44
docs/platforms/mac/webchat.md
Normal file
44
docs/platforms/mac/webchat.md
Normal file
@@ -0,0 +1,44 @@
|
||||
---
|
||||
summary: "How the mac app embeds the gateway WebChat and how to debug it"
|
||||
read_when:
|
||||
- Debugging mac WebChat view or loopback port
|
||||
title: "WebChat (macOS)"
|
||||
---
|
||||
|
||||
The macOS menu bar app embeds the WebChat UI as a native SwiftUI view. It connects to the Gateway and defaults to the primary session for the selected agent (`main`, or `global` when `session.scope` is `global`), with a session switcher for other sessions.
|
||||
|
||||
- **Local mode**: connects directly to the local Gateway WebSocket.
|
||||
- **Remote mode**: forwards the Gateway control port over SSH and uses that tunnel as the data plane.
|
||||
|
||||
## Launch and debugging
|
||||
|
||||
- Manual: Lobster menu -> "Open Chat".
|
||||
- Auto-open for testing:
|
||||
|
||||
```bash
|
||||
dist/OpenClaw.app/Contents/MacOS/OpenClaw --chat
|
||||
```
|
||||
|
||||
(`--webchat` is accepted as a legacy alias.)
|
||||
|
||||
- Logs: `./scripts/clawlog.sh` (subsystem `ai.openclaw`, category `WebChatSwiftUI`).
|
||||
|
||||
## How it is wired
|
||||
|
||||
- Data plane: Gateway WS methods `chat.history`, `chat.send`, `chat.abort`, `chat.inject`, and events `chat`, `agent`, `presence`, `tick`, `health`.
|
||||
- `chat.history` returns a display-normalized transcript: inline directive tags are stripped from visible text, plain-text tool-call XML payloads (`<tool_call>`, `<function_call>`, `<tool_calls>`, `<function_calls>`, including truncated blocks) and leaked model control tokens are stripped, pure silent-token assistant rows such as exact `NO_REPLY`/`no_reply` are omitted, and oversized rows can be replaced with a truncated placeholder.
|
||||
- Session: defaults to the primary session as above; the UI can switch between sessions.
|
||||
- Onboarding uses a dedicated session to keep first-run setup separate.
|
||||
|
||||
## Security surface
|
||||
|
||||
- Remote mode forwards only the Gateway WebSocket control port over SSH.
|
||||
|
||||
## Known limitations
|
||||
|
||||
- The UI is optimized for chat sessions, not a full browser sandbox.
|
||||
|
||||
## Related
|
||||
|
||||
- [WebChat](/web/webchat)
|
||||
- [macOS app](/platforms/macos)
|
||||
65
docs/platforms/mac/xpc.md
Normal file
65
docs/platforms/mac/xpc.md
Normal file
@@ -0,0 +1,65 @@
|
||||
---
|
||||
summary: "macOS IPC architecture for OpenClaw app, gateway node transport, and PeekabooBridge"
|
||||
read_when:
|
||||
- Editing IPC contracts or menu bar app IPC
|
||||
title: "macOS IPC"
|
||||
---
|
||||
|
||||
# OpenClaw macOS IPC architecture
|
||||
|
||||
A local Unix socket connects the node host service to the macOS app for exec approvals and `system.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`. UI automation uses 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, plus PeekabooBridge for UI automation.
|
||||
- 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.*`).
|
||||
- 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
|
||||
|
||||
- A headless node host service connects to the Gateway WebSocket.
|
||||
- `system.run` requests 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.
|
||||
|
||||
Diagram (SCI):
|
||||
|
||||
```text
|
||||
Agent -> Gateway -> Node Service (WS)
|
||||
| IPC (UDS + token + HMAC + TTL)
|
||||
v
|
||||
Mac App (UI + TCC + system.run)
|
||||
```
|
||||
|
||||
### PeekabooBridge (UI automation)
|
||||
|
||||
- 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 an allowlisted TeamID (the bundled `PeekabooBridgeHostCoordinator` allowlists a fixed team plus the app's own signing team); a DEBUG-only same-UID escape hatch is guarded by `PEEKABOO_ALLOW_UNSIGNED_SOCKET_CLIENTS=1` (Peekaboo convention).
|
||||
- See: [PeekabooBridge usage](/platforms/mac/peekaboo) for details.
|
||||
|
||||
## Operational flows
|
||||
|
||||
- Restart/rebuild: `scripts/restart-mac.sh` kills existing instances, rebuilds via Swift, repackages, and relaunches. It auto-detects an available signing identity and falls back to `--no-sign` if none is found; pass `--sign` to require signing (fails if no key is available) or `--no-sign` to force the unsigned path. `SIGN_IDENTITY` set in the environment is unset on the signed path, so `scripts/codesign-mac-app.sh`'s own identity auto-detection picks the cert.
|
||||
- Single instance: the app checks `NSWorkspace.runningApplications` for a duplicate bundle ID and exits if more than one instance is found (`isDuplicateInstance()` in `MenuBar.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, peer-UID check (`getpeereid`), HMAC-SHA256 challenge/response, and a short TTL on requests.
|
||||
|
||||
## Related
|
||||
|
||||
- [macOS app](/platforms/macos)
|
||||
- [macOS IPC flow (Exec approvals)](/tools/exec-approvals-advanced#macos-ipc-flow)
|
||||
87
docs/platforms/macos.md
Normal file
87
docs/platforms/macos.md
Normal file
@@ -0,0 +1,87 @@
|
||||
---
|
||||
summary: "Install and use the OpenClaw macOS menu bar app"
|
||||
read_when:
|
||||
- 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 OpenClaw **menu bar companion**: native tray UI, macOS
|
||||
permission prompts, notifications, WebChat, voice input, Canvas, and
|
||||
Mac-hosted node tools such as `system.run`.
|
||||
|
||||
Only need the CLI and Gateway? Start with [Getting started](/start/getting-started).
|
||||
|
||||
## Download
|
||||
|
||||
Get macOS app builds from [OpenClaw GitHub releases](https://github.com/openclaw/openclaw/releases).
|
||||
When a release ships macOS app assets, look for:
|
||||
|
||||
- `OpenClaw-<version>.dmg` (preferred)
|
||||
- `OpenClaw-<version>.zip`
|
||||
|
||||
Some releases only ship CLI, evidence, or Windows assets. If the newest release
|
||||
has no macOS app asset, use the newest one that does, or build from source with
|
||||
[macOS dev setup](/platforms/mac/dev-setup).
|
||||
|
||||
## First run
|
||||
|
||||
1. Install and launch **OpenClaw.app**.
|
||||
2. Pick **This Mac** for a local Gateway, or connect to a remote Gateway.
|
||||
3. Local mode: wait while the app installs its user-space runtime and Gateway.
|
||||
4. Complete provider setup and the macOS permission checklist.
|
||||
5. Send the onboarding test message.
|
||||
|
||||
For the CLI/Gateway setup path, use [Getting started](/start/getting-started).
|
||||
For permission recovery, use [macOS permissions](/platforms/mac/permissions).
|
||||
|
||||
## Choose a Gateway mode
|
||||
|
||||
| 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; this Mac controls it over SSH, LAN, or Tailnet. | [Remote control](/platforms/mac/remote) |
|
||||
|
||||
Local mode needs an installed `openclaw` CLI. On a fresh Mac, the app installs
|
||||
the matching CLI and runtime automatically before starting the Gateway wizard.
|
||||
See [Gateway on macOS](/platforms/mac/bundled-gateway) for manual recovery.
|
||||
|
||||
## What the app owns
|
||||
|
||||
- Menu bar status, notifications, health, and WebChat.
|
||||
- macOS permission prompts for screen, microphone, speech, automation, and accessibility.
|
||||
- Local node tools: Canvas, camera/screen capture, notifications, and `system.run`.
|
||||
- Exec approval prompts for Mac-hosted commands.
|
||||
- Remote-mode SSH tunnels or direct Gateway connections.
|
||||
|
||||
The app does **not** replace the Gateway or general CLI docs. Gateway
|
||||
configuration, providers, plugins, channels, tools, and security live in their
|
||||
own docs.
|
||||
|
||||
## macOS detail pages
|
||||
|
||||
| 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) |
|
||||
|
||||
## Related
|
||||
|
||||
- [Platforms](/platforms)
|
||||
- [Getting started](/start/getting-started)
|
||||
- [Gateway](/gateway)
|
||||
- [Exec approvals](/tools/exec-approvals)
|
||||
12
docs/platforms/oracle.md
Normal file
12
docs/platforms/oracle.md
Normal file
@@ -0,0 +1,12 @@
|
||||
---
|
||||
summary: "Redirect to /install/oracle"
|
||||
title: "Oracle Cloud (platform)"
|
||||
redirect: /install/oracle
|
||||
---
|
||||
|
||||
This page has moved to [Oracle Cloud](/install/oracle).
|
||||
|
||||
## Related
|
||||
|
||||
- [Install overview](/install)
|
||||
- [VPS hosting](/vps)
|
||||
13
docs/platforms/raspberry-pi.md
Normal file
13
docs/platforms/raspberry-pi.md
Normal file
@@ -0,0 +1,13 @@
|
||||
---
|
||||
summary: "Redirect to /install/raspberry-pi"
|
||||
title: "Raspberry Pi (platform)"
|
||||
redirect: /install/raspberry-pi
|
||||
---
|
||||
|
||||
This page has moved to [Raspberry Pi](/install/raspberry-pi).
|
||||
|
||||
## Related
|
||||
|
||||
- [Install overview](/install)
|
||||
- [Linux server](/vps)
|
||||
- [Platforms](/platforms)
|
||||
329
docs/platforms/windows.md
Normal file
329
docs/platforms/windows.md
Normal file
@@ -0,0 +1,329 @@
|
||||
---
|
||||
summary: "Windows support: Windows Hub, native CLI and Gateway, WSL2 gateway setup, node mode, and troubleshooting"
|
||||
read_when:
|
||||
- Installing OpenClaw on Windows
|
||||
- Choosing between Windows Hub, native Windows, and WSL2
|
||||
- Setting up the Windows companion app or Windows node mode
|
||||
title: "Windows"
|
||||
---
|
||||
|
||||
OpenClaw ships a native **Windows Hub** companion app plus Windows CLI support.
|
||||
Use Windows Hub for a desktop app with setup, tray status, chat, Command
|
||||
Center diagnostics, and Windows node capabilities. Use the PowerShell
|
||||
installer for the CLI/Gateway directly. Use WSL2 for the most
|
||||
Linux-compatible Gateway runtime.
|
||||
|
||||
## Recommended: Windows Hub
|
||||
|
||||
Windows Hub is the native WinUI companion app for Windows 10 20H2+ and
|
||||
Windows 11. It installs without administrator privileges and ships as signed
|
||||
x64 and ARM64 installers on OpenClaw releases.
|
||||
|
||||
Download the latest stable installer from the
|
||||
[OpenClaw releases page](https://github.com/openclaw/openclaw/releases) or
|
||||
directly via `releases/latest/download`:
|
||||
|
||||
- [OpenClawCompanion-Setup-x64.exe](https://github.com/openclaw/openclaw/releases/latest/download/OpenClawCompanion-Setup-x64.exe)
|
||||
- [OpenClawCompanion-Setup-arm64.exe](https://github.com/openclaw/openclaw/releases/latest/download/OpenClawCompanion-Setup-arm64.exe)
|
||||
- [Checksums](https://github.com/openclaw/openclaw/releases/latest/download/OpenClawCompanion-SHA256SUMS.txt)
|
||||
|
||||
If a link above 404s, visit the [releases page](https://github.com/openclaw/openclaw/releases)
|
||||
and look for `OpenClawCompanion-Setup-*` assets on the latest release.
|
||||
|
||||
After install, launch **OpenClaw Companion** from the Start menu or system
|
||||
tray. The installer also adds shortcuts for Gateway Setup, Chat, Settings,
|
||||
Check for Updates, and uninstall.
|
||||
|
||||
### What Windows Hub includes
|
||||
|
||||
- System tray status and launch-at-login.
|
||||
- First-run setup for a local app-owned WSL Gateway.
|
||||
- Connection settings for local, remote, and SSH-tunneled Gateways.
|
||||
- Native chat window plus access to the browser Control UI.
|
||||
- Command Center diagnostics for sessions, usage, channels, nodes, pairing,
|
||||
and repair commands.
|
||||
- Windows node mode for agent-controlled canvas, screen, camera,
|
||||
notifications, device status, talk, and controlled `system.run`.
|
||||
- Local MCP server mode for MCP clients such as Claude Desktop, Claude Code,
|
||||
and Cursor.
|
||||
|
||||
### First launch
|
||||
|
||||
On first launch, Windows Hub opens setup when there is no usable saved
|
||||
Gateway. The fastest path is **Set up locally**, which provisions an
|
||||
app-owned `OpenClawGateway` WSL distro, installs the Gateway inside it, and
|
||||
pairs the app. This does not export or mutate your existing Ubuntu distro.
|
||||
|
||||
Choose **Advanced setup** or open the Connections tab when you already have a
|
||||
Gateway. You can connect to:
|
||||
|
||||
- a local Gateway on this PC
|
||||
- a WSL Gateway on this PC
|
||||
- a remote Gateway by URL and token or setup code
|
||||
- a Gateway reached through an SSH tunnel
|
||||
|
||||
When setup finishes, the tray icon turns green. Open **Command Center** from
|
||||
the tray to confirm connection, pairing, node status, and channel health.
|
||||
|
||||
## Windows node mode
|
||||
|
||||
Windows Hub can register as an OpenClaw node so the agent can use declared
|
||||
Windows-native capabilities through the Gateway. Node commands must be
|
||||
declared by the node and allowed by Gateway policy before they run; see
|
||||
[Nodes](/nodes#command-policy) for the full allow/deny model.
|
||||
|
||||
Common commands:
|
||||
|
||||
| Family | Commands |
|
||||
| ------ | ------------------------------------------------------------------------------------ |
|
||||
| Canvas | `canvas.present`, `canvas.hide`, `canvas.navigate`, `canvas.eval`, `canvas.snapshot` |
|
||||
| Screen | `screen.snapshot`; `screen.record` requires explicit opt-in |
|
||||
| Camera | `camera.list`; `camera.snap`, `camera.clip` require explicit opt-in |
|
||||
| System | `system.notify`, `system.run`, `system.run.prepare`, `system.which` |
|
||||
| Device | `location.get`, `device.info`, `device.status` |
|
||||
| Talk | `talk.ptt.start`, `talk.ptt.stop`, `talk.ptt.cancel`, `talk.ptt.once`, `talk.speak` |
|
||||
|
||||
Node mode requires Gateway pairing. If the app shows a pairing request,
|
||||
approve it from the Gateway host:
|
||||
|
||||
```powershell
|
||||
openclaw devices list
|
||||
openclaw devices approve <requestId>
|
||||
openclaw nodes status
|
||||
```
|
||||
|
||||
The Gateway only forwards commands the node declares and server policy
|
||||
allows. Privacy-sensitive commands such as `screen.record`, `camera.snap`,
|
||||
and `camera.clip` need explicit `gateway.nodes.allowCommands` opt-in.
|
||||
|
||||
## Local MCP mode
|
||||
|
||||
Windows Hub can expose the same Windows-native capability registry as a local
|
||||
MCP server on loopback, so local MCP clients can drive Windows capabilities
|
||||
without a running OpenClaw Gateway.
|
||||
|
||||
Enable it in Windows Hub Settings under the developer/advanced section. The
|
||||
app shows the loopback endpoint and bearer token once the server is enabled.
|
||||
|
||||
Mode matrix:
|
||||
|
||||
| Node mode | MCP server | Behavior |
|
||||
| --------- | ---------- | ---------------------------------- |
|
||||
| off | off | Operator-only desktop app |
|
||||
| on | off | Gateway-connected Windows node |
|
||||
| off | on | Local MCP server only |
|
||||
| on | on | Gateway node plus local MCP server |
|
||||
|
||||
## Native Windows CLI and Gateway
|
||||
|
||||
For terminal-first use, install OpenClaw from PowerShell:
|
||||
|
||||
```powershell
|
||||
iwr -useb https://openclaw.ai/install.ps1 | iex
|
||||
```
|
||||
|
||||
Verify:
|
||||
|
||||
```powershell
|
||||
openclaw --version
|
||||
openclaw doctor
|
||||
openclaw gateway status --json
|
||||
```
|
||||
|
||||
Managed startup uses Windows Scheduled Tasks when available. The task keeps
|
||||
the readable `gateway.cmd` script in the OpenClaw state dir but launches it
|
||||
through a generated `gateway.vbs` WScript wrapper, so the background Gateway
|
||||
does not open a visible console window. If task creation is denied, OpenClaw
|
||||
falls back to a per-user Startup-folder login item.
|
||||
|
||||
Install the Gateway service:
|
||||
|
||||
```powershell
|
||||
openclaw gateway install
|
||||
openclaw gateway status --json
|
||||
```
|
||||
|
||||
For CLI-only use without a managed Gateway service:
|
||||
|
||||
```powershell
|
||||
openclaw onboard --non-interactive --skip-health
|
||||
openclaw gateway run
|
||||
```
|
||||
|
||||
## WSL2 Gateway
|
||||
|
||||
WSL2 remains the most Linux-compatible Gateway runtime on Windows. Windows
|
||||
Hub can set up an app-owned WSL Gateway for you, or install manually inside
|
||||
your own distro.
|
||||
|
||||
Manual setup:
|
||||
|
||||
```powershell
|
||||
wsl --install
|
||||
# Or pick a distro explicitly:
|
||||
wsl --list --online
|
||||
wsl --install -d Ubuntu-24.04
|
||||
```
|
||||
|
||||
Enable systemd inside WSL:
|
||||
|
||||
```bash
|
||||
sudo tee /etc/wsl.conf >/dev/null <<'EOF'
|
||||
[boot]
|
||||
systemd=true
|
||||
EOF
|
||||
```
|
||||
|
||||
Restart WSL from PowerShell:
|
||||
|
||||
```powershell
|
||||
wsl --shutdown
|
||||
```
|
||||
|
||||
Then install OpenClaw inside WSL with the Linux quickstart:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://openclaw.ai/install.sh | bash
|
||||
openclaw gateway status
|
||||
```
|
||||
|
||||
## Gateway auto-start before Windows login
|
||||
|
||||
For headless WSL setups, make sure the full boot chain runs even when no one
|
||||
logs into Windows.
|
||||
|
||||
Inside WSL:
|
||||
|
||||
```bash
|
||||
sudo apt-get install -y dbus-x11
|
||||
sudo loginctl enable-linger "$(whoami)"
|
||||
openclaw gateway install
|
||||
```
|
||||
|
||||
In PowerShell as Administrator:
|
||||
|
||||
```powershell
|
||||
schtasks /create /tn "WSL Boot" /tr "wsl.exe -d Ubuntu --exec dbus-launch true" /sc onstart /ru "$env:USERNAME"
|
||||
```
|
||||
|
||||
Replace `Ubuntu` with your distro name from:
|
||||
|
||||
```powershell
|
||||
wsl --list --verbose
|
||||
```
|
||||
|
||||
<Note>
|
||||
Two changes from older recipes:
|
||||
|
||||
- **`dbus-launch true` instead of `/bin/true`**: on WSL >= 2.6.1.0 a
|
||||
regression ([microsoft/WSL #13416](https://github.com/microsoft/WSL/issues/13416))
|
||||
idle-terminates the distro 15-20 seconds after the last client exits, even
|
||||
with linger enabled. `dbus-launch true` keeps a child-of-init process alive
|
||||
as a workaround (community discussion, [microsoft/WSL #9245](https://github.com/microsoft/WSL/discussions/9245)).
|
||||
- **`/ru "$env:USERNAME"` instead of `/ru SYSTEM`**: per-user WSL distros (the
|
||||
default setup) are not visible to the SYSTEM account, so the task appears
|
||||
to run but the distro never starts. Running as your own account avoids
|
||||
this; Windows prompts for your password when the task is created.
|
||||
|
||||
</Note>
|
||||
|
||||
After reboot, verify from WSL:
|
||||
|
||||
```bash
|
||||
systemctl --user is-enabled openclaw-gateway.service
|
||||
systemctl --user status openclaw-gateway.service --no-pager
|
||||
```
|
||||
|
||||
## Expose WSL services over LAN
|
||||
|
||||
WSL has its own virtual network. If another machine must reach a service
|
||||
inside WSL, forward a Windows port to the current WSL IP. The WSL IP can
|
||||
change after restarts, so refresh the forwarding rule when needed.
|
||||
|
||||
Example in PowerShell as Administrator:
|
||||
|
||||
```powershell
|
||||
$Distro = "Ubuntu-24.04"
|
||||
$ListenPort = 2222
|
||||
$TargetPort = 22
|
||||
|
||||
$WslIp = (wsl -d $Distro -- hostname -I).Trim().Split(" ")[0]
|
||||
if (-not $WslIp) { throw "WSL IP not found." }
|
||||
|
||||
netsh interface portproxy add v4tov4 listenaddress=0.0.0.0 listenport=$ListenPort `
|
||||
connectaddress=$WslIp connectport=$TargetPort
|
||||
|
||||
New-NetFirewallRule -DisplayName "WSL SSH $ListenPort" -Direction Inbound `
|
||||
-Protocol TCP -LocalPort $ListenPort -Action Allow
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
- SSH from another machine targets the Windows host IP, e.g. `ssh user@windows-host -p 2222`.
|
||||
- Remote nodes must point at a reachable Gateway URL, not `127.0.0.1`.
|
||||
- Use `listenaddress=0.0.0.0` for LAN access, `127.0.0.1` for local-only access.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### The tray icon does not appear
|
||||
|
||||
Check Task Manager for `OpenClaw.Tray.WinUI.exe`. If it is running, open the
|
||||
hidden tray-icons area and pin it. If not, launch **OpenClaw Companion** from
|
||||
the Start menu.
|
||||
|
||||
### Local setup fails
|
||||
|
||||
Open the setup log from Windows Hub or inspect:
|
||||
|
||||
```powershell
|
||||
notepad "$env:LOCALAPPDATA\OpenClawTray\Logs\Setup\easy-setup-latest.txt"
|
||||
```
|
||||
|
||||
Common causes: disabled WSL, blocked virtualization, stale app-owned WSL
|
||||
state, or a network failure while installing the Gateway package.
|
||||
|
||||
### The app says pairing is required
|
||||
|
||||
Approve the operator or node request from the Gateway:
|
||||
|
||||
```powershell
|
||||
openclaw devices list
|
||||
openclaw devices approve <requestId>
|
||||
```
|
||||
|
||||
If the device already had a token, reconnect from the Connections tab after
|
||||
approval.
|
||||
|
||||
### Web chat cannot reach a remote Gateway
|
||||
|
||||
Remote web chat needs HTTPS or localhost. For self-signed certificates, trust
|
||||
the certificate in Windows, or use an SSH tunnel to a localhost URL.
|
||||
|
||||
### `screen.snapshot`, camera, or audio commands fail
|
||||
|
||||
Confirm Windows permissions for camera, microphone, screen capture, and
|
||||
notifications. Packaged installs declare the protected capabilities, but
|
||||
Windows may still prompt the first time a command uses them.
|
||||
|
||||
### Git or GitHub connectivity fails
|
||||
|
||||
Some networks block or throttle HTTPS to GitHub. If `git clone` or
|
||||
`gh auth login` fails, try another network, a VPN, or an HTTP/HTTPS proxy.
|
||||
|
||||
For token-based `gh` auth in the current session:
|
||||
|
||||
```powershell
|
||||
$env:GH_TOKEN="<your-token>"
|
||||
gh auth status
|
||||
gh auth setup-git
|
||||
```
|
||||
|
||||
Never commit tokens or paste them into issues or pull requests.
|
||||
|
||||
## Related
|
||||
|
||||
- [Install overview](/install)
|
||||
- [Node.js setup](/install/node)
|
||||
- [Nodes](/nodes)
|
||||
- [Control UI](/web/control-ui)
|
||||
- [Gateway configuration](/gateway/configuration)
|
||||
Reference in New Issue
Block a user