Publishing Packages

What you'll accomplish: Create, build, lint, and publish packages to a registry with automatic provenance.

Package workflow

Every package published to a Hoziron registry receives a cryptographic provenance signature automatically. The registry signs on behalf of the authenticated publisher — no local signing keys needed.

A carrier operator publishes only to their own registry — their private, self-hosted registry, or the registry bundled with their own Hoziron instance. catalog.hoziron.com, Hoziron's shared public registry, only accepts publishes from Hoziron staff; a carrier's API key will not have publish rights there. Everything below assumes you're publishing to a registry you control.

1. Scaffold

hoziron-cli package init --type competency --name my-package

Package types (PackageTypeArg): skill, competency, agent-template, workflow-template, integration. There is no tool package type — a skill is the tool-bundling unit; see the glossary.

For a REST/SOAP carrier integration package (ADR-057), scaffold the JSON adapter contract + Starlark transform starter files instead of mcp.json:

hoziron-cli package init --type integration --name my-carrier-adapter --contract rest-soap

Current REST/SOAP dispatch coverage (Issue #587): only kind = "single" ops actually dispatch today — two_phase, async_callback, and SOAP are rejected with a clear error at both install time and call time, not silently mishandled. Only auth.kind values "none" and "bearer" are implemented; oauth2_client_credentials, basic, and api_key are rejected at connect time. Build against this contract, but don't assume two-phase/SOAP ops or non-bearer auth work yet in this build.

2. Write the MANIFEST.toml

[package]
type = "competency"
name = "my-package"
version = "1.0.0"
description = "What this package does"
license = "MIT"
min_platform_version = "0.5.0"

[package.author]
name = "Your Company"
email = "platform@company.com"

[package.metadata]
keywords = ["insurance", "claims"]
categories = ["insurance"]

[dependencies]
postgresql-connector = "^2.0"

3. Build

hoziron-cli package build ./my-package/

Produces a deterministic .hpkg archive (tar.gz, reproducible builds). Computes a SHA-256 content hash of the payload directory for integrity verification.

4. Lint

hoziron-cli package lint ./my-package/

Validates name, version, description, license, dependencies, and taxonomy categories.

5. Publish

hoziron-cli package publish ./my-package/ --notes "Initial release: claim intake + document OCR"

hoziron-cli package publish runs the full pipeline: lint → build → upload, from a source directory (not a pre-built .hpkg — for that, use hoziron-cli catalog publish <path-to.hpkg> instead, which skips straight to upload). --notes is required — a short, human-readable summary of what changed, like a GitHub Release note. Omit it and you'll be prompted interactively, unless --json is set, in which case it must be supplied on the command line. --skip-lint skips the lint step; --registry <name> overrides scope-based routing and the default publish registry.

Publishing a fork of a package that originated on another registry, and recording the lineage:

hoziron-cli package publish ./my-package/ --notes "Fork: added ZA-specific validation" \
  --derived-from-registry https://catalog.hoziron.com \
  --derived-from-name claims-intake \
  --derived-from-version 1.2.0

The origin registry's content hash for that version is looked up live, not taken on trust from the local flags alone.

On upload, the registry:

  1. Authenticates the publisher via their API key
  2. Validates the manifest (semantics, taxonomy, license compatibility)
  3. Computes the archive hash (SHA-256 of the .hpkg tarball)
  4. Signs the provenance payload: name:version:publisher_id:archive_hash:timestamp
  5. Stores the archive and provenance metadata

Publish target resolution:

  1. Explicit --registry <name> flag
  2. Scope-based routing (@scope/name prefix)
  3. default_publish_registry config field

Provenance

Every published package includes:

FieldDescription
publisher_idThe authenticated identity (API key name)
publisher_roleRBAC role at time of publish
archive_hashSHA-256 of the .hpkg tarball
registry_signatureEd25519 signature over the provenance payload
registry_key_idWhich registry key signed it
signed_atTimestamp of signature

This guarantees: every package in the catalog has a verifiable chain of custody back to an authenticated publisher.

Verification

Verify package integrity locally (content hash):

hoziron-cli package verify ./my-package/

Verify provenance (publisher identity + registry signature):

hoziron-cli package verify ./my-package/ --provenance

The --provenance flag contacts the registry to fetch the signature metadata and the registry's public key, then verifies the Ed25519 signature locally.

Viewing the full lineage chain

hoziron-cli catalog provenance claims-intake

Shows the package's version history on the current registry (publisher, signed-at timestamp, release notes), and if a version carries a derived_from fork snapshot, walks the chain back toward the origin registry — fetching its live history when reachable, falling back to the recorded snapshot when it isn't.

Authentication

Publishing always requires authentication (registry:publish / package:publish — Admin, Operator, or Developer). Bootstrap your first API key on a fresh registry with no keys yet — see Private registries § Bootstrapping a fresh private registry for the hoziron-cli catalog registry bootstrap wrapper, or call the registry's POST /keys directly:

curl -X POST http://registry:4210/keys \
  -H 'Content-Type: application/json' \
  -d '{"name": "ci-publisher", "role": "developer"}'

The token is resolved in this order, per registry: (1) an inline token field in config, (2) the HOZIRON_REGISTRY_TOKEN environment variable (a global fallback, handy for CI where every registry call should use the same credential), (3) auth_token_env — the vault first, then the named env var from config. For a per-registry credential, prefer auth_token_env (see Private registries); reserve HOZIRON_REGISTRY_TOKEN for CI pipelines that only ever talk to one registry.

Size limit

Maximum package size: 50 MB (enforced by the registry on upload).

Registry signing key

The registry's public key is available at:

curl http://registry:4210/signing-key
# → {"key_id": "registry-abcd1234", "public_key": "ed25519:...", "algorithm": "ed25519"}

Clients use this to verify provenance signatures offline after downloading a package.

Next steps


Related: