## FRP 暴露

```bash
bun scripts/cli.ts platform-infra sub2api codex-pool expose
bun scripts/cli.ts platform-infra sub2api codex-pool expose --confirm
```

- 由 YAML `publicExposure` 控制。Codex pool 默认公共端是 target `publicBaseUrl`；host-Docker target 可以使用 `mode: pk01-local` 直接由 PK01 Caddy 反代本机 loopback app，k3s external-active target 可以使用 FRP remotePort。不要把某个 target 的 exposure mode 推断成其它 target 的默认。
- `expose --confirm` 只为 YAML 指定的 `remotePort` 补 master `frps` allow port，并在 G14 创建/更新 `sub2api-frpc`。
- master Caddy site 也由 `publicExposure.masterCaddy` 渲染；`responseHeaderTimeoutSeconds` 必须足够覆盖 Codex `/responses/compact` 长请求，避免 Caddy 先返回 504 而 Sub2API 后台实际稍后成功。具体数值只改 `config/platform-infra/sub2api-codex-pool.yaml`，修改后跑 `codex-pool expose --confirm`，再核对 Caddyfile 中渲染出的 `response_header_timeout`。
- master Caddy 的短窗口边缘重试由 `publicExposure.masterCaddy.edgeRetry` 渲染；用于吸收 FRP remotePort 短暂关闭、`connect: connection refused`、EOF 或 connection reset 这类请求尚未稳定到达 Sub2API 的 502。具体 retry 时长、间隔和 `retryMatch` 范围只写 YAML，修改后跑 `codex-pool expose --confirm`，再核对 Caddyfile 中渲染出的 `lb_try_duration`、`lb_try_interval` 和 `lb_retry_match`。不要手工 patch `/etc/caddy/Caddyfile`。
- PK01 `/etc/caddy/Caddyfile` 是 Sub2API、LangBot、n8n、HWLAB 等多 YAML 来源共享的 edge artifact。Sub2API apply/expose 只能更新自己的 managed block 并保留其他 blocks；同一 Sub2API 服务暴露多个 target 时，D601 保留既有 `# BEGIN unidesk managed sub2api`，非默认 target 必须使用 target-scoped owner（例如 `sub2api-d518`），避免 `api.pikapython.com` 和 `api2.pikapython.com` 互相覆盖。若 apply 输出显示 managed block 数异常，先停止 closeout，检查 PK01 Caddy 合并与 validation 结果，不要手工整文件覆盖。
- 非幂等 POST 的 round-trip retry 必须收窄到 YAML `retryMatch` 声明的安全路径；普通 `/responses` 上游账号错误仍归 Sub2API failover / temp-unschedulable / sentinel 处理，不用 Caddy 重放整段推理请求来掩盖账号池问题。
- 同一个公开入口同时暴露 OpenAI-compatible API 和 Sub2API 管理 UI `/login`。FRP target 使用同一个 FRP TCP 入口；PK01 local target 使用 PK01 Caddy 到本机 app 的 managed block。不要另开第二个管理端口，除非 YAML 明确声明新的暴露决策。
- k3s target 的 Sub2API Kubernetes Service 继续保持 ClusterIP。
- k3s external-active target 的公开路径是 `client -> PK01 Caddy -> PK01 frps remotePort -> target frpc -> Sub2API`；PK01 host-Docker target 的公开路径是 `client -> PK01 Caddy -> 127.0.0.1:<local upstream port> -> Sub2API`。两者都不经过 pikanode，也不经过 master server 反代。PK01 Caddy 下载必须使用 YAML `publicExposure.pk01.caddyDownloadProxyUrl` 指定的 proxy；如果 Caddy 下载慢，先确认 apply 输出里是 `downloadProxy.mode=curl-proxy`。目标域名必须先解析到 YAML 声明的 PK01 公网地址，HTTPS 才能作为最终验证；`api.pikapython.com` 与 `api2.pikapython.com` 应分别按各自 YAML target 验收。
