ADR-0004: A `loomscope` command-line client

Edit this page

Status: Proposed Date: 2026-08-14

Context

Every routine interaction with Loomscope currently goes through a browser. That is wrong for several of its actual users:

  • An engineer who wants to know whether a host appeared overnight, from a terminal, without breaking flow.
  • A CI pipeline that should fail when a deployment introduces a critical CVE.
  • An operator on an air-gapped console with no browser, or on a machine where opening one is a procedure.
  • Anyone doing bulk work — importing fifty networks, acknowledging a class of findings — where clicking is the wrong tool.

Loomscope also already ships a Go binary and a generated Go API client, so the distribution machinery for a second static binary exists.

Decision

Ship loomscope, a single static Go binary, as a first-class client of the public REST API (ADR-0003).

Design principles.

  • Human output by default, machine output on request. Tables for a terminal; --output json or --output csv for a pipe. Detect a non-TTY and drop colour automatically.
  • Exit codes carry meaning. 0 success, 1 runtime error, 2 usage error, and — critically — a documented non-zero code when a query matches something the caller asked to be alerted about. loomscope vuln list --severity critical --fail-on-match should fail a pipeline.
  • Configuration cascades: flags, then environment (LOOMSCOPE_URL, LOOMSCOPE_API_KEY), then ~/.config/loomscope/config.yml. Never a key on the command line by default, since that leaks into shell history and process listings.
  • Every command is a thin API call. No business logic in the CLI. If a command needs something the API cannot express, the API is what is missing.

Initial surface.

loomscope host list [--site] [--since] [--search]
loomscope host get <id>
loomscope service list [--host] [--signature]
loomscope vuln list [--severity] [--state] [--fail-on-match]
loomscope vuln ack <id>
loomscope cert list [--expiring-in 30d]
loomscope network add <cidr> [--site]
loomscope scan run --network <id> [--wait]
loomscope snapshot take
loomscope snapshot diff <from> <to>
loomscope topology export <kind> --format mermaid|drawio
loomscope site list
loomscope config set-context / get-contexts

Distribution. Static binaries for Linux, macOS and Windows on amd64 and arm64, attached to GitHub releases and built by the existing pipeline. Also shipped inside the server image so docker compose exec reaches it in an air-gapped install.

Versioning. The CLI carries the API version it targets and warns clearly on mismatch rather than failing obscurely.

Alternatives considered

A Node CLI in the monorepo. Rejected. It would share TypeScript types with the server, which is genuinely attractive, but it requires a Node runtime on every machine that runs it — unacceptable for an air-gapped console or a minimal CI image — and npx-based distribution is slower and less predictable than a static binary.

A kubectl-style plugin model. Rejected as premature. There is no extension demand yet, and it would complicate the first release for a speculative benefit.

Document curl recipes instead. Rejected as the primary answer, though the documentation will still show raw HTTP. curl plus jq covers one-off queries but not authentication management, pagination, output formatting or meaningful exit codes — and the CI use case needs all four.

Wait until after 1.0. Considered seriously. The counter-argument is that the CLI is the cheapest possible proof that the public API is usable: if a command is awkward to implement, the API design is wrong, and it is far better to learn that before the contract is frozen.

Consequences

Accepted costs.

  • A second user-facing artefact to version, document, release and support.
  • Cross-platform release engineering, including signing on macOS and Windows if we want it to run without warnings.
  • The CLI will inevitably lag the UI in feature coverage, and users will notice.

Gains.

  • Loomscope becomes scriptable, which is the difference between a dashboard and a tool that fits an existing workflow.
  • The CI use case — fail a build on a critical finding — turns Loomscope into part of a delivery pipeline rather than something consulted afterwards.
  • It exercises the public API as a real client, exposing design problems while they are still cheap to fix.

Dependency. Blocked on ADR-0003. Building the CLI against internal endpoints would defeat its main secondary purpose.