Permissions & Approval
Copair uses a human-in-the-loop (HITL) approval gate to ensure the agent never takes destructive actions without your consent. The gate sits outside the agent's reasoning loop — the agent has no reference to it and cannot influence whether it fires.
How Approval Works
When Copair wants to perform an action — write a file, run a shell command, or make a git commit — the approval gate intercepts the request and prompts you:
Approve?
bash: npm test
[y]es [n]o [A]ll [s]imilar [?]helpFor write and edit calls, the prompt also shows a unified diff of the change before you decide:
--- src/auth.ts
+++ src/auth.ts
@@ -12,3 +12,7 @@
+export function validateToken(token: string): boolean {
+ return token.length > 0 && !isExpired(token)
+}The diff is computed before execution — if you deny, nothing has touched disk.
Approval Keys
| Key | Action |
|---|---|
y | Approve this one operation |
n | Deny this operation |
A | Approve all remaining operations in the current turn |
s | Auto-approve future calls to the same tool with similar arguments |
? | Show help |
Read-only operations like git status, git diff, and git log do not require approval.
Permission Modes
Set the default permission mode in your config:
# ~/.copair/config.yaml
permissions:
mode: ask # ask | auto-approve | deny| Mode | Behavior |
|---|---|
ask | Prompt for each operation (default) |
auto-approve | Skip all prompts — agent runs freely |
deny | Block all mutating operations |
Allow-Lists
For operations you trust, define allow-lists so Copair skips the approval prompt automatically.
File Locations
- Global:
~/.copair/allow.yaml— applies to all projects - Project:
.copair/allow.yaml— applies to the current project only
Project entries extend the global list — they never replace it.
Format
# .copair/allow.yaml
bash:
- "npm test" # exact match
- "npm run *" # prefix wildcard
- "pnpm build"
git:
- status # subcommand
- diff
- log
- add
- commit
write:
- "src/**/*.ts" # glob pattern
- "tests/**/*.test.ts"
edit:
- "src/**/*.ts"Matching Rules
| Tool | Rule Type | Example |
|---|---|---|
bash | Exact match or prefix wildcard | "npm test", "npm run *" |
git | Subcommand name | commit, push |
write / edit | Glob path pattern | "src/**/*.ts" |
*matches within a single path segment**matches across segments
Allow-list does not skip approval for new-file writes
A write glob ("src/**/*.ts") auto-approves writes to files that already exist. If the agent tries to create a new file the allow-list still matches, but the approval gate fires anyway — new file creation always requires explicit confirmation. This prevents an agent from silently materialising arbitrary files inside a directory you trusted for edits.
The allow-list is also session-scoped per path, not per tool. Selecting "always allow" on a write to src/auth.ts approves future writes to that file, not future writes to any file.
Reads — tiered approval
Reads are split into three tiers based on what's being read:
| Read target | Behavior |
|---|---|
| Inside the project root | Auto-allowed, no prompt |
Outside the project root (../, ~/, absolute) | Approval required every time |
| Sensitive files inside the project root | Approval required every time |
Sensitive file patterns (always prompt, even inside the project root):
.env,.env.*— environment files*.pem,*.key— private keysid_rsa,id_ed25519— SSH private keys.git/config— git config (may contain credentials)- Files with
credentialsorsecretsin the name
This applies to read, glob, and grep — anything that surfaces file contents to the model.
Auto-Trust for .copair/
When Copair initializes a project (creates .copair/ directory, config files, and .gitignore), it automatically trusts its own directory. Writes to .copair/sessions/, .copair/commands/, and .copair/.gitignore do not trigger approval prompts — even when permissions.mode is set to deny.
This trust is scoped to .copair/ only and does not grant broader write access.
Security Model
The approval gate is architecturally separated from the agent:
- The agent decides it wants to call a tool
- The tool executor passes the request to the approval gate
- The gate checks allow-lists, then prompts the user if needed
- The result (approve/deny) is returned to the tool executor
- If denied, an error result is sent back to the agent — not a crash
The agent cannot see, reference, or reason about the gate. It receives a tool error if denied and must adjust its approach.
Next Steps
- Configuration Reference — Full
permissionsconfig block documentation - Slash Commands — Commands for managing your session