Source: https://www.hype-stack.dev/docs/packs-templates/pack-manifest

# Manifest reference

`manifest.json` is the whole interface between a pack and the CLI. Point `$schema` at the published copy and your editor
will check it as you type:

```json
{ "$schema": "https://www.hype-stack.dev/schema/pack-manifest-v1.json" }
```

## Identity

| Field             | Required | What it does                                                                                                                                                        |
| ----------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`            | yes      | Kebab-case identifier. Match the directory name                                                                                                                     |
| `version`         | yes      | Semver. Recorded in the project so a later install can tell versions apart                                                                                          |
| `displayName`     | yes      | What people see in the picker                                                                                                                                       |
| `description`     | yes      | One line, shown next to the name                                                                                                                                    |
| `category`        | yes      | `starter`, `layout`, or `feature`. The catalog supplies this for our own packs, so a community pack has to declare it, and `validate` fails without it              |
| `tier`            | schema   | `free` or `paid`. The published schema requires it, so write `free`. Nothing in a community pack is licensed, so `paid` has no effect and `validate` warns about it |
| `family`          | -        | Makes the pack a swappable variant of a family (`starter-saas`, `layout`, `pack-billing`). Two packs in one family replace each other                               |
| `dependencies`    | yes      | Packs, or family ids, that must be installed first. `[]` when the pack stands alone                                                                                 |
| `peerPacks`       | -        | Packs that make this one better if present, but are not required                                                                                                    |
| `adminCompatible` | -        | Layouts only: whether the admin shell can render this layout                                                                                                        |

## Files

`files` is a list of `{ source, target, strategy }`. `source` is relative to the pack root, `target` is a path template.
See [Build your own pack](/docs/packs-templates/build-your-own) for the strategies and slots.

`dependencies_npm` adds npm packages per app:

```json
"dependencies_npm": { "frontend": { "recharts": "^2.15.0" }, "backend": { "zod": "^4.0.0" } }
```

`allowBuilds` records the verdict for a dependency that runs a build script. pnpm 12 refuses to install one without a
verdict, so the CLI writes yours into the project's `pnpm-workspace.yaml`:

```json
"allowBuilds": { "sharp": true }
```

## What a pack needs from the starter

| Field      | For                                                                                                              |
| ---------- | ---------------------------------------------------------------------------------------------------------------- |
| `provides` | Starters only. What the starter gives a project: `auth`, `organizations`, `permissions`, `invitations`, `admin`. |
| `requires` | Layouts and feature packs. What the pack needs from the starter, from the same list.                             |

The installer checks `requires` against the union of what is installed and what is selected, and refuses the plan by
name when something is missing. A pack that reads the active organization declares `organizations` and never lands on a
starter without one. The free `starter-auth-betterauth` provides all five (every account owns a personal workspace, the
admin console is sign-in plus admin management, and the invitation inbox exists without a way to send), so every feature
pack installs on it. Swapping it for a SaaS starter later is an upgrade: `compose --packs starter-saas-betterauth`
replaces the starter's files, removes what the free one shipped on its own, and registers the admin app again.

## Wiring

Copying files is rarely enough. These fields tell the CLI to patch a known file in the project so your code is actually
reached. Each one is idempotent, so reinstalling never duplicates an entry.

### Backend

| Field         | Patches                                      | For                                     |
| ------------- | -------------------------------------------- | --------------------------------------- |
| `routes`      | `routes/index.ts`                            | Registering a Hono route under a prefix |
| `sockets`     | `sockets/index.ts`                           | A WebSocket handler                     |
| `context`     | `context.ts`                                 | A value on the backend request context  |
| `env`         | `env.config.ts`, `.env.example`, `.env.test` | Environment variables the pack needs    |
| `jobs`        | `src/jobs/index.ts`                          | A scheduled background job (see below)  |
| `queues`      | `src/queues/index.ts`                        | A task queue for slow work (see below)  |
| `enums`       | `packages/enums/src/index.ts`                | Shared enums both sides import          |
| `permissions` | the shared enums package                     | New permission subjects and role grants |

An `env` entry names the Zod schema file to merge and the variables to add. Each variable is a plain default, or an
object that tells [`onboard`](/docs/cli/onboard) how to treat it: `required` prompts, `generated` rolls a secret at
install, `optional` is offered once per group.

```json
"env": [
  {
    "import": "analyticsEnvSchema",
    "from": "./analytics.env",
    "envExample": {
      "section": "config",
      "vars": {
        "ANALYTICS_WRITE_KEY": { "value": "", "setup": "required" },
        "ANALYTICS_FLUSH_MS": "10000"
      }
    }
  }
]
```

A `jobs` entry names an exported `JobDefinition` and the file it lives in. The template's scheduler picks it up at boot;
see [Scheduled jobs](/docs/backend#scheduled-jobs).

```json
"jobs": [{ "import": "analyticsRollupJob", "from": "./features/analytics/jobs/rollup" }]
```

A `queues` entry names an exported `QueueDefinition` (built with `defineQueue`) and the file it lives in, relative to
`src/queues/index.ts`. The template starts workers for it at boot; see [Task queue](/docs/backend#task-queue). It needs
template v1.7.0 or newer, and the install stops with a clear message on an older project.

```json
"queues": [{ "import": "reportExportQueue", "from": "./report-export.queue" }]
```

### Frontend

| Field               | Patches                                 | For                                         |
| ------------------- | --------------------------------------- | ------------------------------------------- |
| `frontendRoutes`    | `frontend-url/frontend-url.ts`          | Typing a route your pack adds               |
| `sdkConfigurations` | `src/api/sdk.ts`                        | HyperFetch client configuration             |
| `nav`               | `constants/nav.constants.ts`            | A sidebar entry                             |
| `navbarActions`     | `constants/navbar-actions.tsx`          | An icon in the navbar                       |
| `userMenuActions`   | `constants/navbar-actions.tsx`          | An item in the avatar dropdown              |
| `sidebarActions`    | `constants/navbar-actions.tsx`          | A block above a sidebar layout's navigation |
| `drawerActions`     | mobile `constants/navbar-actions.tsx`   | A row at the foot of the mobile drawer      |
| `providers`         | `components/providers/providers.tsx`    | A React provider mounted at the app root    |
| `settingsTabs`      | `constants/settings-tabs.constants.tsx` | A tab on the settings page                  |
| `onboardingSteps`   | `setup-organization/steps/index.ts`     | A step in organization onboarding           |

The four action fields are the shell action areas. Each is an exported array the installed layout renders, so a pack
never imports the layout and a layout never imports the pack. The installer records every action in `stack.json` and
rebuilds the arrays after each install, so it does not matter whether your pack lands before or after the layout. A
`drawerActions` component receives `closeDrawer` and owns whatever sheet it opens.

### Admin

`dashboardRows`, `orgDetailsRows`, `orgDetailsCards`, and `orgDetailsTabs` inject components into the admin app: a row
on the dashboard, a row or card on an organization's detail page, or a whole tab next to Overview, Members, and Flags.
They're skipped entirely in projects with no admin app.

### Whole-app

`postLoginRedirect` sets where the app goes after login. `shouldInvertLogo` flips logo colours in light mode, for marks
drawn on dark backgrounds.

## Copy

User-visible strings belong in a message catalog fragment, not in your JSX:

```json
{
  "source": "files/frontend/messages/en.json",
  "target": "{{frontend.root}}/messages/en.json",
  "strategy": "merge-json",
  "slot": "frontend"
}
```

Keys are flat `snake_case` prefixed with your pack's feature (`analytics_page_title`). Call them as
`m.analytics_page_title()`. Chrome the codemods generate (nav entries, settings tabs) takes a `messageKey` next to
`label` instead of a hardcoded string, so it translates with everything else.

## After the install

`postInstall` is a list of lines printed once the pack lands. Use it for the things you genuinely cannot do for someone:
an API key to create, a webhook to point somewhere, a decision they have to make.

```json
"postInstall": [
  "Set ANALYTICS_WRITE_KEY in .env with a key from your provider dashboard",
  "Events are batched every 10s; change it in src/features/analytics/config.ts"
]
```

## Fields you can ignore

`previewNav`, `preview`, `previewAdmin`, `previewMobile`, and `previewExtension` drive the live previews on the Hype
Stack marketplace. They do nothing in a community pack.

Two things are deliberately not fields. There is no `migrations`: ship `.prisma` files as ordinary `files` entries and
let the CLI generate the migration in the project. There is no hook or script field: a pack is data, and nothing in it
executes.
