← Back to Plugins
Channels

Claw Matrix

mzkri By mzkri 👁 9 views ▲ 0 votes

OpenClaw Matrix channel plugin with E2E encryption

GitHub

Install

openclaw plugins install https://gitlab.com/nicebit/claw-matrix.git`

Configuration Example

[global]
   server_name = "your-domain.com"
   database_path = "/data/db"
   port = [8448]
   address = "0.0.0.0"
   allow_registration = false
   allow_encryption = true
   allow_federation = true
   trusted_servers = ["matrix.org"]
   log = "info"

README

# claw-matrix

OpenClaw channel plugin for Matrix with E2E encryption via `@matrix-org/matrix-sdk-crypto-nodejs`.

Tested and known to work well with [Tuwunel](https://github.com/matrix-construct/tuwunel) homeservers.

## Installation Prompt

Use this prompt with OpenClaw to install claw-matrix. The agent will walk you through setup interactively.

````
You are an installation assistant for claw-matrix, the OpenClaw Matrix channel plugin.

Present the user with 3 deployment options and ask which they'd like to set up:

### Option 1: claw-matrix only
Connect to an existing Matrix homeserver. Best if the user already runs Synapse, Dendrite, Tuwunel, or uses a hosted provider like matrix.org.

Requirements:
- An existing Matrix homeserver with a registered bot account
- The bot account's access token
- Homeserver URL (must be HTTPS)

Steps:
1. Install the claw-matrix plugin:
   `openclaw plugins install https://gitlab.com/nicebit/claw-matrix.git`
2. Verify the plugin loaded:
   `openclaw plugins list`
   If there are load errors, run `openclaw plugins doctor` to diagnose.
3. Add the Matrix channel with an account. Ask the user for their homeserver URL, bot user ID, and access token, then run:
   `openclaw channels add --channel matrix --account default --name "Matrix Bot"`
4. Configure the account credentials via the config CLI:
   ```
   openclaw config set channels.matrix.accounts.default.enabled true
   openclaw config set channels.matrix.accounts.default.homeserver "https://your-homeserver.example.com"
   openclaw config set channels.matrix.accounts.default.userId "@bot:your-homeserver.example.com"
   openclaw config set channels.matrix.accounts.default.accessToken "syt_..."
   openclaw config set channels.matrix.accounts.default.encryption true
   openclaw config set channels.matrix.accounts.default.deviceName "OpenClaw"
   openclaw config set channels.matrix.accounts.default.dm.policy "allowlist"
   openclaw config set channels.matrix.accounts.default.dm.allowFrom '["@youruser:example.com"]'
   openclaw config set channels.matrix.accounts.default.groupPolicy "disabled"
   ```
5. Restart the gateway: `openclaw gateway restart`
6. Verify: `openclaw channels status` and `openclaw channels logs` β€” look for "Matrix monitor started" and successful /sync

### Option 2: Tuwunel + claw-matrix
Self-host a lightweight, high-performance Matrix homeserver using Tuwunel (Rust-based, successor to conduwuit) alongside claw-matrix. Best for users who want full control over their Matrix infrastructure without the resource overhead of Synapse.

Requirements:
- A server with a domain name and valid TLS (or a reverse proxy)
- Podman or Docker for running Tuwunel
- DNS records pointing to the server

Steps:
1. Pull the Tuwunel container image:
   `podman pull ghcr.io/matrix-construct/tuwunel:main`
2. Create data directory:
   `mkdir -p ~/.local/share/tuwunel`
3. Generate a Tuwunel config at `~/.local/share/tuwunel/tuwunel.toml`:
   ```toml
   [global]
   server_name = "your-domain.com"
   database_path = "/data/db"
   port = [8448]
   address = "0.0.0.0"
   allow_registration = false
   allow_encryption = true
   allow_federation = true
   trusted_servers = ["matrix.org"]
   log = "info"
   ```
4. Run Tuwunel:
   ```
   podman run -d --name tuwunel \
     --network=host --userns=keep-id \
     -v ~/.local/share/tuwunel:/data:Z \
     ghcr.io/matrix-construct/tuwunel:main
   ```
5. Register the bot account via the Tuwunel admin API or CLI.
6. Obtain an access token for the bot account.
7. Follow Option 1 steps 1-6 using `https://your-domain.com:8448` as the homeserver URL and the bot's access token.

### Option 3: Tuwunel + Cloudflare + claw-matrix
Full production stack: Tuwunel homeserver proxied through Cloudflare for DDoS protection, TLS termination, and caching β€” plus claw-matrix for OpenClaw integration. Best for public-facing deployments or federation-heavy setups.

Requirements:
- A Cloudflare account with a domain configured
- `cloudflared` (Cloudflare Tunnel daemon) installed
- Podman or Docker for running Tuwunel

Steps:
1. Set up Tuwunel (follow Option 2, steps 1-4), but bind to localhost only:
   - In `tuwunel.toml`, set `address = "127.0.0.1"`
2. Configure Cloudflare DNS:
   - Add an A/AAAA record for `matrix.your-domain.com` (proxied, orange cloud)
   - Add an SRV record: `_matrix._tcp.your-domain.com β†’ matrix.your-domain.com:443`
3. Create a Cloudflare Tunnel:
   ```
   cloudflared tunnel create tuwunel
   cloudflared tunnel route dns tuwunel matrix.your-domain.com
   ```
4. Configure the tunnel (`~/.cloudflared/config.yml`):
   ```yaml
   tunnel: <tunnel-id>
   credentials-file: ~/.cloudflared/<tunnel-id>.json
   ingress:
     - hostname: matrix.your-domain.com
       service: http://localhost:8448
     - service: http_status:404
   ```
5. Start the tunnel:
   `cloudflared tunnel run tuwunel`
   (Or set up as a systemd service for persistence.)
6. Configure Cloudflare SSL/TLS to "Full (strict)".
7. Set up `.well-known` delegation if `server_name` differs from the matrix subdomain:
   - Serve `/.well-known/matrix/server` returning `{"m.server": "matrix.your-domain.com:443"}`
   - Serve `/.well-known/matrix/client` returning `{"m.homeserver": {"base_url": "https://matrix.your-domain.com"}}`
8. Register the bot account and obtain an access token (see Option 2, steps 5-6).
9. Follow Option 1 steps 1-6 using `https://matrix.your-domain.com` as the homeserver URL.


---

After presenting the options, guide the user step-by-step through their chosen option. Ask for each required value interactively (domain, tokens, user IDs). Validate inputs where possible (e.g., homeserver URL must start with https://, userId must match @user:domain format). After installation, help verify the setup is working by checking gateway logs.
````

## Plugin Profile

OpenClaw channel plugin for Matrix with E2E encryption via `@matrix-org/matrix-sdk-crypto-nodejs`.

**Location:** `~/.openclaw/extensions/claw-matrix/`
**Status:** Phase 1 complete β€” text messages, DMs, groups, E2E encryption working. Device cross-signed (2026-02-11).
**Plugin ID:** `claw-matrix` (registers channel ID `matrix`)
**Loaded via:** jiti (JIT TypeScript), NOT compiled β€” entry point is `index.ts` directly

## Architecture

```
index.ts                    β†’ register(api): stores PluginRuntime, registers channel
src/runtime.ts              β†’ module-level PluginRuntime store (get/set) β€” NEW, solves dispatch
src/channel.ts              β†’ ChannelPlugin contract (all OpenClaw adapters)
src/monitor.ts              β†’ sync loop lifecycle + inbound message dispatch
src/config.ts               β†’ Zod schema + ResolvedMatrixAccount resolver
src/actions.ts              β†’ agent tool actions (send/read/channel-list)
src/types.ts                β†’ Matrix event/response TypeScript interfaces
src/client/http.ts          β†’ matrixFetch() β€” authenticated Matrix API client
src/client/sync.ts          β†’ runSyncLoop() β€” long-poll /sync, decrypt, dispatch
src/client/send.ts          → sendMatrixMessage() — encrypt + send (markdown→HTML)
src/client/rooms.ts         β†’ in-memory room state (encryption, type, names, members)
src/crypto/machine.ts       β†’ OlmMachine init/close, crypto store path
src/crypto/outgoing.ts      β†’ processOutgoingRequests() β€” key upload/query/claim/share
```

## Message Flow

### Inbound (Matrix β†’ Agent)
1. `runSyncLoop()` long-polls `/sync` (30s timeout, exponential backoff)
2. To-device events fed to OlmMachine FIRST (key deliveries)
3. UTD queue retried (previously undecryptable events)
4. Timeline events: encrypted β†’ `machine.decryptRoomEvent()` β†’ plaintext
5. `onMessage(event, roomId)` callback fires in `monitor.ts`
6. Monitor checks: skip own messages β†’ access control (allowlist) β†’ empty body
7. `core.channel.routing.resolveAgentRoute()` β†’ determines agent + session key
8. `core.channel.reply.finalizeInboundContext()` β†’ creates FinalizedMsgContext
9. `core.channel.reply.dispatchReplyWithBufferedBlockDispatcher()` β†’ agent turn
10. Agent reply delivered via `deliver` callback β†’ `sendMatrixMessage()`

### Outbound (Agent β†’ Matrix)
1. OpenClaw calls `outbound.sendText({ to: roomId, text })` OR deliver callback
2. `sendMatrixMessage()` formats markdown→HTML via markdown-it + sanitize-html
3. If room encrypted: `ensureRoomKeysShared()` β†’ `machine.encryptRoomEvent()` β†’ PUT encrypted event
4. If plaintext: PUT `m.room.message` directly

## Key Interfaces

### MonitorMatrixOpts (monitor.ts)
```typescript
{ config, account: ResolvedMatrixAccount, accountId, abortSignal, log, getStatus, setStatus }
```

### ResolvedMatrixAccount (config.ts)
```typescript
{ accountId, enabled, homeserver, userId, accessToken, password?, encryption, deviceName,
  dm: { policy, allowFrom[] }, groupPolicy, groups: Record<roomId, { allow, requireMention }>,
  groupAllowFrom[], chunkMode, textChunkLimit, recoveryKey?, trustMode }
```

### MsgContext fields set by monitor.ts
```typescript
{ Body, RawBody, CommandBody, From: "matrix:${sender}" | "matrix:room:${roomId}",
  To: "matrix:${roomId}", SessionKey (from route), AccountId, ChatType: "direct"|"group",
  GroupSubject?, SenderName, SenderId, Provider: "matrix", Surface: "matrix",
  MessageSid, OriginatingChannel: "matrix", OriginatingTo: roomId, Timestamp,
  CommandAuthorized: true }
```

### ChannelPlugin adapters implemented (channel.ts)
- **meta** β€” id, label, blurb
- **capabilities** β€” text only, dm+group, blockStreaming; NO reactions/edit/unsend/reply/media/threads
- **config** β€” listAccountIds, resolveAccount, isEnabled, isConfigured, resolveAllowFrom
- **gateway** β€” startAccount (launches monitor), stopAccount (abortSignal)
- **outbound** β€” deliveryMode: "direct", sendText, sendPayload, resolveTarget
- **security** β€” resolveDmPolicy (returns dm.policy + dm.allowFrom)
- **groups** β€” resolveRequireMention
- **actions** β€” send, read, channel-list (via actions.ts)
- **status** β€” buildAccountSnapshot
- **messaging** β€” normalizeTarget + targetResol

... (truncated)
channels

Comments

Sign in to leave a comment

Loading comments...