Pack Manifest
Pack Manifest¶
Every Intelligence Pack requires a huitzo.yaml manifest file that defines metadata, permissions, and configuration. This file is the single source of truth for all pack configuration — pyproject.toml is auto-generated from it by the CLI.
pyproject.toml Generation: You should never edit
pyproject.tomldirectly. Runhuitzo pack syncto regenerate it fromhuitzo.yaml, or it will be regenerated automatically onhuitzo pack buildandhuitzo pack dev.Manifest Files: Intelligence Packs use
huitzo.yaml. Dashboards usehuitzo-dashboard.yaml. See Dashboard Manifest for dashboard configuration.Optional fullstack scaffolding. When a Pack ships alongside a Dashboard as one app,
huitzo project initcan scaffold both into a single directory. That is a CLI convenience for fullstack work — it does not change the Pack manifest below or anything about howhuitzo pack ...behaves. Standalone Packs created withhuitzo pack newremain fully supported. See Intelligence Projects if fullstack scaffolding is useful for your work.
Quick Example¶
pack:
name: "my-company-analytics"
namespace: "analytics"
version: "1.0.0"
description: "Business analytics and reporting tools"
visibility: "organization"
author: "My Company"
license: "proprietary"
commands:
- name: "generate-report"
description: "Generate analytics report"
entry_point: "my_company_analytics.commands.report:generate_report"
permissions: ["llm:complete"]
timeout: 300
queue: "long"
- name: "quick-summary"
description: "Get quick data summary"
entry_point: "my_company_analytics.commands.summary:quick_summary"
permissions: []
timeout: 30
queue: "fast"
data_types:
- name: "reports"
description: "Generated report data"
ttl_days: 90
- name: "cache"
description: "Temporary calculation cache"
ttl_days: 7
services:
llm:
required: true
models: ["gpt-4o-mini", "claude-sonnet"]
email:
required: false
telegram:
required: false
secrets:
user_required:
- name: "ANALYTICS_API_KEY"
description: "Your analytics provider API key"
help_url: "https://analytics.example.com/api-keys"
user_optional:
- name: "PREMIUM_API_KEY"
description: "Optional premium tier API key"
metadata:
homepage: "https://example.com/analytics-pack"
repository: "https://github.com/example/analytics-pack"
documentation: "https://docs.example.com/analytics"
keywords: ["analytics", "reporting", "business-intelligence"]
category: "business"
File Structure¶
The manifest uses YAML format with these main sections:
| Section | Required | Description |
|---|---|---|
pack |
✅ Yes | Core pack metadata |
deployment |
❌ No | Deployment capability flags |
resources |
❌ No | Resource requirements |
embedded_models |
❌ No | Embedded AI models (edge) |
commands |
✅ Yes | Command definitions |
data_types |
❌ No | Storage data type definitions |
services |
❌ No | External service dependencies |
mcp_servers |
❌ No | MCP server configurations |
ssh_targets |
❌ No | SSH target access declarations |
secrets |
❌ No | User-provided secrets configuration |
metadata |
❌ No | Additional metadata |
Pack Section¶
The pack section defines core metadata about your Intelligence Pack.
Required Fields¶
pack:
name: "my-pack" # Unique identifier (kebab-case)
namespace: "mypack" # Command namespace (lowercase, no dashes)
version: "1.0.0" # Semantic version
description: "Short description of what the pack does"
Optional Fields¶
pack:
# ... required fields ...
visibility: "organization" # public | unlisted | organization | private
author: "Your Name" # Pack author or company
author_email: "[email protected]" # Author email
license: "MIT" # License identifier
min_sdk_version: "2.0.0" # Minimum SDK version required
min_platform_version: "2.0.0" # Minimum platform version required
# Python dependencies beyond huitzo-sdk (which is always included)
dependencies:
- "pydantic>=2.0"
- "requests>=2.31"
# Dev-only dependencies
dev_dependencies:
- "pytest>=9.0"
- "ruff>=0.14"
# Pricing (marketplace only)
pricing:
model: "one-time" # one-time | subscription | usage
price: 49 # USD (0 = free)
Field Reference¶
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
name |
string | ✅ | - | Unique pack identifier. Must be kebab-case. |
namespace |
string | ✅ | - | Command namespace. Lowercase, no special chars. |
version |
string | ✅ | - | Semantic version (MAJOR.MINOR.PATCH) |
description |
string | ✅ | - | Short description (max 200 chars) |
visibility |
enum | ❌ | organization |
Access control level |
author |
string | ❌ | - | Author name or organization |
license |
string | ❌ | proprietary |
License identifier |
min_sdk_version |
string | ❌ | 2.0.0 |
Minimum SDK version |
min_platform_version |
string | ❌ | 2.0.0 |
Minimum platform version |
Visibility Levels¶
| Level | Description | Discovery | Access |
|---|---|---|---|
public |
Available to all tenants | Searchable, browsable | No access grant required |
unlisted |
Hidden from discovery | Not searchable | Requires pack link or ID |
organization |
Requires explicit access grant | Org members only | Must have PluginAccessGrant |
private |
Only available to owner organization | Owner only | Owner tenant only |
Unlisted Visibility¶
Unlisted packs are not discoverable through search or the pack marketplace, but can be accessed and installed using a direct link or pack ID. This is ideal for:
- Agency model: Build custom packs for specific clients without exposing to other customers
- Beta testing: Share with select users before public release
- Custom enterprise: Deliver tailored solutions to specific organizations
Sharing an unlisted pack:
# Get shareable pack ID and link
$ huitzo pack share
Pack ID: pack_a1b2c3d4e5f6
Direct URL: https://app.huitzo.com/packs/pack_a1b2c3d4e5f6
# Client installs via pack ID
$ huitzo pack install pack_a1b2c3d4e5f6
Important: The pack ID is the only way to access unlisted packs. Treat it like a private invitation link.
Best Practice: Use organization (the default) for internal commercial packs. Use unlisted for client-specific deliverables. Use public only for utility/demo packs.
Pricing Section¶
The pricing section configures marketplace pricing for your pack. This section only applies to Cloud (SaaS) deployments; self-hosted packs cannot be sold on the marketplace.
Basic Pricing¶
pack:
pricing:
model: "one-time"
price: 49
Full Pricing Configuration¶
pack:
pricing:
model: "one-time" # one-time | subscription | usage
price: 49 # USD (0 = free, minimum $5 for paid)
currency: "usd" # Only USD initially
# Subscription-only fields (v0.1+)
billing_period: "monthly" # monthly | annual
trial_days: 14 # Free trial period
# Usage-based fields (v2.5+)
per_execution: 0.05 # USD per command execution
monthly_minimum: 5 # Optional minimum monthly charge
Pricing Models¶
One-Time Purchase (v0.0)¶
Customer pays once for permanent access to the pack:
pack:
pricing:
model: "one-time"
price: 49
- Best for: Tools, utilities, standalone solutions
- Customer gets: Lifetime access to current version
- Updates: Major versions may require repurchase (at developer's discretion)
Subscription (v0.1+)¶
Recurring billing for ongoing access:
pack:
pricing:
model: "subscription"
price: 19
billing_period: "monthly"
trial_days: 14
| Field | Type | Default | Description |
|---|---|---|---|
billing_period |
enum | monthly |
monthly or annual |
trial_days |
int | 0 |
Free trial duration (0-30 days) |
- Best for: Packs with ongoing value, regular updates
- Customer gets: Access while subscribed
- Cancellation: Loses access at end of billing period
Usage-Based (v2.5+)¶
Pay-per-execution pricing:
pack:
pricing:
model: "usage"
per_execution: 0.05
monthly_minimum: 5
| Field | Type | Default | Description |
|---|---|---|---|
per_execution |
float | required | USD per command execution |
monthly_minimum |
float | 0 |
Minimum monthly charge |
- Best for: API-style packs, high-volume processing
- Customer pays: Based on actual usage
- Billing: Monthly invoice based on execution count
Free Packs¶
Set price to 0 for free distribution:
pack:
pricing:
model: "one-time"
price: 0
Free packs: - No Stripe account required - No platform fees - Appear in marketplace with "Free" badge - Great for community building, demos, open-source tools
Pricing Field Reference¶
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
model |
enum | ❌ | one-time |
Pricing model |
price |
int | ❌ | 0 |
Price in USD (cents for < $1) |
currency |
string | ❌ | usd |
Currency code (USD only initially) |
billing_period |
enum | ❌ | monthly |
For subscriptions only |
trial_days |
int | ❌ | 0 |
Free trial days (subscriptions only) |
per_execution |
float | ❌ | - | Per-execution price (usage only) |
monthly_minimum |
float | ❌ | 0 |
Minimum monthly charge (usage only) |
Pricing Validation Rules¶
| Rule | Requirement |
|---|---|
| Minimum paid price | $5 USD (prevents race-to-bottom) |
| Maximum price | $10,000 USD |
| Trial days | 0-30 days for subscriptions |
| Per-execution | $0.001 - $100 USD |
Revenue Split¶
See Billing Reference for complete revenue split details.
| Annual Revenue | Platform Fee | Developer Keeps |
|---|---|---|
| $0 - $5,000 | 0% | 100% |
| $5,001 - $50,000 | 20% | 80% |
| $50,001+ | 10% | 90% |
Pricing Examples¶
Example 1: Professional Tool ($49 one-time)
pack:
name: "claims-processor"
pricing:
model: "one-time"
price: 49
Revenue per sale: $49 × 80% = $39.20 (after $5K threshold)
Example 2: SaaS Tool ($19/month subscription)
pack:
name: "analytics-dashboard"
pricing:
model: "subscription"
price: 19
billing_period: "monthly"
trial_days: 7
Monthly revenue per subscriber: $19 × 80% = $15.20
Example 3: API Tool ($0.05/execution usage)
pack:
name: "document-parser"
pricing:
model: "usage"
per_execution: 0.05
monthly_minimum: 5
Revenue for 1,000 executions: $50 × 80% = $40.00
Changing Pricing¶
Pricing changes take effect for new purchases only:
- Existing one-time buyers: Keep their access
- Existing subscribers: Grandfathered at old price until they cancel
- New customers: See new pricing
# Update pricing
$ huitzo pack update-pricing --price 59
# Publish with new pricing
$ huitzo pack publish
Deployment Section¶
The deployment section declares which deployment modes your pack supports. This enables the platform to validate pack compatibility with different deployment targets.
Basic Configuration¶
deployment:
cloud_capable: true # Can run in Huitzo cloud (default: true)
self_hosted_capable: true # Can run on customer infrastructure (default: true)
edge_capable: false # Can run on edge devices (default: false)
offline_capable: false # Can operate without network (default: false)
requires_internet: true # Requires internet for functionality (default: true)
Field Reference¶
| Field | Type | Default | Description |
|---|---|---|---|
cloud_capable |
bool | true |
Pack can run in Huitzo-managed cloud |
self_hosted_capable |
bool | true |
Pack can run on customer infrastructure |
edge_capable |
bool | false |
Pack can run on edge devices (Year 3+) |
offline_capable |
bool | false |
Pack can operate without network (Year 3+) |
requires_internet |
bool | true |
Pack requires internet connectivity |
Deployment Mode Compatibility¶
# Example: Pack that only works in cloud (requires cloud LLM APIs)
deployment:
cloud_capable: true
self_hosted_capable: false
requires_internet: true
# Example: Pack that works everywhere (no external dependencies)
deployment:
cloud_capable: true
self_hosted_capable: true
edge_capable: true # Future
offline_capable: true # Future
requires_internet: false
# Example: Pack designed for regulated environments
deployment:
cloud_capable: false # No cloud for compliance
self_hosted_capable: true
edge_capable: true # Future: air-gapped support
offline_capable: true # Future
requires_internet: false
Validation Behavior¶
Year 1: All fields are validated on pack registration. Only cloud_capable and self_hosted_capable affect deployment routing.
Year 3+: edge_capable and offline_capable will enable edge deployment routing when edge mode is implemented.
Resources Section¶
The resources section declares pack-level resource requirements. This helps the platform optimize execution and validate deployment feasibility.
Basic Configuration¶
resources:
min_memory_mb: 256 # Minimum memory to execute
recommended_memory_mb: 512 # Recommended for performance
requires_gpu: false # GPU required for execution
Field Reference¶
| Field | Type | Default | Description |
|---|---|---|---|
min_memory_mb |
int | 256 |
Minimum memory in MB |
recommended_memory_mb |
int | 512 |
Recommended memory in MB |
requires_gpu |
bool | false |
GPU required for execution |
Future Fields (Validated but Inactive)¶
These fields are validated but do not affect behavior until edge mode is implemented:
resources:
# Future: Hardware acceleration preferences
# accelerator_preference: "cuda|tensorrt|onnx|cpu"
# max_latency_ms: 100
# power_budget_watts: 50
Embedded Models Section (Future)¶
The embedded_models section declares AI models that should be bundled with the pack for edge deployment. This section is validated but inactive until edge mode is implemented (Year 3+).
Configuration (Year 3+)¶
embedded_models:
- id: "custom-ner"
format: "onnx" # onnx | pytorch | tensorflow
size_mb: 128
quantization: "int8" # none | fp16 | int8
description: "Custom NER model for entity extraction"
- id: "summarizer"
format: "onnx"
size_mb: 256
quantization: "fp16"
description: "Text summarization model"
Field Reference (Future)¶
| Field | Type | Description |
|---|---|---|
id |
string | Unique model identifier |
format |
enum | Model format: onnx, pytorch, tensorflow |
size_mb |
int | Model size in MB |
quantization |
enum | Quantization level: none, fp16, int8 |
description |
string | Human-readable description |
Current Status: This section is accepted and validated for forward compatibility, but models are not bundled until edge mode is implemented.
Commands Section¶
The commands section lists all commands provided by the pack with their configurations.
Basic Command¶
commands:
- name: "hello"
description: "Say hello"
entry_point: "my_pack.commands.hello:hello_world"
Full Command Configuration¶
commands:
- name: "analyze-data"
description: "Analyze dataset and generate insights"
entry_point: "my_pack.commands.analyze:analyze_data"
permissions:
- "llm:complete"
- "storage:write"
timeout: 300
queue: "long"
retries: 3
output_format: "json"
deprecated: false
deprecation_message: null
Command Field Reference¶
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
name |
string | ✅ | - | Command name (kebab-case) |
description |
string | ✅ | - | Human-readable description |
entry_point |
string | ✅ | - | Python entry point (module.path:function) |
permissions |
list | ❌ | [] |
Required service permissions |
timeout |
int | ❌ | 60 |
Max execution time (seconds) |
queue |
enum | ❌ | auto |
Task queue: fast, medium, long, auto |
retries |
int | ❌ | 3 |
Max retry attempts |
output_format |
enum | ❌ | json |
Default output format |
schedule |
string | ❌ | - | Cron expression for scheduled execution (e.g., "0 8 * * 1-5") |
deprecated |
bool | ❌ | false |
Mark command as deprecated |
deprecation_message |
string | ❌ | - | Message shown for deprecated commands |
resources |
object | ❌ | - | Resource requirements (see below) |
Scheduled Commands¶
Commands can declare a schedule field with a standard cron expression. The platform's Celery Beat scheduler checks every minute and dispatches commands whose schedule is due.
# pseudocode — scheduled command configuration
commands:
- name: "daily-research"
description: "Fetch and summarize daily news"
timeout: 300
queue: "medium"
schedule: "0 8 * * 1-5" # Mon-Fri at 8:00 AM UTC
When a scheduled command runs, ctx.cron.is_scheduled is True and ctx.cron.schedule contains the cron expression. On-demand invocations of the same command have ctx.cron.is_scheduled set to False.
Cron Expression Format: Standard five-field format: minute hour day-of-month month day-of-week. Examples:
| Expression | Description |
|---|---|
* * * * * |
Every minute |
0 8 * * 1-5 |
Weekdays at 8:00 AM |
0 0 1,15 * * |
1st and 15th of month at midnight |
*/15 * * * * |
Every 15 minutes |
Requirements: Commands with a schedule field must also declare cron in services.
Resources Configuration¶
Specify resource requirements for command execution:
commands:
- name: "heavy-analysis"
description: "Analyze large datasets"
resources:
tier: "standard" # standard | compute | gpu
timeout_seconds: 300 # Max execution time
memory_hint: "256MB" # Helps platform optimize scheduling
cpu_hint: "0.5" # Relative CPU weight (0.1 - 2.0)
| Field | Type | Default | Description |
|---|---|---|---|
tier |
enum | standard |
Worker tier: standard, compute, gpu |
timeout_seconds |
int | 60 |
Max execution time in seconds |
memory_hint |
string | - | Expected memory usage (e.g., "256MB", "1GB") |
cpu_hint |
string | - | Relative CPU intensity (0.1 - 2.0) |
Tier Reference¶
| Tier | Memory | CPU | Status | Use Case |
|---|---|---|---|---|
standard |
512 MB | 0.5 cores | Production | Standard commands, API calls |
compute |
2 GB | 1 core | Future | Data processing, file operations |
gpu |
8 GB + GPU | 2 cores | Future | ML inference, image processing |
Current Status (Year 1):
- Only standard tier is available
- compute and gpu tiers are planned for future releases
- The tier field is accepted for forward compatibility but does not currently affect worker routing
- All commands run on standard tier workers (512MB RAM, 0.5 CPU)
- Future versions will use these hints for intelligent worker pool assignment
Queue Options¶
| Queue | Max Duration | Use Case |
|---|---|---|
fast |
30 seconds | Quick lookups, simple operations |
medium |
5 minutes | Standard operations (default) |
long |
8 hours | LLM calls, complex processing, batch jobs |
auto |
Varies | Automatically determined (default) |
Automatic Queue Assignment¶
When queue: auto (or omitted), Huitzo automatically determines the optimal queue based on execution statistics collected during development.
How It Works¶
┌─────────────────────────────────────────────────────────────────┐
│ 1. During `huitzo pack dev` and `huitzo pack test` │
│ → Execution times are collected per command │
│ │
│ 2. During `huitzo pack build` │
│ → Statistics computed: p50, p95, max execution time │
│ → Embedded in pack metadata │
│ │
│ 3. At runtime (production) │
│ → Platform uses 2-5x multiplier on p95 time │
│ → Assigns to appropriate queue │
└─────────────────────────────────────────────────────────────────┘
Queue Selection Algorithm¶
def select_queue(p95_time: float) -> str:
estimated_max = p95_time * 3 # 3x safety multiplier
if estimated_max <= 5:
return "fast"
elif estimated_max <= 60:
return "medium"
else:
return "long"
Viewing Statistics¶
After running huitzo pack test, view collected statistics:
$ huitzo stats
Command Statistics (from 47 test runs):
COMMAND P50 P95 MAX QUEUE (auto)
get-user 0.02s 0.05s 0.08s fast
analyze-data 2.10s 4.50s 6.20s medium
generate-report 12.30s 45.00s 62.00s long
Overriding Auto-Assignment¶
To force a specific queue, set it explicitly:
commands:
- name: "my-command"
queue: "long" # Force long queue regardless of stats
When to override: - Commands with highly variable execution time - Commands that call external APIs with unpredictable latency - Commands that must run in a specific queue for operational reasons
Permissions¶
Permissions control which platform services a command can access:
permissions:
- "llm:complete" # LLM completion API
- "llm:stream" # LLM streaming API
- "email:send" # Send emails
- "telegram:send" # Send Telegram messages
- "storage:read" # Read from storage
- "storage:write" # Write to storage
- "http:request" # Make external HTTP requests
- "files:read" # Read uploaded files
- "files:write" # Write files
- "mcp:call" # Call MCP server tools
- "ssh:execute" # Execute commands on SSH targets
Best Practice: Request only the permissions your command needs. Users see required permissions before installing.
Data Types Section¶
Define the types of data your pack stores. This helps with data management, TTL policies, and documentation.
data_types:
- name: "conversations"
description: "Chat conversation history"
ttl_days: 90
scope: "user"
- name: "settings"
description: "User preferences and settings"
ttl_days: null # Never expires
scope: "user"
- name: "cache"
description: "Temporary calculation cache"
ttl_days: 1
scope: "tenant"
Data Type Field Reference¶
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
name |
string | ✅ | - | Data type identifier |
description |
string | ❌ | - | Human-readable description |
ttl_days |
int/null | ❌ | null |
Days until auto-deletion (null = never) |
scope |
enum | ❌ | user |
Data isolation scope |
Scope Options¶
| Scope | Description | Use Case |
|---|---|---|
user |
Per-user data, fully isolated | User preferences, history |
tenant |
Shared within organization | Team settings, shared caches |
pack |
Shared across all users of pack | Global pack configuration |
Services Section¶
Declare external service dependencies and configuration requirements.
services:
llm:
required: true
models:
- "gpt-4o-mini"
- "gpt-4o"
- "claude-sonnet"
default_model: "gpt-4o-mini"
email:
required: false
telegram:
required: false
http:
required: true
allowed_domains:
- "api.example.com"
- "data.example.org"
LLM Service Configuration¶
services:
llm:
required: true
models: # Supported models
- "gpt-4o-mini"
- "gpt-4o"
- "claude-sonnet"
- "claude-haiku"
default_model: "gpt-4o-mini" # Default if not specified
max_tokens: 4096 # Max tokens per request
temperature: 0.7 # Default temperature
HTTP Service Configuration¶
services:
http:
required: true
allowed_domains: # Whitelist of allowed domains
- "api.example.com"
- "*.example.org" # Wildcard supported
timeout: 60 # Default timeout
max_redirects: 5 # Max redirect follows
Service Reference¶
| Service | Description | Configuration Options |
|---|---|---|
llm |
Large Language Model API | models, default_model, max_tokens, temperature |
email |
Email sending service | (none) |
telegram |
Telegram bot integration | (none) |
http |
External HTTP requests | allowed_domains, timeout, max_redirects |
cron |
Scheduled task execution | Per-command schedule field (cron expression) |
files |
File upload/download | max_size_mb, allowed_types |
db |
Postgres / MySQL connectors | (none — paired with top-level db_integrations allowlist) |
Database Service Configuration¶
Declare services: db: {} to receive a ctx.db client at runtime. Combine with
a top-level db_integrations list to allow only specific tenant-registered
postgres / mysql Integration rows by name (omit the list to allow any
tenant-visible DB integration):
# huitzo.yaml — pseudocode
services:
db: {}
db_integrations:
- "primary"
- "warehouse"
The actual connection details (host, port, credentials, TLS, allow_ddl,
row_limit) live on the Integration row in the registry — packs never see or
edit them. See Integrations Reference → Database Integration
for the runtime API and security limits.
MCP Servers Section¶
The mcp_servers section declares external MCP (Model Context Protocol) servers that your pack can connect to. MCP provides a standardized way to integrate with external tools and services.
Key Concept: Huitzo consumes MCP servers—it does not expose itself as an MCP server. MCP tools become callable via
ctx.mcpin your commands. No LLM required—this is pure protocol translation.
Basic Configuration (STDIO)¶
For MCP servers that run as local subprocesses:
mcp_servers:
- name: github
type: stdio
command: ["uvx", "mcp-server-github"]
env:
GITHUB_TOKEN: "${secrets.GITHUB_TOKEN}"
HTTP+SSE Configuration (Remote)¶
For MCP servers accessible over HTTP:
mcp_servers:
- name: postgres-remote
type: http
url: "https://mcp.example.com/postgres"
headers:
Authorization: "Bearer ${secrets.MCP_API_KEY}"
timeout: 30
Transport Types¶
| Type | Description | Use Case |
|---|---|---|
stdio |
Local subprocess, communicates via stdin/stdout | CLI tools, local servers |
http |
Remote HTTP endpoint with SSE streaming | Hosted MCP servers, enterprise gateways |
STDIO Transport Field Reference¶
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
name |
string | ✅ | — | Unique server identifier (kebab-case) |
type |
string | ✅ | — | Must be "stdio" |
command |
list[str] | ✅ | — | Command and arguments to spawn |
env |
dict | ❌ | {} |
Environment variables (supports ${secrets.NAME}) |
working_dir |
string | ❌ | Pack root | Working directory for subprocess |
HTTP+SSE Transport Field Reference¶
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
name |
string | ✅ | — | Unique server identifier (kebab-case) |
type |
string | ✅ | — | Must be "http" |
url |
string | ✅ | — | Server endpoint URL (https required in production) |
headers |
dict | ❌ | {} |
HTTP headers (supports ${secrets.NAME}) |
timeout |
int | ❌ | 30 |
Request timeout in seconds |
Secret Interpolation¶
Use ${secrets.NAME} syntax to reference user secrets:
mcp_servers:
- name: github
type: stdio
command: ["uvx", "mcp-server-github"]
env:
GITHUB_TOKEN: "${secrets.GITHUB_TOKEN}" # From user secrets
GITHUB_ENTERPRISE_URL: "${secrets.GH_URL}" # Optional enterprise URL
secrets:
user_required:
- name: "GITHUB_TOKEN"
description: "Your GitHub personal access token"
help_url: "https://github.com/settings/tokens"
user_optional:
- name: "GH_URL"
description: "GitHub Enterprise URL (optional)"
Multiple Servers Example¶
mcp_servers:
# Local GitHub MCP server
- name: github
type: stdio
command: ["uvx", "mcp-server-github"]
env:
GITHUB_TOKEN: "${secrets.GITHUB_TOKEN}"
# Local PostgreSQL MCP server
- name: postgres
type: stdio
command: ["uvx", "mcp-server-postgres"]
env:
DATABASE_URL: "${secrets.DATABASE_URL}"
# Remote Slack MCP server
- name: slack
type: http
url: "https://mcp.company.com/slack"
headers:
Authorization: "Bearer ${secrets.SLACK_MCP_TOKEN}"
Usage in Commands¶
Access MCP tools via ctx.mcp:
from huitzo_sdk import command, Context
@command("create-issue", namespace="devtools")
async def create_issue(args: Args, ctx: Context) -> dict:
"""Create a GitHub issue using MCP."""
result = await ctx.mcp.call(
server="github",
tool="create_issue",
arguments={
"owner": args.owner,
"repo": args.repo,
"title": args.title
}
)
return {"issue_number": result["number"]}
Validation Rules¶
| Rule | Requirement |
|---|---|
| Server name | Unique within pack, kebab-case, 1-64 characters |
| Command array | Non-empty list of strings (STDIO only) |
| URL format | Valid HTTPS URL (HTTP only in development) |
| Timeout | 1-300 seconds |
Related Documentation¶
- MCP Architecture — How MCP integrates with Huitzo
- MCP SDK Reference —
ctx.mcpAPI documentation - MCP Pack Integration Guide — Step-by-step tutorial
SSH Targets Section¶
The ssh_targets section declares which user-registered SSH targets your pack can access via ctx.ssh.run(). This enables packs to orchestrate tasks on user-owned hardware (GPU clusters, custom servers, on-premise machines).
Allow All Targets¶
ssh_targets:
allowed:
- "*" # Allow all user-registered targets
Restrict to Specific Targets¶
ssh_targets:
allowed:
- "gpu-cluster"
- "preprocessing-server"
Field Reference¶
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
allowed |
list[str] | ✅ | [] |
Target names the pack can access. ["*"] = allow all. |
Behavior¶
- If
ssh_targetsis omitted, the pack cannot usectx.ssh(equivalent toallowed: []) - If
allowedcontains"*", the pack can access all targets the user has registered - If
allowedcontains specific names, only those targets are accessible - Target names must match the
namefield used when registering the target
Example: ML Training Pack¶
pack:
name: "ml-trainer"
namespace: "ml"
version: "1.0.0"
description: "Train and deploy ML models on your GPU clusters"
commands:
- name: "train"
description: "Train a model on remote GPU"
permissions: ["storage:write"]
timeout: 3600
queue: "long"
ssh_targets:
allowed:
- "*" # User decides which GPUs to use
Security Model¶
Pack manifests create an allowlist that restricts which SSH targets a pack can access:
- User registers targets (e.g., "gpu-cluster", "web-server", "db-server")
- Pack declares
ssh_targets.allowed: ["gpu-cluster"] - At runtime,
ctx.ssh.run("gpu-cluster", ...)succeeds ctx.ssh.run("web-server", ...)raisesSSHError— not in pack's allowlist
This ensures packs can only access infrastructure the user has explicitly registered and the pack has explicitly declared.
See SSH Integration and SSH Targets Guide for complete documentation.
Secrets Section¶
The secrets section declares external API keys and credentials that users must provide to use your pack. This enables the three-tier secrets management system.
Three-Tier Secrets Model¶
| Tier | Owner | Scope | Access Method |
|---|---|---|---|
| Platform | Admin | All packs | ctx.env.require("OPENAI_API_KEY") |
| Pack | Developer | Single pack | Pack configuration |
| User | End User | User + Pack | ctx.secrets.require("API_KEY") |
User Secrets Configuration¶
secrets:
user_required:
- name: "FINANCIAL_API_KEY"
description: "Your API key from massive.com"
help_url: "https://massive.com/developers/api-keys"
- name: "TRADING_API_SECRET"
description: "Trading platform secret key"
help_url: "https://trading.example.com/api"
user_optional:
- name: "PREMIUM_API_KEY"
description: "Optional premium tier API key for enhanced features"
Secret Field Reference¶
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | ✅ | Environment variable name (UPPER_SNAKE_CASE) |
description |
string | ✅ | Human-readable description shown to users |
help_url |
string | ❌ | Link to documentation for obtaining the key |
Required vs Optional Secrets¶
Required Secrets (user_required):
- Pack will not install until user provides these secrets
- Accessing a missing required secret raises SecretsError
- Users are prompted during pack installation
Optional Secrets (user_optional):
- Pack installs without these secrets
- Use ctx.secrets.get() with fallback behavior
- Enable premium or enhanced features
Accessing User Secrets in Commands¶
from huitzo_sdk import command, Context
from huitzo_sdk.errors import SecretsError
@command("fetch-data", namespace="finance")
async def fetch_data(args: Args, ctx: Context) -> dict:
# Required secret - raises SecretsError if missing
api_key = ctx.secrets.require("FINANCIAL_API_KEY")
# Optional secret - returns None if missing
premium_key = ctx.secrets.get("PREMIUM_API_KEY")
# Check existence
if ctx.secrets.exists("PREMIUM_API_KEY"):
# Use premium features
pass
# Call external API with user's key
response = await ctx.http.get(
"https://api.massive.com/data",
headers={"Authorization": f"Bearer {api_key}"}
)
return response
Security Model¶
User secrets are: - Encrypted at rest using platform encryption keys - Scoped to user + pack - other packs cannot access - Never logged - masked in all logs and error messages - Rotatable - users can update secrets without reinstalling
Validation Rules¶
| Rule | Requirement |
|---|---|
| Name format | UPPER_SNAKE_CASE, alphanumeric + underscore |
| Name length | 1-64 characters |
| Description length | 10-500 characters |
| help_url | Valid URL (https recommended) |
Best Practices¶
- Clear descriptions - Tell users exactly what key they need and why
- Include help_url - Link to the provider's API key page
- Minimize required secrets - Only require what's truly necessary
- Document usage - Explain which commands use which secrets
See Secrets Management Reference for complete documentation.
Metadata Section¶
Additional metadata for discoverability and documentation.
metadata:
homepage: "https://example.com/my-pack"
repository: "https://github.com/example/my-pack"
documentation: "https://docs.example.com/my-pack"
changelog: "https://github.com/example/my-pack/blob/main/CHANGELOG.md"
keywords:
- "analytics"
- "reporting"
- "data"
category: "business"
icon: "chart-bar" # Icon name (Lucide icons)
screenshots:
- "https://example.com/screenshot1.png"
- "https://example.com/screenshot2.png"
Category Options¶
| Category | Description |
|---|---|
business |
Business tools, CRM, analytics |
developer |
Development tools, utilities |
productivity |
Personal productivity, notes, tasks |
finance |
Financial analysis, accounting |
marketing |
Marketing automation, campaigns |
communication |
Email, chat, notifications |
data |
Data processing, ETL, analysis |
ai |
AI/ML tools, model integration |
other |
Uncategorized |
Validation¶
The CLI validates your manifest before building:
huitzo pack validate
Validation Rules¶
- Required fields - All required fields must be present
- Naming conventions - Names follow kebab-case/lowercase rules
- Version format - Version is valid semver
- Command uniqueness - No duplicate command names
- Permission validity - All permissions are valid
- Service references - Services referenced in permissions exist
- Dashboard config - If enabled, subdomain is unique
Common Validation Errors¶
❌ Error: pack.name must be kebab-case
Found: "MyPack"
Expected: "my-pack"
❌ Error: commands[0].timeout exceeds queue limit
Command: "slow-task" (timeout: 120)
Queue: "fast" (max: 5s)
Fix: Change queue to "medium" or "long"
❌ Error: Unknown permission "database:query"
Command: "fetch-data"
Valid permissions: llm:complete, email:send, ...
Complete Example¶
Here's a complete manifest for a real-world pack:
# huitzo.yaml - Financial Intelligence Pack
pack:
name: "plutus-financial"
namespace: "plutus"
version: "2.1.0"
description: "Portfolio analysis and financial reporting tools"
visibility: "organization"
author: "Huitzo Inc."
license: "proprietary"
min_sdk_version: "2.0.0"
# Deployment capabilities
deployment:
cloud_capable: true
self_hosted_capable: true
edge_capable: false # Future
offline_capable: false # Future
requires_internet: true # Needs market data APIs
# Pack-level resource requirements
resources:
min_memory_mb: 256
recommended_memory_mb: 512
requires_gpu: false
# Embedded models (future edge deployment)
embedded_models: []
commands:
- name: "analyze-portfolio"
description: "Analyze investment portfolio performance"
permissions:
- "llm:complete"
- "http:request"
- "storage:write"
timeout: 300
queue: "long"
retries: 2
resources:
tier: "standard"
timeout_seconds: 300
- name: "generate-report"
description: "Generate PDF investment report"
permissions:
- "llm:complete"
- "email:send"
- "storage:read"
- "files:write"
timeout: 600
queue: "long"
resources:
tier: "standard"
timeout_seconds: 600
- name: "get-quote"
description: "Get current stock quote"
permissions:
- "http:request"
timeout: 10
queue: "fast"
resources:
tier: "standard"
timeout_seconds: 10
- name: "list-holdings"
description: "List portfolio holdings"
permissions:
- "storage:read"
timeout: 30
queue: "fast"
resources:
tier: "standard"
timeout_seconds: 30
data_types:
- name: "portfolios"
description: "User portfolio data"
ttl_days: null
scope: "user"
- name: "reports"
description: "Generated reports"
ttl_days: 365
scope: "user"
- name: "market_cache"
description: "Market data cache"
ttl_days: 1
scope: "tenant"
services:
llm:
required: true
models: ["gpt-4o-mini", "gpt-4o"]
default_model: "gpt-4o-mini"
email:
required: true
http:
required: true
allowed_domains:
- "api.polygon.io"
- "api.alpaca.markets"
secrets:
user_required:
- name: "POLYGON_API_KEY"
description: "Your Polygon.io API key for market data"
help_url: "https://polygon.io/dashboard/api-keys"
user_optional:
- name: "ALPACA_API_KEY"
description: "Alpaca Markets API key for trading data"
help_url: "https://alpaca.markets/docs/api-references/broker-api/"
metadata:
homepage: "https://huitzo.com/packs/plutus"
documentation: "https://docs.huitzo.ai/packs/plutus"
keywords: ["finance", "portfolio", "investment", "analysis"]
category: "finance"
icon: "trending-up"
Proposed Sections¶
The following manifest sections are architecturally specified but not yet supported by the runtime. They are included here for forward reference.
Integrations Section (Proposed)¶
Declare reusable service integrations provided by this pack. See Component Model.
# pseudocode — proposed integrations section
integrations:
- name: "salesforce"
class: "my_pack.integrations.salesforce:SalesforceIntegration"
required_secrets:
- name: "SALESFORCE_KEY"
description: "Your Salesforce API key"
help_url: "https://developer.salesforce.com/docs"
allowed_domains:
- "*.salesforce.com"
shared_secrets: [] # secrets shared with consuming packs (empty = none)
Dependencies Section (Proposed)¶
Declare dependencies on other integration packs. Resolved at huitzo pack install time via topological sort. Circular dependencies are rejected. See Component Model — Pack Dependency Resolution.
# pseudocode — proposed dependencies section
dependencies:
- pack: "@acme/salesforce-connector"
version: ">=1.0.0"
integrity: "sha256:abc123..." # content hash pinning
File Mounts Section (Proposed)¶
Declare mountable file system drivers for VFS. See Virtual File System.
# pseudocode — proposed file_mounts section
file_mounts:
- mount_point: "/mnt/s3-reports"
driver: "s3"
config:
bucket: "company-reports"
region: "us-east-1"
required_secrets:
- "AWS_ACCESS_KEY_ID"
- "AWS_SECRET_ACCESS_KEY"
Events Section (Proposed)¶
Declare events published and subscribed to by this pack. See Event Bus.
# pseudocode — proposed events section
events:
publishes:
- type: "invoice.received"
description: "Emitted when an invoice is detected in email"
payload_schema:
sender: "string"
amount: "number"
subscribes:
- type: "invoice.received"
handler: "my_pack.handlers:on_invoice_received"
max_concurrency: 5
Pipelines Section (Proposed)¶
Declare command pipelines. See Piping Protocol.
# pseudocode — proposed pipelines section
pipelines:
invoice-processor:
description: "Extract invoice data from email"
timeout: 300
stages:
- command: "source:scan-inbox"
config:
filter: "subject:invoice"
- command: "action:extract-fields"
- command: "dest:save-to-sheet"
Config Section (Proposed)¶
Declare pack-level configuration defaults for the hierarchical config cascade. See Component Model — Hierarchical Configuration.
# pseudocode — proposed config section
config:
http.timeout: 60
http.max_retries: 5
llm.default_model: "gpt-4o"
commands:
- name: "heavy-report"
config:
http.timeout: 300 # command-level override
Related Documentation¶
- Intelligence Projects – Optional scaffolding for fullstack Pack + Dashboard apps
- Your First Pack – Create a pack from scratch
- SDK Overview – SDK reference
- Components Reference – HuitzoIntegration and component types
- Architecture Overview – Deployment modes and system design
- Component Model – UVM-inspired component taxonomy
- Virtual File System – Mountable file drivers
- Piping Protocol – Command composition via pipes
- Event Bus – Async pack-to-pack communication
- Self-Hosting – Deploy your own instance
- Future Requirements – Edge and compliance roadmap
- CLI Reference – CLI commands for pack development