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

# Config files

> Describe Tool Sets, Agents, variations, memory layers, and Widgets as YAML in .cadenya/ next to a cadenya.yaml, preview the changes with cadenya config plan, and apply them with cadenya config apply or the GitHub Action.

Your Cadenya setup can live in your repository as YAML: a `cadenya.yaml` for settings, and a `.cadenya/` directory with one file per resource. The CLI compares those files with your workspace, then creates, updates, and deletes until the two match.

So the setup goes through code review like everything else. No more "who changed the prompt?"

## Settings

`cadenya.yaml` sits at the root of your repository:

```yaml cadenya.yaml theme={null}
bundleKey: petstore-app
workspaceId: development
```

| Key | Answers |
| - | - |
| `bundleKey` | Which resources does this repository own? Set it here, with `--bundle-key`, or with `CADENYA_BUNDLE_KEY`. It takes 1 to 63 letters, digits, `-`, `_`, or `.`, and starts and ends with a letter or digit. |
| `workspaceId` | Which workspace do the files describe? A workspace ID, or an external ID such as `development`. |
| `baseUrl` | Which API does the CLI call? It defaults to `https://api.cadenya.com`. |
| `resourceDir` | Where are the resource files? It defaults to `.cadenya`. |

The file takes those four keys and nothing else (validation rejects a fifth, typo or not). Flags and environment variables beat the file: `--workspace-id` or `CADENYA_WORKSPACE_ID`, `--base-url` or `CADENYA_BASE_URL`, and `--resource-dir`. So `CADENYA_WORKSPACE_ID` points the same files at another workspace. With no workspace set anywhere, the CLI falls back to its stored default.

Keep the API key out of `cadenya.yaml`. The CLI reads it from `CADENYA_API_KEY`.

## Resource files

Each resource gets one YAML file, in the directory for its type:

```text theme={null}
cadenya.yaml
.cadenya/
  toolSets/
    petstore.yaml            # an OpenAPI Tool Set
    frontend.yaml            # a Bare Tool Set
    frontend/
      fetch-form-state.yaml  # a tool in the frontend Tool Set
  memoryLayers/
    playbooks.yaml
    playbooks/
      tool-set-setup.yaml    # an entry in the playbooks layer
  agents/
    petstore-agent.yaml
    petstore-agent/
      default.yaml           # a variation of petstore-agent
  widgets/
    support-chat.yaml
```

A file holds the same `metadata` and `spec` the API takes, written as YAML. Here's a variation that picks its model and gets a Tool Set:

```yaml .cadenya/agents/petstore-agent/default.yaml theme={null}
metadata:
  name: Default
spec:
  systemPromptTemplate: |
    You are a helpful assistant for the Swagger Petstore. Use the available
    tools to look up pets, inventory, and orders. Do not invent pet data.
  modelConfig:
    modelId: getting-started-key.anthropic-claude-haiku-latest
  assignments:
    - type: toolSetId
      toolSetId: external_id:petstore
```

A few rules shape every bundle:

1. A file's external ID comes from `externalId`, then `metadata.externalId`, then the filename. **Renaming a file without an explicit external ID changes its identity, so the plan shows a delete and a create.**
2. A child directory takes its parent's filename: the variations of `agents/petstore-agent.yaml` live in `agents/petstore-agent/`.
3. Resources in the same bundle refer to each other by external ID, such as `external_id:petstore` for a Tool Set or `external_id:frontend/fetch-form-state` for one of its tools.
4. An Agent file takes `state: published` or `state: draft`. A published Agent needs at least one variation in the bundle.
5. A missing type directory means you want none of that type. Remove `agents/`, and apply deletes every Agent in the bundle.

The CLI's [bundle guide](https://github.com/cadenya/cadenya-cli/blob/main/docs/config-bundles.md) lists every field for Tool Sets, tools, memory layers, memory entries, Agents, variations, and Widgets.

## Trusted endpoints

Config files don't touch trusted endpoints. Cadenya sends Tool Set requests only to hosts your account trusts, and that list lives on the account, not in your repository. So trust each host your Tool Sets call before you apply:

```bash theme={null}
cadenya trusted-endpoints create --name "Swagger Petstore" --host petstore3.swagger.io
```

A host can be exact (`api.stripe.com`, with an optional port) or a wildcard for exactly one label: `*.example.com` covers `api.example.com` but not `example.com` itself. One entry covers every workspace. Every account also trusts `free.cadenya.com`, the server behind Cadenya's onboarding examples.

To check a URL before you apply:

```bash theme={null}
cadenya trusted-endpoints verify --display json --url https://petstore3.swagger.io/api/v3/openapi.json
```

The result is `RESULT_TRUSTED` once the host is on the list.

## Validate, plan, and apply

Three commands take the files from your repository to your workspace:

```bash theme={null}
cadenya config validate   # offline: YAML, fields, and references
cadenya config plan       # reads the workspace, changes nothing
cadenya config apply      # deletes what's missing, then creates and updates
```

Run them from the repository root, or point them at it with `-C path/to/repo`. A plan for the Petstore example looks like this (log lines trimmed):

```
create    toolSet petstore
create    agent petstore-agent
create    agentVariation petstore-agent/default
publish   agent petstore-agent
Plan: 3 create, 0 update, 0 delete, 0 detach, 1 state change
```

Every resource in the bundle gets an update on every apply, so a plan right after an apply lists each one as `update`. That's expected. It isn't drift.

If a request fails, apply stops and reports what finished. Fix the cause and run it again: each run plans from what's in your workspace now.

## Bundle keys

Every resource apply writes gets the label `bundle_key=<your bundle key>`. Plan and apply list everything with that label and compare it with `.cadenya/`.

**Anything that carries your bundle key and has no file gets deleted on the next apply.** That's how you delete a resource: remove its file and apply. It's also why two repositories can't share a key. Each one would delete the other's resources, one apply at a time.

The label also keeps apply away from everything else in your workspace:

1. Apply never adopts a resource without your label. If an external ID you're about to create already exists outside the bundle, the plan fails and names it.
2. Apply never deletes a resource that something outside the bundle still uses, such as a Tool Set that another variation assigns. The plan names the resource in the way.
3. An empty `.cadenya/` means "delete the whole bundle", so apply refuses it unless you pass `--allow-empty`.

Changing the bundle key starts a new, empty bundle. The resources you applied before keep the old label, and the new key leaves them alone.

> [!WARNING]
>
> Give each repository its own bundle key, and read every `delete` line in the plan before you apply.

## Apply from GitHub Actions

The [cadenya-config-loader](https://github.com/cadenya/cadenya-config-loader) Action downloads a pinned CLI release (its `version` input picks which), checks the checksum, and runs the same commands. This workflow plans on every pull request and applies on every push to `main`:

```yaml .github/workflows/cadenya.yaml theme={null}
name: Configure Cadenya
on:
  pull_request:
    paths: [".cadenya/**", "cadenya.yaml"]
  push:
    branches: [main]
    paths: [".cadenya/**", "cadenya.yaml"]
permissions:
  contents: read
jobs:
  plan:
    # Pull requests from forks can't read the secret, so they skip the plan.
    if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: cadenya/cadenya-config-loader@v0.2.0
        with:
          command: plan
          api-key: ${{ secrets.CADENYA_API_KEY }}
  apply:
    if: github.event_name == 'push'
    runs-on: ubuntu-latest
    # Each apply plans from the workspace as it is, so two at once would plan
    # against each other's half-finished work.
    concurrency:
      group: cadenya-development-petstore-app
      cancel-in-progress: false
    steps:
      - uses: actions/checkout@v7
      - uses: cadenya/cadenya-config-loader@v0.2.0
        with:
          command: apply
          api-key: ${{ secrets.CADENYA_API_KEY }}
```

The workspace and the bundle key come from `cadenya.yaml`, so the workflow doesn't repeat them. Inputs such as `workspace-id` and `bundle-key` override the file, and `allow-empty: 'true'` is the Action's `--allow-empty`. On a pull request, the plan job prints the operations in its log, so you see every `delete` before it happens.

Starting a new project from a coding agent? [Set up Cadenya from a coding agent](https://cadenya.com/docs/setup-from-a-coding-agent.md) puts these files and the API key together.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.