Catalog & GitOps
Publishing artifacts to the Hub is only half of a release. The other half is the contract: a
machine-readable record of what was published, how good it is, and how to route to it. That contract
is catalog.lock.json, and it is what connects the foundry to the running platform through GitOps.
The catalog source
Section titled “The catalog source”Humans edit small, reviewable YAML in catalog/: one file for models, one for datasets. A model
entry declares its identity, its output contract, its aliases, and its revisions:
models: - name: centinela-sentiment kind: classifier task: text-classification vertical: finanzas hf_repo: astromesh/Centinela-Qwen3-4B contract: labels: [positivo, neutral, negativo] validation: constrain_label aliases: prod: v0.1 revisions: - version: v0.1 sha: "<real HF revision SHA>" gate: passed base_model: Qwen/Qwen3-4B dataset: centinela-sentiment-es@1 formats: [safetensors, gguf] eval: macro_f1: 0.0 # filled from the real eval after training invalid_rate: 0.0contractis the runtime promise: the exact label set andconstrain_labelvalidation, so a consumer never has to guess or post-process free text.aliasesdecouple a stable name (prod) from a specific revision (v0.1) — promotion is just moving an alias.revisionsare immutable, gated snapshots with their eval numbers recorded.
The compiled lock
Section titled “The compiled lock”scripts/09_catalog.py compile validates the source YAML and emits catalog.lock.json — the single
machine contract everything downstream reads:
uv run python scripts/09_catalog.py compile \ --models catalog/centinela.yaml --datasets catalog/datasets.yaml \ --out catalog.lock.jsonThe Models section of these docs is generated directly from this file — every model card you see is a projection of the lock.
The GitOps loop
Section titled “The GitOps loop”A release travels from the foundry to a live endpoint entirely through pull requests — no manual copying of endpoints, keys, or contracts into agent specs.
NEBULA ASTROMESH ┌──────────────────────────┐ ┌───────────────────────────────────────┐ │ 1. train + gate (HF Job) │ │ 4. bindings: alias ─▶ endpoint │ │ 2. edit catalog/*.yaml │ catalog PR │ 5. apply-endpoints provisions the │ │ 3. compile lock ────────┼──────────────────▶│ live HF Inference Endpoint │ │ (catalog.lock.json) │ (push notifies │ 6. writes providers.centinela.yaml │ └──────────────────────────┘ astromesh) │ 7. agent routes via providerRef │ └───────────────────────────────────────┘- Foundry trains and gates a revision, then records it in
catalog/*.yamland compiles the lock. - A catalog change on the Nebula side notifies Astromesh (repository dispatch), which opens a sync/promotion PR — bumping the pinned catalog and proposing binding changes.
- Bindings map a model alias (
prod) to a serving config; merging them triggers endpoint provisioning, which stands up (or updates) a live HF Inference Endpoint. - Provisioning writes a
providers.centinela.yamlentry; an agent references it by name viaproviderRef, and the runtime resolves the endpoint, contract, and auth automatically.
See the whole thing land on a real model: How a model is made: Centinela.