catalog

package
v0.9.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 4, 2026 License: Apache-2.0 Imports: 31 Imported by: 0

Documentation

Overview

Snapshot assembly. build is intentionally a thin orchestrator: it computes the input-id sets and calls each kind's addX method in the order their dependencies require. The per-kind logic (sanitize + register) lives in build_<kind>.go.

Cross-references in each entity are *sanitized* against the input enabled-id sets before insertion: a ref to a missing or disabled row is silently dropped from the snapshot copy. The full row stays in Postgres for the control plane. Reload never fails over a stale ref — the snapshot is always the consistent reachable subgraph.

Helpers shared by the per-kind build_*.go files.

The build pipeline is intentionally one file per resource: each build_<kind>.go owns its sanitizer (cross-ref filtering) and its "register into the Snapshot" step. build.go orchestrates the order; it never reads or writes Snapshot maps directly.

notify.go implements the PG NOTIFY listener and debouncer for catalog events.

The listener acquires a dedicated connection from the pool, issues LISTEN catalog_events, and forwards parsed events to the debouncer. A flush goroutine drains the debouncer every second, fetches each affected row via the narrow store interfaces, and applies it to the Catalog via the appropriate Apply* method.

On any connection error the listener logs, waits 1 s, and reconnects — it never panics. Run blocks until ctx is cancelled.

Overlay application — the single place TEMPLATE rows become EFFECTIVE rows. Overlays (app/overlay) are user-owned sparse spec patches; the merge happens here at snapshot build/reconcile time, never in storage, so re-seeding templates is overlay-unaware and user customizations survive catalog upgrades.

policy_allow.go precomputes, per policy, the concrete set of (model, host) combinations the policy grants — so request-time authorization is an O(1) membership test instead of a per-request ref-matching loop.

Only EXPLICIT-grant policies (ModelIDs and/or Models set) get a set. An implicit-wildcard policy (both empty) grants everything reachable via its hostkeys; materializing that would be the whole catalog, so it gets no set and PolicyAllowsCombo returns true — the hostkey-coverage gate at resolution is the real authorization in that case. The same sets gate the host tier policy a hostkey is attached to (see routing's key selection).

Built off the hot path: once in Build, and recomputed on any reconcile that can change a grant (provider/host/model/policy writes). It reads policies + models + hosts + providers, so it must run after those are indexed.

Package catalog is the composition layer: it pulls the entity stores together into an atomic in-memory Snapshot the request path can read in O(1). Snapshots are immutable; Reload rebuilds and atomically swaps.

Membership rule (single, simple): every enabled row of every kind enters the snapshot. There is no reachability filter — a Model not bound to any Policy still appears (it's just unreachable through PoolModels until someone wires it up). The "enabled" flag is the entire toggle mechanism.

Reverse joins (modelsByPolicy, pricingByModelHost, etc.) are derived indices over the snapshot — they hold pointers to the same rows.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Bootstrap

func Bootstrap(ctx context.Context, opts BootstrapOptions) (*Catalog, *Listener, *Stores, error)

Bootstrap is the legacy one-shot: stores + Hydrate in a single call. Kept for tests and any caller that doesn't need split-boot semantics. Returns the same triple as before plus the listener primed for Run.

func BootstrapStores

func BootstrapStores(ctx context.Context, opts BootstrapOptions) (*Catalog, *Stores, error)

BootstrapStores wires the eight entity stores against the pool and constructs a Catalog. Does NOT touch row data — no seed, no Reload. Use when the control plane needs the stores but data-plane readiness is deferred (see (*Catalog).Hydrate). Cheap and rarely fails.

Types

type AliasRef added in v0.3.0

type AliasRef struct {
	Model    *model.Model
	Snapshot *model.Snapshot
	// HostID is non-empty only when the matched form carried an "@host"
	// pin (synthesized for exact aliases the same way snapshotAliases
	// pins are).
	HostID string
	// Name is the alias exactly as declared on the model. For exact
	// aliases this is the verbatim upstream wire name; for patterns it
	// identifies the matcher (usage tagging).
	Name string
	// Pattern marks a wildcard match — the upstream wire name is the
	// caller's raw request string, not Name.
	Pattern bool
}

AliasRef is a declared-alias match: the owning Model and the Pointer snapshot the alias resolves to. Declared aliases are resolution-only, last-priority lookup keys (see model.Spec.Aliases) — routing consults them only after ResolveSnapshot misses, so real catalog names always shadow them.

type BindingLister

type BindingLister interface {
	List(ctx context.Context) ([]*binding.Binding, error)
}

type BootstrapOptions

type BootstrapOptions struct {
	Pool      *pgxpool.Pool
	MasterKey []byte

	// AutoSeedDir, when non-empty AND the catalog is empty in PG, triggers
	// a YAML import from this directory before the initial Reload. The
	// expected layout matches wyolet/relay-catalog's data/ tree (providers/
	// <provider>/{provider.yaml,models/}, hosts/<host>/{host.yaml,pricing/,
	// policies/}). filepath.WalkDir walks the tree; dispatch is by the
	// kind field in each YAML doc, so the nested layout is transparent.
	// Idempotent: if any catalog row already exists, seeding is skipped.
	AutoSeedDir string

	// CatalogVersion, when non-empty, pins the seeded catalog to a
	// published relay-catalog ref (tag). At hydrate the stored
	// "catalog-source" marker is compared against it; on mismatch (or an
	// empty catalog) the archive is fetched from CatalogURL, seeded, and
	// the marker updated — no image rebuild needed to move catalog
	// versions. The seed is layering-safe: operator-edited (dirty) rows
	// are skipped and overlays re-merge at snapshot load. A fetch failure
	// against a non-empty catalog logs and continues with the existing
	// rows (never blocks boot); against an empty catalog it falls back to
	// AutoSeedDir when set, else fails hydrate (retried by the caller).
	CatalogVersion string

	// CatalogURL overrides the archive URL template used by
	// CatalogVersion fetches ("{version}" substituted). Empty uses
	// seed.DefaultCatalogURLTemplate (the wyolet/relay-catalog GitHub
	// archive). Point it at a mirror for airgapped deployments.
	CatalogURL string
}

BootstrapOptions configures the one-call Bootstrap helper. Pool and MasterKey are required; MasterKey may be nil if stored-mode HostKeys aren't in use.

type Catalog

type Catalog struct {
	// contains filtered or unexported fields
}

Catalog is the long-lived composition object. Holds the entity stores and the current Snapshot pointer. Construct one per process; call Reload at boot and whenever PG state changes (admin write, NOTIFY watcher).

func New

func New(
	providers ProviderLister,
	hosts HostLister,
	policies PolicyLister,
	models ModelLister,
	keys HostKeyLister,
	rateLimits RateLimitLister,
	relayKeys RelayKeyLister,
	pricings PricingLister,
	bindings BindingLister,
) *Catalog

New constructs a Catalog backed by the supplied stores. Initial Snapshot is empty; call Reload before serving traffic.

func (*Catalog) ApplyHostDelete

func (c *Catalog) ApplyHostDelete(id string) error

func (*Catalog) ApplyHostKeyDelete

func (c *Catalog) ApplyHostKeyDelete(id string) error

func (*Catalog) ApplyHostKeyUpsert

func (c *Catalog) ApplyHostKeyUpsert(k *hostkey.HostKey) error

func (*Catalog) ApplyHostUpsert

func (c *Catalog) ApplyHostUpsert(h *host.Host) error

func (*Catalog) ApplyModelDelete

func (c *Catalog) ApplyModelDelete(id string) error

func (*Catalog) ApplyModelUpsert

func (c *Catalog) ApplyModelUpsert(m *model.Model) error

func (*Catalog) ApplyOverlayDelete added in v0.3.0

func (c *Catalog) ApplyOverlayDelete(kind, resourceID string) error

ApplyOverlayDelete removes an overlay and restores the target's pristine template (factory reset).

func (*Catalog) ApplyOverlayUpsert added in v0.3.0

func (c *Catalog) ApplyOverlayUpsert(o *overlay.Overlay) error

ApplyOverlayUpsert installs (or replaces) an overlay and re-derives the target's effective row. An overlay whose target isn't in the snapshot is registered inert — it merges when the target appears.

func (*Catalog) ApplyPolicyDelete

func (c *Catalog) ApplyPolicyDelete(id string) error

func (*Catalog) ApplyPolicyUpsert

func (c *Catalog) ApplyPolicyUpsert(p *policy.Policy) error

func (*Catalog) ApplyPricingDelete

func (c *Catalog) ApplyPricingDelete(id string) error

func (*Catalog) ApplyPricingUpsert

func (c *Catalog) ApplyPricingUpsert(p *pricing.Pricing) error

func (*Catalog) ApplyProviderDelete

func (c *Catalog) ApplyProviderDelete(id string) error

func (*Catalog) ApplyProviderUpsert

func (c *Catalog) ApplyProviderUpsert(p *provider.Provider) error

func (*Catalog) ApplyRateLimitDelete

func (c *Catalog) ApplyRateLimitDelete(id string) error

func (*Catalog) ApplyRateLimitUpsert

func (c *Catalog) ApplyRateLimitUpsert(r *ratelimit.RateLimit) error

func (*Catalog) ApplyRelayKeyDelete

func (c *Catalog) ApplyRelayKeyDelete(id string) error

func (*Catalog) ApplyRelayKeyUpsert

func (c *Catalog) ApplyRelayKeyUpsert(k *relaykey.RelayKey) error

func (*Catalog) Current

func (c *Catalog) Current() *Snapshot

Current returns the live Snapshot. Safe to call from any goroutine; the returned pointer is immutable until the next successful Reload.

func (*Catalog) Hydrate

func (c *Catalog) Hydrate(ctx context.Context, stores *Stores, opts BootstrapOptions) (*Listener, error)

Hydrate is the expensive half of bootstrap: reload settings, load the hostkey master-key version, optionally auto-seed from YAML, run the first catalog Reload, and construct a NOTIFY listener primed for Run. On any error the Catalog's IsReady stays false and the caller can retry — handlers gate on it and return 503 in the meantime.

func (*Catalog) IsReady

func (c *Catalog) IsReady() bool

IsReady reports whether the catalog has built its first snapshot successfully. Until then, the in-memory snapshot is the zero-value returned by New — empty maps that look identical to a legitimately empty catalog. The inference plane uses this to refuse traffic with 503 instead of silently serving "no models found" lookups.

func (*Catalog) OnSettingsChange

func (c *Catalog) OnSettingsChange(section string, fn func())

OnSettingsChange registers fn to run whenever section's value changes (upsert or delete), after the new value is visible to Setting. fn runs synchronously on the NOTIFY listener goroutine, which applies catalog events serially — so fn MUST be cheap and non-blocking. A consumer with heavy work (sink rebuild, network I/O) should have fn do a non-blocking signal and perform the work on its own goroutine. Subscriptions are process-lifetime; there is no unsubscribe.

func (*Catalog) Reload

func (c *Catalog) Reload(ctx context.Context) error

Reload reads every store, filters to enabled rows, runs cross-entity validation, builds a fresh Snapshot, and atomic-swaps it in. On any error the existing Snapshot stays live — callers can retry.

func (*Catalog) Setting

func (c *Catalog) Setting(section string) (any, bool)

Setting returns the typed value for section, falling back to the section's Defaults() when no row has been seen yet. ok=false means the section name is not registered.

func (*Catalog) UseOverlays added in v0.3.0

func (c *Catalog) UseOverlays(l OverlayLister)

UseOverlays attaches the overlay source. Called once at composition time before the first Reload; nil (the default) keeps overlays dormant.

type HostKeyLister

type HostKeyLister interface {
	List(ctx context.Context) ([]*hostkey.HostKey, error)
}

type HostLister

type HostLister interface {
	List(ctx context.Context) ([]*host.Host, error)
}

type Listener

type Listener struct {
	// contains filtered or unexported fields
}

Listener subscribes to catalog_events NOTIFY, debounces the payload stream, and applies incremental updates to the Catalog.

func NewListener

func NewListener(cat *Catalog, pool *pgxpool.Pool, stores listenerStores) *Listener

NewListener constructs a Listener. Call Run to start it.

func (*Listener) Run

func (l *Listener) Run(ctx context.Context) error

Run blocks until ctx is cancelled. It reconnects on any connection error.

type ModelLister

type ModelLister interface {
	List(ctx context.Context) ([]*model.Model, error)
}

type OverlayLister added in v0.3.0

type OverlayLister interface {
	List(ctx context.Context) ([]*overlay.Overlay, error)
}

type PolicyLister

type PolicyLister interface {
	List(ctx context.Context) ([]*policy.Policy, error)
}

type PricingLister

type PricingLister interface {
	List(ctx context.Context) ([]*pricing.Pricing, error)
}

type ProviderLister

type ProviderLister interface {
	List(ctx context.Context) ([]*provider.Provider, error)
}

type RateLimitLister

type RateLimitLister interface {
	List(ctx context.Context) ([]*ratelimit.RateLimit, error)
}

type RelayKeyLister

type RelayKeyLister interface {
	List(ctx context.Context) ([]*relaykey.RelayKey, error)
}

type SettingsLister

type SettingsLister interface {
	List(ctx context.Context) ([]*settings.Row, error)
	Get(ctx context.Context, section string) (*settings.Row, error)
}

SettingsLister is the narrow read interface the catalog needs to bootstrap and refresh the settings cache. *settings.Store satisfies it.

type Snapshot

type Snapshot struct {
	// contains filtered or unexported fields
}

Snapshot is the immutable in-memory view. All maps are populated at construction and never written after — read accessors are safe to call from any goroutine.

func Build

func Build(
	provs []*provider.Provider,
	hosts []*host.Host,
	pols []*policy.Policy,
	rks []*relaykey.RelayKey,
	models []*model.Model,
	keys []*hostkey.HostKey,
	rls []*ratelimit.RateLimit,
	pricings []*pricing.Pricing,
	bindings []*binding.Binding,
) *Snapshot

Build assembles a Snapshot from entity slices using the same sanitize rules as Reload. Used by catalog-embed and tests; callers supply only the kinds they need — pass nil/empty slices for the rest.

func (*Snapshot) AllBindings

func (s *Snapshot) AllBindings() []*binding.Binding

AllBindings returns every binding in the snapshot, sorted by name.

func (*Snapshot) AllHostKeys

func (s *Snapshot) AllHostKeys() []*hostkey.HostKey

AllHostKeys returns every HostKey in the snapshot, sorted by slug.

func (*Snapshot) AllModels

func (s *Snapshot) AllModels() []*model.Model

AllModels returns every enabled Model in stable slug order. Used by the /catalog/resolve endpoint for host-only refs ("@bedrock") that need to walk the entire catalog rather than a single provider.

func (*Snapshot) AllPolicies

func (s *Snapshot) AllPolicies() []*policy.Policy

AllPolicies returns every Policy in the snapshot, sorted by slug.

func (*Snapshot) AllPricings

func (s *Snapshot) AllPricings() []*pricing.Pricing

AllPricings returns every Pricing in the snapshot, sorted by slug.

func (*Snapshot) AllProviders

func (s *Snapshot) AllProviders() []*provider.Provider

AllProviders returns every Provider in the snapshot, sorted by slug.

func (*Snapshot) AllRateLimits

func (s *Snapshot) AllRateLimits() []*ratelimit.RateLimit

AllRateLimits returns every RateLimit in the snapshot, sorted by slug.

func (*Snapshot) AllRelayKeys

func (s *Snapshot) AllRelayKeys() []*relaykey.RelayKey

AllRelayKeys returns every RelayKey in the snapshot, sorted by slug.

func (*Snapshot) Binding

func (s *Snapshot) Binding(id string) (*binding.Binding, bool)

Binding returns the enabled HostBinding with this id, or false.

func (*Snapshot) BindingForModelHost

func (s *Snapshot) BindingForModelHost(modelID, hostID string) (*binding.Binding, bool)

BindingForModelHost returns the binding for (modelID, hostID), or false. O(1) — the routing hot-path lookup.

func (*Snapshot) BindingsForModel

func (s *Snapshot) BindingsForModel(modelID string) []*binding.Binding

BindingsForModel returns the bindings declared for a model, sorted by binding name. The returned slice must not be mutated.

func (*Snapshot) Dependents

func (s *Snapshot) Dependents(kind refKind, id string) []refKey

Dependents returns every refKey that depends on (kind, id). nil if none. The slice is freshly allocated; mutating it doesn't affect the snapshot.

func (*Snapshot) EnabledHosts

func (s *Snapshot) EnabledHosts() []*host.Host

EnabledHosts returns enabled hosts sorted by Meta.Name.

func (*Snapshot) EnabledModels

func (s *Snapshot) EnabledModels() []*model.Model

EnabledModels returns enabled models sorted by Meta.Name.

func (*Snapshot) Host

func (s *Snapshot) Host(id string) (*host.Host, bool)

Host returns the enabled Host with this id, or false.

func (*Snapshot) HostByName

func (s *Snapshot) HostByName(name string) (*host.Host, bool)

HostByName returns the enabled Host with this slug, or false.

func (*Snapshot) HostKey

func (s *Snapshot) HostKey(id string) (*hostkey.HostKey, bool)

HostKey returns the enabled HostKey with this id, or false.

func (*Snapshot) HostKeysForHost

func (s *Snapshot) HostKeysForHost(hostID string) []*hostkey.HostKey

HostKeysForHost returns every enabled HostKey whose Spec.HostID matches hostID. Order is by hostkey slug — stable across snapshots. Used by routing's policy-less flow (settings.Inference.AllowMissingPolicy) where the policy doesn't narrow the pool.

func (*Snapshot) HostKeysInPolicy

func (s *Snapshot) HostKeysInPolicy(policyID string) []*hostkey.HostKey

HostKeysInPolicy returns the HostKeys attached to this Policy in declaration order (relevant for KeySelectionPrioritized).

func (*Snapshot) HostSlug

func (s *Snapshot) HostSlug(hostID string) (string, bool)

HostSlug returns the Host.Meta.Name for the given Host id, or false.

func (*Snapshot) Hosts

func (s *Snapshot) Hosts() []*host.Host

Hosts returns all enabled Host rows. Stable order by slug. Used by the /v1/proxy/hosts list endpoint.

func (*Snapshot) Model

func (s *Snapshot) Model(id string) (*model.Model, bool)

Model returns the enabled Model with this id, or false.

func (*Snapshot) ModelsByName

func (s *Snapshot) ModelsByName(name string) []*model.Model

ModelsByName returns every enabled Model whose Meta.Name matches. The slug is unique per kind, but the index is multivalued to absorb transient overlap during reload. Customer-facing addressing uses SnapshotByName instead — this accessor is admin-only.

func (*Snapshot) ModelsByProvider

func (s *Snapshot) ModelsByProvider(providerID string) []*model.Model

ModelsByProvider returns every enabled Model whose owning Provider matches providerID. Stable order by slug. Used by the /catalog/resolve admin endpoint to enumerate a provider's catalog.

func (*Snapshot) ModelsInPolicy

func (s *Snapshot) ModelsInPolicy(policyID string) []*model.Model

ModelsInPolicy returns the Models attached to this Policy in declaration order. nil if the Policy is unknown or empty.

func (*Snapshot) Policy

func (s *Snapshot) Policy(id string) (*policy.Policy, bool)

Policy returns the enabled Policy with this id, or false.

func (*Snapshot) PolicyAllowsCombo

func (s *Snapshot) PolicyAllowsCombo(policyID, modelID, hostID string) bool

PolicyAllowsCombo reports whether the policy grants (modelID, hostID). A policy with no materialized set is an implicit wildcard (or has no explicit grants tracked here) and allows everything — implicit-wildcard customer policies handle deprecation separately at resolution; tier policies allow all by design.

func (*Snapshot) PolicyByName

func (s *Snapshot) PolicyByName(name string) (*policy.Policy, bool)

PolicyByName returns the enabled Policy with this slug, or false.

func (*Snapshot) PriceByModelHost

func (s *Snapshot) PriceByModelHost(modelID, hostID string) (*pricing.Pricing, bool)

PriceByModelHost returns the Pricing that covers (modelID, hostID), or false.

func (*Snapshot) Pricing

func (s *Snapshot) Pricing(id string) (*pricing.Pricing, bool)

Pricing returns the enabled Pricing with this id, or false.

func (*Snapshot) PricingForBinding added in v0.3.0

func (s *Snapshot) PricingForBinding(b *binding.Binding) (*pricing.Pricing, bool)

PricingForBinding resolves the rate sheet billing against a binding: the binding's explicit Spec.PricingID first, else the host-owned pricing covering the (model, host) pair. Mirrors catalogview's resolution so the admin read-projection and the emit-time cost stamp agree.

func (*Snapshot) Provider

func (s *Snapshot) Provider(id string) (*provider.Provider, bool)

Provider returns the enabled Provider with this id, or false.

func (*Snapshot) ProviderByName

func (s *Snapshot) ProviderByName(name string) (*provider.Provider, bool)

ProviderByName returns the enabled Provider with this slug, or false.

func (*Snapshot) ProviderSlug

func (s *Snapshot) ProviderSlug(providerID string) (string, bool)

ProviderSlug returns the Provider.Meta.Name for the given Provider id, or false. Hot-path resolution uses this to compare a Model's owning Provider against the suffix/header hint without needing the full Provider row.

func (*Snapshot) RateLimit

func (s *Snapshot) RateLimit(id string) (*ratelimit.RateLimit, bool)

RateLimit returns the enabled RateLimit with this id, or false.

func (*Snapshot) RateLimitByName

func (s *Snapshot) RateLimitByName(name string) (*ratelimit.RateLimit, bool)

RateLimitByName returns the enabled RateLimit with this slug, or false. Used by proxy-mode dispatch to look up the system-owned inference-api-proxy / inference-api-proxy-anonymous buckets.

func (*Snapshot) RateLimitOfPolicy

func (s *Snapshot) RateLimitOfPolicy(policyID string) *ratelimit.RateLimit

RateLimitOfPolicy returns the single RateLimit bound to this Policy, or nil when none is configured.

func (*Snapshot) RelayKey

func (s *Snapshot) RelayKey(id string) (*relaykey.RelayKey, bool)

RelayKey returns the enabled RelayKey with this id, or false.

func (*Snapshot) RelayKeyByHash

func (s *Snapshot) RelayKeyByHash(hash string) (*relaykey.RelayKey, bool)

RelayKeyByHash is the hot-path inbound-auth lookup. Returns the RelayKey whose Spec.KeyHash matches. Caller checks IsActive.

func (*Snapshot) ResolveAlias added in v0.3.0

func (s *Snapshot) ResolveAlias(key string, patterns bool) (AliasRef, bool)

ResolveAlias maps a slug-normalized ref to a declared model alias: exact map probe first, then — when patterns is true — the wildcard list in longest-prefix order. Routing calls this only on ResolveSnapshot miss (exact catalog names always win) and passes patterns=false for refs that carry an "@host" pin, because the pin segment is glued into the normalized key and would corrupt a wildcard match.

func (*Snapshot) ResolveSnapshot

func (s *Snapshot) ResolveSnapshot(key string) (*model.Model, *model.Snapshot, string, bool)

ResolveSnapshot maps a slug-normalized model ref to its model + snapshot, plus an optional pinned HostID (set when the ref named a host via "@host"). Bare snapshot names win over synthesized aliases. The caller normalizes the key with slug.From.

func (*Snapshot) SnapshotByName

func (s *Snapshot) SnapshotByName(name string) (*model.Model, *model.Snapshot, bool)

SnapshotByName returns the Model + Snapshot for a pinned snapshot name, or false. Snapshot names are catalog-wide unique (validation enforces no collision with the owning Model's name or aliases).

type Stores

type Stores struct {
	Provider  *provider.Store
	Host      *host.Store
	Model     *model.Store
	HostKey   *hostkey.Store
	RateLimit *ratelimit.Store
	Policy    *policy.Store
	Pricing   *pricing.Store
	Binding   *binding.Store
	RelayKey  *relaykey.Store
	Overlay   *overlay.Store
	Settings  *settings.Store

	// Secrets is the shared secret-resolution registry (env + stored
	// backends). Exposed so data-plane components (e.g. the payload-logging
	// controller resolving S3 credentials) resolve through the same seam.
	Secrets *pkgsecret.Registry
}

Stores bundles the eight entity stores constructed by Bootstrap. Exposed so callers (admin handlers, seed CLI re-runs, tests) can reach the same underlying stores without re-wiring.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL