> 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/authorization.md).

# Authorization

How Lunar Hub identifies callers and decides which operations each credential may perform.

Most Lunar Hub calls require authentication. Installations use a shared service token, CI jobs use short-lived OIDC tokens from their Git platform, and people sign in through Git platform OAuth. A small set of sign-in setup calls requires no credential.

| Caller       | Credential                                            | Authority                           |
| ------------ | ----------------------------------------------------- | ----------------------------------- |
| Installation | Hub service token in `LUNAR_HUB_TOKEN`                | Every permission                    |
| CI job       | OIDC token from its Git platform in `LUNAR_HUB_TOKEN` | Its repository and commit           |
| Person       | Git platform OAuth through `lunar login`              | Their role on the target repository |

Every installation has a service token. Depending on its configuration, CI jobs and people can also authenticate with their own identities.

| Credential        | Self-hosted                                                                                               | Lunar Dedicated                                                                                           |
| ----------------- | --------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| Service token     | [The chart generates it](/install/lunar-hub/self-hosted/install-walkthrough.md#hub-service-token)         | [Earthly generates it](/install/lunar-hub/dedicated/setup.md#authentication)                              |
| CI job identity   | [Two settings and a key](/install/lunar-hub/self-hosted/install-walkthrough.md#ci-job-identity)           | [Ask Earthly to enable it](/install/lunar-hub/dedicated/setup.md#authentication)                          |
| User logins       | [Register an app, then configure it](/install/lunar-hub/self-hosted/install-walkthrough.md#user-logins)   | [Register an app, send the identifiers](/install/lunar-hub/dedicated/setup.md#step-5-set-up-user-sign-in) |
| Dashboard sign-in | [`grafana.sso.enabled`](/install/lunar-hub/self-hosted/install-walkthrough.md#signing-in-through-the-hub) | [Ask Earthly to enable it](/install/lunar-hub/dedicated/setup.md#dashboard-sign-in)                       |

## Permissions

Every Hub operation is assigned a permission, such as `components.read` or `secrets.write`. The table shows the built-in defaults; run `lunar hub permissions` to inspect the effective rules.

| Permission           | Scope               | Access  | GitHub role | GitLab role  | CI job                                                |
| -------------------- | ------------------- | ------- | ----------- | ------------ | ----------------------------------------------------- |
| `components.read`    | `COMPONENT`         | `READ`  | `read`      | `reporter`   | own repo; fleet-wide: cataloged repo or config repo   |
| `components.collect` | `COMPONENT`         | `WRITE` | `write`     | `developer`  | own commit                                            |
| `components.run`     | `COMPONENT`         | `WRITE` | `write`     | `developer`  | own commit; fleet-wide: config repo, protected branch |
| `bypasses.manage`    | `COMPONENT`         | `WRITE` | `maintain`  | `maintainer` | own commit                                            |
| `hub.read`           | `LUNAR_CONFIG_REPO` | `READ`  | `read`      | `reporter`   | cataloged repo or config repo                         |
| `hub.operate`        | `LUNAR_CONFIG_REPO` | `WRITE` | `maintain`  | `maintainer` | no                                                    |
| `hub.pull`           | `LUNAR_CONFIG_REPO` | `WRITE` | `maintain`  | `maintainer` | config repo, protected branch                         |
| `secrets.read`       | `LUNAR_CONFIG_REPO` | `READ`  | `maintain`  | `maintainer` | no                                                    |
| `secrets.write`      | `LUNAR_CONFIG_REPO` | `WRITE` | `maintain`  | `maintainer` | no                                                    |
| `admin.read`         | `LUNAR_CONFIG_REPO` | `READ`  | `maintain`  | `maintainer` | no                                                    |
| `sql.connect`        | `LUNAR_CONFIG_REPO` | `READ`  | `maintain`  | `maintainer` | no                                                    |

The Hub evaluates each permission according to the caller:

* **Service token:** always allowed.
* **CI job identity:** Git platform roles do not apply. The `CI job` column restricts the call to the job's repository, commit, or configuration repository. For `WRITE` operations scoped to `LUNAR_CONFIG_REPO`, the job must run on the branch the configuration was pulled from, and that branch must be marked protected. It does not need to be the repository's default branch, or be named `main`.
* **Personal sign-in:** the Hub checks the person's role on the repository selected by `Scope`. `COMPONENT` uses the target component's repository; `LUNAR_CONFIG_REPO` uses the configuration repository. Only a granted role counts: the read anyone has on a public repository does not, so a public configuration repository opens nothing to people outside it. Role requirements can be overridden under [`authorization`](/configuration/lunar-config.md#authorization), but cannot go below the `READ` or `WRITE` access floor.

### Configuration repository administrators <a href="#config-repo-administrators" id="config-repo-administrators"></a>

Whoever can change `lunar-config.yml` already decides what the Hub enforces and knows, so the table's target checks do not bind them:

* A person with `maintain` (GitHub) or `Maintainer` (GitLab) or above on the configuration repository holds every permission in the table, on every component, unless [`authorization`](/configuration/lunar-config.md#authorization) raised that permission above `maintain` / `Maintainer`. A raise still binds on the configuration repository: `secrets.read: { github_role: admin }` requires `admin` there, which a Maintainer cannot merge past branch protection to undo. A bypass they create on a component they hold no role on records `verified_role` as their role on the configuration repository, suffixed with it — `github:maintain@github.com/acme/lunar-config` — so an audit can tell it apart from a role verified on the component.
* The configuration repository's own CI job, running on the protected branch the configuration was pulled from, may make any call the `CI job` column opens to CI jobs, on any component or commit. A role raise does not apply: CI jobs have no git platform role. Calls marked `no` stay refused: a workflow on that branch is not the administrator, and any step in it holds the job's token.

The Hub checks this only after the ordinary rule refuses, so nothing that passes today changes, and a refusal reads exactly as before. `internal` calls and `lunar hub pull` are excluded: pulling the configuration from a different repository still requires `hub.pull` on both the current and the new repository. `lunar hub permissions` prints a note under its table when the connected Hub applies this.

Protect the branch the configuration is pulled from so that only reviewed changes reach it. On a Hub pulling `github://acme/lunar-config@release`, that is `release`, not the repository's default branch. Without that, anyone who can push to the branch administers Lunar.

### `components.read` <a href="#components-read" id="components-read"></a>

Reads about a component: its status, Component JSON, catalog entry, and bypasses.

### `components.collect` <a href="#components-collect" id="components-collect"></a>

Records collection data against a commit.

### `components.run` <a href="#components-run" id="components-run"></a>

Runs collectors or catalogers.

### `bypasses.manage` <a href="#bypasses-manage" id="bypasses-manage"></a>

Creates and revokes bypasses, from the CLI and from a [`/lunar bypass` comment](/docs/pr-comments.md#authorization) alike. The comment path checks the commenter's role on the pull or merge request's own repository against this permission's role, so an [`authorization`](/configuration/lunar-config.md#authorization) override moves both surfaces at once; the configuration repository administrator rule below applies to the CLI only.

A CI job may create one only for a component in its own repository and only for its own commit, which it must name, so the next push retires it. Revoking is scoped to the job's repository, since it carries no commit and only removes authority. The [configuration repository's own job](#config-repo-administrators) is not held to either bound, though it must still name the commit. Fork pull-request jobs and GitHub `pull_request_target` jobs are refused, as for any other `WRITE`.

### `hub.read` <a href="#hub-read" id="hub-read"></a>

Reads Hub-wide runtime data. It is also what admits a person to the dashboards when they [sign in through the Hub](/install/lunar-hub/self-hosted/install-walkthrough.md#signing-in-through-the-hub): whoever holds it gets in, as a Grafana Viewer, and whoever loses it is signed out within 15 minutes. Raising its role under [`authorization`](/configuration/lunar-config.md#authorization) raises both the dashboards and the read-only CLI calls together.

### `hub.operate` <a href="#hub-operate" id="hub-operate"></a>

Changes Hub-wide runtime state.

### `hub.pull` <a href="#hub-pull" id="hub-pull"></a>

Installs Lunar configuration, and [cancels a bad configuration version](/docs/guides/cancel-a-bad-config.md) with `lunar hub pull --cancel`.

### `secrets.read` <a href="#secrets-read" id="secrets-read"></a>

Reads secret names and values.

### `secrets.write` <a href="#secrets-write" id="secrets-write"></a>

Sets and deletes secrets.

### `admin.read` <a href="#admin-read" id="admin-read"></a>

Reads operator-only Hub data.

### `sql.connect` <a href="#sql-connect" id="sql-connect"></a>

Obtains a SQL API credential.

### The three fixed permissions

These are not configurable:

* `public` — no credential required
* `authenticated` — any recognized credential
* `internal` — service token only

## When a call is refused

| Code                 | What it means                                                                                                                        |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `Unauthenticated`    | Missing or invalid credential                                                                                                        |
| `PermissionDenied`   | Recognized caller without the required permission                                                                                    |
| `Unavailable`        | Git platform verification failed temporarily                                                                                         |
| `ResourceExhausted`  | Too many failed authentication attempts                                                                                              |
| `FailedPrecondition` | The operation needs Hub state or configuration that is unavailable; token exchange uses this when the session signing key is missing |


---

# 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/authorization.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.
