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
}| Field | Type | Default | Meaning |
|---|---|---|---|
allow | string[] | unset | An 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. |
env | string[] | unset | An allowlist of env var names caddi env pull is permitted to resolve and write to .env.local. |
get_secret | boolean | false | Opt-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
approveandenv pull. - Security model: how the approval gate fits into the broader trust model.