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 |
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 |