故障排查与升级
tool-bridge 的错误通常落在五层之一:目标/网络、身份、路径授权、节点契约、provider/宿主。按层排查能避免把 404 当宕机、把上游 401 当本地 SK 错误,或为临时问题扩大权限。
先收集最小安全证据
Section titled “先收集最小安全证据”在不泄露 Secret 的前提下记录:
- BaseURL 的 host(不含 query/token);
- 宿主类型和对应版本;
- 当前 profile 名、SK id/owner(不含明文);
- 请求 method/path、HTTP status、TBError code、retryable;
- 节点 kind、工具名、schema digest/版本线索;
- 时间、trace id、设备/plugin/remote 的公开标识;
- 能否由最小只读请求复现。
不要粘贴 Authorization、Cookie、OAuth code/state、SecretStore 值、完整敏感 arguments、环境文件或平台资源凭证。
tb whoamitb status --jsontb help --jsontb tree --depth 2 --jsonTARGET_PATH=tools/docstb help "$TARGET_PATH" --json如果 CLI 自身不可用,先用 HTTP:
curl -i https://tb.example.com/healthz
curl -i \ -H "Authorization: Bearer $TB_SK" \ -H "Accept: application/json" \ https://tb.example.com/~helphealthz 200 但 ~help 失败,说明进程存活但认证、bootstrap 或数据面仍有问题。
CLI 的失败会归一为结构化错误 {ok:false, error, code, retryable?}。加 --json 时它以单行 JSON 写入 stdout(人类模式才写 stderr 的 error: <message>),退出码为 1。脚本排障应按 code 分支,而不是解析人类文案;retryable 提示是否值得退避重试。下面各段的 7 个 code 覆盖服务端与传输层失败,对 CLI 与 HTTP 通用;但 Commander 的参数解析错误不在其中(code 形如 commander.unknownOption、无 retryable),判 retryable 前先确认 code 不是 commander.*。
按状态码判断第一落点
Section titled “按状态码判断第一落点”401 permission_denied
Section titled “401 permission_denied”含义:Bearer 缺失、未知、disabled、deleted 或 expired。
检查:是否选错 profile/BaseURL;代理是否保留 Authorization;SK 是否被旋转、禁用或删除。各宿主的权威状态都是强一致后端,签发/吊销立即生效,不存在传播窗口,所以 401 应视为该身份当前确实无效,而不是等待同步。不要改成 query token,也不要立即换 Admin SK 掩盖问题。
404 not_found
Section titled “404 not_found”含义:路径确实不存在,或当前身份没有 read 而被可见性隐藏。
从最近可见父路径执行 tb help/tb tree;用管理员身份只做对照,不要让业务客户端依赖管理员结果。tb search 404 还可能仅表示宿主没有 Search capability。
403 permission_denied
Section titled “403 permission_denied”身份有效且资源可见,但缺 call、write、register 或 admin。检查节点 ~help 声明的 scope、SK 的 allow/deny,以及 registerPaths。不要只添加一个宽泛 ** scope;修正最小动作和路径。
400 invalid_argument
Section titled “400 invalid_argument”重新读取命令级 ~help --json(节点级默认是索引,可用 --schemas 一次取回整节点 schema)和当前 CLI --help。检查是否原样调用 cmds[].path、body 是否为 arguments JSON object、路径占位、Context capability、Plugin export、remote host 写法。不要保留旧字段或公开节点信封 fallback。
409 conflict
Section titled “409 conflict”常见于 Context ifVersion 过期、注册路径被另一 key 占用或并发更新。重新读取权威状态,合并后重试;不要直接删除他人节点或覆盖版本。
429 rate_limited
Section titled “429 rate_limited”读取 retryable,指数退避并限制总时长。Feedback 每 owner/path 也有防刷限制。不要并发洪泛或无上限重试。
503/501 unavailable
Section titled “503/501 unavailable”区分消息来源:设备 offline、Secret 无法解析、上游网络、Plugin disabled、对象存储未装配、remote 环/跳数、未实现 capability。retryable 为 false 时先修配置;为 true 时仍要有退避和截止时间。
500 internal
Section titled “500 internal”使用最小无敏感请求复现,保留时间/trace/版本,检查服务日志中的结构化错误。不要把完整 payload 或 headers 打开到 debug 日志。
发现与 Search
Section titled “发现与 Search”正常发现流程与 Feedback/Search 的分工见发现、反馈与协作。
症状:Search 404,但树正常。
根级 POST /~search 是可选宿主能力。使用 tb tree、父路径 tb help;标准 Node 装配 PG Search,自定义 SDK 以实际 ~describe / 宿主配置为准。
症状:Search 找到旧工具或找不到新工具。
Search 是派生索引。直接读取节点/工具 ~help 判断权威工具表;确认节点仍存在且当前 SK 有 read+call。必要时按对应版本管理入口重建/刷新,不要直接修改 PG 索引表。
keyword search 只做字面子串匹配,不翻译、不扩同义词。中日韩文会用短片段补充召回,但跨语言查询没有命中保证;换同一语言的字面关键词,或回到 tree/help。升级到新的派生索引后旧 cursor 可能一次性失效,收到 cursor invalid_argument 时用相同 query、无 cursor 重新开始。
SecretStore 与认证上游
Section titled “SecretStore 与认证上游”先复核密钥、出站身份与安全边界,不要用扩大本地 SK 或匿名降级掩盖上游凭证错误。
症状:错误提到 authRef/skRef 无法解析。
tb secret ls确认引用名存在,但不要尝试读取明文。检查部署是否正确注入 32 字节 base64url 加密主密钥;主密钥错误会使已有密文不可解。声明引用后必须 fail closed,不能删掉 authRef 让请求匿名出去。
症状:挂载时 permission_denied。
绑定任何 authRef/skRef 除目标路径 register 外,还要求 system/secret:admin。这是防止受限注册者借用平台已有 Secret 的安全门。
症状:上游 401/403。
区分本地 HTTP status 与归一后的上游错误。检查 auth header/scheme、凭证过期和上游 scope;本地调用者 SK 永远不应透传上游。
内置集成与 Plugin
Section titled “内置集成与 Plugin”任务步骤分别见使用内置集成目录和注册外部 Plugin。
catalog 找不到 provider:当前宿主没有打包它,改用其他接入方式,不复制别的实例 catalog。
Plugin health 正常但注册失败:~describe 的 protocolVersion、exports、auth、methods/capabilities 或重复 id 不合法。healthz 只检查生命周期端点,不证明 descriptor 合法。
Plugin 注册成功但调用 unavailable:检查 enabled、transport token、endpoint TLS、binding 是否由宿主装配,以及挂载业务 authRef。transport token 与上游 token 不得混用。
多 export 选错:用 tb plugin get <id> 或 tb integration catalog --json 查看逐 export profile;tools/v1 用 tool 节点,context/v1 用 Context 节点。
回滚时先卸载引用节点,再注销 Plugin,最后确认无引用后删除 Secret。
先确认当前是在排查上游挂载还是入站投影;两个方向的完整流程见挂载 MCP 与 MCP 客户端投影。
先确定故障方向:
- 上游挂载:
tb help <mcp-node>是否能 discovery,OAuth/authRef 是否有效; - 入站投影:MCP client 是否连接
/<base>/~mcp,每次请求是否带 scoped SK。
MCP 客户端里的工具名可能是编码后的名称,以 tools/list 为准。tb_search 缺失只表示宿主未启用 Search。上游返回 isError:true 可以是 HTTP 200 的业务错误,不等同于传输失败。
OAuth 问题检查 canonical origin、redirect allowlist 和实际访问域名;上游只接受 loopback 时按当前 tb tool auth --help 判断 --local 是否适用。
声明式 HTTP
Section titled “声明式 HTTP”工具表、凭证与验证步骤见把 HTTP API 声明为工具。
路径参数缺失时比较 pathTemplate 的 {name} 与 arguments/schema。GET/DELETE 剩余参数进 query,POST/PUT 进 JSON body。复杂签名、响应转换或供应商错误语义无法由简单工具表表达时,迁移为 Plugin,不要继续堆静态 header 或隐藏约定。
公网 endpoint 被拒时改用 HTTPS。不要通过旧环境变量尝试开放当前标准服务的 HTTP 边界。
Context
Section titled “Context”Provider capability、乐观并发和 $ref 边界见挂载与使用 Context。
unknown cmd:该 provider 没有相应 handler/capability;重新读 namespace ~help。
写入 403:缺 write 或节点 readOnly。不要因为 CLI 有 ctx put 就假设所有 Context 可写。
conflict:重新 Get 最新 version,再合并重试。
$ref 404/过期:重新 Get 获取新短期 URL;检查节点是否仍存在、TTL 是否过期、对象是否被删除。
R2 provider unavailable:宿主没有注入 ObjectStore 或加密 key 无法生成中转 token。修宿主装配,不改业务 schema。
ctx upload 不存在:namespace 没有披露 direct-upload/create_upload,通常是对象存储没有 presign 能力或挂载只读。不要回退为把任意二进制塞进 ctx put。
直传返回 412/conflict:缺省 grant 使用 If-None-Match: * 防覆盖;只有用户明确要求替换时才重新申请并加 --force。浏览器报 CORS 时检查准确 origin、PUT、Content-Type/If-None-Match 和 ETag,不要放宽为 * 后长期遗留。
Device
Section titled “Device”tb device lsDEVICE_ID=build-01tb tree "device/$DEVICE_ID" --depth 3设备 SK、Shell/文件白名单与容器模式见Device 反向连接。
连接拒绝时检查 register scope、registerPaths、mountPath、URL/hello deviceId 一致。反复重连检查反向代理 WebSocket upgrade、Authorization 和网络 idle timeout。
Shell 默认 allowlist 为空;所有命令拒绝是安全默认。Sidecar 看不到文件时确认双方挂同一个 volume。实时调用 503 retryable 通常表示设备当前不可路由,可以退避等待重连;不要高频轮询、自动改成 --allow '*',也不要把任意失败改成第二次 Mailbox 入队。
如果命令在 ~help 中声明 delivery: mailbox|both,离线设备仍应留在 ~tree 与 Dashboard,调用方可以显式选择 mailbox/fallback。入队返回 unavailable 时检查 受保护 encryption keyring、PG MailboxRepository 和命令 delivery;设备恢复后 operation 不收敛时检查 CLI/daemon 的 device mailbox: 日志,或自定义 SDK 宿主是否在每次 ready 后调用 processor drain() 并使用真正 durable journal。详见Device 离线投递与 Mailbox。
紧急处置先 disable Device SK,再停止进程,最后通过 registry 管理面处理遗留 offline 节点。
Federation
Section titled “Federation”双层身份、host allowlist、skRef 与 Via 保护见Federation 连接多棵树。
remote baseUrl 不在白名单 时先检查:
tb federation ls --jsonallowlist 需要裸 host suffix;env 基线不可通过 API 删除。skRef 失败按 SecretStore 排查。本地 403/404 与远端权限是两层问题,分别用本地 SK 和远端专用 SK 验证。
环/跳数错误表示拓扑有问题,不要先无限提高 maxHops。远端路径、tree/help 响应被当作不可信数据;路径不规范也会得到不可用错误。
Dashboard
Section titled “Dashboard”浏览器身份、本地存储和同源代理要求见使用 Dashboard。
/ui/ 404 通常是 assets 未部署或代理路径错误。页面能加载但 API 401 是浏览器会话/SK 问题。深链刷新失败时只为 /ui/* 配 SPA fallback,不能吞根 ~help、~mcp、POST 数据面或 system/*。
共享电脑使用后清理该 origin 的站点数据;怀疑泄露时立即 disable/rotate SK。
安装、配置与存储
Section titled “安装、配置与存储”| 现象 | 排查方向 |
|---|---|
| health 成功但 ready 为 503 | 是否仍在安装 / 恢复 / 维护 / draining;PG、active S3、已配置 Redis 是否可达 |
| 配对失败或启动即恢复态 | 本机 bootstrap 是否完整,应用是否能写 /data 及 /data/bootstrap;不要放宽权限或生成替代根 |
| 改旧 TB_* 环境变量无效 | 产品设置已改由 PG 管理,使用 tb config 并显式 apply |
| 保存配置后行为没变化 | 比较 desired、effective、revision / appliedRevision 与脱敏 lastError |
| S3 能建桶但不能激活 | 必须通过真实条件写、读取、分页与 probe cleanup;查看 test 结果 |
| 切换 active 后旧对象读取失败 | 旧对象固定原 backendId,确认原后端与凭证仍可用;切换不会搬数据 |
ctx upload unavailable |
标准 Node 无 presigner,不提供平台 Context direct-upload;独立产物用 tb store upload |
| 部署保存后仍 pending | tb deployment agent 是否在选定 Compose 主机运行,是否已有有效 receipt |
| 第二副本拒绝启动 | 共享 initialized bootstrap / PG / S3,并配置 Redis;不能独立 setup 多个 Pod |
| 维护被登记或 fence 阻塞 | 先证明其他副本正常排空;心跳过期不能当作已退出,不盲目删记录 |
当前不交付旧 Cloudflare Workers、SQLite 或服务端文件存储路线,旧页仅保留说明。新部署见Node / Docker,恢复见维护指南。
安全回滚模板
Section titled “安全回滚模板”- 停止新流量或切回旧路径;
- 禁用新节点/Plugin/Device 身份,而不是立刻删全部状态;
- 用
~help和最小 read-only 调用验证旧路径; - 卸载新节点;
- 确认没有引用后再删除 Plugin、allowlist、Secret;
- 对远端/OAuth 凭证在上游侧完成撤销;
- 保存不含 Secret 的证据和对应版本。
项目处于 pre-launch,升级前阅读发布说明并备份状态。不要依赖隐藏旧 flag、旧 wire fallback 或临时共享部署的状态。
升级不是“换一个 tag 后看 /healthz”。每次升级都应重新证明身份、权限、真实 Provider 与恢复路径仍然成立。
通用升级顺序
Section titled “通用升级顺序”- 记录当前宿主、版本或 commit、BaseURL 与不含 Secret 的资源清单;
- 阅读目标版本 release notes,识别用户可感知和存储变化;
- 限制写入,并备份权威状态、对象数据与 SecretStore 加密根;
- 保留旧镜像、旧 commit,明确回滚目标;
- 在隔离或预览环境部署目标版本;
- 用 Admin SK 验证根
~help、status 和实际 capability; - 用受限 SK 验证 allow/deny/404,并完成一个无破坏性的真实 Provider 调用;
- 验证 Dashboard,以及启用时的 Search、MCP、Device 和 Federation;
- 切换正式流量,并观察结构化错误与上游健康;
- 失败时停止新流量,恢复权威状态和旧版本,再重复最小 smoke。
- Node / Docker / Helm:固定原清单与镜像,备份 bootstrap、PG、S3,在隔离目标验证恢复,再按上线检查验收身份与数据。
- 嵌入式 SDK:随应用升级,核对显式 state / objects / 领域仓库依赖,测试本地工具、远程连接与可选 Mailbox。
- 旧宿主迁移:当前没有旧数据格式与 env 配置自动导入,保留原实例和备份,不把更换镜像或连接地址当作迁移成功。
仍无法定位时
Section titled “仍无法定位时”准备一份最小报告:宿主/版本、无敏感的复现命令、status/code/retryable、期望与实际、相同 SK 的父路径 ~help 结果、是否可在本地/另一宿主复现。源码问题可提交到 tool-bridge GitHub,提交前再次清除 token、URL query 和业务数据。
- 恢复后重新执行生产上线检查清单;
- 权限或 404 不符合预期:回到权限、SK 与可见性;
- 动态 schema 或调用形状不明确:回到从
~help到调用; - CLI 和 HTTP 结果不一致:对照
tbCLI与运行时 HTTP 参考。