# Commands

Every `hushrun` command, grouped by what it's for, with its usage, the flags that matter, their defaults and an example. `hushrun <command> --help` prints every flag.

In a repository that `npx hushrun init` set up, hushrun is in `devDependencies`, so `package.json` scripts call it as `hushrun`. Anywhere else, `npx hushrun <command>` runs it.

## Everyday

### hushrun run

Runs a check's command unless this exact code already passed it, and records the result. [How skipping works](https://hushrun.com/docs/run.md) explains the key and every line it prints, and [Where results go](https://hushrun.com/docs/results.md) where it records.

Usage: `hushrun run [NAME] -- COMMAND`

| Argument or flag | Default | What it does |
|---|---|---|
| `NAME` | The `package.json` script that runs it, else the program's name | The check's name, part of its key |
| `COMMAND` | Required | Everything after `--`, run as it is, argument by argument |
| `--force` | Off | Runs the command even after a pass, and records the result |
| `--scope NAME` | The only scope | The scope whose files make the key. Needed when `hushrun.yaml` has several |
| `--rev REV` | The files as they are now | Hashes a commit's files instead |
| `--digest sha256:…` | Computed | Uses this source digest instead of hashing |
| `-q` | Off | Silences hushrun's own lines |

- hushrun's lines go to stderr and start with `hushrun: `. Stdout is the command's.
- It exits with the command's exit code, or 0 when it skips: see [its exit codes](#hushrun-run-without-a-stage).
- `--all`, `--isolated`, `--timeout`, `--environment-image` and `--plan` need `--stage`, and exit 2 without it. With `--stage`, `run` runs a promotion stage's checks instead: see [hushrun run --stage](#hushrun-run-stage).

```console
$ hushrun run unit -- sleep 2
hushrun: unit running: no pass yet for this exact code
hushrun: unit passed in 2s; it won't run again on this code
$ hushrun run unit -- sleep 2
hushrun: unit skipped: this exact code passed just now (saves 2s)
```

### hushrun init

Sets up `hushrun run` in the git repository it runs in. It finds the checks among `package.json`'s scripts and the GitHub Actions jobs that run them, signs you in, shows each change it would make, and makes them once you agree:

- wraps each check as `hushrun run -- …`, keeping the rest of each file as it was;
- names your organization's backend in `package.json`, and adds `hushrun` to `devDependencies` with the repository's package manager;
- gives the GitHub Actions jobs that run those checks `id-token: write`, so that CI records results with no secret;
- connects a repository on github.com, so that its CI records results with its own ID token; when an admin or owner of the organization runs it, also adds the repository to the organization for promoting its builds, and writes `hushrun.yaml` with its URL on hushrun;
- runs a check twice, to show the second run skip.

Usage: `hushrun init [OPTIONS]`

| Flag | Default | What it does |
|---|---|---|
| `-y`, `--yes` | Off | Applies the changes without asking. Without a terminal, init needs it |
| `--dry-run` | Off | Shows what init would change, and changes nothing: no sign-in, no files, no install |
| `--no-browser` | Off | Prints the sign-in page's address instead of opening a browser |
| `--no-run` | Off | Doesn't end by running a check twice |
| `--run` | Off | With `--yes`, still ends by running a check twice |
| `--pipeline` | Off | Writes a starter `hushrun.yaml` for a promotion pipeline instead |
| `--force` | Off | With `--pipeline`: replaces an existing `hushrun.yaml` |
| `--repository ID` | From the `origin` remote | With `--pipeline`: the repository id, such as `github.com/acme/app` |
| `--staging-branch BRANCH` | `develop` | With `--pipeline`: the staging stage's branch |
| `--production-branch BRANCH` | `main` | With `--pipeline`: the production stage's branch |
| `--staging-image REPOSITORY` | `registry.example.com/staging/app` | With `--pipeline`: the staging image repository |
| `--production-image REPOSITORY` | `registry.example.com/production/app` | With `--pipeline`: the production image repository |

The last six flags imply `--pipeline`, and none of the pipeline flags go with `--yes`, `--dry-run`, `--no-browser`, `--run` or `--no-run`. init exits 0 when it's done, when there is nothing to do, and when you answer no; 2 for usage errors, such as running outside a git repository or without a terminal and without `--yes`; and 1 when a file can't be written or the install fails.

```console
$ hushrun init --dry-run
Looking at /Users/ana/shop
  repository  github.com/acme/shop
  packages    npm (no lockfile)
  checks      lint, test, typecheck
  workflows   .github/workflows/ci.yml runs test, lint (job test)

hushrun init will change:
  package.json
    "lint": "eslint ." → "hushrun run -- eslint ."
    "test": "vitest" → "hushrun run -- vitest run" (vitest alone watches on a terminal)
    "typecheck": "tsc --noEmit" → "hushrun run -- tsc --noEmit"
    "hushrun": {"backend": "<your org's URL, once you sign in>"}, where CI, teammates and agents share results
    devDependencies: "hushrun": "^0.1.0"
  .github/workflows/ci.yml
    job test: adds permissions contents: read and id-token: write. The job's token then has only these two: add any other it needs
  your org
    connects github.com/acme/shop, so that its CI records results with its own ID token
    adds github.com/acme/shop to your org, for promoting its builds, unless it is there already
  hushrun.yaml
    a new file: repository github.com/acme/shop, the URL the org gives it, and its log key

Nothing changed (--dry-run).
```

### hushrun login

Signs in to a backend, so that `hushrun run` shares results with your organization. hushrun shows a code and opens the page that approves it, where you pick the organization. Once you approve, it saves a token only you can read, registers this machine, and makes the organization's URL the backend `hushrun run` uses when nothing else names one.

Usage: `hushrun login [OPTIONS] [URL]`

| Argument or flag | Default | What it does |
|---|---|---|
| `URL` | `HUSHRUN_BACKEND`, else the backend in the repository's `package.json`, else the last one you signed in to, else `https://api.hushrun.com` | Your organization's URL, such as `https://api.hushrun.com/o/acme`, or a server's address |
| `--no-browser` | Off | Prints the approval page's address instead of opening a browser |

URL must use `https://`, or `http://` on `localhost`, or hushrun exits 2. A server that can't be reached exits 6. `HUSHRUN_HOST` names another hosted server than `https://api.hushrun.com`.

Signing in on a machine revoked for going 90 days unused, or by `hushrun logout`, brings it back. The server can refuse to register the machine, and then hushrun saves nothing and exits 1: when you already have 20 machines in the organization, its message links to your machines page, where you revoke one; when you or an admin revoked this machine in the dashboard, hushrun says to remove its key file, so that it makes a new key.

```bash
npx hushrun login
hushrun login https://hushrun.example/o/acme
```

### hushrun logout

Revokes the token `hushrun login` saved for a server, and forgets it, along with the saved backend when it's on that server. hushrun forgets the token even when the server doesn't answer.

Usage: `hushrun logout [OPTIONS] [URL]`

| Argument | Default | What it does |
|---|---|---|
| `URL` | The server `hushrun login` would sign in to | The backend, or its host, to sign out of |

```bash
hushrun logout
```

### hushrun whoami

Shows where `hushrun run` records results here and what named that backend, the credential that goes there, and who the backend says you are: your organization, and for an agent token its agent, repository, expiry and rate limit. It also shows this machine's key id.

Usage: `hushrun whoami [OPTIONS]`

It exits 0 when the backend answered, or when results stay on this machine or are off; 1 when the backend refused the credential; and 6 when it couldn't be reached. `--format json` prints `hushrun.whoami/v1`.

```console
$ hushrun whoami
backend   this machine only, in /Users/ana/Library/Caches/hushrun/results
share     sign in with hushrun login, or name a backend in package.json
```

## Agent tokens

An agent token, which starts with `hra_`, lets an agent in a sandbox, or CI outside GitHub, record and read results without a browser. Its results record as `agent:NAME`, for one repository or the whole organization, until it expires or someone revokes it. Each token comes with the agent's key, its one machine, which signs what it records. The sandbox gets them as `HUSHRUN_TOKEN` and `HUSHRUN_MACHINE_KEY`: see [Agents and sandboxes](https://hushrun.com/docs/agents.md#sandboxes).

The `hushrun token` commands are for organization admins, and use their `hushrun login`, never `HUSHRUN_TOKEN`. Their backend is `--backend`, else `HUSHRUN_BACKEND`, else the backend in the repository's `package.json`, else the one `hushrun login` saved. It must be an organization's URL, such as `https://api.hushrun.com/o/acme`. They exit 2 without a login on that server or when the server answers 400; 1 when it answers 401, 403 or 404; and 6 when it can't be reached or fails.

### hushrun token create

Creates an agent token and the agent's key, and shows both once.

Usage: `hushrun token create [OPTIONS] --agent <NAME>`

| Flag | Default | What it does |
|---|---|---|
| `--agent NAME` | Required | The agent's name, such as `claude-code`, `fleet-a` or `gitlab-ci`. Its results record as `agent:NAME` |
| `--repository REPO` | The repository hushrun runs in | `host/owner/name` of the repository whose results it records and reads |
| `--org-wide` | Off | For every repository of the organization instead |
| `--expires DURATION` | `30d` | How long it lasts, in whole hours: `12h`, `30d`, `8w`; at most `365d` |
| `--rate N` | The server's, 300 on hushrun's hosted service | The most results it records a minute, 1 to 10000 |
| `--backend URL` | See above | The organization's backend |

The token goes alone to stdout, so `$(hushrun token create …)` holds it. Stderr says what it is and which variables to give the sandbox: `HUSHRUN_TOKEN`, `HUSHRUN_PROVIDER=local`, and `HUSHRUN_BACKEND` when the backend isn't on hushrun's hosted service; then the agent's key, a P-256 key hushrun makes and registers with the token, to set as `HUSHRUN_MACHINE_KEY`, or to keep in a file that `HUSHRUN_MACHINE_KEY_FILE` names, as a GitLab File variable does. Every sandbox or pipeline of the agent signs as that one machine, and the server records the agent's results only with its signature. Outside a repository, it needs `--repository` or `--org-wide`. `--format json` prints `hushrun.token/v1`, with the token and the key.

```bash
hushrun token create --agent fleet-a
hushrun token create --agent gitlab-ci --org-wide --expires 365d
```

### hushrun token list

Lists the organization's agent tokens that aren't revoked, expired ones included: each one's prefix, agent, repository or whole organization, expiry, last use and creator.

Usage: `hushrun token list [OPTIONS]`

| Flag | Default | What it does |
|---|---|---|
| `--backend URL` | See above | The organization's backend |

### hushrun token revoke

Revokes an agent token: anything that uses it is refused from then on.

Usage: `hushrun token revoke [OPTIONS] <TOKEN>`

| Argument or flag | Default | What it does |
|---|---|---|
| `TOKEN` | Required | The token's id, the prefix `hushrun token list` shows (`hra_` and 6 characters), the token itself, or its agent's name when only one token has it |
| `--backend URL` | See above | The organization's backend |

An agent name that more than one token has exits 2 and lists them.

```bash
hushrun token revoke hra_AbC123
```

## Promotion

These commands run a promotion pipeline: source digests, plans, signed evidence and artifact promotion by digest. Most need `hushrun.yaml` and the repository's URL on hushrun: see [Promotion](https://hushrun.com/docs/promotion.md) and [Configuration](https://hushrun.com/docs/reference/configuration.md). In a pull request's pipeline, the commands that read or write the repository's events need `--config-ref` naming the branch it merges into.

Many of them take the same options for the content they work on:

| Option | Default | What it does |
|---|---|---|
| `--scope NAME` | The only scope | A scope to work on. Repeatable where a command takes several |
| `--all-scopes` | Off | Every scope |
| `--rev REV` | `HEAD` | The revision |
| `--worktree` | Off | The files as `git add -A && git commit` would record them now: what you are about to commit |
| `--digest sha256:…` | Computed | Uses this source digest instead of computing it, for one scope only |

### hushrun digest

Prints the source digest of each scope. It needs no repository URL, key or server, and without `hushrun.yaml` the one scope, `default`, covers every file.

Usage: `hushrun digest [OPTIONS]`

| Flag | Default | What it does |
|---|---|---|
| `--scope NAME`, `--all-scopes` | Every scope | The scopes to digest |
| `--rev REV` | `HEAD` | The revision to digest |
| `--worktree` | Off | Digests the files as `git add -A && git commit` would record them now |
| `--dir PATH` | Off | Digests a plain directory, such as a clean export |
| `--explain` | Off | Lists every entry, its kind, hash and path, and the pattern that decided its scope |
| `--manifest-out FILE` | Off | Writes the exact manifest bytes, for one scope; `sha256sum` of the file is the digest |

```console
$ hushrun digest
sha256:79a343b472b042bd73eb52f6457750d062d7991490b2d4baacc896dac50c921d  api
sha256:83bc69ea5b9510c5aae5712d3d33a107f205e0e32435b6e657bdffa7a6407273  web
```

### hushrun plan

Decides what a stage does with the digest, and exits 3 when it's blocked:

| Mode | What the pipeline does |
|---|---|
| `promote` | Deploys the artifact an earlier stage built and validated, copied by digest |
| `refresh` | Runs only the checks whose evidence is missing, expired or failed, then promotes |
| `full` | Builds, runs the checks that lack valid evidence, and deploys |
| `wait` | Nothing yet: an upstream run for this digest is still going |
| `skip` | Nothing: this stage already runs this digest |
| `blocked` | Nothing, and the run fails: the upstream failed this digest, or evidence is missing that this stage can't produce |

Usage: `hushrun plan [OPTIONS] --stage <STAGE>`

| Flag | Default | What it does |
|---|---|---|
| `--stage STAGE` | Required | The stage to plan |
| `--scope NAME`, `--all-scopes` | The only scope | The scopes to plan |
| `--rev REV` | `HEAD` | The revision |
| `--worktree` | Off | Plans for the files as `git add -A && git commit` would record them now |
| `--trigger TRIGGER` | From the CI provider, else `manual` | What started this run: `push`, `upstream`, `manual` or `schedule` |
| `--attempt N` | From the CI provider, else one more than recorded | This run's attempt |
| `--force-full` | Off, or `HUSHRUN_FORCE_FULL` | Builds and runs every check, whatever the evidence. A stage with `allow_force: false` is blocked instead |
| `--start` | Off | Records `started` for every scope that deploys |
| `--lease DURATION` | The stage's `lease` | The lease `--start` records |
| `--wait-timeout DURATION` | None | While the decision is `wait`, asks again every 15 seconds for up to DURATION |
| `--out FILE` | Off | Writes the plan as JSON, for later jobs' `--plan` |
| `--explain-json FILE` | Off | Writes the planner's full input and output |

```bash
hushrun plan --stage staging --start --out plan.json
```

### hushrun guard

Refuses to deploy a revision that is no longer the head of the stage's branch. When the branch moved on, it records `superseded`, then outputs `HUSHRUN_STALE=true` and exits 0, or exits 4 with `--on-stale fail`. When it can't resolve the branch's head, it exits 1 and records nothing.

Usage: `hushrun guard [OPTIONS] --stage <STAGE>`

| Flag | Default | What it does |
|---|---|---|
| `--stage STAGE` | Required | The stage being deployed |
| `--branch BRANCH` | The stage's `branch` | The branch whose head the revision must be |
| `--remote REMOTE` | `origin` | The git remote whose branch head it reads |
| `--on-stale ACTION` | `skip` | `skip`: record `superseded`, output `stale=true` and exit 0. `fail`: record `superseded` and exit 4 |

It also takes the content options above.

```bash
hushrun --plan plan.json guard --stage production
```

### hushrun status

Shows each stage's requirements and evidence for the current digest.

Usage: `hushrun status [OPTIONS]`

It takes `--scope`, `--all-scopes`, `--rev` and `--worktree` (see above).

```bash
hushrun status --worktree
```

### hushrun run --stage

Runs a stage's checks and records their evidence with the repository. Without names it runs the plan's checks to run; without a plan, every source check the stage requires that lacks valid evidence. It exits 8 when a check fails, once its evidence is recorded.

Usage: `hushrun run --stage <STAGE> [OPTIONS] [CHECK]... [-- <COMMAND>...]`

| Argument or flag | Default | What it does |
|---|---|---|
| `--stage STAGE` | Required | The stage the evidence is for |
| `CHECK` | The plan's checks to run; without a plan, every source check the stage requires that lacks valid evidence | The checks to run |
| `COMMAND` | The check's `run` | After `--`: the command of a check that has no `run` in `hushrun.yaml` |
| `--all` | Off | Every source check the stage requires, skipping those with valid evidence |
| `--force` | Off | Runs checks whose evidence is valid too |
| `--isolated` | Off | Runs each check in its pinned image and platform, with Docker or Podman, on an export of exactly the digested files, with no network unless the check declares one, and only its `env` variables |
| `--timeout DURATION` | The check's `timeout`, else none | Each check's time limit |
| `--environment-image REF@sha256:…` | None | The image this job runs in, pinned by digest. Checks that pin `image` need it |

It also takes the content options above.

```bash
hushrun run --all --stage staging --isolated --worktree
```

### hushrun attest

Records evidence other tools produced: check results, artifacts and reports.

Usage: `hushrun attest [OPTIONS] --stage <STAGE>`

| Flag | Default | What it does |
|---|---|---|
| `--stage STAGE` | Required | The stage the evidence belongs to |
| `--check NAME=RESULT` | None | A check's result, `pass` or `fail`. Repeatable |
| `--artifact NAME=REF` | None | An artifact this run built, at its location for the stage. A tag is resolved to its digest. Repeatable |
| `--report NAME=FILE` | None | A report the checks produced, recorded by digest. Repeatable |
| `--duration DURATION` | `0s` | How long the checks ran |
| `--attempts N` | `1` | The attempts the checks took |
| `--environment-image REF@sha256:…` | None | The image the checks ran in, pinned by digest. Checks that pin `image` need it |

It also takes the content options above.

```bash
hushrun attest --stage staging --check unit=pass --duration 95s
```

### hushrun mark

Records a lifecycle event: `started`, `deployed`, `passed`, `failed` or `superseded`.

Usage: `hushrun mark [OPTIONS] --stage <STAGE> <KIND>`

| Argument or flag | Default | What it does |
|---|---|---|
| `KIND` | Required | `started`, `deployed`, `passed`, `failed` or `superseded` |
| `--stage STAGE` | Required | The stage |
| `--reason TEXT` | None | Why it failed, for `failed`; `stale` or `cancelled`, for `superseded` |
| `--artifact NAME=REF` | None | An artifact deployed or passed, at its location for the stage. A tag is resolved to its digest. Repeatable |
| `--target NAME[=DETAIL]` | None | Where it was deployed, for `deployed` |
| `--by-revision REV` | None | The revision that took over, for `superseded` |

It also takes the content options above.

```bash
hushrun --plan plan.json mark deployed --stage production
```

### hushrun approve

Records your signed approval of an approval check, for a digest and a stage. It shows what you approve, and signs it, by default with this machine's key. Approvals are people's: in CI, or signing as an agent, it exits 2 and records nothing.

Usage: `hushrun approve [OPTIONS] --stage <STAGE> <CHECK>`

| Argument or flag | Default | What it does |
|---|---|---|
| `CHECK` | Required | The approval check, as `hushrun.yaml` names it |
| `--stage STAGE` | Required | The stage that requires the approval |
| `--comment TEXT` | None | Why, recorded with the approval, up to 1000 characters |
| `--out FILE` | Off | Writes the signed event to FILE instead of recording it, to upload elsewhere, such as the dashboard |

It also takes the content options above.

```bash
hushrun approve release --stage production --comment "QA signed off"
```

### hushrun verify

Verifies events and shows who signed them. It exits 5 when verification fails.

Usage: `hushrun verify [OPTIONS] [FILES]...`

| Argument or flag | Default | What it does |
|---|---|---|
| `FILES` | None | Event files to verify |
| `--scope NAME --digest sha256:…` | None | Verifies every event the repository holds for this digest instead |
| `--signers FILE` | Asks the repository's server | The signers `hushrun repository signers --out` saved, to verify files offline |

```bash
hushrun verify --scope default --digest sha256:…
```

### hushrun artifact resolve

Outputs the reference to deploy for the plan's decision, and copies the artifact to this stage's location by digest when it lives elsewhere. When the copy fails, it falls back to a build, with `HUSHRUN_BUILD=true` and a warning that says why. With `--no-fallback`, or for a stage that requires deployment checks, it exits 7 instead.

Usage: `hushrun artifact resolve [OPTIONS] --stage <STAGE>`

| Flag | Default | What it does |
|---|---|---|
| `--stage STAGE` | Required | The stage being deployed |
| `--scope NAME` | The only scope | The scope |
| `--worktree` | Off | Without a plan, decides for the files as `git add -A && git commit` would record them now |
| `--tag TAG` | The revision | The tag written next to the digest |
| `--no-fallback` | Off | Exits 7 when the copy fails, instead of falling back to a build |

Registry credentials come from the Docker configuration, so `docker login` and `gcloud auth configure-docker` work as they are.

```bash
hushrun --plan plan.json artifact resolve --stage production
```

### hushrun artifact copy

Copies an image or index to another repository by digest, and exits 7 when the transfer fails.

Usage: `hushrun artifact copy [OPTIONS] <SOURCE> <DESTINATION>`

| Argument | What it does |
|---|---|
| `SOURCE` | The source, pinned by digest or tagged; a tag is resolved first |
| `DESTINATION` | The destination repository, `REPOSITORY` or `REPOSITORY:TAG` |

```bash
hushrun artifact copy us-docker.pkg.dev/acme/staging/api@sha256:… us-docker.pkg.dev/acme/prod/api:v1
```

### hushrun artifact digest

Prints the digest a reference points to.

Usage: `hushrun artifact digest [OPTIONS] <REFERENCE>`

```bash
hushrun artifact digest us-docker.pkg.dev/acme/staging/api:v1
```

### hushrun policy validate

Validates `hushrun.yaml`, and exits 2 at an error, which names the file, line and column. It reads the working tree's file and needs no server; in a pull request's pipeline, it reads the pull request's own file.

Usage: `hushrun policy validate [OPTIONS]`

| Flag | Default | What it does |
|---|---|---|
| `--rev REV` | The working tree | Validates `hushrun.yaml` at this revision instead |

```console
$ hushrun policy validate
hushrun.yaml is valid: 2 scope(s), 3 check(s), 1 artifact(s), 2 stage(s)
```

### hushrun policy explain

Shows a stage's requirements, whose events count for each, and whether hushrun's server makes the stage CI-only. Without a repository URL, or when the server doesn't answer within 15 seconds, it shows the rest and says that CI-only stages weren't checked.

Usage: `hushrun policy explain [OPTIONS] --stage <STAGE>`

| Flag | Default | What it does |
|---|---|---|
| `--stage STAGE` | Required | The stage |
| `--signers FILE` | Asks the repository's server | The signers `hushrun repository signers --out` saved, to tell offline whether the stage is CI-only |

```bash
hushrun policy explain --stage production
```

### hushrun adopt

Writes a starter `hushrun.yaml` and GitHub Actions workflows that call hushrun's reusable deploy workflow, and prints the next steps.

Usage: `hushrun adopt [OPTIONS]`

| Flag | Default | What it does |
|---|---|---|
| `--repository ID` | The CI provider's, else the `origin` remote's | The repository id, such as `github.com/acme/app` |
| `--stage NAME=BRANCH` | `staging=develop`, `production=main` | A stage and its branch, in delivery order. Repeatable |
| `--image STAGE=REPOSITORY` | None | A stage's image repository. Repeatable |
| `--repository-base URL` | `HUSHRUN_REPOSITORY_BASE` | The organization's base URL, under which each repository's URL is `<base>/<repository id>` |
| `--workflow OWNER/REPO/PATH@REF` | This release's | The reusable workflow to call, pinned by commit |
| `--hushrun-version VERSION` | This one's | The hushrun version the workflows install |
| `--dry-run` | Off | Prints what it would write, and writes nothing |
| `--force` | Off | Replaces files that differ from what adopt writes |

```bash
hushrun adopt --dry-run
```

## Server and setup

### hushrun repository ls

Lists the repository's events on hushrun's server.

Usage: `hushrun repository ls [OPTIONS]`

| Flag | Default | What it does |
|---|---|---|
| `--scope NAME` | All | Events of this scope |
| `--digest sha256:…` | All | Events of this digest; needs `--scope` |
| `--stage STAGE` | All | Events of this stage |

### hushrun repository show

Shows one event.

Usage: `hushrun repository show [OPTIONS] <KEY>`

### hushrun repository signers

Shows the signers whose events count for the repository, as its server names them, with the server's signed statement over them.

Usage: `hushrun repository signers [OPTIONS]`

| Flag | Default | What it does |
|---|---|---|
| `--out FILE` | Off | Saves the answer, for `hushrun verify --signers` and `hushrun policy explain --signers` |

```bash
hushrun repository signers --out signers.json
```

### hushrun repository gc

Deletes events older than a duration, except the newest deployment of each scope and stage and the events of its digest. For organization admins.

Usage: `hushrun repository gc [OPTIONS] --older-than <DURATION>`

| Flag | Default | What it does |
|---|---|---|
| `--older-than DURATION` | Required | Deletes what was written more than this long ago, such as `180d` |
| `--dry-run` | Off | Lists what it would delete, and deletes nothing |
| `--key FILE` | The command's signer | The key that signs each deletion, on a server that keeps a transparency log |

```bash
hushrun repository gc --older-than 180d --dry-run
```

### hushrun repository monitor

Replays the repository's transparency log on hushrun's server, and checks every checkpoint, and every listing that plans recorded against it. It exits 5 when the server contradicts its own log, and needs `transparency_log` in `hushrun.yaml`.

Usage: `hushrun repository monitor [OPTIONS]`

| Flag | Default | What it does |
|---|---|---|
| `--state FILE` | Off | Remembers the newest checkpoint in FILE, and checks the next run against it |

```bash
hushrun repository monitor --state monitor.json
```

### hushrun repository usage

Shows the organization's plan and the checks recorded this period, against what the plan includes and its limit, with this repository's share. It asks the backend `hushrun run` records to, as `hushrun whoami` shows it, with the same credential. Results kept on this machine aren't metered. It exits 0, or 6 when the server can't be reached, refuses the credential or answers something else. See [Plans and limits](https://hushrun.com/docs/plans.md).

Usage: `hushrun repository usage [OPTIONS]`

```console
$ hushrun repository usage
backend    this machine only, in /Users/ana/Library/Caches/hushrun/results
usage      results stay on this machine, so nothing is metered
```

### hushrun repository stats

Counts what hushrun recorded, to share volume without telemetry, and prints what you may choose to paste. It sends nothing else anywhere.

Usage: `hushrun repository stats [OPTIONS]`

| Flag | Default | What it does |
|---|---|---|
| `--since DURATION` | `30d` | Counts what was recorded in this long, such as `7d` or `12w` |
| `--local` | Off | Counts this machine's `hushrun run` results, in hushrun's cache directory, instead of the repository's events on the server: by verdict, by check, distinct run keys and the time skips saved |

Without `--local`, it reads one listing of the repository's events, and no event: events by kind, distinct digests, and check events by stage.

```console
$ hushrun repository stats --local
hushrun run's results on this machine in the last 30d (since 2026-09-27 16:51 UTC), in /Users/ana/Library/Caches/hushrun/results
total            11
by verdict       skip 5, pass 4, fail 2
run keys         5
by check         unit 7, lint 4
saved by skips   4s
```

### hushrun sigstore refresh

Updates the Sigstore trust root, which verifies keyless signatures and timestamps, from its TUF repository. hushrun verifies what it gets, and caches it for offline verification. It exits 9 when the TUF repository can't be reached.

Usage: `hushrun sigstore refresh [OPTIONS]`

### hushrun keys generate

Generates a signing key, PKCS#8 PEM with mode 0600. It never overwrites a key file.

Usage: `hushrun keys generate [OPTIONS]`

| Flag | Default | What it does |
|---|---|---|
| `--out FILE` | Stdout | Writes the private key to FILE |
| `--algorithm ALGORITHM` | `ed25519` | `ed25519`, or `ecdsa-p256`, which hardware keys use |

```console
$ hushrun keys generate --out signing.pem
public key: ed25519:e2iTpmZCZRojWi76uPqruO7B6y5vW26tItDTBpv6Mh0=
key id: sha256:0c33bcfe81849a0ffe99200fb1e55533ed278adf56e2411e8420bc08cb8e1a97
private key: signing.pem (mode 0600)
```

### hushrun keys public

Prints the public key and key id of a private key.

Usage: `hushrun keys public [OPTIONS]`

| Flag | Default | What it does |
|---|---|---|
| `--key FILE` | The key this command would sign with: `HUSHRUN_SIGNING_KEY`, `HUSHRUN_SIGNING_KEY_FILE` or this machine's | The private key |

### hushrun doctor

Checks the setup and says what to fix: git, the CI provider, where results go, `hushrun.yaml`, the repository's URL, server and signers, its transparency log, the clock, the signer, the timestamp authorities and the Sigstore trust root, registry access, the deploy workflow, configuration baked into the source, and the default branch, or on GitLab its API, protected branches and who may set pipeline variables. It exits 2 when any check fails; warnings don't change the exit code. `--format json` prints `hushrun.doctor/v1`.

Usage: `hushrun doctor [OPTIONS]`

```bash
hushrun doctor --repository-url https://api.hushrun.com/l/acme/shop
```


## Global options

Every command takes these, before or after its name. A flag wins over its variable.

| Option | Variable | Default | What it does |
|---|---|---|---|
| `-C DIR`, `--repo DIR` | None | The current directory | Runs as if hushrun started in DIR |
| `--config PATH` | `HUSHRUN_CONFIG` | `hushrun.yaml` in the content being evaluated | The path of `hushrun.yaml` |
| `--config-ref REF` | None | None | Reads `hushrun.yaml` from this git ref, such as `origin/main`. A pull request's pipeline must name the branch it merges into. Doesn't go with `--config` |
| `--repository-url URL` | `HUSHRUN_REPOSITORY_URL` | `hushrun.yaml`'s `repository_url` | The repository's URL on hushrun. In CI only `HUSHRUN_REPOSITORY_URL` counts, and a different `--repository-url` exits 2 |
| `--plan FILE` | `HUSHRUN_PLAN_JSON`, which holds the plan itself | None | The plan this command carries out: a file, or `-` for stdin |
| `--format FORMAT` | `HUSHRUN_FORMAT` | `text` | The output format on stdout: `text`, `json`, `env` or `dotenv` |
| `--dotenv FILE` | None | None | Also writes the outputs to FILE in dotenv form, for GitLab |
| `--no-ci-output` | `HUSHRUN_NO_CI_OUTPUT` | Off | Writes no CI outputs, step summaries or annotations |
| `-v`, `--verbose` | `HUSHRUN_LOG` | Warnings | More logging on stderr: `-v` info, `-vv` debug, `-vvv` trace |
| `-q`, `--quiet` | None | Off | Errors only on stderr. In `hushrun run` without a stage, none of hushrun's lines |
| `--color WHEN` | `NO_COLOR` | `auto` | Colored logs: `auto`, `always` or `never`. `auto` colors them when stderr is a terminal and `NO_COLOR` isn't set |
| `-j N`, `--jobs N` | None | The available cores | Threads that hash files |
| `-h`, `--help` | None | None | Prints help. `--help` says more than `-h` |
| `-V`, `--version` | None | None | Prints the version |

[Environment variables](https://hushrun.com/docs/reference/environment.md) has every variable, and [Configuration](https://hushrun.com/docs/reference/configuration.md#sources) the order in which hushrun reads them.

## Output formats

`--format` decides what a command prints on stdout. Logs and messages always go to stderr.

| Format | What stdout gets |
|---|---|
| `text` | Lines for people. The default |
| `json` | One JSON object on one line, whose `schema` field names its shape, such as `hushrun.digest/v1` |
| `env` | `HUSHRUN_<KEY>=value` lines, quoted for a POSIX shell's `eval` |
| `dotenv` | The same lines unquoted, as GitLab's dotenv reports read them. hushrun refuses values of several lines, more than 20 variables, or 5 KiB or more, and leaves out `HUSHRUN_JSON` |

- `--dotenv FILE` writes the dotenv form to FILE as well, whatever `--format` is, for `digest`, `plan`, `guard`, `attest`, `mark` and `artifact`.
- A command without outputs prints text for `env` and `dotenv`.
- On GitHub Actions, `plan`, `digest`, `guard`, `attest` and `artifact` also append their outputs to `$GITHUB_OUTPUT` under their lowercase names, such as `mode` and `digest`, write a step summary, and annotate the run, unless `--no-ci-output` is given.
- Keep the output in a variable before you `eval` it: `out=$(hushrun guard --stage production --format env) && eval "$out"`. `eval "$(hushrun …)"` succeeds even when hushrun fails.

The outputs, by command, with `<SCOPE>` and `<ARTIFACT>` uppercased and `-` written as `_`:

| Command | Outputs |
|---|---|
| `hushrun digest` | `HUSHRUN_DIGEST`, or `HUSHRUN_DIGEST_<SCOPE>` for each of several scopes; `HUSHRUN_REVISION` |
| `hushrun plan`, one scope | `HUSHRUN_MODE`, `HUSHRUN_REASON`, `HUSHRUN_DIGEST`, `HUSHRUN_REVISION`, `HUSHRUN_ATTEMPT`, `HUSHRUN_BUILD`, `HUSHRUN_REF`, `HUSHRUN_ARTIFACTS` (JSON), `HUSHRUN_CHECKS` and `HUSHRUN_DEPLOYMENT_CHECKS` (names separated by spaces), `HUSHRUN_JSON` (the whole plan) |
| `hushrun plan`, several scopes | `HUSHRUN_REVISION`, `HUSHRUN_ATTEMPT`, `HUSHRUN_ACTIONABLE_SCOPES` (JSON), `HUSHRUN_JSON` |
| `hushrun guard` | `HUSHRUN_STALE` (`true` or `false`), `HUSHRUN_HEAD` |
| `hushrun attest` | `HUSHRUN_DIGEST`, `HUSHRUN_REVISION`, `HUSHRUN_REF` for a single artifact, `HUSHRUN_REF_<ARTIFACT>` |
| `hushrun mark` | `HUSHRUN_DIGEST`, `HUSHRUN_ATTEMPT` |
| `hushrun artifact resolve` | `HUSHRUN_BUILD`, `HUSHRUN_REF` and `HUSHRUN_DIGEST` for a single artifact, `HUSHRUN_REF_<ARTIFACT>` |
| `hushrun artifact copy`, `hushrun artifact digest` | `HUSHRUN_DIGEST`, `HUSHRUN_REF` |
| `hushrun keys generate`, `hushrun keys public` | `HUSHRUN_PUBLIC_KEY`, `HUSHRUN_KEYID` |

```console
$ hushrun digest --format env
HUSHRUN_DIGEST_API=sha256:79a343b472b042bd73eb52f6457750d062d7991490b2d4baacc896dac50c921d
HUSHRUN_DIGEST_WEB=sha256:83bc69ea5b9510c5aae5712d3d33a107f205e0e32435b6e657bdffa7a6407273
HUSHRUN_REVISION=c3b57fb1add5a62e13a97fa1324e083067bc48c1
$ hushrun digest --scope api --format json
{"canonicalization":"hushrun-source-v1","schema":"hushrun.digest/v1","scopes":[{"bytes":13,"digest":"sha256:79a343b472b042bd73eb52f6457750d062d7991490b2d4baacc896dac50c921d","files":1,"scope":"api"}],"source":{"objectFormat":"sha1","ref":"refs/heads/main","revision":"c3b57fb1add5a62e13a97fa1324e083067bc48c1","tree":"bc00258b51f36ddfe425f303ce7239b69fb1b29d","vcs":"git"},"stats":{"cacheHits":1,"elapsedMs":1,"entries":3,"hashed":0,"hashedBytes":0}}
```

## Exit codes

### Every command

| Code | Meaning | Where |
|---|---|---|
| 0 | Success | Every command. `plan`: any decision but `blocked`. `guard`: a stale revision with `--on-stale skip` |
| 1 | An unexpected error | Every command. `whoami`: the backend refused the credential. `token`: the server answered 401, 403 or 404. `init`: a file couldn't be written, or the install failed. `guard`: the branch's head can't be resolved |
| 2 | A usage or configuration error | Every command: a flag or variable hushrun can't take, a `hushrun.yaml` that doesn't validate, a missing repository URL, or one that doesn't match in CI. `doctor`: a check failed. `init`: no terminal and no `--yes`. `approve`: run in CI |
| 3 | Blocked | `plan` |
| 4 | Stale | `guard --on-stale fail`: the revision is no longer the head of the stage's branch |
| 5 | Verification failed | `verify`. `repository monitor`: the server contradicts its own log |
| 6 | The server is unavailable or refused access | Commands that read or write the repository's events, and a write refused for the organization's plan, other than a check result. `whoami`, `login`, `token` and `repository usage` when the server can't be reached |
| 7 | The transfer failed | `artifact copy`. `artifact resolve` with `--no-fallback`, or for a stage that requires deployment checks |
| 8 | A check failed, and its evidence was recorded | `run --stage` |
| 9 | The signer is unavailable or refused | Commands that sign: the CI's ID token, Fulcio, or every timestamp authority. `sigstore refresh`: the TUF repository |

### hushrun run without a stage

`hushrun run [NAME] -- COMMAND` ends as the command does, so a script or CI job reads the check's own result:

| Code | When |
|---|---|
| The command's | It ran. A code outside 0 to 255 becomes 1 |
| 0 | It skipped: this exact code already passed |
| 2 | A usage error: no command after `--`, more than one NAME, no NAME when the program gives none, or a flag that needs `--stage`. The command doesn't run |
| 126 | The command was found but can't start, such as a file without execute permission |
| 127 | The command wasn't found |
| 128 + N | The command was stopped by signal N, such as 143 after `SIGTERM`. Nothing is recorded |

Results being off, a backend that can't be reached, one that refuses the credential, or a variable hushrun can't read never change the exit code: the command runs, and its code is hushrun's.
