← Back to Plugins
Channels

Msg9

ice5kysl By ice5kysl 👁 69 views ▲ 0 votes

OpenClaw channel plugin for msg9.io — turns a msg9 agent inbox into an OpenClaw channel (poll-based inbound, reply_to auto-close, multi-account)

GitHub

Install

npm install
npm

README

# openclaw-msg9

OpenClaw channel plugin for [msg9.io](https://msg9.io) agent inboxes. Turns a
msg9 agent inbox into an OpenClaw channel: inbound mail drives the agent,
agent replies are delivered back on the original msg9 thread.

- **Channel id:** `msg9` (package / plugin id: `openclaw-msg9`)
- **Transport:** poll-based inbound (`agent.unread()`), direct outbound (`agent.send()`)
- **Content:** text only — msg9 has no attachments, so no `sendMedia`

## Configuration

Single account (`channels.msg9`):

```json5
{
  channels: {
    msg9: {
      enabled: true,
      address: "[email protected]",   // your agent inbox address
      apiKey: "msg9_sk_...",                    // the inbox's agent key — a secret
      // baseUrl: "https://api.msg9.io",        // optional, this is the default
      pollIntervalMs: 3000,                     // inbox poll interval (default 3000)
      allowFrom: ["[email protected]"],  // optional sender whitelist
    },
  },
}
```

Multi-account:

```json5
{
  channels: {
    msg9: {
      enabled: true,
      address: "[email protected]",
      apiKey: "msg9_sk_...",
      accounts: {
        side: {
          address: "[email protected]",
          apiKey: "msg9_sk_...",
          pollIntervalMs: 1000,
        },
      },
    },
  },
}
```

Or via environment variables (default account only):
`MSG9_ADDRESS`, `MSG9_API_KEY`, `MSG9_BASE_URL`.

CLI-style setup tokens follow the same shape as other channels:
`--token "address,apiKey[,baseUrl]"`, or `--use-env` to read the env vars above.

## Addressing

Outbound targets use the `msg9:` prefix:

```
msg9:[email protected]
```

`normalizeTarget` strips the prefix; a bare address containing `@` is also
recognized as a msg9 id (`<user>@<pod>.msg9.io`).

## Inbound semantics

Each poll sweep drains the inbox's unread folder:

1. **Dedupe** by `message_id` (bounded in-memory set, reset at 1000 entries).
   The platform is at-least-once; the set covers a gateway run.
2. **allowFrom** — when the list is non-empty and the sender is not listed,
   the message is `markRead` and skipped: it never reaches the agent, but
   stays in the owner's unprocessed folder for manual review. Empty allowFrom
   allows everyone — configure it before enabling text commands.
3. **markProcessed** — allowed messages are marked processed (implies read,
   idempotent) *before* dispatch, so the agent is driven at-most-once even if
   dispatch or delivery fails.
4. **Dispatch** — the standard channel pipeline
   (`activity.record` → `resolveAgentRoute` → envelope → `finalizeInboundContext`
   → `dispatchReplyWithBufferedBlockDispatcher`). The `deliver` callback sends
   via `agent.send({ ..., reply_to: <original message_id> })`.

### reply_to auto-close

Sending a reply with `reply_to` set makes the server close the original
message automatically. Combined with the mark-processed-first policy, replied
threads are closed exactly once, and messages that never get a reply remain
visible in the unprocessed folder.

## Outbound

`outbound.sendText` resolves the account, strips the `msg9:` prefix, and calls
`agent.send`. A core-provided `replyToId` (the inbound `MessageSid`) is mapped
to msg9 `reply_to`. Returns `{ channel: "msg9", messageId }`. There is no
`sendMedia` — msg9 carries text bodies only.

## Credential hygiene

`apiKey` is a `msg9_sk_...` agent key: anyone holding it can read and send as
that inbox. Keep it in OpenClaw's config/secret stores with restricted file
permissions (0600), never in a repo or a chat log. If a key leaks in
plaintext, rotate it at the platform before reusing the inbox.

## Requirements

- Node.js >= 18
- OpenClaw plugin API `>=2026.3.23-1` (built against SDK `2026.4.26`)

## Development

```sh
npm install
npm run build        # tsc → dist/
npm test             # vitest (no network — the client factory is mocked)
npm run pack:dry-run
```

## Roadmap

- **Webhook inbound** — replace polling with tenant-key webhook subscriptions
  (`registerHttpRoute` + signature verification) once a webhook is provisioned.
- **Inbox tools** — expose `list_inbox` / `mark_processed` style tools to the
  agent via `registerTool`.
- **ClawHub publish** — publishing pipeline (`openclaw.plugin.json` already
  carries the required `compat`/`build` metadata; schema is fully flat, no
  `$ref`/`$defs`).
channels

Comments

Sign in to leave a comment

Loading comments...