Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

矩阵

在 Matrix 房间中运行 ZeroClaw,包括端到端加密(E2EE)房间。

本指南针对的常见故障模式:

矩阵配置正确,检查通过,但机器人未响应。

快速常见问题解答

如果 Matrix 显示已连接但没有回复,请先验证以下内容:

  1. 发送者被 allowed_users 允许(用于测试:["*"])。
  2. 机器人账号已加入目标房间。
  3. Token belongs to the same bot account (whoami check — see §5C).
  4. 加密房间具有可用的设备身份(device_id)和密钥共享功能。
  5. 守护进程已在配置更改后重启。

1. 需求

在测试消息流之前:

  1. 机器人账号已加入目标房间。
  2. 访问令牌属于同一个机器人账户。
  3. allowed_rooms includes the target room (or is empty to allow all rooms the bot has joined). Each entry is either a canonical room ID (!room:server) or an alias (#alias:server); ZeroClaw resolves aliases.
  4. allowed_users 允许指定发送者(["*"] 表示开放测试)。
  5. 对于 E2EE 房间,机器人设备已收到该房间的加密密钥。

2. 配置

所有配置管理都通过 zeroclaw configzeroclaw onboard 进行。请勿手动编辑 ~/.zeroclaw/config.toml

最简单的方法:运行向导,让它提示每个 Matrix 字段:

zeroclaw onboard channels

或者在入职后设置各个字段:

zeroclaw config set channels.matrix.homeserver https://matrix.example.com
zeroclaw config set channels.matrix.access-token           # 提示词,输入已掩码
zeroclaw config set channels.matrix.user-id @bot:matrix.example.com
zeroclaw config set channels.matrix.device-id ABCDEF1234
zeroclaw config set channels.matrix.allowed-users '["*"]'   # 开放用于测试
zeroclaw config set channels.matrix.allowed-rooms '["!room:matrix.example.com"]'  # empty list = allow all joined rooms
zeroclaw config set channels.matrix.ack-reactions true       # default: true (👀 → ✅)
zeroclaw config set channels.matrix.reply-in-thread true     # default: true

Required: homeserver, access-token, allowed-users. Strongly recommended for E2EE: user-id and device-id. allowed-rooms is optional — leave empty to allow every room the bot has joined, or list explicit IDs/aliases to restrict. For the full field index, see the Config reference.

Don’t have an access-token yet? See §3 below — it walks through the Matrix password-login API call that mints a token plus a stable device_id in one shot. If you only need to look up device_id for a token you already have, see §5H.

关于 user-iddevice-id

  • ZeroClaw 尝试从 Matrix /_matrix/client/v3/account/whoami 读取身份信息。
  • 如果 whoami 未返回 device_id,请手动设置 device-id——这对 E2EE 会话恢复至关重要。

3. Obtaining access-token and device-id

Brand-new bot accounts need a Matrix access token before ZeroClaw can connect. Element doesn’t expose the token directly, so the canonical path is a one-shot password-login API call that returns both the access token and a stable device ID together.

If your operator account already has a token (e.g. you copied it from another deployment), skip to §4. If you only need to look up the device_id for an existing token, see §5H Option 1 (whoami) or Option 2 (Element).

Step 1 — Mint a token via password login

Run this once. Replace your.homeserver, the bot username, password, and pick any short device_id string (alphanumeric, no spaces — this is the server-side device label that ZeroClaw will reuse on every restart):

curl -sS -X POST "https://your.homeserver/_matrix/client/v3/login" \
  -H "Content-Type: application/json" \
  -d '{"type":"m.login.password","identifier":{"type":"m.id.user","user":"YOUR_BOT_USERNAME"},"password":"YOUR_PASSWORD","device_id":"NEW_DEVICE_ID"}'

响应:

{"user_id": "@bot:example.com", "access_token": "syt_...", "device_id": "NEWDEVICE"}

Step 2 — Apply both values to ZeroClaw

zeroclaw config set channels.matrix.access-token    # paste the access_token (input is masked)
zeroclaw config set channels.matrix.device-id NEWDEVICE
zeroclaw config set channels.matrix.user-id @bot:example.com

Restart for the new values to take effect: zeroclaw service restart.

The wizard (zeroclaw onboard channels) prompts for these same fields if you’d rather work through it interactively.

备注

  • Keep a copy of the token when you first paste it. Secrets are encrypted at rest and zeroclaw config get will print [masked] for the token field; you can’t retrieve it later. Stash it in a scratch note if you’ll need it for the curl validation snippets in §5C.
  • Reuse the same device_id on every restart — changing it forces a new server-side device registration, which breaks key sharing and verification in encrypted rooms. The auto-recovery path in §8 handles the rare cases where wiping is genuinely the right call.
  • Rotating the access token later without re-running the wizard: run zeroclaw config set channels.matrix.access-token (prompts, input masked), then zeroclaw service restart.
  • Token shows as expired or invalid at startup: mint a new one with the same curl, repeat Step 2.

4. Quick validation

Run zeroclaw onboard channels if you haven’t yet, then restart with zeroclaw service restart (background) or zeroclaw daemon (foreground). Send a plain-text message in the configured Matrix room. Confirm:

  • ZeroClaw 日志显示,Matrix 监听器启动时没有重复的同步/认证错误。
  • 在加密房间中,机器人可以读取并回复来自允许用户的加密消息。

5. Troubleshooting “no response”

按顺序处理。

A. 房间与成员

  • 确认机器人账号已加入房间。
  • 如果使用别名(#...),请验证其是否解析为预期的规范房间。

B. 发件人白名单

  • 如果 allowed_users = [],则所有传入消息都会被拒绝。
  • For diagnosis, temporarily open it: run zeroclaw config set channels.matrix.allowed-users '["*"]', then zeroclaw service restart.
  • 待流程正常运行后,再收紧为显式用户 ID。

C. 令牌和身份

关于以下代码片段中的 $MATRIX_TOKEN ZeroClaw 中的机密数据在静态存储时已加密,并且故意支持通过 zeroclaw config get 进行检索——对于任何机密字段,它会输出 [masked]。你有两个选项:

  1. Get a fresh token by re-running the password-login curl from §3 Step 1. Export the access_token it returns. Good for validation and recovery paths — doesn’t affect what’s in your config.
  2. 在首次将令牌粘贴到 zeroclaw onboardzeroclaw config set channels.matrix.access-token 时,请保留一份副本。这是一个一次性副作用——如果需要稍后运行这些 curl 检查,请将其写入临时笔记中。

非秘密字段确实是可检索的:

MATRIX_HOMESERVER=$(zeroclaw config get channels.matrix.homeserver)
MATRIX_USER=$(zeroclaw config get channels.matrix.user-id)

当设置了 MATRIX_TOKEN 时,在服务器端验证令牌:

curl -sS -H "Authorization: Bearer $MATRIX_TOKEN" \
  "$MATRIX_HOMESERVER/_matrix/client/v3/account/whoami"
  • 返回的 user_id 必须与机器人账号匹配。
  • If device_id is missing from the response, set it manually (see §5H).
  • Rotate the access token without re-running onboard: zeroclaw config set channels.matrix.access-token (prompts, masked), then zeroclaw service restart.

D. E2EE 特定检查

  • 机器人设备必须已从受信任的设备接收房间密钥。
  • 如果尚未向此设备共享密钥,则无法解密加密事件。
  • 从受信任的 Matrix 会话验证设备信任关系和密钥共享。
  • matrix_sdk_crypto::backups: Trying to backup room keys but no backup key was found — key backup recovery isn’t enabled on this device yet. Non-fatal for message flow; still worth completing (see §5I).
  • 如果收件人将机器人消息显示为“未验证”,请从受信任的 Matrix 会话中验证/签名机器人设备,并确保 device-id 在重启之间保持稳定。

E. 日志级别

ZeroClaw 默认将 matrix_sdkmatrix_sdk_basematrix_sdk_crypto 的日志级别抑制为 warn——它们在 info 级别下会产生大量日志。如需调试,请恢复 SDK 的输出:

RUST_LOG=info,matrix_sdk=info,matrix_sdk_base=info,matrix_sdk_crypto=info zeroclaw daemon

F. 消息格式(Markdown)

  • ZeroClaw 将 Matrix 回复作为支持 Markdown 的 m.room.message 文本内容发送。
  • 支持 formatted_body 的 Matrix 客户端会渲染强调、列表和代码块。
  • 如果格式显示为纯文本:首先检查客户端功能,然后确认 ZeroClaw 正在运行支持 Markdown 输出的 Matrix 构建版本。

G. 全新启动测试

配置更改后,请重启守护进程并发送新消息。旧的时间线历史将不会重新播放。

H. Finding device_id for an existing token

Use this when you already have an access token (e.g. inherited from another deployment) and need to look up its device_id. For brand-new bots, see §3 — the password-login flow there returns both values together.

ZeroClaw 需要一个稳定的 device_id 以支持端到端加密(E2EE)会话恢复。如果没有它,每次重启都会注册新设备,从而破坏密钥共享和设备验证。

选项 1 — whoami(最简单)

curl -sS -H "Authorization: Bearer $MATRIX_TOKEN" \
  "https://your.homeserver/_matrix/client/v3/account/whoami"

如果令牌绑定到设备会话,响应中将包含 device_id

{"user_id": "@bot:example.com", "device_id": "ABCDEF1234"}

If device_id is missing, the token was created without a device login (e.g. via the admin API). Mint a new token + device_id together via §3.

Option 2 — From Element or another Matrix client

  1. 以机器人账户身份登录 Element。
  2. 设置 → 会话。
  3. 复制当前会话的设备 ID。
  4. 应用:
zeroclaw config set channels.matrix.device-id ABCDEF1234

Then zeroclaw service restart. Keep device-id stable — changing it forces a new device registration, which breaks existing key sharing and verification.

H(续)。加密存储删除恢复

症状: 检测到 Matrix 一次性密钥上传冲突;停止同步以避免无限重试循环,通道变得不可用。

原因: 本地加密存储已被删除,而旧设备仍在主服务器上注册了一次性密钥。由于旧密钥仍存在于服务器端,SDK 无法上传新密钥,导致无限的一次性密钥(OTK)冲突循环。

修复 — 全新登录

全新登录会创建一个带有新 device_id 的新设备,从而完全绕过 OTK 冲突(无需通过 UIA 进行设备删除)。

  1. 停止 ZeroClaw。

    zeroclaw service stop
    
  2. 获取一个新的访问令牌和 device_id

    curl -sS -X POST "https://matrix.org/_matrix/client/v3/login" \
      -H "Content-Type: application/json" \
      -d {"type":"m.login.password","identifier":{"type":"m.id.user","user":"YOUR_BOT_USERNAME"},"password":"YOUR_PASSWORD","device_id":"NEW_DEVICE_ID"}
    

    保存返回的 access_tokendevice_id

  3. 删除本地加密存储:

    rm -rf ~/.zeroclaw/state/matrix/
    
  4. 应用新的凭据:

    zeroclaw config set channels.matrix.access-token <new_token>
    zeroclaw config set channels.matrix.device-id <new_device_id>
    
  5. 重启:

    zeroclaw service start
    

首次重启时会出现的情况

  • 我们自己的设备可能已被删除——无害;旧设备已不存在。
  • 解密房间事件失败 — 重置前的旧消息;无法恢复。
  • Matrix E2EE recovery successful — room keys restored from server backup (only if recovery_key is set; see §5I).
  • 新消息解密并正常工作。

预防措施: 在计划重新登录之前,不要删除本地状态目录。如果需要从头开始,请先获取新的凭据,然后删除存储,再更新配置。

I. 恢复密钥(推荐用于端到端加密)

恢复密钥允许 ZeroClaw 从服务器端备份自动恢复房间密钥和交叉签名密钥。设备重置、加密存储删除以及全新安装均可自动恢复——无需表情符号验证,也无需手动共享密钥。

步骤 1 — 从 Element 获取您的恢复密钥

  1. 在 Element(网页版或桌面版)中登录机器人账号。
  2. 设置 → 安全与隐私 → 加密 → 安全备份
  3. 如果已设置备份,则在首次启用备份时会显示您的恢复密钥。如果已保存该密钥,请使用它。
  4. 如果尚未设置备份,请点击“设置安全备份”→“生成安全密钥”。保存该密钥,其格式类似于 EsTj 3yST y93F SLpB ...
  5. 退出 Element。

步骤 2 — 将恢复密钥添加到 ZeroClaw

两种方法均可。对于全新安装,使用引导向导更为简便;而对于现有安装,则推荐使用 zeroclaw config set

选项 A — 在入职期间:

zeroclaw onboard channels

当被提示时:

E2EE recovery key (or Enter to skip): EsTj 3yST y93F SLpB jJsz ...

输入被屏蔽。密钥在静态时已加密。

选项 B — 现有安装:

zeroclaw config set channels.matrix.recovery-key    # 输入被屏蔽

Then zeroclaw service restart. The recovery key is encrypted at rest immediately.

步骤 3 — 重启

zeroclaw service restart

启动时你应该看到:

Matrix E2EE recovery successful — room keys and cross-signing secrets restored from server backup.

从现在开始,即使本地加密存储被删除,ZeroClaw 也会在下次启动时自动恢复。

6. Debug logging

矩阵通道特定的诊断:

RUST_LOG=zeroclaw::channels::matrix=debug zeroclaw daemon

表面:

  • 会话恢复确认
  • 每个同步周期完成
  • OTK 冲突标志状态
  • 健康检查结果
  • 瞬态与致命同步错误分类

对于 SDK 级别的详细信息:

RUST_LOG=zeroclaw::channels::matrix=debug,matrix_sdk_crypto=debug zeroclaw daemon

7. Operational notes

  • 将 Matrix 令牌从日志和屏幕截图中排除。
  • 使用宽松的 allowed_users 配置,在验证完成后收紧为明确的用户 ID。
  • 在生产环境中优先使用规范化的房间 ID,以避免别名漂移。
  • Threading: when channels.matrix.reply-in-thread is true (default), every bot reply lives in a thread rooted at the user’s message. Top-level user messages open a fresh thread; existing threads are continued. The main room timeline only carries the user-initiated messages.
  • Thread root context: the first inbound message ZeroClaw sees in any given thread is prefixed with [Thread root from @sender]: <root body> so the agent has the conversation that triggered the reply. Threads the bot itself started skip the preamble. Tracking is in-memory only — after a daemon restart, the next message in each active thread re-injects the preamble exactly once.
  • Inline-reply media: channels.matrix.mention-only = true makes the bot ignore naked media uploads (no text body to mention against). When the user inline-replies to such a dropped event with a question (@bot can you see this?), ZeroClaw walks the reply’s m.relates_to.m.in_reply_to.event_id, fetches the parent event, and pulls its media into the current message — the agent’s vision pipeline sees the image even though the original upload was filtered out.
  • Attachments thread alongside text: room.send_attachment calls carry an AttachmentConfig::reply(...) with EnforceThread::Threaded when a thread anchor is present, so PDFs / images / voice notes land inside the bot’s thread instead of the main timeline.
  • Outbound media markers: the agent emits [image:url|path], [file:url|path], [voice:url|path], [video:...], [audio:...] (and uppercase / [document:...] aliases) inside its reply text; ZeroClaw fetches the bytes (HTTP for http(s)://, local read otherwise) and uploads as the appropriate Matrix message event. Missing or unreadable targets are non-fatal: the channel logs a warning, drops just that marker, and appends a (note: I couldn't deliver the file at <path>.) line so the operator sees what was attempted instead of a silently-dropped reply.
  • Voice messages (MSC3245): inbound m.audio events carrying the org.matrix.msc3245.voice field are saved to {workspace_dir}/matrix_files/ and run through [transcription] so the agent gets both the transcript text and the source path. Outbound voice notes use the [voice:<url|path>] marker; ZeroClaw uploads as m.audio with the voice flag + zero-waveform set so Element renders the bubble as a voice note. Default transcription provider is Groq’s hosted Whisper API — set transcription.default-provider = "local_whisper" and transcription.local-whisper.url for fully on-device transcription.
  • Acknowledgement reactions: controlled by channels.matrix.ack-reactions (default true). When on, the bot reacts with 👀 while processing and ✅ when done. Set to false to keep rooms reaction-free.
  • Streaming modes (channels.matrix.stream-mode):
    • off (default) — reply posts as a single message once the agent finishes.
    • partial — initial draft posted immediately, edited in place every draft-update-interval-ms as the agent generates output. Tool-execution status is shown by the same edit pipeline.
    • multi_message — no initial draft. Each \n\n-bounded paragraph posts as its own threaded message, separated by multi-message-delay-ms. Code-fence-aware: blank lines inside fenced blocks aren’t treated as paragraph breaks.
  • Persistent sessions: on first successful login, ZeroClaw writes ~/.zeroclaw/state/matrix/session.json (user_id + device_id + access_token + optional refresh_token). Subsequent restarts call restore_session() from that blob — no re-login. The matrix-rust-sdk SQLite crypto store lives alongside it at ~/.zeroclaw/state/matrix/store/. Once session.json exists, rotating access-token in config has no effect until the file is deleted — the saved token wins. Delete session.json to force a re-login from config values.
  • Cross-signing: when recovery-key matches what is sealed in your account’s server-side secret storage, ZeroClaw runs recovery().recover(key) on every startup, the SDK imports your existing master / self-signing / user-signing keys, and the freshly registered device is automatically signed. No bootstrap, no UIA, no key rotation. If your account doesn’t yet have cross-signing set up, generate the recovery key in Element (Settings → Security & Privacy → Secure Backup) before configuring recovery-key.
  • Cron delivery: delivery.to should be a plain room id (!abc:server) or alias (#room:server). Older configs that wrote <sender>||<room> are tolerated — ZeroClaw extracts the last !/#-prefixed segment and warns about the malformed value.

8. Auto-recovery from corrupted local state

The matrix-rust-sdk default SQLite store is single-device and assumes the local view stays in sync with the homeserver. Two failure modes break that assumption irrecoverably; ZeroClaw detects each at startup and (when password + user-id are both configured) auto-wipes ~/.zeroclaw/state/matrix/ and re-authenticates so a fresh device is created server-side.

  • Orphan crypto state. A store/ directory exists but session.json doesn’t (manual cleanup, interrupted prior install, etc.). Logging in fresh on top of orphaned crypto state reproduces Duplicate one-time keys / SigningKeyChanged conflicts that don’t self-heal.
  • StateStoreDataKey::OneTimeKeyAlreadyUploaded flag set. The SDK persists this key into the state store the first time it sees a duplicate-OTK upload (per the SDK’s own comment: “we forgot about some of our one-time keys. This will lead to UTDs.”). It survives restarts; the only fix is wipe and re-register.

device-id drift is detected but tolerated, not wiped. If channels.matrix.device-id differs from the device id stored in session.json, the channel logs a warning and honors the saved id (which is the value the homeserver actually assigned at login). Wiping on drift would create a recovery loop because auto-recovery itself generates a new id, leaving config and session permanently out of sync.

When recover() itself fails (typically MAC check for the secret storage key failed), the channel logs the homeserver’s default secret-storage key id, whether the key event has passphrase info, the whitespace-stripped input length, and the full error chain — these point at which layer rejected the recovery key without leaking the value. Recovery failures are non-fatal (they don’t trigger auto-wipe); the bot continues, the new device just won’t be cross-signed.

If password + user-id aren’t configured, auto-recovery can’t run — the channel bails with an actionable error pointing at the two choices: configure them, or rm -rf ~/.zeroclaw/state/matrix/ manually.

另见