Device 反向连接
Device 让本机主动建立 WebSocket,把本地 Shell、文件或 SDK provider 挂到远端树。网关不需要反向访问内网,适合开发机、构建节点、内网服务和 Kubernetes Sidecar。
什么时候使用
Section titled “什么时候使用”适合:
- 能力只存在于内网或本机;
- 希望连接断开时节点明确显示 offline;
- 希望用独立 Device SK 限制注册位置和调用范围;
- 容器或 Sidecar 可以稳定运行一个长驻进程。
不适合:
- 可公开托管的标准 MCP/HTTPS 服务;
- 需要网关主动扫描网络;
- 无法接受长连接和本机命令执行风险的环境。
前置条件:专用最小权限 SK
Section titled “前置条件:专用最小权限 SK”不要把 Admin SK 放进设备。设备 SK 至少需要允许目标路径的注册,并用 registerPaths 收紧位置。具体 tb sk create 参数以当前 CLI 帮助为准;一个典型意图是:
owner: device:build-01registerPaths: [device/build-01]scopes: - device/build-01/**: read,call,registerregisterPaths 只是额外收紧,不能替代 register scope。管理员还应决定谁能对设备路径 read 和 call。
可直接照做的设备 SK 签发与验证流程见权限、SK 与可见性。设备 SK 与上游 Secret 的区别见密钥、出站身份与安全边界。
安全地暴露 Shell 与文件
Section titled “安全地暴露 Shell 与文件”Shell 默认会创建,但 allowlist 缺省为空,所以所有命令均拒绝。只暴露确实需要的命令:
tb connect \ --device-id build-01 \ --path device/build-01 \ --allow uname \ --allow git \ --fs ./shared \ --fs-readonly如果完全不需要 Shell:
tb connect \ --device-id docs-reader-01 \ --path device/docs-reader-01 \ --no-shell \ --fs ./docs \ --fs-readonlytb connect 是前台长驻命令,自带心跳和网络闪断重连;进程崩溃后的拉起交给 systemd、Docker 或 Kubernetes。--timeout 是 HTTP 单请求参数,不适用于长驻连接,CLI 会拒绝它。
连接成功会输出确认后的 mountPath。另一个有权限的身份可以检查:
tb device lstb tree device/build-01 --depth 3tb help device/build-01/shelltb help device/build-01/fs调用 Shell 前先读取 ~help 的参数 schema,再使用信封调用。文件系统作为 Context 暴露,使用 tb ctx ls/cat 等命令;只读挂载不应披露写动词。
成功证据不仅是“WebSocket 已连接”:
- CLI 收到 ready,mountPath 与预期一致;
tb device ls显示 online;- 树中只有预期的 shell/fs 节点;
- allowlist 内命令成功,未授权命令被拒;
--fs-readonly下写动词不存在或被拒;- 使用越界 mountPath 的测试连接被
registerPaths拒绝。
在容器中运行
Section titled “在容器中运行”官方 CLI 镜像包含完整 tb 命令。作为长驻设备时,用运行时 Secret 注入 BaseURL/SK,并同时收紧宿主 volume:
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-readonly0.17.0 是一个明确版本示例;部署时替换为你已经阅读发布说明并验证过的版本或 digest,不要改回浮动 tag。
生产环境固定版本或 digest,不使用 edge。Sidecar 只能看到自己的根文件系统以及与业务容器显式共享的 volume;同 Pod 共享网络不等于共享文件。
使用 SDK 暴露本地 provider
Section titled “使用 SDK 暴露本地 provider”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,并由宿主按回收策略清理;不要假设断线立即永久删除树记录。
docker stop tb-devicetb device ls紧急吊销时先禁用或删除该 Device SK,阻止重连;再由管理员通过当前实例的 registry 管理面清理遗留节点。清理命令和权限以 tb help system/registry 为准,不要使用不存在的 tb device rm。