License file
Your license file, helixor.hxlic, says who you are, which packs and features you may use, and until when. It also carries the key that opens your packs. This page lists every field and every check the runtime makes.
Treat the license file as a secret
crypto.content_key is the key your packs are encrypted with. Anyone who has your license file can decrypt your packs and compile new ones for it. Store the license like a private key: file mode 600, never in source control or a container image layer, and injected at deploy time from your secret store.
Format#
A UTF-8 JSON document. Every field except signature is covered by the signature, so changing any value, including whitespace inside a string, invalidates the file.
{
"license_version": "1.0",
"license_id": "hx_lic_org_example_1790000000",
"issued_at": "2026-09-01T12:00:00+00:00",
"identity": {
"organization": "Example Corp",
"org_id": "org_example",
"contact_email": "security@example.com"
},
"entitlements": {
"tier": "DEVELOPER",
"plan_name": "Developer",
"overage_policy": "TRUE_UP",
"licensed_packs": ["compliance.regulatory_pii_guard.v1", "custom.*"],
"licensed_solvers": ["solver.tcn.*"],
"features": ["in_process_streaming", "decision_as_a_function", "custom_playbook_compilation"],
"max_decisions_per_minute": null,
"max_decisions_lifetime": null
},
"validity": {
"valid_from": 1788264000.0,
"valid_until": 1819800000.0,
"grace_period_days": 14
},
"crypto": {
"key_id": "ep1_2026",
"content_key": "<secret>",
"algorithm": "Ed25519-AES-256-GCM"
},
"signature": "<128 hex characters>"
}
Fields#
| Field | Type | Meaning |
|---|---|---|
license_version | string | 1.0, or 1.1 for a license that carries a signed agreement block (new in 0.3.0). Any other value fails with LicenseFormatError. |
agreement | object | Only in 1.1, where it is required: {agreement_id, agreement_sha256, accepted_at}, the license agreement the licensee accepted before issuance. It is part of the signed fields. A 1.1 license without it, or a 1.0 license with it, fails with LicenseFormatError. |
license_id | string | Unique per issued license. It is bound into every pack sealed for this license (see Packs). |
issued_at | string | ISO 8601 UTC time of issue. |
identity.organization | string | Your organization's name. |
identity.org_id | string | Your organization ID. |
identity.contact_email | string | License contact. |
entitlements.tier | string | COMMUNITY, DEVELOPER or ENTERPRISE. |
entitlements.plan_name | string | Display name of your plan. |
entitlements.overage_policy | string | NO_OVERAGE_FEES, BLOCK_AT_CAP or TRUE_UP. Decides what a compiled pack does past a decision limit: TRUE_UP records the overage and continues; the others refuse the decision (changed in 0.3.0; informational in 0.2.1). |
entitlements.licensed_packs | string[] | Glob patterns (*, ?) of pack IDs you may compile and load, for example custom.*. |
entitlements.licensed_solvers | string[] | Glob patterns of solver IDs your packs may use. If the field is absent, it defaults by tier: ENTERPRISE gets ["*"], DEVELOPER gets ["solver.tcn.*"] (["solver.tcn.*", "bin_packing.*"] before 0.3.1), others get ["solver.tcn.basic"]. A pattern match alone does not let a pack run a solver engine in your process; see solver.embedded. |
entitlements.features | string[] | See Features. |
entitlements.max_decisions_per_minute, max_decisions_lifetime | int | null | Enforced for compiled packs too, per engine instance (changed in 0.3.0; in 0.2.1 only the built-in pack enforced them) (see Licensing). |
validity.valid_from, valid_until | number | POSIX timestamps in seconds. |
validity.grace_period_days | int | Days the license keeps working after valid_until. Default 14. |
crypto.key_id | string | Identifies the content key generation. |
crypto.content_key | string | Secret. The pack encryption key is SHA-256 of this string. |
crypto.algorithm | string | Ed25519-AES-256-GCM. |
agreement | object | Planned Present in 1.1 licenses only. The license agreement version you accepted. See Agreement acceptance. |
signature | string | Hex Ed25519 signature from the Helixor license authority. |
Agreement acceptance#
Planned A license is issued only for an account with a recorded acceptance of the current Helixor Software License Agreement. Licenses that record that acceptance use "license_version": "1.1" and carry a top-level agreement object:
{
"license_version": "1.1",
"license_id": "hx_lic_org_example_1790000000",
"issued_at": "2026-09-01T12:00:00+00:00",
"agreement": {
"agreement_id": "helixor-software-license-agreement-v2",
"agreement_sha256": "ab2e1e2a67e5b99165cc6f4f8c9713e2e8e525dba893797f4f94cbe50a399807",
"accepted_at": "2026-09-01T11:58:40+00:00"
},
"identity": { "...": "..." },
"entitlements": { "...": "..." },
"validity": { "...": "..." },
"crypto": { "...": "..." },
"signature": "<128 hex characters>"
}
| Field | Type | Meaning |
|---|---|---|
agreement.agreement_id | string | Identifier of the agreement version you accepted, for example helixor-software-license-agreement-v2. |
agreement.agreement_sha256 | string | SHA-256 of the exact agreement text you accepted: 64 lowercase hex characters. It matches the digest published on the agreement page. |
agreement.accepted_at | string | ISO 8601 UTC time your acceptance was recorded. |
The agreement object is signed like every other field: it is part of the canonical bytes the Ed25519 signature covers, so changing it invalidates the license.
1.0licenses still verify. A license without anagreementobject and with"license_version": "1.0"is accepted as before.- A
1.1license must carry the agreement. A1.1license without theagreementobject is rejected as invalid. - A new agreement version needs a new acceptance. When Helixor publishes a new version, you accept it before a license is issued or renewed. Your current license keeps the version you accepted.
Verification#
Every time a license is loaded (by helixor-pack, when a pack is unsealed, or in POST /v1/decision with license_data), the runtime checks, in order:
- Signature present
A missing or empty
signaturefails withLicenseSignatureInvalidError. - Signature valid
The runtime rebuilds the canonical form: every field except
signature, serialized as JSON with sorted keys and no spaces. It verifies the Ed25519 signature against the Helixor authority public key built into the runtime. Any change fails withLicenseSignatureInvalidError. A license signed by a revoked authority key fails with its subclassLicenseRevokedAuthorityError: since 0.3.0 the runtime rejects the authority key that 0.2.0 and early 0.2.1 builds trusted, so a license signed with it must be reissued, and packs compiled for it compiled again. - Not before
If the current time is before
valid_from, it fails withLicenseNotYetValidError. - Not expired
If the current time is after
valid_untilplusgrace_period_days× 86,400 seconds, it fails withLicenseExpiredError. Betweenvalid_untiland that point the license still works, andinspect-licensereportsIN GRACE PERIOD.
Verification is offline. It uses the built-in public key and your system clock; no call is made to Helixor. Keep host clocks synchronized.
Pack and solver entitlements are checked separately, when you compile or load a pack. See Packs and manifest.
helixor-pack inspect-license ~/.helixor/helixor.hxlic
Tiers#
| Tier | How you get it | In 0.3.1 |
|---|---|---|
| Community | Built into the runtime. No file. | The runtime with the built-in example pack: evaluate, stream, serve. compile_custom_rule is removed from every engine and raises RemovedCapabilityError. |
| Developer | Issued by Helixor when your request is approved; request one on the sign-up page. You receive a .hxlic file. The CLI cannot mint or sign a license. | Compile and run packs your licensed_packs allow. |
| Enterprise | Commercial agreement. | Same format, wider entitlements. From 0.3.1 it is the only tier that carries solver.embedded. |
Installing a license file is currently manual: save it as ~/.helixor/helixor.hxlic with mode 600 (see Licensing). A helixor license activate command is Planned.
Features#
| Feature | Effect in 0.3.1 |
|---|---|
custom_playbook_compilation | Lets you compile and load a pack whose pack_id is not in licensed_packs. |
in_process_streaming | Recorded; not checked by the runtime. |
decision_as_a_function | Recorded; not checked by the runtime. |
solver.embedded | New in 0.3.1: required to run a Helixor solver engine in your process, that is any solver ID other than the free solver.tcn.* solvers (for example vrp.*, rostering.*, bin_packing.*, gpu.*). Granted only with the Enterprise tier; a license of another tier cannot carry it. Without it, compiling or loading a pack that declares such a solver, or an embedded solve, fails with LicenseEntitlementError, and the message names the missing capability. The hosted solver service does not use this feature. |
Where the runtime looks for the license#
The helixor-pack commands, HelixorEngine.load_pack (see the Python reference), the decision service and HelixorSolver use one search order and take the first file found:
- The explicit path:
--license, orlicense_file=. $HELIXOR_LICENSE_FILE../helixor.hxlicin the working directory.~/.helixor/helixor.hxlic.
The name helixor.lic that runtime 0.3.0 used is still accepted in the two directories. Both names in one directory raise LicenseLocationAmbiguousError (LICENSE_LOCATION_AMBIGUOUS). None found raises LicenseNotFoundError (LICENSE_NOT_FOUND, also a FileNotFoundError), whose message lists the six locations checked and the sign-up URL; the CLI prints it and exits with status 1. An explicit path or $HELIXOR_LICENSE_FILE naming a missing file raises the same error without searching further. HelixorEngine() searches nothing: it always uses the built-in Community license.
See Configuration for the environment variables.