Hamburger Cross Icon
Backstage Cataloger - Lunar Cataloger

Backstage Cataloger

Cataloger Experimental Service Catalog

Sync components and domains from a Backstage software catalog into Lunar. Maps Backstage entities to Lunar catalog with owner, domain, and tags, using the Backstage REST API.

Add backstage to your lunar-config.yml:
uses: github://earthly/lunar-lib/catalogers/backstage@v1.0.5

What This Integration Syncs

This integration includes 1 cataloger that sync data from your systems.

Cataloger cron

sync

Fetches entities from the Backstage catalog REST API and writes them to the Lunar catalog. Component entities populate .components with owner, domain, and tags; Domain entities populate .domains with description and owner. Components are keyed by a configurable annotation (defaults to github.com/project-slug) so they line up with components discovered by the github-org cataloger or by repo-based collectors.

Schedule: 0 2 * * *
service catalog backstage components domains ownership sync
Book a demo

How Catalogers Fit into Lunar

Lunar catalogers sync component metadata into your Lunar catalog from external systems or source code. They can run on a schedule or be triggered by code changes to keep your service registry up-to-date.

By automatically discovering components from GitHub organizations, service registries, or by detecting technology usage in source code, catalogers ensure your guardrails apply to all relevant services without manual configuration.

Learn How Lunar Works
1
Catalogers Sync Context This Integration
Sync component metadata from service catalogs, ownership systems, and external APIs
2
Guardrails Engine
Once cataloged, components are automatically analyzed by collectors and evaluated against your guardrails

Example Catalog Entry

This cataloger syncs component metadata into your Lunar catalog. Here's an example of a catalog entry it creates:

{ } catalog entry Catalog JSON
{
  "components": {
    "github.com/acme/payment-api": {
      "owner": "group:default/team-payments",
      "domain": "platform.payments",
      "tags": ["bs-payments", "bs-tier1", "bs-type-service", "bs-lifecycle-production"]
    },
    "github.com/acme/web-app": {
      "owner": "group:default/team-web",
      "domain": "platform.frontend",
      "tags": ["bs-frontend", "bs-type-website", "bs-lifecycle-production"]
    }
  },
  "domains": {
    "platform.payments": {
      "description": "Payment processing and billing",
      "owner": "group:default/platform-leads"
    },
    "platform.frontend": {
      "description": "Customer-facing web surfaces",
      "owner": "group:default/platform-leads"
    }
  }
}

Configuration

Configure this cataloger in your lunar-config.yml.

Inputs

Input Required Default Description
backstage_url Required Base URL of the Backstage instance (e.g. https://backstage.example.com). The cataloger appends `/api/catalog/entities` to this URL. Required.
entity_kinds Optional Component,Domain Comma-separated list of Backstage entity kinds to sync. Each kind is routed to the appropriate Catalog JSON path: - Component, API, Resource → `.components` - Domain, System → `.domains` Other kinds (User, Group, Location) are ignored.
namespace Optional default Backstage namespace to query. Use `*` to query all namespaces.
component_id_annotation Optional github.com/project-slug Annotation key on a Backstage Component whose value identifies the underlying repo. The cataloger reads this annotation and prepends `component_id_prefix` to build the Lunar component ID. Typical Backstage convention: `github.com/project-slug` (value `owner/repo`) or `backstage.io/source-location` (value `url:https://github.com/owner/repo`).
component_id_prefix Optional github.com/ String prepended to the value of `component_id_annotation` to form the Lunar component ID. For the default annotation `github.com/project-slug` the value is already `owner/repo`, so the prefix is `github.com/` to produce `github.com/owner/repo`.
tag_prefix Optional bs- Prefix added to Backstage `metadata.tags` when mapped to Lunar tags. Also applied to derived tags like `type-<spec.type>` and `lifecycle-<spec.lifecycle>`. Empty string disables the prefix.
include_derived_tags Optional true When `true`, emits derived tags from `spec.type` (e.g. `bs-type-service`) and `spec.lifecycle` (e.g. `bs-lifecycle-production`) in addition to `metadata.tags`.
owner_format Optional as-is How to write `spec.owner` from Backstage into the Lunar `owner` field. Backstage entity refs typically look like `group:default/team-payments` or `user:default/jane`. - `as-is` — pass the Backstage value through verbatim. Matches what the existing `policies/backstage/*` checks accept (`team-payments`, `group:infra`, `user:alice` are all valid). - `bare-name` — strip the `<kind>:<namespace>/` prefix and write only the trailing name (e.g. `team-payments`). Useful when downstream systems want plain names. Email resolution via Backstage User/Group entities is out of scope for v1.
default_owner Required Fallback owner applied (verbatim) to components and domains that have no `spec.owner` in Backstage. Format is whatever you want — entity ref, email, plain string — Lunar stores it as-is. Leave empty to skip entities without an owner.
domain_default_description Required Fallback description for domains that have no `metadata.description` set in Backstage.
filter Required Additional raw Backstage filter expression (passed through to the `?filter=` query parameter). Use to restrict the sync to a subset of entities (e.g. `metadata.annotations.team=platform`). Empty means no extra filter.

Secrets

This cataloger requires the following secrets to be configured in Lunar:

Secret Description
BACKSTAGE_TOKEN Bearer token for the Backstage API. Required if the Backstage instance requires authentication; many internal deployments do.

Documentation

View on GitHub

Backstage Cataloger

Syncs components and domains from a Backstage software catalog into Lunar.

Overview

This cataloger reads entities from a Backstage instance via its REST API (/api/catalog/entities) and writes them into Lunar. Component entities populate .components (with owner, domain, tags); Domain entities populate .domains (description, owner). Use this when you run a Backstage instance and want Lunar to inherit its ownership/domain/tag metadata. Pair with backstage-catalog-info for per-repo catalog-info.yaml augmentation (component-cron, layerable). The per-repo backstage collector is a different shape entirely — it writes .catalog.native.backstage during local / CI Lunar runs.

Synced Data

This cataloger writes to the following Catalog JSON paths:

Path Type Description
.components[*].owner string spec.owner of the Backstage Component (or default_owner fallback)
.components[*].domain string spec.domain of the Backstage Component
.components[*].tags[] array metadata.tags plus derived type-* / lifecycle-* tags, all with tag_prefix
.domains[*].description string metadata.description of the Backstage Domain
.domains[*].owner string spec.owner of the Backstage Domain
Example Catalog JSON output
{
  "components": {
    "github.com/acme/payment-api": {
      "owner": "group:default/team-payments",
      "domain": "platform.payments",
      "tags": ["bs-payments", "bs-tier1", "bs-type-service", "bs-lifecycle-production"]
    },
    "github.com/acme/web-app": {
      "owner": "group:default/team-web",
      "domain": "platform.frontend",
      "tags": ["bs-frontend", "bs-type-website", "bs-lifecycle-production"]
    }
  },
  "domains": {
    "platform.payments": {
      "description": "Payment processing and billing",
      "owner": "group:default/platform-leads"
    },
    "platform.frontend": {
      "description": "Customer-facing web surfaces",
      "owner": "group:default/platform-leads"
    }
  }
}

Catalogers

This integration provides the following catalogers:

Cataloger Description
sync Fetches entities from the Backstage catalog API and writes Components, Domains (and optionally Systems, APIs, Resources) to the Lunar catalog

Hook Type

Hook Schedule Description
cron 0 2 * * * Runs daily at 02:00 UTC

Daily is the conservative default because a full /api/catalog/entities walk paginates through every entity in the Backstage instance — at thousands of components this is a non-trivial fetch against both the Backstage server and the Lunar Runner. Ownership, domain, and tag metadata also change on the order of hours-to-days, not minutes, so a nightly cycle covers the data velocity for almost every catalog. Smaller catalogs are free to tighten the cadence by overriding hook.schedule in their forked copy of lunar-cataloger.yml — promoting schedule to a with: input is a candidate v2 if anyone needs per-deployment tunability without a fork.

Installation

Add to your lunar-config.yml:

catalogers:
  - uses: github.com/earthly/lunar-lib/catalogers/backstage@v1.0.0
    with:
      backstage_url: "https://backstage.example.com"

Authenticated Backstage

Most internal Backstage deployments require a bearer token. Configure it as a Lunar secret:

lunar secret set BACKSTAGE_TOKEN <your-token>

The cataloger reads LUNAR_SECRET_BACKSTAGE_TOKEN automatically — no extra with: is needed.

Layering with the GitHub Org Cataloger

For organisations that already run github-org to enumerate repos, run Backstage after it so its owner/domain/tag values override the GitHub defaults:

catalogers:
  - uses: github.com/earthly/lunar-lib/catalogers/github-org@v1.0.0
    with:
      org_name: "acme"

  - uses: github.com/earthly/lunar-lib/catalogers/backstage@v1.0.0
    with:
      backstage_url: "https://backstage.example.com"

Per Lunar's merge precedence, catalogers declared later override earlier ones.

Mapping Components to Repos

Backstage components are matched to Lunar components by reading an annotation on each Backstage Component entity. Defaults assume the standard github.com/project-slug annotation:

catalogers:
  - uses: github.com/earthly/lunar-lib/catalogers/backstage@v1.0.0
    with:
      backstage_url: "https://backstage.example.com"
      component_id_annotation: "github.com/project-slug"  # value: "acme/payment-api"
      component_id_prefix: "github.com/"                    # → "github.com/acme/payment-api"

For GitLab or other forges, point at the appropriate annotation:

with:
  component_id_annotation: "gitlab.com/project-slug"
  component_id_prefix: "gitlab.com/"

Restricting Synced Kinds

By default, Component and Domain entities are synced. Include other kinds explicitly:

with:
  entity_kinds: "Component,Domain,System,API"
Backstage kind Synced to
Component, API, Resource .components
Domain, System .domains
Other kinds (User, Group, Location, …) Ignored

Filtering Entities

Pass a raw Backstage filter expression through filter:

with:
  filter: "metadata.annotations.team=platform"

Owner Format

Backstage spec.owner is typically an entity reference like group:default/team-payments or user:default/jane, not an email. By default this cataloger passes the value through verbatim — matching what the existing policies/backstage/owner-set policy already accepts (team-payments, group:infra, user:alice are all valid).

If you'd rather store bare names, set owner_format: bare-name to strip the <kind>:<namespace>/ prefix. Resolving entity refs to emails by looking up the User/Group entity is intentionally out of scope for v1 — it adds API calls and only works when User/Group entities carry spec.profile.email.

default_owner is also written verbatim, so you can use whatever convention you prefer (entity ref, email, plain string).

Source System

This cataloger calls the Backstage Catalog REST API — specifically the /api/catalog/entities endpoint. It requires:

  1. Network reach from the Lunar Runner to the Backstage instance
  2. A bearer token (LUNAR_SECRET_BACKSTAGE_TOKEN) if the instance enforces authentication
  3. Read access to the kinds configured in entity_kinds

Pagination is handled automatically; the cataloger streams pages until all matching entities are fetched.

Open Source

This cataloger is open source and available on GitHub. Contribute improvements, report issues, or fork it for your own use.

View Repository

Ready to Automate Your Standards?

See how Lunar can turn your AGENTS.md, engineering wiki, compliance docs, or postmortem action items into automated guardrails with our 200+ built-in guardrails.

Works with any process
check AI agent rules & prompt files
check Post-mortem action items
check Security & compliance policies
check Testing & quality requirements
Automate Now
Paste your AGENTS.md or manual process doc and get guardrails in minutes
Book a Demo