Files
autoreg-skills-max/skills/account-validation/references/github-autoreg-email-domain-blocklist.md

9.1 KiB

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:

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:

# 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:

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:

{
  "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:

{
  "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:

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