The manifest
caldera.agent.yaml sits at the root of a bundle. This page describes it
field by field. The manifest is strict: an unknown field is an error with its
name in it (a typo such as secret: for secrets: would otherwise install an
agent without the credential it needs).
The JSON Schema
caldera.agent.schema.json is the early
warning for an editor. Caldera's own validation, including the rules that compare
fields with each other, is the authority.
For what a blueprint is and how it is published and installed, see Blueprints.
Fields
| Field | Required | What | Rules |
|---|---|---|---|
name |
yes | The blueprint's name. | ^[a-z][a-z0-9-]{0,62}$. Must equal the blueprint's name in the workspace. |
version |
yes | This version. | Semantic version without build metadata: 1.2.3 or 1.2.3-rc.1. |
description |
yes | One paragraph for people. | 1 to 500 characters. |
runtime |
yes | How the agent runs. | type: process with command (argument list, 1 to 50 items) and optional workdir; or type: compose with file and optional workdir; or type: adapter with command, optional workdir (a bundle path or an absolute path) and timeout_seconds (1 to 120). Never a shell string. |
instructions |
no | The files that make the agent what it is (CLAUDE.md, .claude/, prompts). |
Up to 50 paths; each must exist in the bundle. |
mcp |
no | An ordinary .mcp.json. |
One path; must exist. Credentials in it are ${NAME} placeholders. |
settings |
no | schema (up to 50 fields) and defaults. |
See settings. |
secrets |
no | Third-party credentials the agent needs, by name and purpose only. | Up to 50. See secrets. |
bindings |
no | Access to the suite's other products, through bots. | Up to 10, one per product. See bindings. |
state |
no | Paths that hold the agent's state and survive every update. | Up to 50; not inside each other. Created, not shipped. |
health |
no | The command that says whether the agent works. | command (argument list) and timeout_seconds (1 to 600, default 60 in the runtime). |
status |
no | The file the agent writes its report to. | One path. See the runtime. |
controls |
no | The buttons Caldera may show. | Any of pause, resume, run_now; default pause and resume. |
logs |
no | The only logs Caldera may ask for. | Up to 50 of {name, path}; names unique; path relative to the working directory. |
account |
no | false switches the Claude account hint off. |
A boolean; on by default. See the account hint. |
Paths
Every path in a manifest is later joined onto a directory on somebody's machine, so paths are the security surface. A path is valid when it is:
- relative, with forward slashes, at most 200 characters;
- free of
..,.and empty segments, backslashes, NUL bytes, a leading/and a drive letter; - not under
.caldera/(the runtime's own directory).
A trailing slash means the directory (data/, .claude/). The runtime checks
again against the real file system after resolving.
runtime
runtime:
type: process
command: [python3, agent.py] # argv: no shell, no quoting, nothing to inject into
workdir: . # optional, relative to the version directory
compose names a compose file in the bundle. It is untried; see
the runtime.
adapter is for an agent that keeps its own processes (a timer, a bot, a pause
file other things also set):
runtime:
type: adapter
command: [.venv/bin/python, caldera/adapter.py] # argv, run as the runtime's user
workdir: /home/agent/my-agent # the agent's own checkout; or a path inside the bundle
timeout_seconds: 25 # optional: replaces the per-verb timeouts
The runtime then starts nothing, stops nothing and reads no file of the agent: it
reports what adapter status prints and passes commands on to the adapter, which
has the last word. A manifest with this runtime may not name status, logs,
state, secrets, bindings, instructions or mcp. The whole interface is on
Adapter mode and its contract.
settings
The form Caldera shows. The fields have exactly the shape the protocol reports, so what the blueprint declares is what Caldera shows.
settings:
schema:
- key: max_budget_usd # ^[a-z][a-z0-9_]{0,63}$
label: Highest cost of one run # 1 to 80 characters
type: number
min: 1
max: 30
step: 1
unit: USD
group: Work
help: Stops a run that would cost more than this.
defaults:
max_budget_usd: 5
type |
Value | Bounds |
|---|---|---|
percent |
a number from 0 to 1 | min/max narrow it; default range 0 to 1 |
number |
a number | min and max are required |
bool |
true or false | none |
datetime |
whole Unix seconds | min/max optional |
text |
a string of at most 300 characters, no NUL | none |
select |
one of options (value, label) |
options required, up to 50 |
stepis counted frommin.- A default must fit its field; a default for an undeclared key is an error.
- A key with no default is optional: it may be unset, and a
setcommand may clear it. A key with a default cannot be cleared. - Keys are unique.
minmay not exceedmax. - Caldera shows
unitcut to 20,helpto 300 andgroupto 60 characters. - Every
setis checked against these bounds in the runtime, all or nothing. Caldera checks first only to tell a person at once; the runtime decides.
secrets
secrets:
- name: ANTHROPIC_API_KEY # ^[A-Z][A-Z0-9_]{0,63}$, may not start with CALDERA_
purpose: Runs Claude Code, if the machine is not already signed in to Claude
optional: true
A manifest never carries a value. caldera init asks for each secret on the
machine (or reads CALDERA_SECRET_<NAME>) and writes it to
.caldera/secrets.env (mode 600). Secrets never pass through Caldera; they
are in the agent's environment under that name. Caldera holding third-party
credentials is not built.
bindings
bindings:
- product: "<product id>" # one product of the suite, see below
role: member # member | guest, never more
scopes: [content:read, content:write] # 1 to 4 of content:read, content:write, admin:read, admin:write
product is the id of the product the agent needs access to. The valid values
are "basalt", "lithic", "tecto" and "caldera", exactly as the schema file
lists them.
One binding per product, because the token is handed over as one set of environment variables per product. See Bindings and bots.
state
state: [data/]
An update replaces instructions and code and never touches state paths. They
live in <dir>/state and are linked into every version. A file the bundle ships
at a state path is a seed, copied only if nothing is there yet.
health
health:
command: [python3, health.py]
timeout_seconds: 30
Run after an update, in the new version's working directory, with the agent's
environment. Exit 0 means it works. Write it so that it needs the new version to
have run, by comparing a heartbeat with CALDERA_AGENT_STARTED.
A complete example
name: report-agent
version: 1.2.0
description: Writes a weekly report from a repository and keeps a status file.
runtime:
type: process
command: [python3, agent.py]
instructions: [CLAUDE.md, .claude/]
settings:
schema:
- key: minutes_between_runs
label: Minutes between runs
type: number
min: 5
max: 1440
unit: min
group: Work
- key: model
label: Model
type: select
options:
- { value: sonnet, label: Sonnet }
- { value: opus, label: Opus }
defaults:
minutes_between_runs: 60
secrets:
- name: GIT_TOKEN
purpose: Reads the repository
optional: true
state: [data/]
status: data/status.json
controls: [pause, resume, run_now]
logs:
- { name: agent, path: data/agent.log }
health:
command: [python3, health.py]
timeout_seconds: 30