---
type: reference
title: The caputchin.json manifest
summary: Every field of the caputchin.json manifest that describes a marketplace game, the marketplace block, distribution pointers, the replay run artifact, preset blocks, and the .caputchin split files.
---

`caputchin.json` at your repository root is the author and indexer source of truth for a marketplace game. The indexer reads it server-side to learn the game's identity, where its bundle lives, which presets it offers, and how to replay it. It is **never** read in the browser; the SDK does not load it at runtime.

This page is the field reference. For the build, see [Build a marketplace game](/docs/marketplace-game-development/build-a-marketplace-game); for publishing, see [Publish to the marketplace](/docs/marketplace-game-development/publish-to-the-marketplace). The exact types are exported from the SDK as `GameManifest` (see the [SDK reference](/docs/marketplace-game-development/game-sdk-reference)).

## A minimal single game

```json
{
  "terms_accepted": true,
  "license": "MIT",
  "marketplace": {
    "name": "Leaf Memory",
    "description": "Match pairs of tropical leaves before the timer runs out.",
    "preview": "preview.png"
  },
  "npm": "@your-org/leaf-memory",
  "entry": "dist/leaf-memory.js"
}
```

## Top-level fields

| Field | Required | Purpose |
|---|---|---|
| `terms_accepted` | for indexing | Must be the literal `true` to confirm you accept the [Marketplace Submission Terms](/legal/submission-terms). Any other value (or missing) drops the manifest from the index. Self-hosted-only manifests may omit it. |
| `license` | for indexing | An SPDX identifier or expression covering your code and bundled assets. Must evaluate to an approved identifier (see the [publish errors reference](/docs/marketplace-game-development/publish-failed-reference#approved-licenses)). |
| `marketplace` | for indexing | Presence is the "index me" signal. Absent means a valid [self-hosted](/docs/custom-game-development/overview) game that the marketplace ignores. See [The marketplace block](#the-marketplace-block). |
| `npm` | with `entry` | npm package coordinate the indexer resolves to a jsDelivr URL. |
| `entry` | with `npm` | Repo-relative path to the built bundle. `entry`, `npm`, or both must be present for a runnable game. |
| `games` | for collections | Sub-manifest paths for a collection wrapper. Mutually exclusive with `entry` / `npm`. See [Collections](#collections). |
| `run` | optional | A dedicated headless replay artifact. See [The run artifact](#the-run-artifact). |
| `preferred` | optional | Presentation hints the host MAY honor. See [The preferred block](#the-preferred-block). |
| `locales` / `skins` / `configurations` | optional | Preset blocks consumed by the SDK in the iframe. See [Preset blocks](#preset-blocks). |

There is no `version` field: the indexer pins the bundle to an immutable ref itself (the published npm version or the resolved commit SHA).

## The marketplace block [#the-marketplace-block]

| Field | Purpose |
|---|---|
| `name` | Card title. Falls back to the repo name (single / wrapper) or leaf-dir name (collection child). |
| `description` | Card subtitle and detail-page body. |
| `preview` | Image path (relative to repo root) or absolute URL. Around 600x315, subject centered. |
| `version` | Display-only version string for the detail page. Optional. |
| `support` | Author-declared compatibility flags (`responsive`, `touch`, `accessible`, `audio`, ...), never verified by the platform; surfaced as filters and card icons. |
| `author` | Optional `{ name?, url?, email? }`. `name` / `url` render as an author byline on the detail page; `email` is never shown and is the **opt-in** for [publish-failure emails](/docs/marketplace-game-development/publish-to-the-marketplace#get-notified-when-a-publish-fails). All three subfields are independent. |

The block is fully optional. Omit `name` / `url` and the detail page simply shows no author byline (the GitHub owner shown elsewhere on the page is a separate, always-present identity, not a fallback for this byline). Omit `email` and you receive no publish-failure notifications. Set only the subfields you want.

## Distribution pointers

The indexer pins each game's bundle to an immutable ref so the stored integrity hash stays valid, and re-resolves on every run (daily cron plus manual "Publish or update"):

- `entry` + `npm` resolve to `cdn.jsdelivr.net/npm/<npm>@<resolved-version>/<entry>`.
- `entry` only resolves to `cdn.jsdelivr.net/gh/<owner>/<repo>@<commit-sha>/<entry>`.
- `npm` only resolves to the package default entry at the pinned version.

There is no user-side version pinning; to freeze a build, a user self-hosts it via the widget's `game-src` ([custom game](/docs/custom-game-development/self-hosted-game)).

## The run artifact [#the-run-artifact]

By default the replay self-check runs your live `entry` bundle. When your game is large or framework- or WASM-based, ship a dedicated lean headless `run` artifact instead:

```json
{
  "run": {
    "entry": "dist/run.js",
    "modules": [
      { "name": "sim.wasm", "type": "wasm", "path": "dist/sim.wasm" }
    ]
  }
}
```

| Field | Meaning |
|---|---|
| `run.entry` | Repo-relative path to the headless `run` bundle (the [replay contract](/docs/marketplace-game-development/replay-contract) `run` export). |
| `run.modules` | Optional array of module entries the run imports by `name`. |

### Constraints the indexer enforces

These are validated at index time; a violation fails the publish with a `manifest-error` (see the [publish errors reference](/docs/marketplace-game-development/publish-failed-reference)).

**`run.entry`:**

- Must be **JavaScript**: the basename must match `[a-zA-Z0-9_-]+.js`. The run artifact is always JS (WASM ships as a module, below).
- Must be a clean repo-relative path: no leading slash, no `..` traversal, no whitespace, no `?` / `#`, no `scheme://` prefix.

**Each `run.modules[]` entry** is `{ name, type, path }`:

- `name` must match `[a-zA-Z0-9_-]+.(wasm|js)`, a module is either **JS or WASM**, nothing else. The `name` is the import specifier the entry uses.
- `type` must agree with the extension: `wasm` for a `.wasm` name, `js` for a `.js` name.
- `name` must not be a **reserved** name (`entry.js`, `artifact.js`) the replay host uses internally.
- `name` must be **unique** within the array (no duplicates).
- `path` must be a clean repo-relative path (same rules as `run.entry`).
- At most **16** modules.

Omit `run` to replay the live `entry` directly. See [the replay contract](/docs/marketplace-game-development/replay-contract) for what the artifact must export.

## The preferred block [#the-preferred-block]

The optional `preferred` block carries presentation hints. Every key is advisory: the host MAY honor it, and it never overrides an explicit embed attribute.

```json
{
  "preferred": { "width": 360, "height": 480, "layout": "modal" }
}
```

A responsive game that should span its container by default can declare `"full"` on either axis:

```json
{
  "preferred": { "width": "full", "height": 480 }
}
```

| Field | Meaning |
|---|---|
| `width` / `height` | A pixel footprint, or the literal `"full"`. The widget applies it when the embed leaves `width` / `height` unset (an explicit embed value, including `full`, wins instead). A pixel value sizes the iframe to that count; `"full"` stretches that axis to fill the parent, the same effect an embed `width="full"` has. Omit to fall back to the widget's built-in default footprint. |
| `layout` | The shell the widget builds around the game: `inline`, `modal`, or `fullscreen`. Used only when the embed leaves `layout` unset (its default `auto`). Resolution order: the embed's `layout` attribute, then this preferred layout, then `inline`. |

These hints are honored only for games the platform resolves server-side (marketplace games, or a game id given without a site key). A self-hosted `game-src` bundle the platform cannot read before mount ignores both the footprint and the layout hint.

## Preset blocks [#preset-blocks]

`locales`, `skins`, and `configurations` each declare an optional `schema` (per-key types and documentation) plus `presets` (the named option banks). The widget resolves the visitor's choice against these and hands your game the flattened result as [`ctx`](/docs/marketplace-game-development/game-sdk-reference). The full field-type catalogue is the shared [customization schema reference](/docs/references/customization-schema); the same types drive a [custom game's dashboard schema](/docs/custom-game-development/dashboard-schema).

A `skins.schema` field may be a `color`, an asset (`image` / `audio` / `video`), or a scalar (`boolean`, `number`, `range`, `list`) using the same constraint forms as `configurations` (`{ "type": "range", "min": 0, "max": 24 }`, `["dots","stripes"]`, and so on). Scalar skin values resolve to their typed value in `ctx.skin` (a `number` is a real number), exactly like a configuration value; color and asset values resolve to strings.

A skin preset's `_theme` declares the mode it works in: `light`, `dark`, or `any` (omitting it means `any`). A `light` or `dark` preset shows only in that mode; an `any` preset reads on either background and is eligible for both. There is one default per mode. A preset marked `_default: true` is the default for whichever mode(s) it is eligible for, so an `any` default covers both; when several eligible presets are flagged default for a mode, the first one in declaration order wins it. List a mode-specific preset above an `any` one to give that mode a dedicated skin while the `any` preset falls through to the other.

## The `.caputchin/` split files

Preset blocks (especially full locale sets) make `caputchin.json` long. Move any axis into its own file under a `.caputchin/` folder, leaving the manifest short:

```
caputchin.json
.caputchin/locales.json
.caputchin/skins.json
.caputchin/configurations.json
```

Each file's top-level object **is** that axis block (`{ schema?, presets }`). All three are optional. Precedence is whole-axis replace, `caputchin.json` wins: if an axis is declared both inline and as a file, the inline block is used and the file is ignored (publishing warns so you know it was dead). Keep each axis in exactly one place.

## Collections [#collections]

A repo can ship several games. A collection wrapper declares child paths instead of `entry` / `npm`:

```json
{
  "marketplace": { "name": "Caputchin Core Pack", "description": "The official pack." },
  "games": ["./packages/leaf-memory", "./packages/dino-runner"]
}
```

Each path points at a child directory holding its own `caputchin.json`. Child ids are `owner/repo/<leaf-dir>`. Omit the wrapper's `marketplace` block to index the children without a collection page.

## See also

- [Build a marketplace game](/docs/marketplace-game-development/build-a-marketplace-game): producing the bundle this manifest points at.
- [The replay contract](/docs/marketplace-game-development/replay-contract): what the `run` artifact must export.
- [Publish errors reference](/docs/marketplace-game-development/publish-failed-reference): every manifest validation error and the approved license list.
- [Customization schema reference](/docs/references/customization-schema): the field types the preset blocks use.
