zatalog
A Python CLI and library for tools that use the familiar catalog-info.yaml
file structure: expanding descriptor placeholders, resolving entity references,
following the Component/API/Resource → System → Domain hierarchy, and answering
questions like "what Jira project does this actually belong to?" even when
that's only declared several levels up the hierarchy.
Compatibility
Backstage compatibility is not a goal. Zatalog uses the same convenient entity
shape and common kind names so independent tools can share catalog-info.yaml,
but it does not attempt to reproduce the Backstage catalog backend, ingestion
pipeline, policies, or validation results. Zatalog owns its schema and behavior;
they may intentionally differ from Backstage as the needs of local tooling
evolve.
Why
Backstage itself doesn't ship a way to ask "what Jira project covers this
component" from the command line -- it just renders whatever jira/*
annotation happens to be on the entity you're looking at. In practice teams
set jira/project-key once on a System or Domain rather than repeating
it on every Component, so answering that question "fully" means walking
the ownership graph, not just reading one YAML block.
Prior art
There's no existing Python package that does this. What exists instead:
@backstage/catalog-model(TypeScript, in the backstage/backstage monorepo) -- the canonical entity model and JSON Schemas (packages/catalog-model/src/schema/Entity.schema.jsonandkinds/*.schema.json). zatalog'sEntity/EntityMetadatashapes mirror these fields. Zatalog's model starts from the same shape but is independently maintained.plugin-catalog-backend(same repo) -- where Backstage actually computes bidirectionalrelations(e.g. a Component'sspec.systembecomes apartOfrelation, and the System getshasPartback) at ingest time, server-side.zatalog'sCatalog.relations()recomputes the outgoing half of that from raw source YAML, since there's no backend here.- Roadie's catalog validator
and the
@backstage/catalog-modelschemas it wraps -- a Node/GitHub Action tool for schema-checkingcatalog-info.yamlin CI. zatalog'svalidatecommand checks something they don't: that relations declared inspec(system,owner,dependsOn, ...) actually resolve within the loaded catalog. @roadiehq/backstage-plugin-jira-- defines thejira/project-key,jira/component, andjira/labelannotations this tool reads by default, including the comma-separated multi-project andinstance/KEYforms.- The
backstagepackage on PyPI is an unrelated Ubuntu task runner, not a catalog parser.
In short: catalog-info.yaml parsing and relation modeling already exists, just not in Python, and not with a CLI aimed at answering ownership/ops questions like "current Jira project" from a shell prompt.
Install
pip install -e .
Library usage
from pathlib import Path
from zatalog.catalog import load_catalog
from zatalog.query import jira_info
catalog = load_catalog([Path("catalog-info.yaml")])
entity = catalog.get("component:default/checkout-service")
info = jira_info(catalog, entity)
print([str(p) for p in info.projects]) # e.g. ["CHK", "secondary/OTH"]
CLI usage
By default, commands search upward from the current directory for a
catalog-info.yaml/.yml, same as most tools look for a project marker
file. Use -f/--file (repeatable) to load specific files, or
--root DIR --recursive to load every catalog-info file under a directory
tree (a typical Backstage monorepo layout).
Catalogs can compose definitions from Git repositories through Location
entities. Git sources are cached locally and join the same catalog, allowing a
local Component to refer to a centrally maintained System or Domain.
Descriptor substitutions modeled after Backstage are evaluated before entities
are parsed.
$text embeds a referenced file as a string, while $json and
$yaml embed parsed data. Targets may be relative to the descriptor or absolute
HTTP(S) URLs.
spec:
definition:
$text: ./openapi.yaml
customData:
$json: https://example.com/component-data.json
zatalog list [--kind Component]
zatalog show <ref> [--format yaml|json]
zatalog get <ref> <dotted.path> # e.g. spec.type, metadata.tags.0
zatalog annotation <ref> <key> [--no-walk] # walks System -> Domain if unset
zatalog label <ref> <key> [--no-walk]
zatalog jira <ref> [--annotation KEY] [--no-walk] [--format text|json]
zatalog refs <ref> # outgoing relations, flags dangling ones
zatalog validate # schemas valid and supported relations resolve?
Entity references accept kind:namespace/name, namespace/name, or a bare
name (resolved if unambiguous) -- e.g. component:default/checkout-service
or just checkout-service.
Example: "what's the current Jira project?"
$ zatalog jira checkout-service
CHK
secondary/OTH
$ zatalog jira payments-api # no annotation on the Component itself
PAY
(resolved via Domain:default/payments-domain)
Supported kinds
Component, API, Resource, System, Domain, Group, User, Template -- and any
custom kind, since spec is kept as a plain dict. Relation inference
(Catalog.relations(), system_of(), domain_of(), owner_of()) is
defined per-kind in zatalog/catalog.py:RELATION_SPECS and can be extended
for custom kinds.
zatalog validate applies zatalog's own bundled schema to API, Component,
Domain, Group, Location, Resource, System, User, and Template entities. Unknown
versions and custom or plugin-owned kinds are accepted without schema
validation; their spec remains available as a plain dictionary. Validation
runs after $text, $json, and $yaml expansion and reports both schema
violations and unresolved supported relations.
Development
uv sync # or: pip install -e . pytest ruff
pytest
ruff check zatalog tests
Release files for zatalog 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| zatalog-0.1.0.tar.gz | 25.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| zatalog-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 47.3 kB
Release files / zatalog-0.1.0.tar.gz
| Download URL | zatalog-0.1.0.tar.gz |
|---|---|
| Size | 25.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8fff85ba44c9b76290f4d62c3f9dc7d105ebbb389a86e22db3d1a06cf2482eb8
|
|
BLAKE2b-256 checksum How to use checksums |
2577966d8add27096204e9b49025050ac48052c36f7f9c8401be9674d0982438
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 17, 2026.
Transparency logRelease files / zatalog-0.1.0-py3-none-any.whl
| Download URL | zatalog-0.1.0-py3-none-any.whl |
|---|---|
| Size | 21.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
5c9c761be878daccc070686653bd5f3d0c8b244094809dbde5298dbae72440fc
|
|
BLAKE2b-256 checksum How to use checksums |
578ca3a9e223a09f74816fdf149216d488896924f45b120d0446aaa824658688
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 17, 2026.
Transparency log