{
  "title": "ssh <bot-macos-user>@localhost true",
  "content": "exec /usr/bin/ssh -o BatchMode=yes -o ConnectTimeout=5 -T <bot-macos-user>@localhost \\\n  \"/usr/local/bin/imsg\" \"$@\"\njson5 theme={null}\n{\n  channels: {\n    imessage: {\n      enabled: true,\n      accounts: {\n        bot: {\n          name: \"Bot\",\n          enabled: true,\n          cliPath: \"/path/to/imsg-bot\",\n          dbPath: \"/Users/<bot-macos-user>/Library/Messages/chat.db\",\n        },\n      },\n    },\n  },\n}\nbash theme={null}\n#!/usr/bin/env bash\nexec ssh -T gateway-host imsg \"$@\"\njson5 theme={null}\n{\n  channels: {\n    imessage: {\n      cliPath: \"~/imsg-ssh\", // SSH wrapper to remote Mac\n      remoteHost: \"user@gateway-host\", // for SCP file transfer\n      includeAttachments: true,\n    },\n  },\n}\n\n┌──────────────────────────────┐          SSH (imsg rpc)          ┌──────────────────────────┐\n│ Gateway host (Linux/VM)      │──────────────────────────────────▶│ Mac with Messages + imsg │\n│ - openclaw gateway           │          SCP (attachments)        │ - Messages signed in     │\n│ - channels.imessage.cliPath  │◀──────────────────────────────────│ - Remote Login enabled   │\n└──────────────────────────────┘                                   └──────────────────────────┘\n              ▲\n              │ Tailscale tailnet (hostname or 100.x.y.z)\n              ▼\n        user@gateway-host\njson5 theme={null}\n{\n  channels: {\n    imessage: {\n      enabled: true,\n      cliPath: \"~/.openclaw/scripts/imsg-ssh\",\n      remoteHost: \"bot@mac-mini.tailnet-1234.ts.net\",\n      includeAttachments: true,\n      dbPath: \"/Users/bot/Library/Messages/chat.db\",\n    },\n  },\n}\nbash theme={null}\n#!/usr/bin/env bash\nexec ssh -T bot@mac-mini.tailnet-1234.ts.net imsg \"$@\"\njson5 theme={null}\n{\n  channels: {\n    imessage: {\n      groupPolicy: \"allowlist\",\n      groupAllowFrom: [\"+15555550123\"],\n      groups: {\n        \"42\": { requireMention: false },\n      },\n    },\n  },\n}\n\nimsg chats --limit 20\n```\n\n## Configuration reference (iMessage)\n\nFull configuration: [Configuration](/gateway/configuration)\n\n* `channels.imessage.enabled`: enable/disable channel startup.\n* `channels.imessage.cliPath`: path to `imsg`.\n* `channels.imessage.dbPath`: Messages DB path.\n* `channels.imessage.remoteHost`: SSH host for SCP attachment transfer when `cliPath` points to a remote Mac (e.g., `user@gateway-host`). Auto-detected from SSH wrapper if not set.\n* `channels.imessage.service`: `imessage | sms | auto`.\n* `channels.imessage.region`: SMS region.\n* `channels.imessage.dmPolicy`: `pairing | allowlist | open | disabled` (default: pairing).\n* `channels.imessage.allowFrom`: DM allowlist (handles, emails, E.164 numbers, or `chat_id:*`). `open` requires `\"*\"`. iMessage has no usernames; use handles or chat targets.\n* `channels.imessage.groupPolicy`: `open | allowlist | disabled` (default: allowlist).\n* `channels.imessage.groupAllowFrom`: group sender allowlist.\n* `channels.imessage.historyLimit` / `channels.imessage.accounts.*.historyLimit`: max group messages to include as context (0 disables).\n* `channels.imessage.dmHistoryLimit`: DM history limit in user turns. Per-user overrides: `channels.imessage.dms[\"<handle>\"].historyLimit`.\n* `channels.imessage.groups`: per-group defaults + allowlist (use `\"*\"` for global defaults).\n* `channels.imessage.includeAttachments`: ingest attachments into context.\n* `channels.imessage.mediaMaxMb`: inbound/outbound media cap (MB).\n* `channels.imessage.textChunkLimit`: outbound chunk size (chars).\n* `channels.imessage.chunkMode`: `length` (default) or `newline` to split on blank lines (paragraph boundaries) before length chunking.\n\nRelated global options:\n\n* `agents.list[].groupChat.mentionPatterns` (or `messages.groupChat.mentionPatterns`).\n* `messages.responsePrefix`.",
  "code_samples": [
    {
      "code": "Example config:",
      "language": "unknown"
    },
    {
      "code": "For single-account setups, use flat options (`channels.imessage.cliPath`, `channels.imessage.dbPath`) instead of the `accounts` map.\n\n### Remote/SSH variant (optional)\n\nIf you want iMessage on another Mac, set `channels.imessage.cliPath` to a wrapper that runs `imsg` on the remote macOS host over SSH. OpenClaw only needs stdio.\n\nExample wrapper:",
      "language": "unknown"
    },
    {
      "code": "**Remote attachments:** When `cliPath` points to a remote host via SSH, attachment paths in the Messages database reference files on the remote machine. OpenClaw can automatically fetch these over SCP by setting `channels.imessage.remoteHost`:",
      "language": "unknown"
    },
    {
      "code": "If `remoteHost` is not set, OpenClaw attempts to auto-detect it by parsing the SSH command in your wrapper script. Explicit configuration is recommended for reliability.\n\n#### Remote Mac via Tailscale (example)\n\nIf the Gateway runs on a Linux host/VM but iMessage must run on a Mac, Tailscale is the simplest bridge: the Gateway talks to the Mac over the tailnet, runs `imsg` via SSH, and SCPs attachments back.\n\nArchitecture:",
      "language": "unknown"
    },
    {
      "code": "Concrete config example (Tailscale hostname):",
      "language": "unknown"
    },
    {
      "code": "Example wrapper (`~/.openclaw/scripts/imsg-ssh`):",
      "language": "unknown"
    },
    {
      "code": "Notes:\n\n* Ensure the Mac is signed in to Messages, and Remote Login is enabled.\n* Use SSH keys so `ssh bot@mac-mini.tailnet-1234.ts.net` works without prompts.\n* `remoteHost` should match the SSH target so SCP can fetch attachments.\n\nMulti-account support: use `channels.imessage.accounts` with per-account config and optional `name`. See [`gateway/configuration`](/gateway/configuration#telegramaccounts--discordaccounts--slackaccounts--signalaccounts--imessageaccounts) for the shared pattern. Don't commit `~/.openclaw/openclaw.json` (it often contains tokens).\n\n## Access control (DMs + groups)\n\nDMs:\n\n* Default: `channels.imessage.dmPolicy = \"pairing\"`.\n* Unknown senders receive a pairing code; messages are ignored until approved (codes expire after 1 hour).\n* Approve via:\n  * `openclaw pairing list imessage`\n  * `openclaw pairing approve imessage <CODE>`\n* Pairing is the default token exchange for iMessage DMs. Details: [Pairing](/start/pairing)\n\nGroups:\n\n* `channels.imessage.groupPolicy = open | allowlist | disabled`.\n* `channels.imessage.groupAllowFrom` controls who can trigger in groups when `allowlist` is set.\n* Mention gating uses `agents.list[].groupChat.mentionPatterns` (or `messages.groupChat.mentionPatterns`) because iMessage has no native mention metadata.\n* Multi-agent override: set per-agent patterns on `agents.list[].groupChat.mentionPatterns`.\n\n## How it works (behavior)\n\n* `imsg` streams message events; the gateway normalizes them into the shared channel envelope.\n* Replies always route back to the same chat id or handle.\n\n## Group-ish threads (`is_group=false`)\n\nSome iMessage threads can have multiple participants but still arrive with `is_group=false` depending on how Messages stores the chat identifier.\n\nIf you explicitly configure a `chat_id` under `channels.imessage.groups`, OpenClaw treats that thread as a “group” for:\n\n* session isolation (separate `agent:<agentId>:imessage:group:<chat_id>` session key)\n* group allowlisting / mention gating behavior\n\nExample:",
      "language": "unknown"
    },
    {
      "code": "This is useful when you want an isolated personality/model for a specific thread (see [Multi-agent routing](/concepts/multi-agent)). For filesystem isolation, see [Sandboxing](/gateway/sandboxing).\n\n## Media + limits\n\n* Optional attachment ingestion via `channels.imessage.includeAttachments`.\n* Media cap via `channels.imessage.mediaMaxMb`.\n\n## Limits\n\n* Outbound text is chunked to `channels.imessage.textChunkLimit` (default 4000).\n* Optional newline chunking: set `channels.imessage.chunkMode=\"newline\"` to split on blank lines (paragraph boundaries) before length chunking.\n* Media uploads are capped by `channels.imessage.mediaMaxMb` (default 16).\n\n## Addressing / delivery targets\n\nPrefer `chat_id` for stable routing:\n\n* `chat_id:123` (preferred)\n* `chat_guid:...`\n* `chat_identifier:...`\n* direct handles: `imessage:+1555` / `sms:+1555` / `user@example.com`\n\nList chats:",
      "language": "unknown"
    }
  ],
  "headings": [
    {
      "level": "h3",
      "text": "Remote/SSH variant (optional)",
      "id": "remote/ssh-variant-(optional)"
    },
    {
      "level": "h2",
      "text": "Access control (DMs + groups)",
      "id": "access-control-(dms-+-groups)"
    },
    {
      "level": "h2",
      "text": "How it works (behavior)",
      "id": "how-it-works-(behavior)"
    },
    {
      "level": "h2",
      "text": "Group-ish threads (`is_group=false`)",
      "id": "group-ish-threads-(`is_group=false`)"
    },
    {
      "level": "h2",
      "text": "Media + limits",
      "id": "media-+-limits"
    },
    {
      "level": "h2",
      "text": "Limits",
      "id": "limits"
    },
    {
      "level": "h2",
      "text": "Addressing / delivery targets",
      "id": "addressing-/-delivery-targets"
    },
    {
      "level": "h2",
      "text": "Configuration reference (iMessage)",
      "id": "configuration-reference-(imessage)"
    }
  ],
  "url": "llms-txt#ssh-<bot-macos-user>@localhost-true",
  "links": []
}