Comprehensive guide to the security model, permission system, data protection, and safety features.
Coder implements a defense-in-depth security model with multiple layers:
- Permission Engine — Controls what agents can do
- Protected Paths — Prevents access to sensitive filesystem locations
- Command Filtering — Blocks dangerous system commands
- Autonomy Levels — Limits agent freedom based on trust
- Sandboxing — Isolates plugin execution
- Encrypted Storage — Protects API keys and credentials
The PermissionEngine is the central security authority. It evaluates every agent action against a rule set and determines whether to allow, deny, or ask the user.
Agent Action
│
▼
Check Cache ──→ Cache Hit ──→ Return Cached Result
│
▼ Cache Miss
Get Autonomy Level
│
▼
Evaluate Rules (priority-ordered)
│
▼
Dangerous Command? ──→ Upgrade to 'ask'
│
▼
Protected Path? ──→ Upgrade to 'deny'
│
▼
Learned Rule? ──→ Apply if confidence >= threshold
│
▼
Return Verdict (allow/deny/ask)
| Level | Behavior |
|---|---|
| none | Deny everything unless explicitly allowed by a rule |
| limited | Deny by default; upgrade 'ask' verdicts to 'deny' |
| moderate | Default; ask the user for actions without explicit rules |
| full | Allow everything except dangerous commands |
| Rule | Resource | Verdict | Priority |
|---|---|---|---|
Deny /etc/** |
filesystem | deny | 100 |
Deny **/.ssh/** |
filesystem | deny | 100 |
Deny **/.gnupg/** |
filesystem | deny | 100 |
Ask for **/.env |
filesystem | ask | 50 |
Allow registry.npmjs.org |
network | allow | 10 |
Allow pypi.org |
network | allow | 10 |
Ask for npm install* |
command | ask | 20 |
Ask for pip install* |
command | ask | 20 |
Deny format * |
command | deny | 200 |
Deny rm -rf * |
command | deny | 200 |
The RuleEvaluator maintains a list of dangerous command patterns:
rm -rf/rm --recursive --forcesudo/runasformat(disk formatting)chmod/chown/icacls/takeowndd if=/mkfs.shutdown/reboot/halt/poweroffreg add/delete/import(Windows registry)systemctl stop/disable/maskapt remove/npm uninstall -g/pip uninstallgit push --force/git reset --hard/git cleancurl | sh/wget | sh(pipe to shell)eval/execdocker rm/rmikubectl delete
Even at full autonomy, dangerous commands always require explicit user approval.
The ConfigManager defines OS-specific protected paths:
~/.ssh
~/.gnupg
/etc
/usr
/bin
/sbin
/sys
/proc
%USERPROFILE%\.ssh
%USERPROFILE%\.gnupg
%SystemRoot%\System32
C:\Program Files
C:\Program Files (x86)
The PermissionEngine adds deny rules for all protected paths on initialization.
The PermissionStore learns from user decisions to reduce prompt fatigue:
- User makes a decision (approve/deny/always/never)
- Decision is recorded in the store
- After 3+ similar decisions, a learned rule is created
- "Always allow/deny" decisions create rules immediately
- Learned rules have lower priority than explicit rules
| Threshold | Action |
|---|---|
| >= 85% | Auto-apply learned rule |
| >= 70% | Include as lower-priority rule |
| < 70% | Don't auto-apply |
API keys are stored using Electron's safeStorage API:
// Encryption
const encrypted = safeStorage.encryptString(apiKey);
fs.writeFileSync(keyPath, encrypted);
// Decryption
const encrypted = fs.readFileSync(keyPath);
const apiKey = safeStorage.decryptString(encrypted);- Keys are never stored in plaintext
- Encryption uses the OS keychain (Keychain on macOS, Credential Manager on Windows, libsecret on Linux)
- Keys are scoped per-provider
Plugins run in a restricted environment:
- Permission-scoped: Can only access declared permissions
- Resource-limited: Memory and CPU limits enforced
- Network-filtered: Only allowed domains accessible
- Filesystem-scoped: Limited to workspace + plugin directory
- Error-isolated: Plugin errors don't crash the host
| Mode | Description |
|---|---|
| strict | Every action requires approval; autonomy = none |
| normal | Default mode; autonomy = moderate |
| permissive | Most actions allowed; autonomy = full |
| auto | Adjusts based on risk assessment |
All permission evaluations are recorded in the PermissionStore history:
interface PermissionHistoryEntry {
id: string;
agentId: string;
resourceType: string;
resource: string;
requested: PermissionVerdict;
resolved: PermissionVerdict;
source: 'rule' | 'user' | 'auto' | 'learned';
durationMs: number;
timestamp: number;
}History entries can be queried, filtered, and reviewed through the UI or API.
- Never hardcode API keys or secrets in source code
- Always check permissions before performing agent actions
- Use the PermissionEngine instead of direct filesystem/command access
- Validate all inputs from external sources (providers, plugins, IPC)
- Sanitize file paths to prevent directory traversal
- Use parameterized queries for database operations
- Log security events for audit purposes
- Test permission edge cases in your test suites