Matrix (plugin)
Matrix is the Matrix channel plugin for OpenClaw. It uses the officialmatrix-js-sdk and supports DMs, rooms, threads, media, reactions, polls, location, and E2EE.
Plugin required
Matrix is a plugin and is not bundled with core OpenClaw. Install from npm:Setup
- Install the plugin.
- Create a Matrix account on your homeserver.
- Configure
channels.matrixwith either:homeserver+accessToken, orhomeserver+userId+password.
- Restart the gateway.
- Start a DM with the bot or invite it to a room.
- homeserver URL
- auth method: access token or password
- user ID only when you choose password auth
- optional device name
- whether to enable E2EE
- whether to configure Matrix room access now
- If Matrix auth env vars already exist for the selected account, and that account does not already have auth saved in config, the wizard offers an env shortcut and only writes
enabled: truefor that account. - When you add another Matrix account interactively, the entered account name is normalized into the account ID used in config and env vars. For example,
Ops Botbecomesops-bot. - DM allowlist prompts accept full
@user:servervalues immediately. Display names only work when live directory lookup finds one exact match; otherwise the wizard asks you to retry with a full Matrix ID. - Room allowlist prompts accept room IDs and aliases directly. They can also resolve joined-room names live, but unresolved names are only kept as typed during setup and are ignored later by runtime allowlist resolution. Prefer
!room:serveror#alias:server. - Runtime room/session identity uses the stable Matrix room ID. Room-declared aliases are only used as lookup inputs, not as the long-term session key or stable group identity.
- To resolve room names before saving them, use
openclaw channels resolve --channel matrix "Project Room".
~/.openclaw/credentials/matrix/.
The default account uses credentials.json; named accounts use credentials-<account>.json.
Environment variable equivalents (used when the config key is not set):
MATRIX_HOMESERVERMATRIX_ACCESS_TOKENMATRIX_USER_IDMATRIX_PASSWORDMATRIX_DEVICE_IDMATRIX_DEVICE_NAME
MATRIX_<ACCOUNT_ID>_HOMESERVERMATRIX_<ACCOUNT_ID>_ACCESS_TOKENMATRIX_<ACCOUNT_ID>_USER_IDMATRIX_<ACCOUNT_ID>_PASSWORDMATRIX_<ACCOUNT_ID>_DEVICE_IDMATRIX_<ACCOUNT_ID>_DEVICE_NAME
ops:
MATRIX_OPS_HOMESERVERMATRIX_OPS_ACCESS_TOKEN
ops-bot, use:
MATRIX_OPS_BOT_HOMESERVERMATRIX_OPS_BOT_ACCESS_TOKEN
Configuration example
This is a practical baseline config with DM pairing, room allowlist, and E2EE enabled:Streaming previews
Matrix reply streaming is opt-in. Setchannels.matrix.streaming to "partial" when you want OpenClaw to send a single draft reply,
edit that draft in place while the model is generating text, and then finalize it when the reply is
done:
streaming: "off"is the default. OpenClaw waits for the final reply and sends it once.streaming: "partial"creates one editable preview message instead of sending multiple partial messages.- If the preview no longer fits in one Matrix event, OpenClaw stops preview streaming and falls back to normal final delivery.
- Media replies still send attachments normally. If a stale preview can no longer be reused safely, OpenClaw redacts it before sending the final media reply.
- Preview edits cost extra Matrix API calls. Leave streaming off if you want the most conservative rate-limit behavior.
Encryption and verification
In encrypted (E2EE) rooms, outbound image events usethumbnail_file so image previews are encrypted alongside the full attachment. Unencrypted rooms still use plain thumbnail_url. No configuration is needed — the plugin detects E2EE state automatically.
Bot to bot rooms
By default, Matrix messages from other configured OpenClaw Matrix accounts are ignored. UseallowBots when you intentionally want inter-agent Matrix traffic:
allowBots: trueaccepts messages from other configured Matrix bot accounts in allowed rooms and DMs.allowBots: "mentions"accepts those messages only when they visibly mention this bot in rooms. DMs are still allowed.groups.<room>.allowBotsoverrides the account-level setting for one room.- OpenClaw still ignores messages from the same Matrix user ID to avoid self-reply loops.
- Matrix does not expose a native bot flag here; OpenClaw treats “bot-authored” as “sent by another configured Matrix account on this OpenClaw gateway”.
channels.matrix.accounts with per-account credentials and optional name. See Configuration reference for the shared pattern.
Verbose bootstrap diagnostics:
verify commands are concise by default (including quiet internal SDK logging) and show detailed diagnostics only with --verbose.
Use --json for full machine-readable output when scripting.
In multi-account setups, Matrix CLI commands use the implicit Matrix default account unless you pass --account <id>.
If you configure multiple named accounts, set channels.matrix.defaultAccount first or those implicit CLI operations will stop and ask you to choose an account explicitly.
Use --account whenever you want verification or device operations to target a named account explicitly:
channels.matrix.accounts.assistant.encryption.
What “verified” means
OpenClaw treats this Matrix device as verified only when it is verified by your own cross-signing identity. In practice,openclaw matrix verify status --verbose exposes three trust signals:
Locally trusted: this device is trusted by the current client onlyCross-signing verified: the SDK reports the device as verified through cross-signingSigned by owner: the device is signed by your own self-signing key
Verified by owner becomes yes only when cross-signing verification or owner-signing is present.
Local trust by itself is not enough for OpenClaw to treat the device as fully verified.
What bootstrap does
openclaw matrix verify bootstrap is the repair and setup command for encrypted Matrix accounts.
It does all of the following in order:
- bootstraps secret storage, reusing an existing recovery key when possible
- bootstraps cross-signing and uploads missing public cross-signing keys
- attempts to mark and cross-sign the current device
- creates a new server-side room-key backup if one does not already exist
m.login.dummy, then with m.login.password when channels.matrix.password is configured.
Use --force-reset-cross-signing only when you intentionally want to discard the current cross-signing identity and create a new one.
If you intentionally want to discard the current room-key backup and start a new backup baseline for future messages, use openclaw matrix verify backup reset --yes.
Do this only when you accept that unrecoverable old encrypted history will stay unavailable.
Fresh backup baseline
If you want to keep future encrypted messages working and accept losing unrecoverable old history, run these commands in order:--account <id> to each command when you want to target a named Matrix account explicitly.
Startup behavior
Whenencryption: true, Matrix defaults startupVerification to "if-unverified".
On startup, if this device is still unverified, Matrix will request self-verification in another Matrix client,
skip duplicate requests while one is already pending, and apply a local cooldown before retrying after restarts.
Failed request attempts retry sooner than successful request creation by default.
Set startupVerification: "off" to disable automatic startup requests, or tune startupVerificationCooldownHours
if you want a shorter or longer retry window.
Startup also performs a conservative crypto bootstrap pass automatically.
That pass tries to reuse the current secret storage and cross-signing identity first, and avoids resetting cross-signing unless you run an explicit bootstrap repair flow.
If startup finds broken bootstrap state and channels.matrix.password is configured, OpenClaw can attempt a stricter repair path.
If the current device is already owner-signed, OpenClaw preserves that identity instead of resetting it automatically.
Upgrading from the previous public Matrix plugin:
- OpenClaw automatically reuses the same Matrix account, access token, and device identity when possible.
- Before any actionable Matrix migration changes run, OpenClaw creates or reuses a recovery snapshot under
~/Backups/openclaw-migrations/. - If you use multiple Matrix accounts, set
channels.matrix.defaultAccountbefore upgrading from the old flat-store layout so OpenClaw knows which account should receive that shared legacy state. - If the previous plugin stored a Matrix room-key backup decryption key locally, startup or
openclaw doctor --fixwill import it into the new recovery-key flow automatically. - If the Matrix access token changed after migration was prepared, startup now scans sibling token-hash storage roots for pending legacy restore state before giving up on the automatic backup restore.
- If the Matrix access token changes later for the same account, homeserver, and user, OpenClaw now prefers reusing the most complete existing token-hash storage root instead of starting from an empty Matrix state directory.
- On the next gateway start, backed-up room keys are restored automatically into the new crypto store.
- If the old plugin had local-only room keys that were never backed up, OpenClaw will warn clearly. Those keys cannot be exported automatically from the previous rust crypto store, so some old encrypted history may remain unavailable until recovered manually.
- See Matrix migration for the full upgrade flow, limits, recovery commands, and common migration messages.
~/.openclaw/matrix/accounts/<account>/<homeserver>__<user>/<token-hash>/.
That directory contains the sync store (bot-storage.json), crypto store (crypto/),
recovery key file (recovery-key.json), IndexedDB snapshot (crypto-idb-snapshot.json),
thread bindings (thread-bindings.json), and startup verification state (startup-verification.json)
when those features are in use.
When the token changes but the account identity stays the same, OpenClaw reuses the best existing
root for that account/homeserver/user tuple so prior sync state, crypto state, thread bindings,
and startup verification state remain visible.
Node crypto store model
Matrix E2EE in this plugin uses the officialmatrix-js-sdk Rust crypto path in Node.
That path expects IndexedDB-backed persistence when you want crypto state to survive restarts.
OpenClaw currently provides that in Node by:
- using
fake-indexeddbas the IndexedDB API shim expected by the SDK - restoring the Rust crypto IndexedDB contents from
crypto-idb-snapshot.jsonbeforeinitRustCrypto - persisting the updated IndexedDB contents back to
crypto-idb-snapshot.jsonafter init and during runtime
- add SecretRef support for persistent Matrix key material so recovery keys and related store-encryption secrets can be sourced from OpenClaw secrets providers instead of only local files
Profile management
Update the Matrix self-profile for the selected account with:--account <id> when you want to target a named Matrix account explicitly.
Matrix accepts mxc:// avatar URLs directly. When you pass an http:// or https:// avatar URL, OpenClaw uploads it to Matrix first and stores the resolved mxc:// URL back into channels.matrix.avatarUrl (or the selected account override).
Automatic verification notices
Matrix now posts verification lifecycle notices directly into the strict DM verification room asm.notice messages.
That includes:
- verification request notices
- verification ready notices (with explicit “Verify by emoji” guidance)
- verification start and completion notices
- SAS details (emoji and decimal) when available
NO_REPLY.
Device hygiene
Old OpenClaw-managed Matrix devices can accumulate on the account and make encrypted-room trust harder to reason about. List them with:Direct Room Repair
If direct-message state gets out of sync, OpenClaw can end up with stalem.direct mappings that point at old solo rooms instead of the live DM. Inspect the current mapping for a peer with:
- it prefers a strict 1:1 DM that is already mapped in
m.direct - otherwise it falls back to any currently joined strict 1:1 DM with that user
- if no healthy DM exists, it creates a fresh direct room and rewrites
m.directto point at it
Threads
Matrix supports native Matrix threads for both automatic replies and message-tool sends.threadReplies: "off"keeps replies top-level and keeps inbound threaded messages on the parent session.threadReplies: "inbound"replies inside a thread only when the inbound message was already in that thread.threadReplies: "always"keeps room replies in a thread rooted at the triggering message and routes that conversation through the matching thread-scoped session from the first triggering message.dm.threadRepliesoverrides the top-level setting for DMs only. For example, you can keep room threads isolated while keeping DMs flat.- Inbound threaded messages include the thread root message as extra agent context.
- Message-tool sends now auto-inherit the current Matrix thread when the target is the same room, or the same DM user target, unless an explicit
threadIdis provided. - Runtime thread bindings are supported for Matrix.
/focus,/unfocus,/agents,/session idle,/session max-age, and thread-bound/acp spawnnow work in Matrix rooms and DMs. - Top-level Matrix room/DM
/focuscreates a new Matrix thread and binds it to the target session whenthreadBindings.spawnSubagentSessions=true. - Running
/focusor/acp spawn --thread hereinside an existing Matrix thread binds that current thread instead.
ACP conversation bindings
Matrix rooms, DMs, and existing Matrix threads can be turned into durable ACP workspaces without changing the chat surface. Fast operator flow:- Run
/acp spawn codex --bind hereinside the Matrix DM, room, or existing thread you want to keep using. - In a top-level Matrix DM or room, the current DM/room stays the chat surface and future messages route to the spawned ACP session.
- Inside an existing Matrix thread,
--bind herebinds that current thread in place. /newand/resetreset the same bound ACP session in place./acp closecloses the ACP session and removes the binding.
--bind heredoes not create a child Matrix thread.threadBindings.spawnAcpSessionsis only required for/acp spawn --thread auto|here, where OpenClaw needs to create or bind a child Matrix thread.
Thread Binding Config
Matrix inherits global defaults fromsession.threadBindings, and also supports per-channel overrides:
threadBindings.enabledthreadBindings.idleHoursthreadBindings.maxAgeHoursthreadBindings.spawnSubagentSessionsthreadBindings.spawnAcpSessions
- Set
threadBindings.spawnSubagentSessions: trueto allow top-level/focusto create and bind new Matrix threads. - Set
threadBindings.spawnAcpSessions: trueto allow/acp spawn --thread auto|hereto bind ACP sessions to Matrix threads.
Reactions
Matrix supports outbound reaction actions, inbound reaction notifications, and inbound ack reactions.- Outbound reaction tooling is gated by
channels["matrix"].actions.reactions. reactadds a reaction to a specific Matrix event.reactionslists the current reaction summary for a specific Matrix event.emoji=""removes the bot account’s own reactions on that event.remove: trueremoves only the specified emoji reaction from the bot account.
channels["matrix"].accounts.<accountId>.ackReactionchannels["matrix"].ackReactionmessages.ackReaction- agent identity emoji fallback
channels["matrix"].accounts.<accountId>.ackReactionScopechannels["matrix"].ackReactionScopemessages.ackReactionScope
channels["matrix"].accounts.<accountId>.reactionNotificationschannels["matrix"].reactionNotifications- default:
own
reactionNotifications: "own"forwards addedm.reactionevents when they target bot-authored Matrix messages.reactionNotifications: "off"disables reaction system events.- Reaction removals are still not synthesized into system events because Matrix surfaces those as redactions, not as standalone
m.reactionremovals.
History context
channels.matrix.historyLimitcontrols how many recent room messages are included asInboundHistorywhen a Matrix room message triggers the agent.- It falls back to
messages.groupChat.historyLimit. Set0to disable. - Matrix room history is room-only. DMs keep using normal session history.
- Matrix room history is pending-only: OpenClaw buffers room messages that did not trigger a reply yet, then snapshots that window when a mention or other trigger arrives.
- The current trigger message is not included in
InboundHistory; it stays in the main inbound body for that turn. - Retries of the same Matrix event reuse the original history snapshot instead of drifting forward to newer room messages.
- Fetched room context (including reply and thread context lookups) is filtered by sender allowlists (
groupAllowFrom), so non-allowlisted messages are excluded from agent context.
DM and room policy example
Multi-account example
channels.matrix values act as defaults for named accounts unless an account overrides them.
Set defaultAccount when you want OpenClaw to prefer one named Matrix account for implicit routing, probing, and CLI operations.
If you configure multiple named accounts, set defaultAccount or pass --account <id> for CLI commands that rely on implicit account selection.
Pass --account <id> to openclaw matrix verify ... and openclaw matrix devices ... when you want to override that implicit selection for one command.
Private/LAN homeservers
By default, OpenClaw blocks private/internal Matrix homeservers for SSRF protection unless you explicitly opt in per account. If your homeserver runs on localhost, a LAN/Tailscale IP, or an internal hostname, enableallowPrivateNetwork for that Matrix account:
http://matrix.example.org:8008 remain blocked. Prefer https:// whenever possible.
Proxying Matrix traffic
If your Matrix deployment needs an explicit outbound HTTP(S) proxy, setchannels.matrix.proxy:
channels.matrix.accounts.<id>.proxy.
OpenClaw uses the same proxy setting for runtime Matrix traffic and account status probes.
Target resolution
Matrix accepts these target forms anywhere OpenClaw asks you for a room or user target:- Users:
@user:server,user:@user:server, ormatrix:user:@user:server - Rooms:
!room:server,room:!room:server, ormatrix:room:!room:server - Aliases:
#alias:server,channel:#alias:server, ormatrix:channel:#alias:server
- User lookups query the Matrix user directory on that homeserver.
- Room lookups accept explicit room IDs and aliases directly, then fall back to searching joined room names for that account.
- Joined-room name lookup is best-effort. If a room name cannot be resolved to an ID or alias, it is ignored by runtime allowlist resolution.
Configuration reference
enabled: enable or disable the channel.name: optional label for the account.defaultAccount: preferred account ID when multiple Matrix accounts are configured.homeserver: homeserver URL, for examplehttps://matrix.example.org.allowPrivateNetwork: allow this Matrix account to connect to private/internal homeservers. Enable this when the homeserver resolves tolocalhost, a LAN/Tailscale IP, or an internal host such asmatrix-synapse.proxy: optional HTTP(S) proxy URL for Matrix traffic. Named accounts can override the top-level default with their ownproxy.userId: full Matrix user ID, for example@bot:example.org.accessToken: access token for token-based auth. Plaintext values and SecretRef values are supported forchannels.matrix.accessTokenandchannels.matrix.accounts.<id>.accessTokenacross env/file/exec providers. See Secrets Management.password: password for password-based login. Plaintext values and SecretRef values are supported.deviceId: explicit Matrix device ID.deviceName: device display name for password login.avatarUrl: stored self-avatar URL for profile sync andset-profileupdates.initialSyncLimit: startup sync event limit.encryption: enable E2EE.allowlistOnly: force allowlist-only behavior for DMs and rooms.groupPolicy:open,allowlist, ordisabled.groupAllowFrom: allowlist of user IDs for room traffic.groupAllowFromentries should be full Matrix user IDs. Unresolved names are ignored at runtime.historyLimit: max room messages to include as group history context. Falls back tomessages.groupChat.historyLimit. Set0to disable.replyToMode:off,first, orall.streaming:off(default) orpartial.partialenables single-message draft previews with edit-in-place updates.threadReplies:off,inbound, oralways.threadBindings: per-channel overrides for thread-bound session routing and lifecycle.startupVerification: automatic self-verification request mode on startup (if-unverified,off).startupVerificationCooldownHours: cooldown before retrying automatic startup verification requests.textChunkLimit: outbound message chunk size.chunkMode:lengthornewline.responsePrefix: optional message prefix for outbound replies.ackReaction: optional ack reaction override for this channel/account.ackReactionScope: optional ack reaction scope override (group-mentions,group-all,direct,all,none,off).reactionNotifications: inbound reaction notification mode (own,off).mediaMaxMb: media size cap in MB for Matrix media handling. It applies to outbound sends and inbound media processing.autoJoin: invite auto-join policy (always,allowlist,off). Default:off.autoJoinAllowlist: rooms/aliases allowed whenautoJoinisallowlist. Alias entries are resolved to room IDs during invite handling; OpenClaw does not trust alias state claimed by the invited room.dm: DM policy block (enabled,policy,allowFrom,threadReplies).dm.allowFromentries should be full Matrix user IDs unless you already resolved them through live directory lookup.dm.threadReplies: DM-only thread policy override (off,inbound,always). It overrides the top-levelthreadRepliessetting for both reply placement and session isolation in DMs.accounts: named per-account overrides. Top-levelchannels.matrixvalues act as defaults for these entries.groups: per-room policy map. Prefer room IDs or aliases; unresolved room names are ignored at runtime. Session/group identity uses the stable room ID after resolution, while human-readable labels still come from room names.rooms: legacy alias forgroups.actions: per-action tool gating (messages,reactions,pins,profile,memberInfo,channelInfo,verification).
Related
- Channels Overview — all supported channels
- Pairing — DM authentication and pairing flow
- Groups — group chat behavior and mention gating
- Channel Routing — session routing for messages
- Security — access model and hardening