You’ve trained a model. It works on your machine. Now you need to get it to users.
The typical approach: upload the ONNX file somewhere, write a README explaining which files to download, what preprocessing is needed, and hope people follow the instructions.
We built something better: a packaging pipeline that produces self-contained, versioned, checksummed bundles that any Xybrid client can download and run automatically.
The Problem with Raw Model Distribution
A typical model “distribution” looks like this:
huggingface.co/org/my-model/
├── model.onnx (330MB)
├── vocab.json (1.2MB)
├── tokens.txt (45KB)
├── voices.bin (12MB)
├── config.json (2KB)
└── README.md ("download all files to the same directory") Problems:
- No single download — users must fetch 5 files individually
- No integrity checks — how do you know the files aren’t corrupted?
- No metadata — which files are required vs. optional?
- No versioning — updating one file can break compatibility
- No platform variants — Android might need a different quantization than desktop
The .xyb Bundle Format
Xybrid packages models as .xyb bundles: tar + zstd compression with a manifest.json:
kokoro-82m-universal.xyb (compressed)
├── manifest.json
├── model_metadata.json
├── model.onnx
├── tokens.txt
├── voices.bin
└── README.md The manifest declares contents, checksums, and compatibility:
{
"model_id": "kokoro-82m",
"variant": "universal",
"version": "1.0",
"files": [
{ "path": "model.onnx", "sha256": "a1b2c3...", "size": 345678901 },
{ "path": "tokens.txt", "sha256": "d4e5f6...", "size": 45123 },
{ "path": "voices.bin", "sha256": "g7h8i9...", "size": 12345678 }
],
"platforms": ["android", "ios", "macos", "linux", "windows"],
"format": "onnx",
"created_at": "2026-03-15T10:30:00Z"
} One download, verified integrity, self-describing contents.
The xybrid-pack Pipeline
xybrid-pack is our model packaging tool. It takes a model spec (YAML) and produces a published, registered bundle.
Step 1: Write a Model Spec
# model-specs/tts/kokoro-82m.yaml
version: "1.0"
status: ready
model:
id: "kokoro-82m"
family: "kokoro"
task: "text-to-speech"
description: "High-quality multi-voice TTS, 82M parameters"
license: "Apache-2.0"
parameters: 82000000
source:
type: huggingface
repo: "hexgrad/Kokoro-82M"
files:
- source: "kokoro-v0_19.onnx"
target: "model.onnx"
required: true
- source: "voices.bin"
required: true
- source: "tokens.txt"
required: true
execution:
template:
type: SimpleMode
model_file: "model.onnx"
preprocessing:
- type: Phonemize
backend: MisakiDictionary
tokens_file: "tokens.txt"
postprocessing:
- type: TTSAudioEncode
sample_rate: 24000
apply_postprocessing: true
variants:
- id: "universal"
default: true
format: onnx
platforms: [android, ios, macos, linux, windows]
publish:
hf_org: "xybrid-ai"
hf_repo: "kokoro-82m" The spec is the single source of truth for how this model is fetched, packaged, and published.
Step 2: Dry Run
just pack-pipeline-dry model-specs/tts/kokoro-82m.yaml This runs the full pipeline without publishing:
[1/7] Validate ✅ Spec is valid
[2/7] Fetch ✅ Downloaded 3 files from hexgrad/Kokoro-82M
[3/7] Generate ✅ Created model_metadata.json
[4/7] Build ✅ Built kokoro-82m-universal.xyb (156MB → 142MB compressed)
[5/7] Publish ⏭️ Skipped (dry run)
[6/7] Verify ⏭️ Skipped (dry run)
[7/7] Register ⏭️ Skipped (dry run) Step 3: Publish
HF_TOKEN=hf_xxx just pack-pipeline model-specs/tts/kokoro-82m.yaml The full pipeline:
- Validate — checks the spec against the schema
- Fetch — downloads source files from HuggingFace (or GitHub, or URL)
- Generate — creates
model_metadata.jsonfrom the spec’s execution config - Build — creates the
.xybbundle (tar + zstd) - Publish — uploads to HuggingFace under
xybrid-ai/kokoro-82m - Verify — re-downloads the bundle and checks SHA256 matches
- Register — adds the model to
registry.jsonwith download URL and checksum
Step 4: Verify
xybrid models list
# kokoro-82m text-to-speech 82M Apache-2.0
xybrid models info kokoro-82m
# ID: kokoro-82m
# Task: text-to-speech
# Variants: universal (onnx, 142MB)
# Platforms: android, ios, macos, linux, windows Users can now xybrid fetch kokoro-82m and the SDK handles everything.
The Registry
registry.json is a flat index of all published models:
{
"kokoro-82m": {
"model_id": "kokoro-82m",
"task": "text-to-speech",
"variants": {
"universal": {
"url": "https://huggingface.co/xybrid-ai/kokoro-82m/resolve/main/kokoro-82m-universal.xyb",
"sha256": "abc123...",
"size": 148897234,
"platforms": ["android", "ios", "macos", "linux", "windows"]
}
}
}
} Critical rule: registry.json is never edited manually. Only xybrid-pack register or xybrid-pack pipeline writes to it. This ensures the SHA256 always matches what HuggingFace actually serves.
The pipeline order is intentional: publish → verify → register. We upload first, then re-download and hash, then record. This catches upload corruption.
Platform Variants
Some models need different formats per platform. GGUF for LLMs, ONNX for everything else. Spec supports multiple variants:
variants:
- id: "universal"
default: true
format: onnx
platforms: [android, ios, macos, linux, windows]
- id: "q4-mobile"
format: onnx
quantization: q4
source_file: "model-q4.onnx"
platforms: [android, ios] The SDK resolves the best variant for the current platform automatically.
Why HuggingFace?
We host bundles on HuggingFace because:
- Free hosting for open-source models
- CDN — fast downloads globally
- Versioning via git LFS
- Community — users can browse models on the HF website
The .xyb format is HF-agnostic though. The registry just needs a URL and SHA256. You could host on S3, GCS, or your own CDN.
Model Spec as the Source of Truth
The model spec YAML drives everything:
- What to download (source files, source repo)
- How to package (which files, compression)
- How to run (execution template, pre/post processing)
- Where to publish (HF org, repo name)
- What platforms (variant → platform mapping)
Change the spec, re-run the pipeline, and the entire distribution updates consistently.
Quick Reference
# List all model specs
just pack-list
# Validate a spec
just pack-validate model-specs/tts/kokoro-82m.yaml
# Show spec details
just pack-info model-specs/tts/kokoro-82m.yaml
# Dry run (no publish)
just pack-pipeline-dry model-specs/tts/kokoro-82m.yaml
# Full pipeline
HF_TOKEN=hf_xxx just pack-pipeline model-specs/tts/kokoro-82m.yaml Explore the specs: github.com/xybrid-ai/xybrid
If you’re distributing ML models and tired of “download these 5 files and put them in this directory” — this pattern might help.
How do you distribute your models? Let us know in the comments.