Documentation
¶
Overview ¶
Admin surface for the catalog enablement overlay (global-admin only).
GET /v1/admin/catalog full catalog + every entry's state
PATCH /v1/admin/catalog/models/* upsert one model overlay (id may
contain '/', e.g. anthropic/x)
PATCH /v1/admin/catalog/providers/:name upsert one provider overlay
Gating mirrors the rest of cloud: c.IsAdmin() is the gateway-minted X-User-IsAdmin claim, set only on the JWT-validated path (HIP-0026) for members of the global `admin` org. Same trust model the pricing /sync trigger and provisioningsvc already rely on. Non-admins get 403, never the catalog state.
Catalog enablement overlay: the ONE mutable state Hanzo Cloud lays over the static @hanzo/pricing catalog, plus the ONE gate that applies it on read.
Decomplected: the goja bundle (data/pricing.json) stays the sole source of truth for catalog CONTENT and SHAPE. This file adds only per-entry STATE — {enabled, betaOrgs, overrides} keyed by (kind,id) — and a pure function that filters + merges that state onto the bundle's output. Go never reshapes a model; it only hides entries and merges an admin override patch on top.
Default is "everything enabled": a model/provider with no overlay row is visible to every org, unchanged. An empty store therefore leaves the catalog exactly as the bundle ships it — no fabricated state, no regression for live customers until an admin acts.
Package pricingsvc mounts the @hanzo/pricing service into the unified cloud binary under /v1/pricing/* (+ the /v1/models, /v1/gpu, /v1/tools aliases), per HIP-0106.
HONEST GOJA STATUS: @hanzo/pricing is an EXPRESS app. Express needs Node's http/net stack and CANNOT run in goja. So the Express *transport* is dropped and replaced by native zip routes; the pricing *handlers* (pure transforms over data/pricing.json + the @hanzo/plans catalog) run in goja via the goja/bundle.js shipped by github.com/hanzoai/pricing. The sync.mjs MARKUP logic (toMTok/roundPrice/processOpenRouterModel/…) also runs in goja through the bundle's applyMarkup(); the only thing that does NOT run in goja is the live network fetch (OpenRouter/HuggingFace — no fetch/AbortController in goja), which this wrapper performs with Go's net/http and then feeds the raw JSON into applyMarkup. See SyncEnabled.
Module boundary: pricing source + markup logic live in hanzoai/pricing. This wrapper is glue. No pricing data or markup math is reimplemented in Go.
IAM gating + X-Org-Id: read endpoints are open to any authenticated caller (the public pricing catalog). The sync trigger is admin-only (c.IsAdmin()).
Index ¶
- func GateRootData(data map[string]any, snap map[string]Overlay, org string, isAdmin bool)
- func Mount(app *zip.App, deps cloud.Deps) error
- func RunSync(ctx context.Context) (string, error)
- func Shutdown(context.Context) error
- func VisibleProviders(providers map[string]any, snap map[string]Overlay, org string, isAdmin bool) map[string]any
- type Model
- type Overlay
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func GateRootData ¶
GateRootData gates the kitchen-sink root payload (GET /v1/pricing returns the whole pricing blob) IN PLACE, so the root shows the exact same gated catalog as the leaf routes — never an un-gated second source. It filters every field that carries a model or provider identity:
- hanzoModels + thirdPartyModels via VisibleCatalog,
- providers via VisibleProviders,
- the id-reference lists freeModels and families[].models, kept only if the referenced model survived the gate (customers); admins keep every ref.
Aggregate summary counts and non-catalog sections (tools/infrastructure/cloud) carry no catalog identity and are left untouched.
func RunSync ¶
RunSync performs the live third-party model sync: fetch upstream listings (network — Go's net/http, since goja has no fetch), run the markup transform in goja via the bundle's applyMarkup(), and swap the shaped third-party section into the served catalog. Returns an ISO timestamp.
This is the HONEST split: network IO in Go, markup math in JS. Only the dynamic third-party section is refreshed here; the Zen catalog + cloud/DO pricing in sync.mjs need the zen-gateway + DO credentials and stay on the standalone sync path for now.
func VisibleProviders ¶
func VisibleProviders(providers map[string]any, snap map[string]Overlay, org string, isAdmin bool) map[string]any
VisibleProviders filters a provider dict (name -> info) by the provider overlay for org, merging provider overrides (RFC 7386). isAdmin callers get every provider with state annotated under each provider's "_overlay".
Types ¶
type Model ¶
Model is one catalog entry exactly as the @hanzo/pricing bundle emits it (see goja/bundle.js 'models'): an opaque JSON object. The gate reads only the identifier (id, falling back to name) and provider, and passes every other field through untouched — keeping the bundle authoritative for shape.
func VisibleCatalog ¶
VisibleCatalog applies the enablement overlay to the bundle's full model list for org. A model is visible iff its OWN overlay AND its provider's overlay both admit org (enabled, or org on the beta list); an entry with no overlay row is visible by default, so a provider with no row never hides its models. Returned models carry any admin override merged on top (RFC 7386). isAdmin callers receive EVERY model — disabled ones included — each annotated under "_overlay" so the admin UI can render and toggle it.
Pure over (full, snap, org, isAdmin): no IO, no globals. This is the unit under test; the wiring layer fetches `full` from goja and `snap` from the store, then calls it.
type Overlay ¶
type Overlay struct {
Kind string `json:"kind"`
ID string `json:"id"`
Enabled bool `json:"enabled"`
BetaOrgs []string `json:"betaOrgs,omitempty"`
Overrides json.RawMessage `json:"overrides,omitempty"`
UpdatedAt int64 `json:"updatedAt,omitempty"`
}
Overlay is the mutable enablement STATE for one catalog entry. Zero value (no row) == enabled, no beta orgs, no override; the gate treats an absent row as visible, so an empty store is a no-op.