门户¶
门户将运行在 Gateway 主机或节点支持的云 worker 上的开发服务器暴露给操作员的浏览器。它们为实时重载代理 HTTP 和 WebSocket,并显示在 Control UI → Portals 中。
快速开始¶
让智能体打开一个门户:
- “在门户里给我看看。”
- “在门户中启动应用。”
智能体为应用的端口打开一个门户,然后通过后台 exec 调用启动开发服务器。打开门户会分配其代理路由;它不会向你的服务器注入环境变量。智能体在该 exec 命令自身的环境中设置 PORT(它打开的端口)和 PUBLIC_URL(返回的不含令牌的 publicUrl,包括其初始路径),以便应用绑定预期端口并生成正确的绝对 URL。
对于运行在节点支持的云 worker 上的会话(包括内置的 Crabbox 提供商),开发服务器运行在 worker 上。每个门户连接都会收到自己的单次使用票据;已注册节点在连接到选定的回环端口之前,通过与 Gateway 之间采用 TLS 固定的 WebSocket 兑现该票据。这使用现有的已认证节点通道,不会将 worker 暴露给入站流量,也不会创建 SSH 隧道。停止或替换 worker 会关闭其门户。
后台开发服务器在智能体完成回复后继续运行。在同一 worker 环境中的后续回合里,可以用 process 检查或停止它。关闭门户只会关闭代理;它不会停止服务器。有关进程生命周期和容量详情,请参阅 Worker 后台进程。
智能体还可以使用附加到其现有会话的临时 Crabbox,而无需移动会话的主工作区。它将附件的 environmentId 传递给 portal 工具,并使用 screen 配合 portal_show 及返回的 portalId,在聊天侧边面板中打开该精确预览。应用的命令通过该环境上的 Crabbox 工具运行。它托管的后台进程在已完成的回合后继续存活;停止附件会关闭其应用和门户。门户保持与下文所述的相同的独立源网络和访问约定。
打开路由并不证明应用正在运行,也不证明远程浏览器能够访问它。在 Control UI → Portals 中打开返回的 URL,并验证页面、资源和实时重载是否正常。请原样使用返回的 URL;用 Gateway 的地址替换其中的协议、主机名或端口并不会创建入口路由。
远程访问¶
当操作员和 Gateway 共享同一个 tailnet 时,优先使用托管私有 Tailscale Serve。否则,请配置下方的私有通配符入口。只暴露 Gateway 端点的隧道或反向代理不会暴露应用门户。
托管私有 Tailscale Serve¶
在托管的 Gateway Tailscale 入口处于激活状态时,每个门户都会在托管主机名上获得自己私有的 Serve HTTPS 端口。门户持有前台路由声明,直到其关闭。关闭门户、失去其声明、停止其 worker 或重启 Gateway,都会撤销该路由。其他应用拥有的现有路由不会被门户采用,也不会被门户清除。
门户路由使用 Serve,绝不使用 Funnel,即使 Gateway 使用 Funnel 也是如此。操作员的浏览器必须位于 tailnet 上。通过端口 443 访问 Gateway 并不证明能访问门户独立的 HTTPS 端口:限制性的 tailnet 授权或 ACL 必须允许返回 URL 中的端口。OpenClaw 不会编辑这些策略。门户的 Bearer 认证仍然是必需的;Gateway 的身份头认证不会授予门户访问权限。
私有通配符反向代理¶
使用专用的 DNS 命名空间(例如 preview.example.net),与 Gateway 和 Control UI 的主机名区分开。配置一条私有 HTTPS 通配符路由,而不是为每个应用手动配置一条路由:
domain 是裸 DNS 后缀,不含协议、通配符前缀、路径或端口。port 是专用入口监听器在 127.0.0.1 上的 TCP 端口,既不是应用端口,也不是外部 HTTPS 端口。通过重启 Gateway 来应用此配置。配置后,通配符入口优先于托管的门户 Serve 路由。
按如下方式设置操作员拥有的反向代理:
- 将
*.preview.example.net解析到操作员浏览器可访问的私有 HTTPS 边缘节点。为该通配符颁发一份浏览器信任的 TLS 证书。默认不要使预览命名空间可公开访问。 -
在端口
443上终止 HTTPS,并将请求转发到 Gateway 主机上的http://127.0.0.1:18890。保留原始Host头、完整的请求路径和查询字符串,以及 WebSocket 升级。对流式响应禁用响应缓冲。不要剥离路径前缀,也不要转发到 Gateway 的普通 HTTP 端口。 -
使用网络访问控制或覆盖该通配符的显式身份感知访问策略,保持边缘节点私有。确保不可信主机无法访问回环后端。对 Gateway 主机名的认证不会自动保护预览通配符。在转发到后端之前,移除边缘注入的身份和凭据头。门户会剥离 Tailscale 和 Cloudflare Access 的头部,但无法识别每个自定义认证边缘节点的头部。应用的
Authorization头会被保留。 -
打开一个门户,并从远程浏览器验证返回的确切 HTTPS URL,包括资源、导航和 WebSocket 实时重载。主机本地的 fetch 或成功的 Gateway 连接不能算作此验证。
OpenClaw 不会安装 DNS 记录、颁发证书、配置反向代理,也不会将 Gateway 访问策略复制到此命名空间。另一台机器上的代理需要一条单独确保安全的路径来访问回环后端;该设置不会创建这条路径。即使新标签页可以正常打开,边缘登录页或第三方 Cookie 限制也可能阻止 iframe 加载。
每个门户会在配置的后缀下获得一个随机的、在其生命周期内唯一的hostname。只有活跃的门户hostname才会路由到应用程序;未知或已关闭的hostname无法选择本地端口或Gateway API。关闭门户会移除其映射和活跃连接。共享入站监听器归Gateway所有;外部通配符DNS、证书和代理在门户关闭和Gateway重启期间仍归操作员所有。
直接监听器与本地监听器¶
在没有配置通配符入站或托管的Tailscale入站时,返回的URL描述实际的直接监听器及其HTTP或TLS协议。直接监听器使用Gateway的绑定接口。通配符监听器在可用时会通告Gateway发现的私有LAN IPv4地址,因此该LAN上的浏览器无需配置入站即可使用返回的URL。公布的地址在门户生命周期内保持不变;网络变更后请重新打开门户。如果没有可用的私有LAN地址,通配符监听器会发布回环URL。回环URL在Gateway主机上有效,而不适用于无关的远程浏览器。
直接TLS监听器复用Gateway证书。服务优先选择来自gateway.publicOrigin或已配置的Control UI来源的、证书有效的hostname,其次是证书有效的绑定地址,最后是证书中的具体DNS名称。仅凭通配符证书名称无法识别目标;当需要具体hostname时,请配置现有的gateway.publicOrigin。该hostname必须解析到Gateway,并且返回的端口必须可达。直接TLS不需要额外的门户入站配置。
发现的名称和证书名称无法建立穿过防火墙、容器端口映射或远程代理的可达性。仅限Gateway的HTTPS代理仍然需要上述入站路径之一;更改显示的URL不会暴露其门户端口。
HTTPS门户使用安全分区cookie,因此当Control UI位于其他站点时,其身份验证仍然有效。当Control UI使用相同的协议和hostname时,直接HTTP门户可以嵌入;支持不同的端口。否则,Portals页面提供新标签页启动,而不是提供身份验证cookie可能被阻止的嵌入式预览。链接保持服务发布的URL不变。使用SameSite=Strict或SameSite=Lax显式限制自身cookie的应用程序会保留该策略。
声明开发服务器¶
可选地将.openclaw/portals.json提交到工作区仓库,以便agent可以发现可用的开发服务器:
{
"portals": [
{
"name": "web",
"command": "pnpm dev",
"cwd": ".",
"port": 3000,
"title": "App",
"description": "Use the seeded test account."
}
]
}
Gateway永远不会自动执行这些命令。agent会读取该文件并决定何时运行已声明的服务器。
| 字段 | 必需 | 描述 |
|---|---|---|
name |
是 | agent用于标识服务器的稳定名称。 |
command |
是 | agent通过后台exec启动的命令。 |
port |
是 | 应用程序监听的本地TCP端口。 |
cwd |
否 | 相对于工作区根目录的工作目录。 |
title |
否 | Portals页面上显示的标题。 |
description |
否 | 门户旁边显示的操作员指引。 |
path |
否 | 初始URL路径。必须以/开头。 |
应用程序契约¶
应用程序必须遵循PORT。需要生成绝对URL时,请使用PUBLIC_URL。
服务器可以在Gateway主机和工作节点上监听IPv4或IPv6回环地址(127.0.0.1或::1),即使机器的localhost记录只列出了一个地址族。通过反向代理连接时,工作节点流还会保留节点配置的Gateway上下文路径。
代理会将Host重写为本地目标,因此Vite和Next.js等典型开发服务器无需额外配置。WebSocket和热模块替换通过同一门户进行代理。
流式HTTP响应(包括服务器发送事件)会在不等待第一个body块的情况下转发响应头。
可用性与配置¶
portal工具遵循常规工具策略,如工具配置所述。可选的gateway.portals.ingress设置仅配置上述私有通配符入站;它不授予工具访问权限。
开箱即用:
portal属于group:ui和coding配置文件,因此codingagent拥有它,而messaging和minimalagent没有。- 沙盒会话永远不会获得它,因为打开门户会在Gateway主机上启动监听器。
- 它被阻止通过HTTP
POST /tools/invoke调用。全局工具(包括Gateway主机端口和主工作节点部署)仍仅限于会话所有者。 - 非所有者可以为附加到其对话的专用辅助工作节点获得一个受限工具。provider必须明确证明当前租约不是共享主机。如果分类未知、机器为共享主机或缺少附件,则此模式不可用。Gateway重启后,必须重新进行provider检查,租约才能再次符合条件。
- 主云工作节点部署仅在其注册节点通告portal-stream支持时才会收到它。没有该功能的旧节点捆绑包在该部署模式下不会收到该工具。
要在所有地方关闭门户,请在全局策略中拒绝该工具:
要为单个agent关闭它们,同时保持其他agent不变:
tools.profile、tools.allow、byProvider和toolsBySender对portal的适用方式与对任何其他工具相同,因此无需门户专用设置,也可以将门户限制到特定的provider、模型或发送者。
受限工具会自动选择对话的附件。它没有 environmentId 覆盖项,也无法暴露 Gateway 主机端口。其 portal.session.open、portal.session.list 和 portal.session.close RPC 需要对指定对话及其有效 portal 工具策略具有当前写权限。自身会话的写入授权仅适用于其自身对话;更广泛的协作者仍遵循现有的会话共享规则。强制沙箱隔离以及所有 modelSelectionLocked: true 的会话均排除此模式。支持锁定会话需要一份预先准备好的会话所有权契约,包括原生和导入的会话所有权;现有的 owner 工具在其常规策略下仍然可用。这不会改变主要的 worker-turn 放置权限,也不会授予对其他附加环境的访问权限。
作用域预览拥有自己的资源身份。通过全局工具和受限工具打开同一个应用端口会产生不同的链接;关闭或停用作用域预览不会关闭全局预览。在同一会话化身和附件下的重复打开会复用该作用域链接。受限的列出和关闭操作仅覆盖这些作用域预览。
对于次要附件,工具可用性会检查对话策略和专用机器资格。每次打开和连接都会分别验证当前节点的 portal-stream 支持。因此,符合条件的附件即使其节点离线或需要更新,也可能显示该工具;请重新连接或更新该 worker 节点,然后重试。
对于相同的租约、节点和所有者,一次临时性的提供方检查错误会保留上一次显式资格。一次成功的检查如果省略主机分类、将主机报告为共享主机,或不再识别活动租约,就会撤回受限能力及其预览。
在直连模式下,portal 监听器绑定与 Gateway 相同的接口。绑定到 LAN 或 tailnet 地址的 Gateway 也会在该网络上发布其直连 portal 监听器端口。Managed Serve 使用私有回环后端;配置的通配符入口使用专用回环监听器。要访问其中一个仍需要 portal 令牌,但当 Gateway 主机完全不应提供操作员可访问的应用端口时,应拒绝该工具。
安全模型¶
每个 portal 使用独立的源(origin):在 direct/Serve 模式下使用自己的端口,或在使用通配符入口时使用自己的主机名。绝不要将任意应用挂载到 Control UI 源(origin)上,即使使用不同的 URL 路径也不行。访问需要 portal URL 中的令牌。在首次请求时,代理会将该令牌存储在 HttpOnly cookie 中,并从后续的上游请求中移除它。代理自行验证此 cookie,并且绝不会将其转发给应用。
浏览器 cookie 以主机名为作用域,而不是以端口为作用域,因此代理会为每个 portal 实例分配一个随机的 oc_portal_<instance>_ cookie 名称前缀。请求仅转发带有当前 portal 前缀的 cookie,并在到达应用之前移除该前缀;Gateway cookie、无前缀 cookie、来自兄弟或已关闭 portal 的 cookie 都会被丢弃。应用的 Set-Cookie 响应会加上此前缀,并移除任何 Domain 属性,以使 cookie 保持仅限主机(host-only)。
通配符入口使用 Secure; SameSite=None; Partitioned 进行 portal 身份验证,以便嵌入式请求在不同的顶级站点下仍能保持已认证状态。应用 cookie 也会获得 Secure 和 Partitioned;未显式指定 SameSite 属性的 cookie 会获得 SameSite=None。应用显式设置的 SameSite=Lax 或 SameSite=Strict 限制保持不变,并可能阻止跨站点的嵌入式会话。Partitioned cookie 以顶级站点为作用域,因此在新标签页中打开 portal 可能会创建单独的应用会话。浏览器策略仍可能阻止嵌入;代理不会覆盖这些策略。
服务将 publicUrl 作为权威的无令牌应用 URL 返回,并将 url 作为其已认证的启动 URL 返回。listenPort 是传输元数据,而不是浏览器 URL 模板。只读列表和变更事件会省略 url 和 tokenQuery;获得授权的客户端在具有写权限时会重新获取它们。不要将 bearer 凭据放入 PUBLIC_URL,也不要在日志或截图中分享它。
已认证的 portal URL 是可共享的 bearer 链接,包括对话作用域预览。完成一个回合或撤销其发起者并不会使已复制的 URL 失效。撤销会阻止该发起者启动或管理预览。对话作用域资源会在其会话被重置或删除、其附件被停用、其环境停止或 Gateway 重启时结束。失去提供方显式的专用机器资格也会撤回作用域预览。关闭 portal 可立即撤回共享链接。
Portal 仅代理 Gateway 主机或节点支持的云 worker 上的选定开发服务器。worker 连接使用单次使用票据和已注册节点的 TLS 固定 Gateway 连接;它们永远不会暴露公共 worker 端口,也不需要 SSH 转发。Portal 永远不提供 Gateway 数据,并且每个 portal 都会在 Gateway 重启时结束。
限制¶
- 不支持 portal-stream 的旧版节点捆绑包无法打开 worker portal。请更新节点捆绑包,或使用
sessions.move将会话移回 Gateway。 - 由 SSH 支持的
remote-exec放置(包括 Codex 会话)不会运行 OpenClaw worker 工具循环,因此portal工具不适用于那里。当需要 Gateway 托管的 portal 时,请使用sessions.move将会话移回 Gateway。 - 仅 Gateway 的代理、SSH 隧道或外部管理的 Serve 路由不会自动创建 portal 入口。请配置私有通配符入口或 OpenClaw 管理的 Serve。UI 会将远程回环 URL 报告为需要入口;它不会凭空生成可访问的 URL。
- 浏览器可达性探测仅检查传输层。响应可能是身份验证页面或等待页面,而不是渲染后的应用。被内容安全策略(CSP)阻止的探测不能说明 iframe 的可达性。
- Portal 入口不会继承 Gateway 的受信任代理身份、Cloudflare Access 策略或 tailnet ACL 授权。请单独配置并验证这些边界。
- 此前缀隔离转发到每个目标的 cookie;它不会创建独立的浏览器 cookie 存储区。在 direct/Serve 模式下,浏览器端代码可以通过
document.cookie看到同一主机名上兄弟 portal 的非HttpOnlycookie。通配符入口会分隔主机名,但共享公共 DNS 后缀的 portal 不一定是不同的站点,cookie 名称前缀仍然适用。请对敏感的应用 cookie 使用HttpOnly。在浏览器代码中管理 cookie 的应用必须考虑此前缀;由浏览器代码直接写入的无前缀 cookie 不会转发到目标。
故障排除¶
门户显示 502 等待页面¶
代理已就绪,但应用程序未监听所选端口,或其工作节点暂时断开连接。页面会自动重试。请检查后台进程,确认服务器遵循 PORT,并验证工作节点已连接。
无法从此浏览器访问门户¶
请检查返回的确切门户 URL,而不是替换为 Gateway 主机:
- 使用远程 Gateway 的环回 URL: 在 Gateway 主机上打开它,或配置 托管私有 Serve 或通配符入口。仅转发 Gateway 端口 不会转发门户。
- 托管 Serve URL 超时: 验证 tailnet 成员身份以及对
返回的 HTTPS 端口的访问权限,而不仅仅是
443。检查其托管声明是否仍保持活动状态。 - 通配符 URL 的 DNS 或 TLS 失败: 检查通配符 DNS 和证书覆盖范围。 确认私有边缘可从浏览器访问。
- 通配符 URL 返回未知主机响应: 保留原始
Host标头,如果其生命周期已结束,请重新打开门户。不要将其重写为 环回后端主机名。 - 页面加载但流式传输或实时重新加载失败: 保留 WebSocket 升级
和请求路径,禁用缓冲,并检查应用程序的
PUBLIC_URL。 - 新标签页可用但预览不可用: 检查边缘身份验证、 框架策略和浏览器 Cookie 限制。仅被阻止的可达性探测 并不能证明 iframe 不可访问。
更正入口后,选择 重试。如果门户的路由已被
撤回,请重新打开门户;当 PUBLIC_URL 发生变化时,使用新的 PUBLIC_URL 重启应用程序。
关闭门户¶
要求代理“关闭门户”,或使用 Control UI → 门户 页面上的关闭按钮。
本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 cl/openclaw