Files
openclaw/docs/channels/matrix.md
2026-03-11 22:34:02 +00:00

21 KiB

summary, read_when, title
summary read_when title
Matrix support status, setup, and configuration examples
Setting up Matrix in OpenClaw
Configuring Matrix E2EE and verification
Matrix

Matrix (plugin)

Matrix is the Matrix channel plugin for OpenClaw. It uses the official matrix-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:

openclaw plugins install @openclaw/matrix

Install from a local checkout:

openclaw plugins install ./extensions/matrix

See Plugins for plugin behavior and install rules.

Setup

  1. Install the plugin.
  2. Create a Matrix account on your homeserver.
  3. Configure channels.matrix with either:
    • homeserver + accessToken, or
    • homeserver + userId + password.
  4. Restart the gateway.
  5. Start a DM with the bot or invite it to a room.

Interactive setup paths:

openclaw channels add
openclaw configure --section channels

What the Matrix wizard actually asks for:

  • 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

Wizard behavior that matters:

  • 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: true for 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 Bot becomes ops-bot.
  • DM allowlist prompts accept full @user:server values 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:server or #alias:server.
  • To resolve room names before saving them, use openclaw channels resolve --channel matrix "Project Room".

Minimal token-based setup:

{
  channels: {
    matrix: {
      enabled: true,
      homeserver: "https://matrix.example.org",
      accessToken: "syt_xxx",
      dm: { policy: "pairing" },
    },
  },
}

Password-based setup (token is cached after login):

{
  channels: {
    matrix: {
      enabled: true,
      homeserver: "https://matrix.example.org",
      userId: "@bot:example.org",
      password: "replace-me", // pragma: allowlist secret
      deviceName: "OpenClaw Gateway",
    },
  },
}

Matrix stores cached credentials in ~/.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_HOMESERVER
  • MATRIX_ACCESS_TOKEN
  • MATRIX_USER_ID
  • MATRIX_PASSWORD
  • MATRIX_DEVICE_ID
  • MATRIX_DEVICE_NAME

For non-default accounts, use account-scoped env vars:

  • MATRIX_<ACCOUNT_ID>_HOMESERVER
  • MATRIX_<ACCOUNT_ID>_ACCESS_TOKEN
  • MATRIX_<ACCOUNT_ID>_USER_ID
  • MATRIX_<ACCOUNT_ID>_PASSWORD
  • MATRIX_<ACCOUNT_ID>_DEVICE_ID
  • MATRIX_<ACCOUNT_ID>_DEVICE_NAME

Example for account ops:

  • MATRIX_OPS_HOMESERVER
  • MATRIX_OPS_ACCESS_TOKEN

For normalized account ID ops-bot, use:

  • MATRIX_OPS_BOT_HOMESERVER
  • MATRIX_OPS_BOT_ACCESS_TOKEN

The interactive wizard only offers the env-var shortcut when those auth env vars are already present and the selected account does not already have Matrix auth saved in config.

Configuration example

This is a practical baseline config with DM pairing, room allowlist, and E2EE enabled:

{
  channels: {
    matrix: {
      enabled: true,
      homeserver: "https://matrix.example.org",
      accessToken: "syt_xxx",
      encryption: true,

      dm: {
        policy: "pairing",
      },

      groupPolicy: "allowlist",
      groupAllowFrom: ["@admin:example.org"],
      groups: {
        "!roomid:example.org": {
          requireMention: true,
        },
      },

      autoJoin: "allowlist",
      autoJoinAllowlist: ["!roomid:example.org"],
      threadReplies: "inbound",
      replyToMode: "off",
    },
  },
}

E2EE setup

Enable encryption:

{
  channels: {
    matrix: {
      enabled: true,
      homeserver: "https://matrix.example.org",
      accessToken: "syt_xxx",
      encryption: true,
      dm: { policy: "pairing" },
    },
  },
}

Check verification status:

openclaw matrix verify status

Verbose status (full diagnostics):

openclaw matrix verify status --verbose

Include the stored recovery key in machine-readable output:

openclaw matrix verify status --include-recovery-key --json

Bootstrap cross-signing and verification state:

openclaw matrix verify bootstrap

Verbose bootstrap diagnostics:

openclaw matrix verify bootstrap --verbose

Force a fresh cross-signing identity reset before bootstrapping:

openclaw matrix verify bootstrap --force-reset-cross-signing

Verify this device with a recovery key:

openclaw matrix verify device "<your-recovery-key>"

Verbose device verification details:

openclaw matrix verify device "<your-recovery-key>" --verbose

Check room-key backup health:

openclaw matrix verify backup status

Verbose backup health diagnostics:

openclaw matrix verify backup status --verbose

Restore room keys from server backup:

openclaw matrix verify backup restore

Verbose restore diagnostics:

openclaw matrix verify backup restore --verbose

Delete the current server backup and create a fresh backup baseline:

openclaw matrix verify backup reset --yes

All 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>. Use --account whenever you want verification or device operations to target a named account explicitly:

openclaw matrix verify status --account assistant
openclaw matrix verify backup restore --account assistant
openclaw matrix devices list --account assistant

When encryption is disabled or unavailable for a named account, Matrix warnings and verification errors point at that account's config key, for example 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 only
  • Cross-signing verified: the SDK reports the device as verified through cross-signing
  • Signed 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

If the homeserver requires interactive auth to upload cross-signing keys, OpenClaw tries the upload without auth first, then with 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:

openclaw matrix verify backup reset --yes
openclaw matrix verify backup status --verbose
openclaw matrix verify status

Add --account <id> to each command when you want to target a named Matrix account explicitly.

Startup behavior

When encryption: 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.
  • If the previous plugin stored a Matrix room-key backup decryption key locally, startup or openclaw doctor --fix will import it into the new recovery-key flow automatically.
  • 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.

Encrypted runtime state is stored per account and per access token in ~/.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.

Node crypto store model

Matrix E2EE in this plugin uses the official matrix-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-indexeddb as the IndexedDB API shim expected by the SDK
  • restoring the Rust crypto IndexedDB contents from crypto-idb-snapshot.json before initRustCrypto
  • persisting the updated IndexedDB contents back to crypto-idb-snapshot.json after init and during runtime

This is compatibility/storage plumbing, not a custom crypto implementation. The snapshot file is sensitive runtime state and is stored with restrictive file permissions. Under OpenClaw's security model, the gateway host and local OpenClaw state directory are already inside the trusted operator boundary, so this is primarily an operational durability concern rather than a separate remote trust boundary.

Planned improvement:

  • 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

Automatic verification notices

Matrix now posts verification lifecycle notices directly into the Matrix room as m.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

Incoming verification requests from another Matrix client are tracked and auto-accepted by OpenClaw. When SAS emoji verification becomes available, OpenClaw starts that SAS flow automatically for inbound requests and confirms its own side. You still need to compare the emoji or decimal SAS in your Matrix client and confirm "They match" there to complete the verification.

OpenClaw does not auto-accept self-initiated duplicate flows blindly. Startup skips creating a new request when a self-verification request is already pending.

Verification protocol/system notices are not forwarded to the agent chat pipeline, so they do not produce 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:

openclaw matrix devices list

Remove stale OpenClaw-managed devices with:

openclaw matrix devices prune-stale

Threads

Matrix supports native Matrix threads for both automatic replies and message-tool sends.

  • threadReplies: "off" keeps replies top-level.
  • 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.
  • 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 threadId is provided.
  • Runtime thread bindings are supported for Matrix. /focus, /unfocus, /agents, /session idle, /session max-age, and thread-bound /acp spawn now work in Matrix rooms and DMs.
  • Top-level Matrix room/DM /focus creates a new Matrix thread and binds it to the target session when threadBindings.spawnSubagentSessions=true.
  • Running /focus or /acp spawn --thread here inside an existing Matrix thread binds that current thread instead.

Thread Binding Config

Matrix inherits global defaults from session.threadBindings, and also supports per-channel overrides:

  • threadBindings.enabled
  • threadBindings.idleHours
  • threadBindings.maxAgeHours
  • threadBindings.spawnSubagentSessions
  • threadBindings.spawnAcpSessions

Matrix thread-bound spawn flags are opt-in:

  • Set threadBindings.spawnSubagentSessions: true to allow top-level /focus to create and bind new Matrix threads.
  • Set threadBindings.spawnAcpSessions: true to allow /acp spawn --thread auto|here to 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.
  • react adds a reaction to a specific Matrix event.
  • reactions lists the current reaction summary for a specific Matrix event.
  • emoji="" removes the bot account's own reactions on that event.
  • remove: true removes only the specified emoji reaction from the bot account.

Ack reactions use the standard OpenClaw resolution order:

  • channels["matrix"].accounts.<accountId>.ackReaction
  • channels["matrix"].ackReaction
  • messages.ackReaction
  • agent identity emoji fallback

Ack reaction scope resolves in this order:

  • channels["matrix"].accounts.<accountId>.ackReactionScope
  • channels["matrix"].ackReactionScope
  • messages.ackReactionScope

Reaction notification mode resolves in this order:

  • channels["matrix"].accounts.<accountId>.reactionNotifications
  • channels["matrix"].reactionNotifications
  • default: own

Current behavior:

  • reactionNotifications: "own" forwards added m.reaction events 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.reaction removals.

DM and room policy example

{
  channels: {
    matrix: {
      dm: {
        policy: "allowlist",
        allowFrom: ["@admin:example.org"],
      },
      groupPolicy: "allowlist",
      groupAllowFrom: ["@admin:example.org"],
      groups: {
        "!roomid:example.org": {
          requireMention: true,
        },
      },
    },
  },
}

See Groups for mention-gating and allowlist behavior.

Pairing example for Matrix DMs:

openclaw pairing list matrix
openclaw pairing approve matrix <CODE>

If an unapproved Matrix user keeps messaging you before approval, OpenClaw reuses the same pending pairing code and may send a reminder reply again after a short cooldown instead of minting a new code.

See Pairing for the shared DM pairing flow and storage layout.

Multi-account example

{
  channels: {
    matrix: {
      enabled: true,
      defaultAccount: "assistant",
      dm: { policy: "pairing" },
      accounts: {
        assistant: {
          homeserver: "https://matrix.example.org",
          accessToken: "syt_assistant_xxx",
          encryption: true,
        },
        alerts: {
          homeserver: "https://matrix.example.org",
          accessToken: "syt_alerts_xxx",
          dm: {
            policy: "allowlist",
            allowFrom: ["@ops:example.org"],
          },
        },
      },
    },
  },
}

Top-level 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. Pass --account <id> to openclaw matrix verify ... and openclaw matrix devices ... when you want to override that implicit selection for one command.

Target resolution

Matrix accepts these target forms anywhere OpenClaw asks you for a room or user target:

  • Users: @user:server, user:@user:server, or matrix:user:@user:server
  • Rooms: !room:server, room:!room:server, or matrix:room:!room:server
  • Aliases: #alias:server, channel:#alias:server, or matrix:channel:#alias:server

Live directory lookup uses the logged-in Matrix account:

  • 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.
  • defaultAccount: preferred account ID when multiple Matrix accounts are configured.
  • homeserver: homeserver URL, for example https://matrix.example.org.
  • userId: full Matrix user ID, for example @bot:example.org.
  • accessToken: access token for token-based auth.
  • password: password for password-based login.
  • deviceId: explicit Matrix device ID.
  • deviceName: device display name for password login.
  • initialSyncLimit: startup sync event limit.
  • encryption: enable E2EE.
  • allowlistOnly: force allowlist-only behavior for DMs and rooms.
  • groupPolicy: open, allowlist, or disabled.
  • groupAllowFrom: allowlist of user IDs for room traffic.
  • groupAllowFrom entries should be full Matrix user IDs. Unresolved names are ignored at runtime.
  • replyToMode: off, first, or all.
  • threadReplies: off, inbound, or always.
  • 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: length or newline.
  • 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: outbound media size cap in MB.
  • autoJoin: invite auto-join policy (always, allowlist, off).
  • autoJoinAllowlist: rooms/aliases allowed when autoJoin is allowlist.
  • dm: DM policy block (enabled, policy, allowFrom).
  • dm.allowFrom entries should be full Matrix user IDs unless you already resolved them through live directory lookup.
  • accounts: named per-account overrides. Top-level channels.matrix values act as defaults for these entries.
  • groups: per-room policy map. Prefer room IDs or aliases; unresolved room names are ignored at runtime.
  • rooms: legacy alias for groups.
  • actions: per-action tool gating (messages, reactions, pins, memberInfo, channelInfo, verification).