Pack
What the three files of a pack hold, what never goes in them, where packs live, and the gold packs to copy when you start a new one.
A Harness Pack is what a runtime installs: one directory, one id and three files that declare a capability's commands, rules and teaching. The pack declares and the runtime executes, so a pack holds nothing that depends on who runs it and moves unchanged between runtimes. Start by copying the gold pack closest to your capability and changing its id, bin, commands and Binding mode.
Layout#
packs/<id>/
surface.json manifest — id, adapter, Binding mode, named commands, optional triggers
policy.json rules — one row per command and per trigger
HARNESS.md Skill — one screen of teachingThe layout is flat, though a runtime may nest directories for its own
operations without the pack knowing. The three names are exact and
case-sensitive, and install fails when one is missing or when Policy does not
cover every command and trigger. surface.json is the manifest's legacy
filename, kept for wire stability, and has nothing to do with UI surfaces.
| File | Piece | You fill |
|---|---|---|
surface.json | Manifest | Identity (id), how it runs (kind, cliBin, wrap), the credentials it needs (binding.mode), the verbs map of named commands with their CLI forms, and triggers only for an upstream system that pushes, so most packs have none |
policy.json | Policy | For every command and trigger: operation (read | write), privacy.land, and confirm on writes that must not auto-fire |
HARNESS.md | Skill | Lead, hot-path table, examples, anti-patterns |
Optional fields#
Add these to the manifest only when the capability needs them.
| Field | When |
|---|---|
stimulusTriggers[] | The upstream system pushes real events |
binding.family | Related packs should share one login |
wrap.agent_env | The wrapped bin needs environment defaults it already understands |
plane: "control" | Admin or operator CLI, which makes it a second pack |
transports: [... "mcp"] | A runtime will export MCP tools from this pack |
pass_through | Bootstrap only; promote to named commands |
openapi | kind: openapi |
skill_url | A hosted copy of the Skill |
What stays out#
- Secrets, a
secrets/directory or a.envfile. The pack names a Binding mode and the runtime's hub mints the credential. - User allow-lists, roles, tenant ids and connector types, which belong to runtime grants and Principal scope.
- A server, since a pack describes an existing CLI or API.
- A whole binary. A pack names 5 to 12 commands, and
allowedSubcommandspass-through is a bootstrap to promote. - UI widget catalogs or inbox chrome. A UI host invokes commands from outside.
- A
families/directory, since family is a manifest field. - Extra Markdown for scale. A capability scales by adding packs, never by growing one.
Where packs live#
| Location | Purpose |
|---|---|
Your repository, packs/<id>/ | Source of record; one living repo per pack_id |
$HCP_HOME/packs/<id>/ | Installed copy in the runtime store |
$HCP_HOME/skills/<id>/SKILL.md | Skill copy written on activate |
Gold packs#
| Pack | Class | Location |
|---|---|---|
transit | Public read | hcp/examples/packs/transit |
ledger | Gated write | hcp/examples/packs/ledger |
echo | Composite, sandbox-safe | hcp/examples/packs/echo |
hcp-cli | The bootstrap pack (composite, local Binding) | hcp/packs/hcp-cli |
Identity#
A pack's identity is its manifest id, which is also pack_id on the wire and
surface_id in the Policy file, and lint checks that all three agree. One
pack_id maps to one maintained repository. A command is identified by
pack_id + verb_key, a pair that stays stable across the CLI face, the JSON
face, the Skill, the host tool catalog and any MCP export.
Planes#
plane | Meaning | Binding |
|---|---|---|
runtime (default) | End-user capability | public · user · local |
control | Admin, fleet, operator | mothership, in a second pack |
A runtime never silently activates a control-plane pack on an end-user Principal, and a domain CLI with both an admin face and a user face is two packs.
Versioning#
An HCP/1.x pack declares protocol affinity hcp: 1 in its metadata and leaves
content versioning to its repository. A manifest version field with registry
resolution is on the roadmap.
Example#
The gold public-read pack, in full:
{
"id": "transit",
"name": "Transit",
"kind": "wrap",
"cliBin": "transit",
"plane": "runtime",
"transports": ["cli", "json_exec"],
"binding": { "mode": "public", "hub": "public" },
"wrap": { "bin": "../../bins/transit.mjs", "argv_template": ["{{verb}}"] },
"verbs": {
"find": { "description": "Resolve a place.", "cli": ["transit", "find", "{{query}}"] },
"plan": { "description": "Time-first options between two places.", "cli": ["transit", "plan", "{{from}}", "{{to}}"] },
"show": { "description": "Expand one option.", "cli": ["transit", "show", "{{optionId}}"] },
"status": { "description": "Live observation.", "cli": ["transit", "status"] },
"follow": { "description": "Follow a run.", "cli": ["transit", "follow", "{{runId}}"] }
}
}{
"surface_id": "transit",
"version": 1,
"verbs": {
"find": { "operation": "read", "privacy": { "land": "personal" } },
"plan": { "operation": "read", "privacy": { "land": "personal" } },
"show": { "operation": "read", "privacy": { "land": "personal" } },
"status": { "operation": "read", "privacy": { "land": "personal" } },
"follow": { "operation": "read", "privacy": { "land": "personal" } }
},
"stimuli": {}
}# HCP — Transit
Public five-command wrap sample. No login. No secrets. Copy this shape for
discover / plan / show / status / follow.
## Hot path
| Command | CLI |
|---------|-----|
| `find` | `transit find "Central" --json` |
| `plan` | `transit plan "A" "B" --json` |
| `show` | `transit show opt-1 --json` |
| `status` | `transit status --json` |
| `follow` | `transit follow run-1 --json` |
## Anti-patterns
- One mega-command `run`
- Polling `plan` in a tight loop
- Host-absolute paths in this SkillRelated#
- Build packs: the authoring path
- Specification: Pack model