文档 · 开发者
08 错误码、版本策略与安全注意事项
错误码表、版本与兼容策略、安全注意事项。
文档来源本栏目是公开仓库 rfdiosuao/lom-desktop 中
docs/open/phone-access/ 的原文,提交 92044e2;本页对应 08-errors-security.md。以 GitHub 上的最新版为准。错误码表
错误码格式统一为 <域>.<原因>。按 code 做逻辑判断,不要解析 error 文字。 文案栏给出推荐的用户可见中文提示(来自云端 / 桌面的实际文案),你也可以映射成自己的文案或多语言文案。
A. 云端 HTTP 接口返回的错误
响应体:{ "success": false, "code": "…", "error": "…" },HTTP 状态码见表。
| 错误码 | HTTP | 出现在 | 含义 | 推荐文案 | 手机应该做什么 |
|---|---|---|---|---|---|
pairing.qr_invalid | 403 | inspect / claim | claim_token 错误(同一会话累计 5 次作废) | 这不是有效的麓鸣配对码。 | 提示重新扫码 |
pairing.not_found | 404 | 配对各接口 | 会话不存在 | 配对会话不存在,请在电脑上刷新。 | 提示在电脑上刷新二维码 |
pairing.expired | 410 | 配对各接口 | 会话已过期(创建后 5 分钟) | 配对码已过期,请在电脑上刷新。 | 同上,并使确认按钮失效 |
pairing.cancelled | 409 | 配对各接口 | 会话已被作废 / 取消 | 配对已取消,请在电脑上刷新。 | 同上 |
pairing.failed | 409 | 配对各接口 | 桌面核对 key_proof 失败,或 claim_token 错误过多 | 配对校验失败,请刷新二维码重试。 | 同上 |
pairing.already_claimed | 409 | inspect / claim / 其他 | 会话已被认领或已确认 | 这个配对码已被使用,请在电脑上刷新。 | 同上 |
pairing.awaiting_desktop | 409 | confirm | 扫码路径:桌面还没核对完 key_proof | 正在等待电脑核对,请稍候。 | 每秒重试,最多 15 次(03) |
pairing.secret_invalid | 403 | confirm / cancel | claim_secret 不对 | 配对凭据无效,请重新扫码。 | 重新开始配对 |
pairing.code_invalid | 400 | claim-by-code | 配对码格式不对 / 不存在 / 已过期 / 已被认领 | 配对码不正确。 | 让用户重新输入 |
pairing.request_invalid | 400 | claim / claim-by-code | 请求字段非法(公钥串不合法、与桌面公钥相同、key_proof 长度不对…) | 配对请求格式不正确 | 属于实现错误,检查 04 |
pairing.rate_limited | 429 | inspect / claim / claim-by-code | 同一 IP 每分钟认领类请求超过 10 次 | 尝试次数过多,请稍后再试。 | 提示稍后再试,下一分钟再发 |
device.owned_by_other_account | 409 | claim / confirm | 这组公钥已属于另一个账户 | 这台手机已连接到另一个账户,请先在手机上解除配对。 | 引导用户先在原账户移除这台手机 |
sub.phone_limit | 409 | confirm | 账户订阅可接入的手机台数已满 | 已达到手机上限,升级订阅可以连接更多手机。 | 提示用户在电脑上升级订阅或移除旧手机 |
relay.unauthorized | 401 | 设备凭据请求头缺失 / 格式错误;WebSocket 握手令牌无效或过期 | 凭据或令牌无效 | 中转连接凭据无效,请重新连接。 | 握手时:重新换中转令牌再连;请求头问题:修实现 |
relay.device_revoked | 401 | 换令牌、握手 | 设备凭据无效 / 过期 / 设备已被移除 | 这台手机已解除配对,请重新扫码连接。 | 清除凭据,回到未配对,不再重连(05 §6) |
server.internal | 500 | 任意 | 云端内部错误 | 服务暂时不可用,请稍后重试。 | 退避重试 |
非 JSON 响应(网关错误页、框架级 4xx / 5xx)按失败处理,映射为本地
pairing.response_invalid,退避重试。B. 桌面经云端调用手机时可能出现的云端错误(手机不会收到,列出便于理解桌面表现)
| 错误码 | HTTP | 含义 |
|---|---|---|
device.not_found | 404 | 设备不存在,或不属于该账户,或 recipient 与设备登记的公钥串不一致 |
relay.invalid_request | 400 | 信封格式无效(id 不是 32 位十六进制、sender 不是 86 字符等) |
relay.offline | 409 | 手机当前没有 WebSocket 连接(云端不排队)。「手机离线,请打开手机应用。」 |
relay.duplicate_request | 409 | 同一个信封 id 正在等待回复 |
relay.busy | 429 | 该手机在途请求已达 8 个 |
relay.disconnected | 502 | 请求发出后、回复前手机连接断了——执行结果未知 |
relay.reply_timeout | 504 | 15 秒内没收到回复——执行结果未知(含手机丢弃了不可信信封、回复太大被丢弃等情形) |
C. 手机本地错误(不来自云端,建议使用同名错误码)
| 错误码 | 含义 | 推荐文案 |
|---|---|---|
pairing.qr_invalid | 二维码格式 / 版本 / 主机白名单 / 字段长度不合法(此时不发网络请求) | 这不是有效的麓鸣配对码。 |
pairing.key_mismatch | inspect 返回的桌面公钥指纹与二维码 f 不一致,或 pairing_id 对不上 | 配对信息不一致,请重新扫码。 |
pairing.already_paired | 手机已配对,需先解除 | 这台手机已经连接了一台电脑,请先解除配对。 |
pairing.desktop_timeout | awaiting_desktop 重试 15 次仍未通过 | 电脑迟迟没有确认,请重新扫码。 |
pairing.store_failed | 凭据落盘失败 | 保存连接信息失败,请重试。 |
pairing.response_invalid | 响应不是预期的 JSON / 缺字段 | 服务返回了无法识别的内容,请稍后重试。 |
net.unreachable | 网络不可达 / 超时 | 无法连接麓鸣服务,请检查网络。 |
D. 手机在 RPC 回复里返回的错误码
信封内的 RpcResponse.error(06 §7):
| 类别 | 错误码 | 定义位置 |
|---|---|---|
| 协议级 | remote.expired remote.revoked remote.replayed remote.not_ready remote.method_unknown remote.operation_failed | 06 §7 |
| 通用 | sync.conflict sync.invalid_name sync.document_too_large sync.write_failed | 07 |
| 各方法 | model.* chat.* file.* image.* screen.* skill.* schedule.* subagent.* | 各方法小节末尾 |
桌面侧另有一些只在桌面本机产生的错误码(如 remote.repair_required「本机未找到该手机的配对公钥,请重新配对」、remote.trust_store_unavailable、remote.trust_store_invalid),它们说明桌面端的钥匙串状态,与手机实现无关。
版本与兼容策略
当前协议版本是 v=1。版本号出现在这些地方,它们将一起演进:
| 位置 | 取值 |
|---|---|
二维码 / 深链 v 字段 | 1 |
| 密码学域分隔字符串 | LOM-KEY-FP-V1、LOM-PAIR-PROOF-V1、LOM-PAIR-SAS-V1、LOM-REMOTE-V1 |
中转控制消息的 v 字段 | 1(hello、device.revoked) |
| 测试向量文件名 | pairing-v1.json、remote-envelope-v1.json |
兼容原则(面向第三方的约定):
v=1范围内只做向前兼容的增量变化:新增 RPC 方法、在请求 / 响应里新增字段、新增中转控制消息类型,都不会改变已有字段的含义。- 你必须这样写,才能吃到这个保证:
- 解析请求
payload、解密后的RpcRequest、中转ctrl消息时,忽略不认识的字段和type; - 对不认识的
method返回remote.method_unknown,不要崩溃、不要静默忽略; - 回复信封只含六个字段(云端对信封做严格解析);
- 不要依赖字段顺序,不要依赖云端返回字段的「只有」这几个。
- 解析请求
- 破坏性变化会带新版本号:任何改变既有算法、编码或信封字段的修改,会同时提升二维码
v和对应的-V1后缀,并在本目录公布新文档与新向量。 - 手机遇到不认识的二维码版本(
v不是1):协议层按无效处理、不发请求;界面上建议提示「二维码版本较新,请升级 App」而不是「无效」。 - 手机落后于桌面的常见情形是桌面新增了你没实现的可选方法——桌面按 07 §1.3 的方式降级展示,不会因此无法使用。建议实现可选方法
device.capabilities,主动声明已实现的方法,桌面就能提前隐藏或置灰不支持的入口。
向后兼容的具体期限(
v=2 发布后 v=1 保留多久)尚未对外承诺,将在 v=2 方案公布时一并说明。安全注意事项
信任模型
- 手机只信任配对时用户亲手批准的桌面公钥。验签、解密、执行,每一步都建立在「发送方
sender在已批准集合里」之上。不要根据云端的设备列表、其他消息或任何网络来源追加信任。 - 官方手机端同一时刻只配对一台桌面:要换桌面,先解除配对再重新扫码。如果你设计成可信多台桌面,也必须让每一台都经过完整的扫码 / 确认码核对,并给用户查看与单独撤销某台桌面的入口。
- 云端不是可信方:它能看到密文、会话元数据(谁在线、请求大小与时间),但看不到明文,也无法伪造桌面的签名。配对阶段靠
psk指纹 / 确认码防止云端换成自己的公钥。
秘密的生命周期
| 秘密 | 存放 | 禁止 |
|---|---|---|
| 身份私钥(X25519 / Ed25519) | Keystore 包装的存储,noBackupFilesDir | 上传、日志、备份、截图 |
psk、claim_token、claim_secret | 仅内存,配对结束即丢弃 | 持久化、日志、URL |
device_token | Keystore 保护的存储 | 日志、URL、崩溃报告 |
relay_token | 仅内存 | 日志、URL |
model.sync 的 api_key | 加密凭据库(按桌面公钥隔离) | 日志、会话缓存、错误信息、转发给任何其他服务 |
- 所有 HTTP 请求只用 HTTPS / WSS,使用系统证书校验;
- 令牌放请求体或请求头,不放 URL;
- 日志里只允许出现 ID(
pairing_id、device_id、信封id的前几位)、长度、时间、错误码,不允许出现任何令牌、密钥、明文载荷、桌面发来的提示词; - 所有令牌 / 指纹 /
key_proof比较用恒定时间比较。
配对阶段
- 二维码视同临时口令:5 分钟、一次性;主机白名单必须写死在 App 里;
- 来自深链、相册、他人转发的二维码,一律走完整校验并要求用户在手机上确认,不能自动完成;
- 扫码路径必须核对指纹,配对码路径必须让用户比对确认码;
- 已配对时拒绝再次配对(先解除)。
RPC 阶段
- 先验签再解密,验签 / 信任失败不回复(06 §5);
- 严格执行 5 分钟时间窗口,并把去重表持久化;
- 请求里的一切参数都是不可信输入:文件名、路径、ZIP 条目、坐标、包名、会话 ID 都要按 07 的规则校验;绝不执行桌面传来的 shell 命令、不接受任意文件路径;
- 有副作用的方法(
screen.control、schedule.run、chat.send、skills.install、file.save_gallery)必须按幂等键持久化收据,宁可返回「结果未知」也不要重复执行; - 屏幕类方法:密码输入框内容一律脱敏;系统禁止截屏的窗口返回失败而不是绕过;
- 回复大小要自己限制(≤ 350,000 字节明文),不要依赖云端截断;
- 建议在界面上让用户看得到:当前被哪台电脑授权、最近是否被远程操作,并提供随时「解除配对」的入口。
其他
- 手机时钟需基本准确(偏差超过 5 分钟会让所有请求被
remote.expired拒绝); - 解除配对后,清除设备凭据与桌面公钥;用户的会话、人格、记忆、技能保留在手机上,不要删除。