pricing

package
v1.801.307 Latest Latest
Warning

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

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

Documentation

Overview

Admin surface for the catalog enablement overlay (SuperAdmin 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 provisioning 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.

The enablement REGISTRY surface (#30/#31) — the three-level model over the ONE catalog overlay store: global off|beta|ga, per-org beta grants, and user self-service beta opt-in. It reuses the SAME overlay the /v1/pricing catalog gate and /v1/admin/catalog admin surface use — there is ONE enablement registry and ONE resolver (Overlay.visibleTo / .State), never a parallel copy.

GET  /v1/admin/enablement          SuperAdmin: the full managed registry
PUT  /v1/admin/enablement          SuperAdmin: set an item off|beta|ga (+ grant orgs)
GET  /v1/enablement                any authed: the caller's EFFECTIVE view + betas
POST /v1/enablement/optin          authed: opt the caller's OWN org into a beta
POST /v1/enablement/optout         authed: opt the caller's own org back out

SECURITY — the two-way crux RED verifies:

  • Global state is SUPERADMIN only (c.IsAdmin()). A customer/org-admin can never flip an item's off/beta/ga.
  • Self opt-in is scoped to the VALIDATED caller org (principal.Org — a gateway-minted principal, NOT a raw client X-Org-Id, which SanitizeIdentity restores on the bearer-less path) — so a user can only ever enable their OWN org, and only for a BETA item: the store's OptIn refuses `ga` (already on) and `off` (the kill switch), so an opt-in can never bypass an admin `off`.

Package pricing 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

Constants

This section is empty.

Variables

This section is empty.

Functions

func GateRootData

func GateRootData(data map[string]any, snap map[string]Overlay, org string, isAdmin bool)

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 Mount

func Mount(app cloud.Router, deps cloud.Deps) error

Mount registers the pricing surface on app per HIP-0106.

func RunSync

func RunSync(ctx context.Context) (string, error)

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 Shutdown

func Shutdown(context.Context) error

Shutdown drops the goja host and closes the catalog overlay store. Idempotent.

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

type Model map[string]any

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

func VisibleCatalog(full []Model, snap map[string]Overlay, org string, isAdmin bool) []Model

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"`
	Beta      bool            `json:"beta,omitempty"`
	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.

Tri-state (the #30/#31 enablement model), encoded by (Enabled, Beta):

  • ga = Enabled → visible to EVERYONE.
  • beta = !Enabled && Beta → hidden from the public; visible to opted-in/ granted orgs (BetaOrgs). Users may SELF-OPT-IN to a beta item.
  • off = !Enabled && !Beta → hidden from EVERYONE, absolutely. BetaOrgs are IGNORED — a self-opt-in can never bypass an `off` kill switch.

func (Overlay) State

func (o Overlay) State() string

State is the tri-state label (off|beta|ga) an operator sets and the console renders. Derived from (Enabled, Beta) so there is ONE source of truth.

Jump to

Keyboard shortcuts

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