Imported from 7onez/cti-expert (
SKILL.md). Install upstream withnpx skills add 7onez/cti-expert. Copyright stays with the author.
CTI Expert
Cyber threat intelligence and open-source intelligence skill. Turns Claude into a trained CTI/OSINT analyst. Generates precision search queries, interprets public data, builds case timelines, and delivers structured intelligence products — no API keys, no paid subscriptions.
Runs anywhere. Works in Claude Code (Desktop & CLI) and in OpenAI Codex / ChatGPT and other
AGENTS.md-aware agents — seeAGENTS.mdfor the cross-agent runtime contract. Throughout this file,$SKILL_DIR= the directory containing thisSKILL.md(Claude Code:~/.claude/skills/cti-expert; Codex/manual clone: the repo you are working in). Resolve it by locatingSKILL.md— never hard-assume~/.claude. Detect the OS once (Windows/macOS/Linux) and prefer uv for all Python — see §13 Tool Auto-Install Policy.
Collection method: agent-browser when available (JavaScript-heavy sites, infinite-scroll, screenshot evidence), with automatic fallback to web search / web fetch / direct URL fetch. Tool limitations are logged as collection gaps — never as case blockers.
1. Quick Start
# Full autonomous case — runs every applicable technique
/case target.com
# Guided flow for first-time investigators
/flow person
# Summary of what's been found so far
/brief
Append --yolo to any command to skip all interactive prompts and confirmations. The analyst makes every decision autonomously.
2. AEAD Case Lifecycle
Every investigation follows four phases:
| Phase | What Happens |
|---|---|
| Acquire | Collect raw data — /sweep, /query, /username, /phone, /email-deep, /breach-deep, /subdomain, /webpivot + /icp (domain/URL targets), /dork-sweep · /docleak · /github-osint//secrets, /cn-corp · /iban · /hash-id on discovery |
| Enrich | Recursive pivot loop — the pivot orchestration engine treats every discovered identifier as a new seed and expands the graph hop-by-hop (/branch, /crossref, /link-subjects, /signatures) automatically until the frontier is exhausted, no approval prompts (autonomy=auto). Each discovered identifier auto-fires its leak/breach/OSINT/dork legs — email→/breach-deep+/intelx (breach dumps·infostealer logs·pastes·darknet), username→/username+socials, name→/dork-sweep+/docleak, apex→/intelx --phonebook+/secrets+/github-osint — see §"Leak / breach / infostealer auto-fire" + the Dork/GitHub auto-fire matrices. Acquire↔Enrich iterate, not run once. |
| Assess | Score and verify — /exposure, /threat-model, /validate, /coverage, /verify-finding. Judgments carry likelihood terms, coverage gets the 5W1H pass, attributions get an ACH matrix (handbook/analytic-standards.md). If the case has not converged (frontier still open after the pivot loop + deterministic pipeline) and posture is active, /case auto-escalates to the /harness deepening loop — keyless-first (the CLI's own model), egress hard-gated on hostile infra; --no-harness opts out |
| Deliver | Package output — /report, /brief, /render, /workspace save — first ASKS whether to import more evidence from manual investigation (merged into the report JSON before anything is built), always auto-saves the base data bundle (.md + .json + .csv + IOC bundle: .stix.json/.txt/.csv/.jsonl), then ASKS which presentation report to render — (a) PDF · (b) DOCX · (c) HTML · (d) all (both prompts skipped under --yolo/guided-auto, which default to HTML). When CHONGLUADAO_API_KEY is set, the IOC bundle also attaches CLD's STIX + MISP indicator feed as companion artifacts (cld_api.py feed stix2|misp --raw → loadable bundle, not merged into the case graph). Deep-layer persist (automatic, ZERO extra egress): when /backend is live, /case reuses the pivots it already collected — never re-fetches — to persist the versioned case at $SKILL_DIR/intel_engine/cases/<CASE-ID>/ and correlate it cross-case; see the auto-chain note below. See connectors/chongluadao-api.md |
Run /progress at any point to see which phase you're in and what's pending.
/caseand web-infra pivoting. For a domain or URL target,/caseincludes web-infrastructure pivoting (/webpivot) in the Acquire phase. It runs keyless by default (crt.sh + passive DNS + anonymous urlscan) and upgrades automatically when premium keys are set via/apikeys(Shodan/Censys/FOFA/Hunter.how/DNSLytics/SecurityTrails/urlscan-PRO/WhoisXML). With keys the pipeline also: reads the urlscan Pro hostname lifecycle (pre-registration NS/A eras on the timeline, verdict rows in Appendix B), runs the MO-neighbour pivot on the estate's non-CDN origin (co-tenants WHOIS-verified; only a registrant join-key ever seeds, same-MO personas render as a rung-10 Related personas table —--mask-personasto aggregate), measures entitlement (meta.capability.plans, per-casecapability_plans.json; Censys' search is its own probe), and fires SecurityTrails DNS-history + DSL reverse-WHOIS, DNSLytics reverse-IP (co-tenancy-filtered), a once-per-case Censys cert search, Shodan cert/JARM search and IntelX (loop:--fullonly). Every metered leg is--free-only/no_spend-gated; IntelX selectors, WhoisXML/SecurityTrails reverse-WHOIS terms, MO-neighbour origins, DNSLytics reverse-IP and the Censys cert search are bought once per CASE (on-disk memo), while per-host legs (urlscan lifecycle, SecurityTrails subdomains/history, Shodan search) stay per host under per-case caps. Because/webpivotcan fetch the target directly, for hostile infrastructure it prefers passive capture (urlscan/Wayback) — seetechniques/web-pivot.md. It is not run for username/phone/person targets.⭐ ChongLuaDao is the first-party premium upgrade. With
CHONGLUADAO_API_KEYset (/apikeys set chongluadao <KEY>), Acquire/Enrich fold CLD's own datasets into/scam-check,/threat-check,/phone,/breach-deep,/email-deep,/vuln-checkand/impersonate, and/cld <target>is the direct entry point. Your client connects only to ChongLuaDao, never to the target (provable fromscripts/cld/cld_api.py); for URL/AI/IP checks CLD does any target fetch server-side — the safe first-touch verdict on a live scam funnel before a direct pivot. Full catalog + AEAD placement:connectors/chongluadao-api.md.Archive IOC harvest runs by default too. For domain/URL targets the Acquire phase also runs
wayback_harvest.py <domain> --indicators(add--urlscanwhenURLSCAN_API_KEYis set), harvesting emails, phones, crypto wallets, tracking/verification IDs, SaaS-operator IDs, and socials from the entire Wayback history — not just the live page — with first-seen/last-seen per selector. It writes case-schemaindicators[]to<case>/raw/harvest.indicators.json, which merge into the case and flow into the auto-saved IOC bundle at Deliver. This is the step that recovers selectors a network later scrubbed — across the whole snapshot corpus, not just the live page. Passive by construction — only web.archive.org (+ urlscan.io if keyed), never the target.The five v2.6 commands are in the pipeline too — no flags.
/icpruns for every domain/URL/org target (and an IP's resolved hostname);/cn-corp,/ibanand/hash-idfire the moment a company name/USCC, payment detail, or hash appears — and all three feed their yields back into the recursive pivot loop as new seeds, so an ICP licence serial or a reused bank account expands the graph like any other node./redactis the exception: it is opt-in (--redact), because a redacted report is a weaker artifact and that should always be a deliberate choice. Full trigger table: §Technique Activation Matrix. Narrow with--no-cn.
Two layers, one skill: broad collector → deep pipeline. cti-expert is the broad collector — the wide net of Acquire/Enrich commands (
/webpivot,/sweep,/subdomain,/icp,/username,/email-deep,/breach-deep, …) that pull artifacts from anywhere. Theintel_engineengine is now vendored in-repo underintel_engine/(intel_engine/harness/,intel_engine/tools/,intel_engine/WebPivot/,intel_engine/IntelGraph|IntelReport|BinaryPivot|IntelAnalysis/) and supplies the pipeline chains + deeper pivoting logic: a persistent knowledge base (intel_engine/knowledge/), versioned cases (cases/), cross-case correlation, calibrated assessment, and rendering.The chain (automatic for
/case, and it NEVER re-fetches): broad collection (cti-expert) already ranpivot_extractper host during Acquire. When/backendresolves to Tier 1/2 and the run produced ≥1 host seed (domain/URL/IP),/casehands what it already collected to the deterministic pipeline in reuse mode (--no-collect) — one command, a complete case dir, and nothing touches the target again:
- Write each host's already-collected pivot JSON to
$SKILL_DIR/intel_engine/cases/<CASE-ID>/raw/<host>.jsonand the host list to an absolute<CASE-ID>-seeds.txt, both anchored at the engine root (ROOT=$SKILL_DIR/intel_engine) — never a CWD-relative path.intel.py pipeline open <CASE-ID> <abs-seeds.txt> --no-collect—--no-collectskips the live fetch and runs the rest of the existing pipeline over the raw you just wrote: ingest → prior-overlap (/recall) → risk (/risk) → shared cluster seeds →clusters.json→case_graph.json→ ICD-203assessment.md. Every step is a KB/local read: zero egress, zero metered calls.The persisted case lands at
$SKILL_DIR/intel_engine/cases/<CASE-ID>/—raw/,shared.txt,clusters.json,case_graph.json,assessment.md— NOT the current working directory, and it is a COMPLETE case thatintel.py pipeline status <CASE-ID>accepts (not the partial dir a hand-rolled op sequence would leave).<CASE-ID>is the same id as the report filenames (mintCASE-YYYYMMDD-NNwhen none is given). Its cluster/risk/assessment fold back into the auto-saved report. Tier 3 (stateless), or a person/username/phone target with no host seeds → the handoff is skipped silently and the tier noted; broad collection + report are unaffected.Collecting
pipeline open(no--no-collect) stays MANUAL —/casenever runs it.intel.py pipeline openwithout the flag re-fetches every seed directly (collect_manyonhttps://<host>; the egress gate atcollect_core.py:169only fires whenhostileis set, which theopenpath never sets), so a blind auto-openwould be a second live round against infra/casemay have just classified hostile/no-touch — which is why the automatic handoff uses--no-collect. Run collecting mode by hand only for a fresh case with no prior collection, after setting the egress posture (/scope,/cti-proxy, or--passive).
/harnessdeepening AUTO-ESCALATES on non-convergence (--no-harnessopts out). After the--no-collectpipeline, if the case has not converged —intel.py convergence <case>reports status ≠converged(cold= no free leads left = exhausted), orintel.py frontier <case>still lists open leads and posture is active (not--passive, infra not classified hostile/no-touch) —/caseruns the harness Collect→Correlate→Assess loop to close the gap. Egress stays safe by construction: the harness's ownaudit.pyPreToolUse gate turnshostile=Trueinto a hard denial of outbound collection (harness/README.md), so an escalation can never re-touch no-touch infra — on hostile infra it deepens correlation/assessment only. Model — the CLI's own agent: run interactively in Claude Code, the IntelHarness skill front-end drives the same pipeline on your subscription with no separate LLM key; it falls back toHARNESS_BACKEND=local(Ollama/vLLM/LM Studio, keyless) or an API key only for unattended SDK runs. No reasoning backend reachable and non-interactive → the escalation is skipped and noted as a collection gap, never a blocker. A converged case,--passive, or hostile-only infra → no escalation and the deterministic result stands.Self-contained & self-resolving.
/backendresolves to SELF (in-repo) — no external setup. The bundled installer (scripts/install.{sh,ps1}) provisions the deep layer; or by hand:uv venv && uv pip install -r requirements.txt(harness SDK/MCP + IntelGraph renderers; the collector + KB + deterministic pipeline are stdlib and need none). An explicit$INTEL_HOMEstill overrides for a shared external KB. Full architecture, the op map, and the evidence-envelope schema:connectors/intel-backend.md.
2.5. Pivot Priority & False-Positive Control (CRITICAL)
Two failure modes ruin a cluster: asserting a link that isn't there, and missing one that is. This section governs both. Apply it in Enrich, before anything reaches a report.
Pivot priority ladder
Work down this ladder. Never assert same-operator on a lower rung when a higher rung is available or contradicts it. Tag every asserted link in the report with the rung it rests on.
| Rung | Indicator | Strength |
|---|---|---|
| 1 | Registrant email / phone / org — including historic WHOIS | decisive |
| 2 | One domain carrying two identities across its own WHOIS history | decisive — proves an alias |
| 3 | Site-verification token (Google Search Console, etc.) | decisive — proves account control |
| 4 | Shared TLS certificate / SAN cross-cover | strong |
| 5 | Nameserver delegation to a host the operator runs themselves | strong — proves zone control |
| 6 | APK signing certificate | strong |
| 7 | Distinctive favicon / analytics / tracker / backend tenant ID | moderate — verify below |
| 8 | Co-tenancy on a dedicated host (few tenants) | moderate |
| 9 | Site template / framework / kit | weak — kit-level, never operator-level |
| 10 | Co-tenancy on shared/reseller hosting; managed-provider nameservers | information, not a link |
Reverse-WHOIS is the highest-yield pivot here. Always mode=preview first — the count is
free. A term returning hundreds is shared boilerplate; do not purchase it.
Mandatory false-positive control
Before any indicator becomes a cluster edge, run /reference check <value>. If it returns
UNKNOWN, decide and record it with /reference add so the next case inherits the judgement.
Six traps, all of which have produced real false clusters:
| Trap | Why it fools you | Test |
|---|---|---|
| Commodity site kit | A template sold to hundreds of unrelated fraud operators | Search the template path in urlscan/FOFA — a large population means kit-level |
| Privacy-proxy contacts | The registrar's boilerplate phone/email, shared by every customer of that service | Reverse-WHOIS it; a spread of unrelated domains means noise |
| Shared/reseller hosting IP | A 20+-tenant cPanel box links nothing | Count tenants before clustering |
| Managed-provider nameservers | Cloudflare/GoDaddy/Gandi/Wix NS are shared by millions | Self-hosted NS is rung 5; provider NS is rung 10 |
| Org-name collision | A registrant org string that also matches a real, unrelated company | Reverse-WHOIS the org; inspect what comes back before attributing |
| Shared analytics / tag container | Often one web developer reusing a container across unrelated clients | Check domain creation dates — a decade-old business sharing a tag with a new fraud domain is a third party |
Never put an unvalidated indicator into a report that recommends abuse reporting. Naming an uninvolved business is the most damaging error this skill can produce. When a cluster rests on a single rung-7-or-below indicator, label it candidate, single-indicator — not a cluster member.
Never submit the case's own sample to a public sandbox (CRITICAL)
/anyrun is lookup-only: it reads detonations that already happened (anyrun_lookup). The
engine does carry a detonation path — anyrun_submit (T1) / bp_anyrun.py submit — but it is
gated four ways, and every gate is code, not convention:
- Per-submission analyst confirmation.
anyrun_submitwithoutconfirm=truereturns the risk briefing and sends nothing;bp_anyrun.submit()refuses unlessconfirm=Trueis passed (a function parameter —references/anyrun.jsoncan only make the policy stricter). Show the briefing, ask, and only on an explicit yes to this submission call again withconfirm=true. Consent to "analyze this sample" is not consent to detonate it. - Private by default, public refused. Privacy defaults to
owner;publicis refused even withconfirmunlessallow_publicis separately authorised — per call, or as the analyst's standingANYRUN_ALLOW_PUBLIC=1in the gitignored.env. With that set, a plan that cannot go private (gate 3denied) is downgraded to a public task explicitly — the result carriespublic_task,downgraded_from,public_authorized_by— never silently, and never when the plan can go private. Auto-delete defaults to a week. - Free plan fails closed. Pre-flight, before any POST, the engine checks the key's own
account record:
/user→limits.private(observed live;0in any window = denied, and the analyst attestation below cannot override that positive evidence;-1/positive = entitled), else a prior non-public task in the account's history. Neither →refused+plan_evidence, unless the analyst explicitly attests to a paid plan (allow_unverified_plan=true/--i-have-a-paid-plan) — never set it on your own. - Post-submit read-back. The task record carries no privacy while the sandbox is running
(observed live: a
status: "in progress"stub for ~2 min), so after the POST the engine polls the report — bounded tosandbox timeout + 60 s, max 240 s — and, once finished, reads the privacy ANY.RUN actually applied. A forbidden mode is withdrawn (task deleted) and the result saysexposed: true. If the task outlives the wait, the result saysprivacy_verified: nullwith averify_command; run it —bp_anyrun.py verify-privacy <uuid>/anyrun_submitwithverify_task=true, target=<uuid>— to finish the check and the withdrawal. Either way this is detected, not prevented: deletion does not un-publish what the feed already showed, so treat the infrastructure as tipped.
The harness adds a fifth: audit.gate() denies anyrun_submit outright unless the run was
launched with HARNESS_ALLOW_SUBMIT=1. tests/test_no_sample_submission.py asserts the gate is
both marked and enforced and that no upload machinery exists outside it;
intel_engine/tools/eval/test_intelx_anyrun.py §7b–7c exercises every refusal, the plan proof,
the attestation path and the read-back.
Do not work around any of it. Uploading the case's own APK / installer / archive to ANY.RUN — or VirusTotal, or any public sandbox — is an outbound, irreversible act:
- A public task is world-readable: the file, its hash, screenshots and full network log.
- Operators watch for their own samples. The standard response is to rotate the backend, revoke the signing key and re-skin the front — destroying the infrastructure the case is built on, often days before a takedown or referral can land.
- It cannot be recalled. Deleting a task does not un-publish what was already seen.
- A URL detonation fetches the live target from published sandbox egress: the operator learns it was sandboxed, and a "clean" verdict may be the decoy served to datacenter IPs.
Try first, and say what you tried: static analyze_artifact, then an existing detonation of the
hash (anyrun_lookup, VirusTotal, MalwareBazaar, Triage, Koodous). Prefer the downloaded FILE over
the live URL. Never put a case ID or an analyst/client name in tags or the filename. Never on
standing permission inferred from an earlier approval, never as a side effect of a pivot. The same
reasoning governs --submit (urlscan/Wayback): a public urlscan scan of a live scam funnel is
visible to the operator too.
A permuted email is a hypothesis, never a finding (CRITICAL)
When a case yields a real person's name or a username, and you already hold a domain that
matters to the case, run /email-permute. An operator's mailbox is almost never published, but
it is usually derivable — mail hosts use a small set of local-part conventions, and the operator's
own domain is the highest-yield thing to permute against.
That value comes with a matching hazard, so this rule is absolute:
- Permute against the case's own domains. Name × the operator's domain is a narrow, high-prior
question. Name ×
gmail.comis volume with no prior behind it —--freeexists, is capped, and should be a deliberate choice, not a reflex. - Never ingest a candidate into the KB, cite one in a report, or contact one. A fabricated
address that reaches
kb_ingestbecomes a shared indicator, and a shared indicator merges two operator clusters. A permutator wired straight into correlation does not enrich a case — it silently names an innocent party. This is the same failure RULE 5 exists to prevent. - Candidates are not seeds. They never enter the spider-map frontier. Only an address in the
tool's
promotelist — corroborated by independent evidence (Gravatar registration, breach corpus, a GitHub commit, a page/DOM hit, a dork) — may be treated as a real email seed, and that promotion is an analyst decision. - Never validate over SMTP.
RCPT TOprobing connects to the target's mail server, which the egress posture exists to prevent on a hostile case; and a catch-all domain answers250for every address ever tried, so it manufactures confidence instead of measuring it. Use--verify, which gates on MX (RFC 7505 null MX included) and checks Gravatar — both keyless, neither touching the target.
State the status in the turn. "12 candidates, 0 corroborated" is an honest result; presenting those 12 as discovered addresses is not.
Dead seed? Do not stop
Zero pivots, a parked page, or NXDOMAIN is not an answer. Run /fallback <domain> — crt.sh,
the full Wayback timeline, archive.today, and the local KB. A parked apex frequently has live
subdomains: enumerate CT and the Wayback CDX host histogram before writing a seed off. Report an
empty result as empty; a collector that returned nothing is a finding, not something to omit.
Egress control — proxy / rotation
On a hostile case your egress IP is a selector too — a direct fetch of scam
infrastructure exposes your real address to the operator, and repeated lookups
from one IP get you rate-limited or fingerprinted. The /cti-proxy layer routes
every HTTP(S) request the collectors make (keyless crt.sh, Wayback/CDX,
urlscan, the CLD connector, WHOIS, analytics reverses, /apikeys test) through a
configured proxy — or a rotation pool with automatic failover — so collection
egresses from an IP you choose, and successive calls can egress from different
ones. It also tunnels the collector's raw-socket TLS cert probe (/cert-pivot
leaf fingerprint) via CONNECT, failing closed rather than dialling direct.
Raw-socket TLS probes are handled too — nothing dials the target directly behind a proxy. The cert-SHA probes (
wp_pssl.py,wp_recon.py) and JARM (jarm.py) all take their socket fromcti_proxy.proxied_connection: under an HTTP pool it is CONNECT-tunnelled and fails closed (never a direct dial); under a SOCKS pool the in-process socket hook carries it (installed at import viawp_common, andjarm.py's own bootstrap) when PySocks is present in that interpreter — if it is not (e.g. theintel.pypipeline runs tools under$INTEL_PY), the hook is absent and these probes fail closed instead of leaking, so installpysocksthere; with no proxy it dials direct. As a policy choice the/webpivotanalyze path additionally skips JARM under an HTTP pool (ten tunnelled handshakes are slow) and runs it under SOCKS / no proxy — that gate honors the env/pool proxy, not just an explicit--proxy.
uv run scripts/proxy/proxy.py add http://user:pass@host:3128 --label res-1
uv run scripts/proxy/proxy.py add 1.2.3.4:8080 # bare host:port -> http://
uv run scripts/proxy/proxy.py rotation round-robin # | random | sticky | off
uv run scripts/proxy/proxy.py test # confirm each proxy's egress IP
uv run scripts/proxy/proxy.py status # pool + policy + toggles
uv run scripts/proxy/proxy.py disable # back to the real IP
- It is opt-in and additive — with no proxy configured the skill runs exactly as before, from your real IP. A pool, once added, is enabled by default.
- No-leak default: with a pool set, a failed pool is not silently retried
direct — turn that on deliberately with
allow-direct on. - Precedence: env
CTI_PROXY/CTI_PROXIES(and standardHTTPS_PROXY) override the stored pool for a one-off session; the store lives inscripts/proxy/proxies.json(gitignored, chmod-600 — it may hold credentials). - Coverage: the broad collectors (
scripts/…) get full in-process rotation + failover; the deep pipeline (/backend,/pipeline,/harness) inherits the egress for every tool it spawns (one proxy per run). For an ad-hoc tool call or the MCP server, export first:eval "$(python3 scripts/proxy/proxy.py use)". - Formats:
addaccepts a full URL, a barehost:port, a providerhost:port:user:passexport, auser:pass@host:portauthority, or a pastedhttp_proxy="…"line. HTTP/HTTPS get the full rotation + failover +no_proxybehavior;socks5:///socks5h://auto-install PySocks onaddbut rotate only per run — no in-process failover, andno_proxyis not enforced (the global socket hook routes everything). - Full reference:
/cti-proxy(commands/cti-proxy.md).
3. Command Reference
How to read this table — check the marker before you announce a command.
Marker Meaning What you may say T2: / T1: shown Backed by a real CLI op and/or MCP tool. Call it, then report what it returned. [model] No code behind it, and none is needed — it names a way for you to work (a summary style, a checklist, a KB read-back). Do the thing. Never claim a tool ran. [unimplemented] The tradecraft is documented but nothing executes it yet. Say so, then follow the linked technique by hand. Do NOT narrate it as a tool call. A command with no marker and no T2:/T1: line has not been triaged yet — treat it as [unimplemented]. Announcing a tool call that cannot happen is the failure this table exists to prevent: the output looks identical to real collection and is not.
3.0 Entry point & registered commands
/cti <target> is the single entry to this skill. It routes any target type — domain, IP,
email, username, phone, wallet, hash, APK — through recall → collect → cluster → assess. Plain
English works identically ("analyze example.com and pivot the infrastructure"); the command form
just removes ambiguity.
/case <target> is an alias of /cti <target> — the same full pipeline run; /cti is the canonical entry (and the only form that works from a cold prompt). Prefer /cti.
Nine commands are registered with Claude Code by bash scripts/register.sh and work from a
cold prompt in any project:
| Command | Does | Equivalent T2 op | Equivalent T1 tool |
|---|---|---|---|
/cti <target> |
entry point — routes by target type | (whole chain) | (whole chain) |
/cti-recall <seed> |
seen before? run first, always | recall |
domain_verdict, which_cases |
/cti-case <ID> <seeds> |
full deterministic pipeline | pipeline open |
(none — CLI only) |
/cti-pivot <url|ip> |
collect one target | pivot-extract |
pivot_extract |
/cti-cluster <domain> |
correlate & expand | kb, cert-overlap |
kb_cluster, cert_overlap |
/cti-check <indicator> |
false-positive control | reference check |
reference_check, reference_add |
/cti-report <ID> |
render graph + PDF/DOCX | graph, report |
render_diagram, render_report |
/cti-status |
backend / MCP / credits health | backend.py status |
api_usage |
/cti-proxy [op] |
egress proxy / rotation pool for all outbound calls | (none — CLI only) | (none — CLI only) |
Every other
/commandin §3 is a convention read from this file, not a registered command. Once the skill is loaded they are unambiguous instructions; typed at a cold prompt they do nothing. When in doubt use/ctiand describe the goal.
Three layers, one operation. The same capability is reachable three ways and the names differ
by layer — T0 uses kebab-case after a slash, T2 uses kebab-case ops, T1 uses snake_case
tools. The table above is the canonical mapping; when you add a capability, add a row here in the
same commit or the layers drift apart again.
Capabilities that are not registered commands still carry their layer mapping inline in the §3
tables. The engine's WebPivot/BinaryPivot collectors add these: /capabilities (T2 capabilities,
T1 capability_check), /impersonate (T2 impersonate, T1 impersonation_hunt), /search-pivot
(T2 search-pivot, T1 search_pivot), /censys (T2 censys, T1 censys), /intelx
(T2 intelx, T1 intelx_search) and /anyrun (T2 anyrun, T1 anyrun_lookup).
Commands grouped by AEAD phase.
Acquire
| Command | What It Does | Example |
|---|---|---|
/case [target] |
Full pipeline — runs every applicable technique (alias of /cti) T2: intel.py case <seed> (= pipeline) |
/case example.com |
/sweep [target] |
Multi-vector recon on any target type T2: intel.py sweep <target> (= pipeline) |
/sweep @username |
/query [subject] |
Builds 12–15 advanced search operator queries T2: intel.py query <indicator> |
/query example.com |
/username [handle] |
Enumerate handle across 3000+ platforms T2: intel.py username <handle>. T1: username_enum — HYPOTHESES, not findings |
/username johndoe |
/phone [number] |
Carrier, line type, reputation, public associations, infostealer exposure (Hudson Rock); VN scam-phone reports (ChongLuaDao) when keyed T2: intel.py phone +<E164>. T1: phone_osint — carrier/line-type NOT determined |
/phone +84901234567 |
/email-deep [email] |
Accounts, breach history, infrastructure; breach/exposure records (ChongLuaDao data-leaks) when keyed T2: intel.py email-deep <email>. T1: deep_profile — metered steps planned, not fired |
/email-deep u@domain.com |
/subdomain [domain] |
CT logs, brute-force, passive enumeration; flags admin/sensitive subdomains (admin,adm,kef,ador,panel…) per handbook/admin-endpoint-indicators.md T2: intel.py subdomain <domain> (keyless certspotter + hackertarget + crt.sh; names any source that was down) · intel.py subenum <apex> (subfinder auto-keyed from .env, amass, assetfinder, findomain → cases/<id>/subenum/<apex>.json). T1: subdomain_enum — the case-persisting form; live names are queued for the next collection round by case_frontier |
/subdomain example.com |
/breach-deep [email] |
Multi-source breach lookup with context — Hudson Rock, IntelX, ChongLuaDao data-leaks/exposure when keyed T2: intel.py breach-deep <email>. T1: deep_profile (mode=breach) |
/breach-deep u@domain.com |
/traffic [domain] |
Traffic estimation, ranking, audience data T2: intel.py traffic <domain>. T1: traffic_rank — Tranco only; no paid-panel estimates |
/traffic example.com |
/visitors [domain] |
Full visitor intelligence: tech, geo, sources, analytics T2: intel.py visitors <url>. T1: pivot_extract (trackers) |
/visitors example.com |
/techstack [domain] |
Technology fingerprint (CMS, analytics, CDN, server) T2: intel.py techstack <url>. T1: pivot_extract (tech_fingerprint) |
/techstack example.com |
/competitors [domain] |
Competitor & related site discovery [unimplemented] | /competitors example.com |
/secrets [target] |
Exposed credentials in repos and paste sites T2: intel.py secrets <target>. T1: github_osint (secrets=true) — code search needs auth, so queries are EMITTED |
/secrets github.com/org |
/github-osint [target] |
GitHub user/org/repo recon: profiles, repos, code search, commits, forks. Deterministic committer-identity harvest built in — `wp_github.py <login | org |
/cld [target] ⭐ |
ChongLuaDao first-party premium connector (scripts/cld/cld_api.py, needs CHONGLUADAO_API_KEY). Now also wired into the deterministic pipeline open: wp_cld.py runs per collected domain host in enrich_live — the denylist checkurl verdict + IoC-URL analyzer land in live_results["cld"] and ingest as reputation FACTS (cld_verdict/cld_denylisted/cld_reputation_score, never a cluster edge; an empty-evidence label is flagged, not adopted), and for .vn hosts CLD WHOIS is the PRIMARY source (WhoisXML has no .vn coverage; RDAP/port-43 fall back) so the Domain Summary registrar/registrant/dates fill in. Metered → gated by --free-only/no_spend; CLD fetches the target server-side (posture-safe). Auto-routes any indicator (url/domain/ip/hash/email/phone/asn/CVE/.onion; non-indicators are skipped, never a blind metered call) to CLD's own datasets: URL verdict vs a ~20M denylist, deep AI URL analysis (risk 1–10 + findings), IoC verdict+evidence, denylist/brand-lookalike search, data-leak/breach exposure + full data-leak module (machines, stolen/exposed creds, cookies, leaked-accounts, devices, full-export — async start→poll), CVE/KEV + actor feeds, STIX/MISP export. Your client connects only to CLD, never to the target; CLD fetches server-side. 30-min timeout ceiling (--timeout); 403/404 → skip, not fail. Subcmds: route|checkurl|analyze|denylist|checkphone|whois|burner|ioc|exposure|leaks|breaches|machines|stolen-credentials|exposed-credentials|cookies|leaked-accounts|devices|device-detail|device-credentials|full-export|brand-domains|vulns|actors|onion|feed. See connectors/chongluadao-api.md |
/cld https://scam-site.top |
/threat-check [target] |
IP/domain/URL/hash threat intelligence — ChongLuaDao IoC verdict + evidence (registration, reputation, threat feeds/reports) when keyed T2: intel.py threat-check <indicator>. T1: threat_check |
/threat-check 185.1.1.1 |
/scam-check [domain] |
Phishing/scam/malicious domain check — upgraded by ChongLuaDao checkurl (20M-URL denylist verdict) + analyze (deep AI, risk 1–10); client talks only to CLD, which fetches the target server-side T2: intel.py scam-check <domain>. T1: threat_check (mode=scam) |
/scam-check susp-site.xyz |
/webpivot [url] |
Web-infra pivoting — extract favicon mmh3 / GA-GTM-AdSense / wallet / SaaS-operator artifacts from a page's DOM → ranked pivot queries (Shodan/PublicWWW/urlscan/FOFA). Flags: --render, --crawl, --history (Wayback GA), --fetch (pull archived page content — WebFetch can't reach Wayback), --harvest (full-IOC harvest across whole archive history → emails/phones/wallets/IDs/socials), --whois, --graph (cluster), --rank (score same-operator relations), --cert (cert-fingerprint pivot), --suggest, --wallets, --paths. See techniques/web-pivot.md (reverse-lookup engines per artifact → handbook/pivot-services.md) T2: intel.py webpivot <url>. T1: pivot_extract |
/webpivot https://scam-site.top |
| (automatic — no flag) | Four layers now run on every collection and need no command. Asset layer: fetches the page's own JS bundles and re-runs every extractor over the source — the fix for SPA/white-label kits where the shell HTML is empty; yields off-apex api_endpoint/websocket_endpoint (the backend survives a front-end re-skin), build_env:<KEY> tenant tokens, js_bundle_sha256, and via sourceMappingURL the operator's own dev_username/dev_project. SPA route table: reads the app's router literals — spa_route:admin, spa_route:funnel, and a spa_route_signature that survives a re-skin. Zero extra requests, routes are leads only and are never fetched. Well-known/policy files: a fixed standards list (never a wordlist, no path brute-forcing) → adstxt_publisher, apple_team_id, security_contact. JARM: TLS-stack fingerprint of the server. Suppress with --no-assets / --no-well-known; cap fetches with --assets-max N |
(runs inside /cti-pivot) |
/capabilities |
Run this first, and again before reporting any "nothing found". Which optional API keys are configured, and for each absent one the evidence class that went unqueried plus the free path that substitutes. A keyless run extracts every artifact but cannot reverse most of them — so "no sibling domains" with no FOFA/urlscan key is a fact about the credentials, not about the operator. Every collection also records this in meta.capability; carry the limitation statement into the assessment and cap confidence accordingly. T2: capabilities · T1: capability_check |
/capabilities |
/impersonate [domain] |
Hunt lookalike / typosquat domains of a seed — typosquat permutations (omission, insertion, adjacent-key, transposition, homoglyph, hyphenation, combosquat) + a curated scam-heavy TLD sweep + a crt.sh keyword hunt, then existence-checked by live DNS. Output separates confirmed registered lookalikes (each an impersonation:candidate — run /cti-pivot on it and compare) from an unregistered monitoring watchlist. FREE (crt.sh + DNS); --fofa / --urlscan add the metered sweeps. Never live-fetches the lookalike infra. Tune the TLDs/affixes per campaign in intel_engine/WebPivot/references/impersonation.json. T2: impersonate · T1: impersonation_hunt |
/impersonate example.com |
/search-pivot [indicator] |
Multi-engine search-engine pivot — the general-web complement to FOFA/PublicWWW, which only see served HTML. Takes any indicator (domain, slogan, tracking ID, wallet, Telegram/Zalo handle) and emits ready-to-open, URL-encoded dork queries across Google/Yandex/DuckDuckGo/Bing/Brave. It does not scrape: fire the queries with WebSearch, or WebFetch the DuckDuckGo html URL, then feed new hosts back into /cti-pivot. FREE, no keys. T2: search-pivot · T1: search_pivot |
/search-pivot "distinctive slogan" |
/censys [mode] [value] |
Censys Platform — the server-side view FOFA/urlscan don't give. cert <sha256> returns every hostname on that exact leaf certificate (near-decisive cross-brand same-operator evidence, and it works on a free plan); host <ip>, webproperty <host> also free-plan. query <kind> <value> builds the CenQL offline and keyless; budget reports the balance. ⚠️ 100 credits/MONTH per account, no rollover — a lookup is 1, a search 5, and running the emitted CenQL in the web UI costs the same 5. Prefer handing the analyst the query over spending a search. Needs CENSYS_PAT. T2: censys · T1: censys |
/censys cert 1a2b3c… |
/intelx [selector] |
Intelligence X — search ONE strong selector across a corpus nothing else here indexes: breach dumps, infostealer logs, pastes, darknet mirrors, historical WHOIS. Takes an email / domain (*.apex wildcard ok) / URL / IP / phone / wallet / IBAN — never a brand or person name (soft terms are refused and still cost a unit; classify_selector() blocks them locally). --phonebook <domain> inventories every email, subdomain and URL under an apex — the highest-value call, PAID-only. Grading is not optional: a hit in a breach dump or stealer log is EXPOSURE, flagged NOT clusterable — two addresses in one combolist share victims, not an operator. Only whois / pastes / darknet hits may carry a same-operator edge. Keyless ≈ 50%: it still types the selector and hands you the intelx.io URL. T2: intelx · T1: intelx_search |
/intelx registrant@example.com |
/anyrun [indicator] |
ANY.RUN TI Lookup — READ-ONLY. What samples carrying this indicator did when other people detonated them: contacted domains/IPs/URLs/ports, family label, Suricata context, public task links. Run it after /binary on the sample's sha256, backend host or ip:port. It is the only way to recover a packed sample's real endpoints — those exist only at runtime, so a thin string sweep plus a binary:protection finding is exactly the cue. A shared family is same-KIT, never attribution on its own. Keyless ≈ 50%: composes the query + UI link. ⚠️ This tool never submits a sample — see the box below. T2: anyrun · T1: anyrun_lookup |
/anyrun <sha256> |
/cert-pivot [domain] |
Cert-fingerprint pivot — other hosts serving the same TLS cert + SAN siblings (keyless; Shodan/Censys with keys). T2: intel.py cert-pivot <domain>. T1: cert_pivot |
/cert-pivot scam-site.top |
/sensitive-paths [list] |
Classify a Wayback/URL list for exposed paths (.git/.env/backups/configs) — severity + per-year timeline. Pure matching, no request reaches the target. T2: intel.py sensitive-paths --file <list>. T1: sensitive_paths |
/sensitive-paths waymore_index.txt |
/email-hygiene [email] |
Grade an email domain 0–100 + A–F (disposable / MX / free / role). An RFC 7505 null MX (0 .) scores as undeliverable, not valid. T2: intel.py email-hygiene <email>. T1: email_hygiene |
/email-hygiene admin@site.top |
/vuln-check [query] |
CVE/vulnerability lookup (CIRCL + NVD; ChongLuaDao CVE/KEV threat-feed when keyed) T2: `intel.py vuln-check CVE-… | --product . **T1:** vuln_check` |
/ransomware-check [org] |
Check if org is a ransomware victim T2: intel.py ransomware-check <domain>. T1: threat_check (mode=scam) |
/ransomware-check "Acme Corp" |
/stealer-log [folder] |
Triage an infostealer-log folder — stealer-family attribution, victim-vs-operator profiling, cross-log actor correlation, IOC extraction (raw passwords/cookies/autofill/history shown) | /stealer-log ./logs |
/gdoc [url] |
Extract metadata/owner from Google document T2: intel.py gdoc <url>. T1: doc_metadata |
/gdoc https://docs.google.com/... |
/msftrecon [domain] |
M365/Azure tenant recon — tenant ID, federation, MDI, SharePoint T2: intel.py msftrecon <domain>. T1: msft_recon |
/msftrecon example.com |
/icp [domain|serial] |
ICP filing (工信部备案) → registered PRC entity + licence number; reverse the licence serial to sibling domains under the same filing (same-operator, HIGH). See techniques/china-recon.md T2: intel.py icp <domain>. T1: cn_recon — MIIT is CAPTCHA-walled; gates are named |
/icp scam-site.top |
/cn-corp [name|USCC] |
PRC corporate registry chain — GSXT (ground truth) → TianYanCha/QCC/Aiqicha → 信用中国 blacklist → UBO; officers, shareholders, subsidiaries, revoked-status flags T2: intel.py cn-corp --company "<name>". T1: cn_recon — GSXT/TianYanCha gated |
/cn-corp 深圳市某某科技有限公司 |
/iban [value] |
Validate + decompose a bank account as a selector — mod-97 checksum, country, BBAN split, bank code, jurisdiction-mismatch signals. See techniques/fiat-payment-osint.md T2: intel.py iban <IBAN> |
/iban GB29NWBK60161331926819 |
/hash-id [hash] |
Identify a hash's algorithm before lookup — separates file hashes from credential material (32 hex = MD5 or NTLM) so it routes to the right service T2: `intel.py hash-id [--context file | credential]. **T1:** hash_id**T2:**intel.py hash-id [--context file |
/appliance-scan [domain|ip] |
Fingerprint internet-facing edge/VPN appliances (Citrix/F5/Cisco/Ivanti/Forti/PAN/Exchange) + exposed services → CISA KEV/CVE mapping. Passive-first (Shodan InternetDB/Censys); feeds /vuln-check + /threat-model. See techniques/fx-edge-appliance-recon.md [unimplemented] |
/appliance-scan vpn.example.com |
/saas-map [domain] |
Map SaaS tenancy + identity fabric — DNS-TXT tenancy tokens, non-Microsoft IdP fingerprint (Okta/Auth0/OneLogin/Ping/Keycloak/ADFS), unauth API/GraphQL/spec discovery. See techniques/fx-saas-identity-recon.md T2: intel.py saas-map <url>. T1: pivot_extract (saas_ids) |
/saas-map example.com |
/sharelink [url] |
Extract sharer identity from share link T2: intel.py sharelink <url>. T1: sharelink_resolve — contacts the final host |
/sharelink https://vm.tiktok.com/ABC |
/binary [file|url] |
Built-in. Static IOC extraction from a scam/fraud binary (sideloaded APK, desktop trading .exe/.dmg, bundled .jar) via the in-repo BinaryPivot/ — signing-cert SHA-256, package name/permissions, embedded C2/backend hosts, Firebase/S3 tenants, wallets, Telegram/WhatsApp handles. Output is WebPivot-shaped → clusters the app with web infra in the shared KB. See connectors/intel-backend.md §7 |
/binary ./trader.apk |
| /dork-sweep [target] [--telegram\|--docs\|--filetype\|--all] [--after DATE] [--clean] | Zero-auth dork sweep: Telegram ecosystem, 18 doc-hosts, filetype families; 4-tier fallback cascade T2: intel.py dork-sweep <target> | /dork-sweep example.com --filetype |
| /docleak [target] [--platform list] [--severity high] | 18-platform document leak hunt with severity classification (CRITICAL/HIGH/MEDIUM/LOW) T2: intel.py docleak "<target>". T1: dork_builder — emits queries, never runs them | /docleak "Acme Corp" |
| /dns-history [domain] | Historical DNS record changes (A, NS, MX) via passive DNS T2: intel.py dns-history <domain>. T1: wayback_ga | /dns-history example.com |
| /cert-history [domain] | SSL/TLS certificate timeline from CT logs (crt.sh) T2: intel.py cert-history <domain>. T1: passive_ssl | /cert-history example.com |
| /proton-check [email] | Proton Mail account creation date via PGP key [unimplemented] | /proton-check user@proton.me |
| /pgp-lookup [email] | PGP key search — creation date, UIDs, signatures [unimplemented] | /pgp-lookup dev@example.com |
| /wifi [ssid] | WiFi SSID geolocation via Wigle.net T2: intel.py wifi "<ssid>". T1: wifi_ssid — needs a WiGLE account; discloses the gap | /wifi "HomeNetwork" |
| /wifi --bssid [mac] | Exact AP lookup by MAC address | /wifi --bssid AA:BB:CC:DD:EE:FF |
| /register [name] | Add a subject to the case workspace | /register JohnDoe |
| /snapshots [url] | List/fetch archived Wayback snapshots. WebFetch is blocked from web.archive.org (robots.txt) — this reads the archive instead, so the request never reaches the target. T2: intel.py wayback-fetch <url> [--near latest\|earliest\|YYYY] [--list]. T1: wayback_fetch. See analysis/archive-explorer.md | /snapshots example.com |
| /archive-harvest [domain] | Sweep a domain's whole Wayback history for indicators an operator has since scrubbed — the GA ID that clusters the estate is often only in an old capture. T2: intel.py wayback-harvest <domain> --indicators [--from YYYY --to YYYY]. T1: wayback_harvest | /archive-harvest site-a.example |
| /fallback [domain] | Dead-seed recovery (§2.5) — crt.sh + full Wayback timeline + archive.today + local KB when a seed returns zero pivots / parked / NXDOMAIN; enumerates CT + Wayback host history before a seed is written off. T2: fallback · T1: fallback_probe | /fallback scam-site.top |
Enrich
| Command | What It Does | Example |
|---|---|---|
/branch [data] |
Expand a discovered identifier laterally [model] | /branch john@mail.com |
/pivot-suggest |
Rank "what to pivot on next" from findings — leet/variant/reuse/temporal/domain clusters. T2: intel.py pivot-suggest <findings.json>. T1: pivot_suggest |
/pivot-suggest |
/email-permute [name|handle] |
Derive email candidates from a person name or username against case domains. VN/CN/KR family-name-first aware; folds diacritics Unicode won't. --verify = MX gate + Gravatar. Output is hypotheses — see the rule below |
/email-permute "Nguyen Van A" --domain example.com --verify |
/rank-relations |
Score + rank same-operator relations across analyzed pages (noise-filtered). Mechanizes one artifact = lead, two = cluster — run it before asserting a cluster. T2: intel.py rank-relations cases/<CASE>/raw/*.json. T1: rank_relations |
/rank-relations |
/crypto-balance [addr] |
On-chain balance + lifetime flow for a wallet, valued at spot. T2: intel.py crypto-balance <addr>. T1: crypto_balance |
/crypto-balance 1ExampleBitcoinAddressDoNotUse |
/timeline [subject] |
Assemble dated event sequence | /timeline Company Inc |
/crossref |
Detect shared identifiers across subjects T2: intel.py crossref [--case <id>]. T1: kb_crossref |
/crossref |
/link-subjects [A] [B] |
Define a connection between two subjects [model] | /link-subjects John Jane |
/show-connections |
Display all logged connections [model] | /show-connections |
/show-trail [subject] |
Show the evidence chain for a subject [model] | /show-trail JohnDoe |
/watch [subject] |
Add subject to active tracking list [model] | /watch example.com |
/record-finding |
Log a finding with source and confidence [model] | Paste data after command |
/show-findings |
List all recorded findings [model] | /show-findings |
/graph |
Full ASCII subject relationship map | /graph |
/pathfind [A] [B] |
Discover connection path between subjects [model] | /pathfind A B |
/diff [url] |
Diff archived versions of a URL [model] | /diff example.com/page |
Assess
| Command | What It Does | Example |
|---|---|---|
/exposure [target] |
Composite exposure score (0–100) T2: intel.py exposure --set k=v. T1: exposure_score |
/exposure domain.com |
/threat-model |
Build threat model from findings; every attribution claim carries an ACH matrix (competing hypotheses scored by inconsistency, runner-up named) per handbook/analytic-standards.md §3. Backend hook (Assess): if /backend is up, calibrate confidence on your own priors first — intel.py operators list + intel.py risk --case <id> + read knowledge/{calibration.jsonl,analyst_profile.md} — instead of scoring from scratch. See connectors/intel-backend.md §6 [model] |
/threat-model |
/signatures |
Surface recurring behavioral patterns T2: intel.py signatures --set k=v. T1: signature_scan — evaluates, does not observe |
/signatures |
/validate |
Quality audit — score 0–100 [model] | /validate |
/coverage |
Coverage matrix with identified gaps — technique matrix plus the 5W1H substantive pass (Why/How unanswered blocks Deliver-ready) [model] |
/coverage |
/verify-finding [id] |
Re-check a specific finding's sources [model] | /verify-finding 12 |
/subject [name] |
View or create subject record [model] | /subject JohnDoe |
/lookup [name] |
Retrieve a registered subject [model] | /lookup JohnDoe |
/modify [name] |
Update a subject record [model] | /modify JohnDoe |
/archive-subject [name] |
Remove subject from active tracking [model] | /archive-subject JohnDoe |
/find [query] |
Search across all subjects [model] | /find domain:example.com |
/blind-spots |
Prioritized investigation gap analysis [model] | /blind-spots |
/source-check |
Batch source URL accessibility check [model] | /source-check |
/drift [subject] |
Temporal risk score tracking T2: intel.py drift <case> [--snapshot]. T1: case_drift |
/drift example.com |
/clarify [finding] |
Plain-language finding explanation [model] | /clarify fnd-003 |
Deliver
| Command | What It Does | Example |
|---|---|---|
/report |
Full report — always saves the base data bundle (.md + .json + .csv + IOC .stix.json/.txt/.csv/.jsonl), then asks which presentation to render: (a) PDF · (b) DOCX · (c) HTML · (d) all | /report |
/report html |
Interactive self-contained HTML report (primary deliverable) | /report html |
/report brief |
Single-page executive brief | /report brief |
/report json |
Raw data as JSON | /report json |
/report csv |
Spreadsheet-compatible export | /report csv |
/report docx |
Word document in the PDF house style (slate/steel palette, serif body + sans headings, cover/TOC, rich charts + Diagram Design editorial diagrams + cloud figure) — on request | /report docx |
/report legal |
Evidence-formatted for legal proceedings (adds DOCX/PDF) | /report legal |
/report journalist |
Source-citation-heavy format | /report journalist |
/brief |
Plain-language summary (non-technical) [model] | /brief |
/render entities |
ASCII subject relationship diagram [model] | /render entities |
/render timeline |
Chronological event chart | /render timeline |
/render risk |
Exposure heatmap | /render risk |
/render network |
Network topology of connections | /render network |
/stats |
Counts and coverage statistics T2: intel.py stats |
/stats |
/workspace save [name] |
Persist case state [model] | /workspace save mycase |
/workspace open [name] |
Resume a saved case | /workspace open mycase |
/workspace list |
Show saved cases | /workspace list |
/workspace diff [a] [b] |
Diff two saved workspaces | /workspace diff case1 case2 |
/render threat-path |
ASCII attack path flow diagram | /render threat-path |
/render attack-surface |
ASCII attack surface exposure map | /render attack-surface |
/report ioc |
Export IOCs as STIX 2.1 or flat list | /report ioc --format stix |
/redact [file] |
Shareable variant of a report — stable numbered placeholders ([EMAIL_1]) + reversible JSON map; .md/.json/.csv. Opt-in — the base data bundle stays unredacted; request with /redact or --redact |
/redact REPORT.md |
UX & Navigation
| Command | What It Does | Example |
|---|---|---|
/flow [type] |
Guided step-by-step case workflow [model] | /flow person |
/template list |
Browse pre-built case templates [model] | /template list |
/template run [name] |
Run a pre-built template | /template run security-audit |
/novice |
Toggle simplified, low-jargon mode [model] | /novice |
/terms |
OSINT term glossary [model] | /terms |
/progress |
Current case phase and coverage [model] | /progress |
/opsec |
OPSEC checklist for current task [model] | /opsec |
/onboard |
Interactive first-time onboarding guide [model] | /onboard |
/quality |
Investigation quality composite score [model] | /quality |
Configure
| Command | What It Does | Example |
|---|---|---|
/apikeys |
Manage premium/pro API keys (ChongLuaDao ⭐ first-party, Shodan, Censys, FOFA, SecurityTrails, DNSLytics, urlscan-PRO, WhoisXML, Hudson Rock, IntelX, GitHub, SerpAPI…) — status/set/unset/test/unlocks. Keys upgrade existing techniques (especially /cld + /webpivot); keyless/free stays the default. Stored chmod-600 in $SKILL_DIR/.env (gitignored), env-var override. See handbook/api-keys.md |
/apikeys set chongluadao <KEY> |
/backend |
Detect/report the optional persistent-intelligence backend and pick the tier — Tier 1 typed MCP (intel-harness) → Tier 2 CLI → Tier 3 stateless. Runs scripts/backend/backend.py to resolve $INTEL_HOME (env → .mcp.json → sibling dir → symlink) and print the tier line. All the backend commands below dispatch through scripts/backend/intel.py <op> at Tier 2 (or the typed MCP tool at Tier 1). intel.py list maps all 73 engine ops (full CLI parity — CDN ranges, graph-build, hypothesize, calibration, evidence-report, case-store, cost, deterministic pipeline, …); intel.py mcp prints/writes the .mcp.json that enables Tier 1 ("the server"). See connectors/intel-backend.md |
/backend · /backend check |
/kb [query] |
Built-in. Query the shared knowledge base. T2: intel.py kb --stats/--entity <v>/--cluster <domain>/--shared --min N; intel.py operators list. T1: kb_entity/kb_cluster/kb_query_shared |
/kb --entity example.com |
/recall [seed] |
Built-in. "Have I seen this before?" — check a seed against every prior case before collecting. T1: which_cases/domain_verdict (typed MCP). T2: intel.py recall <seed> (query.py --entity; which_cases/domain_verdict are MCP-only). Surfaces known operators up front |
/recall scam-site.top |
/risk [case] |
Built-in. Score a case's hosts for NRD / bulletproof-hosting / money-trail red flags. T2: intel.py risk --case <id> (or --file <pivot.json>). T1: risk_signals |
/risk CASE-0001 |
/reverse-whois [email|name] |
Built-in. Reverse-WHOIS a registrant identity → only high-value pivots; refuses privacy/registrar terms, flags bulk resellers as noise. T2: `intel.py reverse-whois --reverse-email --search-type |
Truncated - read the full file at https://github.com/7onez/cti-expert/blob/d0a4f079065e41b745e1d657ab794f73036f8983/SKILL.md.