Core Editions
Core Editions¶
Huitzo Core runs self-hosted in two editions: Core Preview and Core
Licensed. Huitzo's own hosted service runs a third licence tier. The edition
comes from the tier field of the signed license.json. Every Preview limit
is a claim signed into that file, so an operator cannot raise it by editing
settings or tenant quotas.
Each license also carries a signed purpose: Demo/POC licenses are Core Preview; Pilot, not-for-resale and Paid licenses are Core Licensed (Purposes and term caps). Operators install and renew the file with License status and renewal.
Editions at a glance¶
| Core Preview | Core Licensed | Hosted | |
|---|---|---|---|
Licence tier |
preview |
licensed |
enterprise |
| Who runs it | A prospective customer evaluating Huitzo, on their own infrastructure. At launch it is issued only through partners and Huitzo (ADR-008 D8). | A customer, on their own infrastructure | Huitzo (huitzo.ai production and staging) |
| Purpose | Experimenting, not running a business | Production operations | Huitzo's SaaS service |
| Commercial basis | Free, renewable 90-day licence | Term contract, under the licence agreement/EULA | — |
| Limits | The signed Preview limits | None: unlimited tenants, seats, packs, executions, schedules and runners | None |
white_label |
No | Yes | Yes |
publish_public (non-private publishing) |
No: private only | Yes | Yes (implied by the tier) |
saas (credits, wallets, Stripe, paid marketplace) |
No | No | Yes |
| Banner | "Preview — not for production use" | None | None |
| Expiry grace | 7 days | 14 days | 14 days |
Grace is signed in the file; HUITZO_OFFLINE_GRACE_DAYS may only lower it (ADR-008 D2).
Repo docs quote no licence prices. Commercial terms are in the licence agreement.
Preview limits¶
Canon §3 limits. Each one is a signed claim in license.json. A Preview may
be issued with a lower value, never a higher one: a Preview file signed above
any of these values is INVALID (402).
| Area | Preview limit | Licence claim | Checked at | Refusal |
|---|---|---|---|---|
| Tenants | Home tenants, plus 1 team tenant | max_team_tenants=1 |
Team-tenant create, a staff kind correction to team, and a tenant.create coupon redeem |
409 license_limit_exceeded; a coupon redeem returns the uniform 400 coupon_invalid |
| Seats | 3 per tenant | max_seats_per_tenant=3 |
Tenant tenant.join coupon mint (advisory), coupon tenant.join at registration and redeem (authoritative) |
409 license_limit_exceeded; a coupon redeem returns the uniform 400 coupon_invalid |
| Packs | 3 enabled, deployment-wide | max_packs=3 |
Pack create and pack enable | 409 license_limit_exceeded |
| Executions | 500 per deployment, trailing 30 days | monthly_executions=500 |
REST, MCP and pipeline execute; worker scheduled dispatch | 409 license_limit_exceeded; the worker skips and logs |
| Schedules | 2 | max_schedules=2 |
Schedule create and re-enable (command and Studio), pack publish that adds crons, and admin enable of a pack that declares crons | 409 license_limit_exceeded |
| Runners | 1 | max_runners=1 |
Runner registration and admin re-enable of a disabled runner; disabled runners are not counted | 409 license_limit_exceeded |
| Publishing | Private only | publish_public feature absent |
Pack and dashboard create, and every visibility change | 403 publish_public_required for public, unlisted and organization; private is always allowed |
| Branding and SaaS | No white_label, no saas |
Feature tokens | Licence verification | The licence is INVALID (402) if a Preview carries them |
| UI | Preview banner | tier=preview |
Hub, on every screen | — |
What is counted for each limit is defined in ADR-008 E8. Where each check sits in the code is listed in ADR-008 E9.
What every edition includes¶
Canon §0.1 and §3: "Tenant types, per-tenant metering, the platform-admin usage view and coupons stay available in Preview." Core owns these, so every edition has them.
| Capability | Notes |
|---|---|
| Tenants (home and team), membership, roles, tenant quotas | Tenant quotas may only tighten a licence limit (How limits are enforced) |
| Packs, pipelines, schedules, runners, dashboards | Preview caps the counts |
| Per-tenant usage metering and the platform-admin usage view | GET /api/v1/admin/usage/tenants (ADR-043). Core measures usage but never prices it. |
Coupons (account.create, tenant.join, tenant.create) |
Always on, with no flag (ADR-042); coupons are the only admission path. Redeeming one still passes the seat and team-tenant limits. |
| Private pack and dashboard catalog | Every edition. Non-private visibility needs publish_public. |
Core has no credits, wallets or prices. They belong to the SaaS add-on, which
needs the saas entitlement.
What Core Licensed excludes¶
Canon §3, confirmed by the founder (D-024 r4): Core Licensed "excludes the
saas entitlement (Huitzo's billing/credits/Stripe marketplace) and resale
rights."
| Excluded | Why | How it is enforced |
|---|---|---|
saas: credits, wallets, Stripe, the paid marketplace |
Running a paid multi-customer marketplace requires the partner agreement | The issuer refuses saas on licensed, and the verifier treats a licensed file carrying saas as INVALID (402). Without it, the SaaS extension never mounts. |
| Resale rights | Nobody resells Core Licensed. Under the referral model, a partner refers and implements; Huitzo licenses and invoices each end customer (ADR-008 D10). | Contractual. There is no licence claim for it. |
Hosted edition¶
The hosted edition runs Huitzo's own deployments under tier=enterprise, with
saas and white_label in features.
- No limit claims are set, so every limit is unlimited.
publish_publicis implied by the tier, so the hosted licence needs no reissue.- Founder confirmation is pending (FQ-8).
enterprise is not sold as a self-hosted edition.
Preview banner¶
On a Preview deployment, the Hub shows "Preview — not for production use":
- on every screen, including login and signup, which read the edition from the
unauthenticated
GET /api/v1/capabilities(license.edition); - as a non-dismissible status region (
role="status",aria-live="polite"); - never on Licensed or Hosted.
How limits are enforced¶
- One effective limit.
effective = min(licence ceiling, tenants.quotas, code default). - The code default applies only while a tenant quota is unset; a configured quota replaces it. The licence ceiling bounds both.
- An operator may lower a licence limit through tenant quotas, but never raise it.
- A quota update above the licence ceiling is refused with 422
quota_above_license_ceiling. - Refusals name the binding limit.
- The licence ceiling → 409
license_limit_exceeded, withdetails {key, limit, current, attempted_delta, scope}. - A tighter tenant quota → the existing 409
tenant_quota_exceeded. - REST and MCP return the same body.
- Pack ceiling and allowlist. A pack over the deployment
max_packsceiling is refused with 409license_limit_exceededat pack create and at operator enable. A pack that is not on the licence'slicensed_packsallowlist is refused with 403: that is an authorisation refusal, not a count. Concurrent creates from different tenants are serialized, so the ceiling is never overshot. - Publishing is an entitlement, not a count. Without
publish_public, a non-private visibility is refused with 403publish_public_required,details {visibility, allowed: ["private"]}. - 402 is different. 402
LICENSE_INVALIDmeans the licence file itself is missing, tampered, of an unknown tier or expired past grace (ADR-008 §4). A 409 means the licence is fine and the edition's limit has been reached. - A failed count refuses. If a limit applies and the usage count cannot
be read, the API refuses with 503
license_count_unavailablerather than letting the request through. Licensed and Hosted run no counts. The worker logs the error and proceeds. - Expiry and refresh.
- Expiry is evaluated on every request.
- The file is re-read from disk at most hourly.
HUITZO_LICENSE_CHECK_INTERVALis capped at 3600 seconds; a higher value fails at boot with a message naming the cap. POST /api/v1/license/reloadapplies a new file immediately and requiresadminorowner.- Executions are a soft cap. REST command execute, MCP
tools/calland pipeline execute checkmonthly_executionsbefore they start the run, without a lock, so runs already in flight together can pass the limit by a few. A refused request records no execution. Studio build stages and nestedctx.commandscalls are not separate executions and are not checked. - The worker checks too. It mounts
license.jsonread-only. It skips scheduled runs when the licence is INVALID or the execution ceiling has been reached. - No off switch. No environment variable or setting disables or raises a claim. A higher limit means a newly signed licence.
- Status.
GET /api/v1/license/statusreports the edition and, for each claim,{limit, current, source}. The deployment-widecurrentcounts are returned only to tenantadmins andowners; other members see the edition and the limits.
Moving between editions¶
Changing edition is a licence replacement:
- Obtain the new signed
license.json. - Drop it in place.
- Call
POST /api/v1/license/reload.
No data migration is involved. Moving from Preview to Licensed lifts every limit and removes the banner. License management covers install and renewal.
Retired tiers¶
The licence tiers that came before D-024 are retired with no alias (ADR-008 E14).
- The verifier accepts only
preview,licensedandenterprise. A file carrying any other tier is INVALID (402) from the release that ships CR55. - An outstanding field licence on a retired tier must be reissued as
previeworlicensedbefore that upgrade (FQ-9). - The earlier self-hosted price lists are retired too.
Anchor index¶
Implementation rows cite docs/reference/core-editions.md#<anchor>.
| Anchor | Implemented by |
|---|---|
#editions-at-a-glance |
CR55, CR56, CR64, CR66 |
#preview-limits |
CR55, CR58–CR63, CR66 |
#what-every-edition-includes |
CR66 |
#what-core-licensed-excludes |
CR55, CR56 |
#hosted-edition |
CR55, CR60 |
#preview-banner |
CR64, CR65 |
#how-limits-are-enforced |
CR58, CR62, CR64, CR65, CR66 |
#moving-between-editions |
CR64, CR66 |
#retired-tiers |
CR55, CR56, CR66 |