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