@hanzo/plans
v1.8.9
Published
Canonical Hanzo plan and pricing definitions — single source of truth
Downloads
1,989
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, // free / dev (Pro) / max-5x / max-20x / team / enterprise
dnsPlans, // dns-free / dns-pro / dns-enterprise
cloudPlans, // starter / builder / dev / pro / turbo / ...
blockchainPlans,
gpuTiers,
regions,
seats,
storage,
tools,
pricingPolicy,
} from '@hanzo/plans'
console.log(subscriptionPlans.find((p) => p.id === 'max-5x'))
// → { id: 'max-5x', name: 'Max 5x', priceMonthly: 100, priceAnnual: 83.33, ... }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-5x')
resolvePlan(max)
// → { tenant_id: 'hanzo', id: 'max-5x', 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 }
}The ladder
| id | name | month | year | burst bounds | AI included a month |
|---|---|---|---|---|---|
| free | Free | $0 | — | enso-free and zen-free, 20 messages a day | — |
| dev | Pro | $20 | $200 | 1x: the base | $15 |
| max-5x | Max 5x | $100 | $1,000 | 5x Pro | $75 |
| max-20x | Max 20x | $200 | $2,000 | 20x Pro | $150 |
| team | Team | $25 / member | $240 / member | Pro, per member | $18.75 / member ($15 annual) |
| advisory | Advisory (agency) | $4,999 | — | Max 20x | $150 |
| dedicated | Dedicated Team (agency) | $9,999 | — | Max 20x | $150 |
| enterprise | Enterprise | quoted | quoted | unlimited | by contract |
"Burst bounds" are requestsPerMinute, tokensPerMinute, the four request
windows and ai.session_cents (AI spend per 5-hour session). Max is exactly k
times Pro in each, and test/windows.test.mjs says so.
Included AI is ai.included_cents, 75% of the price: the hard ceiling a billing
period covers. ai.weekly_cents is a quarter of it and ai.weekly_premium_cents
a quarter of the week (premium models are the platform's ai_premium_models
switch); ai.resets free window resets a period never touch the month. All are
per member on team, whose annual numbers sit at
price_ref.recurring.annual_entitlements. Past the ceiling a holder spends only
prepaid credit they bought; no rung mints credit. test/credit-copy.test.mjs
holds every figure to the price and the copy to the figure. A personal year is
ten months; team's is $240, $20 a month.
The agency retainers are hanzo.agency's, sold by card on pay.hanzo.ai: category
agency keeps them off the app's ladder and org gate, they bill monthly with no
annual price (Advisory's quarter minimum is a contract term, not a field), and
they carry Max 20x's AI because the retainer pays for people.
Pro keeps the dev slug because pro names a compute size in plans.json.
max ($99) is retired and max-5x declares "replaces": ["max"]: every reader
that resolves a stored id (findPlan, the goja routes, commerce) serves a max
holder as max-5x, and the subscription keeps the price it was bought at.
Bundles
Several Hanzo platform tiers grant free access to a Hanzo World tier without a second charge:
| Platform plan | Bundles |
|---|---|
| max-5x / max-20x | world-pro |
| team / 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.
Agent runtime (seats.json)
Agent compute is billed by the hour, not by the seat:
| | Rate |
|---|---|
| runtime.agentHourUSD | $0.08 / hour |
One rate covers all agent runtime — agentic coding, chat inference sessions, and a bot running resident. Hours are metered on top of the subscription.
What a tier includes is a capacity, on the tier: limits.agents and limits.bots (canonically ai.agents / ai.bots). Free may run 1 agent, dev 10, max-5x and max-20x 10 plus 1 resident bot, enterprise unlimited (-1). Those cap how many may exist; they are not an allowance of hours, so a consumer needs both halves and must not read one as the other.
0 means the tier may run none of that kind and is a real, enforceable answer; a missing key means nobody has said yet, and must not be read as zero.
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.
