> ## Documentation Index
> Fetch the complete documentation index at: https://docs.blobhub.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Manifest

> The manifest format: the shared base every blob domain extends, and the workflow_blob_deployment body

A manifest is the unit of work for every command in this group. It names one blob and one revision, lists
everything that belongs on that revision, and binds each code component to a place in your repository. There is
no per-definition manifest and no default path — `-f/--file` is required everywhere, and the file it names is
loaded before anything else happens.

The file is parsed by extension: `.yaml`, `.yml` and `.json` all work, and the same manifest written in YAML or
in JSON loads identically. An unsupported extension is `FORMAT_UNSUPPORTED`; a file that does not parse is
`FORMAT_PARSE_ERROR`.

## The shared base

Four fields sit at the top of every manifest the CLI reads, whatever blob domain it belongs to. The base is
parsed and validated by itself, before the body is looked at; `type` is what selects the body.

```yaml theme={null}
type: workflow_blob_deployment
version: "1.0"
blob: acme-corp/checkout
revision: latest
```

| Field      | Value                              | Notes                                                                                                                                                                |
| ---------- | ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `type`     | `workflow_blob_deployment`         | Selects the body shape. A manifest of another type reaching a `workflow` command is `MANIFEST_TYPE_MISMATCH`.                                                        |
| `version`  | `"1.0"`                            | The only version this CLI accepts. Anything else is `MANIFEST_VERSION_UNSUPPORTED`. Quote it — unquoted, YAML reads `1.0` as a float and the field must be a string. |
| `blob`     | `<org>/<blob>`, or a bare `<blob>` | At most one `/`. A bare name inherits its org from `--org` or `BLOBHUB_ORG`.                                                                                         |
| `revision` | `latest`, or a revision UUID       | Checked offline: anything that is neither is `MANIFEST_INVALID` naming the value.                                                                                    |

All four must be non-empty strings. That is the whole base — the scheduler group's manifest carries exactly these
four fields and then its own body, and a future blob domain does the same. Nothing on the shared side changes
when one arrives.

### `blob`

`acme-corp/checkout` qualifies the org in the file. A bare `checkout` does not, and inherits the org from the
global `--org` option or the `BLOBHUB_ORG` environment variable — which org a checkout belongs to is a property
of the checkout, not of the key being used to reach it, so a profile is deliberately not a source for it.

With a bare name and neither supplied, the manifest is `MANIFEST_INVALID` and the message names all three ways
to fix it, since two of them are not in the file in front of you.

An org-scoped API key checks its target as a raw string against the org **UUID**. Addressing that org by its
alias yields a confusing `403` on every call, so under an org-scoped key qualify the manifest — or `--org` — with
the UUID.

The blob's shape is confirmed before any engine call: a `workflow` manifest pointed at a blob of another domain
stops with `BLOB_NOT_WORKFLOW` rather than an opaque `500` one round trip later.

### `revision`

`latest` resolves through the blob's `latest_revision_id` at the moment the command runs. A pinned UUID names one
revision for as long as the manifest says so.

Which revisions a command will accept depends on the direction it moves data:

| Direction | Commands                                          | Requires                                             |
| --------- | ------------------------------------------------- | ---------------------------------------------------- |
| Write     | `push`, `deploy`, `prune`                         | Status `ready`, phase `draft` or `managed`           |
| Read      | `pull`, `diff`, `check`, `ls`, `eject`, `execute` | Status `ready`, phase `draft`, `commit` or `managed` |

The CLI fetches the revision and applies this itself, so a refusal names the phase and points at
[`diff`](/cli/workflow/commands/diff). The platform's own rejection is a bare `400` that does not mention the
revision at all. `build` is exempt from all of it — it never resolves a revision.

## Every path anchors to the manifest's own directory

`source`, `code.base_path` and `value.source` all resolve against the directory holding the manifest file, never
against the current working directory and never against each other. A manifest is revision-scoped, and its
definitions are typically scattered across several directories, so a source-relative anchor would make the
meaning of a `../..` depend on which subdirectory a given definition happened to sit in.

`entry_point` is the one exception: it resolves against `base_path`, because it names a module inside the Python
package `base_path` roots rather than a file in the project.

Paths are collapsed lexically, so `..` segments work and are resolved without touching the filesystem.

## The `workflow_blob_deployment` body

One key: `definitions`, a list with at least one entry. An empty or absent list is `MANIFEST_INVALID`.

| Field        | Required | Meaning                                                                                                                                                                     |
| ------------ | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `alias`      | Yes      | The definition's alias on the revision. Must match `^[a-z0-9_-]{6,42}$`, checked at load time as `DEFINITION_ALIAS_INVALID`.                                                |
| `source`     | Yes      | The definition document, as a path from the manifest's directory. `.yaml`, `.yml` or `.json`.                                                                               |
| `category`   | No       | `workflow` or `playground`. Inferred from the document when omitted.                                                                                                        |
| `components` | No       | Bindings from local files into this definition's component ports. An entry with none is still pulled, pushed and diffed — there is simply nothing local to compile into it. |

Unlisted keys are ignored rather than rejected, so a typo'd field name is silently inert. There is no schema
beyond what is described here.

### `category`

Inferred from the source document's own `type`: `workflow_definition` gives `workflow`, `playground_definition`
gives `playground`. One function owns this rule for every command in the group, `build` included, so no two
commands can disagree about what an entry is.

* **Omitted, document present, `type` recognized** — the inferred category wins.
* **Omitted, document present, `type` unrecognized** — `DEFINITION_CATEGORY_UNKNOWN`. Declare `category`.
* **Omitted, document absent** (before a first [`pull`](/cli/workflow/commands/pull)) — defaults to `workflow`.
* **Declared, `type` unrecognized** — the declared category wins. This is what declaring it is for: a `type` that
  names no native category contradicts nothing.
* **Declared, and it contradicts the document** — `DEFINITION_CATEGORY_UNKNOWN`, naming both.

That last case is an error rather than a warning because nothing server-side catches it. A workflow upload
validates nothing at all, so a playground document declared as a workflow uploads cleanly into the workflow
definition's id and corrupts it silently.

Category is not decoration: a definition is identified by the `(category, alias)` **pair**, so one alias may
legitimately name a workflow definition and a playground definition on the same revision.

### `components[]`

Each binding says which component it addresses, and what to put in it.

| Key           | Rule                                                                                                                                                                      |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id` / `name` | Exactly one of the two. `name` must match exactly one component in the document, or it is `COMPONENT_NAME_AMBIGUOUS`; neither matching anything is `COMPONENT_NOT_FOUND`. |
| `code`        | `{base_path, entry_point}` — compile this Python package into the component's `code` port.                                                                                |
| `value`       | `{source}` — read this JSON/YAML file and write it into the component's `value` port.                                                                                     |

`code` and `value` are mutually exclusive; declaring both is `MANIFEST_INVALID`. A binding with neither resolves
the component and writes nothing, which makes it a no-op that can still fail with `COMPONENT_NOT_FOUND`.

A `code` binding is what [`build`](/cli/workflow/commands/build) and
[`deploy`](/cli/workflow/commands/deploy) act on, and the only thing the
[compiler](/cli/workflow/compiler) is ever pointed at. The bound component's `language` port must be exactly
`python`; anything else, including no `language` port at all, is `LANGUAGE_NOT_PYTHON`.

## A worked example

A revision holding two definitions: a workflow whose `logic.code` component (`id: c1`) is compiled from `pkg/`
and whose pricing table is loaded from a JSON file, plus a playground with nothing bound into it.

```
my-project/
├── manifest.yaml
├── definitions/
│   └── checkout_flow.json
├── playgrounds/
│   └── checkout_playground.json
├── data/
│   └── pricing.json
└── pkg/
    ├── entry.py
    └── lib/
        └── pricing.py
```

<CodeGroup>
  ```yaml manifest.yaml theme={null}
  type: workflow_blob_deployment
  version: "1.0"
  blob: acme-corp/checkout
  revision: latest
  definitions:
    - alias: checkout_flow
      source: definitions/checkout_flow.json
      category: workflow
      components:
        - id: c1
          code:
            base_path: pkg
            entry_point: entry.py
        - name: pricing_table
          value:
            source: data/pricing.json
    - alias: checkout_playground
      source: playgrounds/checkout_playground.json
  ```

  ```json manifest.json theme={null}
  {
    "type": "workflow_blob_deployment",
    "version": "1.0",
    "blob": "acme-corp/checkout",
    "revision": "latest",
    "definitions": [
      {
        "alias": "checkout_flow",
        "source": "definitions/checkout_flow.json",
        "category": "workflow",
        "components": [
          { "id": "c1", "code": { "base_path": "pkg", "entry_point": "entry.py" } },
          { "name": "pricing_table", "value": { "source": "data/pricing.json" } }
        ]
      },
      {
        "alias": "checkout_playground",
        "source": "playgrounds/checkout_playground.json"
      }
    ]
  }
  ```
</CodeGroup>

Both files load to the same manifest. The entry point compiled here is `pkg/entry.py` — `entry_point` is relative
to `base_path`, while `base_path` itself, `source` and `value.source` are all relative to the manifest.

The playground entry declares no `category`, so it is inferred from its document's `type`. It declares no
components either: playground definitions are moved verbatim by
[`pull`](/cli/workflow/commands/pull) and [`push`](/cli/workflow/commands/push), and nothing compiles into them.

## Errors

| Code                                                                        | When                                                                                                                                                                                                                                                                    |
| --------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `MANIFEST_NOT_FOUND`                                                        | The path passed to `-f` does not exist.                                                                                                                                                                                                                                 |
| `MANIFEST_INVALID`                                                          | A base field is missing or not a string; `blob` has more than one `/`; `revision` is neither `latest` nor a UUID; `definitions` is absent or empty; a binding declares both or neither of `id`/`name`, or both `code` and `value`; a bare `blob` with no org available. |
| `MANIFEST_TYPE_MISMATCH`                                                    | `type` is not `workflow_blob_deployment`.                                                                                                                                                                                                                               |
| `MANIFEST_VERSION_UNSUPPORTED`                                              | `version` is not `"1.0"`.                                                                                                                                                                                                                                               |
| `DEFINITION_ALIAS_INVALID`                                                  | An `alias` fails `^[a-z0-9_-]{6,42}$`.                                                                                                                                                                                                                                  |
| `DEFINITION_CATEGORY_UNKNOWN`                                               | The category cannot be inferred and none is declared, or the declared one contradicts the document.                                                                                                                                                                     |
| `FORMAT_UNSUPPORTED`, `FORMAT_PARSE_ERROR`                                  | The manifest, a `source`, or a `value.source` has an unsupported extension or does not parse.                                                                                                                                                                           |
| `COMPONENT_NOT_FOUND`, `COMPONENT_NAME_AMBIGUOUS`, `COMPONENT_PORT_MISSING` | A binding matches no component, matches more than one by `name`, or the component has no `code` / `value` port.                                                                                                                                                         |
| `LANGUAGE_NOT_PYTHON`                                                       | A `code`-bound component's `language` port is not `python`.                                                                                                                                                                                                             |
| `BLOB_NOT_WORKFLOW`, `BLOB_NOT_ACCESSIBLE`                                  | `blob` names a blob of another domain, or one that is absent or unreachable.                                                                                                                                                                                            |
| `REVISION_NOT_ACCESSIBLE`, `REVISION_NOT_WRITABLE`                          | The revision is unreachable, or its status and phase do not allow the direction this command moves data.                                                                                                                                                                |

Every code above, with its remediation, is in [Error codes](/cli/error-codes).

## See also

* [Workflow overview](/cli/workflow/overview) — the shape of a deployable project and the ten commands that
  move it.
* [Compiler](/cli/workflow/compiler) — what a `code` binding produces, and every check it runs first.
* [Sandbox](/cli/workflow/sandbox) — what the compiled code executes against.
* [Concepts](/cli/concepts) — why a manifest is revision-scoped, and why category participates in identity.
* [Workflow definition format](/blob-types/workflow/workflows/definition-format) — the document a `source` points
  at.
