Skip to content

自托管 Relay Hub

Relay Hub 是一个可选的、自托管的服务端,它能把 xacpx 变成一个多租户的远程遥控看板。你的各个 xacpx 实例会通过 WebSocket 主动向外拨号连接到 Hub 并注册;随后你即可从任意浏览器登录 Web 看板,在同一个地方驱动每个实例的会话——聊天、定时任务和编排。

本指南将带运维人员从零开始,搭建一个已配对实例并正常运行的 Hub。

架构速览

   xacpx instance A ─┐                         ┌─ browser (token login)
   xacpx instance B ─┤  WSS (dial-out)         │  HTTPS + WSS
   xacpx instance C ─┴──────────────►  RELAY HUB  ◄───────────┘
                          :8787 HTTP API + web /ws + instance gateway

                              relay.db (SQLite)
  • 单端口(默认)。 HTTP API、看板的 /ws 广播与实例 WebSocket 网关(xacpx 实例在此注册)全部共用 8787——连接器通过根路径上的 WebSocket 升级完成注册。仅当你想要一个可独立配置防火墙的专用网关端口时,才传入 --ws-port
  • 多租户。 每个 token 用户永远只能看到属于自己的实例和会话;服务端会在每次代理调用上打上身份标记。令牌、实例凭据和 Web 会话 Cookie 均以哈希形式存储。
  • 数据真相源仍在实例侧。 Hub 会为看板缓存近期消息,但它并不拥有你的会话——会话归实例所有。

环境要求

  • Node.js ≥ 22.13(使用内置的 node:sqlite或 Bun ≥ 1.2(使用 bun:sqlite)。无需编译原生数据库插件。
  • 一台可被你的实例和浏览器访问到的主机。对于 localhost 之外的任何部署,你都需要一个负责 TLS 终止的反向代理(参见 TLS 与反向代理)。
  • 要配对实例,每个实例都需要 xacpx CLI(即快速开始中的常规安装)。

1. 获取服务端

全局安装 relay Hub。Web 看板已内嵌在包内,所以这一条安装就够了——无需单独构建看板、无需 --web-root

bash
npm i -g @ganglion/xacpx-relay

这会把 xacpx-relay 二进制放进你的 PATH。内嵌看板会在启动时自动检测;start 命令会打印它解析出的路径(见下文)。

改为从源码运行(开发 / 贡献)

克隆仓库并构建服务端——build:relay 同时会构建看板并把它嵌入到 packages/relay/dist/relay-web

bash
git clone https://github.com/gadzan/xacpx
cd xacpx
bun install && bun run build:relay

此时入口是 packages/relay/dist/cli.js——下文中所有 xacpx-relay <command> 都用 node packages/relay/dist/cli.js <command> 代替。

2. 快速上手(无需任何参数)

认证完全基于令牌——没有密码、没有管理员账户、没有邀请流程。一个令牌就是一个用户。签发令牌后启动服务端:

bash
# 步骤 A — 创建用户 + 令牌(DB 自动创建于 ~/.xacpx-relay/relay.db)
xacpx-relay add token

# 步骤 B — 启动服务端(使用相同的默认 DB;看板自动检测)
xacpx-relay start

add token只打印一次令牌:

access token: bBS9nN2W2MwdrdksoLTLrQeMLMah9M5flTOyEcBbIHc
(store it now — not shown again)
hint: use this token for web login AND: xacpx channel add relay --url <host> --token <token>

start 会确认运行状态:

xacpx-relay listening: http :8787, instance gateway: merged on http :8787 (path / or /gateway), db ~/.xacpx-relay/relay.db, dashboard: /usr/lib/node_modules/@ganglion/xacpx-relay/dist/relay-web

打开 http://<host>:8787,将令牌粘贴到 Access token 输入框,即可登录。

3. 令牌管理

Hub 一共有 4 条 CLI 命令,所有标志均为可选:

add token — 创建用户 + 登录令牌

bash
xacpx-relay add token [--label <>] [--db <>]

每次调用都会创建一个独立的用户,并只打印一次访问令牌。同一个令牌可用于:

  1. 登录 Web 看板(粘贴到 Access token 输入框)。
  2. 配对 xacpx 实例(在 channel add relay 中传入 --token <T>)。

在多个实例上复用同一个令牌,可将它们归属于同一个用户。

ls — 列出令牌

bash
xacpx-relay ls [--db <>]

显示:短 ID、备注标签、创建日期、已配对实例数量。

rm token — 吊销令牌(及其用户)

bash
xacpx-relay rm token <值或ID> [--db <>]

删除该令牌对应的用户,并级联删除其实例、Web 会话和缓存消息。吊销后,该令牌的 Web 会话将在下次请求时失效;已打开的看板 /ws 长连接会在下次重连时才断开。若要立即强制断开所有会话:停止 Hub,执行 sqlite3 <db> "DELETE FROM web_sessions;",然后重启。

start — 启动服务端

bash
xacpx-relay start \
  [--db <>] \
  [--web-root <>] \
  [--host 0.0.0.0] \
  [--http-port 8787] \
  [--ws-port <n>] \
  [--history-retention-days 30] \
  [--request-timeout-ms 120000] \
  [--trust-proxy]
标志默认值用途
--db <路径>~/.xacpx-relay/relay.dbSQLite 数据库文件。目录会自动创建。
--http-port <n>8787HTTP API 以及看板的 /ws 广播。
--ws-port <n>(已合并)省略则把实例网关合并到 HTTP 端口(单端口默认)。传入一个端口则会启动一个可单独配置防火墙的专用网关监听器。
--host <地址>0.0.0.0绑定地址。
--web-root <目录>(自动检测)看板资源目录。自动解析包内嵌入的看板(cli.js 旁边的 dist/relay-web);仅在需要覆盖时才传入。
--history-retention-days <n>30超过此天数的缓存消息会被每小时清理一次(同时硬性上限为每会话 2000 条)。
--request-timeout-ms <n>120000代理到实例的每次请求超时时间。
--trust-proxy(关闭)信任 X-Forwarded-For 用于限速。在反向代理后面运行时传入此参数;绝不在直接暴露于公网时使用(否则会导致 IP 伪造)。

没有 stop/status 子命令——请用 Ctrl-C / SIGTERM 停止 Hub(生产环境请在 systemd、pm2 或 Docker 下运行以管理生命周期)。

限速说明

Hub 会对每个客户端 IP 进行限速,并设有全局失败上限。在反向代理后面运行时,传入 --trust-proxy,使 Hub 使用 X-Forwarded-For 中的真实客户端 IP 进行限速,而非代理的回环地址。

4. 配对 xacpx 实例

挂载实例

在运行 xacpx 实例的机器上,使用步骤 2 中创建的同一个令牌添加 relay 连接器频道:

bash
xacpx plugin add @ganglion/xacpx-channel-relay
xacpx channel add relay --url relay.example.com --token <T> [--name home-pc]
xacpx restart

--url 指向与看板相同的主机——裸域名会解析为 wss://relay.example.com,合并后的网关与该主机共用。xacpx plugin add 会从 npm 安装连接器,并自动拉取其依赖 @ganglion/xacpx-relay-protocol。实例侧的 xacpx 核心需 ≥ 0.11.0(连接器的 peer 要求)。

从源码检出目录配对(开发)

如果你从仓库检出目录运行实例,workspace 已经链接好了 channel-relayrelay-protocol——跳过 plugin add,直接运行 xacpx channel add relay … 即可。

--url 简写规则

--url 接受以下多种形式;连接器会自动将其规范化为完整的 WebSocket URL:

传入的值解析结果
relay.example.com(裸域名)wss://relay.example.com
1.2.3.4(IP)ws://1.2.3.4:8787
1.2.3.4:9000ws://1.2.3.4:9000
localhostws://localhost:8787
host:9000ws://host:9000
ws://…wss://…原样使用
http://…https://…映射为 ws://… / wss://…

IPv6

不支持未加方括号的裸 IPv6 地址。请使用 [::1]:8787 格式。

配对工作原理

首次连接时,实例会用访问令牌换取一个长期有效的专属凭据,以 0600 权限写入 <xacpx-home>/relay/credential.json——绝不会写入 config.json(后者只保存 url/name)。令牌仅在首次配对时使用——后续所有重连均使用存储的凭据。

一个令牌,多个实例

可以在多台机器(如 home-pcwork-laptop)上复用同一个令牌,它们都会在看板中归属于同一个用户。

从双端口(0.1.0)布局升级

默认已改为单端口。早先针对旧 8788 网关配对过的连接器,其 config.json 里冻结了 ws://<host>:8788(URL 只在 channel add 时规范化一次),所以它会继续连 :8788。请重新运行 xacpx channel add relay --url <host> …(或直接改存储的 url),让它指向合并后的 HTTP 端口。裸域名连接器(wss://host,不带端口)不受影响——它们本就落在合并网关上。

回到看板,实例上线后会出现在左栏,并带有一个绿色圆点。选中某个会话即可聊天;打开任务面板(右栏,或移动端的 Tasks 按钮)可查看定时任务与编排任务。

TLS 与反向代理

Hub 只说明文 HTTP 和 WS。对于任何非 localhost 的部署,请在反向代理处终止 TLS,并转发那一个 HTTP 端口。实例随后应使用 wss:// 连接。

看板的实时更新会在 HTTP 端口上发起 WebSocket 升级,因此代理也必须在该路由上允许升级。合并后的实例网关通过根路径上的 WebSocket 升级搭乘同一端口,因此这一条被代理的路由即可同时覆盖两者。

Caddy

text
relay.example.com {
    reverse_proxy 127.0.0.1:8787
}

Caddy 会自动为看板的 /ws 和网关根路径代理 WebSocket 升级——无需额外配置。

nginx

nginx
server {
    listen 443 ssl;
    server_name relay.example.com;
    # ssl_certificate ...; ssl_certificate_key ...;
    location / {
        proxy_pass http://127.0.0.1:8787;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
    }
}
进阶:专用网关端口(--ws-port

如果你用 xacpx-relay start --ws-port 8788 启动 Hub,实例网关会获得自己的端口,而不再搭乘 HTTP 端口。届时你可以再加一个被代理的域名(gateway.example.com8788),并让连接器指向 wss://gateway.example.com。仅当你想把网关与看板分开配置防火墙时才需要这样做。

应该暴露哪些端口

端口面向对象是否公开暴露?
8787浏览器(看板 + API + 看板 /ws以及 xacpx 实例(合并后的网关)是,需置于 TLS 之后
8788仅当你启用 --ws-port 时——专用实例网关是,需置于 TLS 之后
relay.db绝不——它是一个本地文件

令牌与用户管理

  • 添加更多用户/实例: 再次运行 add token——每次调用都会创建一个独立的用户。在多个实例上复用同一个令牌,可将它们归属于同一个用户。
  • 审计: ls 显示所有令牌的备注标签、创建日期和实例数量。
  • 吊销: rm token <值或ID> 删除该用户,并级联删除其实例、Web 会话和缓存消息。该令牌的 Web 会话将在下次请求时失效;已打开的 /ws 长连接会在重连时才断开。
  • 强制全局重新登录: 停止 Hub,执行 sqlite3 <db> "DELETE FROM web_sessions;",然后重启。
  • 自动 GC: 一个每小时运行的维护循环会清理超过 --history-retention-days(以及每会话 2000 条上限)的缓存消息,并删除过期的 Web 会话。无需配置 cron。

持久化与备份

所有数据都存放在 --db 指定的那个 SQLite 文件中(默认为 ~/.xacpx-relay/relay.db)。备份时,停止 Hub(或在空闲时刻做快照)并复制该文件:

bash
cp ~/.xacpx-relay/relay.db /backups/relay-$(date +%F).db

丢失它就意味着丢失所有令牌用户、实例注册信息和缓存历史——届时实例需要重新配对。

在 systemd 下运行(示例)

ini
# /etc/systemd/system/xacpx-relay.service
[Unit]
Description=xacpx relay hub
After=network.target

[Service]
# 使用已安装二进制的绝对路径——用 `command -v xacpx-relay` 查出来。
ExecStart=/usr/bin/xacpx-relay start --host 127.0.0.1
Restart=on-failure
User=xacpx

[Install]
WantedBy=multi-user.target

绑定到 127.0.0.1,让你的反向代理面向公网。DB 和看板会从默认位置自动检测;仅在需要非默认路径时才添加 --db

常见问题排查

现象原因解决办法
看板 404 / 空白页start 打印了 dashboard: (none)——没找到内嵌的看板看板随 @ganglion/xacpx-relay 一起出厂;重装该包即可。源码检出时用 bun run build:relay 重建(它会把看板嵌入 dist/relay-web)。
实例始终不变绿网关 URL 错误、令牌被吊销或输入有误--url 指向与看板相同的主机(合并后的网关与 HTTP 端口共用);仅当你以 --ws-port 启动 Hub 时,才使用单独的 :8788 主机。如果令牌已被吊销,重新运行 add token 并重新配对。
自定义 --db 未生效--db 在不同命令间传入不一致add tokenstart 中传入相同的 --db 路径;默认的 ~/.xacpx-relay/relay.db 是固定的绝对路径,省略 --db 是安全的。
经过代理后实时更新卡住代理未在 8787 上转发 WebSocket 升级在看板路由上允许 Upgrade/Connection 请求头。

另请参阅

基于 MIT 许可证发布。