# 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: 1. **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. 2. **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. 3. Balance gate: check `/api/vip/info` every ~40min per account, retire below floor. 4. stdlib-only server = zero deps, survives any venv mess; `-u` for 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): 1. Mine storage keys from the bundles you DO have: grep `localStorage.setItem`, `SIGNED_IN_COOKIE`, persist-store names (zustand → `mirage-auth-store`). 2. Playwright headless: goto site → **seed the persist store** with your API-obtained token: `{"state":{"token":,"expiresAt":,"refreshToken":null},"version":0}` + set the signed-in cookie. 3. 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. 4. 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: ```python 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 (models `apodex-web`, `apodex-web-pro`) - `bridge_reauth.py` — OTP revival worker, proxy-rotated, atomic pool rewrite - `capture_chat.py` — Playwright localStorage-seed + request capture - `test_stream.py` — raw SSE dump of one query (event census)