Repository navigation
Releases: jcleng/opencode-free-provider
Release list
v1.0.3 — 修复 429(Bun 运行时 + <env> 块)并新增 API_KEY
更新内容
1. 修复 429 FreeUsageLimitError(免费层的第二道门)
问题现象:所有请求都返回
429 {"type":"error","error":{"type":"FreeUsageLimitError",
"message":"Free usage limit reached. Free models work in OpenCode …"}}根因不是 IP 限流。实测(同一 IP、同一时间段、同一份请求字节)定位到第二道客户端门:
- 运行时 TLS 指纹:同一请求经本地 TLS 终结隧道转发,Bun 的 TLS 发出 →
200,Node/OpenSSL 发出 →429;调整 Node 的ciphers/ecdhCurve/sigalgs/ALPN/TLS1.2-vs-1.3 均无效;换其它出口 IP 同样429。 - system prompt 指纹:给任意 system prompt 追加 OpenCode 的
<env>块 →200;删掉或截断 → 回到429(请求头与该门无关)。
修复:
- 镜像改用 Bun(
oven/bun:1.4.4,CMD ["bun","server.js"]),npm start/scripts/deploy.sh同样使用 Bun;node server.js仍可启动但会打印警告(客户端门必然 429)。 - 新增
injectOpencodeSystemEnv()/opencodeEnvBlock()(src/conversions.js),在finalizeBody()中为 system prompt 追加 OpenCode 的<env>块:保留 agent 原有指令、幂等(真实 OpenCode 已带<env>时不重复注入)、初始请求与每个 follow-up turn 全覆盖。 src/routes.js读取x-opencode-directory(回退OPENCODE_ENV_CWD)填入块内目录;OPENCODE_SYSTEM_FP=0可关闭注入。- 上游端点统一为
https://opencode.ai/inference/openai/v1;429 错误文案改为真实原因(不再误导为 “per-IP 配额”)。
2. 新增 API_KEY(客户端 → 代理鉴权)
Dockerfile新增ENV API_KEY=public:-e API_KEY=<secret>覆盖为私有密钥;-e API_KEY=(空值)关闭客户端鉴权;- 默认
public保持历史配置"apiKey": "public"继续可用。
- 新增
src/auth.js:接受Authorization: Bearer <API_KEY>(OpenAI 客户端标准写法)与x-api-key,使用 timing-safe 比较。 - 鉴权失败返回
401 {"error":{"code":"invalid_api_key",…}}+WWW-Authenticate: Bearer;CORS 允许x-api-key。 - 保护范围:
/v1/*与/__*(调试端点会暴露 session id);/、/health、/v1保持公开。GET /__session返回auth.required。
客户端把同一个值填到 provider 的 apiKey:
docker run -d --name opencode-free-provider --restart unless-stopped \
-p 48391:8791 -e API_KEY='sk-ofp-<your-secret>' \
ghcr.nju.edu.cn/jcleng/opencode-free-provider:latest3. 其它
- 单元测试 66 → 72 个(新增
<env>块注入、鉴权/401/公开路径等用例),全部通过。 README.md/agents.md记录「两道门」(403 = 头/会话/工具;429 = 运行时 + system prompt)、API_KEY用法与排障映射。
镜像:ghcr.io/jcleng/opencode-free-provider:latest(Dockerfile 运行时为 Bun 1.4.4)
完整变更:8bcda41...v1.0.3
v1.0.2
opencode-free-provider v1.0.2
热修复版本:将免费档上游默认端点从已失效的 /zen/v1 切换为 OpenCode 官方客户端实际使用的 /inference/v1。
本版本重点:解决升级前必然出现的
403 FreeTierError: OpenCode's free tier can only be used from within OpenCode。
修复
Fix: OPENCODE_BASE_OVERRIDE /inference/v1(e987934)src/config.js的getOpencodeBase()默认值由https://opencode.ai/zen/v1改为https://opencode.ai/inference/v1。
背景
/zen/v1 路由已被 OpenCode 停止服务——对免费档请求一律返回 403 FreeTierError。OpenCode 自有客户端实际请求的端点是 https://opencode.ai/inference/v1(可从 https://models.opencode.ai/api.json 确认)。本版本让代理默认打到该端点,与官方客户端行为一致。
指纹机制(纯 JS 生成 x-opencode-session,无需 opencode CLI / MITM)与 v1.0.1 相同,未改动。
升级
# Docker(推荐,ghcr.io 最新镜像已含本修复)
docker pull ghcr.io/jcleng/opencode-free-provider:latest
docker run -d --name opencode-free-provider -p 8791:8791 \
ghcr.io/jcleng/opencode-free-provider:latest
# 旧镜像(默认 /zen/v1,会 403)不必重拉,也可用环境变量强制指向可用端点:
docker run -d --name opencode-free-provider -p 8791:8791 \
-e OPENCODE_BASE_OVERRIDE='https://opencode.ai/inference/v1' \
opencode-free-providerAgent 配置不变:
baseURL: http://127.0.0.1:8791/v1
apiKey: public
model: opencode-free/big-pickle
验证
- 50 个单元测试全部通过(mock upstream,无网络)。
- GitHub Actions 构建
ghcr.io/jcleng/opencode-free-provider:latest成功(run 38733861667)。
完整变更:v1.0.1...v1.0.2
v1.0.1
opencode-free-provider v1.0.1
本地 OpenAI 兼容代理,将聊天请求转发到 OpenCode Zen 免费档,无需 API key 即可使用 big-pickle 等免费模型。
本版本重点:指纹改为纯 JS 生成,不再需要安装 / 运行真实的
opencodeCLI。 Docker 镜像从约 2.4GB 降到约 245MB。
核心特性
纯 JS 指纹(推翻 v1.0.0 的「无法伪造」结论)
v1.0.0 认为免费档的 x-opencode-session 有服务端校验和、无法伪造,因此必须 spawn 真实 opencode 并用 MITM 拦截抓取。该结论已证伪。
经反编译 opencode 源码 + 对真实端点做逐位 ablation 实测,确认该 id 就是 opencode 普通的 descending 标识符:
ses_ + 12 位小写 hex + 14 位 base62 // 共 30 字符
hex = ~(Date.now() * 0x1000 + counter) 的 48 位
服务端实际只校验:
- 低 12 bit 编码的计数器
0xfff - counter必须 counter ≤ 63(64+ → 403); - 前 12 位必须为小写 hex;tail 必须恰好 14 位;
- 时间戳大小 / 新旧不校验(高 36 bit 随便填);
x-opencode-session-id若存在须等于x-opencode-session。
没有任何校验和。 本版本在 src/fingerprint.js 里直接复刻该算法(counter 超过 63 自动进位到下一毫秒),因此:
- 不再 spawn
opencode,不再 MITM/TLS 拦截,不再需要openssl与一次性 CA; - 每个请求都生成一个全新合法的
ses_...(默认ZEN_SESSION_MODE=per-request); - 相关文件:
src/fingerprint.js(encodeIdentifier()/newSessionId()/newMessageId()/buildFingerprintHeaders()),/__session暴露fingerprint.source == "generated"。
Docker 不再需要装 opencode
指纹改为纯 JS 后,镜像不再安装 @opencode/cli(约 200MB 原生二进制)与 openssl:
- 体积从约 2.4GB → 约 245MB;
- 基础镜像仍为
node:24-bookworm-slim(Node ≥ 24 才有NODE_USE_ENV_PROXY)。
模型列表从上游动态发现
GET /v1/models 会实时请求上游 /models,只保留 id 含 big-pickle 或 -free 的模型,并带 TTL 缓存(ZEN_MODELS_TTL_SECONDS,默认 600s),失败时回退到上次良好列表 / 内置 big-pickle。
使用方法
# 本地
npm start # http://127.0.0.1:8791/v1
# Docker
docker build -t opencode-free-provider .
docker run -d --name opencode-free-provider -p 8791:8791 opencode-free-providerAgent 配置:
baseURL: http://127.0.0.1:8791/v1
apiKey: public
model: opencode-free/big-pickle
验证指纹为纯 JS 生成:
curl http://127.0.0.1:8791/__session # 期望 fingerprint.source == "generated"环境变量
| 变量 | 默认 | 说明 |
|---|---|---|
ZEN_SESSION_MODE |
per-request |
per-request(每次请求新 ses_)或 sticky(进程内复用) |
ZEN_SESSION_ROTATE_SECONDS |
0 |
sticky 模式下每隔 N 秒轮换 session |
OPENCODE_UA |
opencode/latest/2.0.22/cli |
发给 gate 的 User-Agent |
ZEN_MODELS_TTL_SECONDS |
600 |
模型列表缓存时长(秒) |
ZEN_MODELS_TIMEOUT_MS |
5000 |
上游 /models 发现超时(毫秒) |
NODE_IMAGE |
node:24-bookworm-slim |
仅构建时:Docker 基础镜像 |
已移除:
OPENCODE_FP_DISABLE/OPENCODE_FP_TTL_MS/OPENCODE_FP_TIMEOUT_MS/OPENCODE_VERSION(不再有 CLI 抓取)。
验证
- 50 个单元测试全部通过(mock upstream,无网络)。
- 真实端点:纯 JS 生成的指纹直接 POST
opencode.ai/zen/v1/chat/completions→HTTP 200。 - 本地代理端到端:
fingerprint.source == "generated",非流式与流式(以[DONE]收尾)均正常。 - 拉取 Actions 构建的镜像实测:容器内同样是
generated,端到端HTTP 200。 - GitHub Actions 已成功构建并推送镜像到
ghcr.io/jcleng/opencode-free-provider:latest。
注意
- 客户端若有
ALL_PROXY(如socks5://127.0.0.1:20170),curl会把 localhost 请求也走代理导致空响应,请用curl --noproxy '*'测试;Node 的fetch默认不读ALL_PROXY,代理服务端不受影响。
变更
feat(fingerprint): generate the OpenCode free-tier fingerprint in pure JS (drop CLI/MITM)(285498c)
完整变更:v1.0.0...v1.0.1
v1.0.0
opencode-free-provider v1.0.0
本地 OpenAI 兼容代理,将聊天请求转发到 OpenCode Zen 免费档,无需 API key 即可使用 big-pickle 等免费模型。
核心特性
动态指纹(绕过免费档 client-fingerprint gate)
OpenCode 免费档服务端会校验请求指纹,其中 x-opencode-session 的 id 结构/校验和由服务端验证,无法伪造——只有真正的 opencode 二进制生成的 ses_... 才通过。
本版本改为:每次需要时 spawn 真实的 opencode 命令,通过本地 MITM/TLS 拦截代理捕获它真正发往 opencode.ai 的请求的 fingerprint headers(user-agent、x-opencode-session、x-opencode-client、x-opencode-project 等),并缓存复用(OPENCODE_FP_TTL_MS,默认 30 分钟)。
- 相关文件:
src/fingerprint.js(getFingerprint()/fingerprintHeaders()),server.js启动预热,/__session暴露fingerprint.source(opencode-cli= 实时抓到;fallback*= 静态回退)。 - 若
opencode不可用或设OPENCODE_FP_DISABLE=1,回退到内置静态指纹,保证代理仍能启动。
Docker 镜像必须安装 opencode
修正了原 Dockerfile 的问题(基于 node:24-alpine 且根本没装 opencode,容器内 opencode: not found):
- 基础镜像
node:24-alpine(musl)→node:24-bookworm-slim(glibc):opencode是 Bun 编译的原生 ELF,链接 glibc,Alpine 跑不了。 npm install -g @opencode/cli@2.0.22(可用OPENCODE_VERSIONbuild-arg 升级)。- 安装
openssl(生成 MITM CA 用)。 - 支持
NODE_IMAGEbuild-arg 指向私有镜像源。
使用方法
# 本地
npm start # http://127.0.0.1:8791/v1
# Docker
docker build -t opencode-free-provider .
docker run -d --name opencode-free-provider -p 8791:8791 opencode-free-providerAgent 配置:
baseURL: http://127.0.0.1:8791/v1
apiKey: public
model: opencode-free/big-pickle
验证指纹已动态获取:
curl http://127.0.0.1:8791/__session # 期望 fingerprint.source == "opencode-cli"
docker exec opencode-free-provider opencode --version环境变量
| 变量 | 默认 | 说明 |
|---|---|---|
OPENCODE_FP_DISABLE |
未设 | 设 1 跳过 spawn,用静态回退指纹 |
OPENCODE_FP_TTL_MS |
1800000 |
抓到的指纹缓存时长(毫秒) |
OPENCODE_FP_TIMEOUT_MS |
15000 |
spawn / MITM 抓取超时(毫秒) |
OPENCODE_VERSION |
2.0.22 |
仅构建时:Docker 内 opencode 版本 |
NODE_IMAGE |
node:24-bookworm-slim |
仅构建时:Docker 基础镜像 |
验证
- 47 个单元测试全部通过。
- 本地端到端:真实动态指纹成功绕过验证,
HTTP 200且返回真实模型回复。 - GitHub Actions 已成功构建并推送镜像到
ghcr.io/jcleng/opencode-free-provider:latest。
注意
- 客户端若有
ALL_PROXY(如socks5://127.0.0.1:20170),curl会把 localhost 请求也走代理导致空响应,请用curl --noproxy '*'测试;Node 的fetch默认不读ALL_PROXY,代理服务端不受影响。 - 新镜像体积比旧的 Alpine 版大(含完整 opencode CLI 原生二进制,约 200MB)。