This file orients an AI assistant (or any new contributor) working in this
repository. CLAUDE.md imports it via @AGENTS.md β edit this file only.
A 2-in-1 OSINT suite for email and username intelligence. It checks
whether an account exists on a given platform and, when found, extracts rich
profile metadata. See README.md for the full feature list and install steps.
README.mdβ overview, features, installation, usage examples.CONTRIBUTING.mdβ the authoritative guide for adding scan modules (naming, validator signatures, orchestrator helpers, return values, style).docs/USAGE.mdβ library-mode usage (calling the engine from Python).docs/FLAGS.mdβ every CLI flag.docs/CROSS_SCAN.mdβ how--cross-scanmines scan metadata for usernames.docs/PATTERNS.mdβ the username/email permutation pattern syntax.
user_scanner/user_scan/<category>/<site>.pyβ username availability modules. Synchronous. Exportdef validate_<site>(user: str) -> Result.user_scanner/email_scan/<category>/<service>.pyβ email OSINT modules. Asynchronous. Exportasync def validate_<service>(email: str) -> Result.user_scanner/core/β engine, orchestrator, helpers,Result, formatters, exporters (JSON/CSV/PDF). Changes here affect every module; review carefully.abandoned/<email_scan|user_scan>/<category>/<site>.pyβ retired modules (dead sites, permanently broken detection). See "Retiring a module" below.tests/β pytest suite. Add tests for new core behavior only. Do not add unit tests for individual scan modules β modules are verified by live-testing against real and nonexistent handles (see "Before opening a PR"), not by mocked unit tests.
Read CONTRIBUTING.md first β it has full examples. The essentials:
- File name = platform name, lowercase, no spaces/special chars
(
github.py,reddit.py). - One validator per module:
validate_<name>returning aResult(Result.available(),Result.taken(extra=..., media=...),Result.error("...")). Image URLs go inmedia, other metadata inextra. - No false positives. Never infer availability from a bare HTTP 200 or a
bare
else. Verify a unique string/JSON key for both the found and not-found states. Confirm not found with an explicit marker too, not just a 404 β transient/blocked responses must returnResult.error, never a verdict. - Map every account namespace a site exposes before trusting one path; many sites split accounts across several public paths and a clean 404 on one must not launder a blocked response on another.
- Pick the transport by how the site defends itself. Reach for the
impersonating transport (
impersonate_validate/impersonate_request, curl_cffi) first for sites behind bot walls; drop togeneric_validate(httpx) only when a plain request already returns clean 200/404. - Never use
raise. ReturnResult.error(...)so the scan continues. - Respect the global CLI flags in any request-making code (timeout
-t, concurrency-C,--allow-loud) β mirror the existing helpers.
Never delete a scan module. When a site shuts down or a module can no longer
work, move the file from user_scanner/<email_scan|user_scan>/<category>/
to abandoned/<email_scan|user_scan>/<category>/ β same scan type, same
category, same file name, contents unchanged. This keeps the module out of
load_modules() while preserving it for revival if the site returns.
Local CI gates must pass:
ruff check .
mypy user_scanner
pytest
Test live against a real handle/email and a nonexistent one, across every namespace and account type the module checks β this is what catches mislabeled types, missed namespaces, and false positives/negatives.
- If the
axCLI (https://ax.yusuke.run) is available, prefer it over throwaway scripts for investigating page structure β--outline,--locate, and--rowcover most namespace-mapping probes. Runax agent-contextfirst to learn how to drive it. Note it uses a plain HTTP client, so bot-walled sites still need curl_cffi. - Sites behind active JS challenges (Cloudflare "Just a momentβ¦") can only be investigated with a real browser β e.g. the Playwright MCP server (https://github.com/microsoft/playwright-mcp).
If a task would benefit from one of these tools and it is not installed, ask
the user once whether they'd like to install it. If they decline, record the
choice in tooling.local.md (git-ignored) and don't ask for that tool again.
*.local.*anddocs/local/are git-ignored β use them for scratch notes, private scan output, and AI working files that must not be committed.output/is git-ignored β write scan outputs there (e.g.-o output/result.json).- Never commit real scan results, targets, credentials, or personal data.