Channels / Mainstream messaging

iMessage

For new iMessage deployments, use BlueBubbles.

The imsg integration is legacy and may be removed in a future release.

Status: legacy external CLI integration. Gateway spawns imsg rpc and communicates over JSON-RPC on stdio (no separate daemon/port).

Quick setup

Local Mac (fast path)

Install and verify imsg

brew install steipete/tap/imsg
imsg rpc --help

Configure Genesis

{
  channels: {
    imessage: {
      enabled: true,
      cliPath: "/usr/local/bin/imsg",
      dbPath: "/Users/user/Library/Messages/chat.db",
    },
  },
}

Start gateway

genesis gateway

Approve first DM pairing (default dmPolicy)

genesis pairing list imessage
genesis pairing approve imessage <CODE>

Pairing requests expire after 1 hour.

Remote Mac over SSH

Genesis only requires a stdio-compatible cliPath, so you can point cliPath at a wrapper script that SSHes to a remote Mac and runs imsg.

#!/usr/bin/env bash
exec ssh -T gateway-host imsg "$@"

Recommended config when attachments are enabled:

{
  channels: {
    imessage: {
      enabled: true,
      cliPath: "~/.genesis/scripts/imsg-ssh",
      remoteHost: "user@gateway-host", // used for SCP attachment fetches
      includeAttachments: true,
      // Optional: override allowed attachment roots.
      // Defaults include /Users/*/Library/Messages/Attachments
      attachmentRoots: ["/Users/*/Library/Messages/Attachments"],
      remoteAttachmentRoots: ["/Users/*/Library/Messages/Attachments"],
    },
  },
}

If remoteHost is not set, Genesis attempts to auto-detect it by parsing the SSH wrapper script. remoteHost must be host or user@host (no spaces or SSH options). Genesis uses strict host-key checking for SCP, so the relay host key must already exist in ~/.ssh/known_hosts. Attachment paths are validated against allowed roots (attachmentRoots / remoteAttachmentRoots).

Requirements and permissions (macOS)

  • Messages must be signed in on the Mac running imsg.
  • Full Disk Access is required for the process context running Genesis/imsg (Messages DB access).
  • Automation permission is required to send messages through Messages.app.

Permissions are granted per process context. If gateway runs headless (LaunchAgent/SSH), run a one-time interactive command in that same context to trigger prompts:

imsg chats --limit 1
# or
imsg send <handle> "test"

Access control and routing

DM policy

channels.imessage.dmPolicy controls direct messages:

  • pairing (default)
  • allowlist
  • open (requires allowFrom to include "*")
  • disabled

Allowlist field: channels.imessage.allowFrom.

Allowlist entries can be handles or chat targets (chat_id:*, chat_guid:*, chat_identifier:*).

Group policy + mentions

channels.imessage.groupPolicy controls group handling:

  • allowlist (default when configured)
  • open
  • disabled

Group sender allowlist: channels.imessage.groupAllowFrom.

Runtime fallback: if groupAllowFrom is unset, iMessage group sender checks fall back to allowFrom when available. Runtime note: if channels.imessage is completely missing, runtime falls back to groupPolicy="allowlist" and logs a warning (even if channels.defaults.groupPolicy is set).

Mention gating for groups:

  • iMessage has no native mention metadata
  • mention detection uses regex patterns (agents.list[].groupChat.mentionPatterns, fallback messages.groupChat.mentionPatterns)
  • with no configured patterns, mention gating cannot be enforced

Control commands from authorized senders can bypass mention gating in groups.

Sessions and deterministic replies

  • DMs use direct routing; groups use group routing.
  • With default session.dmScope=main, iMessage DMs collapse into the agent main session.
  • Group sessions are isolated (agent:<agentId>:imessage:group:<chat_id>).
  • Replies route back to iMessage using originating channel/target metadata.

Group-ish thread behavior:

Some multi-participant iMessage threads can arrive with is_group=false. If that chat_id is explicitly configured under channels.imessage.groups, Genesis treats it as group traffic (group gating + group session isolation).

ACP conversation bindings

Legacy iMessage chats can also be bound to ACP sessions.

Fast operator flow:

  • Run /acp spawn codex --bind here inside the DM or allowed group chat.
  • Future messages in that same iMessage conversation route to the spawned ACP session.
  • /new and /reset reset the same bound ACP session in place.
  • /acp close closes the ACP session and removes the binding.

Configured persistent bindings are supported through top-level bindings[] entries with type: "acp" and match.channel: "imessage".

match.peer.id can use:

  • normalized DM handle such as +15555550123 or user@example.com
  • chat_id:<id> (recommended for stable group bindings)
  • chat_guid:<guid>
  • chat_identifier:<identifier>

Example:

{
  agents: {
    list: [
      {
        id: "codex",
        runtime: {
          type: "acp",
          acp: { agent: "codex", backend: "acpx", mode: "persistent" },
        },
      },
    ],
  },
  bindings: [
    {
      type: "acp",
      agentId: "codex",
      match: {
        channel: "imessage",
        accountId: "default",
        peer: { kind: "group", id: "chat_id:123" },
      },
      acp: { label: "codex-group" },
    },
  ],
}

See ACP Agents for shared ACP binding behavior.

Deployment patterns

Dedicated bot macOS user (separate iMessage identity)

Use a dedicated Apple ID and macOS user so bot traffic is isolated from your personal Messages profile.

Typical flow:

  1. Create/sign in a dedicated macOS user.
  2. Sign into Messages with the bot Apple ID in that user.
  3. Install imsg in that user.
  4. Create SSH wrapper so Genesis can run imsg in that user context.
  5. Point channels.imessage.accounts.<id>.cliPath and .dbPath to that user profile.

First run may require GUI approvals (Automation + Full Disk Access) in that bot user session.

Remote Mac over Tailscale (example)

Common topology:

  • gateway runs on Linux/VM
  • iMessage + imsg runs on a Mac in your tailnet
  • cliPath wrapper uses SSH to run imsg
  • remoteHost enables SCP attachment fetches

Example:

{
  channels: {
    imessage: {
      enabled: true,
      cliPath: "~/.genesis/scripts/imsg-ssh",
      remoteHost: "bot@mac-mini.tailnet-1234.ts.net",
      includeAttachments: true,
      dbPath: "/Users/bot/Library/Messages/chat.db",
    },
  },
}
#!/usr/bin/env bash
exec ssh -T bot@mac-mini.tailnet-1234.ts.net imsg "$@"

Use SSH keys so both SSH and SCP are non-interactive. Ensure the host key is trusted first (for example ssh bot@mac-mini.tailnet-1234.ts.net) so known_hosts is populated.

Multi-account pattern

iMessage supports per-account config under channels.imessage.accounts.

Each account can override fields such as cliPath, dbPath, allowFrom, groupPolicy, mediaMaxMb, history settings, and attachment root allowlists.

Media, chunking, and delivery targets

Attachments and media

  • inbound attachment ingestion is optional: channels.imessage.includeAttachments
  • remote attachment paths can be fetched via SCP when remoteHost is set
  • attachment paths must match allowed roots:
    • channels.imessage.attachmentRoots (local)
    • channels.imessage.remoteAttachmentRoots (remote SCP mode)
    • default root pattern: /Users/*/Library/Messages/Attachments
  • SCP uses strict host-key checking (StrictHostKeyChecking=yes)
  • outbound media size uses channels.imessage.mediaMaxMb (default 16 MB)

Outbound chunking

  • text chunk limit: channels.imessage.textChunkLimit (default 4000)
  • chunk mode: channels.imessage.chunkMode
    • length (default)
    • newline (paragraph-first splitting)

Addressing formats

Preferred explicit targets:

  • chat_id:123 (recommended for stable routing)
  • chat_guid:...
  • chat_identifier:...

Handle targets are also supported:

  • imessage:+1555...
  • sms:+1555...
  • user@example.com
imsg chats --limit 20

Config writes

iMessage allows channel-initiated config writes by default (for /config set|unset when commands.config: true).

Disable:

{
  channels: {
    imessage: {
      configWrites: false,
    },
  },
}

Troubleshooting

imsg not found or RPC unsupported

Validate the binary and RPC support:

imsg rpc --help
genesis channels status --probe

If probe reports RPC unsupported, update imsg.

DMs are ignored

Check:

  • channels.imessage.dmPolicy
  • channels.imessage.allowFrom
  • pairing approvals (genesis pairing list imessage)

Group messages are ignored

Check:

  • channels.imessage.groupPolicy
  • channels.imessage.groupAllowFrom
  • channels.imessage.groups allowlist behavior
  • mention pattern configuration (agents.list[].groupChat.mentionPatterns)

Remote attachments fail

Check:

  • channels.imessage.remoteHost
  • channels.imessage.remoteAttachmentRoots
  • SSH/SCP key auth from the gateway host
  • host key exists in ~/.ssh/known_hosts on the gateway host
  • remote path readability on the Mac running Messages

macOS permission prompts were missed

Re-run in an interactive GUI terminal in the same user/session context and approve prompts:

imsg chats --limit 1
imsg send <handle> "test"

Confirm Full Disk Access + Automation are granted for the process context that runs Genesis/imsg.

Configuration reference pointers