Command-line client for ThreatCluster (`tc`)
Project description
threatcluster-cli
Command-line client for ThreatCluster. Installs the tc command.
pipx install threatcluster-cli
Getting started (3 steps)
1. Install
pipx install threatcluster-cli
2. Mint an agent key
Sign in at https://threatcluster.io → Settings → CLI → Mint key.
Tick the scopes you want, click mint, copy the tc_agent_… value.
Shown once — store it somewhere safe.
3. Log in
tc auth login
Paste the key when prompted. The CLI stores it in your OS keyring (Keychain on macOS, SecretService on Linux desktop, Credential Manager on Windows) and you're ready.
tc threats list --limit 5 | jq -r '.threats[].ai_title'
That's it.
Commands
Every command outputs JSON. Pipe through jq for reading. tc <command> --help
for full options.
tc auth — credentials
tc auth login # paste a refresh credential
tc auth status # show what's loaded
tc auth status -v # + storage backend, bearer jti, key id
tc auth logout # hard kill: revokes server-side AND clears local
tc auth logout --keep-remote # local clear only (e.g. moving the key)
tc search — smart router (entities + threats)
tc search "Volt Typhoon"
tc search lockbit --only entities # entity-only
tc search cisco --only threats --limit 20
tc threats — clusters and IOCs
tc threats list # latest trending
tc threats list --query ransomware --limit 20 # filter by keyword
tc threats list --watch --interval 30 # live feed (NDJSON)
tc threats get <cluster-id> # one cluster
tc threats iocs <cluster-id> # IOCs for one cluster
tc threats iocs <id> --types ip,domain # filter IOC types
tc threats stix <cluster-id> # STIX 2.1 export
tc iocs — bulk indicators
tc iocs feed # plaintext, one per line (last 30d, confirmed)
tc iocs feed --type ip # filter by type
tc iocs feed --type hash --hours 24 # last 24h hashes only
tc iocs export --format csv # for spreadsheets / SIEM
tc iocs export --format json # structured
tc iocs export --type domain --format csv # domains, CSV
tc entities — actors, malware, tools
tc entities search "lazarus" --type apt_group
tc entities get apt_group "Lazarus Group"
tc entities related apt_group "Lazarus Group" # co-occurring entities
tc entities trending # top by recent mentions
tc entities trending --window 7d
tc vulns — CVEs
tc vulns list # latest CVEs
tc vulns list --severity CRITICAL --limit 10
tc vulns list --watch --interval 120 # live CVE feed
tc vulns get CVE-2026-1234 # one CVE detail
tc darkweb — ransomware, breaches, markets
tc darkweb ransomware victims # last 30 days
tc darkweb ransomware victims --group lockbit
tc darkweb ransomware victims --days 7 --limit 100
tc darkweb ransomware victims --watch # live feed
tc darkweb breaches
tc darkweb breaches --watch
tc darkweb keyword-hits # business+/MSSP only
tc feeds — your custom intel feeds
tc feeds list
tc feeds get <feed-id>
tc cluster — analyst pivots
tc cluster open <cluster-id> # opens in browser
tc cluster open <id> --no-browser # just print the URL
Stdin chaining (-)
Any command that takes an id accepts - to read ids from stdin.
Compose freely:
# Top 5 trending clusters → IOCs for each
tc threats list --limit 5 \
| jq -r '.threats[].cluster_id' \
| tc threats iocs -
# CVE detail for everything new in 24h
tc vulns list --since 24h \
| jq -r '.cves[].cve_id' \
| tc vulns get -
# Open every LockBit-related cluster in the browser
tc threats list --query lockbit --limit 3 \
| jq -r '.threats[].cluster_id' \
| tc cluster open -
# STIX bundle for every "ransomware" cluster
tc threats list --query ransomware --limit 20 \
| jq -r '.threats[].cluster_id' \
| tc threats stix -
Use cases
SOC analyst — "what's worth my attention right now?"
# Trending ransomware clusters
tc threats list --query ransomware --limit 10 \
| jq -r '.threats[] | "[\(.threat_score)] \(.ai_title)"'
# CVEs newly added to CISA KEV-style catalogs
tc vulns list --severity CRITICAL --limit 10 \
| jq -r '.cves[] | "\(.cve_id) — \(.description[:120])"'
# What ransomware groups are most active this week?
tc entities trending --window 7d \
| jq -r '.trending.ransomware_group[:10][] | "\(.value) freq=\(.frequency)"'
Threat hunter — "is X in our intel?"
# Pivot from a malware family to clusters mentioning it
tc entities search "cobalt strike" --type malware \
| jq -r '.entities[].entity_value'
# All IOCs from clusters mentioning a tool you're hunting
tc threats list --query "cobalt strike" --limit 50 \
| jq -r '.threats[].cluster_id' \
| tc threats iocs - \
| jq -r '.iocs[]'
MSSP — "give me a feed for $CUSTOMER"
# Live ransomware victims into a Slack webhook
tc darkweb ransomware victims --watch --interval 60 \
| while read line; do
msg=$(echo "$line" | jq -r '"🔒 new victim: \(.group): \(.name)"')
curl -s -X POST -d "{\"text\":\"$msg\"}" "$SLACK_WEBHOOK_URL"
done
# Daily IOC blocklists (cron)
tc iocs feed --type ip > /etc/blocklists/tc-ips.txt
tc iocs feed --type domain > /etc/blocklists/tc-domains.txt
Engineer — "feed our SIEM"
# Bulk IOC export for SIEM ingest
tc iocs export --format csv > tc-iocs.csv
# Filter to only the IOCs from clusters scoring 80+
tc threats list --limit 100 \
| jq -r '.threats[] | select(.threat_score >= 80) | .cluster_id' \
| tc threats iocs - > high-confidence-iocs.json
AI agent — "scoped, budgeted access"
Hand a sub-agent a narrower bearer than your own. The server enforces it — the child cannot escalate scopes or exceed the budget.
TC_SCOPES=threats:read,iocs:read \
TC_SESSION_ID="$(uuidgen)" \
TC_MAX_REQUESTS=50 \
tc threats list --limit 5
Environment
| Var | Purpose |
|---|---|
TC_API_URL |
Override API base (default https://api.threatcluster.io) |
TC_REFRESH_TOKEN |
Refresh credential (overrides keyring + file). For CI only. |
TC_SCOPES |
Comma-separated subset of refresh scopes for the bearer. |
TC_SESSION_ID |
Session id for per-session request budgeting. |
TC_MAX_REQUESTS |
Cap requests per session; subsequent ones get 429. |
TC_PROPAGATE_AUTH |
Set to 1 to propagate auth env to subprocesses. |
TC_DEBUG |
Set to 1 for verbose stderr (auth headers redacted). |
Shell completion
tc --install-completion bash # or zsh, fish
exec $SHELL # restart your shell
tc thr<TAB> # → tc threats
Security notes
- Refresh credential is never sent on argv (
--api-keyflag is rejected). - Bearer JWTs (15 min ttl) are minted on demand, cached in memory only.
- The CLI refuses plaintext
http://URLs unless the host is127.0.0.1/localhost. - Auth env vars are scrubbed from subprocess environments unless
TC_PROPAGATE_AUTH=1. - Credential file is mode 0600 + uid-checked; loose perms are refused.
See the docs for the full reference, and Agent keys & scopes for the security model in detail.
Release flow
See PUBLISHING.md.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file threatcluster_cli-0.1.1.tar.gz.
File metadata
- Download URL: threatcluster_cli-0.1.1.tar.gz
- Upload date:
- Size: 19.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9a9bef6cdbbf2c8574954d4fecb5e1c9c60faac5516e52070cfe5d5fb7296462
|
|
| MD5 |
29cd63e21c2b3b95a3965ccb521e8425
|
|
| BLAKE2b-256 |
92b51143254fe9ddc94dc465a09c3a2bcd23d4e25101288cc699b018af3418f1
|
Provenance
The following attestation bundles were made for threatcluster_cli-0.1.1.tar.gz:
Publisher:
publish.yml on Jam0k/threatcluster-cli
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
threatcluster_cli-0.1.1.tar.gz -
Subject digest:
9a9bef6cdbbf2c8574954d4fecb5e1c9c60faac5516e52070cfe5d5fb7296462 - Sigstore transparency entry: 1401408792
- Sigstore integration time:
-
Permalink:
Jam0k/threatcluster-cli@6de4616671853789cc31d9ab3c78d7b2f6ce83d4 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/Jam0k
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@6de4616671853789cc31d9ab3c78d7b2f6ce83d4 -
Trigger Event:
push
-
Statement type:
File details
Details for the file threatcluster_cli-0.1.1-py3-none-any.whl.
File metadata
- Download URL: threatcluster_cli-0.1.1-py3-none-any.whl
- Upload date:
- Size: 21.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
26bbe04ba48dfceb9bb803e9396285c52ecdee8b24929a6dedc9ec80c37d59cb
|
|
| MD5 |
7110e3c4461f7217712b5f9ac2e96af2
|
|
| BLAKE2b-256 |
97f984b14f5257749530de517197115e5b052bddb3faf0f843514a28dac21cf4
|
Provenance
The following attestation bundles were made for threatcluster_cli-0.1.1-py3-none-any.whl:
Publisher:
publish.yml on Jam0k/threatcluster-cli
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
threatcluster_cli-0.1.1-py3-none-any.whl -
Subject digest:
26bbe04ba48dfceb9bb803e9396285c52ecdee8b24929a6dedc9ec80c37d59cb - Sigstore transparency entry: 1401408876
- Sigstore integration time:
-
Permalink:
Jam0k/threatcluster-cli@6de4616671853789cc31d9ab3c78d7b2f6ce83d4 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/Jam0k
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@6de4616671853789cc31d9ab3c78d7b2f6ce83d4 -
Trigger Event:
push
-
Statement type: