OpenClaw Matrix Integration
OpenClaw Matrix Integration
Research notes on integrating OpenClaw with Matrix, focusing on E2E encryption, threading, and group chats.
Overview
OpenClaw connects to Matrix as a regular user on any homeserver via the @openclaw/matrix plugin. The integration uses the @vector-im/matrix-bot-sdk with optional Rust crypto SDK for E2EE.
Installation:
openclaw plugins install @openclaw/matrix
E2E Encryption (E2EE)
How It Works
- Uses the Rust crypto SDK (
@matrix-org/matrix-sdk-crypto-nodejs) - Encryption enabled via
channels.matrix.encryption: true - Crypto state stored in SQLite at
~/.openclaw/matrix/accounts/<account>/<homeserver>__<user>/<token-hash>/crypto/
Setup Requirements
- Enable encryption in config:
{
channels: {
matrix: {
enabled: true,
homeserver: "https://matrix.example.org",
accessToken: "syt_***",
encryption: true,
},
},
}
-
Device verification: On first connection, OpenClaw requests verification from your other sessions. Open Element (or another client) and approve the verification request to establish trust.
-
Crypto module: If you see missing crypto module errors, run:
pnpm rebuild @matrix-org/matrix-sdk-crypto-nodejs
# or
node node_modules/@matrix-org/matrix-sdk-crypto-nodejs/download-lib.js
Key Points
| Aspect | Details |
|---|---|
| Inbound | Encrypted rooms are decrypted automatically |
| Outbound | Media is encrypted when sending to encrypted rooms |
| Key sharing | Requires device verification with another session |
| Token change | New access token = new crypto store = re-verification needed |
| Beeper | Requires E2EE enabled (encryption: true) |
Limitations
- If crypto module cannot load, E2EE is disabled (logged warning)
- Encrypted rooms will not decrypt without proper setup
- Device must be verified for key sharing to work
Threading
Protocol Support
Matrix threads are stable since spec v1.4 (MSC3440). The m.thread relation provides:
- Thread root event ID on every threaded event
- Stable/unique identifier per thread
- Full Element/modern client support
OpenClaw Thread Handling
Current behavior: Threads are treated as reply formatting only, NOT session isolation. All messages in a room (threaded or not) share the same session key:
agent:<agentId>:matrix:room:<roomId>
Configuration options:
{
channels: {
matrix: {
threadReplies: "inbound", // off | inbound (default) | always
replyToMode: "off", // off (default) | first | all
},
},
}
threadReplies |
Behavior |
|---|---|
off |
Never reply in threads |
inbound (default) |
Reply in thread if message came from thread |
always |
Always reply in threads |
Thread Session Isolation (Feature Request)
There’s an open feature request (#29729) for per-thread session isolation:
Proposed session key format:
# Room-level (current)
agent:main:matrix:room:!abc123:example.org
# Thread-level (proposed)
agent:main:matrix:room:!abc123:example.org:thread:$rootEventId
Proposed config:
{
channels: {
matrix: {
thread: {
historyScope: "thread", // "thread" | "room" (current behavior)
inheritParent: false, // copy parent room transcript
initialHistoryLimit: 20, // fetch N existing thread messages
},
},
},
}
Status: Not yet implemented. Currently, Slack and Telegram have thread/topic session isolation, but Matrix does not.
Current Workarounds
- Use separate rooms instead of threads (heavy, clutters room list)
- Use
/newcommand to reset session when switching topics - Prompt-prefix approach (unreliable, wastes tokens)
Group Chats (Rooms)
Access Control
Default policy: groupPolicy: "allowlist" (mention-gated)
{
channels: {
matrix: {
groupPolicy: "allowlist", // allowlist | open | disabled
groups: {
"!roomId:example.org": { allow: true },
"#alias:example.org": { allow: true },
},
groupAllowFrom: ["@owner:example.org"], // restrict senders
},
},
}
Room Configuration Options
| Setting | Purpose |
|---|---|
groups |
Room allowlist (IDs or aliases) |
groupAllowFrom |
Restrict which senders can trigger the bot |
requireMention: false |
Enable auto-reply in that room |
groups."*" |
Set defaults across all rooms |
autoJoin |
always (default) / allowlist / off |
autoJoinAllowlist |
Allowed room IDs/aliases for auto-join |
Per-Room User Restrictions
{
groups: {
"!roomId:example.org": {
allow: true,
users: ["@allowed:example.org"], // restrict to specific users
},
},
}
Routing Model
- DMs: Share the agent’s main session
- Rooms: Each room gets its own group session
- Threads: Share parent room session (no isolation currently)
Multi-Account Support
Run multiple Matrix accounts with different configs:
{
channels: {
matrix: {
enabled: true,
dm: { policy: "pairing" },
accounts: {
assistant: {
name: "Main assistant",
homeserver: "https://matrix.example.org",
accessToken: "syt_assistant_***",
encryption: true,
},
alerts: {
name: "Alerts bot",
homeserver: "https://matrix.example.org",
accessToken: "syt_alerts_***",
dm: { policy: "allowlist", allowFrom: ["@admin:example.org"] },
},
},
},
},
}
- Each account inherits top-level settings (can override)
- Account startup is serialized (avoids race conditions)
- Crypto state is per account + access token
- Use
bindings[].match.accountIdto route accounts to different agents
Capabilities Summary
| Feature | Status |
|---|---|
| Direct messages | ✅ Supported |
| Rooms (groups) | ✅ Supported |
| Threads | ✅ Supported (reply formatting, no session isolation) |
| E2EE | ✅ Supported (requires crypto module) |
| Media | ✅ Supported |
| Reactions | ✅ Supported |
| Polls | ✅ Send only (inbound as text) |
| Location | ✅ Supported (geo URI) |
| Native commands | ✅ Supported |
Troubleshooting
# Diagnostic ladder
openclaw status
openclaw gateway status
openclaw logs --follow
openclaw doctor
openclaw channels status --probe
openclaw pairing list matrix
Common issues:
| Problem | Cause |
|---|---|
| Room messages ignored | groupPolicy or room allowlist blocking |
| DMs ignored | Sender pending approval (dm.policy: "pairing") |
| Encrypted rooms fail | Crypto module not loaded or device not verified |
| Thread context pollution | No thread session isolation (expected current behavior) |