In front of the user's existing and already verified CLIProxyAPI upstream, add a NewAPI
(calciumion/new-api) billing, rate limiting, and multi-user token layer. This Skill does not install a bare
CLIProxyAPI, nor does it call other deployment Skills to fill in this prerequisite.
All "completion" conclusions must be based onthis session'scommand output (W-16). After price and quota changes, you must actually send a request and check
logs.quota matches this request's token count and the target price.
Operating Contract
- Direct actions: read-only checks, configuration backups, and local dry-runs can be executed directly.
- Escalate before: installing containers, modifying an existing upstream, restarting services, changing firewalls, or writing production quotas requires explicit authorization for this task.
- Evidence-backed pushback: read secrets only from environment variables or a password manager; when asked to write them in plaintext to disk or expose raw ports to the public internet, show the specific risks and switch to a tunnel/TLS.
- Feedback loop: after every change, rerun upstream, NewAPI, and precise billing verification; if any step cannot obtain the real state, stop and report, and do not fake success with default values.
0. Prerequisite confirmation (must ask)
- SSH target:
root@HOSTwhether passwordless - Port allocation:
CLIPROXY_PORT(default8317)、NEWAPI_PORT(default8200) - upstream evidence: CLIProxyAPI is already running on the target host, and the current session can verify health, model list, and authentication through a user-approved SSH tunnel or loopback request; if the evidence is insufficient, stop, do not guess, and do not automatically install a bare upstream
- Login account provider:
codex/claude/qwen/iflow/gemini - Price input format: for each virtual model, give me three USD per 1M numbers for input / cached / output
- Client machines: on which machines to place the
BASE_URLenvironment variable
Secure default: local OAuth + scp sync; NewAPI binds only to the VPS loopback, and first registration is completed through an SSH tunnel. Public access must go through a reverse proxy configured with TLS, and the raw NewAPI HTTP port must not be exposed directly.
Phase 1 — Verify the existing CLIProxyAPI upstream
First record the existing service's process, listening address, health response, model list, and configuration backup location. If any item cannot be verified, stop. Only when the user approves modifying this existing upstream in the current message should you apply the following two patches:
-
Add stability switchto
/root/CLIProxyAPI/config.yaml:disable-cooling: trueSee
references/troubleshooting.md"CLIProxyAPI cooldown principle". If this change is missed, 30 concurrent 5KB payloads will produce a mix of ~50% 503s. -
Do not expose the CLIProxyAPI port to the public internet(different from a bare deployment):
- First use
ip -4 addr show docker0to confirm the Docker bridge address, then bindhostto that address (common value is172.17.0.1) - Delete the existing public
ufw allow <CLIPROXY_PORT>/tcp; management access should uniformly go through an SSH tunnel, and do not keep a public admin backdoor
- First use
Phase 2 — Deploy the NewAPI container
IMAGE='calciumion/new-api@sha256:<VERIFIED_DIGEST>' \
SSH_TARGET=root@<HOST> SSH_KEY=~/.ssh/id_ed25519 PORT=8200 \
scripts/deploy_newapi.sh
The script binds only to the remote 127.0.0.1:8200. Before first registration, keep the firewall closed for that port and establish an
SSH tunnel:
ssh -N -L 8200:127.0.0.1:8200 -i <KEY> <SSH_TARGET>
Access only local http://127.0.0.1:8200 Complete root account registration, and save the password to a password
manager. After registration and login verification succeed, then configure the Caddy/Nginx reverse proxy with TLS; first
verify HTTPS, authentication, and origin restrictions, then open as needed 443/tcp. It is forbidden to open <NEWAPI_PORT>。
Phase 3 — Channels + Token
3a. Create a channel (CLIProxyAPI as upstream)
NewAPI backend → "Channels" → New → OpenAI type:
- base_url:
http://172.17.0.1:<CLIPROXY_PORT>⚠️ Cannot write127.0.0.1— the 127.0.0.1 inside the container is the container itself. Seereferences/troubleshooting.md"Container networking quick notes". - Key: CLIProxyAPI's
cpa_xxxkey - Models: fill in the virtual model names exposed by CLIProxyAPI, comma-separated, for example:
gpt-5.4,gpt-5.3-codex,gpt-5.3-codex-spark,gpt-5.4-mini,gpt-5.2 - The test button should return 200 OK; if it fails, first check
references/troubleshooting.md。
3b. Create a Token (for clients)
NewAPI backend → "Tokens" → New:
- Name:
client-defaultand similar identifiers - QuotaFirst put
unlimitedor a large number (actual billing is controlled by user.quota) - Available models: check the models you have opened to the client
- Copy the generated
sk-xxx(this is the client's BASE_API_KEY)
Phase 4 — Write prices + top up quota
4a. Write prices
Use scripts/set_pricing.py(automatically calculates the three multipliers based on measured USD/1M):
SSH_TARGET=root@<HOST> SSH_KEY=~/.ssh/id_ed25519 \
scripts/set_pricing.py \
--model gpt-5.4 --input 2.5 --cached 0.25 --output 15 \
--model gpt-5.3-codex --input 1.75 --cached 0.175 --output 14
The script will: merge and write into the options table's ModelRatio / CacheRatio / CompletionRatio, and restart the container.
⚠️ For multiplier semantics see references/newapi-pricing.md —— CacheRatio / CompletionRatio isa multiple relative
to the input price, not an absolute unit price.ModelRatio In different forks it may be divided by 2; the first time you configure it, be sure to send a request
and check whether the actual value of logs.quota matches expectations.
4b. Top up quota
NewAPI online top-up is usually not configured. When you need to urgently correct quota, you can use the parameterized SQLite helper:
SSH_TARGET=root@<HOST> SSH_KEY=~/.ssh/id_ed25519 \
scripts/topup.sh <user_id> <quota>
# 例:1 1000000000 → 1B quota ≈ USD 2000 (默认 QuotaPerUnit=500000)
4c. Configure online top-up (optional)
If you want to let users top up by themselves, NewAPI backend → System → Payment:
- Stripe / EasyPay / Custom → fill in
TopUpLinkand other fields - When online top-up is not configured, administrators can use the verified
scripts/topup.sh; the input must be an integer, and the script will parameterize the SQL and verify that exactly one row is updated
Phase 5 — Client environment variables
Write only non-sensitive configuration into the client shell config:
export BASE_URL="https://<NEWAPI_DOMAIN>/v1"
export BASE_MODEL="<虚拟模型名>" # 如 gpt-5.4
The API key must be stored in Keychain or another password manager, and read before the current process starts; it is forbidden to write it into
~/.zshrc, chats, scripts, or command-line arguments. For example, on macOS you can use:
export BASE_API_KEY="$(security find-generic-password -s newapi-client -a client-default -w)"
When syncing across multiple machines (W-14 file ownership): manually SSH into each machine one by one and edit its own rc file, to avoid parallel writes overwriting each other.
Phase 6 — Add a new account (OpenAI/Anthropic/etc)
No need to restart the service; CLIProxyAPI has a file watcher.
Simplest:
PROVIDER=codex \
SSH_TARGET=root@<HOST> SSH_KEY=~/.ssh/id_ed25519 \
CLIPROXY_LOCAL=<本地 CLIProxyAPI 仓库路径> \
scripts/add_codex_account.sh
Script flow:
- Start locally
go run ./cmd/server -<provider>-login -config config.yaml - browserwith the new accountlog in (first log out of the old account in the browser or use an incognito window — otherwise it will reuse the old session and overwrite the old credentials back)
- Automatically diff
~/.cli-proxy-api/<provider>-*.json, and copy the new filescpto the VPS - tail the VPS logs to confirm
auth file changed (CREATE)
See references/multi-account.md(including subscription conditions, polling semantics, account deletion, verification methods).
Verification (must run after Phase 4/5/6)
SSH_TARGET=root@<HOST> SSH_KEY=~/.ssh/id_ed25519 \
CLIPROXY_URL=http://127.0.0.1:<TUNNELED_CLIPROXY_PORT> \
NEWAPI_URL=https://<NEWAPI_DOMAIN> \
CLIPROXY_KEY="$(security find-generic-password -s cliproxy-admin -w)" \
NEWAPI_TOKEN="$(security find-generic-password -s newapi-client -a client-default -w)" \
MODEL=<虚拟模型> INPUT_USD_PER_M=<输入价> OUTPUT_USD_PER_M=<输出价> \
QUOTA_PER_UNIT=500000 \
scripts/verify_stack.sh
Pass criteria:
- Direct CLIProxyAPI HTTP 200
- Via NewAPI HTTP 200
logscorrespondingrequest_id, model and token count match this request- Actual
quotaerror from the target input/output price calculation result does not exceed 5% (and at least 1 quota of integer rounding is allowed)
If any is not satisfied, you must not claim "deployment complete".
Resource Index
| File | Purpose |
|---|---|
scripts/deploy_newapi.sh |
NewAPI container one-click deployment + health self-check |
scripts/set_pricing.py |
Automatically write NewAPI ratios using the three USD/1M parameters |
scripts/topup.sh |
Directly modify via SQLite users.quota |
scripts/verify_stack.sh |
Dual-path + billing log verification |
scripts/add_codex_account.sh |
OAuth login + credential sync + watcher validation |
agents/openai.yaml |
Agent configuration for when independent review of high-risk deployment or billing schemes is needed |
references/newapi-pricing.md |
ModelRatio / CacheRatio / CompletionRatio / QuotaPerUnit full semantics + calculation examples |
references/troubleshooting.md |
Pitfall table for container networking, cooldown, PUT not taking effect, UFW, etc. |
references/multi-account.md |
Multi-account polling semantics + adding / deleting accounts / subscription conditions |
Things not to do
- ❌ Write NewAPI channel base_url as
http://127.0.0.1:<port>(self-reference inside the container) - ❌ Treat
CacheRatioas an absolute unit price (it is actually a multiplier relative to input) - ❌ Prices only go through
/api/option/PUT without verification (this interface is known to possibly fail silently) - ❌ Tinkering with remote OAuth tunnels (5-minute window + tunnel coordination, pitfalls already encountered, uniformly use local login + scp)
- ❌ Share CLIProxyAPI public port's
cpa_key and NewAPI'ssk-token with the same type of client - ❌ Claim "price is in effect" across sessions—you must send a request in this session + check
logs.quota - ❌ Without
disable-cooling: trueperform high-concurrency stress testing