mirror of
https://github.com/hansjone/oclaw.git
synced 2026-10-10 22:15:57 +08:00
重构主控编排与运行时预热链路,统一工作区提示词/专家调度协议并补齐 wiki 记忆注入与写回闭环。
同时收敛启动与运维脚本默认行为(含 wiki worker)、更新 Admin 可观测性与相关测试,降低首轮时延并提高运行稳定性。 Made-with: Cursor
This commit is contained in:
parent
4a23b715a2
commit
dbbe3add6a
14438 changed files with 2693620 additions and 2546 deletions
244
openclaw/docs/platforms/android.md
Normal file
244
openclaw/docs/platforms/android.md
Normal file
|
|
@ -0,0 +1,244 @@
|
|||
---
|
||||
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"
|
||||
---
|
||||
|
||||
# Android App (Node)
|
||||
|
||||
> **Note:** The Android app has not been publicly released yet. The source code is available in the [OpenClaw repository](https://github.com/openclaw/openclaw) under `apps/android`. You can build it yourself using Java 17 and the Android SDK (`./gradlew :app:assemblePlayDebug`). See [apps/android/README.md](https://github.com/openclaw/openclaw/blob/main/apps/android/README.md) for build instructions.
|
||||
|
||||
## 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: [Getting Started](/start/getting-started) + [Pairing](/channels/pairing).
|
||||
- Gateway: [Runbook](/gateway) + [Configuration](/gateway/configuration).
|
||||
- Protocols: [Gateway protocol](/gateway/protocol) (nodes + control plane).
|
||||
|
||||
## System control
|
||||
|
||||
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
|
||||
|
||||
- You can run the Gateway on the “master” machine.
|
||||
- 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.
|
||||
- You can run the CLI (`openclaw`) on the gateway machine (or via SSH).
|
||||
|
||||
### 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 and uses the resolved
|
||||
service endpoint instead of TXT-only hints.
|
||||
|
||||
#### Tailnet (Vienna ⇄ London) discovery via unicast DNS-SD
|
||||
|
||||
Android NSD/mDNS discovery won’t cross networks. If your 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:
|
||||
|
||||
- Manual endpoint (if enabled), otherwise
|
||||
- The last discovered gateway (best-effort).
|
||||
|
||||
### 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).
|
||||
|
||||
### 5) Verify the node is connected
|
||||
|
||||
- Via nodes status:
|
||||
|
||||
```bash
|
||||
openclaw nodes status
|
||||
```
|
||||
|
||||
- Via Gateway:
|
||||
|
||||
```bash
|
||||
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 are
|
||||
stripped from visible text, plain-text tool-call XML payloads (including
|
||||
`<tool_call>...</tool_call>`, `<function_call>...</function_call>`,
|
||||
`<tool_calls>...</tool_calls>`, `<function_calls>...</function_calls>`, and
|
||||
truncated tool-call blocks) and leaked ASCII/full-width 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 placeholders)
|
||||
- Send: `chat.send`
|
||||
- Push updates (best-effort): `chat.subscribe` → `event:"chat"`
|
||||
|
||||
### 7) Canvas + camera
|
||||
|
||||
#### Gateway Canvas Host (recommended for web content)
|
||||
|
||||
If you want the node to 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`).
|
||||
|
||||
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 A2UI host lives at `http://<gateway-host>:18789/__openclaw__/a2ui/`.
|
||||
|
||||
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)
|
||||
|
||||
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: Android uses a single mic on/off flow in the Voice tab with transcript capture and `talk.speak` playback. Local system TTS is used only when `talk.speak` is unavailable. Voice stops when the app leaves the foreground.
|
||||
- Voice wake/talk-mode toggles are currently removed from Android UX/runtime.
|
||||
- Additional Android command families (availability depends on device + permissions):
|
||||
- `device.status`, `device.info`, `device.permissions`, `device.health`
|
||||
- `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). When configured, holding the home button or saying "Hey Google, ask
|
||||
OpenClaw..." opens the app and hands the prompt into the chat composer.
|
||||
|
||||
This uses Android **App Actions** metadata declared in the app manifest. No
|
||||
extra configuration is needed on the gateway side -- the assistant intent is
|
||||
handled entirely by the Android app and forwarded as a normal chat message.
|
||||
|
||||
<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 events. Several controls let you scope which notifications are forwarded and when.
|
||||
|
||||
| Key | Type | Description |
|
||||
| -------------------------------- | -------------- | ------------------------------------------------------------------------------------------------- |
|
||||
| `notifications.allowPackages` | string[] | Only forward notifications from these package names. If set, all other packages are ignored. |
|
||||
| `notifications.denyPackages` | string[] | Never forward notifications from these package names. Applied after `allowPackages`. |
|
||||
| `notifications.quietHours.start` | string (HH:mm) | Start of quiet hours window (local device time). Notifications are suppressed during this window. |
|
||||
| `notifications.quietHours.end` | string (HH:mm) | End of quiet hours window. |
|
||||
| `notifications.rateLimit` | number | Maximum forwarded notifications per package per minute. Excess notifications are dropped. |
|
||||
|
||||
The notification picker also uses safer behavior for forwarded notification events, preventing accidental forwarding of sensitive system notifications.
|
||||
|
||||
Example configuration:
|
||||
|
||||
```json5
|
||||
{
|
||||
notifications: {
|
||||
allowPackages: ["com.slack", "com.whatsapp"],
|
||||
denyPackages: ["com.android.systemui"],
|
||||
quietHours: {
|
||||
start: "22:00",
|
||||
end: "07:00",
|
||||
},
|
||||
rateLimit: 5,
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
<Note>
|
||||
Notification forwarding requires the Android Notification Listener permission. The app prompts for this during setup.
|
||||
</Note>
|
||||
266
openclaw/docs/platforms/digitalocean.md
Normal file
266
openclaw/docs/platforms/digitalocean.md
Normal file
|
|
@ -0,0 +1,266 @@
|
|||
---
|
||||
summary: "OpenClaw on DigitalOcean (simple paid VPS option)"
|
||||
read_when:
|
||||
- Setting up OpenClaw on DigitalOcean
|
||||
- Looking for cheap VPS hosting for OpenClaw
|
||||
title: "DigitalOcean (Platform)"
|
||||
---
|
||||
|
||||
# OpenClaw on DigitalOcean
|
||||
|
||||
## Goal
|
||||
|
||||
Run a persistent OpenClaw Gateway on DigitalOcean for **$6/month** (or $4/mo with reserved pricing).
|
||||
|
||||
If you want a $0/month option and don’t mind ARM + provider-specific setup, see the [Oracle Cloud guide](/platforms/oracle).
|
||||
|
||||
## Cost Comparison (2026)
|
||||
|
||||
| Provider | Plan | Specs | Price/mo | Notes |
|
||||
| ------------ | --------------- | ---------------------- | ----------- | ------------------------------------- |
|
||||
| Oracle Cloud | Always Free ARM | up to 4 OCPU, 24GB RAM | $0 | ARM, limited capacity / signup quirks |
|
||||
| Hetzner | CX22 | 2 vCPU, 4GB RAM | €3.79 (~$4) | Cheapest paid option |
|
||||
| DigitalOcean | Basic | 1 vCPU, 1GB RAM | $6 | Easy UI, good docs |
|
||||
| Vultr | Cloud Compute | 1 vCPU, 1GB RAM | $6 | Many locations |
|
||||
| Linode | Nanode | 1 vCPU, 1GB RAM | $5 | Now part of Akamai |
|
||||
|
||||
**Picking a provider:**
|
||||
|
||||
- DigitalOcean: simplest UX + predictable setup (this guide)
|
||||
- Hetzner: good price/perf (see [Hetzner guide](/install/hetzner))
|
||||
- Oracle Cloud: can be $0/month, but is more finicky and ARM-only (see [Oracle guide](/platforms/oracle))
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- DigitalOcean account ([signup with $200 free credit](https://m.do.co/c/signup))
|
||||
- SSH key pair (or willingness to use password auth)
|
||||
- ~20 minutes
|
||||
|
||||
## 1) Create a Droplet
|
||||
|
||||
<Warning>
|
||||
Use a clean base image (Ubuntu 24.04 LTS). Avoid third-party Marketplace 1-click images unless you have reviewed their startup scripts and firewall defaults.
|
||||
</Warning>
|
||||
|
||||
1. Log into [DigitalOcean](https://cloud.digitalocean.com/)
|
||||
2. Click **Create → Droplets**
|
||||
3. Choose:
|
||||
- **Region:** Closest to you (or your users)
|
||||
- **Image:** Ubuntu 24.04 LTS
|
||||
- **Size:** Basic → Regular → **$6/mo** (1 vCPU, 1GB RAM, 25GB SSD)
|
||||
- **Authentication:** SSH key (recommended) or password
|
||||
4. Click **Create Droplet**
|
||||
5. Note the IP address
|
||||
|
||||
## 2) Connect via SSH
|
||||
|
||||
```bash
|
||||
ssh root@YOUR_DROPLET_IP
|
||||
```
|
||||
|
||||
## 3) Install OpenClaw
|
||||
|
||||
```bash
|
||||
# Update system
|
||||
apt update && apt upgrade -y
|
||||
|
||||
# Install Node.js 24
|
||||
curl -fsSL https://deb.nodesource.com/setup_24.x | bash -
|
||||
apt install -y nodejs
|
||||
|
||||
# Install OpenClaw
|
||||
curl -fsSL https://openclaw.ai/install.sh | bash
|
||||
|
||||
# Verify
|
||||
openclaw --version
|
||||
```
|
||||
|
||||
## 4) Run Onboarding
|
||||
|
||||
```bash
|
||||
openclaw onboard --install-daemon
|
||||
```
|
||||
|
||||
The wizard will walk you through:
|
||||
|
||||
- Model auth (API keys or OAuth)
|
||||
- Channel setup (Telegram, WhatsApp, Discord, etc.)
|
||||
- Gateway token (auto-generated)
|
||||
- Daemon installation (systemd)
|
||||
|
||||
## 5) Verify the Gateway
|
||||
|
||||
```bash
|
||||
# Check status
|
||||
openclaw status
|
||||
|
||||
# Check service
|
||||
systemctl --user status openclaw-gateway.service
|
||||
|
||||
# View logs
|
||||
journalctl --user -u openclaw-gateway.service -f
|
||||
```
|
||||
|
||||
## 6) Access the Dashboard
|
||||
|
||||
The gateway binds to loopback by default. To access the Control UI:
|
||||
|
||||
**Option A: SSH Tunnel (recommended)**
|
||||
|
||||
```bash
|
||||
# From your local machine
|
||||
ssh -L 18789:localhost:18789 root@YOUR_DROPLET_IP
|
||||
|
||||
# Then open: http://localhost:18789
|
||||
```
|
||||
|
||||
**Option B: Tailscale Serve (HTTPS, loopback-only)**
|
||||
|
||||
```bash
|
||||
# On the droplet
|
||||
curl -fsSL https://tailscale.com/install.sh | sh
|
||||
tailscale up
|
||||
|
||||
# Configure Gateway to use Tailscale Serve
|
||||
openclaw config set gateway.tailscale.mode serve
|
||||
openclaw gateway restart
|
||||
```
|
||||
|
||||
Open: `https://<magicdns>/`
|
||||
|
||||
Notes:
|
||||
|
||||
- Serve keeps the Gateway loopback-only and authenticates Control UI/WebSocket traffic via Tailscale identity headers (tokenless auth assumes trusted gateway host; HTTP APIs do not use those Tailscale headers and instead follow the gateway's normal HTTP auth mode).
|
||||
- To require explicit shared-secret credentials instead, set `gateway.auth.allowTailscale: false` and use `gateway.auth.mode: "token"` or `"password"`.
|
||||
|
||||
**Option C: Tailnet bind (no Serve)**
|
||||
|
||||
```bash
|
||||
openclaw config set gateway.bind tailnet
|
||||
openclaw gateway restart
|
||||
```
|
||||
|
||||
Open: `http://<tailscale-ip>:18789` (token required).
|
||||
|
||||
## 7) Connect Your Channels
|
||||
|
||||
### Telegram
|
||||
|
||||
```bash
|
||||
openclaw pairing list telegram
|
||||
openclaw pairing approve telegram <CODE>
|
||||
```
|
||||
|
||||
### WhatsApp
|
||||
|
||||
```bash
|
||||
openclaw channels login whatsapp
|
||||
# Scan QR code
|
||||
```
|
||||
|
||||
See [Channels](/channels) for other providers.
|
||||
|
||||
---
|
||||
|
||||
## Optimizations for 1GB RAM
|
||||
|
||||
The $6 droplet only has 1GB RAM. To keep things running smoothly:
|
||||
|
||||
### Add swap (recommended)
|
||||
|
||||
```bash
|
||||
fallocate -l 2G /swapfile
|
||||
chmod 600 /swapfile
|
||||
mkswap /swapfile
|
||||
swapon /swapfile
|
||||
echo '/swapfile none swap sw 0 0' >> /etc/fstab
|
||||
```
|
||||
|
||||
### Use a lighter model
|
||||
|
||||
If you're hitting OOMs, consider:
|
||||
|
||||
- Using API-based models (Claude, GPT) instead of local models
|
||||
- Setting `agents.defaults.model.primary` to a smaller model
|
||||
|
||||
### Monitor memory
|
||||
|
||||
```bash
|
||||
free -h
|
||||
htop
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Persistence
|
||||
|
||||
All state lives in:
|
||||
|
||||
- `~/.openclaw/` — `openclaw.json`, per-agent `auth-profiles.json`, channel/provider state, and session data
|
||||
- `~/.openclaw/workspace/` — workspace (SOUL.md, memory, etc.)
|
||||
|
||||
These survive reboots. Back them up periodically:
|
||||
|
||||
```bash
|
||||
openclaw backup create
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Oracle Cloud Free Alternative
|
||||
|
||||
Oracle Cloud offers **Always Free** ARM instances that are significantly more powerful than any paid option here — for $0/month.
|
||||
|
||||
| What you get | Specs |
|
||||
| ----------------- | ---------------------- |
|
||||
| **4 OCPUs** | ARM Ampere A1 |
|
||||
| **24GB RAM** | More than enough |
|
||||
| **200GB storage** | Block volume |
|
||||
| **Forever free** | No credit card charges |
|
||||
|
||||
**Caveats:**
|
||||
|
||||
- Signup can be finicky (retry if it fails)
|
||||
- ARM architecture — most things work, but some binaries need ARM builds
|
||||
|
||||
For the full setup guide, see [Oracle Cloud](/platforms/oracle). For signup tips and troubleshooting the enrollment process, see this [community guide](https://gist.github.com/rssnyder/51e3cfedd730e7dd5f4a816143b25dbd).
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Gateway will not start
|
||||
|
||||
```bash
|
||||
openclaw gateway status
|
||||
openclaw doctor --non-interactive
|
||||
journalctl --user -u openclaw-gateway.service --no-pager -n 50
|
||||
```
|
||||
|
||||
### Port already in use
|
||||
|
||||
```bash
|
||||
lsof -i :18789
|
||||
kill <PID>
|
||||
```
|
||||
|
||||
### Out of memory
|
||||
|
||||
```bash
|
||||
# Check memory
|
||||
free -h
|
||||
|
||||
# Add more swap
|
||||
# Or upgrade to $12/mo droplet (2GB RAM)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [Hetzner guide](/install/hetzner) — cheaper, more powerful
|
||||
- [Docker install](/install/docker) — containerized setup
|
||||
- [Tailscale](/gateway/tailscale) — secure remote access
|
||||
- [Configuration](/gateway/configuration) — full config reference
|
||||
55
openclaw/docs/platforms/index.md
Normal file
55
openclaw/docs/platforms/index.md
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
---
|
||||
summary: "Platform support overview (Gateway + companion apps)"
|
||||
read_when:
|
||||
- Looking for OS support or install paths
|
||||
- Deciding where to run the Gateway
|
||||
title: "Platforms"
|
||||
---
|
||||
|
||||
# Platforms
|
||||
|
||||
OpenClaw core is written in TypeScript. **Node is the recommended runtime**.
|
||||
Bun is not recommended for the Gateway (WhatsApp/Telegram bugs).
|
||||
|
||||
Companion apps exist for macOS (menu bar app) and mobile nodes (iOS/Android). Windows and
|
||||
Linux companion apps are planned, but the Gateway is fully supported today.
|
||||
Native companion apps for Windows are also planned; the Gateway is recommended via WSL2.
|
||||
|
||||
## Choose your OS
|
||||
|
||||
- macOS: [macOS](/platforms/macos)
|
||||
- iOS: [iOS](/platforms/ios)
|
||||
- Android: [Android](/platforms/android)
|
||||
- Windows: [Windows](/platforms/windows)
|
||||
- Linux: [Linux](/platforms/linux)
|
||||
|
||||
## VPS & 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)
|
||||
|
||||
## Common links
|
||||
|
||||
- Install guide: [Getting Started](/start/getting-started)
|
||||
- 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>`; legacy `com.openclaw.*`)
|
||||
- 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
|
||||
239
openclaw/docs/platforms/ios.md
Normal file
239
openclaw/docs/platforms/ios.md
Normal file
|
|
@ -0,0 +1,239 @@
|
|||
---
|
||||
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"
|
||||
---
|
||||
|
||||
# iOS App (Node)
|
||||
|
||||
Availability: internal preview. The iOS app is not publicly distributed yet.
|
||||
|
||||
## 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 the Gateway:
|
||||
|
||||
```bash
|
||||
openclaw gateway --port 18789
|
||||
```
|
||||
|
||||
2. In the iOS app, open Settings and pick a discovered gateway (or enable Manual Host and enter host/port).
|
||||
|
||||
3. Approve the pairing 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.
|
||||
|
||||
4. Verify connection:
|
||||
|
||||
```bash
|
||||
openclaw nodes status
|
||||
openclaw gateway call node.list --params "{}"
|
||||
```
|
||||
|
||||
## Relay-backed push for official builds
|
||||
|
||||
Official distributed iOS builds use the external push relay instead of publishing the raw APNs
|
||||
token to the gateway.
|
||||
|
||||
Gateway-side requirement:
|
||||
|
||||
```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 the app receipt.
|
||||
- The relay returns an opaque relay handle plus a registration-scoped send grant.
|
||||
- The iOS app fetches the paired gateway identity 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.
|
||||
- The gateway relay base URL must match the relay URL baked into the official/TestFlight iOS build.
|
||||
- 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/TestFlight relay-backed sends.
|
||||
|
||||
Expected operator flow:
|
||||
|
||||
1. Install the official/TestFlight iOS build.
|
||||
2. Set `gateway.push.apns.relay.baseUrl` on the gateway.
|
||||
3. Pair the app to the gateway and let it finish connecting.
|
||||
4. The app publishes `push.apns.register` automatically after 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.
|
||||
|
||||
Compatibility note:
|
||||
|
||||
- `OPENCLAW_APNS_RELAY_BASE_URL` still works as a temporary env override for the gateway.
|
||||
|
||||
## Authentication and trust flow
|
||||
|
||||
The relay exists to enforce two constraints that 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 first pairs with the gateway through the normal Gateway auth flow.
|
||||
- That gives the app an authenticated node session plus an authenticated operator session.
|
||||
- The operator session is used to call `gateway.identity.get`.
|
||||
|
||||
2. `iOS app -> relay`
|
||||
- The app calls the relay registration endpoints over HTTPS.
|
||||
- Registration includes App Attest proof plus the app receipt.
|
||||
- The relay validates the bundle ID, App Attest proof, and Apple receipt, and requires the
|
||||
official/production distribution path.
|
||||
- This is what blocks local Xcode/dev builds from using the hosted relay. A local build may be
|
||||
signed, but it does not satisfy the official Apple distribution proof the relay expects.
|
||||
|
||||
3. `gateway identity delegation`
|
||||
- Before relay registration, the app fetches the paired gateway identity from
|
||||
`gateway.identity.get`.
|
||||
- The app includes that gateway identity in the relay registration payload.
|
||||
- The relay returns a relay handle and a registration-scoped send grant that are 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.
|
||||
- To avoid storing raw official-build APNs tokens on the gateway.
|
||||
- To allow hosted relay usage only for official/TestFlight OpenClaw builds.
|
||||
- To 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 / TestFlight auth such as `ASC_KEY_ID` and `ASC_ISSUER_ID`; it does not configure
|
||||
direct APNs delivery for local iOS builds.
|
||||
|
||||
Recommended gateway-host storage:
|
||||
|
||||
```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/`.
|
||||
- It is served from the Gateway HTTP server (same port as `gateway.port`, default `18789`).
|
||||
- The iOS node auto-navigates to A2UI on connect when a canvas host URL is advertised.
|
||||
- Return to the built-in scaffold with `canvas.navigate` and `{"url":""}`.
|
||||
|
||||
### 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.
|
||||
- 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_NOT_CONFIGURED`: the Gateway did not advertise a canvas host URL; check `canvasHost` in [Gateway configuration](/gateway/configuration).
|
||||
- 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)
|
||||
100
openclaw/docs/platforms/linux.md
Normal file
100
openclaw/docs/platforms/linux.md
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
---
|
||||
summary: "Linux support + companion app status"
|
||||
read_when:
|
||||
- Looking for Linux companion app status
|
||||
- Planning platform coverage or contributions
|
||||
title: "Linux App"
|
||||
---
|
||||
|
||||
# Linux App
|
||||
|
||||
The Gateway is fully supported on Linux. **Node is the recommended runtime**.
|
||||
Bun is not recommended for the Gateway (WhatsApp/Telegram bugs).
|
||||
|
||||
Native Linux companion apps are planned. Contributions are welcome if you want to help build one.
|
||||
|
||||
## Beginner quick path (VPS)
|
||||
|
||||
1. Install Node 24 (recommended; Node 22 LTS, currently `22.14+`, still works for compatibility)
|
||||
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 you set `gateway.auth.mode: "password"`)
|
||||
|
||||
Full Linux 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 flows: [Bun (experimental)](/install/bun), [Nix](/install/nix), [Docker](/install/docker)
|
||||
|
||||
## Gateway
|
||||
|
||||
- [Gateway runbook](/gateway)
|
||||
- [Configuration](/gateway/configuration)
|
||||
|
||||
## Gateway service install (CLI)
|
||||
|
||||
Use one of these:
|
||||
|
||||
```
|
||||
openclaw onboard --install-daemon
|
||||
```
|
||||
|
||||
Or:
|
||||
|
||||
```
|
||||
openclaw gateway install
|
||||
```
|
||||
|
||||
Or:
|
||||
|
||||
```
|
||||
openclaw configure
|
||||
```
|
||||
|
||||
Select **Gateway service** when prompted.
|
||||
|
||||
Repair/migrate:
|
||||
|
||||
```
|
||||
openclaw doctor
|
||||
```
|
||||
|
||||
## System control (systemd user unit)
|
||||
|
||||
OpenClaw installs a systemd **user** service by default. Use a **system**
|
||||
service for shared or always-on servers. `openclaw gateway install` and
|
||||
`openclaw onboard --install-daemon` already render the current canonical unit
|
||||
for you; write one by hand only when you need a custom system/service-manager
|
||||
setup. The full service guidance lives in the [Gateway runbook](/gateway).
|
||||
|
||||
Minimal setup:
|
||||
|
||||
Create `~/.config/systemd/user/openclaw-gateway[-<profile>].service`:
|
||||
|
||||
```
|
||||
[Unit]
|
||||
Description=OpenClaw Gateway (profile: <profile>, v<version>)
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
ExecStart=/usr/local/bin/openclaw gateway --port 18789
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
TimeoutStopSec=30
|
||||
TimeoutStartSec=30
|
||||
SuccessExitStatus=0 143
|
||||
KillMode=control-group
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
```
|
||||
|
||||
Enable it:
|
||||
|
||||
```
|
||||
systemctl --user enable --now openclaw-gateway[-<profile>].service
|
||||
```
|
||||
75
openclaw/docs/platforms/mac/bundled-gateway.md
Normal file
75
openclaw/docs/platforms/mac/bundled-gateway.md
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
---
|
||||
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"
|
||||
---
|
||||
|
||||
# Gateway on macOS (external launchd)
|
||||
|
||||
OpenClaw.app no longer bundles 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 existing local Gateway if one is already running).
|
||||
|
||||
## Install the CLI (required for local mode)
|
||||
|
||||
Node 24 is the default runtime on the Mac. Node 22 LTS, currently `22.14+`, still works for compatibility. Then install `openclaw` globally:
|
||||
|
||||
```bash
|
||||
npm install -g openclaw@<version>
|
||||
```
|
||||
|
||||
The macOS app’s **Install CLI** button runs the same global install flow the app
|
||||
uses internally: it prefers npm first, then pnpm, then bun if that is the only
|
||||
detected package manager. Node remains the recommended Gateway runtime.
|
||||
|
||||
## Launchd (Gateway as LaunchAgent)
|
||||
|
||||
Label:
|
||||
|
||||
- `ai.openclaw.gateway` (or `ai.openclaw.<profile>`; legacy `com.openclaw.*` may remain)
|
||||
|
||||
Plist location (per‑user):
|
||||
|
||||
- `~/Library/LaunchAgents/ai.openclaw.gateway.plist`
|
||||
(or `~/Library/LaunchAgents/ai.openclaw.<profile>.plist`)
|
||||
|
||||
Manager:
|
||||
|
||||
- The macOS app owns LaunchAgent install/update in Local mode.
|
||||
- The CLI can also install it: `openclaw gateway install`.
|
||||
|
||||
Behavior:
|
||||
|
||||
- “OpenClaw Active” enables/disables the LaunchAgent.
|
||||
- App quit 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/err: `/tmp/openclaw/openclaw-gateway.log`
|
||||
|
||||
## Version compatibility
|
||||
|
||||
The macOS app checks the gateway version against its own version. If they’re
|
||||
incompatible, update the global CLI to match the app version.
|
||||
|
||||
## 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
|
||||
```
|
||||
125
openclaw/docs/platforms/mac/canvas.md
Normal file
125
openclaw/docs/platforms/mac/canvas.md
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
---
|
||||
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"
|
||||
---
|
||||
|
||||
# Canvas (macOS app)
|
||||
|
||||
The macOS app embeds an agent‑controlled **Canvas panel** using `WKWebView`. It
|
||||
is 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>`
|
||||
|
||||
Examples:
|
||||
|
||||
- `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 is switched 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
|
||||
- capture a snapshot image
|
||||
|
||||
CLI examples:
|
||||
|
||||
```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>
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
- `canvas.navigate` accepts **local canvas paths**, `http(s)` URLs, and `file://` URLs.
|
||||
- If you pass `"/"`, the Canvas 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 currently accepts **A2UI v0.8** server→client messages:
|
||||
|
||||
- `beginRendering`
|
||||
- `surfaceUpdate`
|
||||
- `dataModelUpdate`
|
||||
- `deleteSurface`
|
||||
|
||||
`createSurface` (v0.9) is not supported.
|
||||
|
||||
CLI example:
|
||||
|
||||
```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:
|
||||
|
||||
```bash
|
||||
openclaw nodes canvas a2ui push --node <id> --text "Hello from A2UI"
|
||||
```
|
||||
|
||||
## Triggering agent runs from Canvas
|
||||
|
||||
Canvas can trigger new agent runs via deep links:
|
||||
|
||||
- `openclaw://agent?...`
|
||||
|
||||
Example (in JS):
|
||||
|
||||
```js
|
||||
window.location.href = "openclaw://agent?message=Review%20this%20design";
|
||||
```
|
||||
|
||||
The app prompts for confirmation unless a valid key is provided.
|
||||
|
||||
## 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.
|
||||
69
openclaw/docs/platforms/mac/child-process.md
Normal file
69
openclaw/docs/platforms/mac/child-process.md
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
---
|
||||
summary: "Gateway lifecycle on macOS (launchd)"
|
||||
read_when:
|
||||
- Integrating the mac app with the gateway lifecycle
|
||||
title: "Gateway Lifecycle"
|
||||
---
|
||||
|
||||
# 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 you
|
||||
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`; legacy `com.openclaw.*` is supported).
|
||||
- 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 when you don’t have
|
||||
signing keys. To prevent 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. You can 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.
|
||||
107
openclaw/docs/platforms/mac/dev-setup.md
Normal file
107
openclaw/docs/platforms/mac/dev-setup.md
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
---
|
||||
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
|
||||
|
||||
This guide covers the necessary steps to build and run the OpenClaw macOS application from source.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before building the app, ensure you have the following installed:
|
||||
|
||||
1. **Xcode 26.2+**: Required for Swift development.
|
||||
2. **Node.js 24 & pnpm**: Recommended for the gateway, CLI, and packaging scripts. Node 22 LTS, currently `22.14+`, remains supported for compatibility.
|
||||
|
||||
## 1. Install Dependencies
|
||||
|
||||
Install the project-wide dependencies:
|
||||
|
||||
```bash
|
||||
pnpm install
|
||||
```
|
||||
|
||||
## 2. Build and Package the App
|
||||
|
||||
To build the macOS app and package it into `dist/OpenClaw.app`, run:
|
||||
|
||||
```bash
|
||||
./scripts/package-mac-app.sh
|
||||
```
|
||||
|
||||
If you don't have an Apple Developer ID certificate, the script will automatically use **ad-hoc signing** (`-`).
|
||||
|
||||
For dev run modes, signing flags, and Team ID troubleshooting, see the macOS app README:
|
||||
[https://github.com/openclaw/openclaw/blob/main/apps/macos/README.md](https://github.com/openclaw/openclaw/blob/main/apps/macos/README.md)
|
||||
|
||||
> **Note**: Ad-hoc signed apps may trigger security prompts. If the app crashes immediately with "Abort trap 6", see the [Troubleshooting](#troubleshooting) section.
|
||||
|
||||
## 3. Install the CLI
|
||||
|
||||
The macOS app expects a global `openclaw` CLI install to manage background tasks.
|
||||
|
||||
**To install it (recommended):**
|
||||
|
||||
1. Open the OpenClaw app.
|
||||
2. Go to the **General** settings tab.
|
||||
3. Click **"Install CLI"**.
|
||||
|
||||
Alternatively, install it manually:
|
||||
|
||||
```bash
|
||||
npm install -g openclaw@<version>
|
||||
```
|
||||
|
||||
`pnpm add -g openclaw@<version>` and `bun add -g openclaw@<version>` also work.
|
||||
For the Gateway runtime, Node remains the recommended path.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Build Fails: Toolchain or SDK Mismatch
|
||||
|
||||
The macOS app build expects the latest macOS SDK and Swift 6.2 toolchain.
|
||||
|
||||
**System dependencies (required):**
|
||||
|
||||
- **Latest macOS version available in Software Update** (required by Xcode 26.2 SDKs)
|
||||
- **Xcode 26.2** (Swift 6.2 toolchain)
|
||||
|
||||
**Checks:**
|
||||
|
||||
```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 due to a corrupted TCC cache or signature mismatch.
|
||||
|
||||
**Fix:**
|
||||
|
||||
1. Reset the TCC permissions:
|
||||
|
||||
```bash
|
||||
tccutil reset All ai.openclaw.mac.debug
|
||||
```
|
||||
|
||||
2. If that fails, change the `BUNDLE_ID` temporarily 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
|
||||
|
||||
If the gateway status stays on "Starting...", check if a zombie process is holding 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 is holding the port, stop that process (Ctrl+C). As a last resort, kill the PID you found above.
|
||||
34
openclaw/docs/platforms/mac/health.md
Normal file
34
openclaw/docs/platforms/mac/health.md
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
---
|
||||
summary: "How the macOS app reports gateway/Baileys health states"
|
||||
read_when:
|
||||
- Debugging mac app health indicators
|
||||
title: "Health Checks (macOS)"
|
||||
---
|
||||
|
||||
# Health Checks on macOS
|
||||
|
||||
How to see whether the linked channel is healthy from the menu bar app.
|
||||
|
||||
## Menu bar
|
||||
|
||||
- Status dot now reflects Baileys health:
|
||||
- Green: linked + socket opened recently.
|
||||
- Orange: connecting/retrying.
|
||||
- Red: logged out or probe failed.
|
||||
- Secondary line reads "linked · auth 12m" or shows the failure reason.
|
||||
- "Run Health Check" menu item triggers an on-demand probe.
|
||||
|
||||
## Settings
|
||||
|
||||
- General tab gains a Health card showing: linked auth age, session-store path/count, last check time, last error/status code, and buttons for Run Health Check / Reveal Logs.
|
||||
- Uses a cached snapshot so the UI loads instantly and falls back gracefully when offline.
|
||||
- **Channels tab** surfaces channel status + controls for WhatsApp/Telegram (login QR, logout, probe, last disconnect/error).
|
||||
|
||||
## How the probe works
|
||||
|
||||
- App runs `openclaw health --json` via `ShellExecutor` every ~60s and on demand. The probe loads creds and reports status without sending messages.
|
||||
- Cache the last good snapshot and the last error separately to avoid flicker; show the timestamp of each.
|
||||
|
||||
## When in doubt
|
||||
|
||||
- You can still use the CLI flow in [Gateway health](/gateway/health) (`openclaw status`, `openclaw status --deep`, `openclaw health --json`) and tail `/tmp/openclaw/openclaw-*.log` for `web-heartbeat` / `web-reconnect`.
|
||||
31
openclaw/docs/platforms/mac/icon.md
Normal file
31
openclaw/docs/platforms/mac/icon.md
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
---
|
||||
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
|
||||
|
||||
Author: steipete · Updated: 2025-12-06 · Scope: macOS app (`apps/macos`)
|
||||
|
||||
- **Idle:** Normal icon animation (blink, occasional wiggle).
|
||||
- **Paused:** Status item uses `appearsDisabled`; no motion.
|
||||
- **Voice trigger (big ears):** Voice wake detector calls `AppState.triggerVoiceEars(ttl: nil)` when the wake word is heard, keeping `earBoostActive=true` while the utterance is captured. Ears scale up (1.9x), get circular ear holes for readability, then drop via `stopVoiceEars()` after 1s of silence. Only fired from the in-app voice pipeline.
|
||||
- **Working (agent running):** `AppState.isWorking=true` drives a “tail/leg scurry” micro-motion: faster leg wiggle and slight offset while work is in-flight. Currently toggled around WebChat agent runs; add the same toggle around other long tasks when you wire them.
|
||||
|
||||
Wiring points
|
||||
|
||||
- Voice wake: runtime/tester call `AppState.triggerVoiceEars(ttl: nil)` on trigger and `stopVoiceEars()` after 1s of silence to match the capture window.
|
||||
- Agent activity: set `AppStateStore.shared.setWorking(true/false)` around work spans (already done in WebChat agent call). Keep spans short and reset in `defer` blocks to avoid stuck animations.
|
||||
|
||||
Shapes & sizes
|
||||
|
||||
- Base icon drawn in `CritterIconRenderer.makeIcon(blink:legWiggle:earWiggle:earScale:earHoles:)`.
|
||||
- Ear scale defaults to `1.0`; voice boost sets `earScale=1.9` and toggles `earHoles=true` without changing overall frame (18×18 pt template image rendered into a 36×36 px Retina backing store).
|
||||
- Scurry uses leg wiggle up to ~1.0 with a small horizontal jiggle; it’s additive to any existing idle wiggle.
|
||||
|
||||
Behavioral notes
|
||||
|
||||
- No external CLI/broker toggle for ears/working; keep it internal to the app’s own signals to avoid accidental flapping.
|
||||
- Keep TTLs short (<10s) so the icon returns to baseline quickly if a job hangs.
|
||||
57
openclaw/docs/platforms/mac/logging.md
Normal file
57
openclaw/docs/platforms/mac/logging.md
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
---
|
||||
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)
|
||||
|
||||
OpenClaw routes macOS app logs through swift-log (unified logging by default) and can write a local, rotating file log to disk when you need a durable capture.
|
||||
|
||||
- Verbosity: **Debug pane → Logs → App logging → Verbosity**
|
||||
- Enable: **Debug pane → Logs → App logging → “Write rolling diagnostics log (JSONL)”**
|
||||
- Location: `~/Library/Logs/OpenClaw/diagnostics.jsonl` (rotates automatically; old files are suffixed with `.1`, `.2`, …)
|
||||
- Clear: **Debug pane → Logs → App logging → “Clear”**
|
||||
|
||||
Notes:
|
||||
|
||||
- This is **off by default**. Enable only while actively debugging.
|
||||
- Treat the file as sensitive; don’t share it without review.
|
||||
|
||||
## Unified logging private data on macOS
|
||||
|
||||
Unified logging redacts most payloads unless a subsystem opts into `privacy -off`. Per Peter's write-up on macOS [logging privacy shenanigans](https://steipete.me/posts/2025/logging-privacy-shenanigans) (2025) this is controlled by a plist in `/Library/Preferences/Logging/Subsystems/` keyed by the subsystem name. Only new log entries pick up the flag, so enable it before reproducing an issue.
|
||||
|
||||
## 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 is required; logd notices the file quickly, but only new log lines will include private payloads.
|
||||
- View the richer output with the existing helper, e.g. `./scripts/clawlog.sh --category WebChat --last 5m`.
|
||||
|
||||
## 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.
|
||||
- Remember this surface can include phone numbers and message bodies; keep the plist in place only while you actively need the extra detail.
|
||||
81
openclaw/docs/platforms/mac/menu-bar.md
Normal file
81
openclaw/docs/platforms/mac/menu-bar.md
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
---
|
||||
summary: "Menu bar status logic and what is surfaced to users"
|
||||
read_when:
|
||||
- Tweaking mac menu UI or status logic
|
||||
title: "Menu Bar"
|
||||
---
|
||||
|
||||
# Menu Bar Status Logic
|
||||
|
||||
## What is shown
|
||||
|
||||
- We surface the current agent work state in the menu bar icon and in the first status row of the menu.
|
||||
- Health status is hidden while work is active; it returns when all sessions are idle.
|
||||
- The “Nodes” block in the menu lists **devices** only (paired nodes via `node.list`), not client/presence entries.
|
||||
- A “Usage” section appears under Context when provider usage snapshots are available.
|
||||
|
||||
## State model
|
||||
|
||||
- Sessions: events arrive with `runId` (per-run) plus `sessionKey` in the payload. The “main” session is the key `main`; if absent, we fall back to the most recently updated session.
|
||||
- Priority: main always wins. If main is active, its state is shown immediately. If main is idle, the most recently active non‑main session is shown. We do not flip‑flop mid‑activity; we only switch 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 `toolName` and `meta/args`.
|
||||
|
||||
## IconState enum (Swift)
|
||||
|
||||
- `idle`
|
||||
- `workingMain(ActivityKind)`
|
||||
- `workingOther(ActivityKind)`
|
||||
- `overridden(ActivityKind)` (debug override)
|
||||
|
||||
### ActivityKind → glyph
|
||||
|
||||
- `exec` → 💻
|
||||
- `read` → 📄
|
||||
- `write` → ✍️
|
||||
- `edit` → 📝
|
||||
- `attach` → 📎
|
||||
- default → 🛠️
|
||||
|
||||
### Visual mapping
|
||||
|
||||
- `idle`: normal critter.
|
||||
- `workingMain`: badge with glyph, full tint, leg “working” animation.
|
||||
- `workingOther`: badge with glyph, muted tint, no scurry.
|
||||
- `overridden`: uses the chosen glyph/tint regardless of activity.
|
||||
|
||||
## Status row text (menu)
|
||||
|
||||
- While work is active: `<Session role> · <activity label>`
|
||||
- Examples: `Main · exec: pnpm test`, `Other · read: apps/macos/Sources/OpenClaw/AppState.swift`.
|
||||
- When idle: falls back to the health summary.
|
||||
|
||||
## Event ingestion
|
||||
|
||||
- Source: control‑channel `agent` events (`ControlChannel.handleAgentEvent`).
|
||||
- Parsed fields:
|
||||
- `stream: "job"` with `data.state` for start/stop.
|
||||
- `stream: "tool"` with `data.phase`, `name`, optional `meta`/`args`.
|
||||
- Labels:
|
||||
- `exec`: first line of `args.command`.
|
||||
- `read`/`write`: shortened path.
|
||||
- `edit`: path plus inferred change kind from `meta`/diff counts.
|
||||
- fallback: tool name.
|
||||
|
||||
## Debug override
|
||||
|
||||
- Settings ▸ Debug ▸ “Icon override” picker:
|
||||
- `System (auto)` (default)
|
||||
- `Working: main` (per tool kind)
|
||||
- `Working: other` (per tool kind)
|
||||
- `Idle`
|
||||
- Stored via `@AppStorage("iconOverride")`; mapped to `IconState.overridden`.
|
||||
|
||||
## Testing checklist
|
||||
|
||||
- Trigger main session job: verify icon switches immediately and status row shows main label.
|
||||
- Trigger non‑main session job while main idle: icon/status shows non‑main; stays stable until it finishes.
|
||||
- Start main while other active: icon flips to main instantly.
|
||||
- Rapid tool bursts: ensure badge does not flicker (TTL grace on tool results).
|
||||
- Health row reappears once all sessions idle.
|
||||
65
openclaw/docs/platforms/mac/peekaboo.md
Normal file
65
openclaw/docs/platforms/mac/peekaboo.md
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
---
|
||||
summary: "PeekabooBridge integration for macOS UI automation"
|
||||
read_when:
|
||||
- Hosting PeekabooBridge in OpenClaw.app
|
||||
- Integrating Peekaboo via Swift Package Manager
|
||||
- Changing PeekabooBridge protocol/paths
|
||||
title: "Peekaboo Bridge"
|
||||
---
|
||||
|
||||
# Peekaboo Bridge (macOS UI automation)
|
||||
|
||||
OpenClaw can host **PeekabooBridge** as a local, permission‑aware UI automation
|
||||
broker. 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**: use the `peekaboo` CLI (no separate `openclaw ui ...` surface).
|
||||
- **UI**: visual overlays stay in Peekaboo.app; OpenClaw is a thin broker host.
|
||||
|
||||
## Enable the bridge
|
||||
|
||||
In the macOS app:
|
||||
|
||||
- Settings → **Enable Peekaboo Bridge**
|
||||
|
||||
When enabled, OpenClaw starts a local UNIX socket server. If disabled, the host
|
||||
is stopped and `peekaboo` will fall back to other available hosts.
|
||||
|
||||
## 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. You can override with:
|
||||
|
||||
```bash
|
||||
export PEEKABOO_BRIDGE_SOCKET=/path/to/bridge.sock
|
||||
```
|
||||
|
||||
## Security & permissions
|
||||
|
||||
- The bridge validates **caller code signatures**; an allowlist of TeamIDs is
|
||||
enforced (Peekaboo host TeamID + OpenClaw app TeamID).
|
||||
- Requests time out after ~10 seconds.
|
||||
- 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 and expire automatically after a short window.
|
||||
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.
|
||||
50
openclaw/docs/platforms/mac/permissions.md
Normal file
50
openclaw/docs/platforms/mac/permissions.md
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
---
|
||||
summary: "macOS permission persistence (TCC) and signing requirements"
|
||||
read_when:
|
||||
- Debugging missing or stuck macOS permission prompts
|
||||
- Packaging or signing the macOS app
|
||||
- Changing bundle IDs or app install paths
|
||||
title: "macOS Permissions"
|
||||
---
|
||||
|
||||
# macOS permissions (TCC)
|
||||
|
||||
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: changing the bundle ID 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 will forget previous
|
||||
grants, and prompts can disappear entirely until the stale entries are cleared.
|
||||
|
||||
## 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 (replace bundle ID as needed):
|
||||
|
||||
```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.
|
||||
84
openclaw/docs/platforms/mac/remote.md
Normal file
84
openclaw/docs/platforms/mac/remote.md
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
---
|
||||
summary: "macOS app flow for controlling a remote OpenClaw gateway over SSH"
|
||||
read_when:
|
||||
- Setting up or debugging remote mac control
|
||||
title: "Remote Control"
|
||||
---
|
||||
|
||||
# Remote OpenClaw (macOS ⇄ remote host)
|
||||
|
||||
This flow lets the macOS app act as a full remote control for an OpenClaw gateway running on another host (desktop/server). It’s the app’s **Remote over SSH** (remote run) feature. All features—health checks, Voice Wake forwarding, and Web Chat—reuse the same remote SSH configuration from _Settings → General_.
|
||||
|
||||
## Modes
|
||||
|
||||
- **Local (this Mac)**: Everything runs on the laptop. No SSH involved.
|
||||
- **Remote over SSH (default)**: OpenClaw commands are executed on the remote host. The mac app opens an SSH connection with `-o BatchMode` plus your chosen identity/key and a local port-forward.
|
||||
- **Remote direct (ws/wss)**: No SSH tunnel. The mac app connects to the gateway URL directly (for example, via Tailscale Serve or a public HTTPS reverse proxy).
|
||||
|
||||
## Remote transports
|
||||
|
||||
Remote mode supports two transports:
|
||||
|
||||
- **SSH tunnel** (default): Uses `ssh -N -L ...` to forward the gateway port to localhost. The gateway will see 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.
|
||||
|
||||
## 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. Open SSH with key auth. We recommend **Tailscale** IPs for stable reachability off-LAN.
|
||||
|
||||
## macOS app setup
|
||||
|
||||
1. Open _Settings → General_.
|
||||
2. Under **OpenClaw runs**, pick **Remote over SSH** 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 indicates the remote `openclaw status --json` runs correctly. Failures usually mean PATH/CLI issues; exit 127 means the CLI isn’t found remotely.
|
||||
4. Health checks and Web Chat will now run through this SSH tunnel automatically.
|
||||
|
||||
## Web Chat
|
||||
|
||||
- **SSH tunnel**: Web Chat connects to the gateway over the forwarded WebSocket control port (default 18789).
|
||||
- **Direct (ws/wss)**: Web Chat connects straight to the configured gateway URL.
|
||||
- There is no separate WebChat HTTP server anymore.
|
||||
|
||||
## Permissions
|
||||
|
||||
- The remote host needs the same TCC approvals as local (Automation, Accessibility, Screen Recording, Microphone, Speech Recognition, Notifications). Run onboarding on that machine to grant them once.
|
||||
- Nodes advertise their permission state via `node.list` / `node.describe` so agents know what’s available.
|
||||
|
||||
## Security notes
|
||||
|
||||
- Prefer loopback binds on the remote host and connect via SSH or Tailscale.
|
||||
- SSH tunneling uses strict host-key checking; trust the host key first so it exists in `~/.ssh/known_hosts`.
|
||||
- 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 --verbose` **on the remote host**. Scan the QR with WhatsApp on your phone.
|
||||
- Re-run login on that host if auth expires. Health check will surface link problems.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- **exit 127 / not found**: `openclaw` isn’t 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 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.
|
||||
- **Voice Wake**: trigger phrases are forwarded automatically in remote mode; no separate forwarder is needed.
|
||||
|
||||
## Notification sounds
|
||||
|
||||
Pick sounds per notification from scripts with `openclaw` and `node.invoke`, e.g.:
|
||||
|
||||
```bash
|
||||
openclaw nodes notify --node <id> --title "Ping" --body "Remote gateway ready" --sound Glass
|
||||
```
|
||||
|
||||
There is no global “default sound” toggle in the app anymore; callers choose a sound (or none) per request.
|
||||
47
openclaw/docs/platforms/mac/signing.md
Normal file
47
openclaw/docs/platforms/mac/signing.md
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
---
|
||||
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)
|
||||
|
||||
This app is usually built from [`scripts/package-mac-app.sh`](https://github.com/openclaw/openclaw/blob/main/scripts/package-mac-app.sh), which now:
|
||||
|
||||
- sets a stable debug bundle identifier: `ai.openclaw.mac.debug`
|
||||
- writes the Info.plist with that bundle id (override via `BUNDLE_ID=...`)
|
||||
- calls [`scripts/codesign-mac-app.sh`](https://github.com/openclaw/openclaw/blob/main/scripts/codesign-mac-app.sh) to sign the main binary and app bundle so macOS treats each rebuild as the same signed bundle and keeps TCC permissions (notifications, accessibility, screen recording, mic, speech). For stable permissions, use a real signing identity; ad-hoc is opt-in and fragile (see [macOS permissions](/platforms/mac/permissions)).
|
||||
- uses `CODESIGN_TIMESTAMP=auto` by default; it enables trusted timestamps for Developer ID signatures. Set `CODESIGN_TIMESTAMP=off` to skip timestamping (offline debug builds).
|
||||
- inject build metadata into Info.plist: `OpenClawBuildTimestamp` (UTC) and `OpenClawGitCommit` (short hash) so the About pane can show build, git, and debug/release channel.
|
||||
- **Packaging defaults to Node 24**: the script runs TS builds and the Control UI build. Node 22 LTS, currently `22.14+`, remains supported for compatibility.
|
||||
- reads `SIGN_IDENTITY` from the environment. Add `export SIGN_IDENTITY="Apple Development: Your Name (TEAMID)"` (or your Developer ID Application cert) to your shell rc to always sign with your cert. Ad-hoc signing requires explicit opt-in via `ALLOW_ADHOC_SIGNING=1` or `SIGN_IDENTITY="-"` (not recommended for permission testing).
|
||||
- runs a Team ID audit after signing and fails if any Mach-O inside the app bundle is signed by 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
|
||||
|
||||
When signing with `SIGN_IDENTITY="-"` (ad-hoc), the script automatically disables the **Hardened Runtime** (`--options runtime`). This is necessary to prevent crashes when the app attempts to load 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
|
||||
|
||||
`package-mac-app.sh` stamps the bundle with:
|
||||
|
||||
- `OpenClawBuildTimestamp`: ISO8601 UTC at package time
|
||||
- `OpenClawGitCommit`: short git hash (or `unknown` if unavailable)
|
||||
|
||||
The About tab reads these keys to show version, build date, git commit, and whether it’s a debug build (via `#if DEBUG`). Run the packager to refresh these values after code changes.
|
||||
|
||||
## Why
|
||||
|
||||
TCC permissions are tied to the bundle identifier _and_ code signature. Unsigned debug builds with changing UUIDs were causing macOS to forget grants after each rebuild. Signing the binaries (ad‑hoc by default) and keeping a fixed bundle id/path (`dist/OpenClaw.app`) preserves the grants between builds, matching the VibeTunnel approach.
|
||||
40
openclaw/docs/platforms/mac/skills.md
Normal file
40
openclaw/docs/platforms/mac/skills.md
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
---
|
||||
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)"
|
||||
---
|
||||
|
||||
# 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 are derived from `metadata.openclaw.requires` in each `SKILL.md`.
|
||||
|
||||
## Install actions
|
||||
|
||||
- `metadata.openclaw.install` defines install options (brew/node/go/uv).
|
||||
- The app calls `skills.install` to run installers on the gateway host.
|
||||
- Built-in dangerous-code `critical` findings block `skills.install` by default; suspicious findings still warn only. The dangerous override exists on the gateway request, but the default app flow stays fail-closed.
|
||||
- If every install option is `download`, the gateway surfaces all download
|
||||
choices.
|
||||
- Otherwise, the gateway picks one preferred installer using the current
|
||||
install preferences and host binaries: Homebrew first when
|
||||
`skills.install.preferBrew` is enabled and `brew` exists, then `uv`, then the
|
||||
configured node manager from `skills.install.nodeManager`, then later
|
||||
fallbacks like `go` or `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 + config updates happen on the gateway host (not the local Mac).
|
||||
60
openclaw/docs/platforms/mac/voice-overlay.md
Normal file
60
openclaw/docs/platforms/mac/voice-overlay.md
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
---
|
||||
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.
|
||||
|
||||
## Current intent
|
||||
|
||||
- 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. When the user releases: send if there is trimmed text, otherwise dismiss.
|
||||
- Wake-word alone still auto-sends on silence; push-to-talk sends immediately on release.
|
||||
|
||||
## Implemented (Dec 9, 2025)
|
||||
|
||||
- Overlay sessions now carry a token per capture (wake-word or push-to-talk). Partial/final/send/dismiss/level updates are dropped when the token doesn’t match, avoiding stale callbacks.
|
||||
- Push-to-talk adopts any visible overlay text as a prefix (so pressing the hotkey while the wake overlay is up keeps the text and appends new speech). It waits up to 1.5s for a final transcript before falling back to the current text.
|
||||
- Chime/overlay logging is emitted at `info` in categories `voicewake.overlay`, `voicewake.ptt`, and `voicewake.chime` (session start, partial, final, send, dismiss, chime reason).
|
||||
|
||||
## Next steps
|
||||
|
||||
1. **VoiceSessionCoordinator (actor)**
|
||||
- Owns exactly one `VoiceSession` at a time.
|
||||
- API (token-based): `beginWakeCapture`, `beginPushToTalk`, `updatePartial`, `endCapture`, `cancel`, `applyCooldown`.
|
||||
- Drops callbacks that carry stale tokens (prevents old recognizers from reopening the overlay).
|
||||
2. **VoiceSession (model)**
|
||||
- Fields: `token`, `source` (wakeWord|pushToTalk), committed/volatile text, chime flags, timers (auto-send, idle), `overlayMode` (display|editing|sending), cooldown deadline.
|
||||
3. **Overlay binding**
|
||||
- `VoiceSessionPublisher` (`ObservableObject`) mirrors the active session into SwiftUI.
|
||||
- `VoiceWakeOverlayView` renders only via the publisher; it never mutates global singletons directly.
|
||||
- Overlay user actions (`sendNow`, `dismiss`, `edit`) call back into the coordinator with the session token.
|
||||
4. **Unified send path**
|
||||
- On `endCapture`: if trimmed text is empty → dismiss; else `performSend(session:)` (plays send chime once, forwards, dismisses).
|
||||
- Push-to-talk: no delay; wake-word: optional delay for auto-send.
|
||||
- Apply a short cooldown to the wake runtime after push-to-talk finishes so wake-word doesn’t immediately retrigger.
|
||||
5. **Logging**
|
||||
- Coordinator emits `.info` logs in subsystem `ai.openclaw`, categories `voicewake.overlay` and `voicewake.chime`.
|
||||
- Key events: `session_started`, `adopted_by_push_to_talk`, `partial`, `finalized`, `send`, `dismiss`, `cancel`, `cooldown`.
|
||||
|
||||
## 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 should be dropped by the coordinator.
|
||||
- Ensure push-to-talk release always calls `endCapture` with the active token; if text is empty, expect `dismiss` without chime or send.
|
||||
|
||||
## Migration steps (suggested)
|
||||
|
||||
1. Add `VoiceSessionCoordinator`, `VoiceSession`, and `VoiceSessionPublisher`.
|
||||
2. Refactor `VoiceWakeRuntime` to create/update/end sessions instead of touching `VoiceWakeOverlayController` directly.
|
||||
3. Refactor `VoicePushToTalk` to adopt existing sessions and call `endCapture` on release; apply runtime cooldown.
|
||||
4. Wire `VoiceWakeOverlayController` to the publisher; remove direct calls from runtime/PTT.
|
||||
5. Add integration tests for session adoption, cooldown, and empty-text dismissal.
|
||||
67
openclaw/docs/platforms/mac/voicewake.md
Normal file
67
openclaw/docs/platforms/mac/voicewake.md
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
---
|
||||
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
|
||||
|
||||
## Modes
|
||||
|
||||
- **Wake-word mode** (default): 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 (Right Option hold)**: 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 tweak text.
|
||||
|
||||
## Runtime behavior (wake-word)
|
||||
|
||||
- Speech recognizer lives in `VoiceWakeRuntime`.
|
||||
- Trigger only fires when there’s a **meaningful pause** between the wake word and the next word (~0.55s gap). The overlay/chime can start on the pause even before the command begins.
|
||||
- Silence windows: 2.0s when speech is flowing, 5.0s if only the trigger was heard.
|
||||
- Hard stop: 120s to prevent runaway sessions.
|
||||
- Debounce between sessions: 350ms.
|
||||
- Overlay is driven via `VoiceWakeOverlayController` with committed/volatile coloring.
|
||||
- After send, recognizer restarts cleanly to listen for the next trigger.
|
||||
|
||||
## Lifecycle invariants
|
||||
|
||||
- If Voice Wake is enabled and permissions are granted, the wake-word recognizer should be listening (except during an explicit push-to-talk capture).
|
||||
- Overlay visibility (including manual dismiss via the X button) must never prevent the recognizer from resuming.
|
||||
|
||||
## Sticky overlay failure mode (previous)
|
||||
|
||||
Previously, if the overlay got stuck visible and you manually closed it, Voice Wake could appear “dead” because the runtime’s restart attempt could be blocked by overlay visibility and no subsequent restart was scheduled.
|
||||
|
||||
Hardening:
|
||||
|
||||
- Wake runtime restart is no longer blocked by overlay visibility.
|
||||
- Overlay dismiss completion triggers a `VoiceWakeRuntime.refresh(...)` via `VoiceSessionCoordinator`, so manual X-dismiss always resumes listening.
|
||||
|
||||
## Push-to-talk specifics
|
||||
|
||||
- Hotkey detection uses a global `.flagsChanged` monitor for **right Option** (`keyCode 61` + `.option`). We only observe events (no swallowing).
|
||||
- Capture pipeline lives in `VoicePushToTalk`: starts Speech immediately, streams partials to the overlay, and calls `VoiceWakeForwarder` on release.
|
||||
- When push-to-talk starts we pause the wake-word runtime to avoid dueling audio taps; it restarts automatically after release.
|
||||
- Permissions: requires Microphone + Speech; seeing events needs Accessibility/Input Monitoring approval.
|
||||
- External keyboards: some may not expose right Option as expected—offer a fallback shortcut if users report misses.
|
||||
|
||||
## User-facing settings
|
||||
|
||||
- **Voice Wake** toggle: enables wake-word runtime.
|
||||
- **Hold Cmd+Fn to talk**: enables the push-to-talk monitor. Disabled on macOS < 26.
|
||||
- Language & mic pickers, live level meter, trigger-word table, tester (local-only; does not forward).
|
||||
- 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; defaults to the macOS “Glass” system sound. You can pick any `NSSound`-loadable file (e.g. MP3/WAV/AIFF) for each event or choose **No Sound**.
|
||||
|
||||
## Forwarding behavior
|
||||
|
||||
- When Voice Wake is enabled, transcripts are forwarded to the active gateway/agent (the same local vs remote mode used by the rest of the mac app).
|
||||
- Replies are delivered to the **last-used main provider** (WhatsApp/Telegram/Discord/WebChat). If delivery fails, the error is logged and the run is still visible via WebChat/session logs.
|
||||
|
||||
## Forwarding payload
|
||||
|
||||
- `VoiceWakeForwarder.prefixedTranscript(_:)` prepends the machine hint before sending. Shared between wake-word and push-to-talk paths.
|
||||
|
||||
## Quick verification
|
||||
|
||||
- Toggle push-to-talk on, hold Cmd+Fn, speak, release: overlay should show partials then send.
|
||||
- While holding, menu-bar ears should stay enlarged (uses `triggerVoiceEars(ttl:nil)`); they drop after release.
|
||||
51
openclaw/docs/platforms/mac/webchat.md
Normal file
51
openclaw/docs/platforms/mac/webchat.md
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
---
|
||||
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)"
|
||||
---
|
||||
|
||||
# WebChat (macOS app)
|
||||
|
||||
The macOS menu bar app embeds the WebChat UI as a native SwiftUI view. It
|
||||
connects to the Gateway and defaults to the **main session** for the selected
|
||||
agent (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 & debugging
|
||||
|
||||
- Manual: Lobster menu → “Open Chat”.
|
||||
- Auto‑open for testing:
|
||||
|
||||
```bash
|
||||
dist/OpenClaw.app/Contents/MacOS/OpenClaw --webchat
|
||||
```
|
||||
|
||||
- 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 display-normalized transcript rows: inline directive
|
||||
tags are stripped from visible text, plain-text tool-call XML payloads
|
||||
(including `<tool_call>...</tool_call>`,
|
||||
`<function_call>...</function_call>`, `<tool_calls>...</tool_calls>`,
|
||||
`<function_calls>...</function_calls>`, and truncated tool-call blocks) and
|
||||
leaked ASCII/full-width 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 placeholders.
|
||||
- Session: defaults to the primary session (`main`, or `global` when scope is
|
||||
global). 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).
|
||||
61
openclaw/docs/platforms/mac/xpc.md
Normal file
61
openclaw/docs/platforms/mac/xpc.md
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
---
|
||||
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
|
||||
|
||||
**Current model:** a local Unix socket connects the **node host service** to the **macOS app** for exec approvals + `system.run`. A `openclaw-mac` debug CLI 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 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.
|
||||
- The app performs the exec in UI context, prompts if needed, and returns output.
|
||||
|
||||
Diagram (SCI):
|
||||
|
||||
```
|
||||
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 named `bridge.sock` and the PeekabooBridge JSON protocol.
|
||||
- Host preference order (client-side): Peekaboo.app → Claude.app → OpenClaw.app → local execution.
|
||||
- Security: bridge hosts require an allowed TeamID; 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: `SIGN_IDENTITY="Apple Development: <Developer Name> (<TEAMID>)" scripts/restart-mac.sh`
|
||||
- Kills existing instances
|
||||
- Swift build + package
|
||||
- Writes/bootstraps/kickstarts the LaunchAgent
|
||||
- Single instance: app exits early if another instance with the same bundle ID is running.
|
||||
|
||||
## 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.
|
||||
- IPC hardening: socket mode `0600`, token, peer-UID checks, HMAC challenge/response, short TTL.
|
||||
229
openclaw/docs/platforms/macos.md
Normal file
229
openclaw/docs/platforms/macos.md
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
---
|
||||
summary: "OpenClaw macOS companion app (menu bar + gateway broker)"
|
||||
read_when:
|
||||
- Implementing macOS app features
|
||||
- Changing gateway lifecycle or node bridging on macOS
|
||||
title: "macOS App"
|
||||
---
|
||||
|
||||
# OpenClaw macOS Companion (menu bar + gateway broker)
|
||||
|
||||
The macOS app is the **menu‑bar companion** for OpenClaw. It owns permissions,
|
||||
manages/attaches to the Gateway locally (launchd or manual), and exposes macOS
|
||||
capabilities to the agent as a node.
|
||||
|
||||
## What it does
|
||||
|
||||
- Shows native notifications and status in the menu bar.
|
||||
- Owns TCC prompts (Notifications, Accessibility, Screen Recording, Microphone,
|
||||
Speech Recognition, Automation/AppleScript).
|
||||
- Runs or connects to the Gateway (local or remote).
|
||||
- Exposes macOS‑only tools (Canvas, Camera, Screen Recording, `system.run`).
|
||||
- Starts the local node host service in **remote** mode (launchd), and stops it in **local** mode.
|
||||
- Optionally hosts **PeekabooBridge** for UI automation.
|
||||
- Installs the global CLI (`openclaw`) on request via npm, pnpm, or bun (the app prefers npm, then pnpm, then bun; Node remains the recommended Gateway runtime).
|
||||
|
||||
## Local vs remote mode
|
||||
|
||||
- **Local** (default): the app attaches to a running local Gateway if present;
|
||||
otherwise it enables the launchd service via `openclaw gateway install`.
|
||||
- **Remote**: the app connects to a Gateway over SSH/Tailscale and never starts
|
||||
a local process.
|
||||
The app starts the local **node host service** so the remote Gateway can reach this Mac.
|
||||
The app does not spawn the Gateway as a child process.
|
||||
Gateway discovery now prefers Tailscale MagicDNS names over raw tailnet IPs,
|
||||
so the Mac app recovers more reliably when tailnet IPs change.
|
||||
|
||||
## Launchd control
|
||||
|
||||
The app manages a per‑user LaunchAgent labeled `ai.openclaw.gateway`
|
||||
(or `ai.openclaw.<profile>` when using `--profile`/`OPENCLAW_PROFILE`; legacy `com.openclaw.*` still unloads).
|
||||
|
||||
```bash
|
||||
launchctl kickstart -k gui/$UID/ai.openclaw.gateway
|
||||
launchctl bootout gui/$UID/ai.openclaw.gateway
|
||||
```
|
||||
|
||||
Replace the label with `ai.openclaw.<profile>` when running a named profile.
|
||||
|
||||
If the LaunchAgent isn’t installed, enable it from the app or run
|
||||
`openclaw gateway install`.
|
||||
|
||||
## Node capabilities (mac)
|
||||
|
||||
The macOS app presents itself as a node. Common commands:
|
||||
|
||||
- Canvas: `canvas.present`, `canvas.navigate`, `canvas.eval`, `canvas.snapshot`, `canvas.a2ui.*`
|
||||
- Camera: `camera.snap`, `camera.clip`
|
||||
- Screen: `screen.snapshot`, `screen.record`
|
||||
- System: `system.run`, `system.notify`
|
||||
|
||||
The node reports a `permissions` map so agents can decide what’s allowed.
|
||||
|
||||
Node service + app IPC:
|
||||
|
||||
- When the headless node host service is running (remote mode), it connects to the Gateway WS as a node.
|
||||
- `system.run` executes in the macOS app (UI/TCC context) over a local Unix socket; prompts + output stay in-app.
|
||||
|
||||
Diagram (SCI):
|
||||
|
||||
```
|
||||
Gateway -> Node Service (WS)
|
||||
| IPC (UDS + token + HMAC + TTL)
|
||||
v
|
||||
Mac App (UI + TCC + system.run)
|
||||
```
|
||||
|
||||
## Exec approvals (system.run)
|
||||
|
||||
`system.run` is controlled by **Exec approvals** in the macOS app (Settings → Exec approvals).
|
||||
Security + ask + allowlist are stored locally on the Mac in:
|
||||
|
||||
```
|
||||
~/.openclaw/exec-approvals.json
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```json
|
||||
{
|
||||
"version": 1,
|
||||
"defaults": {
|
||||
"security": "deny",
|
||||
"ask": "on-miss"
|
||||
},
|
||||
"agents": {
|
||||
"main": {
|
||||
"security": "allowlist",
|
||||
"ask": "on-miss",
|
||||
"allowlist": [{ "pattern": "/opt/homebrew/bin/rg" }]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
- `allowlist` entries are glob patterns for resolved binary paths.
|
||||
- Raw shell command text that contains shell control or expansion syntax (`&&`, `||`, `;`, `|`, `` ` ``, `$`, `<`, `>`, `(`, `)`) is treated as an allowlist miss and requires explicit approval (or allowlisting the shell binary).
|
||||
- Choosing “Always Allow” in the prompt adds that command to the allowlist.
|
||||
- `system.run` environment overrides are filtered (drops `PATH`, `DYLD_*`, `LD_*`, `NODE_OPTIONS`, `PYTHON*`, `PERL*`, `RUBYOPT`, `SHELLOPTS`, `PS4`) and then merged with the app’s environment.
|
||||
- For shell wrappers (`bash|sh|zsh ... -c/-lc`), request-scoped environment overrides are reduced to a small explicit allowlist (`TERM`, `LANG`, `LC_*`, `COLORTERM`, `NO_COLOR`, `FORCE_COLOR`).
|
||||
- For allow-always decisions in allowlist mode, known dispatch wrappers (`env`, `nice`, `nohup`, `stdbuf`, `timeout`) persist inner executable paths instead of wrapper paths. If unwrapping is not safe, no allowlist entry is persisted automatically.
|
||||
|
||||
## Deep links
|
||||
|
||||
The app registers the `openclaw://` URL scheme for local actions.
|
||||
|
||||
### `openclaw://agent`
|
||||
|
||||
Triggers a Gateway `agent` request.
|
||||
|
||||
```bash
|
||||
open 'openclaw://agent?message=Hello%20from%20deep%20link'
|
||||
```
|
||||
|
||||
Query parameters:
|
||||
|
||||
- `message` (required)
|
||||
- `sessionKey` (optional)
|
||||
- `thinking` (optional)
|
||||
- `deliver` / `to` / `channel` (optional)
|
||||
- `timeoutSeconds` (optional)
|
||||
- `key` (optional unattended mode key)
|
||||
|
||||
Safety:
|
||||
|
||||
- Without `key`, the app prompts for confirmation.
|
||||
- Without `key`, the app enforces a short message limit for the confirmation prompt and ignores `deliver` / `to` / `channel`.
|
||||
- With a valid `key`, the run is unattended (intended for personal automations).
|
||||
|
||||
## Onboarding flow (typical)
|
||||
|
||||
1. Install and launch **OpenClaw.app**.
|
||||
2. Complete the permissions checklist (TCC prompts).
|
||||
3. Ensure **Local** mode is active and the Gateway is running.
|
||||
4. Install the CLI if you want terminal access.
|
||||
|
||||
## State dir placement (macOS)
|
||||
|
||||
Avoid putting your OpenClaw state dir in iCloud or other cloud-synced folders.
|
||||
Sync-backed paths can add latency and occasionally cause file-lock/sync races for
|
||||
sessions and credentials.
|
||||
|
||||
Prefer a local non-synced state path such as:
|
||||
|
||||
```bash
|
||||
OPENCLAW_STATE_DIR=~/.openclaw
|
||||
```
|
||||
|
||||
If `openclaw doctor` detects state under:
|
||||
|
||||
- `~/Library/Mobile Documents/com~apple~CloudDocs/...`
|
||||
- `~/Library/CloudStorage/...`
|
||||
|
||||
it will warn and recommend moving back to a local path.
|
||||
|
||||
## Build & dev workflow (native)
|
||||
|
||||
- `cd apps/macos && swift build`
|
||||
- `swift run OpenClaw` (or Xcode)
|
||||
- Package app: `scripts/package-mac-app.sh`
|
||||
|
||||
## Debug gateway connectivity (macOS CLI)
|
||||
|
||||
Use the debug CLI to exercise the same Gateway WebSocket handshake and discovery
|
||||
logic that the macOS app uses, without launching the app.
|
||||
|
||||
```bash
|
||||
cd apps/macos
|
||||
swift run openclaw-mac connect --json
|
||||
swift run openclaw-mac discover --timeout 3000 --json
|
||||
```
|
||||
|
||||
Connect options:
|
||||
|
||||
- `--url <ws://host:port>`: override config
|
||||
- `--mode <local|remote>`: resolve from config (default: config or local)
|
||||
- `--probe`: force a fresh health probe
|
||||
- `--timeout <ms>`: request timeout (default: `15000`)
|
||||
- `--json`: structured output for diffing
|
||||
|
||||
Discovery options:
|
||||
|
||||
- `--include-local`: include gateways that would be filtered as “local”
|
||||
- `--timeout <ms>`: overall discovery window (default: `2000`)
|
||||
- `--json`: structured output for diffing
|
||||
|
||||
Tip: compare against `openclaw gateway discover --json` to see whether the
|
||||
macOS app’s discovery pipeline (`local.` plus the configured wide-area domain, with
|
||||
wide-area and Tailscale Serve fallbacks) differs from
|
||||
the Node CLI’s `dns-sd` based discovery.
|
||||
|
||||
## Remote connection plumbing (SSH tunnels)
|
||||
|
||||
When the macOS app runs in **Remote** mode, it opens an SSH tunnel so local UI
|
||||
components can talk to a remote Gateway as if it were on localhost.
|
||||
|
||||
### Control tunnel (Gateway WebSocket port)
|
||||
|
||||
- **Purpose:** health checks, status, Web Chat, config, and other control-plane calls.
|
||||
- **Local port:** the Gateway port (default `18789`), always stable.
|
||||
- **Remote port:** the same Gateway port on the remote host.
|
||||
- **Behavior:** no random local port; the app reuses an existing healthy tunnel
|
||||
or restarts it if needed.
|
||||
- **SSH shape:** `ssh -N -L <local>:127.0.0.1:<remote>` with BatchMode +
|
||||
ExitOnForwardFailure + keepalive options.
|
||||
- **IP reporting:** the SSH tunnel uses loopback, so the gateway will see the node
|
||||
IP as `127.0.0.1`. Use **Direct (ws/wss)** transport if you want the real client
|
||||
IP to appear (see [macOS remote access](/platforms/mac/remote)).
|
||||
|
||||
For setup steps, see [macOS remote access](/platforms/mac/remote). For protocol
|
||||
details, see [Gateway protocol](/gateway/protocol).
|
||||
|
||||
## Related docs
|
||||
|
||||
- [Gateway runbook](/gateway)
|
||||
- [Gateway (macOS)](/platforms/mac/bundled-gateway)
|
||||
- [macOS permissions](/platforms/mac/permissions)
|
||||
- [Canvas](/platforms/mac/canvas)
|
||||
305
openclaw/docs/platforms/oracle.md
Normal file
305
openclaw/docs/platforms/oracle.md
Normal file
|
|
@ -0,0 +1,305 @@
|
|||
---
|
||||
summary: "OpenClaw on Oracle Cloud (Always Free ARM)"
|
||||
read_when:
|
||||
- Setting up OpenClaw on Oracle Cloud
|
||||
- Looking for low-cost VPS hosting for OpenClaw
|
||||
- Want 24/7 OpenClaw on a small server
|
||||
title: "Oracle Cloud (Platform)"
|
||||
---
|
||||
|
||||
# OpenClaw on Oracle Cloud (OCI)
|
||||
|
||||
## Goal
|
||||
|
||||
Run a persistent OpenClaw Gateway on Oracle Cloud's **Always Free** ARM tier.
|
||||
|
||||
Oracle’s free tier can be a great fit for OpenClaw (especially if you already have an OCI account), but it comes with tradeoffs:
|
||||
|
||||
- ARM architecture (most things work, but some binaries may be x86-only)
|
||||
- Capacity and signup can be finicky
|
||||
|
||||
## Cost Comparison (2026)
|
||||
|
||||
| Provider | Plan | Specs | Price/mo | Notes |
|
||||
| ------------ | --------------- | ---------------------- | -------- | --------------------- |
|
||||
| Oracle Cloud | Always Free ARM | up to 4 OCPU, 24GB RAM | $0 | ARM, limited capacity |
|
||||
| Hetzner | CX22 | 2 vCPU, 4GB RAM | ~ $4 | Cheapest paid option |
|
||||
| DigitalOcean | Basic | 1 vCPU, 1GB RAM | $6 | Easy UI, good docs |
|
||||
| Vultr | Cloud Compute | 1 vCPU, 1GB RAM | $6 | Many locations |
|
||||
| Linode | Nanode | 1 vCPU, 1GB RAM | $5 | Now part of Akamai |
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Oracle Cloud account ([signup](https://www.oracle.com/cloud/free/)) — see [community signup guide](https://gist.github.com/rssnyder/51e3cfedd730e7dd5f4a816143b25dbd) if you hit issues
|
||||
- Tailscale account (free at [tailscale.com](https://tailscale.com))
|
||||
- ~30 minutes
|
||||
|
||||
## 1) Create an OCI Instance
|
||||
|
||||
1. Log into [Oracle Cloud Console](https://cloud.oracle.com/)
|
||||
2. Navigate to **Compute → Instances → Create Instance**
|
||||
3. Configure:
|
||||
- **Name:** `openclaw`
|
||||
- **Image:** Ubuntu 24.04 (aarch64)
|
||||
- **Shape:** `VM.Standard.A1.Flex` (Ampere ARM)
|
||||
- **OCPUs:** 2 (or up to 4)
|
||||
- **Memory:** 12 GB (or up to 24 GB)
|
||||
- **Boot volume:** 50 GB (up to 200 GB free)
|
||||
- **SSH key:** Add your public key
|
||||
4. Click **Create**
|
||||
5. Note the public IP address
|
||||
|
||||
**Tip:** If instance creation fails with "Out of capacity", try a different availability domain or retry later. Free tier capacity is limited.
|
||||
|
||||
## 2) Connect and Update
|
||||
|
||||
```bash
|
||||
# Connect via public IP
|
||||
ssh ubuntu@YOUR_PUBLIC_IP
|
||||
|
||||
# Update system
|
||||
sudo apt update && sudo apt upgrade -y
|
||||
sudo apt install -y build-essential
|
||||
```
|
||||
|
||||
**Note:** `build-essential` is required for ARM compilation of some dependencies.
|
||||
|
||||
## 3) Configure User and Hostname
|
||||
|
||||
```bash
|
||||
# Set hostname
|
||||
sudo hostnamectl set-hostname openclaw
|
||||
|
||||
# Set password for ubuntu user
|
||||
sudo passwd ubuntu
|
||||
|
||||
# Enable lingering (keeps user services running after logout)
|
||||
sudo loginctl enable-linger ubuntu
|
||||
```
|
||||
|
||||
## 4) Install Tailscale
|
||||
|
||||
```bash
|
||||
curl -fsSL https://tailscale.com/install.sh | sh
|
||||
sudo tailscale up --ssh --hostname=openclaw
|
||||
```
|
||||
|
||||
This enables Tailscale SSH, so you can connect via `ssh openclaw` from any device on your tailnet — no public IP needed.
|
||||
|
||||
Verify:
|
||||
|
||||
```bash
|
||||
tailscale status
|
||||
```
|
||||
|
||||
**From now on, connect via Tailscale:** `ssh ubuntu@openclaw` (or use the Tailscale IP).
|
||||
|
||||
## 5) Install OpenClaw
|
||||
|
||||
```bash
|
||||
curl -fsSL https://openclaw.ai/install.sh | bash
|
||||
source ~/.bashrc
|
||||
```
|
||||
|
||||
When prompted "How do you want to hatch your bot?", select **"Do this later"**.
|
||||
|
||||
> Note: If you hit ARM-native build issues, start with system packages (e.g. `sudo apt install -y build-essential`) before reaching for Homebrew.
|
||||
|
||||
## 6) Configure Gateway (loopback + token auth) and enable Tailscale Serve
|
||||
|
||||
Use token auth as the default. It’s predictable and avoids needing any “insecure auth” Control UI flags.
|
||||
|
||||
```bash
|
||||
# Keep the Gateway private on the VM
|
||||
openclaw config set gateway.bind loopback
|
||||
|
||||
# Require auth for the Gateway + Control UI
|
||||
openclaw config set gateway.auth.mode token
|
||||
openclaw doctor --generate-gateway-token
|
||||
|
||||
# Expose over Tailscale Serve (HTTPS + tailnet access)
|
||||
openclaw config set gateway.tailscale.mode serve
|
||||
openclaw config set gateway.trustedProxies '["127.0.0.1"]'
|
||||
|
||||
systemctl --user restart openclaw-gateway.service
|
||||
```
|
||||
|
||||
`gateway.trustedProxies=["127.0.0.1"]` here is only for the local Tailscale Serve proxy's forwarded-IP/local-client handling. It is **not** `gateway.auth.mode: "trusted-proxy"`. Diff viewer routes keep fail-closed behavior in this setup: raw `127.0.0.1` viewer requests without forwarded proxy headers can return `Diff not found`. Use `mode=file` / `mode=both` for attachments, or intentionally enable remote viewers and set `plugins.entries.diffs.config.viewerBaseUrl` (or pass a proxy `baseUrl`) if you need shareable viewer links.
|
||||
|
||||
## 7) Verify
|
||||
|
||||
```bash
|
||||
# Check version
|
||||
openclaw --version
|
||||
|
||||
# Check daemon status
|
||||
systemctl --user status openclaw-gateway.service
|
||||
|
||||
# Check Tailscale Serve
|
||||
tailscale serve status
|
||||
|
||||
# Test local response
|
||||
curl http://localhost:18789
|
||||
```
|
||||
|
||||
## 8) Lock Down VCN Security
|
||||
|
||||
Now that everything is working, lock down the VCN to block all traffic except Tailscale. OCI's Virtual Cloud Network acts as a firewall at the network edge — traffic is blocked before it reaches your instance.
|
||||
|
||||
1. Go to **Networking → Virtual Cloud Networks** in the OCI Console
|
||||
2. Click your VCN → **Security Lists** → Default Security List
|
||||
3. **Remove** all ingress rules except:
|
||||
- `0.0.0.0/0 UDP 41641` (Tailscale)
|
||||
4. Keep default egress rules (allow all outbound)
|
||||
|
||||
This blocks SSH on port 22, HTTP, HTTPS, and everything else at the network edge. From now on, you can only connect via Tailscale.
|
||||
|
||||
---
|
||||
|
||||
## Access the Control UI
|
||||
|
||||
From any device on your Tailscale network:
|
||||
|
||||
```
|
||||
https://openclaw.<tailnet-name>.ts.net/
|
||||
```
|
||||
|
||||
Replace `<tailnet-name>` with your tailnet name (visible in `tailscale status`).
|
||||
|
||||
No SSH tunnel needed. Tailscale provides:
|
||||
|
||||
- HTTPS encryption (automatic certs)
|
||||
- Authentication via Tailscale identity
|
||||
- Access from any device on your tailnet (laptop, phone, etc.)
|
||||
|
||||
---
|
||||
|
||||
## Security: VCN + Tailscale (recommended baseline)
|
||||
|
||||
With the VCN locked down (only UDP 41641 open) and the Gateway bound to loopback, you get strong defense-in-depth: public traffic is blocked at the network edge, and admin access happens over your tailnet.
|
||||
|
||||
This setup often removes the _need_ for extra host-based firewall rules purely to stop Internet-wide SSH brute force — but you should still keep the OS updated, run `openclaw security audit`, and verify you aren’t accidentally listening on public interfaces.
|
||||
|
||||
### Already protected
|
||||
|
||||
| Traditional Step | Needed? | Why |
|
||||
| ------------------ | ----------- | ---------------------------------------------------------------------------- |
|
||||
| UFW firewall | No | VCN blocks before traffic reaches instance |
|
||||
| fail2ban | No | No brute force if port 22 blocked at VCN |
|
||||
| sshd hardening | No | Tailscale SSH doesn't use sshd |
|
||||
| Disable root login | No | Tailscale uses Tailscale identity, not system users |
|
||||
| SSH key-only auth | No | Tailscale authenticates via your tailnet |
|
||||
| IPv6 hardening | Usually not | Depends on your VCN/subnet settings; verify what’s actually assigned/exposed |
|
||||
|
||||
### Still Recommended
|
||||
|
||||
- **Credential permissions:** `chmod 700 ~/.openclaw`
|
||||
- **Security audit:** `openclaw security audit`
|
||||
- **System updates:** `sudo apt update && sudo apt upgrade` regularly
|
||||
- **Monitor Tailscale:** Review devices in [Tailscale admin console](https://login.tailscale.com/admin)
|
||||
|
||||
### Verify Security Posture
|
||||
|
||||
```bash
|
||||
# Confirm no public ports listening
|
||||
sudo ss -tlnp | grep -v '127.0.0.1\|::1'
|
||||
|
||||
# Verify Tailscale SSH is active
|
||||
tailscale status | grep -q 'offers: ssh' && echo "Tailscale SSH active"
|
||||
|
||||
# Optional: disable sshd entirely
|
||||
sudo systemctl disable --now ssh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Fallback: SSH Tunnel
|
||||
|
||||
If Tailscale Serve isn't working, use an SSH tunnel:
|
||||
|
||||
```bash
|
||||
# From your local machine (via Tailscale)
|
||||
ssh -L 18789:127.0.0.1:18789 ubuntu@openclaw
|
||||
```
|
||||
|
||||
Then open `http://localhost:18789`.
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Instance creation fails ("Out of capacity")
|
||||
|
||||
Free tier ARM instances are popular. Try:
|
||||
|
||||
- Different availability domain
|
||||
- Retry during off-peak hours (early morning)
|
||||
- Use the "Always Free" filter when selecting shape
|
||||
|
||||
### Tailscale will not connect
|
||||
|
||||
```bash
|
||||
# Check status
|
||||
sudo tailscale status
|
||||
|
||||
# Re-authenticate
|
||||
sudo tailscale up --ssh --hostname=openclaw --reset
|
||||
```
|
||||
|
||||
### Gateway will not start
|
||||
|
||||
```bash
|
||||
openclaw gateway status
|
||||
openclaw doctor --non-interactive
|
||||
journalctl --user -u openclaw-gateway.service -n 50
|
||||
```
|
||||
|
||||
### Cannot reach Control UI
|
||||
|
||||
```bash
|
||||
# Verify Tailscale Serve is running
|
||||
tailscale serve status
|
||||
|
||||
# Check gateway is listening
|
||||
curl http://localhost:18789
|
||||
|
||||
# Restart if needed
|
||||
systemctl --user restart openclaw-gateway.service
|
||||
```
|
||||
|
||||
### ARM binary issues
|
||||
|
||||
Some tools may not have ARM builds. Check:
|
||||
|
||||
```bash
|
||||
uname -m # Should show aarch64
|
||||
```
|
||||
|
||||
Most npm packages work fine. For binaries, look for `linux-arm64` or `aarch64` releases.
|
||||
|
||||
---
|
||||
|
||||
## Persistence
|
||||
|
||||
All state lives in:
|
||||
|
||||
- `~/.openclaw/` — `openclaw.json`, per-agent `auth-profiles.json`, channel/provider state, and session data
|
||||
- `~/.openclaw/workspace/` — workspace (SOUL.md, memory, artifacts)
|
||||
|
||||
Back up periodically:
|
||||
|
||||
```bash
|
||||
openclaw backup create
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [Gateway remote access](/gateway/remote) — other remote access patterns
|
||||
- [Tailscale integration](/gateway/tailscale) — full Tailscale docs
|
||||
- [Gateway configuration](/gateway/configuration) — all config options
|
||||
- [DigitalOcean guide](/platforms/digitalocean) — if you want paid + easier signup
|
||||
- [Hetzner guide](/install/hetzner) — Docker-based alternative
|
||||
420
openclaw/docs/platforms/raspberry-pi.md
Normal file
420
openclaw/docs/platforms/raspberry-pi.md
Normal file
|
|
@ -0,0 +1,420 @@
|
|||
---
|
||||
summary: "OpenClaw on Raspberry Pi (budget self-hosted setup)"
|
||||
read_when:
|
||||
- Setting up OpenClaw on a Raspberry Pi
|
||||
- Running OpenClaw on ARM devices
|
||||
- Building a cheap always-on personal AI
|
||||
title: "Raspberry Pi (Platform)"
|
||||
---
|
||||
|
||||
# OpenClaw on Raspberry Pi
|
||||
|
||||
## Goal
|
||||
|
||||
Run a persistent, always-on OpenClaw Gateway on a Raspberry Pi for **~$35-80** one-time cost (no monthly fees).
|
||||
|
||||
Perfect for:
|
||||
|
||||
- 24/7 personal AI assistant
|
||||
- Home automation hub
|
||||
- Low-power, always-available Telegram/WhatsApp bot
|
||||
|
||||
## Hardware Requirements
|
||||
|
||||
| Pi Model | RAM | Works? | Notes |
|
||||
| --------------- | ------- | -------- | ---------------------------------- |
|
||||
| **Pi 5** | 4GB/8GB | ✅ Best | Fastest, recommended |
|
||||
| **Pi 4** | 4GB | ✅ Good | Sweet spot for most users |
|
||||
| **Pi 4** | 2GB | ✅ OK | Works, add swap |
|
||||
| **Pi 4** | 1GB | ⚠️ Tight | Possible with swap, minimal config |
|
||||
| **Pi 3B+** | 1GB | ⚠️ Slow | Works but sluggish |
|
||||
| **Pi Zero 2 W** | 512MB | ❌ | Not recommended |
|
||||
|
||||
**Minimum specs:** 1GB RAM, 1 core, 500MB disk
|
||||
**Recommended:** 2GB+ RAM, 64-bit OS, 16GB+ SD card (or USB SSD)
|
||||
|
||||
## What you need
|
||||
|
||||
- Raspberry Pi 4 or 5 (2GB+ recommended)
|
||||
- MicroSD card (16GB+) or USB SSD (better performance)
|
||||
- Power supply (official Pi PSU recommended)
|
||||
- Network connection (Ethernet or WiFi)
|
||||
- ~30 minutes
|
||||
|
||||
## 1) Flash the OS
|
||||
|
||||
Use **Raspberry Pi OS Lite (64-bit)** — no desktop needed for a headless server.
|
||||
|
||||
1. Download [Raspberry Pi Imager](https://www.raspberrypi.com/software/)
|
||||
2. Choose OS: **Raspberry Pi OS Lite (64-bit)**
|
||||
3. Click the gear icon (⚙️) to pre-configure:
|
||||
- Set hostname: `gateway-host`
|
||||
- Enable SSH
|
||||
- Set username/password
|
||||
- Configure WiFi (if not using Ethernet)
|
||||
4. Flash to your SD card / USB drive
|
||||
5. Insert and boot the Pi
|
||||
|
||||
## 2) Connect via SSH
|
||||
|
||||
```bash
|
||||
ssh user@gateway-host
|
||||
# or use the IP address
|
||||
ssh user@192.168.x.x
|
||||
```
|
||||
|
||||
## 3) System Setup
|
||||
|
||||
```bash
|
||||
# Update system
|
||||
sudo apt update && sudo apt upgrade -y
|
||||
|
||||
# Install essential packages
|
||||
sudo apt install -y git curl build-essential
|
||||
|
||||
# Set timezone (important for cron/reminders)
|
||||
sudo timedatectl set-timezone America/Chicago # Change to your timezone
|
||||
```
|
||||
|
||||
## 4) Install Node.js 24 (ARM64)
|
||||
|
||||
```bash
|
||||
# Install Node.js via NodeSource
|
||||
curl -fsSL https://deb.nodesource.com/setup_24.x | sudo -E bash -
|
||||
sudo apt install -y nodejs
|
||||
|
||||
# Verify
|
||||
node --version # Should show v24.x.x
|
||||
npm --version
|
||||
```
|
||||
|
||||
## 5) Add Swap (Important for 2GB or less)
|
||||
|
||||
Swap prevents out-of-memory crashes:
|
||||
|
||||
```bash
|
||||
# Create 2GB swap file
|
||||
sudo fallocate -l 2G /swapfile
|
||||
sudo chmod 600 /swapfile
|
||||
sudo mkswap /swapfile
|
||||
sudo swapon /swapfile
|
||||
|
||||
# Make permanent
|
||||
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
|
||||
|
||||
# Optimize for low RAM (reduce swappiness)
|
||||
echo 'vm.swappiness=10' | sudo tee -a /etc/sysctl.conf
|
||||
sudo sysctl -p
|
||||
```
|
||||
|
||||
## 6) Install OpenClaw
|
||||
|
||||
### Option A: Standard Install (Recommended)
|
||||
|
||||
```bash
|
||||
curl -fsSL https://openclaw.ai/install.sh | bash
|
||||
```
|
||||
|
||||
### Option B: Hackable Install (For tinkering)
|
||||
|
||||
```bash
|
||||
git clone https://github.com/openclaw/openclaw.git
|
||||
cd openclaw
|
||||
npm install
|
||||
npm run build
|
||||
npm link
|
||||
```
|
||||
|
||||
The hackable install gives you direct access to logs and code — useful for debugging ARM-specific issues.
|
||||
|
||||
## 7) Run Onboarding
|
||||
|
||||
```bash
|
||||
openclaw onboard --install-daemon
|
||||
```
|
||||
|
||||
Follow the wizard:
|
||||
|
||||
1. **Gateway mode:** Local
|
||||
2. **Auth:** API keys recommended (OAuth can be finicky on headless Pi)
|
||||
3. **Channels:** Telegram is easiest to start with
|
||||
4. **Daemon:** Yes (systemd)
|
||||
|
||||
## 8) Verify Installation
|
||||
|
||||
```bash
|
||||
# Check status
|
||||
openclaw status
|
||||
|
||||
# Check service (standard install = systemd user unit)
|
||||
systemctl --user status openclaw-gateway.service
|
||||
|
||||
# View logs
|
||||
journalctl --user -u openclaw-gateway.service -f
|
||||
```
|
||||
|
||||
## 9) Access the OpenClaw Dashboard
|
||||
|
||||
Replace `user@gateway-host` with your Pi username and hostname or IP address.
|
||||
|
||||
On your computer, ask the Pi to print a fresh dashboard URL:
|
||||
|
||||
```bash
|
||||
ssh user@gateway-host 'openclaw dashboard --no-open'
|
||||
```
|
||||
|
||||
The command prints `Dashboard URL:`. Depending on how `gateway.auth.token`
|
||||
is configured, the URL may be a plain `http://127.0.0.1:18789/` link or one
|
||||
that includes `#token=...`.
|
||||
|
||||
In another terminal on your computer, create the SSH tunnel:
|
||||
|
||||
```bash
|
||||
ssh -N -L 18789:127.0.0.1:18789 user@gateway-host
|
||||
```
|
||||
|
||||
Then open the printed Dashboard URL in your local browser.
|
||||
|
||||
If the UI asks for shared-secret auth, paste the configured token or password
|
||||
into Control UI settings. For token auth, use `gateway.auth.token` (or
|
||||
`OPENCLAW_GATEWAY_TOKEN`).
|
||||
|
||||
For always-on remote access, see [Tailscale](/gateway/tailscale).
|
||||
|
||||
---
|
||||
|
||||
## Performance Optimizations
|
||||
|
||||
### Use a USB SSD (Huge Improvement)
|
||||
|
||||
SD cards are slow and wear out. A USB SSD dramatically improves performance:
|
||||
|
||||
```bash
|
||||
# Check if booting from USB
|
||||
lsblk
|
||||
```
|
||||
|
||||
See [Pi USB boot guide](https://www.raspberrypi.com/documentation/computers/raspberry-pi.html#usb-mass-storage-boot) for setup.
|
||||
|
||||
### Speed up CLI startup (module compile cache)
|
||||
|
||||
On lower-power Pi hosts, enable Node's module compile cache so repeated CLI runs are faster:
|
||||
|
||||
```bash
|
||||
grep -q 'NODE_COMPILE_CACHE=/var/tmp/openclaw-compile-cache' ~/.bashrc || cat >> ~/.bashrc <<'EOF' # pragma: allowlist secret
|
||||
export NODE_COMPILE_CACHE=/var/tmp/openclaw-compile-cache
|
||||
mkdir -p /var/tmp/openclaw-compile-cache
|
||||
export OPENCLAW_NO_RESPAWN=1
|
||||
EOF
|
||||
source ~/.bashrc
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
- `NODE_COMPILE_CACHE` speeds up subsequent runs (`status`, `health`, `--help`).
|
||||
- `/var/tmp` survives reboots better than `/tmp`.
|
||||
- `OPENCLAW_NO_RESPAWN=1` avoids extra startup cost from CLI self-respawn.
|
||||
- First run warms the cache; later runs benefit most.
|
||||
|
||||
### systemd startup tuning (optional)
|
||||
|
||||
If this Pi is mostly running OpenClaw, add a service drop-in to reduce restart
|
||||
jitter and keep startup env stable:
|
||||
|
||||
```bash
|
||||
systemctl --user edit openclaw-gateway.service
|
||||
```
|
||||
|
||||
```ini
|
||||
[Service]
|
||||
Environment=OPENCLAW_NO_RESPAWN=1
|
||||
Environment=NODE_COMPILE_CACHE=/var/tmp/openclaw-compile-cache
|
||||
Restart=always
|
||||
RestartSec=2
|
||||
TimeoutStartSec=90
|
||||
```
|
||||
|
||||
Then apply:
|
||||
|
||||
```bash
|
||||
systemctl --user daemon-reload
|
||||
systemctl --user restart openclaw-gateway.service
|
||||
```
|
||||
|
||||
If possible, keep OpenClaw state/cache on SSD-backed storage to avoid SD-card
|
||||
random-I/O bottlenecks during cold starts.
|
||||
|
||||
If this is a headless Pi, enable lingering once so the user service survives
|
||||
logout:
|
||||
|
||||
```bash
|
||||
sudo loginctl enable-linger "$(whoami)"
|
||||
```
|
||||
|
||||
How `Restart=` policies help automated recovery:
|
||||
[systemd can automate service recovery](https://www.redhat.com/en/blog/systemd-automate-recovery).
|
||||
|
||||
### Reduce Memory Usage
|
||||
|
||||
```bash
|
||||
# Disable GPU memory allocation (headless)
|
||||
echo 'gpu_mem=16' | sudo tee -a /boot/config.txt
|
||||
|
||||
# Disable Bluetooth if not needed
|
||||
sudo systemctl disable bluetooth
|
||||
```
|
||||
|
||||
### Monitor Resources
|
||||
|
||||
```bash
|
||||
# Check memory
|
||||
free -h
|
||||
|
||||
# Check CPU temperature
|
||||
vcgencmd measure_temp
|
||||
|
||||
# Live monitoring
|
||||
htop
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ARM-Specific Notes
|
||||
|
||||
### Binary Compatibility
|
||||
|
||||
Most OpenClaw features work on ARM64, but some external binaries may need ARM builds:
|
||||
|
||||
| Tool | ARM64 Status | Notes |
|
||||
| ------------------ | ------------ | ----------------------------------- |
|
||||
| Node.js | ✅ | Works great |
|
||||
| WhatsApp (Baileys) | ✅ | Pure JS, no issues |
|
||||
| Telegram | ✅ | Pure JS, no issues |
|
||||
| gog (Gmail CLI) | ⚠️ | Check for ARM release |
|
||||
| Chromium (browser) | ✅ | `sudo apt install chromium-browser` |
|
||||
|
||||
If a skill fails, check if its binary has an ARM build. Many Go/Rust tools do; some don't.
|
||||
|
||||
### 32-bit vs 64-bit
|
||||
|
||||
**Always use 64-bit OS.** Node.js and many modern tools require it. Check with:
|
||||
|
||||
```bash
|
||||
uname -m
|
||||
# Should show: aarch64 (64-bit) not armv7l (32-bit)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Recommended Model Setup
|
||||
|
||||
Since the Pi is just the Gateway (models run in the cloud), use API-based models:
|
||||
|
||||
```json
|
||||
{
|
||||
"agents": {
|
||||
"defaults": {
|
||||
"model": {
|
||||
"primary": "anthropic/claude-sonnet-4-6",
|
||||
"fallbacks": ["openai/gpt-5.4-mini"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Don't try to run local LLMs on a Pi** — even small models are too slow. Let Claude/GPT do the heavy lifting.
|
||||
|
||||
---
|
||||
|
||||
## Auto-Start on Boot
|
||||
|
||||
Onboarding sets this up, but to verify:
|
||||
|
||||
```bash
|
||||
# Check service is enabled
|
||||
systemctl --user is-enabled openclaw-gateway.service
|
||||
|
||||
# Enable if not
|
||||
systemctl --user enable openclaw-gateway.service
|
||||
|
||||
# Start on boot
|
||||
systemctl --user start openclaw-gateway.service
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Out of Memory (OOM)
|
||||
|
||||
```bash
|
||||
# Check memory
|
||||
free -h
|
||||
|
||||
# Add more swap (see Step 5)
|
||||
# Or reduce services running on the Pi
|
||||
```
|
||||
|
||||
### Slow Performance
|
||||
|
||||
- Use USB SSD instead of SD card
|
||||
- Disable unused services: `sudo systemctl disable cups bluetooth avahi-daemon`
|
||||
- Check CPU throttling: `vcgencmd get_throttled` (should return `0x0`)
|
||||
|
||||
### Service will not start
|
||||
|
||||
```bash
|
||||
# Check logs
|
||||
journalctl --user -u openclaw-gateway.service --no-pager -n 100
|
||||
|
||||
# Common fix: rebuild
|
||||
cd ~/openclaw # if using hackable install
|
||||
npm run build
|
||||
systemctl --user restart openclaw-gateway.service
|
||||
```
|
||||
|
||||
### ARM Binary Issues
|
||||
|
||||
If a skill fails with "exec format error":
|
||||
|
||||
1. Check if the binary has an ARM64 build
|
||||
2. Try building from source
|
||||
3. Or use a Docker container with ARM support
|
||||
|
||||
### WiFi Drops
|
||||
|
||||
For headless Pis on WiFi:
|
||||
|
||||
```bash
|
||||
# Disable WiFi power management
|
||||
sudo iwconfig wlan0 power off
|
||||
|
||||
# Make permanent
|
||||
echo 'wireless-power off' | sudo tee -a /etc/network/interfaces
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Cost Comparison
|
||||
|
||||
| Setup | One-Time Cost | Monthly Cost | Notes |
|
||||
| -------------- | ------------- | ------------ | ------------------------- |
|
||||
| **Pi 4 (2GB)** | ~$45 | $0 | + power (~$5/yr) |
|
||||
| **Pi 4 (4GB)** | ~$55 | $0 | Recommended |
|
||||
| **Pi 5 (4GB)** | ~$60 | $0 | Best performance |
|
||||
| **Pi 5 (8GB)** | ~$80 | $0 | Overkill but future-proof |
|
||||
| DigitalOcean | $0 | $6/mo | $72/year |
|
||||
| Hetzner | $0 | €3.79/mo | ~$50/year |
|
||||
|
||||
**Break-even:** A Pi pays for itself in ~6-12 months vs cloud VPS.
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [Linux guide](/platforms/linux) — general Linux setup
|
||||
- [DigitalOcean guide](/platforms/digitalocean) — cloud alternative
|
||||
- [Hetzner guide](/install/hetzner) — Docker setup
|
||||
- [Tailscale](/gateway/tailscale) — remote access
|
||||
- [Nodes](/nodes) — pair your laptop/phone with the Pi gateway
|
||||
251
openclaw/docs/platforms/windows.md
Normal file
251
openclaw/docs/platforms/windows.md
Normal file
|
|
@ -0,0 +1,251 @@
|
|||
---
|
||||
summary: "Windows support: native and WSL2 install paths, daemon, and current caveats"
|
||||
read_when:
|
||||
- Installing OpenClaw on Windows
|
||||
- Choosing between native Windows and WSL2
|
||||
- Looking for Windows companion app status
|
||||
title: "Windows"
|
||||
---
|
||||
|
||||
# Windows
|
||||
|
||||
OpenClaw supports both **native Windows** and **WSL2**. WSL2 is the more
|
||||
stable path and recommended for the full experience — the CLI, Gateway, and
|
||||
tooling run inside Linux with full compatibility. Native Windows works for
|
||||
core CLI and Gateway use, with some caveats noted below.
|
||||
|
||||
Native Windows companion apps are planned.
|
||||
|
||||
## WSL2 (recommended)
|
||||
|
||||
- [Getting Started](/start/getting-started) (use inside WSL)
|
||||
- [Install & updates](/install/updating)
|
||||
- Official WSL2 guide (Microsoft): [https://learn.microsoft.com/windows/wsl/install](https://learn.microsoft.com/windows/wsl/install)
|
||||
|
||||
## Native Windows status
|
||||
|
||||
Native Windows CLI flows are improving, but WSL2 is still the recommended path.
|
||||
|
||||
What works well on native Windows today:
|
||||
|
||||
- website installer via `install.ps1`
|
||||
- local CLI use such as `openclaw --version`, `openclaw doctor`, and `openclaw plugins list --json`
|
||||
- embedded local-agent/provider smoke such as:
|
||||
|
||||
```powershell
|
||||
openclaw agent --local --agent main --thinking low -m "Reply with exactly WINDOWS-HATCH-OK."
|
||||
```
|
||||
|
||||
Current caveats:
|
||||
|
||||
- `openclaw onboard --non-interactive` still expects a reachable local gateway unless you pass `--skip-health`
|
||||
- `openclaw onboard --non-interactive --install-daemon` and `openclaw gateway install` try Windows Scheduled Tasks first
|
||||
- if Scheduled Task creation is denied, OpenClaw falls back to a per-user Startup-folder login item and starts the gateway immediately
|
||||
- if `schtasks` itself wedges or stops responding, OpenClaw now aborts that path quickly and falls back instead of hanging forever
|
||||
- Scheduled Tasks are still preferred when available because they provide better supervisor status
|
||||
|
||||
If you want the native CLI only, without gateway service install, use one of these:
|
||||
|
||||
```powershell
|
||||
openclaw onboard --non-interactive --skip-health
|
||||
openclaw gateway run
|
||||
```
|
||||
|
||||
If you do want managed startup on native Windows:
|
||||
|
||||
```powershell
|
||||
openclaw gateway install
|
||||
openclaw gateway status --json
|
||||
```
|
||||
|
||||
If Scheduled Task creation is blocked, the fallback service mode still auto-starts after login through the current user's Startup folder.
|
||||
|
||||
## Gateway
|
||||
|
||||
- [Gateway runbook](/gateway)
|
||||
- [Configuration](/gateway/configuration)
|
||||
|
||||
## Gateway service install (CLI)
|
||||
|
||||
Inside WSL2:
|
||||
|
||||
```
|
||||
openclaw onboard --install-daemon
|
||||
```
|
||||
|
||||
Or:
|
||||
|
||||
```
|
||||
openclaw gateway install
|
||||
```
|
||||
|
||||
Or:
|
||||
|
||||
```
|
||||
openclaw configure
|
||||
```
|
||||
|
||||
Select **Gateway service** when prompted.
|
||||
|
||||
Repair/migrate:
|
||||
|
||||
```
|
||||
openclaw doctor
|
||||
```
|
||||
|
||||
## Gateway auto-start before Windows login
|
||||
|
||||
For headless setups, ensure the full boot chain runs even when no one logs into
|
||||
Windows.
|
||||
|
||||
### 1) Keep user services running without login
|
||||
|
||||
Inside WSL:
|
||||
|
||||
```bash
|
||||
sudo loginctl enable-linger "$(whoami)"
|
||||
```
|
||||
|
||||
### 2) Install the OpenClaw gateway user service
|
||||
|
||||
Inside WSL:
|
||||
|
||||
```bash
|
||||
openclaw gateway install
|
||||
```
|
||||
|
||||
### 3) Start WSL automatically at Windows boot
|
||||
|
||||
In PowerShell as Administrator:
|
||||
|
||||
```powershell
|
||||
schtasks /create /tn "WSL Boot" /tr "wsl.exe -d Ubuntu --exec /bin/true" /sc onstart /ru SYSTEM
|
||||
```
|
||||
|
||||
Replace `Ubuntu` with your distro name from:
|
||||
|
||||
```powershell
|
||||
wsl --list --verbose
|
||||
```
|
||||
|
||||
### Verify startup chain
|
||||
|
||||
After a reboot (before Windows sign-in), check from WSL:
|
||||
|
||||
```bash
|
||||
systemctl --user is-enabled openclaw-gateway.service
|
||||
systemctl --user status openclaw-gateway.service --no-pager
|
||||
```
|
||||
|
||||
## Advanced: expose WSL services over LAN (portproxy)
|
||||
|
||||
WSL has its own virtual network. If another machine needs to reach a service
|
||||
running **inside WSL** (SSH, a local TTS server, or the Gateway), you must
|
||||
forward a Windows port to the current WSL IP. The WSL IP changes after restarts,
|
||||
so you may need to refresh the forwarding rule.
|
||||
|
||||
Example (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
|
||||
```
|
||||
|
||||
Allow the port through Windows Firewall (one-time):
|
||||
|
||||
```powershell
|
||||
New-NetFirewallRule -DisplayName "WSL SSH $ListenPort" -Direction Inbound `
|
||||
-Protocol TCP -LocalPort $ListenPort -Action Allow
|
||||
```
|
||||
|
||||
Refresh the portproxy after WSL restarts:
|
||||
|
||||
```powershell
|
||||
netsh interface portproxy delete v4tov4 listenport=$ListenPort listenaddress=0.0.0.0 | Out-Null
|
||||
netsh interface portproxy add v4tov4 listenport=$ListenPort listenaddress=0.0.0.0 `
|
||||
connectaddress=$WslIp connectport=$TargetPort | Out-Null
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
- SSH from another machine targets the **Windows host IP** (example: `ssh user@windows-host -p 2222`).
|
||||
- Remote nodes must point at a **reachable** Gateway URL (not `127.0.0.1`); use
|
||||
`openclaw status --all` to confirm.
|
||||
- Use `listenaddress=0.0.0.0` for LAN access; `127.0.0.1` keeps it local only.
|
||||
- If you want this automatic, register a Scheduled Task to run the refresh
|
||||
step at login.
|
||||
|
||||
## Step-by-step WSL2 install
|
||||
|
||||
### 1) Install WSL2 + Ubuntu
|
||||
|
||||
Open PowerShell (Admin):
|
||||
|
||||
```powershell
|
||||
wsl --install
|
||||
# Or pick a distro explicitly:
|
||||
wsl --list --online
|
||||
wsl --install -d Ubuntu-24.04
|
||||
```
|
||||
|
||||
Reboot if Windows asks.
|
||||
|
||||
### 2) Enable systemd (required for gateway install)
|
||||
|
||||
In your WSL terminal:
|
||||
|
||||
```bash
|
||||
sudo tee /etc/wsl.conf >/dev/null <<'EOF'
|
||||
[boot]
|
||||
systemd=true
|
||||
EOF
|
||||
```
|
||||
|
||||
Then from PowerShell:
|
||||
|
||||
```powershell
|
||||
wsl --shutdown
|
||||
```
|
||||
|
||||
Re-open Ubuntu, then verify:
|
||||
|
||||
```bash
|
||||
systemctl --user status
|
||||
```
|
||||
|
||||
### 3) Install OpenClaw (inside WSL)
|
||||
|
||||
For a normal first-time setup inside WSL, follow the Linux Getting Started flow:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/openclaw/openclaw.git
|
||||
cd openclaw
|
||||
pnpm install
|
||||
pnpm build
|
||||
pnpm ui:build
|
||||
pnpm openclaw onboard --install-daemon
|
||||
```
|
||||
|
||||
If you are developing from source instead of doing first-time onboarding, use the
|
||||
source dev loop from [Setup](/start/setup):
|
||||
|
||||
```bash
|
||||
pnpm install
|
||||
# First run only (or after resetting local OpenClaw config/workspace)
|
||||
pnpm openclaw setup
|
||||
pnpm gateway:watch
|
||||
```
|
||||
|
||||
Full guide: [Getting Started](/start/getting-started)
|
||||
|
||||
## Windows companion app
|
||||
|
||||
We do not have a Windows companion app yet. Contributions are welcome if you want
|
||||
contributions to make it happen.
|
||||
Loading…
Add table
Add a link
Reference in a new issue