Cavil-CLI

 view release on metacpan or  search on metacpan

docs/Architecture.md  view on Meta::CPAN

itself rather than approximating it.

The archive is a gzip tarball built by shelling out to `tar`, chosen over an in-process library because a
vendored tree can be hundreds of megabytes and should stream to disk rather than sit in memory. Its MD5 is
computed as it is written and sent with the upload, so a truncated transfer is rejected rather than reviewed as
incomplete sources.

Before uploading, the client checks the archive against the server's upload limit (the Cavil default, or
`CAVIL_MAX_UPLOAD_MB` for an instance configured to accept more) and refuses with guidance if it is over, so an
accidental large file fails fast rather than after a slow doomed upload. The server enforces the same limit, so
a client whose limit is set too high still gets a clear rejection rather than a raw error.

### Uploading and waiting

The archive is posted to Cavil, which starts the same unpack, index and analyze pipeline any other package
goes through. Submitting the same archive under the same name again is idempotent on the server, so a re-run of
an unchanged tree does not pile up duplicate reviews.

The review is not instant, so the client polls the report endpoint, which answers "not ready" until the
analysis finishes and then returns the report. Its "not ready" reply names the pipeline stage (queued,
unpacking, indexing, analyzing, finalizing), which the client shows on the progress line so the wait names what
is happening rather than sitting on a bare "Reviewing". The line is on standard error so it never pollutes the
report or a pipe, and only on a real terminal so CI logs stay clean. A timeout bounds the wait.

### The verdict

The report carries the maximum license risk Cavil found, on its one-to-nine scale, and the instance's own
acceptable-risk threshold. The client turns those into a headline, a short tally, the licenses found ordered by
risk, and a link to the full web report, in colour on a terminal and plain text otherwise. For CI there is a
flat JSON form with the same facts.

## The gate

In CI the report is a gate: the check fails when the risk is at or above a threshold, and passes otherwise.
The threshold defaults to the instance's own `acceptable_risk` plus one, so "would Cavil consider this
acceptable" and "does the CLI pass" line up by default and no project needs to configure a number to match its
Cavil. `--fail-on-risk` overrides it for a project with a stricter or looser bar.

Gating on risk rather than on the presence of any license is the point: every project has licenses, and most
are fine. Cavil's scale captures more than copyleft (obligations, non-commercial and unknown all have their
place on it), so a single threshold expresses a real policy. The exit code is conventional: zero when within
the threshold, one when the gate fails, two for a usage or configuration problem, and three for a server or
connection error, so any non-zero code is the simple signal CI acts on while the risk itself is in the report.

## Access, and why it is high for now

Submitting a package runs the full pipeline and puts a review in the legal backlog, so it is gated exactly like
Cavil's own web upload form: it needs a read-write API key whose user has admin (`infra`) access. That is a
high bar on purpose for a first version - it keeps random submissions out of the production queue while the
workflow is proven. The planned sandbox namespace is what will open ad-hoc checks to ordinary users without
that cost; until it exists, `check` is for admins and for CI configured with an appropriately privileged key.
The client preflights with `whoami` so a key that cannot submit is reported clearly before a large tree is
packaged, rather than as an opaque rejection afterwards.

## The service contract

The CLI depends on a small, stable contract with the Cavil server, deliberately narrow so it can be reasoned
about and mocked. An identity endpoint backs `whoami`. An upload endpoint takes the archive and its checksum
and starts a review, returning the new package's id. A report endpoint returns the report for that id, or "not
ready" while it builds, with the risk and acceptable-risk fields the gate needs. A documents endpoint serves
the generated SPDX SBOM and NOTICE files, likewise "not ready" until generated. That is all; the CLI needs no
knowledge of Cavil's internals beyond these questions and their answers.

The upload also carries an `ephemeral` flag, asking for a one-off report with no lasting side effects, no open
review left in the legal backlog. That is what a developer or CI check always wants, so it is always sent. It
is the forward edge of the planned sandbox mode: today's servers do not act on it (which is why submission is
still gated to high access), but a server that gains the ad-hoc mode can honour it without any client change,
and the access gate can then relax for ephemeral requests.

## Testing

The CLI is tested against a mock of the Cavil server, not a real one. Each scenario stands up a small
in-process web service that answers those endpoints with canned data and records what the CLI asked it. The
requests go through the real HTTP machinery, so the tests exercise the true wire behaviour, but there is no
database, no network, and no port to bind. A field problem becomes a test with almost no translation: the
responses that triggered it become the mock's answers, and the requests that led to it become the assertions.

The parts that never talk to the server - the archive builder, the report renderer, the risk gate and the exit
codes - are plain functions of their inputs and tested directly, with small throwaway directories and git
repositories as fixtures.

## Design choices and their reasons

A few decisions are worth stating on their own, because they are the ones a reader is most likely to question.

- **Reuse the real review, do not reinvent it.** The value is that the local answer is the authoritative one.
  A separate client-side approximation would drift from what the legal workflow decides, which is the one thing
  it must not do.
- **Include vendored code by default.** Honouring `.gitignore` would be the tidy choice and the wrong one: the
  installed dependencies are most of what a legal review is for. The default optimises for a correct review,
  and `--respect-gitignore` is there for the rare case that wants the lean tree.
- **Gate on risk, defaulted to the instance.** A copyleft yes-or-no is too coarse, and a hardcoded number would
  fight each instance's own policy. Defaulting the threshold to the instance's acceptable risk makes the common
  case need no configuration at all.
- **High access first, sandbox later.** Rather than invent a weaker analysis to make submission safe for
  everyone, the first version reuses the real pipeline behind the real access gate, and leaves room for a
  backlog-free sandbox to open it up properly.



( run in 3.407 seconds using v1.01-cache-2.11-cpan-364913b4093 )