> For the complete documentation index, see [llms.txt](https://docs-lunar.earthly.dev/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs-lunar.earthly.dev/docs/lunar-cli.md).

# Lunar CLI Reference

Complete reference for every Lunar CLI command, subcommand, flag, and environment variable across collectors, catalogers, policies, and Hub operations.

This document provides a comprehensive reference for all available options and commands in the Lunar CLI.

## Global options

### `--config-dir <config-dir>`, `LUNAR_CONFIG_DIR=<config-dir>`

* Type: `string`
* Optional
* Default: `.`

The path to the directory containing `lunar-config.yml`. This path is relative to the current working directory.

### `--hub-host <hostname>`, `LUNAR_HUB_HOST=<hostname>`

* Type: `string`
* Optional

Override the host name of the Lunar Hub. This setting is inferred from the Lunar config if not specified.

Correct:

* `hub.example.com`
* `myhost`

Incorrect:

* `https://hub.example.com`
* `http://hub.example.com`
* `hub.example.com:8080`
* `myhost:443`

### `--hub-grpc-port <port>`, `LUNAR_HUB_GRPC_PORT=<port>`

* Type: `integer`
* Optional

Override the GRPC port of the Lunar Hub. This setting is inferred from the Lunar config if not specified.

### `--hub-http-port <port>`, `LUNAR_HUB_HTTP_PORT=<port>`

* Type: `integer`
* Optional

Override the HTTP port of the Lunar Hub. This setting is inferred from the Lunar config if not specified.

### `--hub-insecure`, `LUNAR_HUB_INSECURE=true`

* Type: `boolean`
* Optional

If true, use insecure HTTP connections to the Hub server.

### `--no-hub`, `LUNAR_NO_HUB=true`

* Type: `boolean`
* Optional

Skip Hub interactions for dev commands (`lunar collector dev` and `lunar policy dev`). When enabled, the commands will run without connecting to Lunar Hub. Note that `--component-json` is required for `lunar policy dev` when this option is enabled, and a [Git platform token](#lunar_github_token-lunar_gitlab_token) must be set if GitHub or GitLab access is needed.

### `LUNAR_HUB_TOKEN`

* Type: `string`
* Optional once you have run `lunar login`; otherwise required for commands that interact with the Hub server

When set, this credential takes precedence. Otherwise the CLI uses the session stored by [`lunar login`](#lunar-login). See [Authorization](/docs/authorization.md).

### `LUNAR_GITHUB_TOKEN`, `LUNAR_GITLAB_TOKEN`

* Type: `string`
* Optional

Git platform tokens for commands that read repositories: `lunar hub pull --dry-run`, `lunar collector dev` and `lunar policy dev`. These commands resolve a credential in this order:

1. `LUNAR_GITHUB_TOKEN` or `LUNAR_GITLAB_TOKEN`
2. The token stored by `lunar login`
3. For a service-token caller, a Hub-vended credential
4. For every other caller, the token from `gh` or `glab`

The last two are alternatives: a service-token caller does not fall through from the Hub to `gh` or `glab`. Those CLI credentials provide Git access only; they do not sign in to Lunar Hub.

## Login commands

### `lunar login`

```bash
lunar login [--forge github|gitlab] [--host <host>] [--list]
```

* Requires: [`public`](/docs/authorization.md#the-three-fixed-permissions)

Signs in through a configured GitHub or GitLab provider. GitHub uses Device Flow; GitLab uses Device Flow when enabled and PKCE otherwise. The CLI stores and refreshes the login in the OS keyring, or in `~/.lunar/credentials.json` with mode `0600`.

* `--forge`, `--host`: select a Git platform and host when the Hub accepts more than one
* `--list`: list accepted providers and exit

### `lunar logout`

```bash
lunar logout
```

* Requires: [`authenticated`](/docs/authorization.md#the-three-fixed-permissions)

Deletes the stored login and revokes its Git platform token when supported. Otherwise it prints the manual revocation URL. The Hub session expires on its own.

### `lunar whoami`

```bash
lunar whoami
```

* Requires: [`authenticated`](/docs/authorization.md#the-three-fixed-permissions)

Prints the current principal, Git platform, host, and credential source. A session expiry is also shown when the credential came from a stored `lunar login`.

## Licence Commands

Inspect a Lunar licence JWT and extract artefacts for cluster bootstrap. These commands run **locally** — they verify the licence against a trust list embedded in the `lunar` binary, so they work before a Hub exists (the canonical bootstrap case).

### Shared options

#### `--licence-file <path>`, `LUNAR_LICENCE_FILE=<path>`

* Type: `string`
* Required (or set the env var)

Path to the licence JWT on disk. All `lunar licence` subcommands accept this flag, or read `LUNAR_LICENCE_FILE` if it's unset.

### `lunar licence verify`

* Form:

  ```bash
  lunar licence verify --licence-file <path>
  ```
* Requires: nothing — runs locally, with no Hub call

Verify the licence signature against the trust list embedded in this binary and print a customer-facing summary: tenant, expiry, and whether the licence carries a GHCR image-pull credential. Credentials are never printed — use `lunar licence registry-token` if you need the credential itself for manual `docker login`.

Example output:

```
Licence valid for tenant: acme-corp
Expires:                  2027-12-31 (in 587 days)

GHCR pull credential: configured — run `lunar licence pull-secret` to provision
```

### `lunar licence pull-secret`

* Form:

  ```bash
  lunar licence pull-secret --licence-file <path> --namespace <ns> [--name <name>] [--out <path>]
  ```
* Requires: nothing — runs locally, with no Hub call

Emit a Kubernetes `imagePullSecret` (type `kubernetes.io/dockerconfigjson`) that authenticates the cluster against `ghcr.io` using the credential carried in the licence. Pipe to `kubectl apply -f -`, commit to GitOps, whatever fits.

Errors if the licence does not carry a GHCR pull credential.

#### `--namespace <ns>` | `-n`

* Type: `string`
* Required

Kubernetes namespace for the Secret.

#### `--name <name>`

* Type: `string`
* Optional
* Default: `regcred`

Kubernetes Secret name. The Hub chart's `imagePullSecrets` reference defaults to `regcred`.

#### `--out <path>` | `-o`

* Type: `string`
* Optional

Write the manifest to a file instead of stdout. The file is created with mode `0600` because it contains a sensitive credential.

#### Examples

```bash
# Provision the pull secret in the lunar namespace, applied to the cluster directly.
lunar licence pull-secret \
  --licence-file=path/to/hub-licence.jwt \
  --namespace=lunar | kubectl apply -f -

# Same, but for the snippet pods namespace.
lunar licence pull-secret \
  --licence-file=path/to/hub-licence.jwt \
  --namespace=lunar-scripts | kubectl apply -f -

# Write the manifest to disk for GitOps.
lunar licence pull-secret \
  --licence-file=path/to/hub-licence.jwt \
  --namespace=lunar \
  --out=regcred.yaml
```

### `lunar licence registry-token`

* Form:

  ```bash
  lunar licence registry-token --licence-file <path>
  ```
* Requires: nothing — the credential is read from the licence file, with no Hub call

Print the raw GHCR pull credential from the licence to stdout, suitable for piping into `docker login --password-stdin`. Errors if the licence does not carry one.

Example:

```bash
lunar licence registry-token --licence-file=path/to/hub-licence.jwt | \
  docker login ghcr.io -u earthly-bot --password-stdin
```

## Config Commands

### `lunar config schema`

* Form:

  ```bash
  lunar config schema
  ```
* Requires: nothing — the schema is generated from the binary, with no Hub call

Print the JSON Schema (draft-07 format) for `lunar-config.yml` to stdout, generated from the same manifest definition the Hub uses. Redirect it into your config repo for editor autocomplete and inline structural feedback:

```bash
lunar config schema > lunar-config.schema.json
```

The schema is a structural aid only (it can't resolve `uses:` plugins). For authoritative pre-merge validation, use [`lunar hub pull --dry-run`](#lunar-hub-pull). See [Validating your config](/configuration/lunar-config/validation.md).

### `lunar hub pull`

* Form:

  ```bash
  lunar hub pull [--dry-run] [--rerun-code-collectors|-l] [--include-pr-commits] [--pr-max-age-days <days>] [--rerun-catalogers|-t] [--cancel <config-commit-sha>]... [--actor <name>] <repo>
  ```
* Requires: [`hub.pull`](/docs/authorization.md#hub-pull), including with `--rerun-code-collectors` and `--cancel`; `--dry-run` runs locally

The `lunar hub pull` command is used to instruct Lunar Hub to pull the latest configuration from a given repository.

For GitHub Actions workflows, the [`sync-config` action](/install/lunar-hub/self-hosted/sync-config.md) is a thin wrapper around this command and exposes the same flags as inputs.

{% hint style="warning" %}
Using `--rerun-code-collectors` or `--rerun-catalogers` can trigger a large amount of work in Lunar Hub, especially across many components or PRs. Expect a backlog and elevated load while the reruns process.
{% endhint %}

#### `<repo>`

* Type: `string`
* Form: `github://<owner>/<repo>[/<config-path>]@<branch-or-sha>` or `gitlab://<host>/<namespace>/<project>[/-/<config-path>]@<branch-or-sha>`
* Required

The repository to pull configuration from. This should be the main repository containing your lunar configuration files.

**Examples:**

* `github://acme-corp/lunar@main`
* `github://acme-corp/lunar@de4adbeef`
* `gitlab://gitlab.com/acme-corp/lunar@main`
* `gitlab://gitlab.example.com/acme-corp/platform/lunar@main`

The GitLab form requires an explicit host, including for `gitlab.com`, and the namespace may contain subgroups.

The optional `<config-path>` selects which configuration file to load, so one repository can hold several — one per Hub or per environment — and a monorepo can keep its configuration somewhere other than the root. It may name the file itself (`github://acme-corp/lunar/lunar-config.dev.yml@main`) or a directory holding a `lunar-config.yml` (`github://acme-corp/lunar/configs/dev@main`). Omitted, the entry point is `lunar-config.yml` at the repository root.

A local filesystem path is also accepted, and may likewise name either a directory or the configuration file inside it — useful with `--dry-run`.

#### `--dry-run`

* Type: `boolean`
* Optional

Validate the configuration and exit **without** applying it. The dry run performs the Hub's own load-and-validate steps against the same code — clone the config, load the entry point + its fragments, resolve every `uses:` plugin, validate the whole manifest, check that the image every collector, policy, and cataloger runs in exists, and resolve each unpinned component's default branch — then stops before persisting. It does not install a script's dependencies, so that one step could still fail on a real pull; see [Validating your config](/configuration/lunar-config/validation.md) for the details.

Public repositories resolve anonymously; private and wildcard repositories use a [Git platform token](#lunar_github_token-lunar_gitlab_token). The Hub checks the configuration's images with its own registry credentials, so the dry run needs a Hub unless `--no-hub` skips that check. See [Script images](/configuration/lunar-config/validation.md#script-images).

#### `--rerun-code-collectors` | `-l`

* Type: `boolean`
* Optional

Rerun affected code collectors after applying the configuration.

#### `--include-pr-commits`

* Type: `boolean`
* Optional

Include PR commits when rerunning code collectors. On a pull request, only the components the pull request's changed files matched are rerun, as matched under the configuration in place at its last push: a component added or widened since is picked up at the pull request's next push, and stays on its previous configuration until then. A pull request whose changed files matched no component is skipped; one Lunar Hub never saw a push for reruns every component of its repository.

#### `--pr-max-age-days <days>`

* Type: `integer`
* Optional
* Default: `5`

Ignore PR commits older than this maximum number of days.

#### `--rerun-catalogers` | `-t`

* Type: `boolean`
* Optional
* Default: `false`

Rerun global catalogers after pulling the manifest. Catalogers are skipped by default — pulling a manifest no longer triggers them automatically. Per-component catalogers (`component-repo`, `component-cron`) are unaffected and continue to run on their own hooks.

#### `--cancel <config-commit-sha>`

* Type: `string`, repeatable
* Optional

Cancel a configuration version, named by the full commit SHA of the config repository it was pulled from. Its queued work stops, and every commit it judged is re-evaluated under the configuration that is current once this pull finishes. Repeat the flag to cancel several versions. The cancel applies even when the pulled version was already installed, which is the usual case once the revert has synced. See [Cancel a Bad Config](/docs/guides/cancel-a-bad-config.md) for what happens next and what it can't undo.

Lunar Hub refuses a version it doesn't know and the version that is current, and cancels nothing when it refuses any of them. A Lunar Hub that predates `--cancel` ignores it, so the command fails with `the Hub did not confirm canceling version <sha>` rather than reporting success.

#### `--actor <name>`

* Type: `string`
* Optional

Who `--cancel` is recorded as. Required with the service token. A logged-in user is recorded as themselves, and a CI job as the person it runs for.

### `lunar hub run-code-collectors`

{% hint style="warning" %}
**Deprecated — use** [**`lunar collector run`**](#lunar-collector-run)**.** That command covers cron collectors as well, and can scope a rerun to one component, PR, or commit. `lunar collector run --only-code` is the closest equivalent.

This command still works and is not scheduled for removal. It remains the only collector trigger available against a Lunar Hub older than the release that added `lunar collector run`, and it is currently the only way to sweep primary-branch commits *without* also covering recent PR commits.
{% endhint %}

* Form:

  ```bash
  lunar hub run-code-collectors [--pr-max-age-days <days>] [--include-pr-commits]
  ```
* Requires: [`hub.pull`](/docs/authorization.md#hub-pull)

The `lunar hub run-code-collectors` command instructs Lunar Hub to rerun code collectors.

{% hint style="warning" %}
This command can trigger a large amount of work in Lunar Hub. It reruns code collectors across all components — and across recent PR commits when `--include-pr-commits` is set — which can produce a significant backlog and elevated load.
{% endhint %}

#### `--pr-max-age-days <days>`

* Type: `integer`
* Optional
* Default: `5`

Ignore PR commits older than this maximum number of days.

#### `--include-pr-commits`

* Type: `boolean`
* Optional

Include PR commits when running code collectors. On a pull request, only the components the pull request's changed files matched are rerun, as matched under the configuration in place at its last push: a component added or widened since is picked up at the pull request's next push, and stays on its previous configuration until then. A pull request whose changed files matched no component is skipped; one Lunar Hub never saw a push for reruns every component of its repository.

### `lunar hub permissions`

* Form:

  ```bash
  lunar hub permissions [--verbose] [--all] [--defaults] [--output-json]
  ```
* Requires: [`authenticated`](/docs/authorization.md#the-three-fixed-permissions); `--defaults` requires nothing

Prints the effective [permission table](/docs/authorization.md#permissions). `*` marks configured role overrides. A note under the table says when the connected Hub lets [configuration repository administrators](/docs/authorization.md#config-repo-administrators) past it.

* `--verbose`: include each RPC and its request checks
* `--all`: include `internal`, `authenticated`, and `public`
* `--defaults`: print built-in defaults without contacting the Hub
* `--output-json`: print the full table as JSON

### `lunar hub merge-gate`

* Form:

  ```bash
  lunar hub merge-gate status [--group <path>] [--output-json]
  lunar hub merge-gate off [--group <path>] [--for <duration>] --reason <text> [--actor <name>] [--yes]
  lunar hub merge-gate on [--group <path>] [--reason <text>] [--actor <name>] [--yes]
  ```
* Requires: [`hub.read`](/docs/authorization.md#hub-read) for `status` and to preview; [`hub.operate`](/docs/authorization.md#hub-operate) to switch with `--yes`

The `lunar hub merge-gate` command switches Lunar's merge surface on GitLab off and back on without a redeploy: the `Earthly Lunar` [external status check](/install/git-platforms/gitlab.md#external-status-checks-ultimate) and Lunar's [project badges](/install/git-platforms/gitlab.md#project-badges), for every project or for a group and its subgroups. The Hub applies a change within minutes of `--yes`. Every change is recorded in `hub.merge_gate_changes`, an append-only ledger readable over the SQL API. On a Hub without GitLab the command reports that GitHub is not implemented yet.

Like `lunar queue clear`, `off` and `on` print what they would do and exit without `--yes`.

#### `status`

Lists the rules in force, least specific first, with who set each, why and until when, and how many projects the Hub has still to converge. The first line shows the operator's flags: with `HUB_GITLAB_MERGE_GATE_ENABLED=false` the gate is off whatever the rules say.

#### `off`

Removes the status check, so open merge requests fall back to a commit status, and removes Lunar's badges until the gate is back on. It always expires.

#### `on`

Switches its scope on: the scope's own off is retired and, while a broader off still covers it, the scope is pinned on underneath that off for as long as it stands (`status` shows the pin as `on`, expiring with the off). The check is re-created, answering `passed` for every merge request open at that moment, and badges return on each project's next default-branch evaluation. A narrower off keeps its own expiry. To stage a rollout, switch every project off and pin groups on one at a time.

#### `--group <path>`

* Type: `string`
* Optional
* Default: every project

A group path; its subgroups are included.

#### `--for <duration>`

* Type: `string`
* Optional (`off` only)
* Default: the Hub's `HUB_MERGE_GATE_OFF_MAX`

How long the off lasts, such as `4h` or `7d`. It may not exceed `HUB_MERGE_GATE_OFF_MAX` (default `720h`; self-hosted operators set it on the Hub, Lunar Dedicated customers ask Earthly). A longer value is rejected, not shortened.

#### `--reason <text>`

* Type: `string`
* Required for `off`, optional for `on`

Recorded in the ledger.

#### `--actor <name>`

* Type: `string`
* Optional

Who is switching, when using the service token. A logged-in user is recorded as themselves.

#### `--yes`

* Type: `boolean`
* Optional

Switch. Without it the command previews and exits.

#### `--output-json`

* Type: `boolean`
* Optional (`status` only)

Print the status as JSON.

#### Examples

```bash
# Switch a group off for four hours.
lunar hub merge-gate off --group acme/secure --for 4h --reason "pilot" --yes

# See the rules in force and how far the Hub has converged.
lunar hub merge-gate status

# Switch the group back on early.
lunar hub merge-gate on --group acme/secure --yes

# Stage a rollout: everything off, then one group at a time.
lunar hub merge-gate off --for 30d --reason "staged rollout" --yes
lunar hub merge-gate on --group acme/pilot --reason "first wave" --yes
```

### `lunar hub get-logs`

* Form:

  ```bash
  lunar hub get-logs [--namespace <namespace>] [--output <format>] [--tail <lines>]
  ```
* Requires: [`admin.read`](/docs/authorization.md#admin-read)

The `lunar hub get-logs` command retrieves logs from Lunar Hub.

#### `--namespace <namespace>` | `-n`

* Type: `string`
* Optional

The Kubernetes namespace to retrieve logs from.

#### `--output <format>` | `-o`

* Type: `string`
* Optional

Output format for the logs.

#### `--tail <lines>`

* Type: `integer`
* Optional
* Default: `1000`

Number of lines to capture from the end of each container's log. Pass `-1` to capture the whole log.

## Domain Commands

### `lunar domain ls`

{% hint style="info" %}
**Coming Soon** — This feature is not yet available.
{% endhint %}

* Form:

  ```bash
  lunar domain ls
  ```

The `lunar domain ls` command is used to list all domains.

## Component Commands

### `lunar component ls`

{% hint style="info" %}
**Coming Soon** — This feature is not yet available.
{% endhint %}

* Form:

  ```bash
  lunar component ls
  ```

The `lunar component ls` command is used to list all components.

### `lunar component get-json`

* Form:

  ```bash
  lunar component get-json [--git-sha <git-sha>] [--pr <pr-number>] [component-name]
  ```
* Requires: [`components.read`](/docs/authorization.md#components-read)

The `lunar component get-json` command is used to retrieve the component JSON for a specified component.

#### `[component-name]`

* Type: `string`

The name of the component to retrieve the JSON for. If not provided, falls back to the `LUNAR_COMPONENT_ID` environment variable.

#### `--git-sha <git-sha>`

* Type: `string`
* Optional

The specific git SHA to retrieve the component JSON for. The result includes every collection Lunar Hub has recorded at that commit, including one written a moment earlier, so a collector or CD step can read back data it just submitted. If not specified, the latest component JSON is retrieved, which can trail a fresh write by a short while.

#### `--pr <pr-number>`

* Type: `integer`
* Optional

The PR number to retrieve the component JSON for. If not specified, and no git SHA is provided, the component JSON for the primary branch will be retrieved. Combined with `--git-sha`, the Component JSON is the same with or without it, because it is recorded per commit; a `--git-sha` on its own resolves the commit whether it was reached through the primary branch or a PR.

#### `--pretty` | `-p`

* Type: `boolean`
* Optional

Pretty-print the JSON output.

Example:

```bash
lunar component get-json github.com/my-org/my-repo
```

## Cataloger Commands

`lunar cataloger` manages catalogers from outside. This is distinct from `lunar catalog`, which is the SDK command catalogers use *inside* their own scripts to emit catalog entries.

### `lunar cataloger get-json`

* Form:

  ```bash
  lunar cataloger get-json [--cataloger <name>] [--component <name>] [--ts <timestamp>] [--pretty]
  ```
* Requires: [`components.read`](/docs/authorization.md#components-read)

The `lunar cataloger get-json` command retrieves the catalog JSON from Lunar Hub. By default it returns the latest merged catalog snapshot (the same JSON that all catalogers contribute to). Use `--cataloger` to fetch a single cataloger's most recent contribution (its delta), `--component` to scope a per-component cataloger to one component, and `--ts` to fetch a historical snapshot.

#### `--cataloger <name>`

* Type: `string`
* Optional

Name of the cataloger whose delta should be returned. If omitted, the merged catalog snapshot is returned. The cataloger is resolved by exact name in the current manifest; an unknown name returns an error.

Whether `--component` is allowed depends on the cataloger's hooks:

* A **global** cataloger (only `cron` / `repo` hooks) returns a single delta. Passing `--component` is rejected.
* A **per-component** cataloger (any `component-cron` / `component-repo` hook) stores one delta per component. `--component` is required; omitting it is rejected with a "per-component scoped" error.

#### `--component <name>`

* Type: `string`
* Optional

Component to scope the lookup to (e.g. `github.com/my-org/my-repo`). Only valid together with `--cataloger`, and only for per-component catalogers — required for those, rejected for purely global ones. Returns the delta that cataloger emitted for the named component.

#### `--ts <timestamp>`

* Type: `string`
* Optional

Return the row whose `created_at` is at or immediately before this timestamp. If omitted, the latest row is returned. The command exits with an error if no row exists at or before the timestamp.

Accepted formats (parsed in this order):

* Date only: `2026-05-01` (interpreted as `2026-05-01T00:00:00Z`)
* Date + time without timezone: `2026-05-01T12:34:56` (interpreted as UTC)
* Full RFC3339: `2026-05-01T12:34:56Z` or `2026-05-01T12:34:56-07:00`

#### `--pretty` | `-p`

* Type: `boolean`
* Optional

Pretty-print the JSON output.

Examples:

```bash
# Latest merged catalog snapshot
lunar cataloger get-json --pretty
# A global cataloger's most recent delta
lunar cataloger get-json --cataloger sbom --pretty
# A per-component cataloger's delta for one component, as of a date
lunar cataloger get-json --cataloger sbom --component github.com/my-org/my-repo --ts 2026-05-01 --pretty
```

### `lunar cataloger run`

* Form:

  ```bash
  lunar cataloger run [--cataloger <name>] [--component <name>] [--output-json]
  ```
* Requires: [`components.run`](/docs/authorization.md#components-run), plus [`hub.read`](/docs/authorization.md#hub-read) to poll the jobs it started

The `lunar cataloger run` command triggers cataloger execution in Lunar Hub. The CLI enqueues the matching catalogers, then polls every 10 seconds and prints `queued / success / error` counters until every job is in a terminal state. With no flags, it runs every cataloger in the current manifest. Use `--cataloger` and/or `--component` to narrow the scope.

#### `--cataloger <name>`

* Type: `string`
* Optional

Run only the named cataloger (exact match or dotted-plugin prefix like `myplugin`). If omitted, every cataloger in the manifest participates. When the named cataloger has only global hooks, combining it with `--component` is rejected. When the named cataloger has per-component hooks and `--component` is omitted, it fans out to every component in the manifest.

#### `--component <name>`

* Type: `string`
* Optional

Scope per-component hooks (`component-repo` / `component-cron`) to this single component. When omitted, those hooks fan out across every component in the manifest. Global hooks (`cron` / `repo`) are unaffected.

#### `--output-json`

* Type: `boolean`
* Optional

After the polling loop completes, fetch and print the merged catalog JSON. If no catalog row exists yet (fresh manifest with no completed catalogers), prints `(no catalog yet)` to stderr and exits with the same code as if `--output-json` were not set.

Failures: for any job that ends in the `error` bucket, the CLI prints a one-line summary including a deep link to the Grafana run-details dashboard (if Hub is configured with `HUB_GRAFANA_URL_BASE`). When no snippet\_run is found for a job (rare — worker crashed before recording the run), the link is replaced with `(no run record; check hub logs)`.

Example:

```bash
lunar cataloger run --cataloger sbom --component github.com/my-org/my-repo --output-json
```

### `lunar cataloger dev`

* Form:

  ```bash
  lunar cataloger dev [cataloger-name] \
    [--component <name> | --component-dir <path>] \
    [--config <repo>] \
    [--git-sha <sha>] \
    [--script <path>] [--script-lang <bash|python|node>] \
    [--use-system-runtime] [--no-cache] [--verbose] \
    [--secrets <k=v,k=v>] \
    [--output-json]
  ```
* Requires: [`secrets.read`](/docs/authorization.md#secrets-read), since the cataloger runs with the secrets it would get on the Hub. `--no-hub` runs without any Hub call.

{% hint style="warning" %}
Catalogers can be highly environment-dependent. Be mindful of "works on my machine" types of issues.
{% endhint %}

The `lunar cataloger dev` command runs catalogers locally on the user's machine without applying changes to the catalog. It mirrors `lunar collector dev` for catalogers: the cataloger snippet is fetched from the configured manifest, executed under the configured runtime (Python / Node / Bash, or Docker when the snippet has an image), and its output is printed. No data is written to Lunar Hub.

#### `[cataloger-name]`

* Type: `string`
* Optional

Cataloger name (or dotted plugin name like `myplugin.sync`) to execute. If omitted, every cataloger applicable to the resolved scope is run.

#### `--component <name>`

* Type: `string`
* Optional

Look the component up in the resolved manifest. Required when running a per-component cataloger. Mutually exclusive with `--component-dir`.

#### `--component-dir <path>`

* Type: `string`
* Optional

Treat the given local directory as the component checkout (no clone). Mutually exclusive with `--component` and `--config`.

#### `--config <repo>`

* Type: `string`
* Optional

Remote configuration repository to load the manifest from (e.g. `github.com/org/repo` or `github://org/repo@branch`). Mutually exclusive with `--component-dir`.

#### `--git-sha <sha>`

* Type: `string`
* Optional

Pin the component checkout to this SHA on the component's primary branch. Only meaningful for per-component catalogers. Defaults to the tip of the primary branch.

{% hint style="info" %}
Unlike `lunar collector dev`, there is no `--pr` flag — catalogers run on pushes to main, not on PR commits.
{% endhint %}

#### `--script <path>`

* Type: `string`
* Optional

Override the cataloger's `main` script with a local file path. Combine with `--script-lang` to control the runtime used.

#### `--script-lang <language>`

* Type: `string`
* Optional
* Default: `bash`

Language for `--script` (`bash` / `python` / `node`).

#### `--use-system-runtime`

* Type: `boolean`
* Optional

Use the host's system Python / Node / Bash instead of the bundled Lunar runtimes. Defaults to `true` on non-Linux-amd64 hosts.

#### `--no-cache`

* Type: `boolean`
* Optional

Delete cacheable temporary files before running. Use this to force a clean run when troubleshooting.

#### `--verbose`

* Type: `boolean`
* Optional

Stream the cataloger's engine stdout / stderr to the terminal as it runs.

#### `--secrets <k=v,k=v>`

* Type: `string`
* Optional

Supplemental secrets injected into the cataloger's environment, parsed as comma-separated `key=value` pairs.

#### `--output-json`

* Type: `boolean`
* Optional

Print the merged Catalog JSON the user would see if these catalogers ran in Hub (manifest base + cataloger deltas + manifest overrides), in the same shape as [Catalog JSON](/docs/catalog-json.md). Without this flag, each cataloger's raw emitted JSON is printed under a per-cataloger header.

Example:

```bash
lunar cataloger dev sbom --component github.com/my-org/my-repo --verbose
```

## Collector Commands

### `lunar collector run`

* Form:

  ```bash
  lunar collector run [--output-json] [--pr <pr-number>] \
    [--git-sha <git-sha>] [--only-code] [--only-cron] \
    [--collector <collector-name>] [--pr-max-age-days <days>] \
    [--all] [<component-name>]
  ```
* Requires: [`components.run`](/docs/authorization.md#components-run)

The `lunar collector run` command is used to rerun code and cron collectors for a given component. This command triggers execution in the cloud via Lunar Hub.

Collectors are dispatched asynchronously, so the command reports what it triggered rather than waiting for results. Read the outcome with [`lunar component get-json`](#lunar-component-get-json) once the collectors have run.

{% hint style="info" %}
**Naming a collector that isn't in the current manifest is an error.** Lunar Hub checks the names given to `--collector` before dispatching anything, so a typo fails the command instead of quietly running nothing.
{% endhint %}

#### `<component-name>`

* Type: `string`
* Optional

The name of the component to rerun collectors for. Omit it, and pass `--all`, to cover every component, across both code and cron collectors. The code leg also covers recent PR commits, bounded by `--pr-max-age-days`.

{% hint style="warning" %}
**Omitting the component name can trigger a large amount of work in Lunar Hub.** It reruns collectors across every component, which can produce a significant backlog and elevated load.

Naming a component is the only way to bound both legs. `--pr-max-age-days` bounds the code leg alone: the cron leg fans out over every open pull request of every component it matches, capped per component rather than by age.
{% endhint %}

#### `--all`

Rerun every component. Required when no component name is given; without it, nothing runs. Cannot be combined with a component name.

#### `--pr <pr-number>`

* Type: `integer`
* Optional

The PR number to rerun collectors for. If not specified, collectors will be run for the component's primary branch. Requires a component name, since the PR is resolved against that component's repository.

#### `--git-sha <git-sha>`

* Type: `string`
* Optional

The specific git SHA to rerun collectors for. On its own, the commit is rerun as seen on the primary branch. With `--pr`, it is rerun as that PR saw it, which is how an earlier commit of a PR, or one that only exists on a PR branch, is rerun; the commit must belong to that PR. Requires a component name, and the commit must be one Lunar Hub has already ingested for that component's repository.

#### `--only-code`

Run only code collectors.

#### `--only-cron`

Run only cron collectors.

#### `--collector <collector-name>`

* Type: `string`
* Optional
* Repeatable

Run only the specified collector. This flag can be repeated to run multiple specific collectors. A name matches a collector exactly, or matches every sub-collector of a plugin: `--collector trivy` selects `trivy.auto` and `trivy.rescan`.

#### `--pr-max-age-days <days>`

* Type: `integer`
* Optional
* Default: `5`

Ignore PR commits older than this maximum number of days. Only consulted for the fleet-wide **code** sweep, which covers recent PR commits as well as each repository's primary branch. Cron collectors ignore it, so it has no effect at all under `--only-cron`.

#### `--output-json`

Output the results in JSON format: one object per triggered run, carrying `component`, `collector`, `source` (`code` or `cron`), `repo_uri`, `commit_sha`, and `pr`. A run that Lunar Hub deduplicated onto an identical run already in flight is reported with `already_pending: true` rather than omitted, so an empty array means nothing matched.

Example:

```bash
# Rerun all collectors for all components, limiting PRs to 1 day old ones
lunar collector run --all --pr-max-age-days 1
# Rerun collectors for a component (primary branch)
lunar collector run github.com/my-org/my-repo
# Rerun collectors for a specific PR
lunar collector run --pr 123 github.com/my-org/my-repo
# Rerun collectors for a specific git SHA
lunar collector run --git-sha abc123 github.com/my-org/my-repo
# Run only code collectors
lunar collector run --only-code github.com/my-org/my-repo
# Run a cron collector now, without waiting for its next tick
lunar collector run --only-cron --collector pagerduty.oncall github.com/my-org/my-repo
# Run only a specific collector
lunar collector run --collector collector-name github.com/my-org/my-repo
```

### `lunar collector dev`

* Name Form:

  ```bash
  lunar collector dev [--pr <pr-number>] [--git-sha <git-sha>] \
    [--component <component-name> | --component-dir <path>] \
    [--fake-ci-cmd <bash-command>] \
    <collector-name>
  ```
* Script Form:

  ```bash
  lunar collector dev \
    [--pr <pr-number>] [--git-sha <git-sha>] \
    [--component <component-name> | --component-dir <path>] \
    [--fake-ci-cmd <bash-command>] \
    --script <path-to-collector-script>
  ```
* Requires: [`secrets.read`](/docs/authorization.md#secrets-read), since the collector runs with the secrets it would get on the Hub. `--no-hub` runs without any Hub call.

{% hint style="warning" %}
Collectors can be highly environment-dependent. Be mindful of "works on my machine" types of issues.
{% endhint %}

The `lunar collector dev` command is used to run a collector for a given component in development mode without applying changes. This command executes locally on the user's machine and outputs the resulting component JSON.

#### `<collector-name>`

* Type: `string`
* Required in Name Form

#### `--script <path-to-collector-script>`

* Type: `string`
* Required in Script Form

#### `--component <component-name>`

* Type: `string`

The name of the component to run collectors for. If not provided, falls back to the `LUNAR_COMPONENT_ID` environment variable. Mutually exclusive with `--component-dir`.

#### `--component-dir <path>`

* Type: `string`

Local directory containing the component to run collectors for. The directory must be a git repository. The component name is derived from the git remote URL. Mutually exclusive with `--component`.

#### `--pr <pr-number>`

* Type: `integer`
* Optional

The PR number to run collectors for. If not specified, collectors will be run for the component's primary branch.

{% hint style="warning" %}
`--pr` requires a GitHub connection and is not available for components on GitLab — the command exits with an error. Use `--git-sha`, or the component's branch, instead.
{% endhint %}

#### `--git-sha <git-sha>`

* Type: `string`
* Optional

The specific git SHA to run collectors for. If specified, this takes precedence over `--pr`.

#### `<collector-name>`

* Type: `string`
* Optional

The name of the collector to run. If not specified, all collectors will be run.

The path to a bash collector script file to run in development mode.

#### `--fake-ci-cmd <bash-command>`

* Type: `string`
* Optional

A command that the CI would have executed, that would cause lunar instrumentation to trigger an event for. This command is not actually executed, it is merely used to test collector triggering logic (e.g. would the collector trigger regex match the command line). This option is used for testing CI collectors locally without requiring an actual CI pipeline execution.

#### `--config <repo>`

* Type: `string`
* Optional

Remote config repository to use.

#### `--use-system-runtime`

* Type: `boolean`
* Optional

Use the system runtime instead of a containerized environment.

#### `--no-cache`

* Type: `boolean`
* Optional

Disable caching.

#### `--verbose`

* Type: `boolean`
* Optional

Enable verbose output.

#### `--merge`

* Type: `boolean`
* Optional

Merge the collected data into the existing component JSON.

#### `--secrets`

* Type: `boolean`
* Optional

Fetch and inject secrets into the collector execution environment.

#### Examples

```bash
# Run collectors in development mode for a component (primary branch)
lunar collector dev --component github.com/my-org/my-repo
# Run collectors in development mode for a specific PR
lunar collector dev --component github.com/my-org/my-repo --pr 123
# Run a specific collector
lunar collector dev --component github.com/my-org/my-repo collector-name
# Run a specific collector script file
lunar collector dev --component github.com/my-org/my-repo --script ./path/to/collector.sh
# Test a CI collector with a fake CI command
lunar collector dev --component github.com/my-org/my-repo --fake-ci-cmd "npm test" ci-collector-name
# Run collectors against a local directory
lunar collector dev --component-dir ./my-local-repo collector-name
# Run collectors against a local directory with a specific git SHA
lunar collector dev --component-dir ./my-local-repo --git-sha abc123 collector-name
```

#### Credentials

What credentials `lunar collector dev` needs depends on how you name the component:

* **`--component-dir <path>`** runs against a checkout you already have. It performs only local git operations (no clone), so it needs **no Hub and no GitHub or GitLab token**. This is the way to smoke-test a collector against a local component with no credentials — pair it with `--no-hub` to skip Hub entirely, and `--script` to run a single script without resolving the manifest.
* **`--component <name>`** resolves and clones the component through a [Git platform token](#lunar_github_token-lunar_gitlab_token). Without access, the command points you at `--component-dir`.

## Policy Commands

### `lunar policy ls`

{% hint style="info" %}
**Coming Soon** — This feature is not yet available.
{% endhint %}

* Form:

  ```bash
  lunar policy ls
  ```

The `lunar policy ls` command is used to list all policies.

### `lunar policy check ls`

{% hint style="info" %}
**Coming Soon** — This feature is not yet available.
{% endhint %}

* Form:

  ```bash
  lunar policy check ls
  ```

The `lunar policy check ls` command is used to list all checks.

### `lunar policy run`

{% hint style="info" %}
**Coming Soon** — This feature is not yet available.
{% endhint %}

* Form:

  ```bash
  lunar policy run [--output-json] [--pr <pr-number>] \
    [--git-sha <git-sha>] [--policy <policy-name>] \
    [--initiative <initiative-name>] <component-name>
  ```

The `lunar policy run` command is used to rerun all policies for a given component. This command triggers execution in the cloud via Lunar Hub.

#### `<component-name>`

* Type: `string`

The name of the component to rerun policies for.

#### `--pr <pr-number>`

* Type: `integer`
* Optional

The PR number to rerun policies for. If not specified, policies will be run for the component's primary branch.

#### `--git-sha <git-sha>`

* Type: `string`
* Optional

The specific git SHA to rerun policies for. If specified, this takes precedence over `--pr`.

#### `--policy <policy-name>`

* Type: `string`
* Optional
* Repeatable

Run only the specified policy. This flag can be repeated to run multiple specific policies.

#### `--initiative <initiative-name>`

* Type: `string`
* Optional
* Repeatable

Run only policies under the specified initiative. This flag can be repeated to run policies under multiple initiatives.

#### `--output-json`

Output the results in JSON format.

Example:

```bash
# Rerun policies for a component (primary branch)
lunar policy run github.com/my-org/my-repo
# Rerun policies for a specific PR
lunar policy run --pr 123 github.com/my-org/my-repo
# Rerun policies for a specific git SHA
lunar policy run --git-sha abc123 github.com/my-org/my-repo
# Run only a specific policy
lunar policy run --policy policy-name github.com/my-org/my-repo
# Run policies under a specific initiative
lunar policy run --initiative initiative-name github.com/my-org/my-repo
```

### `lunar policy dev`

* Name Form:

  ```bash
  lunar policy dev [--component <component-name>] [--component-json <path-to-json-or-stdin>] \
    [--pr <pr-number>] [--git-sha <git-sha>] \
    <policy-name>
  ```
* Script Form:

  ```bash
  lunar policy dev [--component <component-name>] [--component-json <path-to-json-or-stdin>] \
    [--pr <pr-number>] [--git-sha <git-sha>] \
    --script <path-to-policy-script>
  ```
* Requires: [`secrets.read`](/docs/authorization.md#secrets-read), and [`components.read`](/docs/authorization.md#components-read) with `--component`. `--no-hub` together with `--component-json` runs without any Hub call.

{% hint style="warning" %}
Policies can be highly environment-dependent. Be mindful of "works on my machine" types of issues.
{% endhint %}

The `lunar policy dev` command is used to run a policy against a component for local testing purposes. This command executes locally on the user's machine and outputs the check results in JSON format.

#### `<policy-name>`

* Type: `string`
* Required in Name Form

#### `--script <path-to-policy-script>`

* Type: `string`
* Required in Script Form

#### `--component <component-name>`

* Type: `string`

The name of the component to run the policy against. If not provided, falls back to the `LUNAR_COMPONENT_ID` environment variable.

#### `--component-json <path-to-json-or-stdin>`

* Type: `string`

The path to the component JSON file or `-` to read from stdin.

#### `--pr <pr-number>`

* Type: `integer`
* Optional

The PR number to run the policy against. If not specified, the policy will be run against the component's primary branch.

#### `--git-sha <git-sha>`

* Type: `string`
* Optional

The specific git SHA to run the policy against. If specified, this takes precedence over `--pr`.

#### `--with <args>`

* Type: `string`
* Optional

Arguments passed to the policy script.

#### `--script-lang <language>`

* Type: `string`
* Optional
* Default: `python`

The script programming language.

#### `--output <format>`

* Type: `string`
* Optional
* Values: `json`, `list`

Output format for the policy results.

#### `--config <repo>`

* Type: `string`
* Optional

Remote config repository to use.

#### `--use-system-runtime`

* Type: `boolean`
* Optional

Use the system runtime instead of a containerized environment.

#### `--no-cache`

* Type: `boolean`
* Optional

Disable caching.

#### `--verbose`

* Type: `boolean`
* Optional

Enable verbose output.

#### `--secrets`

* Type: `boolean`
* Optional

Fetch and inject secrets into the policy execution environment.

#### Example

```bash
# Run policy with component JSON from file
lunar policy dev --component-json path/to/component.json --script ./path/to/policy.py
# Run policy by specifying component directly
lunar policy dev --component github.com/my-org/my-repo --script ./path/to/policy.py
# Run policy with component JSON from stdin
lunar component get-json --git-sha ... github.com/my-org/my-repo | \
  lunar policy dev --component-json - --script ./path/to/policy.py
# Run specific policy from config
lunar policy dev --component github.com/my-org/my-repo my-policy
# Run policy for a specific PR
lunar policy dev --component github.com/my-org/my-repo --pr 123 --script ./path/to/policy.py
```

### `lunar policy ok-release`

* Form:

  ```bash
  lunar policy ok-release <component> <git_sha>
  lunar policy ok-release  # uses LUNAR_COMPONENT_ID and GITHUB_SHA
  ```
* Requires: [`components.read`](/docs/authorization.md#components-read)

The `lunar policy ok-release` command is used to check if a component at a specific git SHA passes its release policies.

Either both positional arguments must be provided, or neither. When no arguments are given, both `LUNAR_COMPONENT_ID` and `GITHUB_SHA` environment variables must be set — outside GitHub Actions, including in GitLab CI, pass the component and SHA as arguments instead.

The verdict block this prints can be replaced with your own wording through [`customization.ok_release_template`](/configuration/lunar-config/customization.md#ok_release_template). The exit code is unaffected.

#### `<component>`

* Type: `string`

The name of the component to check. Falls back to the `LUNAR_COMPONENT_ID` environment variable when no arguments are given.

#### `<git_sha>`

* Type: `string`

The git SHA to check. Falls back to the `GITHUB_SHA` environment variable when no arguments are given. Unlike `bypass-release` / `bypass-pr`, this gate does not read `CI_COMMIT_SHA`.

#### `--poll-interval <duration>`

* Type: `duration`
* Optional
* Default: `10s`

How often to poll for results.

#### `--timeout <duration>`

* Type: `duration`
* Optional
* Default: `10m`

Maximum time to wait for results before timing out.

#### `--workflow-id <id>`

* Type: `string`
* Optional

The CI workflow ID to associate with the check. Auto-detected from `GITHUB_RUN_ID`; it has no equivalent on GitLab, where Lunar does not record CI runs.

#### `--pr <pr-number>`

* Type: `integer`
* Optional

The PR number to check policies for. Auto-detected from `GITHUB_REF` when running in GitHub Actions — in any other CI, including GitLab, pass it explicitly. On GitLab, use the merge request's number.

#### `--fail-open[=<mode>]`

* Type: `string`
* Optional
* Default: unset (the gate fails when it cannot get a verdict)
* Values: `unreachable` (the default when the flag is given without a value), `timeout`, `both`

Exit `0` instead of erroring when Lunar Hub could not produce a verdict, so an outage never stalls a deploy or a merge. Must be written as `--fail-open=<mode>`; a bare `--fail-open` means `unreachable`.

Fail-open trips on *unavailability*, never on a *verdict* — a component that genuinely fails a blocking policy still exits `1` under every mode.

| Mode          | Unblocks on                                                                                                                                                                                        |
| ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `unreachable` | No verdict could be obtained because Lunar Hub was not serving: unreachable from the first call, contact lost mid-poll, rejecting under load, or never answering a single poll before `--timeout`. |
| `timeout`     | Lunar Hub was answering — reporting the evaluation as still running — but never finished it before `--timeout`.                                                                                    |
| `both`        | Either of the above.                                                                                                                                                                               |

`timeout` is deliberately opt-in. A timeout while Lunar Hub is up and evaluating is a verdict still in progress, not an outage, so unblocking on it can let a slow-but-genuinely-blocking evaluation through — especially with a short `--timeout`.

Authentication, authorization and unknown-component errors are **always fatal**, under every mode.

Every fail-open event is logged to standard error.

### `lunar policy ok-pr`

* Form:

  ```bash
  lunar policy ok-pr <component> <git_sha>
  lunar policy ok-pr  # uses LUNAR_COMPONENT_ID and GITHUB_SHA
  ```
* Requires: [`components.read`](/docs/authorization.md#components-read)

The `lunar policy ok-pr` command is used to check if a component at a specific git SHA passes its PR policies.

Either both positional arguments must be provided, or neither. When no arguments are given, both `LUNAR_COMPONENT_ID` and `GITHUB_SHA` environment variables must be set — outside GitHub Actions, including in GitLab CI, pass the component and SHA as arguments instead.

#### `<component>`

* Type: `string`

The name of the component to check. Falls back to the `LUNAR_COMPONENT_ID` environment variable when no arguments are given.

#### `<git_sha>`

* Type: `string`

The git SHA to check. Falls back to the `GITHUB_SHA` environment variable when no arguments are given. Unlike `bypass-release` / `bypass-pr`, this gate does not read `CI_COMMIT_SHA`.

#### `--poll-interval <duration>`

* Type: `duration`
* Optional
* Default: `10s`

How often to poll for results.

#### `--timeout <duration>`

* Type: `duration`
* Optional
* Default: `10m`

Maximum time to wait for results before timing out.

#### `--workflow-id <id>`

* Type: `string`
* Optional

The CI workflow ID to associate with the check. Auto-detected from `GITHUB_RUN_ID`; it has no equivalent on GitLab, where Lunar does not record CI runs.

#### `--pr <pr-number>`

* Type: `integer`
* Optional

The PR number to check policies for. Auto-detected from `GITHUB_REF` when running in GitHub Actions — in any other CI, including GitLab, pass it explicitly. On GitLab, use the merge request's number.

#### `--fail-open[=<mode>]`

* Type: `string`
* Optional
* Default: unset (the gate fails when it cannot get a verdict)
* Values: `unreachable` (the default when the flag is given without a value), `timeout`, `both`

Exit `0` instead of erroring when Lunar Hub could not produce a verdict, so an outage never stalls a deploy or a merge. Must be written as `--fail-open=<mode>`; a bare `--fail-open` means `unreachable`.

Fail-open trips on *unavailability*, never on a *verdict* — a component that genuinely fails a blocking policy still exits `1` under every mode.

| Mode          | Unblocks on                                                                                                                                                                                        |
| ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `unreachable` | No verdict could be obtained because Lunar Hub was not serving: unreachable from the first call, contact lost mid-poll, rejecting under load, or never answering a single poll before `--timeout`. |
| `timeout`     | Lunar Hub was answering — reporting the evaluation as still running — but never finished it before `--timeout`.                                                                                    |
| `both`        | Either of the above.                                                                                                                                                                               |

`timeout` is deliberately opt-in. A timeout while Lunar Hub is up and evaluating is a verdict still in progress, not an outage, so unblocking on it can let a slow-but-genuinely-blocking evaluation through — especially with a short `--timeout`.

Authentication, authorization and unknown-component errors are **always fatal**, under every mode. Otherwise an expired token, or one typo in a shared pipeline template, would silently turn every gate into a no-op that never goes red.

Every fail-open event is logged to standard error.

### Bypassing a block

`lunar policy ok-release` and `lunar policy ok-pr` exit `1` when a blocking policy fails. A *bypass* overrides that verdict for a bounded window, so a known failure can be shipped past without disabling the policy for everyone. A pull or merge request can also be bypassed from its own thread with a [`/lunar bypass` comment](/docs/pr-comments.md); that override is pinned to a commit rather than time-bound, but lands on the same ledger as the commands below.

| Command                       | Purpose                                       |
| ----------------------------- | --------------------------------------------- |
| `lunar policy bypass-release` | Override the release gate for a component     |
| `lunar policy bypass-pr`      | Override the PR/MR merge gate for a component |
| `lunar policy bypass-ls`      | List a component's bypasses                   |
| `lunar policy bypass-rm`      | Revoke a bypass early                         |

Three properties hold for every bypass **these commands create**. The expiry property does not hold for a comment-driven one, which is bound to a commit rather than a clock and carries a role the Git platform verified.

* **It always expires.** There is no way to create one that lasts forever. Omitting `--for` uses the configured [`bypass.max_duration`](/configuration/lunar-config.md#bypass), never infinity, because a bypass that never expires is a silently disabled policy.
* **It is authorized and audited.** A logged-in user needs [`bypasses.manage`](/docs/authorization.md#bypasses-manage), and the Hub records their verified login and role. A CI job holds it for its own repository and commit, and the Hub records the actor from the OIDC token its Git platform signed, without claiming a human role was checked. The service token grants the permission directly, so its caller-supplied actor is recorded as unverified.
* **It never goes silently green.** A gate cleared by a bypass still prints every check that was suppressed, who authorized it, and until when:

  ```
  🔓 sbom.no-critical-vulns bypassed by alice until 2026-08-17T09:00:00Z: waiting on upstream patch
  	* 2 critical vulnerabilities found
  ```

  A commit-bound bypass reads `while its commit stands` in place of `until <timestamp>`. A partially bypassed gate still fails: checks the bypass does not cover keep blocking.

Both the bypasses and the individual checks they suppressed are queryable over the SQL API, through the [`bypasses`](/sql-api/views/bypasses.md) and [`bypassed_checks`](/sql-api/views/bypassed-checks.md) views.

#### Scope

A bypass covers one component and one gate, optionally narrowed by SHA, PR number, and policy. **Any narrowing dimension left unset matches every value of it** — that is what makes a bypass component-wide.

* **The gate is not transitive.** A policy that blocks both PRs and releases fails in each gate separately and needs a bypass in each. Clearing a PR merge never silently clears a deploy.
* **`--policy` names either a whole plugin (`sbom`) or a single check (`sbom.no-critical-vulns`).** It forgives that policy's failures only. A pending check is not failing, so a `--policy` bypass never clears a stuck check; use `--sha` without `--policy` for that.
* **A component-wide bypass has to be narrowed on purpose.** With no `--sha`, `--pr` or `--policy` it masks every failing blocking check for the gate, including policies added to the configuration *after* it was created. Lunar Hub rejects that blast radius unless it is narrowed, or given a window chosen rather than inherited:

  ```
  a component-wide bypass must narrow its scope: set at least one of sha, pr, policy, or an explicit duration
  ```

In CI, when neither `--sha` nor `--pr` is given, the commit is inferred from `GITHUB_SHA` or `CI_COMMIT_SHA` and the bypass is scoped to it. A SHA-scoped bypass dies on the next push, which is a tighter bound than any clock. Outside CI there is no SHA to infer, so the bypass stays component-wide and the rule above applies.

When several active bypasses cover the same check, the first match wins. Since each one is a deliberate override in its own right, which one wins affects only the actor the output credits, not what is masked.

#### Durations

`--for`, and [`bypass.max_duration`](/configuration/lunar-config.md#bypass) that caps it, accept Go's duration grammar extended with `d` (24 hours) and `w` (7 days) units. The units may be mixed: `36h`, `3d`, `2w`, `1w2d`, `1d12h30m`.

A `--for` longer than `bypass.max_duration` is **rejected, not shortened**. Silently clamping it would let you believe a two-week waiver is in place when it expires in two days.

### `lunar policy bypass-release`

* Form:

  ```bash
  lunar policy bypass-release <component> --reason "<why>" [--for <duration>]
  lunar policy bypass-release --reason "<why>"  # uses LUNAR_COMPONENT_ID
  ```
* Requires: [`bypasses.manage`](/docs/authorization.md#bypasses-manage)

Records a time-bound override of a component's **release** block, so `lunar policy ok-release` passes for as long as it lasts.

Creating or revoking one also refreshes what Lunar reports for the component's default-branch head, re-posting the results already stored for it, so the [GitLab project badge](/install/git-platforms/gitlab.md#project-badges) catches up without waiting for a push: a fully bypassed component reads `release bypassed`. The default-branch commit status keeps the un-bypassed verdict, since it gates nothing.

{% code title="Waive a known finding for a week while the fix lands" %}

```bash
lunar policy bypass-release github.com/my-org/my-service \
  --policy sbom.no-critical-vulns \
  --reason "upstream patch expected in v2.4, tracked in ENG-1234" \
  --for 1w
```

{% endcode %}

The command prints the stored record, including the `id` needed to revoke it.

A CI job authenticated with its GitHub Actions or GitLab OIDC token may create a bypass only for a component in its own repository, and only for its own commit, which it must name. The default SHA inference supplies that bound for `bypass-release`, where no dimension is usually given; a request without a SHA is rejected, including a PR-only bypass, which would outlive the head it was granted for. So `bypass-pr` from a CI job takes both flags, since naming `--pr` turns the inference off:

```bash
lunar policy bypass-pr <component> --sha "$GITHUB_SHA" --pr "$PR_NUMBER" \
  --reason "<why>" --for 1h
```

Fork pull-request jobs and GitHub `pull_request_target` jobs cannot write bypasses. A protected ref is not required: the commit is what bounds the job, the same as for other CI writes. The exception is the [configuration repository's own pipeline](/docs/authorization.md#config-repo-administrators), which administers every component and so may bypass any of them, on a commit it names. That is how a central break-glass job runs on its OIDC identity instead of a stored Hub token.

#### `<component>`

* Type: `string`
* Optional

The component to override. Falls back to the `LUNAR_COMPONENT_ID` environment variable when no argument is given.

#### `--reason <text>`

* Type: `string`
* Required, unless [`bypass.require_reason`](/configuration/lunar-config.md#require_reason) is `false`

Why the block is being overridden. An unexplained override is not much of an audit trail, which is why this is required by default.

#### `--sha <git-sha>`

* Type: `string`
* Optional
* Default: `GITHUB_SHA` or `CI_COMMIT_SHA`, when neither `--sha` nor `--pr` is given

Limit the bypass to one commit. A CI OIDC job has to name a commit, and naming `--pr` turns the inference off, so from CI pass this alongside `--pr`.

#### `--pr <pr-number>`

* Type: `integer`
* Optional

Limit the bypass to one PR or MR number.

#### `--policy <selector>`

* Type: `string`
* Optional

Limit the bypass to one plugin (`sbom`) or one check (`sbom.no-critical-vulns`). Unset covers every blocking check for the gate.

#### `--for <duration>`

* Type: `duration`
* Optional
* Default: the configured [`bypass.max_duration`](/configuration/lunar-config.md#bypass)

How long the bypass lasts. Rejected if it exceeds the configured cap.

#### `--actor <name>`

* Type: `string`
* Optional
* Default: the authenticated login, the CI job's OIDC actor, or `GITLAB_USER_LOGIN`, `GITHUB_ACTOR`, or `USER` with the service token

Logged-in actors are verified; service-token actors are recorded as unverified. A CI job records the actor its Git platform signed, and rejects a `--actor` naming anyone else.

### `lunar policy bypass-pr`

* Form:

  ```bash
  lunar policy bypass-pr <component> --reason "<why>" [--for <duration>]
  lunar policy bypass-pr --reason "<why>"  # uses LUNAR_COMPONENT_ID
  ```
* Requires: [`bypasses.manage`](/docs/authorization.md#bypasses-manage)

Records a time-bound override of a component's **PR/MR merge** block, so `lunar policy ok-pr` passes for as long as it lasts.

The bypass also clears the Git platform's `Earthly Lunar` signal: the [GitHub check run](/install/git-platforms/github.md#blocking-merges) that branch protection can require, and on GitLab the [external status check](/install/git-platforms/gitlab.md#merge-gate) where the merge gate is live, or the commit status that **Pipelines must succeed** reads elsewhere. The checks it covers stop counting against the rollup, and the PR/MR comment lists each of them with who bypassed it and until when. With `--sha`, Lunar re-posts the signal for that commit as soon as the bypass is created or revoked. Without it, the signal updates on the next policy evaluation (a new commit, a collection), not the moment the bypass is created or expires.

Takes exactly the same arguments and flags as [`lunar policy bypass-release`](#lunar-policy-bypass-release); only the gate differs. A bypass on one gate has no effect on the other, so a policy that blocks both PRs and releases needs one of each.

From a [CI OIDC job](#lunar-policy-bypass-release) this takes both `--sha "$GITHUB_SHA"` and `--pr`, since such a job must name a commit and naming `--pr` turns the SHA inference off.

### `lunar policy bypass-ls`

* Form:

  ```bash
  lunar policy bypass-ls <component> [--active]
  lunar policy bypass-ls  # uses LUNAR_COMPONENT_ID
  ```
* Requires: [`components.read`](/docs/authorization.md#components-read)

Lists a component's bypasses, newest first. Expired and revoked records are included by default.

```
  id:        6f1c2f8e-6a2d-4d3f-8a45-2b0f9c7d1e34
  component: github.com/my-org/my-service
  gate:      release
  scope:     policy sbom.no-critical-vulns
  reason:    upstream patch expected in v2.4, tracked in ENG-1234
  actor:     alice (unverified)
  source:    cli
  created:   2026-08-10T09:00:00Z
  expires:   2026-08-17T09:00:00Z
  status:    active
```

`scope` reads `entire component, every blocking check` when nothing narrows the bypass. `source` is where the bypass came from: `cli`, `github`, `gitlab`, or `ui`.

`status` names why a bypass is or is not in effect, and matches what the Bypasses dashboard reports:

| Status                                                 | Meaning                                                                                               |
| ------------------------------------------------------ | ----------------------------------------------------------------------------------------------------- |
| `active`                                               | Still masks its checks.                                                                               |
| `revoked <when> by <who>`                              | Ended early with `bypass-rm`.                                                                         |
| `spent — PR #N was merged or closed`                   | A PR-gate bypass whose PR is finished; nothing is left to authorize.                                  |
| `superseded — a later push moved PR #N past sha <sha>` | A PR-gate bypass pinned to a commit the PR head has since moved past; the new push re-armed the gate. |
| `expired`                                              | Its `--for` window ran out.                                                                           |

The last two are the reason a commit-bound bypass (`expires: never`) can still be inactive: its bound is the commit and the PR, not the clock.

A comment-driven bypass reads differently on two lines, since the platform verified who asked and the commit is the bound:

```
  actor:     alice (verified: github:maintain)
  source:    github
  expires:   never — bound to its commit (a new push re-arms the gate)
```

The label after the actor says what the Hub checked when the bypass was created. The dashboards' Role column carries the same distinction:

| Label                                             | Role column        | Meaning                                                                                                                                                                                                                                                                         |
| ------------------------------------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `(verified: <role>)`                              | `<role>`           | A signed-in user or a Git platform comment. The Hub verified that the actor holds this role on the component.                                                                                                                                                                   |
| `(CI identity, authorized via OIDC)`              | `ci (oidc)`        | A CI job, authorized through the OIDC token its Git platform signed. The actor is who the token says triggered the job; no human role was checked, because the job's own repository and commit are what authorized it. This is the expected label for a bypass created from CI. |
| `(CI job of the configuration repository <repo>)` | `ci job of <repo>` | The Lunar configuration repository's own pipeline, which may bypass any component.                                                                                                                                                                                              |
| `(unverified)`                                    | `unverified`       | The Hub service token. The actor is whatever the caller supplied, and nothing checked it. Only these rows match the dashboards' `Unverified` status filter.                                                                                                                     |

#### `<component>`

* Type: `string`
* Optional

The component whose bypasses to list. Falls back to `LUNAR_COMPONENT_ID`.

#### `--active`

* Type: `boolean`
* Optional
* Default: `false`

Show only bypasses with `status: active` — not expired, not revoked, and for PR-gate bypasses, not superseded by a newer push and not on a merged or closed PR.

#### `--sha <git-sha>`

* Type: `string`
* Optional

Show only bypasses that cover this SHA.

#### `--pr <pr-number>`

* Type: `integer`
* Optional

Show only bypasses that cover this PR or MR number.

### `lunar policy bypass-rm`

* Form:

  ```bash
  lunar policy bypass-rm <bypass-id>
  ```
* Requires: [`bypasses.manage`](/docs/authorization.md#bypasses-manage)

Ends a bypass before it expires, taking the block that it was masking back into effect.

This is a revoke, not a delete: the record survives with the revoker and timestamp attached, and still appears in `bypass-ls` without `--active`. Revoking an id that does not exist, or one that is already revoked, is an error.

#### `<bypass-id>`

* Type: `string`
* Required

The id of the bypass to revoke, as printed by `bypass-ls` or by the command that created it.

#### `--actor <name>`

* Type: `string`
* Optional
* Default: verified login, or `GITLAB_USER_LOGIN`, `GITHUB_ACTOR`, or `USER` with the service token

Logged-in actors are verified; service-token actors are recorded as unverified.

## SDK Commands

### `lunar catalog`

Saves catalog-related information from within a cataloger.

* Requires: nothing — it writes to the cataloger's output, with no Hub call

For detailed documentation on the `lunar catalog` command and all its options, see the [Cataloger Bash SDK](/plugin-sdks/bash-sdk/cataloger.md) page.

### `lunar collect`

Collects SDLC metadata into a component's JSON. Pass `--component` and `--sha` to collect against a specific component and commit from anywhere.

* Requires: [`components.collect`](/docs/authorization.md#components-collect) when run out of band. Inside a collector it writes to the collector's output, with no Hub call.

For detailed documentation on the `lunar collect` command and all its options, see the [Collector Bash SDK](/plugin-sdks/bash-sdk/collector.md) page.

## SQL Commands

### `lunar sql connection-string`

* Form:

  ```bash
  lunar sql connection-string
  ```
* Requires: [`sql.connect`](/docs/authorization.md#sql-connect), or [`admin.read`](/docs/authorization.md#admin-read) with `--grafana`

Returns the shared read-only SQL user for the service token, a personal role for `lunar login`, and `PermissionDenied` for CI identity. See [SQL API credentials](/sql-api/sql-api.md#credentials).

Example:

```bash
# Get the connection string
lunar sql connection-string
# Connect using psql (interactive)
psql $(lunar sql connection-string)
# Export checks data as CSV
psql $(lunar sql connection-string) -c "COPY (
  SELECT *
  FROM checks
  WHERE component_id = 'github.com/my-org/my-repo'
    AND status = 'fail'
) TO STDOUT WITH CSV HEADER" > failed_checks.csv
# Export component data as JSON
psql $(lunar sql connection-string) -c "
  SELECT json_agg(row_to_json(c))
  FROM (
    SELECT *
    FROM components
    WHERE tags @> '{\"team\":\"ui\"}'
  ) c" > platform_components.json
# Make decisions based on check results
psql $(lunar sql connection-string) -t -c "
  SELECT COUNT(*)
  FROM checks c
  JOIN components comp ON c.component_id = comp.component_id
  WHERE comp.domain = 'payments'
    AND c.status = 'fail'
    AND c.enforcement IN ('block-pr-and-release', 'block-release')" | \
  grep -q "^0$" || \
  (echo "Release blocking checks are failing in payments domain!" && exit 1)
```

For more examples of the SQL API in action, see the [SQL API](/sql-api/sql-api.md) documentation.

### `lunar sql credentials ls`

* Form:

  ```bash
  lunar sql credentials ls [--output-json]
  ```
* Requires: [`admin.read`](/docs/authorization.md#admin-read)

Lists personal SQL credentials and their status. `--output-json` prints the same audit rows as JSON.

### `lunar sql credentials revoke`

* Form:

  ```bash
  lunar sql credentials revoke <login>
  ```
* Requires: [`hub.operate`](/docs/authorization.md#hub-operate)

Disables the person's SQL role and terminates its sessions. They can mint another credential while they retain `sql.connect`.

### `lunar secret set`

```bash
  lunar secret set <name> [value]
```

* Requires: [`secrets.write`](/docs/authorization.md#secrets-write)

Set a secret that will be available to collectors, policies, or catalogers as `LUNAR_SECRET_<NAME>`.

If `value` is omitted, it is read from stdin — recommended, as it keeps the secret out of your shell history. At a terminal Lunar prompts for the value and does not echo what you type; Enter submits it. Piped or redirected input is read to end-of-input instead, so it may span several lines.

Multi-line material such as a PEM key is best passed with `--file`, which the terminal prompt does not accept.

A trailing newline is dropped from whatever stdin or `--file` supplies, so `echo` and a file that ends in a newline both store the value without it.

Secrets are encrypted at rest using AES-256-GCM. The Hub must have `HUB_SECRETS_ENCRYPTION_KEY` configured.

#### Options

* `--scope <scope>` — Secret scope: `collector` (default), `policy`, or `cataloger`.
* `--file <path>` — Read the value from this file. Cannot be combined with `value`.

#### Examples

```bash
# Type the value in: Lunar prompts, and what you type is not shown
lunar secret set GH_TOKEN

# Pipe it in (scripts and CI)
echo "ghp_abc123" | lunar secret set GH_TOKEN

# Read it from a file — the route for multi-line secrets
lunar secret set --scope cataloger GITLAB_APP_KEY --file ./key.pem

# Pass it as an argument (it lands in your shell history)
lunar secret set GH_TOKEN ghp_abc123

# Set a policy secret
lunar secret set --scope policy JIRA_API_KEY my-api-key
```

### `lunar secret delete`

```bash
  lunar secret delete <name>
```

* Requires: [`secrets.write`](/docs/authorization.md#secrets-write)

Delete a previously configured secret.

#### Options

* `--scope <scope>` — Secret scope: `collector` (default), `policy`, or `cataloger`.

#### Examples

```bash
lunar secret delete GH_TOKEN
lunar secret delete --scope policy JIRA_API_KEY
```

### `lunar secret list`

```bash
  lunar secret list
```

* Requires: [`secrets.read`](/docs/authorization.md#secrets-read)

List the names of configured secrets for a given scope. Values are never displayed.

#### Options

* `--scope <scope>` — Secret scope: `collector` (default), `policy`, or `cataloger`.

#### Examples

```bash
# List collector secrets
lunar secret list

# List policy secrets
lunar secret list --scope policy
```

## GitHub App Commands

Collectors and catalogers that call the GitHub API can mint a short-lived installation token from a GitHub App instead of reading a personal access token from a secret. The Hub holds the App's private key, encrypted in the same store as secrets, and mints the tokens itself, so a script only ever sees a token that expires within the hour. Scripts can call `lunar github-app get-token` once their image ships a Lunar CLI release that includes it.

To set one up, create a GitHub App with the read permissions your collectors and catalogers need and its webhook off, generate a private key, install the App on your organizations, then add it with [`lunar github-app add`](#lunar-github-app-add). Lunar never creates the App on your behalf, and its key stays in your Hub. Installing the App on more organizations later needs no change in Lunar, but a private App installs only on the account that owns it, so either make it public or add one App per organization under the same name.

Lunar treats these Apps as secrets. Adding or removing one takes [`secrets.write`](/docs/authorization.md#secrets-write), and listing them or minting a token takes [`secrets.read`](/docs/authorization.md#secrets-read), which collectors and catalogers hold through the Hub's service token.

**Give them their own App rather than the Hub's.** The Hub's [own GitHub App](/install/git-platforms/github.md) is Lunar's, and lending it to collectors and catalogers costs more than creating a second one:

* **Changing the Hub's App takes a deploy.** Its key is a file the Hub reads at boot. `lunar github-app add` registers an App at runtime, including your own for an off-the-shelf collector that needs different permissions.
* **The Hub's App carries Lunar's write access.** It writes checks, PR comments and webhooks, and the tokens the Hub mints from it can't be narrowed. Giving collectors a permission such as `administration` would mean widening Lunar's own App on every organization, and each organization owner would have to approve the change.
* **Their calls would spend the Hub's rate limit.** GitHub limits each App installation separately, so calls through a separate App leave the Hub's budget for webhooks and checks.

### `lunar github-app add`

* Form:

  ```bash
  lunar github-app add <name> --app-id <id> --private-key-file <path> [--host <host>] [--base-url <url>]
  ```
* Requires: [`secrets.write`](/docs/authorization.md#secrets-write)

Adds a GitHub App under `<name>`, the name plugins mint tokens by. The Hub proves the key with GitHub before storing it and prints the accounts the App is installed on. No command returns the key once it is stored, and `lunar secret` can't list or change it. Adding an App that `<name>` already holds on the same host replaces its key, which is how you rotate one.

One name can hold several Apps: one on github.com and one on a GitHub Enterprise Server host, or one per organization. [`lunar github-app get-token`](#lunar-github-app-get-token) picks the one installed on the owner it is asked about.

The Hub must have `HUB_SECRETS_ENCRYPTION_KEY` configured.

#### Options

* `--app-id <id>` — The App's ID, from its settings page. Required.
* `--private-key-file <path>` — The `.pem` private key GitHub generated for the App. `-` reads it from stdin, so the key need not be written to disk. Required.
* `--host <host>` — The GitHub host the App lives on. Default `github.com`.
* `--base-url <url>` — The REST API URL, when it is neither `https://api.github.com` nor `https://<host>/api/v3`.

#### Examples

```bash
lunar github-app add lunar-lib --app-id 123456 --private-key-file ~/Downloads/acme-lunar.private-key.pem
```

### `lunar github-app list`

* Form:

  ```bash
  lunar github-app list [--output-json]
  ```
* Requires: [`secrets.read`](/docs/authorization.md#secrets-read)

Lists the Apps added to the Hub: name, host, App ID, slug, and who stored the current key and when. Keys are never shown.

### `lunar github-app remove`

* Form:

  ```bash
  lunar github-app remove <name> [--app-id <id>] [--host <host>]
  ```
* Requires: [`secrets.write`](/docs/authorization.md#secrets-write)

Removes the Apps stored under `<name>`, with their keys. `--app-id` and `--host` narrow the removal to one App. Tokens already minted stay valid until they expire.

### `lunar github-app get-token`

* Form:

  ```bash
  lunar github-app get-token <name> [--owner <owner>] [--host <host>] [--repo <repo>]... [--permission <name>=<level>]... [--output-json]
  ```
* Requires: [`secrets.read`](/docs/authorization.md#secrets-read). Collectors, policies and catalogers run with the Hub's service token, which holds it.

Prints an installation token minted from the App named `<name>`, for its installation on `--owner`. Inside a collector, policy or cataloger, `--owner` and `--host` default to the component's, taken from `LUNAR_COMPONENT_ID`; anywhere else `--owner` is required.

`--repo` narrows the token to some of the owner's repositories, and `--permission` to some of the App's permissions at `read` or `write`. Without them the token can do everything the installation was granted. A permission name GitHub does not define is refused rather than silently dropped.

Tokens last an hour. The Hub mints one per installation and scope and hands the same token to every caller while it has at least 30 minutes left, so a script that runs longer than that asks again.

#### Examples

```bash
# In a collector: a token for the component's organization
export GH_TOKEN="$(lunar github-app get-token lunar-lib)"

# In a cataloger, for one organization of several
lunar github-app get-token lunar-lib --owner acme

# Only what the script needs
lunar github-app get-token lunar-lib --repo api --permission contents=read --permission metadata=read

# With its expiry and the permissions GitHub granted
lunar github-app get-token lunar-lib --output-json
```

## Webhook Commands

These manage [outbound webhooks](/configuration/hub-configuration/webhooks.md): HTTP endpoints the Hub notifies when an event happens.

### `lunar webhooks events`

* Form:

  ```bash
  lunar webhooks events [--output-json]
  ```
* Requires: [`hub.operate`](/docs/authorization.md#hub-operate)

Lists the events a subscription can name, each with the fields of its payload as paths a filter can use.

### `lunar webhooks subscribe`

* Form:

  ```bash
  lunar webhooks subscribe <url> --event <event> [--event <event>...] [--description <text>] [--filter <jsonpath>] [--output-json]
  ```
* Requires: [`hub.operate`](/docs/authorization.md#hub-operate)

Registers `url` for the events, plus `ping`, and sends it a ping. Prints the signing secret, which is shown only this once. `--filter` limits delivery to the events a SQL/JSON path predicate accepts; see [Filters](/configuration/hub-configuration/webhooks.md#filters).

```bash
lunar webhooks subscribe https://hooks.example.com/lunar --event bypass.exercised \
    --filter '$.data.gate == "release"'
```

### `lunar webhooks filter-test`

* Form:

  ```bash
  lunar webhooks filter-test <jsonpath> --event <event> [--limit <n>] [--output-json]
  ```
* Requires: [`hub.operate`](/docs/authorization.md#hub-operate)

Evaluates a filter against the most recent deliveries of an event (20 by default, at most 100) and says which it would have delivered. Sends nothing, and exits non-zero on a filter that does not parse.

### `lunar webhooks ls`

* Form:

  ```bash
  lunar webhooks ls [--output-json]
  ```
* Requires: [`hub.operate`](/docs/authorization.md#hub-operate)

Lists the subscriptions with their full URL, events, filter, state and last delivery. Never their secrets.

### `lunar webhooks update`

* Form:

  ```bash
  lunar webhooks update <subscription-id> [--add-event <event>...] [--remove-event <event>...] [--event <event>...] [--filter <jsonpath>] [--description <text>] [--output-json]
  ```
* Requires: [`hub.operate`](/docs/authorization.md#hub-operate)

Changes a subscription's events, filter or description in place, keeping its ID, URL and secret. What is not given is left as it is. `--add-event` and `--remove-event` change the current set, `--event` replaces it; every subscription keeps `ping`, and at least one event must remain. `--filter ''` drops the filter. A delivery already queued for an event removed here still goes out. Prints the subscription.

```bash
lunar webhooks update 7b1f9c2e-8a4d-4c1e-9f0a-2d6b3e5c7a91 --add-event script_run.error
lunar webhooks update 7b1f9c2e-8a4d-4c1e-9f0a-2d6b3e5c7a91 --filter '$.data.gate == "release"'
```

### `lunar webhooks deliveries`

* Form:

  ```bash
  lunar webhooks deliveries [--subscription <subscription-id>] [--event <event>] [--status <status>] [--limit <n>] [--output-json]
  ```
* Requires: [`hub.operate`](/docs/authorization.md#hub-operate)

Lists deliveries newest first (50 by default, at most 500), each with its subscription, event, status, number of attempts, creation time and how its last attempt went. `--subscription`, `--event` and `--status` (`queued`, `delivered`, `failed`, `filtered` or `paused`) narrow the list, and an unsubscribed subscription's deliveries are still listed. `--output-json` adds each payload. What the receiver answered is only shown by `lunar webhooks delivery`.

```bash
lunar webhooks deliveries --event bypass.exercised --status failed
```

### `lunar webhooks delivery`

* Form:

  ```bash
  lunar webhooks delivery <delivery-id> [--output-json]
  ```
* Requires: [`hub.operate`](/docs/authorization.md#hub-operate)

Shows one delivery: the payload sent and every attempt, with its HTTP status, duration, error and the receiver's answer.

### `lunar webhooks redeliver`

* Form:

  ```bash
  lunar webhooks redeliver <delivery-id>
  ```
* Requires: [`hub.operate`](/docs/authorization.md#hub-operate)

Sends a delivery again with the same payload. A paused subscription has to be resumed first, and a delivery that still has an attempt queued, waiting on a retry or running is refused, since that attempt sends it.

### `lunar webhooks ping`

* Form:

  ```bash
  lunar webhooks ping <subscription-id>
  ```
* Requires: [`hub.operate`](/docs/authorization.md#hub-operate)

Sends the subscription a ping now, even while it is paused.

### `lunar webhooks resume`

* Form:

  ```bash
  lunar webhooks resume <subscription-id>
  ```
* Requires: [`hub.operate`](/docs/authorization.md#hub-operate)

Re-enables a subscription paused after consecutive failed deliveries. Events recorded as `paused` meanwhile are not sent; redeliver the ones you need.

### `lunar webhooks unsubscribe`

* Form:

  ```bash
  lunar webhooks unsubscribe <subscription-id>
  ```
* Requires: [`hub.operate`](/docs/authorization.md#hub-operate)

Stops the subscription. Its deliveries are kept, and pending ones are not sent.

## Queue Commands

Collectors, policies and catalogers are dispatched through a queue and executed in the background. These commands let you inspect that queue and empty it, which is how you recover when a misconfiguration has filled it with work that can only fail and retry.

Jobs that are already running are never deleted. They finish on their own.

### `lunar queue status`

```bash
  lunar queue status
```

* Requires: [`hub.read`](/docs/authorization.md#hub-read)

Show how many snippet executions are queued, broken down by type.

```
TYPE         CLEARABLE   RETRYING   RUNNING   DISCARDED
collectors        1204        892         6           0
policies            12          0         2           3
catalogers           0          0         0           0
```

* **CLEARABLE** — jobs `lunar queue clear` would delete: waiting to start, plus backing off after a failure.
* **RETRYING** — the subset of clearable jobs that have already failed at least once. A large number here is the signature of a failure loop.
* **RUNNING** — currently executing. Never deleted by a clear.
* **DISCARDED** — out of retries. These consume no capacity and are cleaned up automatically, so a clear leaves them alone.

### `lunar queue clear`

```bash
  lunar queue clear <collectors|policies|catalogers|all>
```

* Requires: [`hub.read`](/docs/authorization.md#hub-read) to preview, and [`hub.operate`](/docs/authorization.md#hub-operate) to delete with `--yes`

Delete queued snippet executions of the given type.

Run it without `--yes` first: it prints exactly what would be removed and stops without deleting anything. Add `--yes` to go through with it. There is no interactive prompt, so the command behaves identically in a terminal, through a pipe, and in CI.

{% hint style="warning" %}
Cleared jobs are gone — they are not rescheduled. Collectors and policies re-run on the next push to a component, but any result that a cleared job would have produced for an in-flight commit will be missing until then.
{% endhint %}

#### Options

* `--yes` — Actually delete. Without it the command previews and exits.

#### Examples

```bash
# See what's queued
lunar queue status

# Preview what clearing collectors would remove — deletes nothing
lunar queue clear collectors

# Go through with it
lunar queue clear collectors --yes
```

If the queue still shows running jobs after a clear, that is expected — a clear never interrupts work in progress.

## Utility Commands

### `lunar clear-cache`

* Form:

  ```bash
  lunar clear-cache
  ```
* Requires: nothing — runs locally, with no Hub call

The `lunar clear-cache` command deletes Git and installation file caches used by Lunar. This can be useful to resolve issues caused by stale cached data.

### `lunar version`

* Form:

  ```bash
  lunar version
  ```
* Requires: nothing — runs locally, with no Hub call

The `lunar version` command prints the Lunar CLI version and commit SHA.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs-lunar.earthly.dev/docs/lunar-cli.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
