Skip to main content
The catalog is the set of providers, hosts, models, host bindings, pricing and host tier policies Relay routes against. A fresh Relay seeds it on first boot. You add host keys, relay keys, policies and rate limits on top, and edit catalog rows where you need to.

The catalog repo

The catalog is open data in its own repository, wyolet/relay-catalog, separate from the engine. It is a tree of YAML under data/:
Providers own their models; hosts own their bindings, pricing and tier policies. Each release is published as a versioned archive, and the Docker images bundle one release at /catalog, so a fresh container boots with a populated catalog and no network fetch. Because the data lives in its own repo, provider, model and pricing updates ship on their own schedule: you can move to a newer catalog without upgrading Relay.

Naming a model in a request

The model field of an inference request resolves against the catalog. These forms are accepted: Names are normalized before lookup, so dotted and hyphenated forms match (gpt-5.5 and gpt-5-5 are the same name). A dated snapshot name the model lists (for example gpt-4o-2024-08-06) selects that snapshot. When a model has bindings on several hosts and you don’t pin one, Relay uses the first binding it finds that your policy grants and whose host has a usable host key in the policy. The order between hosts is not configurable, so pin a host when it matters, or grant only the host you want. Pin a host with @host in the model name, or send X-WR-Upstream-Host: <host>; Relay appends the header’s host to the model name unless the name already contains @. A pin limits routing to that host; it does not grant access the policy doesn’t give. See Policies for how grants are written. GET /v1/models lists the models your credential can reach. GET /api/catalog/resolve?ref=<ref> on the control API shows what a policy grant such as anthropic@anthropic or @ollama-self expands to.

Model aliases

A model can declare aliases in spec.aliases: extra names that resolve to it. Aliases are checked last, after every catalog name and its provider- and host-qualified forms miss, so they never shadow a real model. An alias may contain one * after a literal prefix (claude-fable-5[*]). When a request matches an alias, Relay routes to the model and sends the name from the request upstream unchanged; this is how a client can send claude-fable-5[1m] to a provider that understands that suffix. Aliases are lookup keys only: they are not valid in policy grants, and usage records the model’s catalog name. To add an alias to a catalog model without losing it on the next catalog update, use an overlay.

Ownership

Every row has an owner, and the owner decides who may change it. Providers, hosts, models, host bindings and pricing are shared catalog data: no user, team or project owns one. Under RELAY_AUTHZ=rbac, writing them takes the admin or catalog-editor role, and system-owned rows stay admin-only. Host keys, relay keys, policies and rate limits can belong to a tenant. An API edit to a catalog row marks it dirty (metadata.dirty), and catalog upgrades skip dirty rows from then on. To change a catalog model and keep receiving upstream updates to the fields you did not touch, use an overlay instead.

Self-hosted upstreams (Ollama)

The catalog ships an ollama-self host for an Ollama server you run, with noAuth: true and bindings for the models Ollama serves, but no base URL, so it is unroutable until you set one. As an admin, read the host and send it back with spec.baseURL set:
The base URL is the server root; the binding’s shape adds the path (/v1/chat/completions for openai). A host whose endpoint differs from the shape’s default path can set spec.path; an explicit empty path means the base URL is the full endpoint. A noAuth host needs no host key, and Relay sends no auth header to it. Because no host key gates it, a policy reaches it only through an explicit grant such as @ollama-self or <provider>/<model>@ollama-self; a policy with an empty model list does not cover it. For any other OpenAI-compatible server, create your own Host with its baseURL (and noAuth: true if it takes no key), the Model rows, and a HostBinding per model with adapter: openai, through the control API or relay apply.

System rate limits

A few rate limits ship with Relay itself, not with the catalog: inference-api-proxy and inference-api-proxy-anonymous for proxy mode, and otlp-export for the OTLP receiver. See Rate limits for what each does and how to change it.

Editing the catalog locally

You can point Relay at a local clone of the catalog data instead of the bundled release, for example to add a provider or model the published catalog doesn’t have:
On first boot against an empty database, Relay walks the mounted tree and seeds it. See Configuration for the seed variables.

Pinning a catalog version

To move between published catalog releases without rebuilding or remounting anything, set RELAY_CATALOG_VERSION to a relay-catalog release tag, or to latest to track the newest release compatible with your Relay version:
On boot, Relay compares the requested version with the one it last seeded (recorded in the catalog-source settings section). If they differ, including on a fresh database, it seeds that release and updates the marker. The bundled tree is used when its version matches; otherwise the release is downloaded. Changing the variable and restarting is a catalog upgrade. latest is resolved again on every boot. Unset, the images seed their bundled /catalog once into an empty database; RELAY_CATALOG_VERSION=off disables version pinning. Re-seeding preserves your changes:
  • Rows you edited through the control API are marked dirty and skipped.
  • Overlays are merged over the new catalog rows when the snapshot is rebuilt, so fields you did not override pick up the new release.
  • Rows you created yourself are never touched.
A catalog row the new release no longer ships, such as a retired tier policy and its rate limit or a removed model, is not deleted: host keys, relay keys and bindings that point at it keep working. The upgrade sets metadata.catalogDetachedAt on it, and from then on the row is governed as yours: you can edit or delete it without changing the governance:* settings. Look for the field on API reads to find these rows and decide whether to keep, repoint or delete them. If a later release ships a row with the same name again, the upgrade clears the field and updates the row, unless you edited it, in which case it stays yours. Only a RELAY_CATALOG_VERSION upgrade sets or clears the field; POST /api/apply and API edits leave it as it is. Releases are downloaded from https://catalog.wyolet.com by default and extracted only when they match the release’s published sha256; a mismatch or a missing checksum counts as a failed download. If the download fails and the database already has a catalog, Relay logs the failure and keeps serving the existing data, so an unreachable catalog server never blocks boot. Use RELAY_CATALOG_URL to fetch from a mirror in airgapped environments. Catalog server describes the HTTP layout releases are served from, how to point Relay and the Go SDK at another server, and how to run one.