OpenClaw Tlon/Urbit 渠道插件:私网 Ship 接入、群组授权与 Owner 审批流完整指南
【免费下载链接】openclawThe AI that really does things. Any OS. Any Platform. The lobster way. 🦞项目地址: https://gitcode.com/GitHub_Trending/cl/openclaw
本文基于 OpenClaw 仓库中的 Tlon 渠道文档 与 extensions/tlon 插件源码,完整覆盖 Tlon/Urbit 渠道的安装、登录认证、私网 SSRF 放行、群组发现与授权、Owner 审批系统与设置热重载的全部配置细节,并结合插件源码说明审批队列、SSRF 策略和设置存储的底层实现机制,帮助你在本地或私有网络中稳定运行一个带访问控制的 Tlon 机器人。
1. Tlon 渠道是什么,能力边界在哪
Tlon 是构建在 Urbit 上的去中心化即时通讯应用。OpenClaw 通过 Tlon 渠道插件连接到你的 Urbit ship(相当于一个去中心化主机实例),响应私聊(DM)和群组频道消息。群聊回复默认要求 @ mention,并在其上叠加了授权规则和 Owner 审批流。
能力现状(来自 docs/channels/tlon.md 的能力矩阵):
| 功能 | 状态 |
|---|---|
| 私聊(DM) | 支持 |
| 群组/频道 | 支持(默认需要 mention 才回复) |
| 线程(Threads) | 支持(机器人一旦参与过该线程,后续消息免 mention 继续回复) |
| 富文本 | Markdown 转换为 Tlon 原生格式(粗体、斜体、代码、标题、列表) |
| 图片 | 入站下载、出站上传 |
| 表情回应(Reactions) | 仅可通过捆绑的 skill 操作 |
| 投票(Polls) | 不支持 |
| 原生命令 | 默认仅 Owner 可用 |
插件为随版本捆绑发布:当前 OpenClaw 的打包版本内置 Tlon,无需单独安装。在较旧版本或自定义安装中可通过 npm 安装:
openclaw plugins install @openclaw/tlon建议使用裸包名以跟踪当前发布标签;只有需要可复现安装时才固定版本(@openclaw/tlon@x.y.z)。从本地检出目录安装:
openclaw plugins install ./path/to/local/tlon-plugin从插件清单 openclaw.plugin.json 可以看到,插件声明了channels: ["tlon"]、configRepair的 doctor 契约,并捆绑了@tloncorp/tlon-skill这个技能包。
2. 初始接入:channels add 与配置文件两种方式
最快的接入方式是 CLI 向导:
openclaw channels add --channel tlon --ship ~sampel-palnet --url https://your-ship-host --code lidlut-tabwed-pillex-ridrup也可以直接编辑配置文件:
{ channels: { tlon: { enabled: true, ship: "~sampel-palnet", url: "https://your-ship-host", code: "lidlut-tabwed-pillex-ridrup", ownerShip: "~your-main-ship", // 推荐:你的 ship,始终被授权 }, }, }登录 code 的获取与轮换:code就是 ship 的 web 登录 code,在 ship 的 dojo 里执行+code打印当前值。code 会轮换,一旦认证开始失败就重新读取。
认证链路源码佐证:从 extensions/tlon/src/urbit/auth.ts 可以看到,认证实现就是向POST /~/login提交password=code表单,成功响应后从Set-Cookie头取出会话 cookie;拿不到 cookie 会抛出missing_cookie类型的UrbitAuthError。请求默认 15 秒超时、最多 3 次重定向,并携带tlon-urbit-login审计上下文——这正是文档中"认证失败就重新+code"的底层原因:code 过期时登录请求会直接失败。
配置修改遵循 Gateway 的 hot reload 机制。改完配置后执行openclaw channels status --probe验证(Gateway 离线时先启动),然后 DM 机器人或在群里 @ 它。
3. 私网 / 局域网 Ship:SSRF 防护的显式放行
OpenClaw 默认拦截私有/内网主机名和 IP 段以防 SSRF 攻击。如果你的 ship 运行在私有网络(localhost、局域网 IP、内网域名),必须显式声明信任:
{ channels: { tlon: { url: "http://localhost:8080", network: { dangerouslyAllowPrivateNetwork: true, }, }, }, }该开关覆盖http://localhost:8080、http://192.168.x.x:8080、http://my-ship.local:8080这类目标。只对完全信任的 ship URL 开启——它会让该账号的 HTTP 请求失去 SSRF 保护。
注意:旧的扁平键channels.tlon.allowPrivateNetwork已废弃,openclaw doctor --fix会自动把它迁移到channels.tlon.network.dangerouslyAllowPrivateNetwork(插件通过 doctor.ts 注册的 legacy config 规则实现该迁移)。
源码层面的双重校验:
- extensions/tlon/src/urbit/base-url.ts 中,
validateUrbitBaseUrl会拒绝非 http/https 协议、URL 内嵌凭据、把 base URL 归一化为纯 origin(防止夹带路径/查询参数),再调用isBlockedUrbitHostname→ 插件 SDK 的isBlockedHostnameOrIp判断主机名是否在拦截名单内; - 拦截判断发生在
urbitFetch层,dangerouslyAllowPrivateNetwork: true最终转化为ssrfPolicy: { allowPrivateNetwork: true }透传给每次请求——extensions/tlon/src/urbit/auth.ssrf.test.ts 等测试专门验证了"默认拦截私网、开启后放行"的行为边界。
4. 群组频道:手动固定与自动发现
两种管理群组频道的方式,可并用:
{ channels: { tlon: { groupChannels: ["chat/~host-ship/general", "chat/~host-ship/support"], autoDiscoverChannels: true, }, }, }groupChannels是手动固定的频道 nest 列表,格式为chat/~host-ship/channel;autoDiscoverChannels在配置文件未显式设置时默认为false;而 setup 向导会把该提示的默认回答设为 yes 并显式写入true。开启后,插件在启动时 scry 已加入的群组、监听随群组邀请被接受而新出现的频道,并且每 2 分钟复查一次。
频道 nest 的解析规则可以在 extensions/tlon/src/targets.ts 中确认:parseChannelNest用正则^chat\/([^/]+)\/([^/]+)$提取 host ship 与频道名,ship 名会自动补~前缀归一化。
5. 访问控制:DM 白名单与逐频道授权
DM 白名单(空 = 除ownerShip外任何 DM 都不允许):
{ channels: { tlon: { dmAllowlist: ["~zod", "~nec"], }, }, }群组授权默认每个频道为restricted模式。用defaultAuthorizedShips设基线,再按频道 nest 覆盖:
{ channels: { tlon: { defaultAuthorizedShips: ["~zod"], authorization: { channelRules: { "chat/~host-ship/general": { mode: "restricted", allowedShips: ["~zod", "~nec"], }, "chat/~host-ship/announcements": { mode: "open", }, }, }, }, }, }源码级解析逻辑:extensions/tlon/src/monitor/authorization.ts 的resolveChannelAuthorization实现了三级回退——
- 先取设置存储(settings store)中的
channelRules,没有再取文件配置中的channelRules; mode未匹配规则时回落到"restricted"(即文档说的"默认 restricted");allowedShips未指定时回落到defaultAuthorizedShips(同样是"设置存储优先、文件配置兜底"的顺序)。
线程续答与 mention 门控:机器人一旦在某个线程里回复过,后续该线程内的消息就无需再次 mention 也会继续响应。想强制每次显式 mention 时:
{ channels: { tlon: { implicitMentions: { threadParticipation: false }, }, }, }多账号场景用channels.tlon.accounts.<id>.implicitMentions覆盖。另外 Tlon 目前不产生replyToBot/quotedBot事实,这两个 flag 在 Tlon 渠道上不生效。
6. Owner 与审批系统
配置ownerShip:
{ channels: { tlon: { ownerShip: "~your-main-ship", }, }, }Owner ship 处处被授权:DM 邀请总是自动接受、群组邀请总是自动接受、频道消息总是通过授权——它不需要出现在dmAllowlist、defaultAuthorizedShips或groupInviteAllowlist里。
关键在于:设置ownerShip后,未授权请求不会被直接丢弃,而是排队等待审批并 DM 通知 Owner。会触发待审批请求的场景:
- 来自不在
dmAllowlist中的 ship 的 DM 请求; - 在发送者未通过授权的频道中的 mention;
- 来自不在
groupInviteAllowlist中的 ship 的群组邀请(在自动接受关闭、或开启但邀请人不在白名单时)。
Owner 在 DM 中回复即可处理请求:
| Owner 回复 | 效果 |
|---|---|
approve/deny/block | 作用于最近一条待审批请求 |
approve <id>/deny <id> | 按 id 处理指定请求 |
block | 同时以 Tlon 原生方式封禁该 ship,使其无法重连 |
unblock ~ship | 解除原生封禁 |
blocked | 列出当前被封禁的 ship |
pending | 列出现有待审批请求 |
审批机制源码实现(extensions/tlon/src/monitor/approval.ts):
- 待审批对象带唯一 id,格式为
{type}-{timestamp}-{shortHash}(generateApprovalId,L38-L42),type为dm/channel/group三类; parseApprovalResponse(L113-L126)用正则^(approve|deny|block)(?:\s+(.+))?$解析 Owner 回复,支持带 id 与不带 id(取最近一条,见findPendingApprovalL140-L149);parseAdminCommand(L209-L229)解析unblock ~ship、blocked、pending三类管理命令,ship 名正则限定为~[\w-]+;- 每个
PendingApproval除了 id、类型、请求方 ship 外,还保留originalMessage完整上下文(消息 id、文本、内容、时间戳、父消息 id、是否线程回复),用于审批通过后的消息补发。
未配置ownerShip时,未授权 DM 和频道 mention 只会被丢弃并记日志,没有任何审批提示。
7. 自动接受(Auto-accept)策略
自动接受 DM 邀请(仅针对已在dmAllowlist中的 ship;Owner 无论该 flag 如何都总是自动接受):
{ channels: { tlon: { autoAcceptDmInvites: true, }, }, }自动接受群组邀请(fails closed 语义:autoAcceptGroupInvites: true且groupInviteAllowlist为空时,任何非 Owner 邀请都不会被接受):
{ channels: { tlon: { autoAcceptGroupInvites: true, groupInviteAllowlist: ["~zod"], }, }, }8. 入站持久化与投递语义
这部分常被忽略,但对生产部署很重要(来自 docs/channels/tlon.md 的 Inbound durability 一节):
- OpenClaw 在把已接受的 Tlon DM/群事件派发给 agent 之前先持久化;pending 或可重试的轮次能扛过 Gateway 重启;
- 工作按"群组频道"或"DM 对端"为单位串行处理;
- 稳定的 Urbit 消息 id 会在队列记录或保留的完成记录存在时抑制重复投递的事件;
- 从队列到 agent 的边界是at-least-once投递:handoff 期间崩溃可能重放一个轮次,因此产生外部副作用的 agent 动作应尽量保持幂等。
9. 通过 Urbit 设置存储实现配置热重载
上面大部分设置(dmAllowlist、groupInviteAllowlist、groupChannels、defaultAuthorizedShips、autoDiscoverChannels、autoAcceptDmInvites、autoAcceptGroupInvites、ownerShip、showModelSignature)首次运行时会被镜像写入 ship 的%settingsagent(deskmoltbot,buckettlon),之后从那里实时读取——所以经由 Landscape 客户端或捆绑 skill 的设置命令所做的修改无需重启 Gateway即生效。channelRules和待审批列表也作为 JSON 持久化在该处;文件配置对"从未写入设置存储的值"仍是唯一事实来源。
实现细节(extensions/tlon/src/settings.ts):
- 初始加载通过
scry("/settings/all.json"),响应形状为{ all: { [desk]: { [bucket]: { [key]: value } } } }(L332-L349); - 变更监听通过 SSE 订阅
settings应用的/desk/moltbot路径,处理put-entry/del-entry两类事件(parseSettingsEvent,L200-L232),每次命中后增量更新本地状态并通知监听器; - 由于 Urbit settings store 不支持嵌套对象,
channelRules和pendingApprovals以 JSON 字符串形式存储,读回时做容错解析与逐条结构校验(parseChannelRules/parsePendingApprovals,L70-L95、L162-L195); - 设置 desk 尚不存在时
load()静默回退为空设置——插件不会因此启动失败。
10. 出站投递目标(CLI / cron)
配合openclaw message send或 cron 投递使用:
- DM:
~sampel-palnet或dm/~sampel-palnet - 群组:
chat/~host-ship/channel或group:~host-ship/channel
extensions/tlon/src/targets.ts 中的parseTlonTarget实际比文档列出的更多:还支持room:前缀、group:~host/channel两段式简写(自动拼成chat/~host/channelnest)、可选的tlon:渠道前缀,ship 名缺~前缀时自动补齐。出错时提示的标准写法为dm/~sampel-palnet | ~sampel-palnet | chat/~host-ship/channel | group:~host-ship/channel。
11. 图片媒体限制(mediaMaxMb)
channels.tlon.mediaMaxMb以 MiB 为单位限制每张入站图片的下载和出站图片的加载。多账号下可用accounts.<id>.mediaMaxMb覆盖;未设置时依次回落渠道根、再回落agents.defaults.mediaMaxMb。图片下载与上传存在 6 MiB 的硬上限。行为差异:
- 配置了 cap 时:尺寸检查失败或下载失败会让发送失败,而不是内嵌一个未经检查的 URL;有界下载成功后若上传失败,仍可使用原始 URL;
- 未配置 cap 时:即使图片无法在限制内下载,仍保留原有的"直接发链接"回退。
12. 捆绑 Skill:tlon-skill
插件捆绑了@tloncorp/tlon-skill,一个用于直接 Urbit 操作的 CLI,安装插件后即可自动使用,能力覆盖:
- Activity:mentions、replies、unreads
- Channels:列出、创建、重命名
- Contacts:列出/获取/更新 profile
- Groups:创建、加入、邀请/请求流程、roles
- Hooks:管理频道 hooks
- Messages:历史、搜索
- DMs:发送、react、接受/拒绝
- Posts:react、删除
- Notebook:向 diary 频道发帖
- Settings:通过上文第 9 节的设置存储热重载插件配置
13. 完整配置参考表
| 键 | 含义 |
|---|---|
channels.tlon.enabled | 启用/禁用渠道启动 |
channels.tlon.ship | 机器人的 Urbit ship 名(如~sampel-palnet) |
channels.tlon.url | Ship URL(如https://sampel-palnet.tlon.network) |
channels.tlon.code | Ship 登录 code |
channels.tlon.network.dangerouslyAllowPrivateNetwork | 允许 localhost/LAN ship URL(SSRF 显式放行) |
channels.tlon.ownerShip | Owner ship:处处被授权,接收审批请求 |
channels.tlon.dmAllowlist | 允许 DM 的 ship(空 = 除 Owner 外都不允许) |
channels.tlon.autoAcceptDmInvites | 自动接受dmAllowlist中 ship 的 DM |
channels.tlon.autoAcceptGroupInvites | 自动接受来自groupInviteAllowlist的群组邀请 |
channels.tlon.groupInviteAllowlist | 群组邀请可被自动接受的 ship 列表 |
channels.tlon.autoDiscoverChannels | 自动发现已加入的群组频道(默认false) |
channels.tlon.implicitMentions.threadParticipation | 允许已参与线程的后续消息绕过 mention 门控 |
channels.tlon.groupChannels | 手动固定的频道 nest 列表 |
channels.tlon.defaultAuthorizedShips | 所有频道默认授权的 ship(无规则匹配时使用) |
channels.tlon.authorization.channelRules | 逐频道 nest 的授权模式 + 白名单 |
channels.tlon.showModelSignature | 在回复末尾追加_[Generated by <model>]_ |
channels.tlon.responsePrefix | 自动回复前缀:字面量、"auto"或"[{model}]"模板;账号覆盖优先,""禁用 |
channels.tlon.accounts.<id> | 额外命名账号(多 ship 部署) |
完整的 Zod schema 定义见 extensions/tlon/src/config-schema.ts,其中network为 strict 对象(只接受dangerouslyAllowPrivateNetwork一个键),顶层还有文档表格未列出的historyLimit(整数 ≥ 0)、name、configWrites等字段。
14. 故障排查
常用诊断命令:
openclaw status openclaw gateway status openclaw logs --follow openclaw doctor常见故障与定位方向:
- DM 被忽略:发送者不在
dmAllowlist且未配置ownerShip(没有审批流兜底); - 群消息被忽略:频道未被发现/固定,或发送者未通过授权且无
ownerShip排队审批; - 连接错误:确认 ship URL 可达;本地 ship 需设置
network.dangerouslyAllowPrivateNetwork; - 认证错误:登录 code 会轮换——回 ship 重新
+code获取当前值; - 旧配置迁移:出现
allowPrivateNetwork扁平键时跑openclaw doctor --fix。
15. 行为备注与相关文档
其他值得注意的行为约定:
- 群回复需要 @ mention(如
~your-bot-ship),除非机器人已加入该线程; - 线程回复落在线程内;agent 还会拿到线程最近 10 条消息作为上下文前缀;
- 富文本(粗体、斜体、代码、标题、列表)自动转换为 Tlon 原生格式;
- 入站消息若请求频道摘要(例如 "summarize this channel"),会触发内置的历史摘要流程而非普通回复;
- Tlon 不属于声明 pairing 的渠道——它的 DM 认证用的是
dmAllowlist+ownerShip审批流,而不是 Pairing 机制。
延伸阅读(仓库相对路径):
- 渠道总览、群组行为
- 插件机制
- Gateway 热重载
- Tlon 插件入口、SSE 客户端、审批测试
【免费下载链接】openclawThe AI that really does things. Any OS. Any Platform. The lobster way. 🦞项目地址: https://gitcode.com/GitHub_Trending/cl/openclaw
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考