For the complete documentation index, see llms.txt. This page is also available as Markdown.

CI Job Identity

Authenticate CI jobs to Lunar Hub with the OIDC token their CI platform issues, on GitHub Actions and GitLab CI.

Put the OIDC token issued by the job's Git platform in LUNAR_HUB_TOKEN to authenticate without storing the Hub service token in each repository. See Authorization for the permission model. GitHub Actions and GitLab CI are supported; jobs on other CI platforms keep the service token.

jobs:
  test:
    runs-on: ubuntu-latest
    permissions:
      id-token: write      # lets the job request its OIDC token
      contents: read
    steps:
      - uses: actions/checkout@v5
      - run: |
          export LUNAR_HUB_TOKEN="$(curl -fsS \
            -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
            "$ACTIONS_ID_TOKEN_REQUEST_URL&audience=hub.example.com" | jq -r .value)"
          lunar ci-tracer run -- make test
        env:
          LUNAR_CI_TYPE: github
          LUNAR_HUB_HOST: hub.example.com

Set audience= to the bare Hub host in HUB_AUTH_AUDIENCE. When using the earthly/lunar-ci-tracer action, request the OIDC token first and pass it in env:.

job:
  variables:
    LUNAR_HUB_HOST: hub.example.com
  id_tokens:
    LUNAR_HUB_TOKEN:
      aud: hub.example.com
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
  script:
    - >-
      lunar collect
      --component "$CI_SERVER_HOST/$CI_PROJECT_PATH"
      --sha "$CI_COMMIT_SHA"
      --pr "$CI_MERGE_REQUEST_IID"
      .build.image_digest "$DIGEST"
    - >-
      lunar policy ok-pr
      --pr "$CI_MERGE_REQUEST_IID"
      "$CI_SERVER_HOST/$CI_PROJECT_PATH"
      "$CI_COMMIT_SHA"

GitLab writes the token into LUNAR_HUB_TOKEN. The CI Tracer does not yet run on GitLab, but CLI commands such as lunar collect can use it.

What the job may do

The job can read and collect for its repository and commit or pull request, and create a bypass for that same commit, which it must name with --sha. It cannot target other repositories or commits, or use operator-only permissions. Once configuration is installed, only the protected branch it was pulled from can pull it. During first bootstrap, any protected branch of the repository pinned by HUB_LUNAR_CONFIG_REPO may install it.

The configuration repository's own job, on that protected branch, is the exception to "other repositories or commits": it may read and collect for any component, and run collectors and catalogers anywhere. It still cannot read secrets or use any other operator-only permission. An out-of-band collect it makes for another component is recorded as that component's, on the component's tracked branch, with no ref of its own. See Configuration repository administrators.

Protected and unprotected refs

A commit can be on several refs at once, so a job on an unprotected branch can write for a commit the default branch also points at. The token says whether the job's ref is protected, and the Hub leaves records written from an unprotected ref out of the Component JSON it builds for a commit on the default branch. Those records still count for the branch or pull request they were written from, and records written with the service token or by a person are never excluded.

Scheduled (cron) collections from an unprotected ref are excluded everywhere rather than only on the default branch, because a cron record applies to the whole component rather than to one commit.

Pull requests from forked repositories

A CI job cannot write data for a pull or merge request whose source is a forked repository, even when its Git platform gives the job the original repository's identity. The Hub refuses the write with PermissionDenied; reads are unaffected. That covers GitHub private repositories with Send write tokens to workflows from pull requests enabled, and GitLab fork merge requests whose pipeline runs in the parent project. Every write from a GitHub pull_request_target job is refused too, since that event always carries the original repository's identity and its token names no pull request — use pull_request for jobs that collect.

So a fork pull request gets no data from CI collectors. A policy that reads it reports no data, and if the check is required to merge, the request waits for a bypass. Hub-side collectors, which run from the webhook rather than in the job, still collect for it.

Limits

  • Catalog the repository before switching its jobs. A token for an uncataloged repository authenticates, but Hub-wide tracer requests return PermissionDenied and component reads return NotFound.

  • A Hub session lasts HUB_AUTH_CI_SESSION_TTL (default 6h) and the tracer, started at the top of the job, does not renew it, so a traced job longer than that loses Hub access for its tail. Operators with such jobs raise the TTL. Standalone lunar collect steps are unaffected: each invocation exchanges the token in LUNAR_HUB_TOKEN again, and on GitHub Actions a step can request a fresh OIDC token at any time.

Last updated