openclaw/docs/platforms/ios.md
Chris Herold 6e33f3f0f3
iOS: implement dual-connection gateway architecture with deadlock fix
Aligns the iOS app with the Clawnet refactor by implementing proper role
separation for gateway connections. Uses separate operator and node sessions
to match the gateway's authorization requirements.

Changes:
- New GatewayOperatorSession: Wraps GatewayChannelActor for operator-role
  RPC requests (chat.*, health, sessions.list) without invoke handling
- Dual-connection architecture: Operator session for requests, node session
  for node.event calls (e.g., chat.subscribe)
- Separate websocket sessions: Each connection gets its own URLSession to
  prevent response cross-talk
- Updated chat transport: IOSGatewayChatTransport uses operator session for
  requests, node session for subscriptions

ClawdbotKit (shared):
- Deadlock fix in GatewayChannel.swift: Moved connection finalization
  (listen(), connected=true, isConnecting=false, waiter resumption) to occur
  before calling pushHandler. This fixes a latent bug where requests made
  from onConnected callbacks would deadlock. Does not affect macOS (its
  callback doesn't make requests).
- Package.swift: Fixed argument order for Swift 6.2 compatibility

iOS chat is now working. This is the base PR to unlock further work on
the iOS app.
2026-01-27 14:46:27 -08:00

4.5 KiB

summary read_when
iOS node app: connect to the Gateway, pairing, canvas, and troubleshooting
Pairing or reconnecting the iOS node
Running the iOS app from source
Debugging gateway discovery or canvas commands

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 (moltbot.internal.), or
    • Manual host/port (fallback).

Quick start (authenticate + pair + connect)

1. Start the Gateway

moltbot gateway --port 18789

2. Configure authentication in the iOS app

Open Settings in the Moltbot iOS app and configure one of the following:

  • Gateway Token: Paste the token from moltbot config get gateway.token (recommended for secure setups)
  • Gateway Password: Enter the password from moltbot config get gateway.password (simpler alternative)

If neither is set on the gateway, you can set one:

moltbot config set gateway.token "your-secret-token"
# or
moltbot config set gateway.password "your-password"

3. Select the gateway

In the iOS app Settings, pick a discovered gateway from the list, or enable Manual Host and enter the host/port manually.

4. Approve the pairing request

The iOS app requires device pairing for both operator and node roles. On the gateway host, list and approve pending devices:

moltbot devices list
moltbot devices approve <id>

You may need to approve twice (once for operator role, once for node role).

Note

: Use moltbot devices for WebSocket pairing, not moltbot nodes pending/approve.

5. Verify connection

moltbot nodes status
moltbot gateway call node.list --params "{}"

Discovery paths

Bonjour (LAN)

The Gateway advertises _moltbot._tcp on local.. The iOS app lists these automatically.

Tailnet (cross-network)

If mDNS is blocked, use a unicast DNS-SD zone (recommended domain: moltbot.internal.) and Tailscale split DNS. See 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:

moltbot nodes invoke --node "iOS Node" --command canvas.navigate --params '{"url":"http://<gateway-host>:18793/__moltbot__/canvas/"}'

Notes:

  • The Gateway canvas host serves /__moltbot__/canvas/ and /__moltbot__/a2ui/.
  • 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

moltbot nodes invoke --node "iOS Node" --command canvas.eval --params '{"javaScript":"(() => { const {ctx} = window.__moltbot; 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\"; })()"}'
moltbot 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.
  • Authentication failed: Ensure the gateway token or password in iOS Settings matches what is configured on the gateway (moltbot config get gateway.token or gateway.password).
  • Pairing prompt never appears: Run moltbot devices list to see pending requests and approve with moltbot devices approve <id>.
  • Reconnect fails after reinstall: The Keychain pairing token was cleared; re-pair the device using moltbot devices list and moltbot devices approve.