.cabinet — manifest schema
Every cabinet folder has a .cabinet file at its root. It's the only file Cabinet requires. Without it, a folder is just a folder; with it, the folder becomes a cabinet that the app can open, the registry can install, and other cabinets can nest.
schemaVersion: 1
id: my-startup
name: My Startup
description: One-person SaaS company with an AI team.
kind: root
version: 0.1.0
entry: index.md
Every field
| Field | Type | Required | Default | What it does |
|---|---|---|---|---|
schemaVersion | integer | yes | — | Always 1 right now. Lets Cabinet evolve the schema without breaking old cabinets. |
id | string | yes | — | Slug used as the cabinet's stable identifier. [a-z0-9-]+. |
name | string | yes | — | Display name. Shown in the sidebar header and in the registry. |
description | string | no | "" | One-line summary. ≤80 chars — that's the registry card limit. |
kind | "root" | "child" | yes | — | Root cabinets stand alone; child cabinets nest under another cabinet's folder. |
version | semver | no | "0.1.0" | Useful for templates published to cabinets.sh. |
entry | path | no | "index.md" | The page Cabinet opens when you click into the cabinet. |
icon | string | no | inherits | Path or URL to a square icon used in the sidebar and registry. |
tags | string[] | no | [] | Free-form tags used for registry filtering. |
license | SPDX id | no | inherits | MIT, Apache-2.0, etc. Shown on the registry page. |
author | object | no | — | {name, url, email}. Shown on the registry page. |
providers | object | no | — | Provider routing config. See BYOAI for the shape. |
dispatch | object | no | — | Cross-department dispatch policy. See Org chart. |
approvals | object | no | — | Auto-approval rules for low-impact proposals. |
Root vs. child
A root cabinet is the top of a tree. It has its own .cabinet, lives directly in $CABINET_DATA_DIR, and shows up in cabinetai list.
A child cabinet is a folder inside another cabinet that gets its own scope, agents, and settings. The child has its own .cabinet, and the parent treats it as a sub-cabinet (it shows up nested in the sidebar).
~/cabinets/
├── my-startup/ ← root
│ ├── .cabinet ← kind: root
│ └── clients/
│ └── acme/
│ ├── .cabinet ← kind: child
│ └── index.md
└── job-hunt-hq/ ← root
└── .cabinet ← kind: root
Provider routing example
providers:
defaults:
lead: claude-opus-4-7
specialist: claude-sonnet-4-6
fallbacks:
claude-opus-4-7:
- claude-sonnet-4-6
- gpt-4.1
budgets:
daily:
maxCostUsd: 25
perTask:
maxCostUsd: 5
localOnly: false
| Subkey | Purpose |
|---|---|
defaults.lead / defaults.specialist | Default model per agent type. |
fallbacks | Per-model fallback chain when the primary returns 429 or hits a budget cap. |
budgets.daily.maxCostUsd | Hard cap per cabinet per day. |
budgets.perTask.maxCostUsd | Hard cap per individual task. |
localOnly | If true, refuse any non-local provider. |
Dispatch policy example
dispatch:
allow:
- from: marketing/gtm-lead
to: research/*
kinds: [LAUNCH_TASK]
- from: operations/ops-coordinator
to: marketing/copywriter
kinds: [LAUNCH_TASK, SCHEDULE_JOB]
acceptFromParent:
- from: ops-coordinator
kinds: [LAUNCH_TASK]
See Org chart & departments for the full story.
Approvals example
approvals:
auto:
- kind: LAUNCH_TASK
maxCostUsd: 0.10
fromAgent: linkedin-operator
toAgent: copywriter
- kind: SCHEDULE_TASK
maxCostUsd: 0.50
fromAgent: ceo
Anything outside policy queues for manual approval. There's no global "trust everything" toggle.
Validation
Cabinet validates the manifest at boot. cabinetai doctor re-runs validation. A bad manifest exits with code 6 and a precise field-level error.
Read on
- Persona schema — the agent file format.
- Job schema — the routine YAML format.
- Skill schema — the SKILL.md format.
- File structure — what else lives in a cabinet folder.