自托管隧道 · cicy-hub
把你的每台设备串起来:节点(cicy-code)主动拨出到 cicy-hub,任何人都能在 https://<slug>.gw.<你的域名>/ 访问那台设备,体验和它的 localhost:8008 完全一样。 节点自身不开任何入站端口。
这不是"AI 网关"
cicy-hub 是让设备可达的反向隧道中继。它和 本地 AI 网关 (模型路由、按 agent 记账)是两个完全不同的东西——改名就是为了不再和"网关"撞词。
核心:透明转发,不做鉴权
cicy-hub 是一根中立的管子:按 slug 把请求原样转发给节点,不鉴权、不改 header、不碰 CORS、不看内容。 鉴权是节点自己的事——用它自己的 api_token,和你在本地访问 localhost:8008?token= 一模一样。
- 访问
<slug>.gw.<域名>= 访问那台机器的本地; - 带对
?token=<该节点的 api_token>才进得去(乱填直接 401); - 隧道唯一验的是节点拨入时的签名 token(用来知道哪个 slug 对应哪条活隧道),客户端流量从不被检查。
数据主权:cicy-hub 不落库、不解密业务、看不到你的 token。你甚至可以把它跑在自己的云上,和我们彻底无关。
你需要准备的(仅这三样)
- 一个你拥有的域名;
- 一条泛解析 DNS:
*.gw.<你的域名>→ 服务器公网 IP; - 一台服务器,
443端口空着。
其余全自动。
一条命令起(自签证书,即起即用)
git clone https://github.com/cicy-ai/cicy-hub && cd cicy-hub
DOMAIN=example.com docker compose up -d --build首次启动容器会自动配好一切,零 cicy-cloud 依赖:
- 生成节点准入签名密钥对(决定谁能拨入);
- 自签一张泛域名 TLS 证书
*.gw.example.com; - 自签一张授权 license(默认放开 50 节点上限);
- 起中继,监听
:443。
密钥 / 证书 / license 都持久化在 gwdata 卷里,重启不变。
接入一台设备
docker compose exec hub enroll my-mac
# 输出(Option A 推荐):一段可直接落盘的 tunnel.json
# {"url":"wss://my-mac.gw.example.com/_tunnel/connect","token":"<token>","insecure":true}把那段 JSON 存成节点的 ~/cicy-ai/db/tunnel.json,然后**直接 cicy-code(不用任何参数)**即可自动拨号(见下方「节点侧配置」)。 节点拨通后,浏览器打开:
https://my-mac.gw.example.com/?token=<该 Mac 的 api_token>?token= 会写进 localStorage,之后自动带上——和 localhost:8008 无差别。
节点侧配置(cicy-code 怎么接上)
隧道只需要两样数据:url + token(外加自签证书时的 insecure)。token 放命令行不安全 (ps 能看到),所以配置尽量落在文件或环境变量里 —— 常规情况下节点一个参数都不用传。
方式一:tunnel.json(推荐,0 参数)
把 enroll 输出的那段 JSON 存成 ~/cicy-ai/db/tunnel.json:
{ "url": "wss://my-mac.gw.example.com/_tunnel/connect", "token": "<token>", "insecure": true }然后直接:
cicy-code # 检测到 tunnel.json → 自动拨号,无需任何 --tunnel 参数方式二:环境变量(容器 / CI)
export CICY_TUNNEL_URL=wss://my-mac.gw.example.com/_tunnel/connect
export CICY_TUNNEL_TOKEN=<token>
cicy-code方式三:内联 flag(临时)
cicy-code --tunnel wss://my-mac.gw.example.com/_tunnel/connect --tunnel-insecure
# token 仍从 tunnel.json / CICY_TUNNEL_TOKEN 取,不进命令行| 名称 | 作用 |
|---|---|
--tunnel <url> | 隧道地址,触发拨号(token 走文件/env) |
--tunnel-insecure | 跳过 TLS 校验(自签证书时必带) |
CICY_TUNNEL_URL / CICY_TUNNEL_TOKEN | 等价于文件里的 url / token |
不需要 --public
隧道转发到节点的 127.0.0.1:<port>(回环),而 cicy-code 默认就绑 127.0.0.1,所以不用加 --public。 保持默认回环反而更安全:局域网里没人能直连 :8008,唯一入口是隧道 + 节点 api_token。 --public(绑 0.0.0.0)只在你还想让同网段直接访问时才加,和隧道无关。
自签证书 = insecure
cicy-hub 默认 TLS_MODE=selfsigned 时,tunnel.json 里要 "insecure": true (或加 --tunnel-insecure),否则拨号会因 TLS 校验失败而不断重连。换成 Let's Encrypt / 自带正式证书后设回 false / 去掉它。enroll 已按当前 TLS 模式自动填好 insecure。
旧名仍可用(deprecated)
--gateway / --gateway-token[-file] / --gateway-insecure / CICY_GATEWAY_* / gateway.json 作为兼容别名保留,老部署不受影响;新配置请用上面的 --tunnel* / tunnel.json。
浏览器信任的证书(Let's Encrypt,自动)
泛域名证书要走 DNS-01 校验,把 DNS 服务商的 API token 给 cicy-hub 即可自动签发。以 Cloudflare 为例:
DOMAIN=example.com \
TLS_MODE=letsencrypt \
ACME_EMAIL=you@example.com \
LEGO_PROVIDER=cloudflare \
CF_DNS_API_TOKEN=xxxxxxxx \
docker compose up -d --build其他服务商见 lego 支持列表,设置对应的 LEGO_PROVIDER 与其环境变量。
自带证书
# 把 fullchain 与 key 放进卷:/data/tls-cert.pem /data/tls-key.pem
DOMAIN=example.com TLS_MODE=custom docker compose up -d --build环境变量
| 变量 | 默认 | 含义 |
|---|---|---|
DOMAIN | —(必填) | 对外服务 *.gw.$DOMAIN |
ORG | self | 你名下节点的逻辑归属 |
LICENSE_MAX_NODES | 50 | 本隧道的并发节点上限 |
TLS_MODE | selfsigned | selfsigned / letsencrypt / custom |
NODE_TTL | 8760h | 签发的节点 token 有效期 |
ADDR | :443 | 监听地址 |
常用命令
docker compose exec hub info # 打印当前配置
docker compose exec hub enroll <slug> # 接入一台新设备
docker compose logs -f hub # 看隧道起落别丢 gwdata 卷
签名密钥在这个卷里。重新生成密钥会让你已经签发的所有节点 token 失效,得逐台重新 enroll。
和 Cloudflare 隧道的区别
单机部署 里的 --cft 走 Cloudflare,快、免运维,但域名与信任链在 Cloudflare 手上。 cicy-hub 则是你自己的中立中继:自己的域名、自己的证书、自己签发准入,数据只从你的机器过一遍。 两者可以并存——按设备选。