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  [?]help

For 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

KeyAction
yApprove this one operation
nDeny this operation
AApprove all remaining operations in the current turn
sAuto-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
ModeBehavior
askPrompt for each operation (default)
auto-approveSkip all prompts — agent runs freely
denyBlock 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

ToolRule TypeExample
bashExact match or prefix wildcard"npm test", "npm run *"
gitSubcommand namecommit, push
write / editGlob 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 targetBehavior
Inside the project rootAuto-allowed, no prompt
Outside the project root (../, ~/, absolute)Approval required every time
Sensitive files inside the project rootApproval required every time

Sensitive file patterns (always prompt, even inside the project root):

  • .env, .env.* — environment files
  • *.pem, *.key — private keys
  • id_rsa, id_ed25519 — SSH private keys
  • .git/config — git config (may contain credentials)
  • Files with credentials or secrets in 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:

  1. The agent decides it wants to call a tool
  2. The tool executor passes the request to the approval gate
  3. The gate checks allow-lists, then prompts the user if needed
  4. The result (approve/deny) is returned to the tool executor
  5. 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

Last updated May 12, 2026