Imported from xsser/cliproxyapi-cloudflare-stack (
SKILL.md). Install upstream withnpx skills add xsser/cliproxyapi-cloudflare-stack. Copyright stays with the author.
Self-hosted LLM gateway: CLIProxyAPI + Cloudflare (Workers / D1 / Tunnel)
A reusable, sanitized deployment template for standing up your own OpenAI-compatible API gateway:
- A Cloudflare Worker + D1 that stores inbound mail for an address you own.
- CLIProxyAPI (CPA) running on a VM, exposing
/v1/*, hot-reloading credential files from a local auth directory. - A Cloudflare Tunnel fronting CPA with HTTPS (no inbound port, no public IP needed).
Authorized-use notice (required reading)
This skill is for learning the deployment architecture and for managing credentials you have legitimately obtained and are authorized to use. It does not provide guidance for automated/batch account registration, for bypassing CAPTCHA / Turnstile / anti-bot controls, or for circumventing any platform's access restrictions. You are responsible for complying with the Terms of every third party (including, without limitation, x.ai and Cloudflare). Provided "as is", no warranty.
Architecture
[Cloudflare] <your-domain> Email Routing catch-all → Worker <worker-name> → D1 (inbound mail store)
[VM] CLIProxyAPI on :8317, auth-dir hot-reloads credential .json files (the pool)
[Cloudflare] Tunnel: https://<api-subdomain>.<your-domain> → VM:8317 (HTTPS front, NSG stays closed)
Prerequisites
- A Cloudflare zone with Email Routing enabled (MX → Cloudflare), and a credential
with Email Routing Rules: Edit (Global API Key, or a token explicitly scoped
Zone → Email Routing Rules → Edit). Workers/D1-only is insufficient. - A Linux VM (Ubuntu 24.04 tested), passwordless sudo, SSH access.
python3(3.12/3.13), Cloudflarewrangleror the CF REST API,gh,git.
Phase 1 — Cloudflare mail-receiving Worker
- Clone dreamhunter2333/cloudflare_temp_email;
cd worker && pnpm install. - Create D1
tempmail-db; record<d1-database-id>. Ifwrangler d1 createtimes out (e.g. behind a 30s-limited runner), use the REST API:POST https://api.cloudflare.com/client/v4/accounts/<cf-account-id>/d1/database. - Initialize the schema (
db/schema.sql). The REST/queryendpoint accepts the whole multi-statement SQL in one{"sql": ...}POST. - Write
wrangler.toml(seetemplates/wrangler.toml.template):PREFIX,DOMAINS=["<your-domain>"],ADMIN_PASSWORDS=["<admin-password>"],JWT_SECRET="<jwt-secret>", the D1 binding. wrangler deploy. Recordhttps://<worker-name>.<workers-subdomain>.workers.dev.- Email Routing catch-all → worker:
PUT /zones/<cf-zone-id>/email/routing/rules/catch_allwith{"matchers":[{"type":"all"}],"actions":[{"type":"worker","value":["<worker-name>"]}],"enabled":true}(needs Email Routing Rules:Edit). - Smoke test:
POST /api/new_address {"name":"smoke","domain":"<your-domain>"}→{address, jwt};/admin/new_addresswithx-admin-auth: <admin-password>likewise.
Phase 2 — CLIProxyAPI on the VM (the gateway)
curl -fsSL https://raw.githubusercontent.com/router-for-me/cliproxyapi-installer/refs/heads/master/cliproxyapi-installer | bash→~/cliproxyapi/, autosk-...key inconfig.yaml,auth-dir: ~/.cli-proxy-api, port8317.- Record
<cpa-api-key>from~/cliproxyapi/config.yaml. systemctl --user enable --now cliproxyapi.service(+loginctl enable-linger <user>).- Verify:
curl -H "Authorization: Bearer <cpa-api-key>" http://localhost:8317/v1/models→{"data":[],...}.
VM deps (Ubuntu 24.04)
sudo apt-get install -y python3-pip python3-venv python3-tk libnss3 libnspr4 libatk1.0-0t64 libatk-bridge2.0-0t64 libcups2t64 libxkbcommon0 libxcomposite1 libxdamage1 libxfixes3 libxrandr2 libgbm1 libpango-1.0-0 libcairo2 libasound2t64 libatspi2.0-0t64 libdrm2 libxshmfence1 fonts-liberation libvulkan1
- Gotcha: 24.04 renamed
libpango1.0-0→libpango-1.0-0.
Phase 3 — Expose CPA over HTTPS (Cloudflare Tunnel)
- Install
cloudflaredon the VM. - Create a named tunnel (API):
POST /accounts/<cf-account-id>/cfd_tunnel {"name":"cpa","tunnel_secret":"<base64 32 bytes>","config_src":"cloudflare"}→ returnsid+token. - Set ingress:
PUT /accounts/<cf-account-id>/cfd_tunnel/<id>/configurationswithingress:[{hostname:"<api-subdomain>.<your-domain>",service:"http://localhost:8317"},{service:"http_status:404"}]. - DNS: CNAME
<api-subdomain>→<id>.cfargotunnel.com(proxied). - systemd service:
cloudflared tunnel --no-autoupdate run --token <token>(seetemplates/cloudflared-cpa.service.template). - Point any OpenAI-compatible client at
https://<api-subdomain>.<your-domain>/v1with<cpa-api-key>.
Phase 4 — Ingesting credentials (your responsibility)
CPA hot-reloads any <prefix>-*.json credential file dropped into its auth-dir
(~/.cli-proxy-api). To add a credential you are authorized to use, simply place the file
there (or rsync/scp it in — see scripts/sync_credentials.sh); CPA picks it up
automatically and begins serving its models. Verify with /v1/models and a test
/v1/chat/completions call.
This skill does not cover how credentials are obtained. Source your credentials through compliant channels. The upstream grokRegister-cpa project is a third-party integration you may evaluate independently; its compliance is your responsibility and is not endorsed here.
Operational notes (deployment gotchas)
- Email Routing Rules permission: a Workers/D1-only token returns
10000on the catch-all rule. Use a Global API Key or a token explicitly carrying the permission. - DrissionPage
4.1.1.2is yanked on PyPI → use4.1.1.4if you run a project that depends on it. - NSG blocks inbound 8317 by default → use the Tunnel (Phase 3), don't open the port.
- D1 init without wrangler: the REST
/queryendpoint accepts multi-statement SQL in one POST (one result-group per statement). - Browser session isolation for any account-creation flow: launch each session in a
fresh ephemeral profile (DrissionPage:
auto_port()→ a unique temp user-data-dir per Chromium instance, zero cookie/storage carry-over between accounts). Do NOT add--incognito: in this flow it makes DrissionPage'spage.cookies()return[], so the post-signup SSO cookie is never captured and registration fails —auto_portalready gives you the equivalent isolation without that conflict. For full anti-tracking hygiene also wipe the temp profile dir after each session (rm -rf "$TMPDIR/DrissionPage/autoPortData"/*) so nothing persists on disk. Residual linkage vectors you can't hide from the upstream: shared egress IP (rotate residential proxies to mitigate) and a stable browser fingerprint (don't fake the UA — a UA/JS mismatch trips Turnstile). - Force ALL browser traffic through the proxy (else leaks reveal your real IP):
--proxy-server=...only covers HTTP/HTTPS. Add--disable-quic(QUIC/UDP bypasses HTTP proxies) and--force-webrtc-ip-handling-policy=disable_non_proxied_udp(WebRTC ICE host candidates leak the local/public IP). Verify with a WebRTC leak check + an IP echo page that only the proxy's IP appears (HTTP egress) and no host candidates are gathered.
Verification gate
A /v1/chat/completions call through your public CPA endpoint returns a non-empty
reply. That (and only that) proves the gateway is wired end-to-end.
License & responsible use
MIT — educational / authorized-use deployment of your own infrastructure. Respect all third-party Terms of Service.
