In the Configuration lesson, you set up basic permissions with "ask" rules for destructive commands like rm and rmdir. Now let’s go further — adding guardrails for Git operations and learning about per-project overrides.
The three permission levels
Every action OpenCode takes is governed by one of three rules:
| Level | What happens |
|---|---|
allow | The action runs immediately, no questions asked |
ask | OpenCode pauses and asks for your approval before proceeding |
deny | The action is blocked entirely |
You’ve already seen allow and ask in your config. The deny level blocks an action outright — OpenCode won’t even attempt it. Use deny for things you never want to happen automatically, like chmod or chown.
When OpenCode asks for permission
When OpenCode hits an ask rule, it pauses and shows you three options:
- Allow Once — approve this specific action, just this time
- Allow Always — approve this action and similar ones for the rest of the session
- Reject — deny this specific action
This is the flow you’ll see whenever OpenCode tries to run rm, rmdir, or any other command gated by an ask rule.

Add Git guardrails
Destructive file commands aren’t the only thing worth guarding. Git operations can be just as consequential — an accidental git push to the wrong branch or a git checkout that discards uncommitted work. Let’s add rules for those.
Update the bash block in your global config:
"bash": {
"*": "allow",
"rm *": "ask",
"rmdir *": "ask",
"git push *": "ask",
"git checkout *": "ask"
}
Now OpenCode will pause before pushing to a remote or checking out a branch, giving you a chance to confirm.
Per-project permissions
Your global config applies to every project, but sometimes you need different rules for different repos. You can create a .opencode/opencode.json file in any project directory to override your global permissions for that project.
For example, if you’re working on a project where you trust the workflow and want OpenCode to push freely:
{
"permission": {
"bash": {
"git push *": "allow"
}
}
}
Project-level settings are layered on top of global ones — they override only the specific rules you set, leaving everything else unchanged.
Working outside the project
OpenCode is scoped to the directory where you started it. If it needs to read or edit a file somewhere else on your machine, the external_directory permission kicks in.
By default, external_directory is set to "ask" — OpenCode will pause and ask before touching anything outside the project. This is a good default: it prevents an agent from wandering into unrelated directories without your knowledge.
If you regularly work across multiple repos, you can allow specific external paths:
{
"permission": {
"external_directory": {
"~/projects/**": "allow"
}
}
}
Paths allowed via external_directory inherit the same tool defaults as your project. So if read is set to "allow", reads are also allowed in those external directories. You can layer additional rules on top — for example, allowing reads but denying edits in external paths.
For the full list of permissions and pattern syntax, see the permissions documentation.
Once you’ve added at least one new permission rule (like "git push *": "ask") to your global config, this lesson is complete.