Skip to content

CLI reference

linchpin <global options> <command> ... (or python -m linchpin). Every command prints JSON on stdout; warnings and errors go to stderr. LINCHPIN only reads files: it sends no packets.

State: ingest and scenario write the findings to the state file (default .linchpin/findings.json, env LINCHPIN_STATE); scenario, and ingest with a topology YAML that sets config keys, also store a config.json next to it, which then overrides --config. Every analysis command rebuilds the attack graph from the state deterministically.

Global options

option meaning
--version show program's version number and exit
--state STATE findings state file (env LINCHPIN_STATE; default .linchpin/findings.json)
--config CONFIG weights / entry points / crown jewels YAML (env LINCHPIN_CONFIG; default config.yaml)

Commands

command purpose
ingest parse exports (nmap/OpenVAS/Nessus XML, SharpHound JSON, inventory YAML, native JSON) into the state file
scenario load a scenario YAML (exports + topology overlay + intel) as the state
synth write a synthetic enterprise (findings JSON + ground truth)
intel-build build the NVD / EPSS / KEV lookup cache from downloaded feeds
build build the attack graph and print its size
paths k cheapest entry -> crown-jewel attack paths
fix ordered remediation plan with rationale and evidence paths
cuts exact minimum remediation cut and single-node chokepoints
whatif path statistics with some nodes removed (nothing is persisted)
node one node with its inbound / outbound attack edges
export export the attack graph (Neo4j .cypher script or GraphML)
neo4j-push mirror the attack graph into Neo4j (password from NEO4J_PASSWORD)
serve run the API + web UI (localhost only by default; needs the [api] extra)

ingest

parse exports (nmap/OpenVAS/Nessus XML, SharpHound JSON, inventory YAML, native JSON) into the state file.

usage: linchpin ingest [-h] [--replace] [--intel INTEL] [--match-cpe] paths [paths ...]
argument meaning
paths files or directories (directories are scanned one level deep)
--replace discard previously ingested findings
--intel INTEL CVE intel cache from intel-build, to fill in CVSS / EPSS / KEV
--match-cpe map detected service versions (e.g. nmap -sV) to CVEs with the packaged offline NVD version-range index

scenario

load a scenario YAML (exports + topology overlay + intel) as the state.

usage: linchpin scenario [-h] [--data-dir DATA_DIR] [--no-intel] path
argument meaning
path scenario YAML, e.g. scenarios/composite_lab.yaml
--data-dir DATA_DIR base directory of the exports it lists (default: the YAML's folder)
--no-intel skip NVD / EPSS / KEV enrichment

synth

write a synthetic enterprise (findings JSON + ground truth).

usage: linchpin synth [-h] [--family {single,multi,none,ad,legacy}] [--hosts HOSTS] [--seed SEED]
                      [--out OUT]
argument meaning
--family FAMILY topology family; all but legacy plant real CVEs from the packaged NVD/EPSS/KEV pool (legacy uses placeholder CVE-2099-* ids) (default single) (choices: single, multi, none, ad, legacy)
--hosts HOSTS number of hosts (default 20)
--seed SEED random seed (default 0)
--out OUT output file (default data/synth.json)

intel-build

build the NVD / EPSS / KEV lookup cache from downloaded feeds.

usage: linchpin intel-build [-h] [--data-dir DATA_DIR] [--out OUT]
argument meaning
--data-dir DATA_DIR folder written by scripts/download_data.py (default ../../datasets/linchpin)
--out OUT cache path (default /derived/cve_intel.csv.gz)

build

build the attack graph and print its size.

usage: linchpin build [-h]

paths

k cheapest entry -> crown-jewel attack paths.

usage: linchpin paths [-h] [--k K] [--explain]
argument meaning
--k K number of paths (default 10)
--explain plain-English hop list instead of JSON

fix

ordered remediation plan with rationale and evidence paths.

usage: linchpin fix [-h] [--budget BUDGET]
argument meaning
--budget BUDGET maximum number of fixes (default 5)

cuts

exact minimum remediation cut and single-node chokepoints.

usage: linchpin cuts [-h] [--weighted]
argument meaning
--weighted also the minimum-effort cut (config fix_cost)

whatif

path statistics with some nodes removed (nothing is persisted).

usage: linchpin whatif [-h] --remove REMOVE [REMOVE ...]
argument meaning
--remove REMOVE node ids; bare names mean host:

node

one node with its inbound / outbound attack edges.

usage: linchpin node [-h] id
argument meaning
id node id, e.g. host:jump-01 or cred:svc_backup

export

export the attack graph (Neo4j .cypher script or GraphML).

usage: linchpin export [-h] [--format {cypher,graphml}] --out OUT [--graph-name GRAPH_NAME]
argument meaning
--format FORMAT default cypher (choices: cypher, graphml)
--out OUT output file
--graph-name GRAPH_NAME graph tag on every node (default default)

neo4j-push

mirror the attack graph into Neo4j (password from NEO4J_PASSWORD).

usage: linchpin neo4j-push [-h] [--graph-name GRAPH_NAME]
argument meaning
--graph-name GRAPH_NAME graph tag on every node (default default)

serve

run the API + web UI (localhost only by default; needs the [api] extra).

usage: linchpin serve [-h] [--host HOST] [--port PORT] [--scenario SCENARIO] [--data-dir DATA_DIR]
argument meaning
--host HOST bind address (default 127.0.0.1)
--port PORT port (default 8000)
--scenario SCENARIO scenario YAML to preload (else a synthetic demo)
--data-dir DATA_DIR base directory for the scenario's exports

Configuration file

weights: {w1: 0.5, w2: 0.3, w3: 0.2}
skill_penalty: {network_exploit: 0.1, cred_reuse: 0.2, client_side: 0.4, privesc: 0.3, acl_abuse: 0.25}
entrypoints: ["auto:internet_facing"]          # or host ids
crown_jewels: ["auto:sensitivity=high"]        # or node / host ids
k_shortest: 10
exploitability_source: intel                   # or: learned (M11 model score where present)
fix_cost: {Vuln: 1, Credential: 1, Ace: 1, Host: 3}   # effort weights for `cuts --weighted`
neo4j: {uri: "bolt://localhost:7687", user: neo4j}  # password only via NEO4J_PASSWORD

Environment

variable used by meaning
LINCHPIN_STATE all default for --state
LINCHPIN_CONFIG all default for --config
NEO4J_PASSWORD neo4j-push Neo4j password (never read from the config file)
LINCHPIN_SCENARIO, LINCHPIN_DATA_DIR, ... serve see HTTP API