# GitHub Autoreg — Email Domain Blocklist & Working Providers Captured 2026-08-10 from extended autoreg testing with CDP + Camoufox. ## Blocked Disposable Domains GitHub **silently rejects** accounts registered with these domains. The signup form accepts the OTP, GitHub redirects to the home page (`github.com/`), but the account **never exists** (API returns 404). No error message, no warning — the rejection is silent. | Domain | Provider | Status | |--------|----------|--------| | `emalupe.com` | mail.tm | ❌ Silently blocked | | `*.mail.tm` | mail.tm (all domains) | ❌ Likely blocked | ## Confirmed Working Providers | Domain | Provider | Notes | |--------|----------|-------| | `t-online.de` | Telekom Germany | 212 accounts created 6-7 Aug 2026, all alive. IMAP-based mailbox. Requires `t_online.py` provider from `авторег проект/backend/providers/mailbox/`. 17,872 emails in `data/working_mails_live.txt`. | ## Detection: How to Tell if Account Was Created After OTP acceptance, the account may or may not exist: ```python import urllib.request, json url = f"https://api.github.com/users/{username}" req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"}) resp = urllib.request.urlopen(req, timeout=10) data = json.loads(resp.read()) if "login" in data: print(f"LIVE: {username} (created: {data.get('created_at')})") else: print(f"DEAD: {username} — 404 Not Found") ``` **Key insight:** `pending_login` status in the CDP script means "OTP was accepted but session not confirmed" — NOT that the account exists. Always verify with the GitHub API before assuming the account was created. ## Pattern: Silent Rejection Flow 1. Signup form submitted → `account_verifications` page 2. OTP code entered → form accepted 3. GitHub redirects to `https://github.com/` (home page) 4. No `user_session` cookie set 5. Navigate to `/settings/profile` → redirected to `/login` 6. GitHub API: `404 Not Found` The account was **never created**. The OTP form accepts the code but the server-side validation rejects the signup silently. ## Camoufox OTP Submit The launch-code form uses `form[data-target="launch-code.form"]`. The correct way to submit after filling individual `launch_code[]` boxes: ```python # Fill each box for i, digit in enumerate(code): try: boxes[i].click(timeout=2000) # focus except Exception: pass boxes[i].fill(digit) time.sleep(0.08) time.sleep(0.6) # Submit via JS (Playwright's click may fail on disabled button) page.evaluate(""" const f = document.querySelector('form[data-target="launch-code.form"]'); if (f) f.requestSubmit(); """) ``` `requestSubmit()` is preferred over `form.submit()` because it triggers the form's submit event handlers and validation. ## Python 3.11 Isolation from Hermes Venv When running `py -3.11`, the hermes venv's site-packages (`C:\Users\User\AppData\Local\hermes\hermes-agent-0.18.2-latest\venv\Lib\site-packages\`) can leak into `sys.path`, causing: - `PermissionError: [Errno 13] Permission denied` on playwright imports - Wrong versions of packages being loaded **Fix:** Set `PYTHONPATH=` to empty before running, or use `-I` flag: ```bash PYTHONPATH= py -3.11 main.py # or use -I (isolated mode): py -3.11 -I script.py ``` ## ENI AUTOREG v3.0 Backend - Location: `C:\Users\User\Desktop\авторег проект\backend\` - Start: `cd backend && PYTHONPATH= py -3.11 main.py` (runs on port 8002) - **CRITICAL: `PYTHONPATH=` is mandatory** — without it, hermes-venv playwright leaks into Python 3.11's sys.path causing `PermissionError` - API: FastAPI with `/api/tasks` endpoint for task creation - Task API: `POST /api/tasks` with `{"platform":"github","action":"register","count":N,"config":{...}}` - GitHub platform: `browser_register` capability via Camoufox (supported executors: `headless`, `headed`) - Mailbox: `t-online.de` via IMAP pool (17,872 emails in `data/working_mails_live.txt`). Provider key: `imap_pool` - DB: `account_manager.db` (SQLite) - Provision: `provision.py` (2FA + PAT via HTTP, no browser needed) - Warmup: `_warmup_account()` in `browser_register.py` (avatars, bio, display name) ### Task Config Format **Correct format for headed browser with ZTE proxy:** ```json { "platform": "github", "action": "register", "count": 3, "config": { "executor_type": "headed", "captcha": "yescaptcha_api", "proxy": "http://127.0.0.1:8899", "extra": { "use_proxy": "0" } } } ``` **CRITICAL**: `use_proxy:"0"` MUST be in `extra` sub-dict, NOT top-level config. The task runtime reads `extra.get("use_proxy")` (line 252 of `services/task_runtime.py`). If omitted or placed in the wrong location, the proxy pool's round-robin selection overrides the custom `proxy` URL. **Default executor**: If `executor_type` is omitted, defaults to `"protocol"` (HTTP-based), which GitHub doesn't support. Must explicitly set `"headed"` or `"headless"`. ### DataDome Captcha Solver Compatibility GitHub triggers DataDome challenge on the signup page. Only **2Captcha** supports DataDome: | Solver | DataDome Support | Status (2026-08-12) | |--------|-----------------|---------------------| | 2Captcha (`twocaptcha_api`) | ✅ Yes | Balance: ZERO (`ERROR_ZERO_BALANCE`) | | YesCaptcha (`yescaptcha_api`) | ❌ `不支持 DataDome` | — | | CapSolver (`capsolver_api`) | ❌ `不支持 DataDome` | — | When DataDome pre-solve fails, the browser continues to the signup form but may encounter additional challenges. Without a working DataDome solver, the email input may not become visible within timeout. ### IMAP Pool Provider Config (Pitfall) When updating the `imap_pool` provider via `PUT /api/providers/mailbox/imap_pool`, the `config` dict REPLACES the existing config entirely. Always include the full config: ```json { "provider_type": "mailbox", "provider_key": "imap_pool", "is_default": true, "config": { "imap_pool_file": "data/working_mails_live.txt", "imap_host": "secureimap.t-online.de", "imap_port": 993, "imap_ssl": true, "imap_pool_allow_reuse": "true" } } ``` Sending `"config":{}` wipes all fields and the pool fails to load emails. ## Proxy Pool Status | Proxy | Type | Status | Date | |-------|------|--------|------| | bpproxy.at (all) | residential + datacenter | ❌ ALL DEAD — 10/10 proxies return empty response, curl exits 0 but no HTTP headers | 2026-08-10 | | `residential-x.bpproxy.at:1000` | residential hardsession | ❌ Dead | 2026-08-10 | | `datacenter-x.bpproxy.at:2000` | datacenter hardsession | ❌ Dead | 2026-08-10 | | **ZTE mobile proxy** (`127.0.0.1:8899`) | 4G mobile (Vodafone UA) | ✅ **WORKS** — browser reaches GitHub, DataDome pre-solve triggers | 2026-08-12 | ### ZTE Mobile Proxy (2026-08-12) The ZTE MF935 4G modem provides a mobile IP that bypasses GitHub's proxy blocking. The `mobile_proxy.py` in `backend/platforms/android_gmail/` creates a local HTTP/CONNECT proxy on port 8899 that binds to the ZTE interface (192.168.0.4). **How to use:** 1. ZTE modem must be connected (check: `curl http://192.168.0.1` → redirect to index.html) 2. Mobile proxy must be running on port 8899 (check: `netstat -ano | grep ":8899"`) 3. Task config: `{"executor_type":"headed","captcha":"yescaptcha_api","proxy":"http://127.0.0.1:8899","extra":{"use_proxy":"0"}}` 4. **CRITICAL**: `use_proxy:"0"` MUST be in `extra` sub-dict, not top-level config. Without it, the proxy pool overrides the custom proxy URL. **Network topology:** - ZTE interface: `192.168.0.4` → gateway `192.168.0.1` - Default route: WiFi `192.168.50.1` (metric 55), ZTE `192.168.0.1` (metric 10000 — backup only) - Mobile proxy forces upstream through `192.168.0.4` via `socket.bind((src_ip, 0))` **Proxy format:** `http://bpuser-XXX:[REDACTED-EMAIL]:[REDACTED] **Playwright/Camoufox proxy format:** Must use `playwright_proxy_config()` from `core/proxy_utils.py` — splits URL into `{server, username, password}`. Embedding credentials in the `server` field causes `NS_ERROR_PROXY_CONNECTION_REFUSED`. **Testing proxies:** Use `curl -sI --max-time 10 -x "$URL" https://github.com` — must return HTTP/1.1 200 with headers. Empty response = dead proxy. ## CDP Sync Client (reg_cdp.py pattern) The CDP Chrome auto-registration uses a sync (non-async) client with `urllib.request` + `websocket-client`. Simpler than the async aiohttp pattern for single-threaded flows: ```python import json, urllib.request, websocket def cdp_connect(ws_url): ws = websocket.create_connection(ws_url, timeout=15) ws.send(json.dumps({"id":1,"method":"Page.enable"})) ws.send(json.dumps({"id":2,"method":"Runtime.enable"})) return ws def cdp_cmd(ws, method, params=None): mid = random.randint(10000, 99999) ws.send(json.dumps({"id":mid,"method":method,"params":params or {}})) while True: r = json.loads(ws.recv()) if r.get("id") == mid: return r.get("result", {}) def cdp_eval(ws, code): r = cdp_cmd(ws, "Runtime.evaluate", {"expression":code,"returnByValue":True}) return r.get("result",{}).get("value") # Usage: ws = cdp_connect("ws://127.0.0.1:9222/devtools/page/XXX") cdp_eval(ws, "window.location.href") ``` Chrome launch: `chrome.exe --remote-debugging-port=9222 --user-data-dir=C:\Users\User\gh_work\chrome_profile`