---
sidebar_position: 1
title: The declaration model
---

# Agent templates — the declaration model

An **agent template** is the source of a reusable agent: its graph, its state
schema, and a **declaration block** that says what the platform must do when
somebody installs it.

If you have written CloudFormation, you already know the shape:

| CloudFormation | Zibby |
|---|---|
| stack template | the template |
| `Resources` | the declaration blocks (`models`, `stores`, `requires`, …) |
| `create-stack` | deploying the template from the catalog |
| `Ref` / logical names | a declared **name** the platform binds at deploy |
| *Update requires: Replacement* | fields frozen at deploy — see [Update behavior](./update-behavior.md) |

The rule the whole system is built around:

> **Adding a new agent means writing only a declaration.** No new UI branch, no
> new gate, no code that knows this agent's name.

Everything the platform does for your agent — which model keys it asks you for,
which stores it provisions, which service containers it pulls, which endpoints
it publishes, which other agents it installs alongside — is computed from the
declaration. One renderer, one provisioner, one deploy path, for every agent
that exists or ever will.

## Where a declaration lives

```
my-agent/
├── graph.mjs        ← the graph; NODES carry their own declarations
├── state.js         ← the input schema; fields can carry deploy-time annotations
├── workflow.json    ← optional: which events start a run
├── package.json     ← the versions this agent will be FROZEN at
└── icon.png         ← optional
```

plus the **`spec` block** that declares everything else:

```js
spec: {
  slug: 'my-agent',
  tagline: 'One clamped line that IS the card header.',
  tags: ['Coding'],
  capabilities: [
    'Each bullet is one thing, starting with a verb',
  ],
  models: [ /* … */ ],
  surfaces: ['cli', 'webhook', 'schedule', 'mcp'],
  releaseNotes: 'What changed in THIS version.',
}
```

A template with no `spec` block is not in the catalog at all. It is still
usable as a local scaffold, but nothing syncs, nothing renders, nothing deploys.

:::note `marketplace:` is the same block
Older templates spell this block `marketplace:`. That spelling still works and
always will — `spec:` is simply the neutral name for the same thing. You never
need to rename an existing template.
:::

## What happens at deploy

```
        your declaration
               │
               ▼
      ┌────────────────────┐
      │  the catalog card  │   models → credential slots
      └────────────────────┘   surfaces → which sections the page shows
               │               requires → "installs a team of N"
               ▼
        you press Deploy
               │
   ┌───────────┼───────────┬──────────────┬─────────────┐
   ▼           ▼           ▼              ▼             ▼
 stores    sidecars     member         remote MCP    selections
 created   pulled +     agents         servers       written to
 + bound   verified     installed      bound         the Env bag
           + started    + attached
```

Every step is idempotent. Deploying the same version twice changes nothing;
deploying a newer version re-runs the steps and keeps what you configured by
hand.

## Backward compatibility by synthesis

A block you do not declare is not "missing" — the platform **synthesises the
equivalent**, so an old template behaves exactly as it always did:

- no `models` → one ordinary bring-your-own-key model block;
- no `surfaces` → the four standard channels (CLI, webhook, schedule, MCP);
- no `entryPoints` → the platform serves each surface itself.

:::tip Empty is not the same as absent
`models: []` is a **real declaration**: "this agent runs no model at all". The
deploy will not ask you for an AI key. Leaving `models` out entirely means
something different — "give me the normal one".
:::

## Names, not addresses

No template ever writes down a concrete id, host or URL. It declares a **name**;
the platform binds that name to a real resource at deploy; and whatever consumes
it resolves the name late — differently on the cloud and on your own box.

That is why the same template works in both places, and why nothing breaks when
a resource is re-created.

:::danger Never put a credential in a declaration
A declaration is public data that travels with the card. Tokens, keys and
passwords reach an agent **only** through its encrypted Env bag — never through
a template, a prompt, a trigger payload or a chat message.
:::

## The blocks

| Block | What it declares |
|---|---|
| [`models`](./models.md) | which model(s) the agent runs, and who supplies the key |
| [`stores`](./stores.md) | durable storage, provisioned at deploy and injected by name |
| [`requires`](./requires.md) | an endpoint another agent publishes, installed and attached for you |
| [`dispatchesWorkflow` / `kind`](./composition.md) | other agents this one runs as sub-graphs — a fleet |
| [`surfaces` / `entryPoints`](./surfaces.md) | which channels the agent offers, and where each one lives |
| [`sidecars` / `sidecarSpecs`](./sidecars.md) | a long-lived service container the agent needs |
| [`remoteMcp`](./remote-mcp.md) | a hosted third-party MCP server the agent is for |
| [deploy-time configuration](./deploy-time-config.md) | pickers, inputs, a default schedule, trigger events |
| [node declarations](./node-declarations.md) | skills, integrations and vendor pins, per node |
| [catalog metadata](./catalog-metadata.md) | slug, tagline, tags, release notes, availability |
| [Update behavior](./update-behavior.md) | what changes on re-deploy, what is seeded once, what is frozen |
