Packs and manifest
What is inside a compiled .hxpack file, what it is bound to, and the fields of embedded-pack-manifest.json, the file the typed SDKs are generated from.
The .hxpack container#
helixor-pack compile turns a playbook into a .hxpack file. helixor-pack download-pack fetches a catalog pack already sealed for your license. The file holds your compiled playbook, encrypted so that only your license can open it. The runtime decrypts it in memory and never writes the plaintext to disk.
Layout#
| Offset | Size | Field |
|---|---|---|
| 0 | 4 bytes | Magic: ASCII HXPK |
| 4 | 2 bytes | Format version, big-endian. 1 in 0.3.1. |
| 6 | 2 bytes | Length n of the pack ID, big-endian. |
| 8 | n bytes | pack_id, UTF-8, in clear text. |
| 8 + n | 12 bytes | AES-GCM nonce, random per compile. |
| 20 + n | 32 bytes | SHA-256 of the plaintext playbook, used as an integrity check after decryption. |
| 52 + n | rest | AES-256-GCM ciphertext of the compiled playbook (canonical JSON). |
The pack_id is readable without a key, so you can tell which pack a file holds. Everything else, including your rules and patterns, is encrypted.
What a pack is bound to#
- Your license's content key. The encryption key is derived from the
content_keyin your.hxlicfile. A pack compiled for one license cannot be opened with another. - The pack ID and license ID.
"<pack_id>:<license_id>"is authenticated as associated data. Changing the header'spack_id, or using a different license, makes decryption fail.
Renewing a license means recompiling
A renewed or reissued license has a new license_id and content key. Packs sealed for the old license fail to open with it, with PackUnsealError: Decryption failed: content key mismatch or corrupted pack envelope. Recompile or re-download every pack when you install a new license, and deploy the license and packs together. See Packs and Licensing.
Load-time checks#
When a pack is loaded (helixor-pack run or serve), the runtime checks, in order:
- The license file's signature and validity window (see License file).
- The file is at least 56 bytes, the magic is
HXPKand the format version is1. - The license allows the pack:
pack_idmatches alicensed_packspattern, or the license has thecustom_playbook_compilationfeature. - Decryption succeeds with the license's content key and associated data.
- The SHA-256 of the plaintext matches the header.
- Every solver the pack declares matches a
licensed_solverspattern. - Every rule, codon and hard rule in the pack is one the engine executes (see Playbook schema).
Any failure stops loading with an error, and no partial pack is used. The exceptions are listed in Errors.
Not in 0.3.1
Packs are encrypted and integrity-checked, but they are not signed by Helixor. Nothing in the file proves who compiled it; anyone holding your license can compile a pack for it. Signed packs are Planned.
embedded-pack-manifest.json#
The manifest describes a pack's typed interface: its state fields, rules and actions. Embedded SDK generation reads it to produce typed clients for Python, Java, C#, Rust and C; see the language capability matrix. The runtime does not read it when evaluating.
Top level#
| Field | Type | Value |
|---|---|---|
generator | string | helixor-sdk-gen |
generation_mode | string | embedded_decision |
packs | array | One entry per pack. |
Pack entry#
| Field | Type | Meaning |
|---|---|---|
pack_id | string | For example compliance.regulatory_pii_guard.v1 (the example pack). |
schema_version | string | helixor.pack_manifest.v1 |
domain_name | string | Used to name generated types, for example RegulatoryPiiGuard → RegulatoryPiiGuardClient. |
goal_id | string | The decision this pack answers: optimize_decision for the example pack. |
actions | string[] | The pack's actions. |
default_action | string | The action when no hard rule fires: permit_clean_payload for the example pack. |
floor | number | Confidence floor, for example 0.85. |
budget_max_latency_ms | number | Declared latency budget. 1.0 for the example pack. |
state_schema | object | Map of field name to {name, property_type, description, required, min_value, max_value, default}. property_type is for example boolean. |
hard_rules | array | {id, condition, verdict, action, factor, operator, threshold, threshold_str, is_boolean, factors}. verdict is refuse for every rule of the example pack, including the contact rule; action is the action the rule sets; operator is for example is_true. factors lists every state field the condition reads when there is more than one, and is empty otherwise. |
has_decision_head, decision_head | bool, object | null | false and null for the example pack. |
merkle_root | string | For an unsealed playbook, the same value as raw_pack_sha256. Despite the name, it is a SHA-256 content digest, not a Merkle root. See the note below. |
sealed_at, raw_pack_sha256, is_sealed | string | null, string, bool | raw_pack_sha256 is the SHA-256 of the pack's playbook as canonical JSON (keys sorted, compact separators). sealed_at is null and is_sealed is false in 0.3.1: manifests are not tied to a sealed pack yet. |
solvers | array | Solvers the pack uses. Empty for the example pack. |
codons | string[] | Codon codes only. Empty in the example pack's manifest. |
metadata | object | Free-form. |
Changed in 0.3.0
merkle_root is no longer a fixed placeholder. In the example pack's manifest it is d924f870…55ee, the SHA-256 of playbooks/regulatory_pii_guard.yaml as canonical JSON, the same value as raw_pack_sha256. It is an unkeyed digest: it identifies the playbook, but it is not a Merkle proof and not tamper evidence.
Known issue in 0.3.1
The contact rule's factor names one field. In RULE-GDPR-CONTACT-REDACT, factor is still has_email although the condition also covers phone and IP. Read factors, which lists all three.
Excerpt#
{
"generator": "helixor-sdk-gen",
"generation_mode": "embedded_decision",
"packs": [
{
"pack_id": "compliance.regulatory_pii_guard.v1",
"schema_version": "helixor.pack_manifest.v1",
"domain_name": "RegulatoryPiiGuard",
"goal_id": "optimize_decision",
"default_action": "permit_clean_payload",
"floor": 0.85,
"state_schema": {
"has_ssn": {"name": "has_ssn", "property_type": "boolean",
"description": "Evaluated by invariant RULE-GLBA-SSN-BLOCK", "required": true,
"min_value": null, "max_value": null, "default": null}
},
"hard_rules": [
{"id": "RULE-GLBA-SSN-BLOCK", "condition": "state.has_ssn == True",
"verdict": "refuse", "action": "block_glba_ssn_leakage",
"factor": "has_ssn", "operator": "is_true",
"threshold": null, "threshold_str": null, "is_boolean": true,
"factors": []}
],
"merkle_root": "d924f870db0232502ca7cdaee40f1403d99c7348cf25b40e5a90dd2b3d2555ee",
"raw_pack_sha256": "d924f870db0232502ca7cdaee40f1403d99c7348cf25b40e5a90dd2b3d2555ee",
"codons": [],
"is_sealed": false
}
]
}