lookup-liberty91
SkillSecurityUse when you need Liberty91 platform intelligence, what actually happened (deduplicated Threat Events with every source, Admiralty reliability/credibility and verification stage), whether an IOC is already known to your account, the canonical threat library (actors, malware, vulnerabilities, clusters, ATT&CK TTPs), alert matches, or which of your customer organizations an occurrence affects, or when you need to push intelligence back: ingesting your own report, generating an intelligence package, or uploading an organization document. Also supplies event provenance fields, when the platform returns them, and Threat Library alias resolution to /source-provenance and /quality-of-information-check. Two-way, first-party integration. Commonly invoked by /ip-investigation and friends, /ioc-enrichment-workflow, /threat-actor-profiling, /campaign-tracking and /vulnerability-intelligence. Reads $LIBERTY91_API_KEY.
Use lookup-liberty91 in Claude, ChatGPT or Ahel Desktop
Free. Sign in, add lookup-liberty91 and connect your AI. About a minute.
Also: Claude Code · Cursor · Codex
Then ask your AI: use the lookup-liberty91 skill
Details
Instructions available. Your AI can read the instructions. Execution depends on the setup they require.
Account requirements not reviewed. Check the skill instructions before use; ahel provides instructions and does not run this skill.
No other account needed.
Add ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.
What this skill tells your AI
The instructions your AI receives, as published by liberty91ltd/cti-skills in skills/lookup-liberty91/SKILL.md and read by ahel’s review.
Two-way bridge to the Liberty91 platform — the pack's only first-party integration. Where VirusTotal or Shodan answer "what is known about this indicator", Liberty91 answers "what happened, who reported it, how well corroborated is it, and which of my organizations does it touch".
Like /lookup-misp and /lookup-opencti, this skill also writes: it ingests your own reports, queues intelligence packages, and manages organization profiles. Writes publish into your Liberty91 account and are metered — confirm with the user before invoking any write command, and never run one inside an autonomous enrichment loop.
The two-layer model — read this first
Everything else follows from it.
- A Threat Event is a real-world occurrence: one breach, one exploitation campaign, one leak. Deduplicated, with every source you are entitled to attached.
- An Event is one report about an occurrence. A breach covered by a vendor write-up, three news articles and a leak-site post is five Events and one Threat Event.
| You want | Command |
|---|---|
| What happened, deduplicated, with all its sources | threat-events |
| The individual documents — who published what | events |
Default to threat-events. Drop to events only when the question is genuinely about documents ("what did vendor X publish this week", "poll my ingested report until enrichment completes"). Every Event carries threat_event_id so you can move between layers.
When to invoke
Read:
- The user asks what has happened — to a sector, a country, an organization, in a date window (
threat-events) - An indicator surfaced elsewhere and you want to know whether the platform already holds it (
ioc-lookup) - An actor / malware family / CVE needs its canonical record, aliases, ATT&CK TTPs, linked occurrences or co-occurring entities (
library,entity) - A vendor's name for an actor needs resolving to the canonical entry (
library threat-actors --alias "…") /source-provenanceor/quality-of-information-checkneeds an occurrence's provenance, or needs two vendors' names matched to one entity (see Provenance fields and Alias resolution below)- The user wants their alert rules' matches, or a saved search re-run (
alert-matches,run-search) - A TIP (MISP, OpenCTI) needs parented STIX for an occurrence (
threat-event <id> --section iocs-export) - The user asks which of their customer organizations is affected, or about an org's assets/suppliers (
threat-event --section detail,orgs,org)
Write — confirm with the user first:
- A finished investigation or an external report should live in the platform (
ingest) - The user wants an intelligence package generated (
report-generate— 50 credits per report) - An organization profile needs a document ingested or extracted entities confirmed (
upload-document,confirm-entities,refresh-description)
Do NOT invoke for:
- Raw internet reconnaissance — Liberty91 is curated intelligence, not a scanner. Use Shodan/Censys.
- Bulk third-party reputation sweeps — run VT/OTX/RL first, then check the vetted subset here.
- Community sharing — that is MISP's job (
/lookup-misp). Liberty91 is your intelligence source and customer-facing production platform.
How to invoke
Single Python CLI, stdlib only — no install, no venv.
Occurrences
# Connectivity, quota, and which API surface this host serves
python3 tools/clis/liberty91.py quota
# Triage feed: occurrences at least two independent sources agree on, mapped to your account
python3 tools/clis/liberty91.py threat-events --relevant --verification corroborated --min-credibility 2
# Scope by sector / country / technique / time
python3 tools/clis/liberty91.py threat-events --target-sector energy --target-country AE --last-reported-after 2026-07-01
python3 tools/clis/liberty91.py threat-events --technique T1566.001 --event-class security-incident
python3 tools/clis/liberty91.py threat-events --organization <org-uuid> --all --max-pages 5
# One occurrence: the evidence — sources with stance + reliability, entities, ATT&CK, org relevance
python3 tools/clis/liberty91.py threat-event <id>
python3 tools/clis/liberty91.py threat-event <id> --section iocs
python3 tools/clis/liberty91.py threat-event <id> --section sources # every report behind it, paginated
python3 tools/clis/liberty91.py threat-event <id> --section iocs-export --out bundle.json # parented STIX for a TIP
The list row now carries the entities. From API v2.1, threat-events rows include actors, malware, vulnerabilities, techniques, victims and source_countries alongside the sectors and regions they always had. Actor/malware/vulnerability entries are {id, name, from_private_source} where id is the canonical catalog id, so you can go straight from a list row to entity <type> <id> without paying for a detail call first, which matters more now that pages are a quarter the size. Three caveats that change what you may conclude:
- List rows show publicly-linked entities only. An entity named only in a report private to your account appears on the detail response but not on the list row. Absence from a row is not absence from the occurrence.
source_countriescounts only publicly-sourced, non-disputing reports. Private and user-uploaded reports never contribute one, by design.- Only
threat-eventsrows gained these.searchandalert-matchesstill return the base Threat Event shape without them, so do not write a pipeline that expectsactorson every occurrence row regardless of where it came from.
--section sources vs the sources in detail. The detail response embeds a sources[] block that is capped: for a heavily corroborated occurrence it is a sample, not the record. --section sources paginates the full set, newest first, and is the one to use when you are counting or citing reporting. Both are masked to what your account is entitled to, so neither reveals reporting you do not hold. It costs an extra scope: threat-events.read plus events.read.
Threat library (canonical catalog)
# Resolve a name — or a vendor's alias — to a canonical id, then track the id, not the string
python3 tools/clis/liberty91.py library threat-actors --name "APT28"
python3 tools/clis/liberty91.py library threat-actors --alias "Fancy Bear"
python3 tools/clis/liberty91.py library vulnerabilities --q "CVE-2026-2" --tracked
# The record and its sub-resources (entity types: threat-actors | malware | vulnerabilities | clusters)
python3 tools/clis/liberty91.py entity threat-actors <id>
python3 tools/clis/liberty91.py entity threat-actors <id> --section techniques # dated ATT&CK observations
python3 tools/clis/liberty91.py entity threat-actors <id> --section threat-events # occurrences it was named in
python3 tools/clis/liberty91.py entity threat-actors <id> --section related # co-occurring entities
python3 tools/clis/liberty91.py entity malware <id> --section iocs
Search, events, IOCs, alerts, organizations
# Search returns Threat Events, not reports (POST, but a read — a default-scoped key can use it)
python3 tools/clis/liberty91.py search --free-text "wiper" --target-sector energy --date-from 2026-06-01T00:00:00Z
python3 tools/clis/liberty91.py search --query-tree-file tree.json # the full grammar
python3 tools/clis/liberty91.py saved-searches
python3 tools/clis/liberty91.py run-search <id>
# Individual reports
python3 tools/clis/liberty91.py events --since 2026-07-01 --threat-event <uuid>
python3 tools/clis/liberty91.py event <id>
# IOCs
python3 tools/clis/liberty91.py ioc-lookup 185.220.101.45
python3 tools/clis/liberty91.py iocs --kind domain --verdict MALICIOUS --since 2026-07-01
python3 tools/clis/liberty91.py ioc-export --format csv --out iocs.csv # blocklist, not TIP context
# Alerts, packages, organizations
python3 tools/clis/liberty91.py alerts
python3 tools/clis/liberty91.py alert-matches <id> # Threat Events; --results reports for documents
python3 tools/clis/liberty91.py reports --status DRAFT
python3 tools/clis/liberty91.py report-download <id> --type md --out report.md
python3 tools/clis/liberty91.py orgs
python3 tools/clis/liberty91.py org <org-id> --section suppliers
Canonical entity filters (v2.1). Six new query_tree leaf fields, usable in search and in saved searches:
| Leaf | Values | Matches |
|---|---|---|
canonical_actor | UUID[] | threat actor from the deduplicated cross-tenant catalog |
canonical_malware | UUID[] | malware, same catalog |
canonical_vulnerability | UUID[] | vulnerability, same catalog |
incident_technique | T1566, T1566.001 | ATT&CK techniques derived for the occurrence |
affected_organization | UUID[] | one of your own customer organizations |
relevant_to_me | one of true/false/1/0/yes/no | occurrence intersects any of your organizations' profiles |
Prefer the canonical leaves. The legacy leaves (threat_actor, malware, vulnerability, threat_cluster) still work and are not being removed, but they match your account-local entities while the canonical ones match the deduplicated catalog. Resolve a name with library threat-actors --name/--alias, then filter on the canonical id.
technique and incident_technique answer different questions, and both are kept: technique matches what a single report named, incident_technique matches the set derived for the occurrence, which merges every source and can therefore carry an assertion no individual report made. Tree grammar and caps are unchanged: depth 3, 50 nodes, 50 values per leaf, 3 free-text leaves.
The caveat that will look like missing data. An entity filter returns strictly fewer results than a free-text search for the same name. A report not yet matched into an occurrence has no canonical links, so no entity filter can reach it, even when its text plainly names the actor. This is correct behaviour, not a gap. When someone asks why filtering APT28 by id returns less than searching the string, that is the answer. For a completeness sweep run the free-text search as well, and say which one the finding came from.
Write operations — confirm with the user first
# Push a report in. Private to your account; extracted IOCs are TLP:RED. 10 credits.
python3 tools/clis/liberty91.py ingest --title "Phishing wave against UAE energy" \
--text-file findings.md --source "Internal CTI" --actor APT34 --vulnerability CVE-2026-1234
# Then poll until enrichment completes, and read threat_event_id for the occurrence it matched
python3 tools/clis/liberty91.py event <event_id>
# Intelligence package — 50 credits PER REPORT generated
python3 tools/clis/liberty91.py report-generate --organization-id <org-id> --intelligence-requirement <ir-id>
# Organization profile (orgs.write)
python3 tools/clis/liberty91.py upload-document <org-id> supplier-list.xlsx --description "2026 vendor register"
python3 tools/clis/liberty91.py confirm-entities <org-id> <doc-id> --id <entity-id> --id <entity-id>
python3 tools/clis/liberty91.py refresh-description supplier <entity-id>
Every command accepts --dry-run (preview the request, send nothing) and --insecure (skip TLS verification — non-production hosts only). Lists accept --page-size, --all, --max-pages and --cursor; next is an opaque complete URL, never build cursor params. The CLI exits 2 if LIBERTY91_API_KEY is unset. Report a missing key; never fabricate results.
Page size shrank in v2.1, and it costs you. The server default drops 100 → 25 and the maximum 500 → 100, on every paginated endpoint. Credits are charged per request and the rate limit is a fixed window per key, so the same walk now costs up to 4x the credits and 4x the rate-limit budget. The CLI omits page_size unless you pass --page-size, so it follows whichever contract the host serves and needs no change. What does need your attention is --max-pages (default 10): the same --all --max-pages 10 that returned up to 5,000 rows returns 250 against a v2.1 host. Check truncated before reporting a count as complete, and raise --max-pages deliberately rather than assuming the default still covers the set.
Reading the trust signals
Three separate judgements travel with every occurrence. Do not collapse them into one number — and do not re-derive them, they are already Admiralty.
- Source reliability (
A–F, per source) — who said it. Government sources start at A, vendors at B, news at C, and move on evidence. - Credibility (
1–6, per occurrence, lower is better) — how well-supported the claim is. 1 Confirmed · 2 Probably True · 3 Possibly True · 4 Doubtful · 5 Improbable · 6 Cannot be judged. Syndicated re-publications collapse to one source, so they cannot inflate it.nullmeans unscored, not average. - Verification — where the occurrence stands:
auto,corroborated,verified,disputed,rejected,merged(seemerged_into). Analyst judgements are never overwritten by the pipeline. - Stance — a property of each report:
claims,corroborates,updates,mentions,disputes. When sources disagree, say so — report the dispute, don't average it away.
--verification corroborated --min-credibility 2 is the triage filter for "actionable now".
Provenance fields
/source-provenance and /quality-of-information-check read the following per occurrence when the platform returns them. They are planned platform work. As of 27 September 2026 they are not in the documented API contract and the CLI has no code that depends on them, so expect them to be absent.
| Field | Content |
|---|---|
primaries[] | The originating sources, each with org, type, access, date, url, stated_confidence, interest. Values follow the evidence item schema |
independent_primaries | Count of primaries with separate access. Reports that cite one another count once |
secondary_reports | Count of reports that relay a primary without adding evidence |
chains[] | Per report, the ordered hops from that outlet to its primary |
most_recent_observation | Date the activity was last observed, which is not the publication date |
superseded_by | Later report by the same primary that replaces this one, or null |
The CLI passes the API response through, so once a host serves these fields they appear in threat-event <id> output with no CLI change. Check the response, or /api/v1/schema/, rather than assuming.
Which label the result gets:
- Fields present →
provenance_basis: platform-resolved. Use the values verbatim. - Fields absent, or no
LIBERTY91_API_KEYconfigured → fall back to/source-provenance, which resolves the chain by script for the URLs in hand. The result is labelledscript-resolved, neverplatform-resolved. - Some present, some absent → each evidence item takes the label of where its own provenance came from, and the run reports the worst of them.
What today's API does return is not a substitute. --section sources gives source_name, url, published_at, stance and reliability per report. That says who reported, not who originated. Do not build primaries[] or chains[] from it, and do not read verification: corroborated or the number of corroborates stances as independent_primaries. Pass the report URLs to /source-provenance instead. Never fill a missing provenance field with a guess. The per-source reliability letter and the occurrence credibility number are the platform's ratings of the outlet and the event. They are passed to /source-assessment as Admiralty-style ratings where that skill is used. They are never copied into an evidence grade: /quality-of-information-check grades each claim itself, in words (access level and claim support).
Alias resolution
/quality-of-information-check has to know that two vendors mean the same actor or malware before it can count them as corroborating each other. With a key, the Threat Library does the matching, and the evidence item records alias_source: liberty91.
python3 tools/clis/liberty91.py library threat-actors --alias "Sednit"
python3 tools/clis/liberty91.py library threat-actors --name "APT28"
python3 tools/clis/liberty91.py library malware --alias "X-Agent"
Two names match when both resolve to the same canonical id. Compare ids, not strings.
What the CLI can do today:
--aliasis a case-insensitive exact match and--namematches the canonical name. Neither is fuzzy. Try the name as written, then--qfor a substring, and treat a--qhit as a candidate to confirm on the record, not as a match.- Vendor-cluster merge records (a vendor's unnamed cluster, such as a UNC or Storm designation, later merged into a named group) are used when the library record returns them. The CLI has no dedicated command or flag for them today. If the older designation is listed as an alias on the canonical record,
--aliasfinds it. If it is not, the names do not match. clustersentries are account-local and have no canonical record, so they cannot match names across vendors.- No result means not matched. It does not mean the two are different actors. Corroboration for that claim stays at 1,
unchecked. - The library endpoints need API v2.0 or later. When
quotareportsoccurrence_layer_available: false, alias resolution is unavailable on that host.
Without a key, or when resolution is unavailable, QoI uses the user's own references/aliases.yml (alias_source: user) or matches no names (alias_source: none). Hash, CVE and infrastructure matching do not depend on aliases.
Response format
All commands return JSON on stdout:
source: liberty91
operation: threat-events | threat-event | library | entity | search | ioc-lookup | ingest | ...
query_time: <ISO8601>
# lists:
count: <n>
pages_walked: <n>
next: <complete URL or null>
truncated: true # only when --max-pages stopped the walk
results: [...]
unlinked_report_count: <n> # search and alert-matches only
# ioc-lookup:
found: true|false
results: [...]
# _meta on every successful call:
_meta:
rate_limit_remaining: <n>
credits_remaining: <n>
Watch _meta.credits_remaining and surface it when it gets low. Reads cost 1 credit (2 for detail/search, 5 for STIX and downloads, 10 for IOC export and ingest, 25 for document upload, 50 per generated report). Failed requests are never charged.
Source reliability (Admiralty default)
Liberty91 emits Admiralty ratings natively — use the platform's own numbers rather than a default:
- Occurrence carries per-source
reliabilityand an occurrencecredibility→ pass both to/source-assessmentverbatim - Occurrence unscored (
credibility: null) → B6 — reliability from the source class, credibility cannot be judged - Nothing else available → B2 (the frontmatter default)
- A report your own analysts ingested → A1; a report you ingested from a third party → rate the original author
When an occurrence rests on a single D/E-graded publisher, downgrade to that worst contributing source no matter how confident the summary reads.
These ratings apply to the occurrence and its outlets as lookup results. They are not evidence grades. Claims from the reports behind an occurrence are graded by /quality-of-information-check with access level and claim support, and neither is converted into the other.
Operational notes
Shortened here. Read the whole file on GitHub.
Signals
- GitHub stars
- 27
- Forks
- 8
- Last commit
- Sep 2026
Advanced
- Item type
- skill
- Key
lookup-liberty91- Source
- github.com/liberty91ltd/cti-skills
github.com/liberty91ltd/cti-skills
Related picks
Skill · mukul975
The pick for Vulnerabilitiesai-prompt-engineering-safety-review
Skill · github
The pick for Vulnerabilitiesgws-shared
Skill · googleworkspace
More in Securitydefi-amm-security
Skill · affaan-m
More in Securityfastapi-patterns
Skill · affaan-m
More in Securityfiat
Skill · ccxt
More in Security