Skip to content

Command-line client

The `loomscope` binary, its output and its exit codes

Edit this page
On 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

bash
make cli          # builds ./bin/loomscope

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

bash
export LOOMSCOPE_URL=https://loomscope.example.com
export LOOMSCOPE_API_KEY=lsk_...
loomscope host list

For anything longer-lived, use a context:

bash
echo "$KEY" | loomscope config set-context prod --url https://loomscope.example.com
loomscope config get-contexts
loomscope --context staging vuln list

There 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

CommandWhat 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 takePoint-in-time captures
site list, topology listSites 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:

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

CodeMeaning
0Success.
1A runtime failure — unreachable server, an error from it.
2The command line was wrong.
3--fail-on-match, and something matched. Not an error: the answer.
4The 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

bash
loomscope vuln list --severity critical --state open --fail-on-match

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