Usage
For operators · running scans
Referenceadler <username> scans the embedded registry; everything else is a
knob. Text output shows Found and Uncertain rows by default and hides
NotFound — pass --all for the full list. Results stream into a terminal
as they resolve; piped output is collected and ordered. For a browser
view, pass --web (see Web UI).
Exit codes: 0 something found, 1 nothing found, 2 error.
adler --help has the complete flag reference; the buckets below cover
the common ones by intent.
Filtering
Section titled “Filtering”adler --only github,gitlab alice # restrict to matching site namesadler --exclude reddit alice # drop matching site namesadler --tag social,dev alice # filter by tag(s)adler --tag region:ru alice # by region tagadler --exclude-tag bot-protected alice # skip login-walled sitesadler --list-sites --only git # discover filter terms (no scan)adler --list-tags # show all tags + countsOutput
Section titled “Output”adler --format json alice > out.json # JSON arrayadler --format ndjson alice # one JSON object per line (jq-friendly)adler --format csv alice > out.csv # spreadsheet tableadler --format html alice > out.html # self-contained HTML reportadler --report-scan <ID> > report.md # Markdown investigation reportadler --report-scan <ID> --report-format json > report.jsonadler --report-scan <ID> --report-format html > report.htmladler --all alice # include NotFound rowsadler -q alice # quiet: only Found URLsadler --explain alice # show which signal produced each verdictadler --color never alice # disable colors (also honors NO_COLOR)--format json for a live scan intentionally remains a top-level array
of CheckOutcome objects. Case-file output uses --report-scan so
downstream tools can opt into the richer InvestigationReport model
without breaking scan JSON consumers. See
Investigation reports for the full case-file
workflow.
Network & sessions
Section titled “Network & sessions”adler --concurrency 64 alice # in-flight probes (default 32)adler --max-rps 5 alice # cap total request rateadler --proxy socks5://host:1080 alice # single proxy for everythingadler --proxy-pool pool.toml alice # per-site geo/IP-type routingadler --sessions sessions.toml alice # operator-supplied sessionsadler --tor alice # local Tor SOCKS proxyadler --rotate-ua alice # rotate User-Agent per requestSee Access engine → Egress pool
for how --proxy-pool interacts with the registry’s per-site access
policies, and Access engine →
Sessions
for the session TOML format.
For TLS-fingerprint-blocked sites, build with --features impersonate
(see Access engine → TLS-fingerprint
impersonation).
Browser & cache
Section titled “Browser & cache”adler --browser-backend local alice # headless Chrome for bot-protectedadler --browser-backend browserbase alice # Browserbase cloud sessionadler --browser-budget 20 alice # cap browser-routed probes (default 50)adler --no-browser alice # off for this run
adler --escalation-budget 50 alice # automatic escalation cap (default 30)adler --no-escalation alice # cheap-path verdicts only
adler --no-cache alice # bypass the result cacheadler --cache-ttl 86400 alice # custom TTL (default 3600 s)adler --cache-clear # drop the cacheCache lives at ~/.cache/adler/. The browser backend and automatic
escalation are described in Access engine → Browser backend
and Access engine → Automatic escalation.
Batch & enrichment
Section titled “Batch & enrichment”adler --input users.txt # batch many usernames, grouped outputadler --watch alice # diff vs last run; new/removedadler --watch --interval 3600 alice # keep watchingadler --enrich alice # extract name/bio/avataradler --avatar-hash --format json alice # opt-in avatar hash evidenceadler --correlate alice # group accounts by signal overlapadler --permute aggressive alice # search spelling variantsadler --completions zsh > _adler # shell completions--avatar-hash only runs after profile enrichment has found avatar URLs.
It fetches images through bounded size/type/timeout checks and stores a
versioned perceptual hash such as dhash64_v1:...; raw image bytes are
not written to scan artifacts, reports, Web API responses, or MCP
payloads. Avatar hash matches are supporting identity evidence, not a
standalone hard merge.
Investigation reports
Section titled “Investigation reports”Finished scans persisted under ~/.cache/adler/scans/ can be rendered
as case files:
adler --report-scan scan_123 # Markdown, defaultadler --report-scan scan_123 --report-format json # direct InvestigationReport JSONadler --report-scan scan_123 --report-format html # local/offline HTML case fileReports combine the scan summary, found accounts, high-confidence
accounts, signal evidence, normalized profile evidence, confidence
reasons, identity clusters, timeline events, and limitations. JSON is
the direct InvestigationReport shape; Markdown and HTML are renderers
over the same model. Report generation applies the same read-time
historical confidence overlay as persisted Web/MCP views and does not
rewrite the stored scan artifact. The dedicated
Investigation reports guide covers CLI,
Web API, MCP, privacy, and troubleshooting details.
Doctor
Section titled “Doctor”The registry’s detection signals occasionally rot — a site changes its
response markup, or its known-present test account gets deleted. The
doctor probes both halves of every signal (a known-present user must
resolve to Found, a random nonsense user must not) and reports any
site whose detection no longer holds.
adler --doctor # check every siteadler --doctor --only github,gitlab # subsetadler --doctor --fix --only patreon # propose a corrected signatureadler --doctor --suggest-known-present # find candidate users for stale entriesadler --doctor --suggest-extract # derive extract blocks from OpenGraph; since v0.11.6adler --doctor --fix --apply --sites overrides.json --yes # patch signals in place; since v0.11.2adler --doctor --suggest-known-present --apply --sites overrides.json --yes # patch known_present in place; since v0.11.6adler --doctor --suggest-extract --apply --sites overrides.json --yes # patch extract blocks in place; since v0.11.6adler --doctor --suggest-protection # cross-scan telemetry; since v0.11.2adler --doctor --format ndjson # structured output (json|ndjson); since v0.12.0--doctor --fix diffs the present/absent responses and prints a paste-
ready signal you can drop into the registry (or a local override). A
nightly GitHub Action runs the doctor across the whole registry and flags
structural rot.
--format json / --format ndjson since v0.12.0 emits
structured output instead of the human-readable [OK] / [FAIL] lines.
json produces a single envelope {"sites":[…],"summary":{…}} (pretty-
printed); ndjson streams one record per line plus a final tagged
{"type":"summary",…} line, ideal for piping through jq or feeding
into scripts/doctor_aggregate.py (which auto-detects either format).
Each per-site record is {"name":<str>,"verdict":"healthy"|"unhealthy", "issues":[<str>]}. The --format csv and --format html modes from
the scan path don’t fit the doctor’s shape and are rejected with an
actionable error.
--apply since v0.11.2 closes the
doctor → suggestion → patch loop in three flavours:
- With
--fix— walks the JSON via--sites, replaces the matching entry’ssignalsarray with the diffed suggestion, and writes back through a sibling*.tmpso a crash mid-write leaves the original intact. - With
--suggest-known-presentsince v0.11.6 — discovers a freshknown_presentcandidate for each failing site (probes a small pool of well-known usernames) and writes the discovered value into the entry’sknown_presentfield. Same atomic-rename pattern. - With
--suggest-extractsince v0.11.6 — walks every healthy site that doesn’t yet declare anyextractrules, fetches theknown_presentprofile page, and minesOpenGraph(og:title/og:description/og:image) and Twitter Card meta tags to derive a candidateextractblock. The derived rules read each tag’scontentattribute, so they survive unrelated CSS churn as long as the meta block itself stays put. Sites with an existingextractarray are skipped so hand-authored selectors aren’t clobbered.
Every flavour prints a per-site - old + new diff and prompts once
for confirmation; --yes skips the prompt for CI batch repair.
Sites with no suggestion are skipped, names absent from the JSON file
are reported and skipped (never erased), and --apply requires
--sites <writable> because the embedded registry isn’t patchable in
place. A bare --apply --sites … without any of --fix,
--suggest-known-present, or --suggest-extract errors out —
--apply is the verb, the other flag is the noun.
--suggest-protection since v0.11.2
reads the persisted scan history (default
$XDG_CACHE_HOME/adler/scans/, override with --scans-dir) and
surfaces sites that consistently escalated through the browser
backend. These are candidates for adding protection: cloudflare
to sites.json so future scans skip the failing HTTP probe and pick
the right transport up front. Pure suggestion path — never auto-
modifies, same convention as --suggest-known-present. Output is a
paste-ready table plus a PROTECTION additions: block.
MCP server since v0.12.0
Section titled “MCP server since v0.12.0”adler --mcp # stdio (Claude Desktop / Cursor / local agents)adler --mcp-http 127.0.0.1:8766 # HTTP+SSE, endpoint http://127.0.0.1:8766/mcpAdler ships a Model Context Protocol
server (adler-mcp crate, default-on) that exposes the OSINT surface
to AI agents. Same tool / resource / prompt set regardless of
transport — pick stdio when the agent spawns Adler as a subprocess
(Claude Desktop, Cursor); pick HTTP+SSE when the agent runs out-of-
process or on a different host.
Tools (callable agent actions):
list_sites— browse the enabled registry; filter bytag/exclude_tag/include_nsfw.scan_username— single-username scan; streams per-site progress as MCPnotifications/progresswhen the client supplies aprogressToken.scan_batch— sequential multi-username scan; same sharedScanFiltershape asscan_username.doctor_check— health probe one named site; useful for triaging “why didn’t this site come back Found?”.get_scan_history— recent persisted scans from$XDG_CACHE_HOME/adler/scans/(whereadler --webwrites).diff_scans— compare two persisted scan ids; returns added / removed Found accounts, verdict changes, and profile-evidence changes.get_investigation_report— return a case-level report as direct JSONInvestigationReportor rendered Markdown.
Resources (browsable data):
adler://registry/sites— every enabled site (compact: name, URL template, tags, popularity).adler://registry/tags— every tag with its enabled-site count.adler://registry/disabled— disabled entries with theirdisabled_reasonannotations (audit surface).adler://scans/recent— recent persisted scan summaries.adler://scans/{id}— full envelope for one scan (template).adler://scans/{from}/diff/{to}— persisted scan diff (template).adler://timelines/{username}— added / removed / reappeared / evidence-changed timeline for a username.adler://reports/{id}— JSONInvestigationReportfor a persisted scan.
scan_username, scan_batch, persisted scan resources, and reports
surface evidence, profile_evidence, confidence, transport
metadata, and identity_clusters. Agent prompts instruct consumers to
use clusters first, cite confidence reasons, and explicitly mark
uncertain: true clusters as tentative.
Prompts (templated OSINT workflows the agent can prompts/get):
investigate_username(username, regions?, categories?)— full OSINT walk for one identity.audit_registry_health(focus?)— doctor + dedup + disabled audit with a built-in ~5-site doctor budget.correlate_accounts(usernames)— scan a list and look for shared profile signal (Strong / Plausible / Weak / Distinct rubric).
Security defaults. The HTTP transport binds loopback by default
and inherits rmcp’s allowed_hosts filter (localhost, 127.0.0.1,
::1) as a DNS-rebind guard. Binding a non-loopback address exposes
the API without authentication — only do it on a trusted network.
Stdio carries no network surface and is the recommended transport
for desktop integrations.
Ethical line. The MCP instructions block sent to the agent on
initialize restates the project’s bound: authorised security
testing / OSINT research / defensive work only; no harassment /
doxxing / unauthorised surveillance. The investigate_username
prompt repeats this in its body for context.
When things go wrong
Section titled “When things go wrong”Real shapes for the most common failure modes — copy-and-paste examples of what each error looks like so you can match a pattern in your own output without guessing.
Unknown egress name from POST /api/scan since v0.11
Section titled “Unknown egress name from POST /api/scan since v0.11”When the SPA’s Advanced filters → Egress section sends an egress_names
entry that’s not in the loaded pool, the API rejects at the boundary
rather than silently dropping to “no egress matched”:
HTTP/1.1 400 Bad RequestContent-Type: application/json
{ "error": "unknown_egress", "message": "egress not in pool: us-residential-typo"}CLI equivalent: the --proxy-pool TOML is read at startup, so a typo
in the file surfaces as a config error before any scan runs.
Site policy needs an egress the pool can’t supply
Section titled “Site policy needs an egress the pool can’t supply”The site declares access.geo = ["pl"] (a Polish IP), the pool has
nothing tagged pl. The probe never goes out; the verdict carries the
reason:
{ "site": "VK", "kind": "uncertain", "reason": "geo_unavailable", "transport": "http", "escalations": 0, "elapsed_ms": 0}Remedy: add a Polish egress to pool.toml, or accept the Uncertain (a
location you can’t reach is not evidence the account is absent).
Named session missing
Section titled “Named session missing”The site declares access.session = "ig" and you didn’t pass
--sessions <file> (or the file doesn’t have an [ig] block). Same
pattern, different reason:
{ "site": "Instagram", "kind": "uncertain", "reason": "session_required", "transport": "http", "escalations": 0, "elapsed_ms": 0}Remedy: copy the Instagram cookies from your browser’s devtools into
[ig] block of sessions.toml.
Deadline exceeded mid-scan
Section titled “Deadline exceeded mid-scan”You set --deadline-secs 30 on a 2,000-site scan and ran out of time.
Sites that didn’t get their turn surface with:
{ "site": "Wattpad", "kind": "uncertain", "reason": "deadline", "elapsed_ms": 0}Remedy: narrow with --tag / --only, raise --deadline-secs, or
raise --concurrency.
Browser budget exhausted
Section titled “Browser budget exhausted”--browser-budget 5 set, more than five bot-protected sites in the
scope:
{ "site": "Threads", "kind": "uncertain", "reason": "browser_budget", "elapsed_ms": 0}The first five bot-protected sites went through the browser
successfully; the rest fall back to this honest Uncertain rather than
silently dropping out of the scan. Remedy: raise --browser-budget or
--exclude-tag bot-protected to skip the subset entirely.