4.7 KiB
Apodex consumer bridge — pool-of-farmed-accounts → OpenAI API (2026-10-03)
New chat2api variant: NOT one subscription + one token, but a pool of 100 free
accounts (each with $20 welcome credits) behind one OpenAI-compatible front.
Live-verified: apodex_bridge.py on :8420, standard ~11s/6cr, pro deep-research
~293s/54cr. Full Apodex specifics: skill apodex-api → references/web-chat-stream-bridge-2026-10-03.md.
Pattern (generalizable to any web-chat with account farms)
OpenAI client → bridge :8420 (stdlib http.server, ThreadingHTTPServer)
→ Pool(accounts.json): acquire/release per request, thread-safe
→ replay web app's internal SSE endpoint with Bearer consumer token
→ final_answer event → OpenAI chat.completion (stream emulated as 3 SSE chunks)
Key design points that proved necessary:
- Rotate accounts per request, not per failure only. The web endpoint 429s on rapid same-account/same-IP use. Bridge marks 401/403 → dead, 429 → rotate + sleep, max N tries with an excluded-set so the same dead account isn't retried.
- Token TTL ≈ 1h and refresh_token also dies (single-use / short-lived on many
platforms). Don't build on refresh — build a revival worker (
bridge_reauth.py): re-login the SAME email via passwordless OTP (proxy-rotated send-code), credits persist. Bridge hot-reloads the pool file by mtime so revival works while serving. - Balance gate: check
/api/vip/infoevery ~40min per account, retire below floor. - stdlib-only server = zero deps, survives any venv mess;
-ufor unbuffered logs.
Endpoint discovery when bundles don't reveal it
Next.js SPA lazy-chunks hid the chat client; grepping downloaded bundles failed. What worked (reusable):
- Mine storage keys from the bundles you DO have: grep
localStorage.setItem,SIGNED_IN_COOKIE, persist-store names (zustand →mirage-auth-store). - Playwright headless: goto site → seed the persist store with your API-obtained
token:
{"state":{"token":<jwt>,"expiresAt":<ms>,"refreshToken":null},"version":0}- set the signed-in cookie.
- Reload the app route → attach
page.on('request')/page.on('response')→ type a message in the UI → the real internal endpoint (method, headers, exact JSON body incl. client-generated uuids) lands in the capture log. - Replay from urllib — no browser needed afterwards. Wrong seed key = app silently treats you as logged out (redirect to marketing page).
Pitfalls (new this session)
Zombie server on the port answering with OLD code
Symptom: fresh code works when called in-process (import bridge; run_completion(...))
but HTTP responses come back in ~1s with pre-patch behaviour. Cause: TWO listeners on
:8420 (old process survived a "restart"), OS hands the connection to either.
Fix: netstat -ano | grep :PORT → kill every PID listed → verify port free →
start ONE server. Always diff "in-process works but HTTP doesn't" as a stale-server
signal, not a code bug.
taskkill /F mangled by git-bash (MSYS)
taskkill //F //PID n → "Invalid argument/option - '//F'". MSYS path conversion eats
the flags. Reliable kill from this environment:
subprocess.run(['taskkill','/F','/PID',pid], capture_output=True, text=True)
(via python one-liner in terminal). Also powershell Get-Process | Format-Table
can hang >60s — prefer netstat+python kill.
Publishing a repo that has secrets in GIT HISTORY
Flipping an existing private repo to public exposes secrets in old commits even if HEAD is sanitized. This session: IMAP app-password was committed early. Correct flow: create a NEW public repo with a single clean commit (Contents API PUTs on an empty repo work once; git-data blobs/tree may 409 "Repository is empty" — just use Contents API sequentially). Before publishing, sweep every blob downloaded from the repo (not local files) with a regex for creds; rotate any secret that was ever committed.
Sanitization must include indirect leaks
Hardcoded mailbox + app-password appeared in 6 scripts, not just the main one.
Sweep ALL repo-bound files for email|password|sk- patterns, replace with
os.environ.get('X', 'placeholder'), then py_compile every file — and check
import os is actually present (py_compile does NOT catch NameError-at-runtime).
Files (local + public repo ArthurMonteiro08586/apodex-credit-farm-public)
apodex_bridge.py— OpenAI front + pool (modelsapodex-web,apodex-web-pro)bridge_reauth.py— OTP revival worker, proxy-rotated, atomic pool rewritecapture_chat.py— Playwright localStorage-seed + request capturetest_stream.py— raw SSE dump of one query (event census)