Permissions & --yolo
Fred asks before every tool call by default. Approving means "run this," denying means "don't, explain why instead." The system is two orthogonal axes: approval mode (the prompt gate) and sandbox mode (the kernel-enforced ground truth). Together with permission rules, that gives you a knob for every level of trust from "ask me everything" to "go for it."
The prompt
● bash: pytest tests/test_auth.py -q
[y]es / [n]o / [a]ll / [d]etails ›| Key | Effect |
|---|---|
y | Run this one call, ask again next time. |
n | Refuse. Fred sees the refusal + reason in the message stream. |
a | Approve all calls of this kind for the rest of the session — and persist as an allow rule. |
d | Show full args (file diffs, command stdin, etc.) before deciding. |
Read-only tools (view, grep, glob) skip the prompt entirely — they can't change anything. Bash, file writes (str_replace, create_file), and shell-affecting tools all need approval before they run.
Permission categories
Every tool declares a requires_permission category. The dispatcher consults that flag before doing anything user-visible.
- Read-only — auto-approved, no prompt.
view,grep,glob,repo_map,request_rule. - Permission-required — prompts unless a rule allows.
bash,str_replace,create_file, MCP tools, anything that mutates state.
Approval modes
Approval mode controls the y/n/a/d panel. Three settings, ported from Codex CLI's same axis:
| Mode | Behavior |
|---|---|
untrusted | Always prompt. Even an explicit allow-rule still shows the panel. Use when you don't trust the prompt yet. |
on_request | Default. Prompt unless a rule auto-allows. The friendly middle ground. |
never | Skip prompts entirely. Subset of --yolo; rules and the sandbox are your only defenses. |
Set via:
--approval <mode>flag at startupFRED_APPROVAL_MODEenv var/sandbox approval <mode>in the REPL
Resolution priority: explicit flag > env var > default (on_request). Even in never mode, deny rules still apply — they're a hard floor that no approval setting overrides.
Sandbox modes
Sandbox mode controls what bash commands can actually do at the kernel level. macOS only for now (via sandbox-exec / Seatbelt — same primitive Codex CLI uses).
| Mode | Filesystem |
|---|---|
ro | Read-only filesystem. Bash can read but not write anywhere outside cwd's repo metadata. |
workspace-write | Default. Writes inside cwd allowed; can't escape upward. Network is denied. |
full | No sandbox. Full trust. The world is yours; act accordingly. |
Set via:
--sandbox <mode>flag at startupFRED_SANDBOX_MODEenv var/sandbox <mode>in the REPL
Bash invocations get wrapped in /usr/bin/sandbox-exec -p <profile> before spawning. The path to sandbox-exec is hardcoded — defense against PATH-injection attacks. When the kernel denies an op, you'll see a sandbox-exec: prefix in stderr.
Permission rules
Rules are an allowlist/denylist of Tool(specifier) patterns. Stored in ~/.dsc/settings.local.json (and project .dsc/settings.json for team-shared rules). Three lists, evaluated in this order:
- deny — short-circuits. If a pattern matches, the tool refuses without ever asking.
- ask — always prompts. Useful to shadow a previous "allow always" pick — drop a rule into
askand the next call gets the panel back. - allow — auto-yes. Skips the panel.
Manage rules via slash commands:
/perms # list all active rules
/perms add Bash(npm run *) allow # append to allow list
/perms add Bash(rm *) deny # append to deny list
/perms add Bash(curl *) ask # always prompt for curl
/perms remove Bash(npm run *) # remove from any listRules are loaded by walking from cwd up toward $HOME, stopping at the first .git boundary. Project rules (settings.json) are committed and shared; local rules (settings.local.json) are gitignored and personal.
--yolo flag
fred --yoloAlias for --approval never. Prompts skipped, all tools auto-approved up to sandbox limits. Useful for:
- Greenfield projects where there's nothing to lose.
- Well-tested CI pipelines.
- When you trust both the prompt and the model and want to watch it move.
Don't use it on a machine that has access to anything you can't afford to lose. The sandbox is your remaining defense — pair --yolo with --sandbox workspace-write at minimum.
Common rule patterns
Pattern syntax is Tool(specifier) — same grammar across the rule engine, hooks, and /perms. Globs use the standard * wildcard.
| Pattern | Matches |
|---|---|
Bash(git status) | One literal command. Exact match. |
Bash(git *) | Any git invocation. Won't match shell-chained commands. |
Bash(npm test) | Just npm test. Common safe one to allowlist. |
Bash(pytest) | Just pytest with no args. |
Bash(pytest *) | Pytest with any args. |
View(src/*) | Read anything under src/. Probably already auto-allowed; explicit just to be safe. |
Bash(rm *) | Any rm. Usually want this in deny or ask. |
Bash(curl *) | Any curl. Network egress — usually ask at minimum. |
Important: an allow-rule glob like Bash(git *) does NOT authorize git status; rm -rf /. Bash specifiers containing shell-control metacharacters (;, &&, ||, |, &, backticks, $(), newlines) are never authorized by an allow-glob — the panel falls through and you get a chance to look. Deny-rules deliberately still match these strings (so you can deny a quoting bypass intentionally).
Persisting choices
When the panel asks y/n/a/d and you hit a, two things happen:
- The tool name + specifier is added to the in-session always-allow set so the same call doesn't prompt again this session.
- The rule is written to
~/.dsc/settings.local.jsonso the prompt doesn't repeat next session either.
Persistence is best-effort — a write failure (read-only home, perms, etc.) just means the in-session set still covers the rest of THIS run. Picking y approves once; only a persists.
Bash safety
bash calls run with a 30s timeout. Stdout + stderr are captured (up to 4KB) and returned to the model so it can react to errors. The model never sees secrets in your shell — only what the command prints.
Combined with sandbox mode, this means even an aggressive bash command is constrained: a workspace-write sandbox can't escape cwd, can't reach the network, can't write to ~/.ssh. The kernel says no before the model sees a result.
--yolo + --sandbox full is fully autonomous. Use it on disposable VMs or new branches, not on your main repo with uncommitted work. There's no /restore from a force-push.
If you're constantly answering "yes" to the same prompt, hit a once and Fred remembers — both for this session and next time.
Related
See /docs/configuration for the settings.json file shape, and /docs/checkpoints for the rollback story when permissions weren't strict enough.