Catalog & Package System
How the package ecosystem works — format specification, dependency resolution, multi-registry architecture, signing, and air-gapped distribution.
System Overview
.hpkg Package Format
A .hpkg file is a deterministic tar.gz archive containing:
package-name-1.0.0.hpkg (tar.gz)
├── MANIFEST.toml ← always first (streamable extraction)
├── SIGNATURE.ed25519 ← ed25519 signature (optional)
├── README.md ← documentation (optional)
├── LICENSE ← license text (optional)
├── sbom.json ← software bill of materials (optional)
└── payload/ ← package contents (sorted recursively)
├── COMPETENCY.md ← markdown with YAML frontmatter (the artifact)
├── references/ ← optional reference docs
├── assets/ ← optional templates, schemas
└── ...
Markdown-First Payload Format
The .md file IS the package artifact. YAML frontmatter carries structured metadata, the markdown body carries the prompt/instructions. One .md file per package type:
| Package Type | Payload File | Required Frontmatter |
|---|---|---|
| Skill | SKILL.md | name, description |
| Competency | COMPETENCY.md | id, name, description, category, skills |
| Tool | TOOL.md | name, description, runtime, entry |
| AgentTemplate | AGENT.md | name, description |
| WorkflowTemplate | WORKFLOW.md | name, description |
| Integration | INTEGRATION.md | name, description, transport |
Example SKILL.md:
---
name: rust-expert
description: "Rust programming expert"
tags: [rust, systems, async]
runtime: prompt-only
---
# Rust Expert
You are an expert Rust developer...
The platform translates .md payloads to kernel-native format (e.g., skill.toml) only at the install boundary — never in the archive, never in the registry.
Determinism
Archives are reproducible:
- Files sorted lexicographically by path
- Fixed timestamp:
2024-01-01 00:00:00 UTC(hardcoded) - Same input always produces same output (byte-identical)
Size Limit
Maximum package size for upload: 50 MB (enforced client-side before publish).
MANIFEST.toml Schema
[package]
type = "competency" # tool | skill | competency | agent-template | workflow-template | integration
name = "claims-intake" # kebab-case, 3-64 chars
version = "1.2.0" # semver 2.0
description = "FNOL processing" # ≤500 chars
license = "MIT" # SPDX identifier
min_platform_version = "0.5.0" # minimum Hoziron version
[package.author]
name = "Insurance Corp"
email = "platform@company.com" # optional
url = "https://company.com" # optional
[package.metadata]
repository = "https://github.com/..." # optional, must be http(s)
homepage = "https://..." # optional
keywords = ["insurance", "claims"] # max 20, each ≤50 chars
categories = ["insurance/claims"] # taxonomy paths
regions = ["us", "uk"] # deployment regions (empty = all)
[dependencies]
document-ocr = "^1.0" # semver constraint
postgresql-connector = ">=2.0.0, <3.0.0"
[optional-dependencies]
email-skill = "^1.2"
[peer-dependencies]
guidewire-claimcenter = "^3.0" # must be installed separately
[standards]
mcp_compatible = true # exposes MCP tool interface
openapi_spec = "payload/openapi.yaml" # only for tool/skill/integration
[signing]
publisher_key = "ed25519:mK3xR7..." # populated by `package sign`
content_hash = "sha256:a1b2c3d4..." # populated by `package build`
Package Types
| Type | Contains | Use Case |
|---|---|---|
tool | Single tool implementation | Python/WASM/Node/Shell script |
skill | Tool bundle + config | Multi-tool skill package |
competency | COMPETENCY.toml + prompts + knowledge | Agent behavior definition |
agent-template | Full agent config | Pre-configured agent |
workflow-template | Workflow JSON + agent refs | Multi-agent pipeline |
integration | MCP server implementation | External service connector |
Validation Rules
| Field | Rule |
|---|---|
name | Lowercase, digits, hyphens only. No leading/trailing/consecutive hyphens. 3–64 chars. |
version | Valid semver (MAJOR.MINOR.PATCH, optional pre-release) |
description | Non-empty, ≤500 characters |
license | Non-empty SPDX identifier |
dependencies | Each constraint must be a valid semver range |
standards.openapi_spec | Only valid for tool, skill, integration types |
standards.mcp_compatible | Only valid for tool, skill, integration types |
Cryptographic Signing
Key Generation
hoziron package keygen
- Algorithm: Ed25519
- Private key: 32 random bytes (OS RNG), stored base64-encoded at
$HOZIRON_HOME/keys/default.key - Public key format:
ed25519:<base64-of-32-bytes> - File permissions: 0600 (owner read/write only)
Signing Flow
Verification Flow
Content Hash (Merkle Root)
The content hash is a deterministic SHA-256 over the payload:
For each file in payload/ (sorted lexicographically by relative path):
entry_hash = SHA-256(relative_path_bytes + file_content_bytes)
content_hash = SHA-256(entry_hash_1 + entry_hash_2 + ... + entry_hash_N)
Dependency Resolution
Algorithm
Semver Constraints
| Syntax | Meaning | Example |
|---|---|---|
* | Any version | Matches everything |
1.2.3 | Caret (same as ^1.2.3) | >=1.2.3, <2.0.0 |
^1.2.3 | Compatible updates | >=1.2.3, <2.0.0 |
~1.2.3 | Patch-level only | >=1.2.3, <1.3.0 |
=1.2.3 | Exact match | Only 1.2.3 |
>=1.0.0, <2.0.0 | Range | Compound constraint |
>1.0.0 | Greater than | Comparison |
Conflict Detection
When two packages require incompatible versions of the same dependency:
Package A requires C@^1.0.0 (resolves to 1.x)
Package B requires C@^2.0.0 (resolves to 2.x)
→ No single version of C satisfies both → VersionConflict error
The error reports which packages imposed which constraints:
version conflict for package 'pkg-c':
- pkg-a requires pkg-c@^1.0.0
- pkg-b requires pkg-c@^2.0.0
Cycle Detection
DFS with visited/stack sets. When a back-edge is found, the full cycle path is reported:
dependency cycle detected: pkg-a → pkg-b → pkg-c → pkg-a
Diamond Dependencies
Handled correctly — the resolver selects the highest version that satisfies all constraints:
A requires C@^1.0.0
B requires C@^1.5.0
Available: C@1.0.0, C@1.5.0, C@1.8.0
→ Resolves C to 1.8.0 (satisfies both ^1.0.0 and ^1.5.0)
Lockfile (packages.lock)
Ensures reproducible installs by recording exact resolved versions:
[metadata]
generated_at = "2026-06-04T10:00:00Z"
resolver_version = 1
[[package]]
name = "claims-intake"
version = "1.2.0"
source = "https://catalog.hoziron.com"
content_hash = "sha256:a1b2c3d4..."
dependencies = ["document-ocr", "postgresql-connector"]
[[package]]
name = "document-ocr"
version = "1.4.0"
source = "https://catalog.hoziron.com"
content_hash = "sha256:e5f6a7b8..."
dependencies = []
Behavior
- If lockfile exists and all constraints are satisfied → use locked versions (no network)
- If
--force-resolveor lockfile is stale → re-resolve and rewrite - Supports merge (incremental installs) and remove (uninstalls)
Multi-Registry Architecture
Configuration
[catalog]
verify_signatures = true
packages_dir = "packages"
cache_ttl_secs = 3600
default_publish_registry = "internal"
[[catalog.registries]]
name = "internal"
url = "https://packages.internal.company.com"
priority = 1
auth_token_env = "INTERNAL_REGISTRY_TOKEN"
enabled = true
[[catalog.registries]]
name = "hoziron"
url = "https://catalog.hoziron.com"
priority = 100
enabled = true
[catalog.scopes]
internal = "https://packages.internal.company.com"
hoziron = "https://catalog.hoziron.com"
Scoped Packages
@scope/name syntax pins a package to a specific registry:
# This ONLY queries the registry mapped to "internal"
hoziron catalog install @internal/proprietary-claims-tool
Scope rules:
- Scope names: lowercase alphanumeric + hyphens
- Must be mapped in
[catalog.scopes]→ error if unknown scope - Bypasses priority-order resolution entirely
- Prevents name collisions between registries
Publish Target Resolution
When publishing, the target registry is resolved:
- Explicit
--registry <name>flag - Scope-based routing (if package name has
@scope/prefix) default_publish_registryconfig field- Error: "no publish target specified"
Collections
Curated, self-contained sets of packages for quick onboarding.
COLLECTION.toml
name = "insurance-starter"
display_name = "Insurance Starter Kit"
description = "Everything needed to start processing claims"
version = "1.0.0"
license = "MIT"
featured = true
tags = ["insurance", "claims", "starter"]
icon = "shield-check"
[author]
name = "Hoziron Team"
[[packages]]
name = "claims-intake"
version = "1.2.0"
package_type = "competency"
is_dependency = false
[[packages]]
name = "document-ocr"
version = "1.4.0"
package_type = "tool"
is_dependency = true
[[packages]]
name = "postgresql-connector"
version = "2.1.0"
package_type = "tool"
is_dependency = true
Closure Invariant
A collection must include ALL transitive dependencies of every package it contains. Installing a collection never requires fetching packages outside the collection.
Validated by validate_collection_closure() — reports which package is missing which dependency.
Installation
hoziron collection install insurance-starter
# Installs all packages, skipping any already installed
Collections are additive — users can install multiple, overlapping packages are deduplicated.
Air-Gapped Transfer
For environments without network access:
- Export creates a plain tar of the installed package directory
- Import extracts, reads the manifest, and records installation
- No network required on the air-gapped side
- Content hash verified locally after import
Taxonomy
Packages are categorized using a hierarchical taxonomy:
Domain → Subdomain → Function → Variants
Structure (Insurance domain example)
insurance/
├── claims/
│ ├── fnol (variants: auto, property, liability, workers-comp, marine, aviation)
│ ├── adjudication
│ ├── subrogation
│ ├── reserving
│ ├── litigation
│ ├── catastrophe (variants: weather, wildfire, earthquake, flood)
│ └── total-loss
├── fraud/
│ ├── detection (variants: claims, application, premium)
│ ├── investigation
│ ├── prevention
│ └── network-analysis
├── underwriting/
│ ├── risk-assessment
│ ├── rating
│ ├── quoting
│ └── submission-intake
├── policy-admin/
│ ├── issuance
│ ├── endorsements
│ ├── renewals
│ └── cancellations
└── ... (actuarial, distribution, compliance, billing, reinsurance, LOB)
Additional Domains
- Banking & Financial Services — lending, payments, KYC/AML
- Healthcare — clinical, revenue cycle
- Document Processing — extraction, generation
- General — productivity, development, data
Taxonomy Versioning (ADR-004)
- Append-only (nodes added, never removed)
- Semver versioned (patch = description changes, minor = new nodes, major = structural)
- Compiled into the binary as offline fallback
- Registry serves canonical version at runtime
- Unknown nodes gracefully degrade (display raw ID, never hard error)