Files
autoreg-skills-max/skills/github-autoreg/references/camoufox-registration-patterns.md

7.4 KiB

Camoufox Registration Patterns (2026-08-10)

OctoCaptcha Form Validation (_wait_form_valid)

GitHub signup has two independent gates keeping the submit button disabled:

  1. Field auto-checks (email validity, username, password)
  2. OctoCaptcha (DataDome iframe) — hidden octocaptcha-token stays empty until verification finishes

Must wait for BOTH before clicking submit. JS evaluation to probe state:

def wait_form_valid(page, timeout_s=60):
    """Wait for GitHub's async field validation AND octocaptcha to resolve."""
    deadline = time.time() + timeout_s
    while time.time() < deadline:
        state = page.evaluate("""() => {
            const out = {email_taken: false, errors: [], pending: false,
                         fields_valid: false, captcha_pending: false,
                         submit_enabled: false};
            const fields = [
                ['email', 'input[name="user[email]"], #email'],
                ['username', 'input[name="user[login]"], #login'],
                ['password', 'input[name="user[password]"], #password'],
            ];
            let successCount = 0, total = 0;
            for (const [label, sel] of fields) {
                const el = document.querySelector(sel);
                if (!el) continue;
                total += 1;
                if (el.getAttribute('aria-invalid') === 'true') {
                    const container = el.closest('auto-check') || el.parentElement;
                    const errEl = container ? container.querySelector('.error') : null;
                    const txt = errEl ? errEl.textContent.trim().replace(/\\s+/g,' ') : (label + ' invalid');
                    out.errors.push(label + ': ' + txt.slice(0, 160));
                    if (/already associated|already been taken|already taken/i.test(txt))
                        out.email_taken = true;
                } else if (el.classList.contains('is-autocheck-successful')) {
                    successCount += 1;
                }
                if (el.classList.contains('is-autocheck-in-progress')) out.pending = true;
            }
            out.fields_valid = (total > 0 && successCount === total && out.errors.length === 0);
            const tok = document.querySelector('input[name="octocaptcha-token"], .js-octocaptcha-token');
            const tokenVal = tok ? (tok.value || '').trim() : '';
            const spinner = document.querySelector('.js-octocaptcha-spinner');
            const success = document.querySelector('.js-octocaptcha-success');
            out.captcha_pending = (!!tok && !tokenVal && !success.classList.contains('d-none') && !!spinner && !spinner.classList.contains('d-none'));
            const email = document.querySelector('input[name="user[email]"], #email');
            const form = email ? email.closest('form') : null;
            if (form) {
                const b = Array.from(form.querySelectorAll('button, input[type=submit]'))
                    .find(x => (x.getAttribute('name')==='commit') ||
                               /create account/i.test(x.textContent||x.value||''));
                if (b) out.submit_enabled = !b.disabled;
            }
            return out;
        }""")
        if state.get("submit_enabled") and not state.get("pending"):
            return {"ready": True}
        time.sleep(1)
    return {"ready": False, "timeout": True}

Proper Click (NOT social buttons)

GitHub's signup page has BOTH email-signup submit buttons AND social-login buttons ("Continue with Google/Apple"). A naive page.click("button:has-text('Create account')") may click the wrong button.

Use JS evaluation to find the right button:

page.evaluate("""() => {
    const email = document.querySelector('input[name="user[email]"], #email');
    const form = email.closest('form');
    const act = (form.getAttribute('action') || '').toLowerCase();
    // Skip social/OAuth forms
    if (act.includes('/sessions/social/') || act.includes('oauth')) return 'social_form';
    const btns = Array.from(form.querySelectorAll('button, input[type=submit]'));
    const good = btns.find(b => {
        const t = (b.textContent || b.value || '').toLowerCase();
        const prov = b.getAttribute('data-provider') || '';
        if (prov) return false;  // skip social buttons
        return t.includes('create') || t.includes('account') || b.getAttribute('name') === 'commit';
    });
    if (good) { good.click(); return 'clicked'; }
    return 'no_button';
}""")

Navigation Wait (post-submit)

After clicking submit, the page navigates from /signup to /account_verifications. The navigation can be slow via proxies (30+ seconds). Instead of polling page.url (which is a live property — see pitfall), use page.wait_for_selector:

try:
    page.wait_for_selector('input[name="launch_code[]"]', state="visible", timeout=90000)
    # On verification page
except:
    # Check for rate limit or unexpected redirect
    body = page.inner_text("body")[:500]
    if "rate limit" in body.lower():
        return "rate_limited"

Camoufox Launch Options

launch_opts = {"headless": False, "geoip": True}
if proxy_config:
    launch_opts["proxy"] = {
        "server": proxy["server"],
        "username": proxy["username"],
        "password": proxy["password"],
    }
with Camoufox(**launch_opts) as browser:
    ...

geoip=True is recommended when using proxies to avoid LeakWarning.

Mail.tm API

base = "https://api.mail.tm"
# Create account
r = requests.post(f"{base}/accounts", json={"address": f"{username}@{domain}", "password": pw})
token = [REDACTED]"{base}/token", json={"address": email, "password": pw}).json()["token"]
# Poll inbox
headers = {"Authorization": f"Bearer {token}"}
r = requests.get(f"{base}/messages", headers=headers)

Pitfalls from this session

page.url is a LIVE property

page.url re-evaluates every time it's accessed. Between log(f"URL: {page.url}") and if "account_verif" in page.url:, the page can navigate. Fix: read once into a local variable.

Post-OTP "accepted" without session

After OTP, GitHub may redirect to root path (/ or /dashboard) making it look like success, but the account actually needs a post-signup login. Fix: after "accepted" result, navigate to /settings/profile. If redirected to /login, do the post-signup login.

Rate limit persistence

GitHub secondary rate limits can persist for hours, not minutes. A 15-minute wait is often insufficient. Rotate IPs aggressively.

Proxy pool dead (2026-08-10)

bpproxy.at residential pool — ALL 10 proxies dead. curl returns empty response, Camoufox returns NS_ERROR_PROXY_CONNECTION_REFUSED. Proxies are not rate-limited — they are physically unreachable. Always verify proxy alive before use:

curl -sI --max-time 10 -x <proxy_url> https://github.com | head -1

If no HTTP/1.1 200 response, proxy is dead — don't use it.

Camoufox proxy config format

Playwright REQUIRES credentials in separate username/password keys:

from core.proxy_utils import playwright_proxy_config
# Input: "http://user:pass@host:1000"
# Output: {"server": "http://host:1000", "username": "user", "password": "pass"}

Embedding credentials in the server field yields NS_ERROR_PROXY_CONNECTION_REFUSED.

mail.tm domains blocked (2026-08-10)

GitHub silently rejects accounts registered with mail.tm domains (emalupe.com). OTP is accepted, but account returns 404 on API check. Use t-online.de IMAP pool instead.