Hype StackHypeStack

Search

Search the packs, templates, docs, and pages

Run your own registry

A registry is anywhere that answers one GET with one JSON document. Buyers point a namespace at it in stack.json and install with @you/pack-name. Whether they're allowed to have the pack is your server's call, made before it serves a byte. The CLI attaches a credential and reads the answer. It never decides entitlement, and it never sees your prices.

This is the same shape shadcn uses for its registries, on purpose. If you've hosted one of those, you've hosted one of these.

The document

For each pack, serve:

json
{
  "manifest": { "name": "pack-analytics", "version": "1.2.0", "category": "feature", "...": "the normal manifest" },
  "files": [
    { "path": "files/frontend/src/features/analytics/index.ts", "content": "<base64>" }
  ],
  "revision": { "commit": "3f2a..." }
}
  • manifest is exactly the manifest you'd write for a local pack. Its name must match the name that was requested.
  • files carries every file the manifest references, path relative to the pack root, content base64 encoded. A static host can serve this; nothing has to be dynamic.
  • revision is optional. The CLI hashes the files itself either way.

The document may be up to 16 MB.

The buyer's side

In stack.json, or in ~/.config/hype-stack/registries.json for every project on a machine:

json
"registries": {
  "@you": "https://packs.you.dev/{name}.json",
  "@vendor": {
    "url": "https://api.vendor.com/hype/{name}",
    "headers": { "Authorization": "Bearer ${VENDOR_TOKEN}" },
    "params": { "license": "${VENDOR_LICENSE}", "channel": "stable" }
  }
}
  • The value is a URL with {name} in it, or an object with url, headers, and params.
  • ${VAR} anywhere in those three expands from the buyer's environment when the request is built. The token never sits in a file that gets committed, and a missing variable is a clear error naming it, not an empty header.
  • A project entry wins over the user file for the same namespace.

Then:

bash
npx @hype-stack/cli community add @vendor/pack-analytics

A pack from a registry is a community pack like any other: it goes through the trust prompt, the plan summary names where it came from and every npm package it adds, the path guards apply to every file in the document, and nothing in it ever executes.

Selling a pack

Check whatever your headers or params carry, and answer 401 or 403 when it doesn't check out:

json
{ "reason": "no-access", "purchaseUrl": "https://you.dev/buy/pack-analytics" }

The buyer sees the reason and the URL. That's the whole integration: your server, your key format, your checkout, your refund policy. The CLI carries the key.

Two things the CLI will refuse, so don't rely on them:

  • Redirects. Following one would carry the buyer's credentials to whatever host you pointed at. Answer directly.
  • Files outside the pack. A path with .. in it is rejected before anything is written.

Dependencies between your packs

A registry pack can depend on another pack from the same registry by its namespaced name:

json
"dependencies": ["@you/pack-base", "starter-saas-workos"]

@you/pack-base is fetched from your registry. starter-saas-workos is the Hype Stack catalog. A bare name always means the catalog; a namespaced one always means that namespace. There's no search order to reason about.

Or just use git or npm

You may not need any of this. A private GitHub repo or a private npm package is already a paid pack:

bash
npx @hype-stack/cli community add github:you/pack-analytics#v1.2.0
npx @hype-stack/cli community add npm:@you/pack-analytics

The buyer's own git or npm credentials get the fetch through, and you never run a server. Reach for a registry when you want your own URL, your own key format, or a purchase link in the refusal.

Sponsor open source

Every purchase and sponsorship funds my 8+ years of work on open source given freely to the community. It keeps the lights on, funds new packs, and keeps the ecosystem alive. Even a small tier means a lot. Thank you!

Sponsor on GitHub