Documentation
¶
Overview ¶
Package plan mounts the @hanzo/plans catalog into the unified cloud binary under /v1/plans/*, per HIP-0106.
STRATEGY: wrap, don't rewrite. @hanzo/plans is a Node data package (JSON catalog + entitlements.mjs transforms). We do NOT reimplement the entitlement vocabulary in Go and we do NOT copy the catalog into cloud. Instead:
- github.com/hanzoai/plans (the service repo's Go embed module) ships goja/bundle.js — the ESM-free port of entitlements.mjs + the /v1/plans route table — plus the embedded *.json catalog (plans.Data()).
- This wrapper loads that bundle into a goja runtime (clients/goja), injects the catalog as globalThis.__PLANS_DATA__, and registers thin zip handlers that call globalThis.handle({route, params, tenant}). The entitlement transforms (fromLegacy/toLicenseFeatures/resolvePlan) run in goja — real JS, not a Go reimplementation.
The plans data is read-only public-catalog content; there are no secrets here. The licensing SIGNER/fingerprint that consumes toLicenseFeatures stays in hanzoai/licensing. This wrapper is pure glue.
IAM gating + X-Org-Id tenant scope: every /v1/plans route reads the gateway-minted identity off the zip.Ctx (c.Org()) and threads it into the bundle as the tenant, so a reseller org (tenant_id != "hanzo") sees its own catalog overrides. The plan catalog is readable by any authenticated caller; no admin scope is required for reads.
Index ¶
- func Entitlements(ctx context.Context, id string) (map[string]any, error)
- func LicenseEntitlement(ctx context.Context, id string) (entitlements map[string]any, features []string, found bool, err error)
- func Mount(app cloud.Router, deps cloud.Deps) error
- func Paid(id string) (bool, error)
- func Shutdown(context.Context) error
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Entitlements ¶
Entitlements resolves the canonical entitlement block for a plan id from the @hanzo/plans catalog (the single source of truth). It runs the bundle's "entitlements" route on the shared goja host and returns the parsed, namespaced `entitlements` map (e.g. "world.api_rate_limit", "ai.tokens_per_min"). This is the one Go seam other subsystems use to read plan entitlements without importing the catalog data or reimplementing the fromLegacy derivation. Tenant is the public "hanzo" catalog (plan entitlements are tenant-independent metadata).
It errors if the plans subsystem is not mounted or the plan id is unknown, so callers can fail closed rather than silently granting a default tier.
func LicenseEntitlement ¶
func LicenseEntitlement(ctx context.Context, id string) (entitlements map[string]any, features []string, found bool, err error)
LicenseEntitlement resolves BOTH the canonical entitlement block AND the flat license-feature list for a plan id from the @hanzo/plans catalog (the single source of truth). It runs the bundle's "entitlements" route on the shared goja host — the SAME route /v1/plans/entitlements/:id serves — and returns the parsed `entitlements` map plus the `license_features` list the bundle's toLicenseFeatures transform produces. It is the seam the commerce entitlement resolver (commerce.CheckEntitlement) uses to map a subscription's plan tier to the flat features a signed license carries, WITHOUT reimplementing the vocabulary in Go (the entitlement transforms stay in one place — the JS bundle).
found reports whether the plan id exists in the catalog. A 404 from the bundle is (nil, nil, false, nil): a real "unknown plan", NOT a machinery error — so a caller scanning several subscriptions can skip an unknown tier and keep going. ANY other failure (plans subsystem not mounted, dispatch error, non-200/404 status, decode error) returns a non-nil error so the money-path caller FAILS CLOSED rather than treating an unresolved plan as "grants nothing".
func Paid ¶
Paid reports whether plan tier id is a PAID Hanzo cloud subscription tier — the paywall's "this org holds a real plan" predicate. Pro/Plus/Max/Team/Team-Max/ Enterprise/Custom → (true, nil); the free Developer tier, a non-cloud product plan (world-*/social-*), an empty or unknown id → (false, nil).
A non-nil error means the embedded catalog could not be read (a build/embed defect — effectively impossible in a booted binary). It is returned rather than swallowed so the caller can FAIL OPEN (admit the request) rather than mistake a catalog outage for "no paid tier" and lock a subscriber out. This function NEVER fabricates a grant: it returns true only for a tier the catalog says is a paid cloud account tier.
Types ¶
This section is empty.