跳转到内容

Device 反向连接

Device 让本机主动建立 WebSocket,把本地 Shell、文件或 SDK provider 挂到远端树。网关不需要反向访问内网,适合开发机、构建节点、内网服务和 Kubernetes Sidecar。

适合:

  • 能力只存在于内网或本机;
  • 希望连接断开时节点明确显示 offline;
  • 希望用独立 Device SK 限制注册位置和调用范围;
  • 容器或 Sidecar 可以稳定运行一个长驻进程。

不适合:

  • 可公开托管的标准 MCP/HTTPS 服务;
  • 需要网关主动扫描网络;
  • 无法接受长连接和本机命令执行风险的环境。

不要把 Admin SK 放进设备。设备 SK 至少需要允许目标路径的注册,并用 registerPaths 收紧位置。具体 tb sk create 参数以当前 CLI 帮助为准;一个典型意图是:

owner: device:build-01
registerPaths: [device/build-01]
scopes:
- device/build-01/**: read,call,register

registerPaths 只是额外收紧,不能替代 register scope。管理员还应决定谁能对设备路径 readcall

可直接照做的设备 SK 签发与验证流程见权限、SK 与可见性。设备 SK 与上游 Secret 的区别见密钥、出站身份与安全边界

Shell 默认会创建,但 allowlist 缺省为空,所以所有命令均拒绝。只暴露确实需要的命令:

Terminal window
tb connect \
--device-id build-01 \
--path device/build-01 \
--allow uname \
--allow git \
--fs ./shared \
--fs-readonly

如果完全不需要 Shell:

Terminal window
tb connect \
--device-id docs-reader-01 \
--path device/docs-reader-01 \
--no-shell \
--fs ./docs \
--fs-readonly

tb connect 是前台长驻命令,自带心跳和网络闪断重连;进程崩溃后的拉起交给 systemd、Docker 或 Kubernetes。--timeout 是 HTTP 单请求参数,不适用于长驻连接,CLI 会拒绝它。

连接成功会输出确认后的 mountPath。另一个有权限的身份可以检查:

Terminal window
tb device ls
tb tree device/build-01 --depth 3
tb help device/build-01/shell
tb help device/build-01/fs

调用 Shell 前先读取 ~help 的参数 schema,再使用信封调用。文件系统作为 Context 暴露,使用 tb ctx ls/cat 等命令;只读挂载不应披露写动词。

成功证据不仅是“WebSocket 已连接”:

  1. CLI 收到 ready,mountPath 与预期一致;
  2. tb device ls 显示 online;
  3. 树中只有预期的 shell/fs 节点;
  4. allowlist 内命令成功,未授权命令被拒;
  5. --fs-readonly 下写动词不存在或被拒;
  6. 使用越界 mountPath 的测试连接被 registerPaths 拒绝。

官方 CLI 镜像包含完整 tb 命令。作为长驻设备时,用运行时 Secret 注入 BaseURL/SK,并同时收紧宿主 volume:

Terminal window
docker run -d \
--name tb-device \
--restart unless-stopped \
-e TB_BASE_URL=https://tb.example.com \
-e TB_SK="$TB_DEVICE_SK" \
-v "$PWD/shared:/workspace:ro" \
ghcr.io/tokenrollai/tool-bridge-cli:0.17.0 \
connect \
--device-id build-01 \
--path device/build-01 \
--allow uname \
--fs /workspace \
--fs-readonly

0.17.0 是一个明确版本示例;部署时替换为你已经阅读发布说明并验证过的版本或 digest,不要改回浮动 tag。

生产环境固定版本或 digest,不使用 edge。Sidecar 只能看到自己的根文件系统以及与业务容器显式共享的 volume;同 Pod 共享网络不等于共享文件。

SDK 的 connect() 默认把本实例通过 registerTool/registerContext 登记的节点和工具表上报:

const connection = tb.connect(
'https://tb.example.com',
process.env.TB_DEVICE_SK!,
{ deviceId: 'my-service-01' },
)
await connection.ready

长驻服务应显式设置稳定 deviceId,避免重启后出现新的身份。SDK 默认 expose 面向自定义 nodes;CLI 才提供现成 Shell 和文件 executor。

现象 处理
ready 前收到权限拒绝 检查 register scope、registerPaths、mountPath 和 deviceId
设备反复 reconnect 检查代理是否保留 WebSocket upgrade、Authorization,以及网络空闲策略
Shell 所有命令都拒绝 缺省 allowlist 为空;显式增加最小 --allow
Sidecar 看不到业务文件 两个容器没有挂同一个 volume,或挂载路径不同
文件能读不能写 使用了 :ro/--fs-readonly,这是预期安全行为
调用返回 503 retryable 设备当前 offline;等待重连,不要无限高频重试
相同 deviceId 互相替换 每个实例应使用唯一、稳定 ID;旧 generation 不会完成新调用

正常停止 tb connect 或终止容器即可关闭连接。节点会标为 offline,并由宿主按回收策略清理;不要假设断线立即永久删除树记录。

Terminal window
docker stop tb-device
tb device ls

紧急吊销时先禁用或删除该 Device SK,阻止重连;再由管理员通过当前实例的 registry 管理面清理遗留节点。清理命令和权限以 tb help system/registry 为准,不要使用不存在的 tb device rm

  • 只暴露文件:优先 --no-shell --fs ... --fs-readonly
  • 能力已有公网 HTTPS/MCP:改用MCP 挂载
  • 需要嵌入业务进程:阅读嵌入现有应用,并核对目标版本类型定义;
  • 生产 Sidecar 的镜像、架构和 CA 证书要求以当前 tb CLI 容器文档与 tb connect --help 为准;
  • ready、WebSocket 或 offline 状态异常:进入故障排查与升级