Licensing and tiers
A license decides which packs and features your runtime may use. It is a signed file that the runtime verifies locally; no license server is contacted during evaluation.
Tiers#
| Tier | Unlocks | Limits |
|---|---|---|
| Community | The runtime with the built-in example pack: evaluate, stream, batch, serve. | Static rules; cannot compile custom rules. |
| Developer | Compiling your own playbooks into packs, with your own regex rules. (compile_custom_rule has no public engine to run on in 0.2.1: a loaded pack is sealed. It is removed in the next release. Put custom rules in the playbook.) | Per-minute decision limit, stated in the license. |
| Launch trial | As Developer. | Limits are tracked rather than enforced. |
| Enterprise | Licensed packs and solvers, all features. | None in the runtime. |
Studio (ontology viewer, data-source binding) and Expert pack authoring are portal capabilities that sit on top of these runtime tiers.
What a license contains#
A .hxlic file is JSON with five parts, signed as a whole:
| Part | Contents |
|---|---|
identity | Organization, organization ID, contact email. |
entitlements | Tier, plan, licensed pack and solver patterns (wildcards allowed), features, optional decision limits. |
validity | valid_from, valid_until, and a grace period (14 days by default). |
crypto | Key ID and the content key used to decrypt packs issued for this license. |
signature | Ed25519 signature over the canonical JSON of everything above. |
Field-level detail is in the license file reference.
How verification works#
- The runtime checks the Ed25519 signature against the Helixor license authority's public key built into the runtime. Any edit to the file invalidates it. 0.2.1 trusts a new authority key and rejects licenses signed with the previous one (
LicenseRevokedAuthorityError), so ask for a reissued license when you upgrade from 0.2.0, and compile or download your packs again with it. - It checks that the current time is after
valid_fromand beforevalid_untilplus the grace period. - Before unsealing a pack, it checks that the pack ID (and every solver the pack declares) matches the license's entitlements.
Any failure raises a typed error (LicenseSignatureInvalidError, LicenseExpiredError, LicenseNotYetValidError, LicenseEntitlementError) and nothing is evaluated. The runtime fails closed.
Where the runtime looks#
- An explicit path (
--licenseon the CLI). HELIXOR_LICENSE_FILE../helixor.lic(CLI only; the Python loader skips this step).~/.helixor/helixor.lic.
helixor-pack inspect-license ~/.helixor/helixor.lic
Protect the license file#
The license is a secret
It contains the content key that decrypts every pack issued for it. Anyone with your license and your packs can read your rules. Store it in a secret manager, mount it read-only, set file mode 600, and never bake it into an image, commit it, or pass it in a URL.
Expiry, grace and renewal#
- Monitor expiry. Results carry
license_status(EXPIRING_SOON,IN_GRACE_PERIOD), for the built-in pack and for compiled packs. Also checkhelixor-pack inspect-licenseon a schedule (see Reliability). Either signal should page someone. After the grace period the runtime refuses to load packs, and an engine that is already running a compiled pack raisesLicenseExpiredErroron everyevaluate()(new in 0.2.1; in 0.2.0 a running service kept working until its next start). Monitor expiry; do not wait for requests to fail. - Renewal issues a new license ID, and packs are bound to the license ID they were compiled for. Recompile or re-download your packs with the new license and deploy both together.
- Time comes from the host clock. Keep hosts synchronized to a trusted time source.
Rate limits on Developer licenses#
If the license sets max_decisions_per_minute, the runtime counts decisions in a 60-second window per engine instance. In strict mode it raises DeveloperQuotaExceededError once the limit is reached; in trial mode it records the overage and continues. Counters are per process and reset on restart.
Known issue in 0.2.1
Only the built-in example pack enforces these limits. A compiled pack loaded with load_pack(), run or serve does not.
Fixed in the next release: a compiled pack enforces max_decisions_per_minute and max_decisions_lifetime before each decision. Past a limit, a license whose overage_policy is TRUE_UP records the overage and keeps deciding; any other policy raises DeveloperQuotaExceededError. The license's validity window is also checked on every decision, as in 0.2.1.
Planned#
- Planned One-step activation:
helixor license activate. - Planned Binding a license to a host or cluster, revocation, and offline activation.
- Planned Wrapping the content key so that the license file alone cannot decrypt packs.