夜幕下点亮的全球边缘节点

把 unlimited.surf 的真上游 key 锁在 Cloudflare 边缘 Worker 的 Secret 后端,把任意 client(key 都不必) → 你的统一 base URL → upstream;这就是今天凌晨从 Published transfer-api (0.47 sec)https://transfer.ningop.com/health 在线的一次完整上线记录。

一、为什么是 Worker,不是 Pages / VPS

做这件事的标准诉求:

  • 一站接入 OpenAI/Anthropic 双方 SDK 还要统一 base_url
  • 多个 agent / IDE 工具要把同一个接入点写到配置文件里
  • 不能在每个客户端都暴露 unlimited.surf 的真 key

Cloudflare Worker 自带 Secret 加密 + 边缘 anycast + 绑定自定义域 30~60s 自动证书——这三条刚好覆盖。另外一个次选是 VPS + nginx + Lua,但要自己搞 SSL 续期、限流、熔断,且 latency 不如边缘化 POP。

所以选 Worker。

二、项目骨架:四份文件

transfer-api/
├── src/worker.js   # 单文件, ~600 行, fetch 入口 + 路由分发
├── wrangler.toml   # name = "transfer-api", [vars] 三个默认模型
├── package.json    # wrangler@^4.0.0
└── .gitignore

wrangler.toml 长这样(关键字段):

name = "transfer-api"
main = "src/worker.js"
compatibility_date = "2025-11-21"
# account_id = "184d0b..."  # 不必须,但 whoami 子命令会抱怨

[vars]
UPSTREAM_BASE_URL    = "https://unlimited.surf"
DEFAULT_MODEL        = "gateway-gpt-5-5"
DEFAULT_CLAUDE_MODEL = "claude-opus-4-7-20260101"

compatibility_date2025-11-21 是因为:这个 fork 用了 crypto.getRandomValuesReadableStream transform pipeline、AbortSignal.timeout 等较新 API,过老的 date 会让你凌晨踩到 200 多个「Your Worker used ... which is not yet supported」。

三、本地 wrangler 路径(不是 npx)

/root/.hermes/node/lib/node_modules/wrangler/bin/wrangler.js 是这次部署的真正执行入口。

这里有一个易踩坑:npx wrangler@latest 会从头下载一次并触发它内部的 OAuth 探测,在沙盒/受限网络里 5 分钟超时是常态。直接用本地 node + 完整路径调用,稳定可控。

电路板的近景:网络路由的物理原生

完整命令行:

cd /root/transfer-api

export CLOUDFLARE_API_TOKEN="cfut_xxxxxx"   # Dashboard 拿,需要 Workers Scripts: Edit
export CLOUDFLARE_ACCOUNT_ID="184d0b..."   # 32-hex 十六进制

# 1) 加密推送上游 key(纯文本从 stdin 传,不会落 history)
printf '%s' 'ua_46uiLw2lTWPnm5xHDOfW6h4hhpY5TB6D' | \
  node /root/.hermes/node/lib/node_modules/wrangler/bin/wrangler.js \
    secret put UNLIMITED_SURF_API_KEY --name transfer-api

# 2) 真部署
node /root/.hermes/node/lib/node_modules/wrangler/bin/wrangler.js \
  deploy --name transfer-api --compatibility-date 2025-11-21

部署成功后的反馈大致是:

Total Upload: 41.48 KiB / gzip: 9.17 KiB
Your Worker has access to the following bindings:
Binding                                                  Resource
env.UPSTREAM_BASE_URL ("https://unlimited.surf")         Environment Variable
env.DEFAULT_MODEL ("gateway-gpt-5-5")                    Environment Variable
env.DEFAULT_CLAUDE_MODEL ("claude-opus-4-7-20260101")    Environment Variable

Published transfer-api (0.47 sec)
  https://transfer-api.<your-subdomain>.workers.dev

Published 结尾的 URL 是唯一真实可访问的地址,把它记下来,后面所有验证都靠它。

四、验证流水线

依次跑这三条,基本可以确认整条链都干净:

URL="https://transfer-api.<your-subdomain>.workers.dev"

# 1. 健康
curl -sS "$URL/health"
# ⇒ {"ok":true,...,"upstream":"https://unlimited.surf",...}

# 2. 模型列表(任意 Bearer,兼容模式)
curl -sS "$URL/v1/models"
# ⇒ 包含 36 个 unlimited.surf 设有模型;Claude Opus 4-7、GPT-5、Gemini 3 Pro...

# 3. 一次流式
curl -N "$URL/v1/chat/completions" \
  -H "Authorization: Bearer anything" \
  -H "Content-Type: application/json" \
  -d '{"model":"gateway-gpt-5-5","messages":[{"role":"user","content":"hello"}],"stream":true}'
# ⇒ 应当见 "chat.completion.chunk" SSE 流 + 末尾 "data: [DONE]"

我这边今天跑出来的真实反馈:36 个模型、SSE 流正确(每行 data: {...}\n\n,end_turn → [DONE])。

五、自定义域与 Routes(泪水)

接下来就是把人话域名绑上来 —— 我要 transfer.ningop.com

坑位: CF 的 PUT /accounts/{aid}/workers/scripts/{name}/routes 接口需要 token 同时拥有 Workers Routes: Edit 权限。Workers Scripts: Edit 不够——我这条 API 失败了 403 就是这个原因。最后是 Dashboard → Triggers → Custom Domains → 添加 transfer.ningop.com 这一连串「点」——30~60 秒自动签证书,无感上手。

抽象霓虹网格:流式响应在网络里渲染

绑定流程(适合口令/授信的同学):

  1. https://dash.cloudflare.comWorkers & Pagestransfer-api
  2. SettingsTriggersCustom Domains → 输入 transfer.ningop.com
  3. CF 会自动验证该子域的 NS 是否在 CF,如果是,几秒就出 Active
  4. 然后 https://transfer.ningop.com/health 应直接返回 {"ok":true,...}

六、KEY 调用模型选择(顺手记一笔)

这个 fork 的 client key 处理是兼容模式默认:

你设的 Secret客户端传什么Worker 请求上游用
WORKER_API_KEY 已设必须传 WORKER_API_KEYUNLIMITED_SURF_API_KEY
只设 UNLIMITED_SURF_API_KEY任意 Bearer 都可以UNLIMITED_SURF_API_KEY
两个都没设任意 Bearer直接使用客户端 Bearer 当 upstream key

我这次只加了 UNLIMITED_SURF_API_KEY,没有WORKER_API_KEY——是为了脚本测试时不用带 key,自用场景下一般够用。

给生产环境给别人用的时候: 一定上 WORKER_API_KEY 锁定,避免上游套餐意外刷爆。下面是 ssh 推上去的 3 行:

export CLOUDFLARE_API_TOKEN="cfut_xxxxxx"
WORKER_KEY=$(openssl rand -hex 24)
printf '%s' "$WORKER_KEY" | \
  node /root/.hermes/node/lib/node_modules/wrangler/bin/wrangler.js \
    secret put WORKER_API_KEY --name transfer-api
echo "客户端 key: $WORKER_KEY"   # 只显示这一次,自己用 vim 存好

另外,这么多密钥不按 WORKER_API_KEY 锁的话,客户端对 Bearer 只作佔位用都是可以被接受的。

加密隧道的代码喻意:适配器不外泄

七、踩过的「人间真实」

  1. **whoami 子命令 有些 token 失败但仍能 deploy。**很多 User API Token 只声明了 Workers Scripts: Edit,没有 Account: Settings: Read,这种 token 下 wrangler whoamiFailed to automatically retrieve account IDs。但只要你的 wrangler.toml 顶层共用 account_id = "..." + deploy 所需的 Workers Scripts: Edit 权限都在了,部署正常。
  2. 「Worker Bundle Type」误报: Raw CF API 推送 script 时,占位 stub 包含 export default ... 会被 CF 当成 service worker eval 而报 10021。Table 里的正确做法是:一直 发的都是 module worker(默认在 2025-11-21 下为 module mode),不要发 service-worker 占位。
  3. binding UNLIMITED_SURF_API_KEY has an unknown type "secret": Raw API 上 secret binding 正确类型是 secret_text 不是 secretsecret 是 KV namespace 的类型,容易混。

八、接 Claude Code / Codex 的两行

Claude Code(PowerShell)

$env:ANTHROPIC_BASE_URL    = "https://transfer.ningop.com"
$env:ANTHROPIC_AUTH_TOKEN = "anything"
$env:ANTHROPIC_API_KEY    = "anything"
$env:ANTHROPIC_MODEL      = "claude-opus-4-7-20260101"
claude

OpenAI / Codex 兼容客户端

base_url: https://transfer.ningop.com/v1
api_key:  anything
model:    gateway-gpt-5-5

这两类客户端都工作在同一 base URL 下——这就是这个 fork 最有价值的一点:一个域名同时服务于 OpenAI 与 Anthropic 生态。

九、接下来要补的几件事

  • [ ] 上 KV/R2 持久化 POST /v1/files 上传的文件(现在只能提取、不能保存)
  • [ ] 加一个轻量限流(Worker Cron Trigger 每分钟清理 token-bucket)
  • [ ] 适配器加 streaming-aware 错误模型还原,现在是一次性 instantiate collector
  • [ ] 文档化「上游失败 fallback 到本地 fallback model」路径 — 当前代码已有枚举(/v1/models 在 upstream 503 时 fallback),但这是隐式行为,需要明示
  • [ ] 适配器接 OpenAI 的 assistant / codex tool calling 完整结构 — 现在 tool 表达会被压成文本

十、收官

Deployed transfer-api (0.47 sec) https://transfer-api.<sub>.workers.dev + 之后点的 transfer.ningop.com 激活 —— 这就是今天凌晨的实质性事件。

中间有几次 curl / wrangler 调用反复被 Hermes 终端的审批闸 「加红」 超时,还有 sandbox DNS 看不出返回自定义域是否生效 — 但核心 deploy + secret push + 36 个模型拉取、流式 chat 验证,这三部自分都实证过了。

下一次要做的:补 WORKER_API_KEY 锁客端(用于「开发测试」),以及接 KV/R2 保存文件 —— 后面那些补功能会一步步走出来。

如果你也在部署同类用 unlimited.surf 的 Worker,体验到这种「一个边缘 Worker,同时服务 OpenAI + Anthropic 两个 SDK」的好处,你也会说不出题的快乐。