Caddi
Sign inGet started

Docs

caddi.json

An optional file at a project’s root that scopes what a coding agent working in that project can reach through the gateway. No caddi.json at all is a valid, common state: it just means nothing is scoped down.

Where it’s found

The CLI and gateway walk up from the current working directory looking for a caddi.json, the same way tsconfig.json or package.json resolution works: they check the current directory first, then each parent, up to the filesystem root. The first one found wins, so a caddi.json at a monorepo root scopes every package underneath it unless a package has its own.

A caddi.json that exists but is malformed (invalid JSON, or a field with the wrong shape) throws rather than being silently treated as “no config present.” A project that intentionally scoped its agent down must never silently fall back to unscoped access just because of a typo.

Schema

{
  "allow": ["vercel_*", "github_create_pr"],
  "env": ["VERCEL_TOKEN", "GITHUB_TOKEN"],
  "get_secret": false
}
FieldTypeDefaultMeaning
allowstring[]unsetAn allowlist of MCP tool-name globs. Only * is a wildcard (matches any run of characters); every other character matches literally. A tool is shown to the agent only if its name matches at least one glob.
envstring[]unsetAn allowlist of env var names caddi env pull is permitted to resolve and write to .env.local.
get_secretbooleanfalseOpt-in flag that registers the raw get_secret MCP tool for this project. Off unless set to true.

The default is everything, not nothing

This is the detail worth stating plainly: no caddi.json, or a caddi.json with no allow key, means every tool in the registry stays visible to the agent, for every provider you’ve connected. allow is opt-in scoping, not a default-deny gate. If you want an agent limited to a specific set of tools in a given project, you have to write an allow list; it doesn’t happen automatically just because the file exists.

The one exception is allow: []: present, but empty. Thatdoes deny everything. An explicit empty allowlist is read as a deliberate lockdown, not as “unset.”

Recommendation: for any project where a coding agent runs against real credentials, write an explicit allow list scoped to the tools that project actually needs, rather than relying on the wide-open default.

get_secret and the env allowlist need a human ack too

get_secret and a non-empty env allowlist are the two fields that widen what a project can reach: a get_secret call returns a connected service’s decrypted credential as plaintext, and caddi env pull writes plaintext to disk. Neither takes effect just because a repo-committed caddi.json asks for it. A human has to approve it once, outside the repo, with:

caddi approve get_secret
caddi approve env

The approval is recorded at $CADDI_HOME/approvals.json, keyed by a hash of the caddi.json’s resolved directory, never by anything the repo itself controls, so a cloned repo’s caddi.json can never approve itself. This closes a specific threat: a malicious cloned repo whose caddi.json requests get_secret or a broad env list, combined with a prompt-injected agent that tries to exfiltrate whatever it’s handed.

Other useful forms of the command:

  • caddi approve get_secret --yes: skip the confirmation prompt, for scripting.
  • caddi approve --project ./apps/web: approve for a project other than the current directory.
  • caddi approve --list: list every recorded approval.
  • caddi approve --revoke (or --revoke <hash>): revoke an approval.

Next

  • CLI reference: the full command list, including approve and env pull.
  • Security model: how the approval gate fits into the broader trust model.