Self-hosted challenge orchestration for AI agents and authorized automation.
A provider-agnostic integration layer that helps an AI agent handle web challenges locally when an authorized application presents Cloudflare, Turnstile, reCAPTCHA, hCaptcha, AWS WAF, or image CAPTCHA.
Each user clones and runs the toolkit on their own laptop, workstation, or VPS with their own browser runtime, provider credentials, quotas, and network policy. This repository is not a shared solver service and does not use the UcelX VPS as a public endpoint.
AI agents that browse authorized applications can encounter a challenge in the middle of a workflow. Without an integration layer, every agent ends up with hardcoded provider calls, inconsistent error handling, leaked tokens in logs, and fragile retries.
This project standardizes that boundary:
AI agent → challenge detector → solver router → local/provider adapter
← authorized result + safe metadata ←
The toolkit solves the integration problem—configuration, routing, validation, redaction, and failure handling. It does not promise that every challenge can be solved, and it never invents a successful result.
flowchart LR
A[AI Agent] --> B[Authorized target]
B -->|challenge detected| C[Challenge Request]
C --> D[Target allowlist + SSRF guard]
D --> E[SolverRouter]
E --> F[Local browser/service]
E --> G[User-configured provider]
F --> H[Semantic validation]
G --> H
H -->|solved=true| I[In-memory handoff]
H -->|failure| J[Retry / next provider / stop]
I --> K[Agent continues workflow]
J --> L[Redacted error + metrics]
| Stage | Responsibility | Safety rule |
|---|---|---|
| 1. Detect | Agent identifies a challenge in an authorized flow | Do not automate outside scope |
| 2. Describe | Build a typed request: type, URL, sitekey, action, timeout | Validate URL and target allowlist |
| 3. Route | Try local-first providers in configured order | Never use unrelated credentials |
| 4. Solve | User-owned browser, sidecar, or API adapter handles the challenge | Preserve browser/network/session context |
| 5. Verify | Parse HTTP status and response body | 200 is not success unless solved=true |
| 6. Handoff | Return only the minimum in-memory result to the agent | Never log tokens or cookies |
| 7. Recover | Retry, switch provider, or stop with a clear error | Fail closed; no fabricated token |
┌──────────────────────────────────────────────────────────────┐
│ User's AI Agent │
│ browser automation · form workflow · authorized API client │
└──────────────────────────────┬───────────────────────────────┘
│ SolveRequest
▼
┌──────────────────────────────────────────────────────────────┐
│ CAPTCHA Solver Toolkit │
│ typed models · URL policy · router · redaction · timeouts │
└───────────────┬───────────────────────┬──────────────────────┘
│ │
▼ ▼
┌─────────────────┐ ┌──────────────────────┐
│ Local adapters │ │ User provider adapter │
│ browser/sidecar │ │ API / self-hosted │
└────────┬────────┘ └──────────┬───────────┘
└──────────────┬─────────┘
▼
SolveResult + safe metadata
│
▼
Agent resumes its flow
- Local-first: prefer services running on the user's machine or private network.
- Provider-agnostic: adapters share one request/result contract.
- Context-aware: browser, user agent, network route, cookies, and challenge output may need to stay together.
- Fail-closed: provider errors remain errors; no guessed success.
- Secret-safe: tokens, cookies, API keys, authorization headers, and proxy userinfo are redacted.
- Observable: record provider name, elapsed time, and error class—not secret values.
- Explicit scope: authorized targets and user-owned infrastructure only.
| Challenge | Request style | Current repository status |
|---|---|---|
| Cloudflare Managed Challenge | Page URL + browser context | Guide + generic HTTP adapter |
| Turnstile | URL + sitekey + optional action | Guide + generic HTTP + Capsolver task adapter |
| reCAPTCHA v2/v3/Enterprise | URL + sitekey + version/action | Guide + adapter contract |
| hCaptcha | URL + sitekey + browser context | Guide + adapter contract |
| AWS WAF | Page-level URL | Guide + adapter contract |
| Image CAPTCHA | Image fixture + local service | Generic adapter contract |
| BotGuard | Page-level browser signal | Documented boundary; no replay implementation |
| DataDome / PerimeterX / Akamai | Page-level vendor-specific flow | Roadmap |
| Aliyun / Arkose puzzles | CV/model-assisted flow | Roadmap; no model binaries vendored |
A guide or adapter contract is not the same as a guaranteed live solver. Tokens and clearance cookies can be bound to origin, IP, browser fingerprint, action, session, and expiry.
.
├── captcha_toolkit/
│ ├── config.py # environment settings and bounded timeouts
│ ├── models.py # SolveRequest and SolveResult
│ ├── providers.py # HTTP + Capsolver adapters and semantic validation
│ ├── policy.py # hostname allowlist, DNS resolution, SSRF protection
│ ├── redaction.py # recursive secret redaction
│ └── router.py # local-first fail-closed routing
├── docs/
│ ├── solver-guides.md # challenge-by-challenge integration guide
│ └── upstream-capability-reference.md
├── examples/
│ ├── healthcheck.py
│ └── unified_router.py
├── tests/
├── .github/workflows/test.yml
├── docker-compose.example.yml
├── .env.example
└── requirements.txt
git clone https://github.com/UcelX/captcha-solver.git
cd captcha-solver
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
pip install pytest
cp .env.example .env
python examples/healthcheck.py
pytest -qExpected local test result:
14 passed
# Local/private services owned by the user
SELF_HOSTED_SOLVER_URL=http://127.0.0.1:8001
SECONDARY_SOLVER_URL=http://127.0.0.1:8002
SOLVER_TIMEOUT_S=60
# Optional provider credentials belong in the user's private .env.
# Never commit real values.
# CAPSOLVER_API_KEY=
# TWOCAPTCHA_API_KEY=The repository does not ship a CAPTCHA backend, provider keys, cookies, proxies, or a central endpoint. Users connect their own adapters according to the solver guides.
from captcha_toolkit import Settings, SolveRequest, SolverRouter, TargetPolicy
from captcha_toolkit.providers import HTTPProvider
settings = Settings.from_env()
policy = TargetPolicy(allowed_hosts={"your-authorized-test.example"})
router = SolverRouter([
HTTPProvider("local", settings.local_url, settings.timeout_s, policy=policy),
# Add a second user-owned provider here when configured.
])
result = router.solve(SolveRequest(
challenge_type="turnstile",
url="https://your-authorized-test.example/form",
sitekey="TEST_SITEKEY",
timeout_s=settings.timeout_s,
metadata={"action": "submit"},
))
if not result.solved:
raise RuntimeError(f"challenge failed: {result.error}")
# Consume sensitive material only in memory and only in the authorized flow.
print(result.provider, result.elapsed_s, result.metadata)Recommended local-first order:
1. User-owned browser or local sidecar
2. User-owned self-hosted solver
3. Explicitly configured provider API
4. Stop and report the failure
Fallback must be explicit. The router must not silently switch egress, reuse another user's session, or write credentials into logs.
Before deploying a page-level adapter:
- Allowlist authorized domains.
- Reject non-HTTP(S) schemes.
- Block loopback, private, link-local, multicast, and metadata-service addresses by default.
- Bind local services to
127.0.0.1or a protected private network. - Require authentication before remote access.
- Redact query strings that may contain secrets.
- Keep challenge output in memory and destroy temporary artifacts after use.
This project is for applications you own or are explicitly authorized to test. It does not provide credential stuffing, unauthorized account creation, private-cookie replay, or a public bypass endpoint.
Live CAPTCHA solving is intentionally excluded from CI. Deterministic tests cover:
- URL scheme, hostname allowlist, DNS, and non-public-address rejection
- Semantic
solved=truehandling - Fail-closed router behavior
- Recursive secret redaction
- Bounded environment timeout configuration
pytest -qGitHub Actions runs the test suite on pushes and pull requests.
The included docker-compose.example.yml is intentionally generic and localhost-bound. Replace the example image with a solver service the user owns, then add authentication and network controls before any remote deployment.
User laptop/VPS A User's AI agent
┌─────────────────────┐ ┌──────────────┐
│ toolkit + adapters │◄──────────►│ browser flow │
│ local sidecars │ └──────────────┘
└─────────────────────┘
No request is sent to the UcelX VPS by default.
- Solver Guides
- Upstream Capability Reference
- Advanced Solver Patterns
- Troubleshooting
- Adapter Roadmap
- Unified Router Example
- Healthcheck Example
- Typed request/result contract
- Local-first router
- Semantic response validation
- Recursive secret redaction
- Challenge-specific guides
- CI and deterministic tests
- Domain allowlist and SSRF protection
- Provider health scoring and circuit breaker
- Async task/queue adapter
- Browser lifecycle manager
- Metrics exporter with redacted labels
- Optional adapters for DataDome, PerimeterX, Akamai, Aliyun, and Arkose after implementation and license review
- Fork the repository.
- Add a focused adapter or documentation improvement.
- Include deterministic tests and update the capability matrix.
- Run
pytest -q. - Check the staged diff for secrets before opening a pull request.
Never include API keys, cookies, proxy credentials, account data, or production screenshots in issues or pull requests.
MIT — see LICENSE.
Capability coverage was reviewed against waguriagentic/captcha-solver. No upstream source files or model binaries are vendored in this repository.
Use this toolkit responsibly, only within authorized scope and applicable laws. No solver can guarantee success against every modern web challenge.
Built for practical AI-agent integration: route clearly, validate semantically, redact aggressively, and fail honestly.