# For pack authors

> Source: https://openwop.dev/for/pack-authors/ · OpenWOP, the open-source protocol for multi-agent workflow orchestration.

You're packaging reusable workflow components — node implementations, agent definitions, or chained workflow templates — for other OpenWOP users to consume. This page covers the manifest shape, the signing recipe, the namespace tiers, and how to publish to the canonical registry.

## What a pack is

An OpenWOP pack is a signed tarball containing:

- A `pack.json` manifest declaring the pack name, version, and the types it exports.
- One or more node implementations (`runtime.language` is `javascript`, `python`, `go`, `wasm`, `wasm-component`, or `remote` — an MCP server the host calls; see [Node-pack runtimes](https://openwop.dev/spec/v2/core/node-pack-runtimes.html)).
- Optional agent manifests (per [RFC 0003](https://openwop.dev/rfcs/0003-agent-packs.html)).
- A detached Ed25519 signature over the RFC 8785 canonical-JSON bytes of the `pack.json` inside the tarball (the v2 `ed25519-canonical-json` scheme).

Hosts consume packs at install time, verify the signature against the publisher's public key, and register the declared types in their node catalog. The host's pack-consumer reference implementation is at [`examples/hosts/postgres/src/pack-consumer.ts`](https://github.com/openwop/openwop-examples/blob/main/examples/hosts/postgres/src/pack-consumer.ts) in `openwop/openwop-examples`. It shows the install-time security check: lockfile parse, SRI integrity, version-drift detection and Ed25519 signature verification.

## Read these first

- [Node packs](https://openwop.dev/spec/v2/core/packs.html) — pack format, manifest schema, dependency resolution, lockfile semantics, signing recipe.
- [Workflow chain packs](https://openwop.dev/spec/v2/core/workflow-chain-packs.html) — composing reusable multi-step workflow templates (RFC 0013).
- [Node-pack runtimes](https://openwop.dev/spec/v2/core/node-pack-runtimes.html) — the runtime languages a host may execute, the WASM ABI, and the MCP registry record for `remote` packs.
- [Host services](https://openwop.dev/spec/v2/core/host-services.html) — the advertised `host.*` service surfaces a node pack invokes through `ctx`.
- Registry operations — submission, deprecation, yank, key rotation. The registry tree and signing rules are in [Packs](https://openwop.dev/spec/v2/core/packs.html).
- [`agent-manifest.schema.json`](https://github.com/openwop/openwop/blob/main/schemas/agent-manifest.schema.json) — agent pack manifest shape (RFC 0003).

## Namespace tiers

Packs are partitioned into three tiers. The tier is part of the pack identifier; there's no implicit promotion.

| Tier | Publishing rule | Signing root | Example |
|---|---|---|---|
| `core.*` | OpenWOP-maintained; canonical baseline | `openwop-team-1` or `openwop-registry-root` (both permitted for `core.openwop.*`) | `core.openwop.http` |
| `vendor.*` | Named vendor; the vendor's own Ed25519 key, listed in the registry's `signingKeys[]` with `permittedNamespaces` scoped to `vendor.<org>.*` | vendor-held keypair | `vendor.myndhyve.canvas` |
| `community.*` | Open publishing via PR; the publisher's own Ed25519 key, listed in the registry's `signingKeys[]` with `permittedNamespaces` scoped to the publisher's `community.<publisher>.*` names | per-publisher Ed25519 keypair | `community.openwop-team.demo` |

Host-private deployments can additionally use `private.*` — these never enter the public registry.

## Sign your tarball

v2 has one signing scheme (per [Packs](https://openwop.dev/spec/v2/core/packs.html) §"Signing"): `signing` on the version manifest is `{ keyId, scheme }`, and `scheme` MUST be `ed25519-canonical-json`. The recipe is:

1. Build a deterministic tarball containing your `pack.json`.
2. Compute the SRI integrity of the tarball.
3. Sign the RFC 8785 (JCS) canonical bytes of that `pack.json` with your Ed25519 private key — a detached 64-byte signature.
4. Submit the `.tgz` + the `.sig` + a version manifest referencing both, with the SRI integrity matching the tarball.

A signature over the raw tarball bytes is **not** a v2 signature; a pack signed that way must be re-signed, not relabeled. A verifier checks the signature against the issuing registry's key for `keyId` and the pack name against that key's `permittedNamespaces`.

A reference workflow lives at [`scripts/build-pack-tarball.mjs`](https://github.com/openwop/openwop-registry/blob/main/scripts/build-pack-tarball.mjs) in `openwop/openwop-registry` — you can mine it for the canonical command sequence.

## Publish to the canonical registry

The reference registry at [`packs.openwop.dev`](https://packs.openwop.dev/) has no write API by design. Publishing is via PR against [`openwop/openwop-registry`](https://github.com/openwop/openwop-registry):

1. Fork the repo.
2. Add your pack tarball + signature + manifest under the appropriate namespace tier.
3. Add a row to the registry index via the canonical generator script.
4. Open a PR with the conventional commit prefix `feat(registry):`.

The registry rebuilds on merge. Packs are served under `registry/v2/packs/<name>/-/<version>.{json,sbom.json,sig,tgz}`. Clients resolve every registry path through the `endpoints` in `.well-known/openwop-registry.json` instead of building one.

## Publish to a private registry

You can stand up your own OpenWOP registry by serving the discovery doc + the pack URLs over HTTPS. The discovery document is `.well-known/openwop-registry.json`, defined at [Packs](https://openwop.dev/spec/v2/core/packs.html) §"The registry tree". Hosts that point at your registry will consume your packs identically to the canonical one.

Use cases: tenant-scoped private packs, air-gapped deployments, enterprise compliance ("our packs never leave our network").

## Common pitfalls

- **Don't reuse a signing key across publishers.** Each `community.<publisher>.*` pack should use a publisher-scoped Ed25519 keypair.
- **Pin your dependencies.** Pack-to-pack dependencies are resolved via lockfile; install-time integrity checks fail closed on version drift.
- **Honor SR-1.** If your nodes touch secrets, follow the [secret-redaction invariant](https://github.com/openwop/openwop/blob/main/SECURITY/invariants.yaml) — never put raw secrets in node outputs or event payloads.
- **Bump majors honestly.** A pack version bump that changes node input/output shapes is a breaking change; downstream workflows must be able to pin to the prior major.

## Next steps

| Action | Where |
|---|---|
| Read the node-pack spec | [/spec/v2/core/packs.html](https://openwop.dev/spec/v2/core/packs.html) |
| Browse the registry | [packs.openwop.dev](https://packs.openwop.dev/) |
| See an example pack | [`examples/packs/rust-hello/`](https://github.com/openwop/openwop-examples/tree/main/examples/packs/rust-hello) |
| Open a publishing PR | [`openwop/openwop-registry`](https://github.com/openwop/openwop-registry) |
