@hanzo/plans
v1.1.4
Published
Canonical Hanzo plan and pricing definitions — single source of truth
Readme
@hanzo/plans
Canonical plan + pricing definitions for the Hanzo platform. Single source of truth, consumed by every product surface that renders a price tag or gates a feature.
Install
npm install @hanzo/plansUsage
import {
subscriptionPlans, // developer / pro / max / team / team-max / enterprise / custom / world-*
dnsPlans, // dns-free / dns-pro / dns-enterprise
cloudPlans, // starter / builder / dev / pro / turbo / ...
blockchainPlans,
gpuTiers,
regions,
storage,
tools,
pricingPolicy,
} from '@hanzo/plans'
console.log(subscriptionPlans.find((p) => p.id === 'pro'))
// → { id: 'pro', name: 'Pro', priceMonthly: 49, bundles: ['world-pro'], ... }Or import a single JSON file:
import subscription from '@hanzo/plans/subscription.json'
import dns from '@hanzo/plans/dns.json'Entitlements — the canonical vocabulary (keystone)
Entitlements used to be defined three times: plan features were display strings,
plan limits were ad-hoc machine keys, commerce duplicated them, and licensing
needed its own machine keys for the engine. This package now defines the one
machine-readable entitlement vocabulary, here, once.
entitlements.schema.json— JSON-Schema (draft 2020-12) of the namespaced vocabulary. Every key isnamespace.keywith a fixed type +x-unit, across nine namespaces:ai.*,cloud.*,licensing.*,world.*,dns.*,rpc.*,data.*,tools.*,commerce.*. Headline keys:ai.tokens_per_min,ai.models,cloud.max_vms,cloud.gpu_class,licensing.app_ids,licensing.seats. A numeric-1means unlimited;nullmeans unset/inherit.plan.schema.json— the plan envelope. Addstenant_id(so each reseller/org has its own catalog), the typedentitlementsblock, and theprice_refblock. Displayfeaturesand legacylimitsremain optional.entitlements.mjs— runtime helpers (ENTITLEMENT_KEYS,fromLegacy,toPriceRefStub,toLicenseFeatures,resolvePlan).
tenant_id — multi-tenant catalogs
Plan ids are no longer globally unique. The unique key is the pair
(tenant_id, id). tenant_id defaults to "hanzo" (the first-party catalog)
when omitted, so every existing record stays valid. A reseller ships its own plan
rows under its own tenant_id without colliding with hanzo's pro/max/etc.
The data contract: entitlements → price_ref → license features
plan.entitlements (machine keys; the source of truth)
│ fromLegacy(plan) ← derives entitlements from legacy limits/addons/payouts
▼
price_ref (pricing.hanzo.ai)
├─ recurring flat subscription line (monthly/annual, per_seat)
└─ metered[] usage meters: { entitlement, unit, source, included }
resolved live vs OpenRouter / HF / Zen gateway / DO
│ toLicenseFeatures(entitlements)
▼
license token `features` (hanzoai/licensing → the proprietary engine)
flat []string the engine's release gate matches
(releases.go hasFeatures): "inference", "training",
"ai.premium", "licensing.app:hanzo",
"licensing.product:engine", "deploy.on_prem", …- commerce reads
entitlementsto populate itsEntitlement(members, SSO, product ids, seats) instead of re-deriving from display strings. - licensing copies
licensing.engine_featuresplus the derived capability tokens into the signed tokenfeatures; the engine verifies them offline. - Quantitative quotas (tokens/min, seats, VMs) ride the token as structured limits, not as feature strings, keeping the engine's string-set check meaningful.
import { resolvePlan, ENTITLEMENT_KEYS, fromLegacy, toLicenseFeatures } from '@hanzo/plans'
const max = subscriptionPlans.find((p) => p.id === 'max')
resolvePlan(max)
// → { tenant_id: 'hanzo', id: 'max', entitlements: {...}, price_ref: {...},
// license_features: ['ai.premium','training','licensing.app:hanzo', ...] }Schema
Subscription plans (subscription.json)
type Plan = {
tenant_id?: string // owning reseller/org; defaults to 'hanzo'.
// unique key is (tenant_id, id)
id: string // 'pro', 'world-pro', 'team-max', ... (unique per tenant)
name: string
description: string
priceMonthly: number | null // legacy convenience price; authoritative is price_ref
priceAnnual: number | null // USD per month billed annually
category: 'personal' | 'team' | 'enterprise' | 'world'
popular?: boolean
contactSales?: boolean
features: string[] // DISPLAY strings — do not gate on these
limits?: { // DEPRECATED: back-compat source for `entitlements`
requestsPerMinute?: number
tokensPerMinute?: number
freeCredit?: number
maxMembers?: number
maxAlerts?: number
apiRateLimit?: number
mcpRateLimit?: number
}
entitlements?: Entitlements // CANONICAL machine truth (see entitlements.schema.json)
price_ref?: PriceRef // billing binding: { currency, recurring, metered[] }
bundles?: string[] // slugs of plans this plan also grants free
includedIn?: string[] // slugs of parent plans that bundle this one
payouts?: { idleResalePercent: number; description: string }
}Bundles
Several Hanzo platform tiers grant free access to a Hanzo World tier without a second charge:
| Platform plan | Bundles |
|---|---|
| pro / max | world-pro |
| team / team-max / enterprise | world-team |
A subscription created against a parent plan should mint zero-priced child subscriptions for every entry in bundles. Hanzo Commerce does this automatically in its CreateBillingSubscription handler.
Contributing
hanzoai/plans is the only canonical source. Every consumer (commerce, pricing, console, cloud, hanzo.ai marketing) reads this package — never their own copy. PRs against this repo cascade out via npm publish + downstream image rebuilds.
License
Apache 2.0 — see LICENSE. The data itself is customer-facing pricing; the licence covers the JavaScript bindings.
