Devin's settings live not in one file but in several: your personal, the team's and the machine's local ones. While they agree, the difference is invisible; but as soon as one file allows what another forbids, you need to know exactly whose answer will be the final one - otherwise you trust a permission or a setting that is in fact overridden. And learning it after the fact means discovering that an action you thought allowed was quietly not happening, or, worse, that something you thought closed was happening.
The naive move is to apply the usual config logic: the most specific file wins, or the most recently edited one, or the one where you just added an allow. It seems natural: add a permission, get the capability. With ordinary settings that is how it works.
It breaks on the merging of permissions. Permission decisions are not combined by a "last writer wins" rule: a deny at a higher level is not overridden by an allow at a lower one. If the organization denies Exec(sudo), your personal allow on Exec(sudo) gives nothing - the organization denial always wins. Relying on the usual logic, you would be sure you granted a permission that in fact does not exist.
It helps to start with the three files and their roles. Below is a map: where each config lives, what it is responsible for, and whether it is committed. You return to it every time you decide where to put a setting so it lands in the right scope and does not leak into a place it does not belong.
The order of authority runs top to bottom. At the very top are organization and team settings: they cannot be overridden. Then come session grants - interactive approvals living only in the session's memory. Below them are project-local (.devin/config.local.json), project (.devin/config.json) and, at the bottom, user (~/.config/devin/config.json). But over this whole ladder one rule holds: when decisions are merged, deny always wins. Session grants sit high for a reason: an approval given in a live session overrides file settings, but it lives only in memory and disappears with the session, never turning into a permanent right.
Why it is built this way. The organization is on top so that team policy cannot be quietly canceled on your own machine. Deny-wins is there so that a restriction is never weakened by a permissive layer added below. Security in this model composes upward, not downward: the hard rule on top holds, the soft one below does not break through. This makes behavior predictable across a team where different people have different local configs. A side benefit is debugging: if behavior differs between two machines, you look for the cause not among equal files but in which level holds the deciding deny or the organization policy.
| File | Purpose | Commit |
|---|---|---|
| ~/.config/devin/config.json | Personal global settings | No |
| .devin/config.json | Team project policy | Yes |
| .devin/config.local.json | Local overrides | No |
The project config has a deliberately narrow scope. In .devin/config.json only three categories are allowed: permissions (allow, deny, ask rules), import settings via read_config_from (reading the Cursor, Windsurf and Claude formats) and hooks. Everything else - model selection, theme, keymap, proxy, the sandbox network filter, auto-update, attribution, subagent enablement - are user-only fields; in a team file they are ignored, and they should not be placed there. This is not arbitrary but a safeguard: a team file is committed and applies to everyone, so it allows exactly what makes sense as team policy, while personal preferences and machine details stay outside it.
A couple of practical details settle frequent questions. The format is JSON with comments: both line // and block /* */ are allowed, so scopes can be labeled right in the file. On Windows the global path is in %APPDATA%\devin\. And the MCP configuration is moved into separate mcp_config.json files (user, project, project-local) - it is not looked for here.
The effective permission must be checked not by one file but by the merged result - and remember that a deny anywhere above beats an allow below. If a capability "will not turn on", do not add new allows blindly: look first for a deny at a higher level. One glance up the ladder is most often the answer, not another permission in your own file. The reverse rule helps too: before allowing something in a personal config, make sure it is not denied above - otherwise you spend time on an allow that will never take effect.
The typical failures repeat from project to project. User-only fields are put into .devin/config.json, and people are surprised they have no effect. An attempt is made to beat a higher deny with a lower allow - and the same prohibition remains. Secrets or personal overrides are committed into the shared project file - creating either a leak or an imposition on the team. Project-local is expected to outrank the organization - a mistake about priority. The sign is always the same: "I allowed it, but it is still blocked". The answer is almost always a deny at a higher level; look up first, then edit.