Command-line client
The `loomscope` binary, its output and its exit codes
Edit this pageOn this page
loomscope is a single static binary that talks to the
public REST API. It has no business logic of its own: if a
command needs something the API cannot express, the API is what is missing
(ADR-0004).
╷ ╷ ╷
───┼───┼───┼─── LOOMSCOPE
│ │ │
───┼───┼───┼─── Network discovery, topology and cyber-inventory.
│ │ │
───┼───┼───┼─── v0.13.1
╵ ╵ ╵Getting a binary
make cli # builds ./bin/loomscopeRelease builds are static, for Linux, macOS and Windows on amd64 and arm64,
and one ships inside the server image so docker compose exec server loomscope … works on an air-gapped install with nothing to download.
Pointing it at a deployment
Configuration cascades: environment first, then the config file.
export LOOMSCOPE_URL=https://loomscope.example.com
export LOOMSCOPE_API_KEY=lsk_...
loomscope host listFor anything longer-lived, use a context:
echo "$KEY" | loomscope config set-context prod --url https://loomscope.example.com
loomscope config get-contexts
loomscope --context staging vuln listThere is no --api-key flag, deliberately. Anything on a command line
lands in your shell history and in ps output, where every other user on
the machine can read it. The key is read from standard input or from the
environment; typing --api-key gets you an error telling you to rotate the
key you just exposed, rather than a silent success.
The config file lives at ~/.config/loomscope/config.yml
(%APPDATA%\loomscope\config.yml on Windows), is written 0600, and is
replaced atomically — an interrupted write cannot leave a truncated file
that locks you out of your own contexts.
Commands
| Command | What it does |
|---|---|
host list [--site] [--all] | Machines discovery has found |
service list [--host] | Software identified on them |
vuln list [--severity] [--state] | CVE findings |
vuln set <id> <state> [--note] | Triage a finding |
cert list [--host] [--trust] | TLS certificates and their expiry |
network list / network add <cidr> | Ranges Loomscope knows about |
scan run --network <id> | Queue a discovery scan |
snapshot list / snapshot take | Point-in-time captures |
site list, topology list | Sites and generated maps |
config … | Which deployment, and which key |
loomscope help <command> goes deeper on any of them.
Output
Tables for a terminal, machine formats on request:
loomscope host list --output json | jq '.[] | .hostname'
loomscope vuln list --output csv > findings.csv--output json returns the API's own objects, not a re-encoding of the
table — a script should not get a lossy view because the columns had to fit
a terminal.
Colour switches off automatically when the output is not a terminal, and
honours NO_COLOR. It is never the only information: a severity is
coloured and spelled, so a pipe, a log file and a colour-blind reader all
get the same answer.
Exit codes
These are a contract, so a pipeline can branch on them rather than parsing output.
| Code | Meaning |
|---|---|
0 | Success. |
1 | A runtime failure — unreachable server, an error from it. |
2 | The command line was wrong. |
3 | --fail-on-match, and something matched. Not an error: the answer. |
4 | The key was missing, rejected, or lacked the scope. |
The reason 3 is separate is that a build step must be able to tell "I
found the critical CVE you asked me to watch for" from "the server was
unreachable". Collapsing those into 1 is how a broken check comes to look
like a passing one.
Failing a pipeline on a finding
loomscope vuln list --severity critical --state open --fail-on-matchExits 3 when anything matches, 0 when nothing does, and 1 if it could
not ask. scripts/cli-exit-codes.sh exercises all of them against a running
deployment; make cli-check runs it.
Scopes
The CLI needs no more than the key it is given. scan run needs scan:run,
which is deliberately not inventory:write — writing a record changes
what Loomscope believes, while running a scan sends packets across the
customer's network, and a key for a reporting integration should hold the
first and not the second.
What it does not do yet
cert list --expiring-in 30d. The API has no such filter, and applying one in the CLI would filter a page rather than the collection — answering "none expiring" when the answer was on page two. The filter belongs in the API.snapshot diff. The comparison exists in the interface and is not yet on the public API.topology export. Export is a session-authenticated route today, not part of the public surface.